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 @@ ...@@ -4,7 +4,7 @@
"version": "0.0.0", "version": "0.0.0",
"main": "./server.js", "main": "./server.js",
"dependencies": { "dependencies": {
"derby": "0.3.12", "derby": "0.3.13",
"express": "3.0.0beta4", "express": "3.0.0beta4",
"gzippo": ">=0.1.4" "gzippo": ">=0.1.4"
}, },
......
...@@ -3,7 +3,9 @@ derby = require 'derby' ...@@ -3,7 +3,9 @@ derby = require 'derby'
derby.use(require '../../ui') derby.use(require '../../ui')
## ROUTES ## view.fn 'noItems',
get: (list) -> !list.length
set: ->
# Redirect the visitor to a random todo list # Redirect the visitor to a random todo list
get '/', (page) -> get '/', (page) ->
...@@ -11,99 +13,69 @@ get '/', (page) -> ...@@ -11,99 +13,69 @@ get '/', (page) ->
# Sets up the model, the reactive function for stats and renders the todo list # Sets up the model, the reactive function for stats and renders the todo list
get '/:groupName', (page, model, {groupName}) -> get '/:groupName', (page, model, {groupName}) ->
groupTodosQuery = model.query('todos').forGroup(groupName) model.query('todos').forGroup(groupName).subscribe ->
model.subscribe "groups.#{groupName}", groupTodosQuery, (err, group, groupTodos) -> model.set '_groupName', groupName
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'
# Transitional route for enabling a filter model.ref '_list.all', model.filter('todos')
get {from: '/:groupName', to: '/:groupName/:filterName'}, .where('group').equals(groupName)
forward: (model, {filterName}, next) ->
# enable the filter
model.set '_filter', filterName
back: (model, params, next) ->
# disable the filter
model.del '_filter'
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' model.set '_filter', 'all'
group = model.at '_group' model.ref '_list.shown', '_list', '_filter'
all_completed = group.at 'all_completed'
group.on 'set', 'all_completed', (all_completed, previous_value, isLocal, e) -> page.render()
# 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
# Is there a way to do this with one call rather than iterating? # Transitional route for enabling a filter
for {id} in list.get() get from: '/:groupName', to: '/:groupName/:filterName',
model.set "todos.#{id}.completed", all_completed 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' newTodo = model.at '_newTodo'
exports.add = -> exports.add = ->
# Don't add a blank todo # Don't add a blank todo
text = newTodo.get().trim() text = newTodo.get().trim()
newTodo.set '' newTodo.set ''
return unless text return unless text
todos.add text: text, group: model.get('_groupName')
list.push text: text, completed: false, group: group.get('id') exports.del = (e, el) ->
all_completed.set false
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() todos.del model.at(el).get('id')
exports.clearCompleted = -> exports.clearCompleted = ->
completed_indexes = (i for {completed}, i in list.get() when completed) for {id} in model.get('_list.completed')
list.remove(i) for i in completed_indexes.reverse() todos.del id
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() == ''
exports.startEdit = (e) -> exports.clickToggleAll = ->
item = model.at(e.target) 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 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 { ...@@ -46,14 +46,6 @@ section.empty-list, footer.empty-list {
display: none; display: none;
} }
ul#todo-list.active li.completed {
display: none;
}
ul#todo-list.completed li.active {
display: none;
}
section.empty-list #toggle-all { section.empty-list #toggle-all {
display: none; display: none;
} }
......
<head:> <Head:>
<link href=/base.css rel=stylesheet> <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:> <Title:>
DerbyJS • TodoMVC DerbyJS • TodoMVC
...@@ -13,57 +7,59 @@ ...@@ -13,57 +7,59 @@
<Header:> <Header:>
<ui:connectionAlert> <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:> <Body:>
<section id="todoapp"> <section id="todoapp">
<app:TodoHeader> <app:todoHeader>
<section id="main" class="{#unless _todoList}empty-list{/}"> <section id="main" class="{#unless _list.shown}empty-list{/}">
<input id="toggle-all" type="checkbox" checked="{_group.all_completed}"> <input id="toggle-all" type="checkbox" checked="{noItems(_list.active)}" x-bind=click:clickToggleAll>
<label for="toggle-all">Mark all as complete</label> <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> </section>
<app:TodoFooter> <app:todoFooter>
</section> </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:> <todo:>
<li data-id={{id}} class="{#if .completed}completed{else}active{/}{#if ._editing} editing{/}"> <li data-id={{id}} class="{#if .completed}completed{else}active{/}{#if ._editing} editing{/}">
<div> <div>
<input class="toggle" type="checkbox" checked={.completed} x-bind=change:checkAllCompleted> <input class="toggle" type="checkbox" checked={.completed}>
<form x-bind=submit:endEdit> <form x-bind=submit:submitEdit>
<input class="text" x-bind="focus:startEdit, blur:endEdit" value="{.text}"> <input class="text" x-bind="focus:startEdit, blur:endEdit" value="{.text}">
</form> </form>
<button class="destroy" x-bind=click:del></button> <button class="destroy" x-bind=click:del></button>
</div> </div>
</li> </li>
<TodoFooter:> <todoFooter:>
<footer id="footer" class="{#unless _todoList}empty-list{/}"> <footer id="footer" class="{#unless _list.all}empty-list{/}">
<span id="todo-count"><strong>{_stats.remaining}</strong> item{#unless _stats.oneOnly}s{/} left</span> <span id="todo-count"><strong>{_list.active.length}</strong> item{#unless equal(_list.active.length, 1)}s{/} left</span>
<ul id="filters"> <ul id="filters">
<li class="all"> <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>
<li class="active"> <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>
<li class="completed"> <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> </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 _list.completed}non-completed{/}">
Clear completed (<span>{_list.completed.length}</span>)
</button>
</footer> </footer>
<Info:> <info:>
<footer id="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>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>
......
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