Formotor Todo MVC
Todo application with formotor.js
by Felix Yang
HTML
<!DOCTYPE html>
<html lang="en" data-framework="jquery">
<head>
<meta charset="utf-8" />
<title>Formotor • TodoMVC</title>
</head>
<body>
<section class="todoapp" fm-app>
<header class="header" fm-component="todo-header">
<h1>todos</h1>
<input
class="new-todo"
placeholder="What needs to be done?"
autofocus
/>
</header>
<section class="main" fm-component="todo-content">
<input id="toggle-all" class="toggle-all" type="checkbox" />
<label for="toggle-all">Mark all as complete</label>
<ul class="todo-list"></ul>
</section>
<footer class="footer" fm-component="todo-footer"></footer>
</section>
<footer class="info">
<p>Double-click to edit a todo</p>
<p>Created by <a href="https://felixpy.com">Felix Yang</a></p>
<p>Part of <a href="https://felixpy.github.io/formotor">Formotor</a></p>
</footer>
<script id="todo-template" type="text/x-handlebars-template">
{{#this}}
<li {{#if completed}}class="completed"{{/if}} data-id="{{id}}">
<div class="view">
<input class="toggle" type="checkbox" {{#if completed}}checked{{/if}}>
<label class="title">{{title}}</label>
<button class="destroy"></button>
</div>
<input class="edit" value="{{title}}">
</li>
{{/this}}
</script>
<script id="footer-template" type="text/x-handlebars-template">
<span class="todo-count"><strong>{{activeTodoCount}}</strong> {{activeTodoWord}} left</span>
<ul class="filters">
<li>
<a {{#eq filter 'all'}}class="selected"{{/eq}} href="#/all">All</a>
</li>
<li>
<a {{#eq filter 'active'}}class="selected"{{/eq}}href="#/active">Active</a>
</li>
<li>
<a {{#eq filter 'completed'}}class="selected"{{/eq}}href="#/completed">Completed</a>
</li>
</ul>
{{#if completedTodos}}<button...
CSS
hr {
margin: 20px 0;
border: 0;
border-top: 1px dashed #c5c5c5;
border-bottom: 1px dashed #f7f7f7;
}
.learn a {
font-weight: normal;
text-decoration: none;
color: #b83f45;
}
.learn a:hover {
text-decoration: underline;
color: #787e7e;
}
.learn h3,
.learn h4,
.learn h5 {
margin: 10px 0;
font-weight: 500;
line-height: 1.2;
color: #000;
}
.learn h3 {
font-size: 24px;
}
.learn h4 {
font-size: 18px;
}
.learn h5 {
margin-bottom: 0;
font-size: 14px;
}
.learn ul {
padding: 0;
margin: 0 0 30px 25px;
}
.learn li {
line-height: 20px;
}
.learn p {
font-size: 15px;
font-weight: 300;
line-height: 1.3;
margin-top: 0;
margin-bottom: 0;
}
#issue-count {
display: none;
}
.quote {
border: none;
margin: 20px 0 60px 0;
}
.quote p {
font-style: italic;
}
.quote p:before {
content: '“';
font-size: 50px;
opacity: 0.15;
position: absolute;
top: -20px;
left: 3px;
}
.quote p:after {
content: '”';
font-size: 50px;
opacity: 0.15;
position: absolute;
bottom: -42px;
right: 3px;
}
.quote footer {
position: absolute;
bottom: -40px;
right: 0;
}
.quote footer img {
border-radius: 3px;
}
.quote footer a {
margin-left: 5px;
vertical-align: middle;
}
.speech-bubble {
position: relative;
padding: 10px;
background: rgba(0, 0, 0, 0.04);
border-radius: 5px;
}
.speech-bubble:after {
content: '';
position: absolute;
top: 100%;
right: 30px;
border: 13px solid transparent;
border-top-color: rgba(0, 0, 0, 0.04);
}
.learn-bar > .learn {
position: absolute;
width: 272px;
top: 8px;
left: -300px;
padding: 10px;
border-radius: 5px;
background-color: rgba(255, 255, 255, 0.6);
transition-property: left;
transition-duration: 500ms;
}
@media (min-width: 899px) {
.learn-bar {
width: auto;
padding-left: 300px;
}
.learn-bar > .learn {
left: 8px;
}
}
html,
body {
margin: 0;
padding: 0;
}
button {
margin: 0;
padding: 0;
border: 0;
...
JavaScript
/**
* @file Todo application with formotor.js.
* @author Felix Yang
*/
/* eslint-disable-next-line */
jQuery(function($) {
'use strict'
var Global = window || {}
var Router = Global.Router || {}
var Handlebars = Global.Handlebars || {}
var Formotor = Global.Formotor || {}
var KEYBOARD = {
ENTER_KEY: 13,
ESCAPE_KEY: 27
}
/**
* Create application instance with tiny MVC.
* @param {object} options
* @returns {object}
*/
var createMvcApp = (function () {
var App = {
version: '1.0.0'
}
var util = {
pluralize: function (count, word) {
return count === 1 ? word : word + 's'
},
compile: function (selector) {
return Handlebars.compile($(selector).html())
}
}
/**
* Formotor component Config
* @property {object} todo-header - todo input box.
* @property {object} todo-content - todo list.
* @property {object} todo-footer - todo status bar.
*/
App.components = {
'todo-header': {
ready: function () {
this.build()
},
methods: {
build: function () {
this.bindEvents()
},
bindEvents: function () {
this.$el.on('keyup', '.new-todo', this.create)
this.$listen('tree:render', this.render)
},
render: function () {
this.$find('.new-todo').focus()
},
create: function (e) {
var self = this
var $element = $(e.target)
var todoTask = $element.val().trim()
if (e.which !== KEYBOARD.ENTER_KEY || !todoTask) {
return
}
self.model.addTodo({
title: todoTask,
completed: false
})
$element.val('')
self.$broadcast('tree:render')
}
}
},
'todo-content': {
ready: function () {
this.build()
...