Skip to content

Commit

Permalink
jsblocks TodoMVC implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
astoilkov authored and Joscha Rohmann committed Oct 16, 2015
1 parent 9c7b1a2 commit c3a5a41
Show file tree
Hide file tree
Showing 11 changed files with 15,789 additions and 0 deletions.
9 changes: 9 additions & 0 deletions examples/jsblocks/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
node_modules/blocks/*
!node_modules/blocks/blocks.js

node_modules/todomvc-app-css/*
!node_modules/todomvc-app-css/index.css

node_modules/todomvc-common/*
!node_modules/todomvc-common/base.css
!node_modules/todomvc-common/base.js
59 changes: 59 additions & 0 deletions examples/jsblocks/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<!doctype html>
<html lang="en" data-framework="jsblocks">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>jsblocks • TodoMVC</title>
<link rel="stylesheet" href="node_modules/todomvc-common/base.css">
<link rel="stylesheet" href="node_modules/todomvc-app-css/index.css">
</head>
<body>
<section data-query="view(Todos)" class="todoapp">
<header class="header">
<h1>todos</h1>
<input data-query="val(newTodo.title).keydown(addTodo)" class="new-todo" placeholder="What needs to be done?" autofocus>
</header>
<section data-query="visible(todos().length > 0)" style="display: none;" class="main">
<input data-query="checked(todos.remaining() == 0).click(todos.toggleAll)" class="toggle-all" type="checkbox">
<label for="toggle-all">Mark all as complete</label>
<ul data-query="each(todos.view)" class="todo-list">
<li data-query="setClass('completed', completed).setClass('editing', editing).dblclick(edit)">
<div class="view">
<input data-query="checked(completed).click(toggleComplete)" class="toggle" type="checkbox">
<label>{{title}}</label>
<button data-query="click(destroy)" class="destroy"></button>
</div>
<input data-query="val(title).keydown(handleAction).blur(closeEdit).focused(editing)" class="edit" />
</li>
</ul>
</section>
<footer data-query="visible(todos().length > 0)" style="display: none;" class="footer">
<span class="todo-count">
<strong>{{todos.remaining}}</strong> {{todos.remaining() == 1 ? 'item' : 'items'}} left
</span>
<ul class="filters">
<li>
<a data-query="setClass('selected', filter() == 'all')" href="#/">All</a>
</li>
<li>
<a data-query="setClass('selected', filter() == 'active')" href="#/active">Active</a>
</li>
<li>
<a data-query="setClass('selected', filter() == 'completed')" href="#/completed">Completed</a>
</li>
</ul>
<button data-query="visible(todos().length != todos.remaining()).click(todos.clearCompleted)" class="clear-completed">
Clear completed
</button>
</footer>
</section>
<footer class="info">
<p>Double-click to edit a todo</p>
<p>Created by <a href="https://github.com/astoilkov">Antonio Stoilkov</a></p>
<p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
</footer>
<script src="node_modules/todomvc-common/base.js"></script>
<script src="node_modules/blocks/blocks.js"></script>
<script src="js/app.js"></script>
</body>
</html>
152 changes: 152 additions & 0 deletions examples/jsblocks/js/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
/*global blocks */

(function () {
'use strict';

var ENTER_KEY = 13;
var ESCAPE_KEY = 27;

var App = blocks.Application();

var Todo = App.Model({
title: App.Property(),

completed: App.Property(),

editing: blocks.observable(),

init: function () {
var collection = this.collection();

// collection is undefined when a Todo is still not part of the Todos collection
if (collection) {
// save to Local Storage on each attribute change
this.title.on('change', collection.save);
this.completed.on('change', collection.save);
}

this.title.on('change', function (newValue) {
this.title((newValue || '').trim());
});
},

toggleComplete: function () {
this.completed(!this.completed());
},

edit: function () {
this.lastValue = this.title();
this.editing(true);
},

closeEdit: function () {
if (this.title()) {
this.editing(false);
} else {
this.destroy();
}
},

handleAction: function (e) {
if (e.which === ENTER_KEY) {
this.closeEdit();
} else if (e.which === ESCAPE_KEY) {
this.title(this.lastValue);
this.editing(false);
}
}
});

var Todos = App.Collection(Todo, {
remaining: blocks.observable(),

init: function () {
this
// load the data from the Local Storage
.reset(JSON.parse(localStorage.getItem('todos-jsblocks')) || [])
// save to Local Storage on each item add or remove
.on('add remove', this.save)
.updateRemaining();
},

// set all todos as completed
toggleAll: function () {
var complete = this.remaining() === 0 ? false : true;
this.each(function (todo) {
todo.completed(complete);
});
},

// remove all completed todos
clearCompleted: function () {
this.removeAll(function (todo) {
return todo.completed();
});
},

// saves all data back to the Local Storage
save: function () {
var result = [];

blocks.each(this(), function (model) {
result.push(model.dataItem());
});

localStorage.setItem('todos-jsblocks', JSON.stringify(result));

this.updateRemaining();
},

// updates the observable
updateRemaining: function () {
this.remaining(this.reduce(function (memo, todo) {
return todo.completed() ? memo : memo + 1;
}, 0));
}
});

App.View('Todos', {
options: {
// creates a route for the View in order to handle
// /all, /active, /completed filters
route: blocks.route('{{filter}}').optional('filter')
},

filter: blocks.observable(),

newTodo: new Todo(),

// holds all todos for the current view
// todos are filtered if "Active" or "Completed" is clicked
todos: new Todos().extend('filter', function (value) {
var mode = this.filter();
var completed = value.completed();
var include = true;

if (mode === 'active') {
include = !completed;
} else if (mode === 'completed') {
include = completed;
}

return include;
}),

// filter the data when the route have changed
// the callback is fired when "All", "Active" or "Completed" have been clicked
routed: function (params) {
if (params.filter !== 'active' && params.filter !== 'completed') {
params.filter = 'all';
}
this.filter(params.filter);
},

addTodo: function (e) {
if (e.which === ENTER_KEY && this.newTodo.title()) {
this.todos.push(this.newTodo);
// return all Todo values to their defaults
this.newTodo.reset();
}
}
});
})();
Loading

0 comments on commit c3a5a41

Please sign in to comment.