/* Riot WIP, @license MIT */ import { autobindMethods, defineProperties, isObject, defineProperty, IS_PURE_SYMBOL, PARENT_KEY_SYMBOL, ATTRIBUTES_KEY_SYMBOL, PROPS_KEY, evaluateAttributeExpressions, STATE_KEY, TEMPLATE_KEY_SYMBOL, ROOT_KEY, SLOTS_KEY, ON_BEFORE_MOUNT_KEY, ON_MOUNTED_KEY, SHOULD_UPDATE_KEY, ON_BEFORE_UPDATE_KEY, IS_COMPONENT_UPDATING, ON_UPDATED_KEY, ON_BEFORE_UNMOUNT_KEY, ON_UNMOUNTED_KEY, isFunction } from '@riotjs/util'; import { addCssHook } from './add-css-hook.js'; import { bindDOMNodeToComponentInstance } from './bind-dom-node-to-component-instance.js'; import { computeComponentState } from './compute-component-state.js'; import { computeInitialProps } from './compute-initial-props.js'; import { createAttributeBindings } from './create-attribute-bindings.js'; import { runPlugins } from './run-plugins.js'; /** * Component creation factory function that will enhance the user provided API * @param {Object} component - a component implementation previously defined * @param {Array} options.slots - component slots generated via riot compiler * @param {Array} options.attributes - attribute expressions generated via riot compiler * @returns {Riot.Component} a riot component instance */ function manageComponentLifecycle(component, _ref) { let { slots, attributes, props } = _ref; return autobindMethods(runPlugins(defineProperties(isObject(component) ? Object.create(component) : component, { mount(element, state, parentScope) { if (state === void 0) { state = {}; } // any element mounted passing through this function can't be a pure component defineProperty(element, IS_PURE_SYMBOL, false); this[PARENT_KEY_SYMBOL] = parentScope; this[ATTRIBUTES_KEY_SYMBOL] = createAttributeBindings(element, attributes).mount(parentScope); defineProperty(this, PROPS_KEY, Object.freeze(Object.assign({}, computeInitialProps(element, props), evaluateAttributeExpressions(this[ATTRIBUTES_KEY_SYMBOL].expressions)))); this[STATE_KEY] = computeComponentState(this[STATE_KEY], state); this[TEMPLATE_KEY_SYMBOL] = this.template.createDOM(element).clone(); // link this object to the DOM node bindDOMNodeToComponentInstance(element, this); // add eventually the 'is' attribute component.name && addCssHook(element, component.name); // define the root element defineProperty(this, ROOT_KEY, element); // define the slots array defineProperty(this, SLOTS_KEY, slots); // before mount lifecycle event this[ON_BEFORE_MOUNT_KEY](this[PROPS_KEY], this[STATE_KEY]); // mount the template this[TEMPLATE_KEY_SYMBOL].mount(element, this, parentScope); this[ON_MOUNTED_KEY](this[PROPS_KEY], this[STATE_KEY]); return this; }, update(state, parentScope) { if (state === void 0) { state = {}; } if (parentScope) { this[PARENT_KEY_SYMBOL] = parentScope; this[ATTRIBUTES_KEY_SYMBOL].update(parentScope); } const newProps = evaluateAttributeExpressions(this[ATTRIBUTES_KEY_SYMBOL].expressions); if (this[SHOULD_UPDATE_KEY](newProps, this[PROPS_KEY]) === false) return; defineProperty(this, PROPS_KEY, Object.freeze(Object.assign({}, this[PROPS_KEY], newProps))); this[STATE_KEY] = computeComponentState(this[STATE_KEY], state); this[ON_BEFORE_UPDATE_KEY](this[PROPS_KEY], this[STATE_KEY]); // avoiding recursive updates // see also https://github.com/riot/riot/issues/2895 if (!this[IS_COMPONENT_UPDATING]) { this[IS_COMPONENT_UPDATING] = true; this[TEMPLATE_KEY_SYMBOL].update(this, this[PARENT_KEY_SYMBOL]); } this[ON_UPDATED_KEY](this[PROPS_KEY], this[STATE_KEY]); this[IS_COMPONENT_UPDATING] = false; return this; }, unmount(preserveRoot) { this[ON_BEFORE_UNMOUNT_KEY](this[PROPS_KEY], this[STATE_KEY]); this[ATTRIBUTES_KEY_SYMBOL].unmount(); // if the preserveRoot is null the template html will be left untouched // in that case the DOM cleanup will happen differently from a parent node this[TEMPLATE_KEY_SYMBOL].unmount(this, this[PARENT_KEY_SYMBOL], preserveRoot === null ? null : !preserveRoot); this[ON_UNMOUNTED_KEY](this[PROPS_KEY], this[STATE_KEY]); return this; } })), Object.keys(component).filter(prop => isFunction(component[prop]))); } export { manageComponentLifecycle };