Commit 7188e3bf authored by Laszlo Bacsi's avatar Laszlo Bacsi Committed by Sindre Sorhus

DerbyJS: Various improvements

parent 66197be6
...@@ -3,21 +3,25 @@ derby = require 'derby' ...@@ -3,21 +3,25 @@ derby = require 'derby'
derby.use(require '../../ui') derby.use(require '../../ui')
# Define a rendering function to handle both /:groupName and ## ROUTES ##
# /:groupName/filterName.
renderGroup = (page, model, {groupName, filterName}) -> # Redirect the visitor to a random todo list
get '/', (page) ->
page.redirect '/' + parseInt(Math.random() * 1e9).toString(36)
# Sets up the model, the reactive function for stats and renders the todo list
get '/:groupName', (page, model, {groupName}) ->
groupTodosQuery = model.query('todos').forGroup(groupName) groupTodosQuery = model.query('todos').forGroup(groupName)
model.subscribe "groups.#{groupName}", groupTodosQuery, (err, group, groupTodos) -> model.subscribe "groups.#{groupName}", groupTodosQuery, (err, group, groupTodos) ->
model.ref '_group', group model.ref '_group', group
model.setNull('_group._id', groupName) group.setNull 'id', groupName
todoIds = group.at 'todoIds' or [] todoIds = group.at 'todoIds' or []
# The refList supports array methods, but it stores the todo values on an # The refList supports array methods, but it stores the todo values
# object by id. The todos are stored on the object # on an object by id. The todos are stored on the object 'todos',
# 'groups.groupName.todos', and their order is stored in an array of ids at # and their order is stored in an array of ids at '_group.todoIds'
# '_group.todoIds' model.refList '_todoList', 'todos', todoIds
model.refList '_todoList', "groups.#{groupName}.todos", todoIds
# Create a reactive function that automatically keeps '_stats' # Create a reactive function that automatically keeps '_stats'
# updated with the number of remaining and completed todos. # updated with the number of remaining and completed todos.
...@@ -36,63 +40,60 @@ renderGroup = (page, model, {groupName, filterName}) -> ...@@ -36,63 +40,60 @@ renderGroup = (page, model, {groupName, filterName}) ->
oneOnly: remaining == 1, oneOnly: remaining == 1,
} }
filterName = filterName or 'all' # Do not filter the list by default
page.render 'todo', model.del '_filter'
filterName: filterName
groupName: groupName
## ROUTES ## page.render 'todo'
get '/', (page) ->
page.redirect '/' + parseInt(Math.random() * 1e9).toString(36)
get '/:groupName', renderGroup # Transitional route for enabling a filter
get '/:groupName/:filterName', renderGroup get {from: '/:groupName', to: '/:groupName/:filterName'},
forward: (model, {filterName}, next) ->
# enable the filter
model.set '_filter', filterName
back: (model, params, next) ->
# disable the filter
model.del '_filter'
ready (model) -> ready (model) ->
list = model.at '_todoList' list = model.at '_todoList'
group = model.at '_group' group = model.at '_group'
all_completed = group.at 'all_completed'
group.on 'set', 'select_all', (select_all, previous_value, isLocal, e) -> group.on 'set', 'all_completed', (all_completed, previous_value, isLocal, e) ->
# We only want to react to select_all being set if it's in response # We only want to react to all_completed being set if it's in response
# to a UI event (as opposed to our checkAllCompleted below checking # to a UI event (as opposed to our checkAllCompleted below checking
# individual items). # individual items).
return unless e return unless e
# Is there a way to do this with one call rather than iterating? # Is there a way to do this with one call rather than iterating?
todos = model.at('_group.todos') for {id} in list.get()
for item in list.get() model.set "todos.#{id}.completed", all_completed
todos.set("#{item.id}.completed", select_all)
newTodo = model.at '_newTodo' newTodo = model.at '_newTodo'
exports.add = -> exports.add = ->
# Don't add a blank todo # Don't add a blank todo
text = view.escapeHtml newTodo.get().trim() text = newTodo.get().trim()
newTodo.set '' newTodo.set ''
return unless text return unless text
# Insert the new todo before the first completed item in the list
# or append to the end if none are completed list.push text: text, completed: false, group: group.get('id')
items = list.get() all_completed.set false
i = 0
if items
for todo, i in list.get()
break if todo.completed
list.insert i, {text:text, completed: false, group: model.get '_group.id'}
group.set('select_all', false)
exports.del = (e) -> exports.del = (e) ->
# Derby extends model.at to support creation from DOM nodes # Derby extends model.at to support creation from DOM nodes
model.at(e.target).remove() model.at(e.target).remove()
exports.clearCompleted = -> exports.clearCompleted = ->
completed_indexes = (i for item, i in list.get() when item.completed) completed_indexes = (i for {completed}, i in list.get() when completed)
list.remove(i) for i in completed_indexes.reverse() list.remove(i) for i in completed_indexes.reverse()
group.set('select_all', false) all_completed.set false
exports.checkAllCompleted = -> exports.checkAllCompleted = ->
allCompleted = true for {completed} in list.get() when not completed
allCompleted &&= item.completed for item in list.get() all_completed.set false
group.set('select_all', allCompleted) return
all_completed.set true
exports.endEdit = (e) -> exports.endEdit = (e) ->
target = e.target target = e.target
...@@ -100,11 +101,9 @@ ready (model) -> ...@@ -100,11 +101,9 @@ ready (model) ->
target.firstChild.blur() target.firstChild.blur()
return return
item = model.at(target) item = model.at(target)
item.set('_editing', false) item.set '_editing', false
text = item.get('text').trim() item.remove() if item.get('text').trim() == ''
if not text
item.remove()
exports.startEdit = (e) -> exports.startEdit = (e) ->
item = model.at(e.target) item = model.at(e.target)
item.set('_editing', true) item.set '_editing', true
...@@ -61,12 +61,6 @@ button#clear-completed.non-completed { ...@@ -61,12 +61,6 @@ button#clear-completed.non-completed {
display: none; display: none;
} }
#filters.all li.all a,
#filters.active li.active a,
#filters.completed li.completed a {
font-weight: bold;
}
#todo-list li.editing input.toggle, #todo-list li.editing input.toggle,
#todo-list li.editing button.destroy { #todo-list li.editing button.destroy {
display: none; display: none;
......
...@@ -24,9 +24,9 @@ ...@@ -24,9 +24,9 @@
<app:TodoHeader> <app:TodoHeader>
<section id="main" class="{#unless _todoList}empty-list{/}"> <section id="main" class="{#unless _todoList}empty-list{/}">
<input id="toggle-all" type="checkbox" checked="{_group.select_all}"> <input id="toggle-all" type="checkbox" checked="{_group.all_completed}">
<label for="toggle-all">Mark all as complete</label> <label for="toggle-all">Mark all as complete</label>
<ul id=todo-list class={{filterName}}>{#each _todoList}<app:todo>{/}</ul> <ul id=todo-list class={_filter}>{#each _todoList}<app:todo>{/}</ul>
</section> </section>
<app:TodoFooter> <app:TodoFooter>
...@@ -47,15 +47,15 @@ ...@@ -47,15 +47,15 @@
<TodoFooter:> <TodoFooter:>
<footer id="footer" class="{#unless _todoList}empty-list{/}"> <footer id="footer" class="{#unless _todoList}empty-list{/}">
<span id="todo-count"><strong>{_stats.remaining}</strong> item{#unless _stats.oneOnly}s{/} left</span> <span id="todo-count"><strong>{_stats.remaining}</strong> item{#unless _stats.oneOnly}s{/} left</span>
<ul id="filters" class="{filterName}"> <ul id="filters">
<li class="all"> <li class="all">
<a href="/{{groupName}}">All</a> <a href="/{{_group.id}}" class="{#unless _filter}selected{/}">All</a>
</li> </li>
<li class="active"> <li class="active">
<a href="/{{groupName}}/active">Active</a> <a href="/{{_group.id}}/active" class="{#if equal(_filter, 'active')}selected{/}">Active</a>
</li> </li>
<li class="completed"> <li class="completed">
<a href="/{{groupName}}/completed">Completed</a> <a href="/{{_group.id}}/completed" class="{#if equal(_filter, 'completed')}selected{/}">Completed</a>
</li> </li>
</ul> </ul>
<button x-bind=click:clearCompleted id="clear-completed" class="{#unless _stats.completed}non-completed{/}">Clear completed (<span>{_stats.completed}</span>)</button> <button x-bind=click:clearCompleted id="clear-completed" class="{#unless _stats.completed}non-completed{/}">Clear completed (<span>{_stats.completed}</span>)</button>
...@@ -63,7 +63,8 @@ ...@@ -63,7 +63,8 @@
<Info:> <Info:>
<footer id="info"> <footer id="info">
<h3>Open this <a href="/{{groupName}}">ToDo list</a> in another browser, or share it with a friend to collaborate!</h3> <h3>Open this <a href="/{{_group.id}}">ToDo list</a> in another browser, or share it with a friend to collaborate!</h3>
<p>Click on a todo to edit</p>
<p>Template by <a href="http://github.com/sindresorhus">Sindre Sorhus</a></p> <p>Template by <a href="http://github.com/sindresorhus">Sindre Sorhus</a></p>
<p>Created by <a href="http://micknelson.wordpress.com">Michael Nelson</a> and <a href="https://github.com/lackac">László Bácsi</a></p> <p>Created by <a href="http://micknelson.wordpress.com">Michael Nelson</a> and <a href="https://github.com/lackac">László Bácsi</a></p>
<p>Part of <a href="http://todomvc.com">TodoMVC</a></p> <p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment