import { isPlainObject, toObject } from './flatTool'; import { canUseVirtualHost } from '../version'; const RawLifeCycles = ['Created', 'Attached', 'Ready', 'Moved', 'Detached', 'Error']; const NativeLifeCycles = RawLifeCycles.map((k) => k.toLowerCase()); const ComponentNativeProps = [ 'properties', 'data', 'observers', 'methods', 'behaviors', ...NativeLifeCycles, 'relations', 'externalClasses', 'options', 'lifetimes', 'pageLifeTimes', 'definitionFilter', ]; export const toComponent = function toComponent(options) { const { relations, behaviors = [], externalClasses = [] } = options; if (options.properties) { Object.keys(options.properties).forEach((k) => { let opt = options.properties[k]; if (!isPlainObject(opt)) { opt = { type: opt }; } options.properties[k] = opt; }); const ariaProps = [ { key: 'ariaHidden', type: Boolean }, { key: 'ariaRole', type: String }, { key: 'ariaLabel', type: String }, { key: 'ariaLabelledby', type: String }, { key: 'ariaDescribedby', type: String }, { key: 'ariaBusy', type: Boolean }, ]; ariaProps.forEach(({ key, type }) => { options.properties[key] = { type, }; }); options.properties.style = { type: String, value: '' }; options.properties.customStyle = { type: String, value: '' }; } if (!options.methods) options.methods = {}; if (!options.lifetimes) options.lifetimes = {}; const inits = {}; if (relations) { const getRelations = (relation, path) => Behavior({ created() { Object.defineProperty(this, `$${relation}`, { get: () => { const nodes = this.getRelationNodes(path) || []; return relation === 'parent' ? nodes[0] : nodes; }, }); }, }); const map = {}; Object.keys(relations).forEach((path) => { const comp = relations[path]; const relation = ['parent', 'ancestor'].includes(comp.type) ? 'parent' : 'children'; const mixin = getRelations(relation, path); map[relation] = mixin; }); behaviors.push(...Object.keys(map).map((key) => map[key])); } options.behaviors = [...behaviors]; options.externalClasses = ['class', ...externalClasses]; Object.getOwnPropertyNames(options).forEach((k) => { const desc = Object.getOwnPropertyDescriptor(options, k); if (!desc) return; if (NativeLifeCycles.indexOf(k) < 0 && typeof desc.value === 'function') { Object.defineProperty(options.methods, k, desc); delete options[k]; } else if (ComponentNativeProps.indexOf(k) < 0) { inits[k] = desc; } else if (NativeLifeCycles.indexOf(k) >= 0) { options.lifetimes[k] = options[k]; } }); if (Object.keys(inits).length) { const oldCreated = options.lifetimes.created; const oldAttached = options.lifetimes.attached; const { controlledProps = [] } = options; options.lifetimes.created = function (...args) { Object.defineProperties(this, inits); if (oldCreated) oldCreated.apply(this, args); }; options.lifetimes.attached = function (...args) { if (oldAttached) oldAttached.apply(this, args); controlledProps.forEach(({ key }) => { const defaultKey = `default${key.replace(/^(\w)/, (m, m1) => m1.toUpperCase())}`; const props = this.properties; if (props[key] == null) { this._selfControlled = true; } if (props[key] == null && props[defaultKey] != null) { this.setData({ [key]: props[defaultKey], }); } }); }; options.methods._trigger = function (evtName, detail, opts) { const target = controlledProps.find((item) => item.event == evtName); if (target) { const { key } = target; if (this._selfControlled) { this.setData({ [key]: detail[key], }); } } this.triggerEvent(evtName, detail, opts); }; } return options; }; export const wxComponent = function wxComponent() { return function (constructor) { class WxComponent extends constructor { } const current = new WxComponent(); current.options = current.options || {}; if (canUseVirtualHost()) { current.options.virtualHost = true; } const obj = toComponent(toObject(current)); Component(obj); }; };