diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..fe99c89 --- /dev/null +++ b/CHANGELOG.md @@ -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 diff --git a/README.md b/README.md index 8c3341a..fa0a6e9 100644 --- a/README.md +++ b/README.md @@ -66,16 +66,18 @@ app.model({ } }) -const mainView = (params, state, send) => choo.view` -
-

${state.title}

- - send('input:update', { payload: e.target.value })}> -
-` +const mainView = (params, state, send) => { + return choo.view` +
+

${state.input.title}

+ + send('input:update', { payload: e.target.value })}> +
+ ` +} app.router((route) => [ route('/', mainView) @@ -240,7 +242,6 @@ choo.model({ }) ``` - ### Server Sent Events (SSE) [Server Sent Events (SSE)][sse] allow servers to push data to the browser. They're the unidirectional cousin of `websockets` and compliment `HTTP` diff --git a/examples/title/client.js b/examples/title/client.js index d1d2274..7b6a348 100644 --- a/examples/title/client.js +++ b/examples/title/client.js @@ -1,26 +1,31 @@ -const choo = require('../') +const choo = require('../../') const app = choo() -app.model('title', { - state: { title: 'my-demo-app' }, +app.model({ + namespace: 'input', + state: { + title: 'my demo app' + }, reducers: { - 'update': (action, state) => ({ title: action.payload }) + update: (action, state) => ({ title: action.payload }) }, effects: { - 'update': (action, state, send) => (document.title = action.payload) + update: (action, state, send) => (document.title = action.payload) } }) -const mainView = (params, state, send) => choo.view` -
-

${state.title}

- - send('title:update', { payload: e.target.value })}> -
-` +const mainView = (params, state, send) => { + return choo.view` +
+

${state.input.title}

+ + send('input:update', { payload: e.target.value })}> +
+ ` +} app.router((route) => [ route('/', mainView) diff --git a/index.js b/index.js index 0cb0e23..5edc432 100644 --- a/index.js +++ b/index.js @@ -11,9 +11,7 @@ module.exports = choo // A framework for creating sturdy web applications // null -> fn -function choo (opts) { - opts = opts || {} - const name = opts.name || 'choo' +function choo () { const _models = [] var _router = null @@ -40,17 +38,19 @@ function choo (opts) { } // start the application - // null -> DOMNode - function start () { + // obj -> DOMNode + function start (opts) { + opts = opts || {} + const name = opts.name || 'choo' const initialState = {} const reducers = {} const effects = {} _models.push(appInit(opts)) _models.forEach(function (model) { - if (model.state) apply(model.name, model.state, initialState) - if (model.reducers) apply(model.name, model.reducers, reducers) - if (model.effects) apply(model.name, model.effects, effects) + if (model.state) apply(model.namespace, model.state, initialState) + if (model.reducers) apply(model.namespace, model.reducers, reducers) + if (model.effects) apply(model.namespace, model.effects, effects) }) const send = sendAction({ @@ -68,7 +68,7 @@ function choo (opts) { }) 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) return tree @@ -77,14 +77,26 @@ function choo (opts) { var _effects = false var newState = null - if (reducers[action.type]) { - newState = xtend(state, reducers[action.type](action, state)) + if (/:/.test(action.type)) { + 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 } - if (effects[action.type]) { - effects[action.type](action, newState || state, send) - newState = newState || state + const nsEffects = ns ? effects[ns] : effects + if (nsEffects) { + nsEffects[action.type](action, newState || state) _effects = true } @@ -92,13 +104,13 @@ function choo (opts) { throw new Error('Could not find action ' + action.type) } - return newState + return newState || state } // update on every change function onchange (action, state) { const oldTree = document.querySelector('#' + rootId) - const newTree = _router(state.location, state, send) + const newTree = _router(state.app.location, state, send) newTree.setAttribute('id', rootId) yo.update(oldTree, newTree) } @@ -113,9 +125,7 @@ function choo (opts) { // create a new model // (str?, obj) -> null - function model (name, model) { - if (!model) model = name - if (typeof name === 'string') model.name = name + function model (model) { _models.push(model) } } @@ -124,6 +134,7 @@ function choo (opts) { // obj -> obj function appInit (opts) { const model = { + namespace: 'app', state: { location: document.location.href }, reducers: { location: setLocation }, subscriptions: [] @@ -158,7 +169,10 @@ function appInit (opts) { // (str, obj, obj) -> null function apply (name, source, target) { Object.keys(source).forEach(function (key) { - if (name) target[name + ':' + key] = source[key] - else target[key] = source[key] + if (name) { + if (!target[name]) target[name] = {} + target[name][key] = source[key] + target[name][key].namespace = name + } else target[key] = source[key] }) }