models: sturdify namespaces
This commit is contained in:
@@ -0,0 +1,10 @@
|
|||||||
|
## `2.0.0`
|
||||||
|
### breaking changes
|
||||||
|
- namespaces are now enforced more strictly
|
||||||
|
- models now only accept a single argument
|
||||||
|
- the `namespace` key was introduced inside of models (was prior the leading
|
||||||
|
string in models)
|
||||||
|
- namespaced models can now only operate within themselves
|
||||||
|
|
||||||
|
## `1.0.0`
|
||||||
|
- first version of choo
|
||||||
@@ -66,16 +66,18 @@ app.model({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const mainView = (params, state, send) => choo.view`
|
const mainView = (params, state, send) => {
|
||||||
<main class="app">
|
return choo.view`
|
||||||
<h1>${state.title}</h1>
|
<main class="app">
|
||||||
<label>Set the title</label>
|
<h1>${state.input.title}</h1>
|
||||||
<input
|
<label>Set the title</label>
|
||||||
type="text"
|
<input
|
||||||
placeholder=${state.input.title}
|
type="text"
|
||||||
oninput=${(e) => send('input:update', { payload: e.target.value })}>
|
placeholder=${state.input.title}
|
||||||
</main>
|
oninput=${(e) => send('input:update', { payload: e.target.value })}>
|
||||||
`
|
</main>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
app.router((route) => [
|
app.router((route) => [
|
||||||
route('/', mainView)
|
route('/', mainView)
|
||||||
@@ -240,7 +242,6 @@ choo.model({
|
|||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
### Server Sent Events (SSE)
|
### Server Sent Events (SSE)
|
||||||
[Server Sent Events (SSE)][sse] allow servers to push data to the browser.
|
[Server Sent Events (SSE)][sse] allow servers to push data to the browser.
|
||||||
They're the unidirectional cousin of `websockets` and compliment `HTTP`
|
They're the unidirectional cousin of `websockets` and compliment `HTTP`
|
||||||
|
|||||||
+20
-15
@@ -1,26 +1,31 @@
|
|||||||
const choo = require('../')
|
const choo = require('../../')
|
||||||
|
|
||||||
const app = choo()
|
const app = choo()
|
||||||
app.model('title', {
|
app.model({
|
||||||
state: { title: 'my-demo-app' },
|
namespace: 'input',
|
||||||
|
state: {
|
||||||
|
title: 'my demo app'
|
||||||
|
},
|
||||||
reducers: {
|
reducers: {
|
||||||
'update': (action, state) => ({ title: action.payload })
|
update: (action, state) => ({ title: action.payload })
|
||||||
},
|
},
|
||||||
effects: {
|
effects: {
|
||||||
'update': (action, state, send) => (document.title = action.payload)
|
update: (action, state, send) => (document.title = action.payload)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const mainView = (params, state, send) => choo.view`
|
const mainView = (params, state, send) => {
|
||||||
<main class="app">
|
return choo.view`
|
||||||
<h1>${state.title}</h1>
|
<main class="app">
|
||||||
<label>Set the title</label>
|
<h1>${state.input.title}</h1>
|
||||||
<input
|
<label>Set the title</label>
|
||||||
type="text"
|
<input
|
||||||
placeholder=${state.title}
|
type="text"
|
||||||
oninput=${(e) => send('title:update', { payload: e.target.value })}>
|
placeholder=${state.input.title}
|
||||||
</main>
|
oninput=${(e) => send('input:update', { payload: e.target.value })}>
|
||||||
`
|
</main>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
app.router((route) => [
|
app.router((route) => [
|
||||||
route('/', mainView)
|
route('/', mainView)
|
||||||
|
|||||||
@@ -11,9 +11,7 @@ module.exports = choo
|
|||||||
|
|
||||||
// A framework for creating sturdy web applications
|
// A framework for creating sturdy web applications
|
||||||
// null -> fn
|
// null -> fn
|
||||||
function choo (opts) {
|
function choo () {
|
||||||
opts = opts || {}
|
|
||||||
const name = opts.name || 'choo'
|
|
||||||
const _models = []
|
const _models = []
|
||||||
var _router = null
|
var _router = null
|
||||||
|
|
||||||
@@ -40,17 +38,19 @@ function choo (opts) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// start the application
|
// start the application
|
||||||
// null -> DOMNode
|
// obj -> DOMNode
|
||||||
function start () {
|
function start (opts) {
|
||||||
|
opts = opts || {}
|
||||||
|
const name = opts.name || 'choo'
|
||||||
const initialState = {}
|
const initialState = {}
|
||||||
const reducers = {}
|
const reducers = {}
|
||||||
const effects = {}
|
const effects = {}
|
||||||
|
|
||||||
_models.push(appInit(opts))
|
_models.push(appInit(opts))
|
||||||
_models.forEach(function (model) {
|
_models.forEach(function (model) {
|
||||||
if (model.state) apply(model.name, model.state, initialState)
|
if (model.state) apply(model.namespace, model.state, initialState)
|
||||||
if (model.reducers) apply(model.name, model.reducers, reducers)
|
if (model.reducers) apply(model.namespace, model.reducers, reducers)
|
||||||
if (model.effects) apply(model.name, model.effects, effects)
|
if (model.effects) apply(model.namespace, model.effects, effects)
|
||||||
})
|
})
|
||||||
|
|
||||||
const send = sendAction({
|
const send = sendAction({
|
||||||
@@ -68,7 +68,7 @@ function choo (opts) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const rootId = name + '-root'
|
const rootId = name + '-root'
|
||||||
const tree = _router(send.state().location, send.state(), send)
|
const tree = _router(send.state().app.location, send.state(), send)
|
||||||
tree.setAttribute('id', rootId)
|
tree.setAttribute('id', rootId)
|
||||||
return tree
|
return tree
|
||||||
|
|
||||||
@@ -77,14 +77,26 @@ function choo (opts) {
|
|||||||
var _effects = false
|
var _effects = false
|
||||||
var newState = null
|
var newState = null
|
||||||
|
|
||||||
if (reducers[action.type]) {
|
if (/:/.test(action.type)) {
|
||||||
newState = xtend(state, reducers[action.type](action, state))
|
const arr = action.type.split(':')
|
||||||
|
var ns = arr[0]
|
||||||
|
action.type = arr[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
const nsReducers = ns ? reducers[ns] : reducers
|
||||||
|
if (nsReducers[action.type]) {
|
||||||
|
if (ns) {
|
||||||
|
state[ns] = reducers[ns][action.type](action, state[ns])
|
||||||
|
newState = state
|
||||||
|
} else {
|
||||||
|
newState = xtend(state, reducers[action.type](action, state))
|
||||||
|
}
|
||||||
_reducers = true
|
_reducers = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (effects[action.type]) {
|
const nsEffects = ns ? effects[ns] : effects
|
||||||
effects[action.type](action, newState || state, send)
|
if (nsEffects) {
|
||||||
newState = newState || state
|
nsEffects[action.type](action, newState || state)
|
||||||
_effects = true
|
_effects = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,13 +104,13 @@ function choo (opts) {
|
|||||||
throw new Error('Could not find action ' + action.type)
|
throw new Error('Could not find action ' + action.type)
|
||||||
}
|
}
|
||||||
|
|
||||||
return newState
|
return newState || state
|
||||||
}
|
}
|
||||||
|
|
||||||
// update on every change
|
// update on every change
|
||||||
function onchange (action, state) {
|
function onchange (action, state) {
|
||||||
const oldTree = document.querySelector('#' + rootId)
|
const oldTree = document.querySelector('#' + rootId)
|
||||||
const newTree = _router(state.location, state, send)
|
const newTree = _router(state.app.location, state, send)
|
||||||
newTree.setAttribute('id', rootId)
|
newTree.setAttribute('id', rootId)
|
||||||
yo.update(oldTree, newTree)
|
yo.update(oldTree, newTree)
|
||||||
}
|
}
|
||||||
@@ -113,9 +125,7 @@ function choo (opts) {
|
|||||||
|
|
||||||
// create a new model
|
// create a new model
|
||||||
// (str?, obj) -> null
|
// (str?, obj) -> null
|
||||||
function model (name, model) {
|
function model (model) {
|
||||||
if (!model) model = name
|
|
||||||
if (typeof name === 'string') model.name = name
|
|
||||||
_models.push(model)
|
_models.push(model)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -124,6 +134,7 @@ function choo (opts) {
|
|||||||
// obj -> obj
|
// obj -> obj
|
||||||
function appInit (opts) {
|
function appInit (opts) {
|
||||||
const model = {
|
const model = {
|
||||||
|
namespace: 'app',
|
||||||
state: { location: document.location.href },
|
state: { location: document.location.href },
|
||||||
reducers: { location: setLocation },
|
reducers: { location: setLocation },
|
||||||
subscriptions: []
|
subscriptions: []
|
||||||
@@ -158,7 +169,10 @@ function appInit (opts) {
|
|||||||
// (str, obj, obj) -> null
|
// (str, obj, obj) -> null
|
||||||
function apply (name, source, target) {
|
function apply (name, source, target) {
|
||||||
Object.keys(source).forEach(function (key) {
|
Object.keys(source).forEach(function (key) {
|
||||||
if (name) target[name + ':' + key] = source[key]
|
if (name) {
|
||||||
else target[key] = source[key]
|
if (!target[name]) target[name] = {}
|
||||||
|
target[name][key] = source[key]
|
||||||
|
target[name][key].namespace = name
|
||||||
|
} else target[key] = source[key]
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user