riot
Version:
Simple and elegant component-based UI library
110 lines (99 loc) • 4.09 kB
JavaScript
/* Riot v10.1.2, @license MIT */
import { IS_PURE_SYMBOL, COMPONENTS_IMPLEMENTATION_MAP } from '../dependencies/@riotjs/util/constants.js';
import { camelToDashCase } from '../dependencies/@riotjs/util/strings.js';
import { callOrAssign } from '../dependencies/@riotjs/util/functions.js';
import { memoize } from '../dependencies/@riotjs/util/misc.js';
import { MOCKED_TEMPLATE_INTERFACE } from './mocked-template-interface.js';
import { componentTemplateFactory } from './component-template-factory.js';
import { createPureComponent } from './create-pure-component.js';
import { instantiateComponent } from './instantiate-component.js';
/**
* Create the subcomponents that can be included inside a tag in runtime
* @param {object} components - components imported in runtime
* @returns {object} all the components transformed into Riot.Component factory functions
*/
function createChildrenComponentsObject(components = {}) {
return Object.entries(callOrAssign(components)).reduce(
(acc, [key, value]) => {
acc[camelToDashCase(key)] = createComponentFromWrapper(value);
return acc
},
{},
)
}
/**
* Create the getter function to render the child components
* @param {RiotComponentWrapper} componentWrapper - riot compiler generated object
* @returns {Function} function returning the component factory function
*/
const createChildComponentGetter = (componentWrapper) => {
const childrenComponents = createChildrenComponentsObject(
componentWrapper.exports ? componentWrapper.exports.components : {},
);
return (name) => {
// improve support for recursive components
if (name === componentWrapper.name)
return memoizedCreateComponentFromWrapper(componentWrapper)
// return the registered components
return childrenComponents[name] || COMPONENTS_IMPLEMENTATION_MAP.get(name)
}
};
/**
* Performance optimization for the recursive components
* @param {RiotComponentWrapper} componentWrapper - riot compiler generated object
* @returns {object} component like interface
*/
const memoizedCreateComponentFromWrapper = memoize(createComponentFromWrapper);
/**
* Create the component interface needed for the @riotjs/dom-bindings tag bindings
* @param {RiotComponentWrapper} componentWrapper - riot compiler generated object
* @param {string} componentWrapper.css - component css
* @param {Function} componentWrapper.template - function that will return the dom-bindings template function
* @param {object} componentWrapper.exports - component interface
* @param {string} componentWrapper.name - component name
* @returns {object} component like interface
*/
function createComponentFromWrapper(componentWrapper) {
const { css, template, exports: exports$1, name } = componentWrapper;
const templateFn = template
? componentTemplateFactory(
template,
componentWrapper,
createChildComponentGetter(componentWrapper),
)
: MOCKED_TEMPLATE_INTERFACE;
return ({ slots, attributes, props }) => {
// pure components rendering will be managed by the end user
if (exports$1 && exports$1[IS_PURE_SYMBOL])
return createPureComponent(exports$1, {
slots,
attributes,
props,
css,
template,
})
const componentAPI = callOrAssign(exports$1) || {};
const component = instantiateComponent({
css,
template: templateFn,
componentAPI,
name,
})({ slots, attributes, props });
// notice that for the components created via tag binding
// we need to invert the mount (state/parentScope) arguments
// the template bindings will only forward the parentScope updates
// and never deal with the component state
return {
mount(element, parentScope, state) {
return component.mount(element, state, parentScope)
},
update(parentScope, state) {
return component.update(state, parentScope)
},
unmount(preserveRoot) {
return component.unmount(preserveRoot)
},
}
}
}
export { createComponentFromWrapper };