Skip to content

Commit

Permalink
#14: add comments and a memorize helper so the logic can be framework…
Browse files Browse the repository at this point in the history
… agnostic later
  • Loading branch information
Leo Y. Li committed Apr 18, 2019
1 parent c333db5 commit b485b30
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 33 deletions.
4 changes: 4 additions & 0 deletions addons/addon-contexts/src/@types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ export declare type MergeSettings = (
...args: [Partial<AddonSetting>, Partial<AddonSetting>]
) => ContextNode;
export declare type GetNodes = (settings: WrapperSettings) => ContextNode[];
export declare type Memorize = <T, U extends any[]>(
fn: (...arg: U) => T,
resolver: (...arg: U) => unknown
) => (...arg: U) => T;
export declare type UseChannel = (
event: string,
eventHandler: GenericFnWithReturn<void>,
Expand Down
103 changes: 70 additions & 33 deletions addons/addon-contexts/src/libs/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,39 +1,31 @@
import { createElement as h } from 'react';
import {
Memorize,
GetNodes,
MergeSettings,
RenderAggregatedComponents,
RenderAggregatedContexts,
} from '../@types';

export const renderAggregatedComponents: RenderAggregatedComponents = (
components,
props,
options,
last
) => (next: Function) =>
// when set to disable
options.disable ||
// when opt-out context
props === null ||
// when get uninitialized props but set to non-cancelable (i.e. props is required)
(props === undefined && !options.cancelable)
? next()
: components
.reverse()
.reduce(
(acc, C, index) => h(C, options.deep || index === last ? props : null, acc),
next()
);

export const renderAggregatedContexts: RenderAggregatedContexts = (nodes, propsTree) => (next) =>
nodes
.map(({ nodeId, components = [], options = {} }) =>
renderAggregatedComponents(components, propsTree[nodeId], options, components.length - 1)
)
.reduce((acc, agg) => agg(() => acc), next());
/**
* @private
* Memorizes the calculated result of a function by an ES6 Map;
* @return the memorized version of a function.
*/
export const _memorize: Memorize = (fn, resolver) => {
const memo = new Map();
return (...arg) => {
const key = resolver && resolver(...arg) || arg[0];
return memo.get(key) || memo.set(key, fn(...arg)).get(key);
};
};

export const mergeSettings: MergeSettings = (
/**
* @private
* Merges the global (options) and the local (parameters) from a pair of setting;
* @return the normalized definition for a contextual environment (-> node).
*/
export const _mergeSettings: MergeSettings = (
{ icon = '', title = '', components = [], params = [], options = {} },
{ params: storyParams = [], options: storyOptions = {} }
) => ({
Expand All @@ -55,14 +47,59 @@ export const mergeSettings: MergeSettings = (
),
});

export const getNodes: GetNodes = ({ options = [], parameters = [] }) => {
const titleSet = new Set(options.concat(parameters).map(({ title }) => title));
return Array.from(titleSet)
/**
* @private
* pairs up settings for merging normalizations to produce the contextual definitions (-> nodes);
* it guarantee the adding order can be respected but not duplicated.
*/
export const _getNodes: GetNodes = ({ options, parameters }) => {
const titles = Array()
.concat(options, parameters)
.map(({ title } = {}) => title);
return [...new Set(titles)]
.filter(Boolean)
.map((title) =>
mergeSettings(
options.find((option) => option.title === title) || {},
parameters.find((param) => param.title === title) || {}
_mergeSettings(
(options && options.find((option) => option.title === title)) || {},
(parameters && parameters.find((param) => param.title === title)) || {}
)
);
};

/**
* @private
* Aggregates components with activated props in a descending order,
* based on the given options in the contextual environment setup.
*/
export const _renderAggregatedComponents: RenderAggregatedComponents = (
components,
props,
options,
last
) => (next) =>
// when set to disable
options.disable ||
// when opt-out context
props === null ||
// when get uninitialized props but set to non-cancelable (i.e. props is required)
(props === undefined && !options.cancelable)
? next()
: components
.reverse()
.reduce(
(acc, C, index) => h(C, options.deep || index === last ? props : null, acc),
next()
);

/**
* Aggregate aggregated-components among all contextual nodes in a descending order.
*/
export const renderAggregatedContexts: RenderAggregatedContexts = (nodes, propsTree) => (next) =>
nodes
.map(({ nodeId, components = [], options = {} }) =>
_renderAggregatedComponents(components, propsTree[nodeId], options, components.length - 1)
)
.reduce((acc, agg) => agg(() => acc), next());


export const getNodes = _memorize(_getNodes, ({ parameters }) => parameters);

0 comments on commit b485b30

Please sign in to comment.