Commit d23c0c33 authored by nateps's avatar nateps Committed by Sindre Sorhus

DerbyJS: Rework to use model filters instead of CSS

parent 7188e3bf
......@@ -4,7 +4,7 @@
"version": "0.0.0",
"main": "./server.js",
"dependencies": {
"derby": "0.3.12",
"derby": "0.3.13",
"express": "3.0.0beta4",
"gzippo": ">=0.1.4"
},
......
......@@ -3,7 +3,9 @@ derby = require 'derby'
derby.use(require '../../ui')
## ROUTES ##
view.fn 'noItems',
get: (list) -> !list.length
set: ->
# Redirect the visitor to a random todo list
get '/', (page) ->
......@@ -11,99 +13,69 @@ get '/', (page) ->
# 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)
model.subscribe "groups.#{groupName}", groupTodosQuery, (err, group, groupTodos) ->
model.ref '_group', group
group.setNull 'id', groupName
todoIds = group.at 'todoIds' or []
# The refList supports array methods, but it stores the todo values
# on an object by id. The todos are stored on the object 'todos',
# and their order is stored in an array of ids at '_group.todoIds'
model.refList '_todoList', 'todos', todoIds
# Create a reactive function that automatically keeps '_stats'
# updated with the number of remaining and completed todos.
model.fn '_stats', '_todoList', (list) ->
remaining = 0
completed = 0
if list
for todo in list
if todo?.completed
completed++
else
remaining++
return {
completed: completed,
remaining: remaining,
oneOnly: remaining == 1,
}
# Do not filter the list by default
model.del '_filter'
page.render 'todo'
model.query('todos').forGroup(groupName).subscribe ->
model.set '_groupName', groupName
# Transitional route for enabling a filter
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'
model.ref '_list.all', model.filter('todos')
.where('group').equals(groupName)
ready (model) ->
model.ref '_list.completed', model.filter('todos')
.where('group').equals(groupName)
.where('completed').equals(true)
model.ref '_list.active', model.filter('todos')
.where('group').equals(groupName)
.where('completed').notEquals(true)
list = model.at '_todoList'
group = model.at '_group'
all_completed = group.at 'all_completed'
model.set '_filter', 'all'
model.ref '_list.shown', '_list', '_filter'
group.on 'set', 'all_completed', (all_completed, previous_value, isLocal, e) ->
# 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
# individual items).
return unless e
page.render()
# Is there a way to do this with one call rather than iterating?
for {id} in list.get()
model.set "todos.#{id}.completed", all_completed
# Transitional route for enabling a filter
get from: '/:groupName', to: '/:groupName/:filterName',
forward: (model, {filterName}) ->
model.set '_filter', filterName
back: (model, params) ->
model.set '_filter', 'all'
get from: '/:groupName/:filterName', to: '/:groupName/:filterName',
forward: (model, {filterName}) ->
model.set '_filter', filterName
ready (model) ->
todos = model.at 'todos'
newTodo = model.at '_newTodo'
exports.add = ->
# Don't add a blank todo
text = newTodo.get().trim()
newTodo.set ''
return unless text
todos.add text: text, group: model.get('_groupName')
list.push text: text, completed: false, group: group.get('id')
all_completed.set false
exports.del = (e) ->
exports.del = (e, el) ->
# Derby extends model.at to support creation from DOM nodes
model.at(e.target).remove()
todos.del model.at(el).get('id')
exports.clearCompleted = ->
completed_indexes = (i for {completed}, i in list.get() when completed)
list.remove(i) for i in completed_indexes.reverse()
all_completed.set false
exports.checkAllCompleted = ->
for {completed} in list.get() when not completed
all_completed.set false
return
all_completed.set true
exports.endEdit = (e) ->
target = e.target
if target.nodeName == "FORM"
target.firstChild.blur()
return
item = model.at(target)
item.set '_editing', false
item.remove() if item.get('text').trim() == ''
for {id} in model.get('_list.completed')
todos.del id
exports.startEdit = (e) ->
item = model.at(e.target)
exports.clickToggleAll = ->
value = !!model.get('_list.active.length')
for {id} in model.get('_list.all')
todos.set id + '.completed', value
exports.submitEdit = (e, el) ->
el.firstChild.blur()
exports.startEdit = (e, el) ->
item = model.at(el)
item.set '_editing', true
exports.endEdit = (e, el) ->
item = model.at(el)
item.set '_editing', false
if item.get('text').trim() == ''
todos.del item.get('id')
......@@ -46,14 +46,6 @@ section.empty-list, footer.empty-list {
display: none;
}
ul#todo-list.active li.completed {
display: none;
}
ul#todo-list.completed li.active {
display: none;
}
section.empty-list #toggle-all {
display: none;
}
......
<head:>
<Head:>
<link href=/base.css rel=stylesheet>
<!-- The following ie inclusion is useless as it'll be stripped
out before getting to the client, but serves as a reminder
that it needs to be sorted out for ie compatability :) -->
<!--[if IE]>
<script src="/ie.js"></script>
<![endif]-->
<Title:>
DerbyJS • TodoMVC
......@@ -13,57 +7,59 @@
<Header:>
<ui:connectionAlert>
<TodoHeader:>
<header id="header">
<h1>todos</h1>
<form x-bind=submit:add><input id="new-todo" placeholder="What needs to be done?" autofocus value={_newTodo}></form>
</header>
<Body:>
<section id="todoapp">
<app:TodoHeader>
<app:todoHeader>
<section id="main" class="{#unless _todoList}empty-list{/}">
<input id="toggle-all" type="checkbox" checked="{_group.all_completed}">
<section id="main" class="{#unless _list.shown}empty-list{/}">
<input id="toggle-all" type="checkbox" checked="{noItems(_list.active)}" x-bind=click:clickToggleAll>
<label for="toggle-all">Mark all as complete</label>
<ul id=todo-list class={_filter}>{#each _todoList}<app:todo>{/}</ul>
<ul id=todo-list>{#each _list.shown}<app:todo>{/}</ul>
</section>
<app:TodoFooter>
<app:todoFooter>
</section>
<app:Info>
<app:info>
<todoHeader:>
<header id="header">
<h1>todos</h1>
<form x-bind=submit:add><input id="new-todo" placeholder="What needs to be done?" autofocus value={_newTodo}></form>
</header>
<todo:>
<li data-id={{id}} class="{#if .completed}completed{else}active{/}{#if ._editing} editing{/}">
<div>
<input class="toggle" type="checkbox" checked={.completed} x-bind=change:checkAllCompleted>
<form x-bind=submit:endEdit>
<input class="toggle" type="checkbox" checked={.completed}>
<form x-bind=submit:submitEdit>
<input class="text" x-bind="focus:startEdit, blur:endEdit" value="{.text}">
</form>
<button class="destroy" x-bind=click:del></button>
</div>
</li>
<TodoFooter:>
<footer id="footer" class="{#unless _todoList}empty-list{/}">
<span id="todo-count"><strong>{_stats.remaining}</strong> item{#unless _stats.oneOnly}s{/} left</span>
<todoFooter:>
<footer id="footer" class="{#unless _list.all}empty-list{/}">
<span id="todo-count"><strong>{_list.active.length}</strong> item{#unless equal(_list.active.length, 1)}s{/} left</span>
<ul id="filters">
<li class="all">
<a href="/{{_group.id}}" class="{#unless _filter}selected{/}">All</a>
<a href="/{{_groupName}}" class="{#if equal(_filter, 'all')}selected{/}">All</a>
</li>
<li class="active">
<a href="/{{_group.id}}/active" class="{#if equal(_filter, 'active')}selected{/}">Active</a>
<a href="/{{_groupName}}/active" class="{#if equal(_filter, 'active')}selected{/}">Active</a>
</li>
<li class="completed">
<a href="/{{_group.id}}/completed" class="{#if equal(_filter, 'completed')}selected{/}">Completed</a>
<a href="/{{_groupName}}/completed" class="{#if equal(_filter, 'completed')}selected{/}">Completed</a>
</li>
</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 _list.completed}non-completed{/}">
Clear completed (<span>{_list.completed.length}</span>)
</button>
</footer>
<Info:>
<info:>
<footer id="info">
<h3>Open this <a href="/{{_group.id}}">ToDo list</a> in another browser, or share it with a friend to collaborate!</h3>
<h3>Open this <a href="/{{_groupName}}">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>Created by <a href="http://micknelson.wordpress.com">Michael Nelson</a> and <a href="https://github.com/lackac">László Bácsi</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