UNPKG

@xailabs/altx

Version:

Flux flavor based on alt.js

792 lines 114 kB
[ { "__docId__": 0, "kind": "file", "name": "src/altInstance.js", "content": "/**\r\n * This is the alt instance that holds the state of the entire application.\r\n * All stores and actions are created on this instance.\r\n */\r\n\r\nimport Alt from 'alt';\r\n\r\nlet instance;\r\n\r\nsetAltInstance(new Alt());\r\n\r\nexport function setAltInstance(alt) {\r\n if (instance) teardown(instance);\r\n setup(alt);\r\n}\r\nexport function getAltInstance() {\r\n return instance;\r\n}\r\n\r\n\r\n\r\nconst root = (global || window);\r\n\r\nfunction setup(alt) {\r\n instance = alt;\r\n if (process.env.NODE_ENV !== 'production') {\r\n\r\n // Debugging with chrome devtools\r\n // @see https://github.com/goatslacker/alt-devtool\r\n require('alt-utils/lib/chromeDebug')(instance);\r\n\r\n // sometimes, chromeDebug just doesn't update until an action is dispatched.\r\n // we use a dummy action to do just that\r\n const refreshAction = instance.generateActions('__refresh__').__refresh__;\r\n instance.handleMessage = (e) => {\r\n if (e.data && e.data.type === 'ALT' && e.data.source === 'alt-devtools') {\r\n refreshAction.defer();\r\n }\r\n };\r\n root && root.addEventListener && root.addEventListener('message', instance.handleMessage);\r\n }\r\n}\r\nfunction teardown(alt) {\r\n if (process.env.NODE_ENV !== 'production') {\r\n root && root.removeEventListener && root.removeEventListener('message', instance.handleMessage);\r\n }\r\n}", "static": true, "longname": "src/altInstance.js", "access": null, "description": null, "lineNumber": 1 }, { "__docId__": 1, "kind": "function", "name": "setAltInstance", "memberof": "src/altInstance.js", "generator": false, "async": false, "static": true, "longname": "src/altInstance.js~setAltInstance", "access": null, "export": true, "importPath": "@xailabs/altx/src/altInstance.js", "importStyle": "{setAltInstance}", "description": null, "lineNumber": 12, "undocument": true, "unknown": [ { "tagName": "@_undocument", "tagValue": "" } ], "params": [ { "name": "alt", "types": [ "*" ] } ] }, { "__docId__": 2, "kind": "function", "name": "getAltInstance", "memberof": "src/altInstance.js", "generator": false, "async": false, "static": true, "longname": "src/altInstance.js~getAltInstance", "access": null, "export": true, "importPath": "@xailabs/altx/src/altInstance.js", "importStyle": "{getAltInstance}", "description": null, "lineNumber": 16, "undocument": true, "unknown": [ { "tagName": "@_undocument", "tagValue": "" } ], "params": [], "return": { "types": [ "*" ] } }, { "__docId__": 3, "kind": "variable", "name": "root", "memberof": "src/altInstance.js", "static": true, "longname": "src/altInstance.js~root", "access": null, "export": false, "importPath": "@xailabs/altx/src/altInstance.js", "importStyle": null, "description": null, "lineNumber": 22, "undocument": true, "unknown": [ { "tagName": "@_undocument", "tagValue": "" } ], "type": { "types": [ "*" ] } }, { "__docId__": 4, "kind": "function", "name": "setup", "memberof": "src/altInstance.js", "generator": false, "async": false, "static": true, "longname": "src/altInstance.js~setup", "access": null, "export": false, "importPath": "@xailabs/altx/src/altInstance.js", "importStyle": null, "description": null, "lineNumber": 24, "undocument": true, "unknown": [ { "tagName": "@_undocument", "tagValue": "" } ], "params": [ { "name": "alt", "types": [ "*" ] } ] }, { "__docId__": 5, "kind": "function", "name": "teardown", "memberof": "src/altInstance.js", "generator": false, "async": false, "static": true, "longname": "src/altInstance.js~teardown", "access": null, "export": false, "importPath": "@xailabs/altx/src/altInstance.js", "importStyle": null, "description": null, "lineNumber": 43, "undocument": true, "unknown": [ { "tagName": "@_undocument", "tagValue": "" } ], "params": [ { "name": "alt", "types": [ "*" ] } ] }, { "__docId__": 6, "kind": "file", "name": "src/BindHandlers.js", "content": "import {\r\n bind as bindAlt\r\n} from 'alt-utils/lib/decorators';\r\n\r\n\r\n// DEPRECATED\r\n// replace with \"calls and viewActions!\"\r\n\r\n\r\n/**\r\n * Creates a decorator for binding action handlers to a store.\r\n * A binding is created for each action that has a matching handler method mapping.\r\n *\r\n * The decorator takes any number of action handler definitions as arguments and applies their\r\n * bindings after concatinating them to a flat list, so you can pass either of these:\r\n * - an array of handler definitions\r\n * - any number of array of handler definitions\r\n * - any number of handlerdefinitions without arrays\r\n * - any mixed variation\r\n *\r\n * See manual/usage/action-handlers.md for more on this topic.\r\n */\r\nexport default function BindHandlers(...args) {\r\n const definitions = args.reduce((result, def) => {\r\n if (Array.isArray(def)) {\r\n def.forEach(d => result.push(d));\r\n }\r\n else {\r\n result.push(def);\r\n }\r\n return result;\r\n }, []);\r\n return function decorateStore(StoreClass) {\r\n definitions.forEach(function bindActions({handler, bindings}, i) {\r\n if (!handler || !handler.prototype) {\r\n throw new Error('Invalid action handler');\r\n }\r\n // we need unique prefixes for the potentionally same method names\r\n // using handler.prototype.constructor.name alone is useless after mangling/uglifying!\r\n const name = `$${i}_${handler.prototype.constructor.name}`;\r\n // collect the names of the methods defined in the handler decorator\r\n const methodNames = Object.keys(bindings);\r\n // now for each decorator method that has an action by the same name...\r\n methodNames.forEach(function bindAction(methodName) {\r\n if (typeof bindings[methodName] !== 'function') {\r\n throw new Error(`bindings.${methodName} is not a function (handler: ${name})`);\r\n }\r\n if (typeof handler.prototype[methodName] !== 'function') {\r\n throw new Error(`${name}.${methodName} is not a function`);\r\n }\r\n const storeMethodName = `${name}_${methodName}`;\r\n // ...copy the method from the decorator class to the store class\r\n StoreClass.prototype[storeMethodName] = handler.prototype[methodName];\r\n // and bind the action to it, using Alt's 'bind' util\r\n const applyBinding = bindAlt(bindings[methodName]);\r\n applyBinding(\r\n StoreClass,\r\n storeMethodName,\r\n Object.getOwnPropertyDescriptor(StoreClass.prototype, storeMethodName)\r\n );\r\n });\r\n });\r\n return StoreClass;\r\n };\r\n}\r\n\r\n", "static": true, "longname": "src/BindHandlers.js", "access": null, "description": null, "lineNumber": 1 }, { "__docId__": 7, "kind": "function", "name": "BindHandlers", "memberof": "src/BindHandlers.js", "generator": false, "async": false, "static": true, "longname": "src/BindHandlers.js~BindHandlers", "access": null, "export": true, "importPath": "@xailabs/altx/src/BindHandlers.js", "importStyle": "BindHandlers", "description": "Creates a decorator for binding action handlers to a store.\nA binding is created for each action that has a matching handler method mapping.\n\nThe decorator takes any number of action handler definitions as arguments and applies their\nbindings after concatinating them to a flat list, so you can pass either of these:\n- an array of handler definitions\n- any number of array of handler definitions\n- any number of handlerdefinitions without arrays\n- any mixed variation\n\nSee manual/usage/action-handlers.md for more on this topic.", "lineNumber": 23, "params": [ { "name": "args", "types": [ "...*" ], "spread": true } ], "return": { "types": [ "*" ] } }, { "__docId__": 8, "kind": "file", "name": "src/callFactory.js", "content": "import {validateCreator, validateDefinition} from './utils/validate';\r\nimport {createLogger} from './utils/logging';\r\nimport createActions from './createActions';\r\n\r\nexport default function callFactory(name, {\r\n namespace='global',\r\n defaultActions=['loading', 'error', 'success'],\r\n actions=createActions(`${namespace}:${name}`, defaultActions),\r\n logger=createLogger(`${namespace}:${name}`),\r\n}={}) {\r\n const errors = validateCreator({name, actions, logger}, logger);\r\n const errorKeys = errors && Object.keys(errors);\r\n if (errorKeys && errorKeys.length) {\r\n throw new Error(errors[errorKeys[0]]);\r\n }\r\n\r\n return {\r\n\r\n name,\r\n actions,\r\n\r\n create: (definition) => {\r\n const createDefinition = typeof definition === 'function' ? definition : () => definition;\r\n const call = Object.assign(createDefinition({name, actions, logger}), {\r\n name,\r\n actions,\r\n logger\r\n });\r\n if (validateDefinition(call, logger)) {\r\n throw new Error('Invalid call');\r\n }\r\n call.dataSource = {\r\n // set the default actions\r\n ...(call.actions || {}),\r\n // set dataSource from the definition passed om - potentially overriding the actions\r\n ...(call.dataSource || {})\r\n };\r\n return call;\r\n },\r\n createActions: (actionNames) => createActions(`${namespace}:${name}`, actionNames)\r\n };\r\n}\r\n", "static": true, "longname": "src/callFactory.js", "access": null, "description": null, "lineNumber": 1 }, { "__docId__": 9, "kind": "function", "name": "callFactory", "memberof": "src/callFactory.js", "generator": false, "async": false, "static": true, "longname": "src/callFactory.js~callFactory", "access": null, "export": true, "importPath": "@xailabs/altx/src/callFactory.js", "importStyle": "callFactory", "description": null, "lineNumber": 5, "undocument": true, "unknown": [ { "tagName": "@_undocument", "tagValue": "" } ], "params": [ { "name": "name", "types": [ "*" ] }, { "name": "objectPattern1", "optional": true, "types": [ "{\"namespace\": *, \"defaultActions\": *, \"actions\": *, \"logger\": *}" ], "defaultRaw": {}, "defaultValue": "{}" } ], "return": { "types": [ "{\"name\": *, \"actions\": *, \"create\": *, \"createActions\": *}" ] } }, { "__docId__": 10, "kind": "file", "name": "src/callSeries.js", "content": "import Promise from 'promise';\r\n/**\r\n * Retuns a promise and executes a series of calls.\r\n * Uses a timeout initially so that we don't run into errors when still in the middle of a dispatch.\r\n *\r\n * import {callSeries} from 'shared/flux';\r\n *\r\n * @param {array} calls - An array of function that each returns a promise\r\n * @param {object} options - An array of function that each returns a promise\r\n * @param {boolean} options.log - Whether to log individual calls\r\n * @return {promise} - A promise that will be resolved when all calls succeeded or rejected if one call failed\r\n */\r\nexport default function callSeries(calls, {log = false} = {}) {\r\n return new Promise((resolve, reject) => {\r\n let results = [];\r\n setTimeout(() => {\r\n function series(list) {\r\n log && console.log('[callSeries] list', list);\r\n var p = Promise.resolve();\r\n return list.reduce(function(pacc, fn) {\r\n return pacc = pacc.then(res => {\r\n log && console.log('[callSeries] res', res);\r\n results.push(res);\r\n return fn(res);\r\n });\r\n }, p);\r\n }\r\n series(calls)\r\n .then(() => setTimeout(() => resolve(results)))\r\n .catch((err) => reject(err));\r\n });\r\n });\r\n}\r\n", "static": true, "longname": "src/callSeries.js", "access": null, "description": null, "lineNumber": 1 }, { "__docId__": 11, "kind": "function", "name": "callSeries", "memberof": "src/callSeries.js", "generator": false, "async": false, "static": true, "longname": "src/callSeries.js~callSeries", "access": null, "export": true, "importPath": "@xailabs/altx/src/callSeries.js", "importStyle": "callSeries", "description": "Retuns a promise and executes a series of calls.\nUses a timeout initially so that we don't run into errors when still in the middle of a dispatch.\n\n import {callSeries} from 'shared/flux';", "lineNumber": 13, "params": [ { "nullable": null, "types": [ "array" ], "spread": false, "optional": false, "name": "calls", "description": "An array of function that each returns a promise" }, { "nullable": null, "types": [ "object" ], "spread": false, "optional": false, "name": "options", "description": "An array of function that each returns a promise" }, { "nullable": null, "types": [ "boolean" ], "spread": false, "optional": false, "name": "options.log", "description": "Whether to log individual calls" } ], "return": { "nullable": null, "types": [ "promise" ], "spread": false, "description": "A promise that will be resolved when all calls succeeded or rejected if one call failed" } }, { "__docId__": 12, "kind": "file", "name": "src/createActions.js", "content": "import {getAltInstance} from './altInstance';\r\n\r\n/**\r\n * Creates simple (data-pass-through) actions with a namespace.\r\n * Basically just like `alt.generateActions`, but within a given namespace instead of 'global'.\r\n *\r\n * @param {string} namespace - The namespace to use for the actions\r\n * @param {array<string>} actions - An array of action names\r\n * @return {object} An object containing the generated actions and constants\r\n */\r\nexport default function createActions(namespace, actions) {\r\n return getAltInstance().createActions({\r\n name: namespace,\r\n ...[].concat(actions).reduce((result, action) => {\r\n result[action] = (data={}) => data;\r\n return result;\r\n }, {})\r\n });\r\n}\r\n", "static": true, "longname": "src/createActions.js", "access": null, "description": null, "lineNumber": 1 }, { "__docId__": 13, "kind": "function", "name": "createActions", "memberof": "src/createActions.js", "generator": false, "async": false, "static": true, "longname": "src/createActions.js~createActions", "access": null, "export": true, "importPath": "@xailabs/altx/src/createActions.js", "importStyle": "createActions", "description": "Creates simple (data-pass-through) actions with a namespace.\nBasically just like `alt.generateActions`, but within a given namespace instead of 'global'.", "lineNumber": 11, "params": [ { "nullable": null, "types": [ "string" ], "spread": false, "optional": false, "name": "namespace", "description": "The namespace to use for the actions" }, { "nullable": null, "types": [ "array<string>" ], "spread": false, "optional": false, "name": "actions", "description": "An array of action names" } ], "return": { "nullable": null, "types": [ "object" ], "spread": false, "description": "An object containing the generated actions and constants" } }, { "__docId__": 14, "kind": "file", "name": "src/createStore.js", "content": "import Immutable from 'immutable';\r\nimport { decorate, datasource, bind } from 'alt-utils/lib/decorators';\r\nimport immutable from 'alt-utils/lib/ImmutableUtil';\r\n\r\nimport {getAltInstance} from './altInstance';\r\nimport ImmutableStore from './ImmutableStore';\r\nimport getSources from './getSources';\r\n\r\nimport bindCalls from './decorators/bindCalls';\r\nimport bindActions from './decorators/bindActions';\r\n\r\nexport default function createStore(displayName, {\r\n alt,\r\n state,\r\n calls,\r\n sources,\r\n viewActions\r\n} = {}) {\r\n\r\n if (!displayName) {\r\n throw new Error('displayName is required');\r\n }\r\n\r\n alt = alt || getAltInstance();\r\n state = state || Immutable.fromJS({});\r\n\r\n calls = calls || [];\r\n sources = sources || getSources(calls);\r\n viewActions = viewActions || [];\r\n\r\n return alt.createStore((\r\n @decorate(alt)\r\n @datasource(sources)\r\n @bindCalls(calls)\r\n @bindActions(viewActions)\r\n @immutable\r\n class extends ImmutableStore {\r\n constructor() {\r\n super(state);\r\n }\r\n }\r\n ), displayName);\r\n\r\n}\r\n", "static": true, "longname": "src/createStore.js", "access": null, "description": null, "lineNumber": 1 }, { "__docId__": 15, "kind": "function", "name": "createStore", "memberof": "src/createStore.js", "generator": false, "async": false, "static": true, "longname": "src/createStore.js~createStore", "access": null, "export": true, "importPath": "@xailabs/altx/src/createStore.js", "importStyle": "createStore", "description": null, "lineNumber": 12, "undocument": true, "unknown": [ { "tagName": "@_undocument", "tagValue": "" } ], "params": [ { "name": "displayName", "types": [ "*" ] }, { "name": "objectPattern1", "optional": true, "types": [ "{\"alt\": *, \"state\": *, \"calls\": *, \"sources\": *, \"viewActions\": *}" ], "defaultRaw": {}, "defaultValue": "{}" } ], "return": { "types": [ "*" ] } }, { "__docId__": 16, "kind": "file", "name": "src/decorators/bindActions.js", "content": "import {bind} from 'alt-utils/lib/decorators';\r\nimport flatten from '../utils/flatten';\r\nimport {getLevel as getLogLevel, logLevel} from '../utils/logging';\r\n\r\n/**\r\n * Decorates a store with any number of viewAction definitions.\r\n */\r\nexport default function bindActions(...args) {\r\n return function decorate(storeClass) {\r\n const calls = flatten(args);\r\n calls.forEach(call => bindViewActionHandler(storeClass, call));\r\n return storeClass;\r\n };\r\n}\r\n\r\n\r\nfunction bindViewActionHandler(storeClass, definition) {\r\n\r\n const {name, action, reducer, sideEffect, logger, logging} = definition;\r\n\r\n const handlerName = `${name}`;\r\n\r\n if (storeClass.prototype[handlerName]) throw new Error(`Duplicate handler \"${handlerName}\"`);\r\n\r\n storeClass.prototype[handlerName] = function handleViewAction(payload) {\r\n\r\n if (logging || getLogLevel() === logLevel.FORCE) {\r\n logger[payload instanceof Error ? 'error' : 'log'](payload && payload.toJS ? payload.toJS() : payload);\r\n }\r\n\r\n const currentState = this.state;\r\n let nextState = currentState;\r\n\r\n try {\r\n nextState = reducer && reducer(currentState, payload);\r\n }\r\n catch (error) {\r\n console.error(`${handlerName}: error executing reducer`, error);\r\n }\r\n\r\n if (nextState && nextState !== currentState) {\r\n this.setState(nextState);\r\n }\r\n\r\n if (sideEffect) {\r\n setTimeout(() => {\r\n try {\r\n sideEffect({state: nextState, prevState: currentState, payload});\r\n }\r\n catch (error) {\r\n console.error(`${handlerName}: error executing sideEffect`, error);\r\n }\r\n });\r\n }\r\n };\r\n\r\n const bindActionHandler = bind(action);\r\n\r\n bindActionHandler(\r\n storeClass,\r\n handlerName,\r\n Object.getOwnPropertyDescriptor(storeClass.prototype, handlerName)\r\n );\r\n\r\n};\r\n", "static": true, "longname": "src/decorators/bindActions.js", "access": null, "description": null, "lineNumber": 1 }, { "__docId__": 17, "kind": "function", "name": "bindActions", "memberof": "src/decorators/bindActions.js", "generator": false, "async": false, "static": true, "longname": "src/decorators/bindActions.js~bindActions", "access": null, "export": true, "importPath": "@xailabs/altx/src/decorators/bindActions.js", "importStyle": "bindActions", "description": "Decorates a store with any number of viewAction definitions.", "lineNumber": 8, "params": [ { "name": "args", "types": [ "...*" ], "spread": true } ], "return": { "types": [ "*" ] } }, { "__docId__": 18, "kind": "function", "name": "bindViewActionHandler", "memberof": "src/decorators/bindActions.js", "generator": false, "async": false, "static": true, "longname": "src/decorators/bindActions.js~bindViewActionHandler", "access": null, "export": false, "importPath": "@xailabs/altx/src/decorators/bindActions.js", "importStyle": null, "description": null, "lineNumber": 17, "undocument": true, "unknown": [ { "tagName": "@_undocument", "tagValue": "" } ], "params": [ { "name": "storeClass", "types": [ "*" ] }, { "name": "definition", "types": [ "*" ] } ] }, { "__docId__": 19, "kind": "file", "name": "src/decorators/bindCalls.js", "content": "import {bind} from 'alt-utils/lib/decorators';\r\nimport flatten from '../utils/flatten';\r\nimport {getLevel as getLogLevel, logLevel} from '../utils/logging';\r\n/**\r\n * Decorates a store with any number of call definitions.\r\n */\r\nexport default function bindCalls(...args) {\r\n return function decorate(storeClass) {\r\n const calls = flatten(args);\r\n calls.forEach(call => decorateStoreWithCall(storeClass, call));\r\n return storeClass;\r\n };\r\n}\r\n\r\n/**\r\n * Decorates a store with a single call definition.\r\n * Attaches the dataSource specified in the call definition using alt's datasource decorator.\r\n * Creates and binds a handler function for all reducers and actions specified in the call definition.\r\n */\r\nfunction decorateStoreWithCall(storeClass, callDefinition) {\r\n const actionNames = Object.keys(callDefinition.actions).reduce((result, key) => {\r\n // remove ACTION_CONSTANT variants generated by alt\r\n if (result.indexOf(key.toLowerCase()) === -1) {\r\n result.push(key);\r\n }\r\n return result;\r\n }, []);\r\n actionNames.forEach(reducerName => {\r\n bindReducerHandler(reducerName, storeClass, callDefinition);\r\n });\r\n}\r\n\r\n/**\r\n * Attaches a single reducer handling to the store.\r\n * A new handler method will be created on the store for each action associated\r\n * with a reducer (defaults to the action names: loading, error, success). Each handler will pass\r\n * the current state and the action payload to the reducer with the same name\r\n * and mutate the store with the new state returned by the reducer.\r\n * Any sideEffects defined in the call will be executed with a ({state, prevState, payload}) signature.\r\n */\r\nfunction bindReducerHandler(reducerName, storeClass, callDefinition) {\r\n\r\n const handlerName = `_${callDefinition.name}_${reducerName}`;\r\n\r\n if (storeClass.prototype[handlerName]) throw new Error(`Duplicate handler \"${handlerName}\"`);\r\n\r\n storeClass.prototype[handlerName] = function handleCallAction(payload) {\r\n\r\n const reducer = callDefinition.hasOwnProperty('reducers') && callDefinition.reducers[reducerName];\r\n const sideEffect = callDefinition.hasOwnProperty('sideEffects') && callDefinition.sideEffects[reducerName];\r\n const logging = callDefinition.hasOwnProperty('logging') && callDefinition.logging;\r\n const logger = callDefinition.hasOwnProperty('logger') && callDefinition.logger;\r\n const isError = payload instanceof Error;\r\n if (isError) {\r\n if (payload.response && payload.response.body && payload.response.body.message) {\r\n logger.error(reducerName, payload.response.body.message);\r\n }\r\n // logger.debug(payload);\r\n }\r\n else if (logging || getLogLevel() === logLevel.FORCE) {\r\n logger[isError ? 'error' : 'log'](reducerName, payload && payload.toJS ? payload.toJS() : payload);\r\n }\r\n\r\n const currentState = this.state;\r\n let nextState = currentState;\r\n\r\n if (reducer) {\r\n //console.log(`[${handlerName}]`, payload, callDefinition);\r\n try {\r\n nextState = reducer(currentState, payload);\r\n }\r\n catch (error) {\r\n console.error(`Error in reducer (${callDefinition.name}, ${reducerName})`, error);\r\n }\r\n }\r\n\r\n if (reducer && !nextState) console.warn(`reducer \"${reducerName}\" in call \"${callDefinition.name}\" did not return a new state. Either you forgot to return it, or you should consider using a sideEffect instead of a reducer if no return value is needed.`);\r\n\r\n if (nextState) {\r\n this.setState(nextState);\r\n }\r\n\r\n if (sideEffect) {\r\n setTimeout(() => {\r\n try {\r\n sideEffect({state: nextState, prevState: currentState, payload});\r\n }\r\n catch (error) {\r\n console.error(`Error in sideEffect (${callDefinition.name}, ${reducerName})`, error);\r\n }\r\n });\r\n }\r\n };\r\n\r\n const action = callDefinition.actions[reducerName];\r\n const bindActionHandler = bind(action);\r\n\r\n bindActionHandler(\r\n storeClass,\r\n handlerName,\r\n Object.getOwnPropertyDescriptor(storeClass.prototype, handlerName)\r\n );\r\n\r\n};\r\n", "static": true, "longname": "src/decorators/bindCalls.js", "access": null, "description": null, "lineNumber": 1 }, { "__docId__": 20, "kind": "function", "name": "bindCalls", "memberof": "src/decorators/bindCalls.js", "generator": false, "async": false, "static": true, "longname": "src/decorators/bindCalls.js~bindCalls", "access": null, "export": true, "importPath": "@xailabs/altx/src/decorators/bindCalls.js", "importStyle": "bindCalls", "description": "Decorates a store with any number of call definitions.", "lineNumber": 7, "params": [ { "name": "args", "types": [ "...*" ], "spread": true } ], "return": { "types": [ "*" ] } }, { "__docId__": 21, "kind": "function", "name": "decorateStoreWithCall", "memberof": "src/decorators/bindCalls.js", "generator": false, "async": false, "static": true, "longname": "src/decorators/bindCalls.js~decorateStoreWithCall", "access": null, "export": false, "importPath": "@xailabs/altx/src/decorators/bindCalls.js", "importStyle": null, "description": "Decorates a store with a single call definition.\nAttaches the dataSource specified in the call definition using alt's datasource decorator.\nCreates and binds a handler function for all reducers and actions specified in the call definition.", "lineNumber": 20, "params": [ { "name": "storeClass", "types": [ "*" ] }, { "name": "callDefinition", "types": [ "*" ] } ] }, { "__docId__": 22, "kind": "function", "name": "bindReducerHandler", "memberof": "src/decorators/bindCalls.js", "generator": false, "async": false, "static": true, "longname": "src/decorators/bindCalls.js~bindReducerHandler", "access": null, "export": false, "importPath": "@xailabs/altx/src/decorators/bindCalls.js", "importStyle": null, "description": "Attaches a single reducer handling to the store.\nA new handler method will be created on the store for each action associated\nwith a reducer (defaults to the action names: loading, error, success). Each handler will pass\nthe current state and the action payload to the reducer with the same name\nand mutate the store with the new state returned by the reducer.\nAny sideEffects defined in the call will be executed with a ({state, prevState, payload}) signature.", "lineNumber": 41, "params": [ { "name": "reducerName", "types": [ "*" ] }, { "name": "storeClass", "types": [ "*" ] }, { "name": "callDefinition", "types": [ "*" ] } ] }, { "__docId__": 23, "kind": "file", "name": "src/decorators/bindHandlers.js", "content": "import {bind} from 'alt-utils/lib/decorators';\r\nimport flattenArrays from '../utils/flatten';\r\n\r\n/**\r\n * Decorates a store with any number of action handlers.\r\n */\r\nexport default function bindHandlers(actions, ...handlers) {\r\n return function decorateStore(storeClass) {\r\n // attach bound handler methods to the store class for each handler definition\r\n flattenArrays(handlers).forEach(handler => attachBoundHandler(storeClass, actions[handler.name], handler));\r\n return storeClass;\r\n };\r\n}\r\n/**\r\n * Attaches a single reducer handling to the store.\r\n *\r\n * A new handler method will be created on the store class.\r\n * The handler method invokes the reducer giving it the current store state.\r\n * It then sets the reducer result as the new store state.\r\n *\r\n * If a sideEffect function is defined, it will be called with the signature `{state, prevState, payload}`\r\n *\r\n * @param {object} storeClass - The class of the store to be decorated\r\n * @param {object} action - An altjs action\r\n * @param {object} handler - An handler object with `{name[, reducer, sideEffect]}`\r\n */\r\nfunction attachBoundHandler(storeClass, action, handler) {\r\n // name is required\r\n\r\n const methodName = `__handle_${handler.name}`;\r\n\r\n if (storeClass.prototype[methodName]) throw new Error(`Duplicate method \"${methodName}\"`);\r\n\r\n /**\r\n * Handles an action call and sets the next state of the store.\r\n *\r\n * @param {any} payload - the single argument that can be specified when calling an action.\r\n * If you need to use more than one argument, use an object with any properties you need.\r\n */\r\n storeClass.prototype[methodName] = function handleAction(payload) {\r\n\r\n const reducer = handler.hasOwnProperty('reducer') && handler.reducer;\r\n const sideEffect = handler.hasOwnProperty('sideEffect') && handler.sideEffect;\r\n\r\n const currentState = this.state;\r\n\r\n\r\n // the actual operation: run the reducer and set its result as state\r\n let nextState = currentState;\r\n if (reducer) {\r\n try {\r\n nextState = reducer(currentState, payload);\r\n }\r\n catch (error) {\r\n console.error(`Error in reducer (${handler.name}, ${handler.name})`, error);\r\n }\r\n }\r\n if (nextState) {\r\n this.setState(nextState);\r\n }\r\n else if (reducer) {\r\n console.warn(`reducer \"${handler.name}\" in call \"${handler.name}\" did not return a new state.\r\n Either you forgot to return it, or if no state change is required, maybe you should use a sideEffect instead of a reducer.\r\n `);\r\n }\r\n\r\n\r\n\r\n if (sideEffect) {\r\n try {\r\n sideEffect({state: nextState, prevState: currentState, payload});\r\n }\r\n catch (error) {\r\n console.error(`Error in sideEffect (${handler.name}, ${handler.name})`, error);\r\n }\r\n }\r\n };\r\n\r\n const bindhandler = bind(action);\r\n\r\n bindhandler(\r\n storeClass,\r\n methodName,\r\n Object.getOwnPropertyDescriptor(storeClass.prototype, methodName)\r\n );\r\n\r\n};\r\n", "static": true, "longname": "src/decorators/bindHandlers.js", "access": null, "description": null, "lineNumber": 1 }, { "__docId__": 24, "kind": "function", "name": "bindHandlers", "memberof": "src/decorators/bindHandlers.js", "generator": false, "async": false, "static": true, "longname": "src/decorators/bindHandlers.js~bindHandlers", "access": null, "export": true, "importPath": "@xailabs/altx/src/decorators/bindHandlers.js", "importStyle": "bindHandlers", "description": "Decorates a store with any number of action handlers.", "lineNumber": 7, "params": [ { "name": "actions", "types": [ "*" ] }, { "name": "handlers", "types": [ "...*" ], "spread": true } ], "return": { "types": [ "*" ] } }, { "__docId__": 25, "kind": "function", "name": "attachBoundHandler", "memberof": "src/decorators/bindHandlers.js", "generator": false, "async": false, "static": true, "longname": "src/decorators/bindHandlers.js~attachBoundHandler", "access": null, "export": false, "importPath": "@xailabs/altx/src/decorators/bindHandlers.js", "importStyle": null, "description": "Attaches a single reducer handling to the store.\n\nA new handler method will be created on the store class.\nThe handler method invokes the reducer giving it the current store state.\nIt then sets the reducer result as the new store state.\n\nIf a sideEffect function is defined, it will be called with the signature `{state, prevState, payload}`", "lineNumber": 27, "params": [ { "nullable": null, "types": [ "object" ], "spread": false, "optional": false, "name": "storeClass", "description": "The class of the store to be decorated" }, { "nullable": null, "types": [ "object" ], "spread": false, "optional": false, "name": "action", "description": "An altjs action" }, { "nullable": null, "types": [ "object" ], "spread": false, "optional": false, "name": "handler", "description": "An handler object with `{name[, reducer, sideEffect]}`" } ] }, { "__docId__": 26, "kind": "file", "name": "src/decorators/connect.js", "content": "import connectToStores from 'alt-utils/lib/connectToStores';\r\nimport React, { createElement } from 'react';\r\n\r\n// TODO: deprecate connectAlternative asap!\r\n\r\n\r\n/* eslint-disable */\r\n/**\r\n * A component decorator for connecting to immutable stores.\r\n *\r\n * Basically a wrapper around `alt/utils/connectToStores`. \r\n * Adds the necessary static methods `getStores()` and `getPropsFromStores()` to the decorated component.\r\n *\r\n * - Supports multiple stores.\r\n * - Supports a simplified, string-based access to stores, with optional renaming of props.\r\n * - Supports more flexible, redux-like access to stores using mapper functions.\r\n *\r\n * ### String notation\r\n *\r\n * @example\r\n * @connect([{store: MyStore, props: ['myValue', 'anotherValue']}])\r\n * export default class MyComponent extends React.Component {\r\n * render() {\r\n * const {myValue, anotherValue} = this.props;\r\n * ...\r\n * }\r\n * }\r\n *\r\n * You can rename props using the ` as ` alias syntax\r\n *\r\n * @example\r\n * @connect([{\r\n * store: PeopleStore,\r\n * props: ['items as people']\r\n * }, {\r\n * store: ProductStore,\r\n * props: ['items as products']\r\n * }])\r\n * export default class MyComponent extends React.Component {\r\n * render() {\r\n * // this.props.people, this.props.products, ...\r\n * }\r\n * }\r\n *\r\n * ### Function notation\r\n *\r\n * Use mapper functions instead of strings in order to manually retrieve store values.\r\n * The function receives the store state and the component props.\r\n *\r\n * @example\r\n * @connect([{\r\n * store: MyStore,\r\n * props: (state, props) => {\r\n * return {\r\n * item: state.get('items').filter(item => item.get('id') === props.id)\r\n * }\r\n * }\r\n * }])\r\n * export default class MyComponent extends React.Component {\r\n * render() {\r\n * const item = this.props.item;\r\n * }\r\n * }\r\n *\r\n * Technically, you could also mix all access methods, but this defeats the purpose of simple access:\r\n *\r\n * @example\r\n * @connect([{\r\n * store: MyStore,\r\n * props: ['someProp', 'anotherProp', (state, props) => {\r\n * return {\r\n * item: state.get('items').filter(item => item.get('id') === props.id)\r\n * }\r\n * }, 'some.nested.value as foo']\r\n * }])\r\n * export default class MyComponent extends React.Component {\r\n * ...\r\n * }\r\n *\r\n * There are however valid usecase for mixing access methods. For example, you might have keys that themselves contain dots.\r\n * For example, that is the case when using `validate.js` with nested constraints and keeping validation results in the store.\r\n * There might be an `errors` map in your storewith keys like `user.address.street`. In such a case you wouldn't be able to access those values because the dots do not\r\n * represent the actual keyPath in the tree:\r\n *\r\n * @example\r\n * @connect([{\r\n * store,\r\n * props: ['user.address.street', (state) => ({errors: state.getIn(['errors', 'user.address.street'])})]\r\n * }])\r\n *\r\n * @see https://github.com/goatslacker/alt/blob/master/docs/utils/immutable.md\r\n * @see https://github.com/goatslacker/alt/blob/master/src/utils/connectToStores.js\r\n *\r\n * @param {Array<{store: AltStore, props: Array<string>}>} definitions - A list of objects that each define a store connection\r\n */\r\n/* eslint-enable */\r\nexport default function connect(definitions) {\r\n return function(targetClass) {\r\n targetClass.getStores = function() {\r\n return definitions.map((def) => def.store);\r\n };\r\n targetClass.getPropsFromStores = function(componentProps) {\r\n return definitions.reduce((result, def) => {\r\n if (typeof def.props === 'function') {\r\n // the props definition is itself a function. return with its result.\r\n return Object.assign(result, def.props(def.store.state, componentProps));\r\n }\r\n // the props definition is an array. evaluate and reduce each of its elements\r\n return def.props.reduce((result, accessor) => {\r\n return Object.assign(result, mapProps(accessor, def.store.state, componentProps));\r\n }, result);\r\n }, {});\r\n };\r\n return connectToStores(targetClass);\r\n };\r\n}\r\n\r\nfunction mapProps(accessor, state, props) {\r\n switch (typeof accessor) {\r\n case 'function':\r\n return mapFuncAccessor(accessor, state, props);\r\n case 'string':\r\n return mapStringAccessor(accessor, state);\r\n }\r\n}\r\n\r\nfunction mapFuncAccessor(accessor, state, props) {\r\n return accessor(state, props);\r\n}\r\n\r\nfunction mapStringAccessor(accessor, state) {\r\n const {keyPath, propName} = parseAccessor(accessor);\r\n return {\r\n [propName]: state.getIn(keyPath)\r\n };\r\n}\r\n\r\n/**\r\n * Takes the accessor defined by the component and retrieves `keyPath` and `propName`\r\n * The accessor may be the name of a top-level value in the store, or a path to a nested value.\r\n * Nested values can be accessed using a dot-separated syntax (e.g. `some.nested.value`).\r\n *\r\n * The name of the prop received by the component is the last part of the accessor in case of\r\n * a nested syntax, or the accessor itself in case of a simple top-level accessor.\r\n *\r\n * If you need to pass the value using a different prop name, you can use the ` as ` alias syntax,\r\n * e.g. `someProp as myProp` or `some.prop as myProp`.\r\n *\r\n * examples:\r\n *\r\n * 'someValue' // {keyPath: ['someValue'], propName: 'someValue'}\r\n * 'someValue as foo' // {keyPath: ['someValue'], propName: 'foo'}\r\n * 'some.nested.value' // {keyPath: ['some', 'nested', 'value'], propName: 'value'}\r\n * 'some.nested.value as foo' // {keyPath: ['some', 'nested', 'value'], propName: 'foo'}\r\n *\r\n * @param {string} string - The value accessor passed by the component decorator.\r\n * @return {object} result - A `{storeName, propName}` object\r\n * @return {string} result.keyPath - An immutablejs keyPath array to the value in the store\r\n * @return {string} result.propName - name for the prop as expected by the component\r\n */\r\nfunction parseAccessor(accessor) {\r\n let keyPath, propName;\r\n if (accessor.indexOf(' as ') > -1) {\r\n // e.g. 'foo as bar' or 'some.foo as bar'\r\n const parts = accessor.split(' as ');\r\n keyPath = parts[0].split('.');\r\n propName = parts[1];\r\n }\r\n else {\r\n // e.g. 'foo' or 'some.foo'\r\n keyPath = accessor.split('.');\r\n propName = keyPath[keyPath.length - 1];\r\n }\r\n return {keyPath, propName};\r\n}\r\n\r\n\r\nfunction connectAlternative(store, mapStateToProps, WrappedComponent) {\r\n return class Connect extends React.Component {\r\n\r\n constructor(props, context) {\r\n super(props, context);\r\n const storeState = store.getState();\r\n this.state = { storeState: mapStateToProps(storeState, props) };\r\n }\r\n\r\n componentDidMount() {\r\n this._isMounted = true;\r\n this.storeSubscription = store.listen(this.handleStoreUpdate);\r\n }\r\n\r\n componentWillUnmount() {\r\n this._isMounted = false;\r\n if (this.storeSubscription) {\r\n store.unlisten(this.storeSubscription);\r\n }\r\n }\r\n\r\n // if we use props in mapStateToProps,\r\n // we need to run it again when props have changed\r\n componentWillReceiveProps(nextProps) {\r\n //untested! should work though\r\n if(mapStateToProps.length > 1) {\r\n this.setState({storeState: mapStateToProps(store.getState(), nextProps)});\r\n }\r\n }\r\n\r\n handleStoreUpdate = state => {\r\n if(this._isMounted) {\r\n this.setState({ storeState: mapStateToProps(state, this.props) });\r\n }\r\n }\r\n\r\n render() {\r\n const mergedProps = { ...this.props, ...this.state.storeState };\r\n return createElement(WrappedComponent, mergedProps);\r\n }\r\n\r\n };\r\n}\r\n\r\nexport {connectAlternative};\r\n", "static": true, "longname": "src/decorators/connect.js", "access": null, "description": null, "lineNumber": 1 }, { "__docId__": 27, "kind": "function", "name": "connect", "memberof": "src/decorators/connect.js", "generator": false, "async": false, "static": true, "longname": "src/decorators/connect.js~connect", "access": null, "export": true, "importPath": "@xailabs/altx/src/decorators/connect.js", "importStyle": "connect", "description": "A component decorator for connecting to immutable stores.\n\nBasically a wrapper around `alt/utils/connectToStores`. \nAdds the necessary static methods `getStores()` and `getPropsFromStores()` to the decorated component.\n\n- Supports multiple stores.\n- Supports a simplified, string-based access to stores, with optional renaming of props.\n- Supports more flexible, redux-like access to stores using mapper functions.\n\n### String notation", "examples": [ "@connect([{store: MyStore, props: ['myValue', 'anotherValue']}])\nexport default class MyComponent extends React.Component {\n render() {\n const {myValue, anotherValue} = this.props;\n ...\n }\n}\n\nYou can rename props using the ` as ` alias syntax", "@connect([{\n store: PeopleStore,\n props: ['items as people']\n}, {\n store: ProductStore,\n props: ['items as products']\n}])\nexport default class MyComponent extends React.Component {\n render() {\n // this.props.people, this.props.products, ...\n }\n}\n\n### Function notation\n\nUse mapper functions instead of strings in order to manually retrieve store values.\nThe function receives the store state and the component props.", "@connect([{\n store: MyStore,\n props: (state, props) => {\n return {\n item: state.get('items').filter(item => item.get('id') === props.id)\n }\n }\n}])\nexport default class MyComponent extends React.Component {\n render() {\n const item = this.props.item;\n }\n}\n\nTechnically, you could also mix all access methods, but this defeats the purpose of simple access:", "@connect([{\n store: MyStore,\n props: ['someProp', 'anotherProp', (state, props) => {\n return {\n item: state.get('items').filter(item => item.get('id') === props.id)\n }\n }, 'some.nested.value as foo']\n}])\nexport default class MyComponent extends React.Component {\n ...\n}\n\nThere are however valid usecase for mixing access methods. For example, you might have keys that themselves contain dots.\nFor example, that is the case when using `validate.js` with nested constraints and keeping validation results in the store.\nThere might be an `errors` map in your storewith keys like `user.address.street`. In such a case you wouldn't be able to access those values because the dots do not\nrepresent the actual keyPath in the tree:", "@connect([{\n store,\n props: ['user.address.street', (state) => ({errors: state.getIn(['errors', 'user.address.street'])})]\n}])" ], "see": [ "https://github.com/goatslacker/alt/blob/master/docs/utils/immutable.md", "https://github.com/goatslacker/alt/blob/master/src/utils/connectToStores.js" ], "lineNumber": 97, "params": [ { "nullable": null, "types": [ "Array<{store: AltStore, props: Array<string>}>" ], "spread": false, "optional": false, "name": "definitions", "description": "A list of objects that each define a store connection" } ], "return": { "types": [ "*" ] } }, { "__docId__": 28, "kind": "function", "name": "mapProps", "memberof": "src/decorators/connect.js", "generator": false, "async": false, "static": true, "longname": "src/decorators/connect.js~mapProps", "access": null, "export": false, "importPath": "@xailabs/altx/src/decorators/connect.js", "importStyle": null, "description": null, "lineNumber": 118, "undocument": true, "unknown": [ { "tagName": "@_undocument", "tagValue"