-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
61c4bed
commit 45e6f49
Showing
24 changed files
with
617 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
node_modules | ||
dist | ||
# dist | ||
.DS_Store | ||
.nyc_output | ||
build_test | ||
index.js | ||
# index.js | ||
src |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
import { IAppSetup } from './interfaces'; | ||
export default function App(): IAppSetup; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import RegistryConfigurator from './RegistryConfigurator'; | ||
import Bootstrapper from './Bootstrapper'; | ||
import Setup from './Setup'; | ||
export default function App() { | ||
const configurator = RegistryConfigurator(); | ||
const bootstrapper = Bootstrapper(); | ||
return Setup(configurator, bootstrapper); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
import { IBootstrapper } from './interfaces'; | ||
export default function Bootstrapper(): IBootstrapper; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import { invokeMap, reduce, map } from 'lodash'; | ||
import invariant from './utils/invariant'; | ||
import isModel from './utils/isModel'; | ||
export default function Bootstrapper() { | ||
/** | ||
* Bootstraps an application on a DOM element. | ||
* @param {IBootstrapRegistry} registry registry that contains all registered components and model | ||
* @param {Element} root host element | ||
* @return {IDestroyFn} function to unsubscribe from model changes | ||
*/ | ||
function bootstrap(registry, root) { | ||
invariant(isModel(registry.model), `Please specify application model using the \`Model\` factory: \`App().model(Model(...))\``); | ||
let components = initComponents(registry.components, root, registry.model); | ||
let unsubscribe = registry.model.subscribe(() => update(components)); | ||
registry.model.emit(); | ||
return () => { | ||
unsubscribe(); | ||
invokeMap(components, 'onDestroy'); | ||
}; | ||
} | ||
/** | ||
* Calls `update` on all components | ||
* @param {IComponent[]} components registered components | ||
*/ | ||
function update(components) { | ||
invokeMap(components, 'update'); | ||
} | ||
/** | ||
* Initializes components | ||
* @param {IDictionary<IComponentStatic>} source component registry | ||
* @param {Element} root host element | ||
* @param {IModel} model application model | ||
* @return {IComponent[]} initialized components | ||
*/ | ||
function initComponents(source, root, model) { | ||
return reduce(source, (acc, ctor, selector) => { | ||
let elements = root.querySelectorAll(selector); | ||
let instances = map(elements, element => new ctor(element, model)); | ||
return acc.concat(instances); | ||
}, []); | ||
} | ||
return { bootstrap }; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { IComponent, IModel } from './interfaces'; | ||
/** | ||
* Provides class to extend all components from | ||
*/ | ||
export default class Component implements IComponent { | ||
element: Element; | ||
model: IModel; | ||
constructor(element: Element, model: IModel); | ||
update(): void; | ||
onDestroy(): void; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
/** | ||
* Provides class to extend all components from | ||
*/ | ||
export default class Component { | ||
constructor(element, model) { | ||
this.element = element; | ||
this.model = model; | ||
} | ||
update() { | ||
// implemented in concrete classes | ||
} | ||
onDestroy() { | ||
// implemented in concrete classes | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
import { IEventEmitter } from './interfaces'; | ||
export default function EventEmitter(): IEventEmitter; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import { forEach } from 'lodash'; | ||
export default function EventEmitter() { | ||
let listeners = []; | ||
/** | ||
* Removes all listeners | ||
*/ | ||
function unsubscribe() { | ||
listeners = []; | ||
} | ||
/** | ||
* Adds a new listener | ||
* @param {Function} listener | ||
* @return {Funcrion} function to unsubscribe | ||
*/ | ||
function subscribe(listener) { | ||
listeners.push(listener); | ||
return unsubscribe; | ||
} | ||
/** | ||
* Calls all listeners | ||
*/ | ||
function emit() { | ||
forEach(listeners, listener => listener()); | ||
} | ||
return { | ||
subscribe, | ||
unsubscribe, | ||
emit | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { IModel } from './interfaces'; | ||
/** | ||
* A factory to instantiate application model | ||
* @param {any = {}} data initial state | ||
* @return {IModel} | ||
*/ | ||
export default function Model(data?: any): IModel; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import EventEmitter from './EventEmitter'; | ||
import { get as _get, set as _set, assign } from 'lodash'; | ||
/** | ||
* A factory to instantiate application model | ||
* @param {any = {}} data initial state | ||
* @return {IModel} | ||
*/ | ||
export default function Model(data = {}) { | ||
let params = data; | ||
let emitter = EventEmitter(); | ||
let model = assign(emitter, { | ||
reset, | ||
get, | ||
set, | ||
getState | ||
}); | ||
/** | ||
* Gets a deep value from the current state | ||
* @param {string} path path to value | ||
* @param {any} defaultValue | ||
* @return {any} | ||
*/ | ||
function get(path, defaultValue) { | ||
return _get(params, path, defaultValue); | ||
} | ||
/** | ||
* Sets a deep value to the current state. Triggers application update. | ||
* @param {string} path path to value | ||
* @param {any} value | ||
* @return {IModel} | ||
*/ | ||
function set(path, value) { | ||
_set(params, path, value); | ||
model.emit(); | ||
return model; | ||
} | ||
/** | ||
* Resets the whole state. Triggers application update. | ||
* @param {any} data new state | ||
* @return {IModel} | ||
*/ | ||
function reset(data) { | ||
params = data; | ||
model.emit(); | ||
return model; | ||
} | ||
/** | ||
* Returns current state | ||
* @return {any} | ||
*/ | ||
function getState() { | ||
return params; | ||
} | ||
return model; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
import { IRegistryConfigurator } from './interfaces'; | ||
export default function RegistryConfigurator(): IRegistryConfigurator; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
export default function RegistryConfigurator() { | ||
let registry = { | ||
components: {} | ||
}; | ||
let configurator = { | ||
setModel, | ||
registerComponent, | ||
getRegistry | ||
}; | ||
function setModel(model) { | ||
registry.model = model; | ||
return configurator; | ||
} | ||
function registerComponent(selector, component) { | ||
registry.components[selector] = component; | ||
return configurator; | ||
} | ||
function getRegistry() { | ||
return registry; | ||
} | ||
return configurator; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
import { IRegistryConfigurator, IBootstrapper, IAppSetup } from './interfaces'; | ||
export default function Setup(configurator: IRegistryConfigurator, bootstrapper: IBootstrapper): IAppSetup; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
export default function Setup(configurator, bootstrapper) { | ||
let setup = { | ||
model, | ||
component, | ||
bootstrap | ||
}; | ||
function model(instance) { | ||
configurator.setModel(instance); | ||
return setup; | ||
} | ||
function component(selector, component) { | ||
configurator.registerComponent(selector, component); | ||
return setup; | ||
} | ||
function bootstrap(root) { | ||
let registry = configurator.getRegistry(); | ||
return bootstrapper.bootstrap(registry, root); | ||
} | ||
return setup; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
import App from './App'; | ||
import Component from './Component'; | ||
import Model from './Model'; | ||
export { App, Component, Model }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
import App from './App'; | ||
import Component from './Component'; | ||
import Model from './Model'; | ||
export { App, Component, Model }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
export interface IUnsubscriptionFn { | ||
(): void; | ||
} | ||
export interface IEventEmitter { | ||
/** | ||
* Adds a new listener | ||
* @param {Function} listener | ||
* @return {IUnsubscriptionFn} a function to unsubscribe from changes | ||
*/ | ||
subscribe(listener: Function): IUnsubscriptionFn; | ||
/** | ||
* A function to unsubscribe from changes | ||
* @type {IUnsubscriptionFn} | ||
*/ | ||
unsubscribe: IUnsubscriptionFn; | ||
/** | ||
* Calls all listeners | ||
*/ | ||
emit(): void; | ||
} | ||
export interface IModel extends IEventEmitter { | ||
/** | ||
* Gets a deep value from the current state | ||
* @param {string} path path to value | ||
* @param {any} defaultValue | ||
* @return {any} | ||
*/ | ||
get(path: string, defaultValue?: any): any; | ||
/** | ||
* Sets a deep value to the current state. Triggers application update. | ||
* @param {string} path path to value | ||
* @param {any} value | ||
* @return {IModel} | ||
*/ | ||
set(path: string, value: any): IModel; | ||
/** | ||
* Resets the whole state. Triggers application update. | ||
* @param {any} data new state | ||
* @return {IModel} | ||
*/ | ||
reset(data: any): IModel; | ||
/** | ||
* Returns current state | ||
* @return {any} | ||
*/ | ||
getState(): any; | ||
} | ||
export interface IComponent { | ||
model: IModel; | ||
element: Element; | ||
/** | ||
* Being called on every update of application state | ||
*/ | ||
update(): void; | ||
/** | ||
* Being called on application destroy. | ||
* Useful for removing all event listeners registered during component lifecycle. | ||
*/ | ||
onDestroy(): void; | ||
} | ||
export interface IComponentStatic { | ||
new (element: Element, model: IModel): IComponent; | ||
} | ||
export interface IDictionary<V> { | ||
[key: string]: V; | ||
} | ||
export interface IBootstrapRegistry { | ||
components: IDictionary<IComponentStatic>; | ||
model?: IModel; | ||
} | ||
export interface IRegistryConfigurator { | ||
registerComponent(selector: string, component: IComponentStatic): IRegistryConfigurator; | ||
setModel(model: IModel): IRegistryConfigurator; | ||
getRegistry(): IBootstrapRegistry; | ||
} | ||
export interface IAppSetup { | ||
/** | ||
* Registers an application model with initial state. | ||
* A model first need to be created by `Model` factory. | ||
* | ||
* ``` | ||
* import {App, Model} from 'athletic'; | ||
* | ||
* let state = {prop: 'value'}; | ||
* let model = Model(state); | ||
* | ||
* App().model(model) | ||
* ``` | ||
* @param {any} data initial state | ||
* @return {IAppSetup} | ||
*/ | ||
model(data: any): IAppSetup; | ||
/** | ||
* Registers a component constructor bound to a DOM selector. | ||
* @param {string} selector | ||
* @param {IComponentStatic} component | ||
* @return {IAppSetup} | ||
*/ | ||
component(selector: string, component: IComponentStatic): IAppSetup; | ||
/** | ||
* Bootstraps an application on a given element. | ||
* Returns a function which when being called unsubscribes from all state changes and calls `onDestroy` on each registered component instance. | ||
* @param {Element} root host element | ||
* @return {IDestroyFn} | ||
*/ | ||
bootstrap(root: Element): IDestroyFn; | ||
} | ||
export interface IBootstrapper { | ||
bootstrap(registry: IBootstrapRegistry, root: Element): IDestroyFn; | ||
} | ||
export interface IDestroyFn { | ||
/** | ||
* Removes all state listeners. | ||
* Calls `onDestroy` method on each registered component. | ||
*/ | ||
(): void; | ||
} |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export default function invariant(assertion: any, msg: string): void; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
export default function invariant(assertion, msg) { | ||
if (!assertion) | ||
throw new Error(msg); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export default function isModel(model: any): boolean; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import { isObject, isFunction, every } from 'lodash'; | ||
export default function isModel(model) { | ||
let object = isObject(model); | ||
let methods = every(['get', 'set', 'reset'], key => isFunction(model[key])); | ||
return object && methods; | ||
} |
Oops, something went wrong.