-
Notifications
You must be signed in to change notification settings - Fork 595
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
choo components #639
choo components #639
Changes from 21 commits
e410c0c
70e9f5e
ae880ec
6c3105d
3318a94
ec622e3
5066115
246befe
f96becd
06fa2dc
2645a5f
4765b90
181b3bb
2f86431
b7395db
3651662
1c8ccc8
c1f621d
b9b2a83
050649a
39fc241
8662b8c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
var assert = require('assert') | ||
var LRU = require('nanolru') | ||
|
||
module.exports = ChooComponentCache | ||
|
||
function ChooComponentCache (state, emit, lru) { | ||
assert.ok(this instanceof ChooComponentCache, 'ChooComponentCache should be created with `new`') | ||
|
||
assert.equal(typeof state, 'object', 'ChooComponentCache: state should be type object') | ||
assert.equal(typeof emit, 'function', 'ChooComponentCache: state should be type function') | ||
|
||
if (typeof lru === 'number') this.cache = new LRU(lru) | ||
else this.cache = lru || new LRU(100) | ||
this.state = state | ||
this.emit = emit | ||
} | ||
|
||
// Get & create component instances. | ||
ChooComponentCache.prototype.render = function (Component, id) { | ||
assert.equal(typeof Component, 'function', 'ChooComponentCache.render: Component should be type function') | ||
assert.ok(typeof id === 'string' || typeof id === 'number', 'ChooComponentCache.render: id should be type string or type number') | ||
|
||
var el = this.cache.get(id) | ||
if (!el) { | ||
var args = [] | ||
for (var i = 2, len = arguments.length; i < len; i++) { | ||
args.push(arguments[i]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should probably drop class Foo extends Component {
constructor (id, state, emit, the_class, id, actual_constructor_argument) { … }
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice, done! |
||
} | ||
args.unshift(Component, id, this.state, this.emit) | ||
el = newCall.apply(newCall, args) | ||
this.cache.set(id, el) | ||
} | ||
|
||
return el | ||
} | ||
|
||
// Because you can't call `new` and `.apply()` at the same time. This is a mad | ||
// hack, but hey it works so we gonna go for it. Whoop. | ||
function newCall (Cls) { | ||
return new (Cls.bind.apply(Cls, arguments)) // eslint-disable-line | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
module.exports = require('nanocomponent') |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
var html = require('bel') | ||
|
||
module.exports = deleteCompleted | ||
|
||
function deleteCompleted (emit) { | ||
return html` | ||
<button class="clear-completed" onclick=${deleteAllCompleted}> | ||
Clear completed | ||
</button> | ||
` | ||
|
||
function deleteAllCompleted () { | ||
emit('todos:deleteCompleted') | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
var html = require('bel') | ||
|
||
module.exports = filterButton | ||
|
||
function filterButton (name, filter, currentFilter, emit) { | ||
var filterClass = filter === currentFilter | ||
? 'selected' | ||
: '' | ||
|
||
var uri = '#' + name.toLowerCase() | ||
if (uri === '#all') uri = '/' | ||
|
||
return html`<li> | ||
<a href=${uri} class=${filterClass}> | ||
${name} | ||
</a> | ||
</li>` | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
var Component = require('../../../component') | ||
var html = require('bel') | ||
|
||
var clearButton = require('./clear-button') | ||
var filterButton = require('./filter-button') | ||
|
||
module.exports = class Footer extends Component { | ||
constructor (name, state, emit) { | ||
super(name) | ||
this.state = state | ||
this.emit = emit | ||
|
||
this.local = this.state.components.footer = {} | ||
this.setState() | ||
} | ||
|
||
setState () { | ||
this.local.rawTodos = this.state.todos.clock | ||
this.local.rawHref = this.state.href | ||
|
||
this.local.filter = this.state.href.replace(/^\//, '') || '' | ||
this.local.activeCount = this.state.todos.active.length | ||
this.local.hasDone = this.state.todos.done.length || null | ||
} | ||
|
||
update () { | ||
if (this.local.rawTodos !== this.state.todos.clock || | ||
this.local.rawHref !== this.state.href) { | ||
this.setState() | ||
return true | ||
} else { | ||
return false | ||
} | ||
} | ||
|
||
createElement () { | ||
return html`<footer class="footer"> | ||
<span class="todo-count"> | ||
<strong>${this.local.activeCount}</strong> | ||
item${this.state.todos.all === 1 ? '' : 's'} left | ||
</span> | ||
<ul class="filters"> | ||
${filterButton('All', '', this.local.filter, this.emit)} | ||
${filterButton('Active', 'active', this.local.filter, this.emit)} | ||
${filterButton('Completed', 'completed', this.local.filter, this.emit)} | ||
</ul> | ||
${this.local.hasDone && clearButton(this.emit)} | ||
</footer>` | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
var Component = require('../../component') | ||
var html = require('bel') | ||
|
||
module.exports = class Header extends Component { | ||
constructor (name, state, emit) { | ||
super(name) | ||
this.state = state | ||
this.emit = emit | ||
} | ||
|
||
update () { | ||
return false | ||
} | ||
|
||
createElement () { | ||
return html`<header class="header"> | ||
<h1>todos</h1> | ||
<input class="new-todo" | ||
autofocus | ||
placeholder="What needs to be done?" | ||
onkeydown=${this.createTodo.bind(this)} /> | ||
</header>` | ||
} | ||
|
||
createTodo (e) { | ||
var value = e.target.value | ||
if (e.keyCode === 13) { | ||
e.target.value = '' | ||
this.emit('todos:create', value) | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
var Component = require('../../component') | ||
var html = require('bel') | ||
|
||
module.exports = class Info extends Component { | ||
update () { | ||
return false | ||
} | ||
|
||
createElement () { | ||
return html`<footer class="info"> | ||
<p>Double-click to edit a todo</p> | ||
<p>choo by <a href="https://yoshuawuyts.com/">Yoshua Wuyts</a></p> | ||
<p>Created by <a href="http://shuheikagawa.com">Shuhei Kagawa</a></p> | ||
</footer>` | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
var Component = require('../../../component') | ||
var html = require('bel') | ||
|
||
var Todo = require('./todo') | ||
|
||
module.exports = class Header extends Component { | ||
constructor (name, state, emit) { | ||
super(name) | ||
this.state = state | ||
this.emit = emit | ||
this.local = this.state.components[name] = {} | ||
this.setState() | ||
} | ||
|
||
setState () { | ||
this.local.rawTodos = this.state.todos.clock | ||
this.local.rawHref = this.state.href | ||
|
||
this.local.allDone = this.state.todos.done.length === this.state.todos.all.length | ||
this.local.filter = this.state.href.replace(/^\//, '') || '' | ||
this.local.todos = this.local.filter === 'completed' | ||
? this.state.todos.done | ||
: this.local.filter === 'active' | ||
? this.state.todos.active | ||
: this.state.todos.all | ||
} | ||
|
||
update () { | ||
if (this.local.rawTodos !== this.state.todos.clock || | ||
this.local.rawHref !== this.state.href) { | ||
this.setState() | ||
return true | ||
} else { | ||
return false | ||
} | ||
} | ||
|
||
createElement () { | ||
return html`<section class="main"> | ||
<input | ||
class="toggle-all" | ||
type="checkbox" | ||
checked=${this.local.allDone} | ||
onchange=${() => this.toggleAll()}/> | ||
<label for="toggle-all" style="display: none;"> | ||
Mark all as done | ||
</label> | ||
<ul class="todo-list"> | ||
${this.local.todos.map(todo => Todo(todo, this.emit))} | ||
</ul> | ||
</section>` | ||
} | ||
|
||
createTodo (e) { | ||
var value = e.target.value | ||
if (e.keyCode === 13) { | ||
e.target.value = '' | ||
this.emit('todos:create', value) | ||
} | ||
} | ||
|
||
toggleAll () { | ||
this.emit('todos:toggleAll') | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
var html = require('bel') | ||
|
||
module.exports = Todo | ||
|
||
function Todo (todo, emit) { | ||
var clx = classList({ completed: todo.done, editing: todo.editing }) | ||
return html` | ||
<li id=${todo.id} class=${clx}> | ||
<div class="view"> | ||
<input | ||
type="checkbox" | ||
class="toggle" | ||
checked="${todo.done}" | ||
onchange=${toggle} /> | ||
<label ondblclick=${edit}>${todo.name}</label> | ||
<button | ||
class="destroy" | ||
onclick=${destroy} | ||
></button> | ||
</div> | ||
<input | ||
class="edit" | ||
value=${todo.name} | ||
onkeydown=${handleEditKeydown} | ||
onblur=${update} /> | ||
</li> | ||
` | ||
|
||
function toggle (e) { | ||
emit('todos:toggle', todo.id) | ||
} | ||
|
||
function edit (e) { | ||
emit('todos:edit', todo.id) | ||
} | ||
|
||
function destroy (e) { | ||
emit('todos:delete', todo.id) | ||
} | ||
|
||
function update (e) { | ||
emit('todos:update', { | ||
id: todo.id, | ||
editing: false, | ||
name: e.target.value | ||
}) | ||
} | ||
|
||
function handleEditKeydown (e) { | ||
if (e.keyCode === 13) update(e) // Enter | ||
else if (e.code === 27) emit('todos:unedit') // Escape | ||
} | ||
|
||
function classList (classes) { | ||
var str = '' | ||
var keys = Object.keys(classes) | ||
for (var i = 0, len = keys.length; i < len; i++) { | ||
var key = keys[i] | ||
var val = classes[key] | ||
if (val) str += (key + ' ') | ||
} | ||
return str | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
emit should be type function 🎹
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks!