JSFiddle - React, Tailwind, and code Playground
by rich_harris
HTML
<div id="todoapp" style="width:500px;margin:10px"></div>
<!-- Templates -->
<script id="main" type="text/ractive">
<header id="header">
<h1>todos</h1>
<!-- In app.js, we define a custom `enter` event, which fires when the user hits the enter key, submitting a new todo. -->
<input id="new-todo" on-enter="newTodo" placeholder="What needs to be done?" autofocus>
</header>
<!--
We only render the main section and the footer if we have one or more todos - in other words, `items.length` is not falsy.
-->
{{#items.length}}
<section id="main">
<!-- Here, we compare the total number of tasks (`items.length`) with the number of completed tasks (`completedTasks().length`). This calculation happens reactively, so we never need to manually trigger an update. When the `change` event fires on the input, the `toggleAll` event fires on the Ractive instance. -->
<input twoway='false' id="toggle-all" type="checkbox" on-change="toggleAll" checked='{{ items.length === completedTasks().length }}'>
<label for="toggle-all">Mark all as complete</label>
<ul id="todo-list">
{{#items:i}}
<!-- The {{>item}} partial is defined in the script tag below -->
{{>item}}
{{/items}}
</ul>
</section>
<footer id="footer">
<span id="todo-count">
<!-- The number of active tasks updates reactively -->
<strong>{{ activeTasks().length }}</strong> {{ activeTasks().length === 1 ? 'item' : 'items' }} left
</span>
<ul id="filters">
<li>
<a class="{{ currentFilter === 'all' ? 'selected' : '' }}" href="#/">All</a>
</li>
<li>
<a class="{{ currentFilter === 'active' ? 'selected' : '' }}" href="#/active">Active</a>
</li>
<li>
<a class="{{ currentFilter === 'completed' ? 'selected' : '' }}" href="#/completed">Completed</a>
</li>
</ul>
<!-- This section is only rendered if there are one or more completed tasks -->
{{#...
JavaScript
(function (window, Ractive) {
'use strict';
// Create some filter functions, which we'll need later
var filters = {
completed: function (item) { return item.completed; },
active: function (item) { return !item.completed; }
};
// The keycode for the 'enter' and 'escape' keys
var ENTER_KEY = 13;
var ESCAPE_KEY = 27;
// Create our Ractive instance
var todoList = new Ractive({
// Specify a target element - an ID, a CSS selector, or the element itself
el: 'todoapp',
// Specify a template, or the ID of a script tag containing the template
template: '#main',
// This is our viewmodel - as well as our list of tasks (which gets added
// later from localStorage - see persistence.js), it includes helper
// functions and computed values
data: {
filter: function (item) {
// Because we're doing `this.get('currentFilter')`, Ractive understands
// that this function needs to be re-executed reactively when the value of
// `currentFilter` changes
var currentFilter = this.get('currentFilter');
if (currentFilter === 'all') {
return true;
}
return filters[currentFilter](item);
},
// completedTasks() and activeTasks() are computed values, that will update
// our app view reactively whenever `items` changes (including changes to
// child properties like `items[1].completed`)
completedTasks: function () {
return this.get('items').filter(filters.completed);
},
activeTasks: function () {
return this.get('items').filter(filters.active);
},
// By default, show all tasks. This value changes when the route changes
// (see routes.js)
currentFilter: 'all'
},
// We can define custom events. Here, we're defining an `enter` event,
// and an `escape` event, which fire when the user hits those keys while
// an input is focused:
//
// <input id="edit" class="edit" on-blur-enter="submit" on-escape="cancel" autofocus>
events: (function () {
var makeCustomEvent = function...