UNPKG

@v4fire/client

Version:

V4Fire client core library

443 lines (340 loc) • 9.03 kB
/*! * V4Fire Client Core * https://github.com/V4Fire/Client * * Released under the MIT license * https://github.com/V4Fire/Client/blob/master/LICENSE */ import symbolGenerator from 'core/symbol'; import type { VNode, VNodeData, VNodeDirective, DirectiveOptions } from 'vue'; import config from 'core/component/engines/zero/config'; import { document } from 'core/component/engines/zero/const'; import { eventModifiers, eventModifiersRgxp, SVG_NMS, XLINK_NMS } from 'core/component/engines/zero/helpers/const'; import type { ComponentInterface } from 'core/component/interface'; import type { DirElement, DocumentFragmentP } from 'core/component/engines/zero/helpers/interface'; export * from 'core/component/engines/zero/helpers/const'; export * from 'core/component/engines/zero/helpers/interface'; export const $$ = symbolGenerator(); export function addToRefs(el: Element, data: Nullable<VNodeData>, refs: Nullable<Dictionary>): void { if (data == null) { return; } const {ref} = data; if (ref == null || ref === '' || refs == null) { return; } if (data.refInFor === true) { const arr = <Element[]>(refs[ref] ?? []); refs[ref] = arr; arr.push(el); } else { refs[ref] = el; } } export function createSVGChildren(ctx: ComponentInterface, children: Nullable<Element[]>): SVGElement[] { if (children == null || children.length === 0) { return []; } const res = <ReturnType<typeof createSVGChildren>>[]; for (let i = 0; i < children.length; i++) { const el = children[i], node = document.createElementNS(SVG_NMS, el.tagName.toLowerCase()), data = el[$$.data]; if (data != null) { const dirs = el[$$.directives]; addDirectives(ctx, node, data, dirs); addStaticDirectives(ctx, data, dirs, node); const // @ts-ignore (access) refs = ctx.$refs; addToRefs(el, data, refs); addStyles(node, el[$$.styles]); addAttrs(node, el[$$.attrs]); attachEvents(node, el[$$.events]); if (Object.isTruly(el.className)) { node.setAttributeNS(null, 'class', el.className); } res.push(node); } else { res.push(<SVGElement>children[i]); } if (Object.size(el.children) > 0) { appendChild(node, createSVGChildren(ctx, Array.from(el.children))); } } return res; } export function addProps(el: DirElement, props?: Dictionary): void { if (!props) { return; } el[$$.props] = props; for (let keys = Object.keys(props), i = 0; i < keys.length; i++) { const key = keys[i]; el[key] = props[key]; } } export function addAttrs(el: DirElement, attrs?: Dictionary<string>): void { if (!attrs) { return; } el[$$.attrs] = attrs; for (let keys = Object.keys(attrs), i = 0; i < keys.length; i++) { const key = keys[i], val = attrs[key]; if (val != null) { if (el instanceof SVGElement) { el.setAttributeNS(key.split(':', 1)[0] === 'xlink' ? XLINK_NMS : null, key, val); } else { el.setAttribute(key, val); } } } } export function addStyles(el: DirElement, styles?: CanArray<Nullable<string | object>>): void { const normalizedStyles = Array.concat([], styles); if (normalizedStyles.length === 0) { return; } el[$$.styles] = normalizedStyles; const strStyles = <string[]>[]; for (let i = 0; i < normalizedStyles.length; i++) { const styles = normalizedStyles[i]; if (!Object.isTruly(styles)) { continue; } if (Object.isString(styles)) { strStyles.push(styles); continue; } let str = ''; for (let keys = Object.keys(styles), i = 0; i < keys.length; i++) { const key = keys[i], el = styles[key]; if (el != null && el !== '') { str += `${key.dasherize()}: ${el};`; } } strStyles.push(str); } el.setAttribute('style', strStyles.join(';')); } export function createTemplate(): DocumentFragmentP { const el = document.createDocumentFragment(), attrs = {}; el['getAttribute'] = (key) => attrs[key]; el['setAttribute'] = (key, val) => attrs[key] = val; return Object.cast(el); } export function addClass(el: Element, opts: VNodeData): void { const className = Array .concat([], el.getAttribute('class') ?? '', opts.staticClass ?? '', ...Array.concat([], opts.class)) .join(' ') .trim(); if (className.length > 0) { if (el instanceof SVGElement) { el.setAttributeNS(null, 'class', className); } else { el.setAttribute('class', className); } } } export function attachEvents(el: Node, events?: Dictionary<CanArray<Function>>): void { if (events == null) { return; } for (let keys = Object.keys(events), i = 0; i < keys.length; i++) { const key = keys[i], mods = eventModifiersRgxp.exec(key), handlers = Array.concat([], events[key]), flags = {}; if (mods) { for (let o = mods[0], i = 0; i < o.length; i++) { flags[eventModifiers[o[i]]] = true; } } for (let i = 0; i < handlers.length; i++) { const fn = handlers[i]; if (Object.isFunction(fn)) { const event = key.replace(eventModifiersRgxp, ''), cache = el[$$.events] ?? {}; el[$$.events] = cache; cache[event] = {fn, flags}; el.addEventListener(event, fn, flags); } } } } export function appendChild(parent: Nullable<Node>, node: Nullable<CanArray<Node>>): void { if (parent == null || node == null) { return; } if (Object.isArray(node) || node instanceof HTMLCollection) { for (let i = 0; i < node.length; i++) { const el = node[i]; // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (el != null) { appendChild(parent, el); } } } else { parent.appendChild(node); } } export function warn(message: string, vm: object): void { // eslint-disable-next-line @typescript-eslint/unbound-method if (Object.isFunction(config.warnHandler)) { config.warnHandler.call(null, message, vm); } else if (typeof console !== 'undefined' && Object.isFunction(console.error) && !config.silent) { console.error(`[Vue warn]: ${message}`); } } export function addStaticDirectives( component: ComponentInterface, data: VNodeData, directives?: VNodeDirective[], node?: DirElement ): void { if (directives == null) { return; } const store = component.$options.directives; if (store == null) { return; } if (node != null) { node[$$.directives] = directives; } for (let o = directives, i = 0; i < o.length; i++) { const dir = o[i]; switch (dir.name) { case 'show': if (!Object.isTruly(dir.value)) { const rule = ';display: none;'; if (node != null && data.tag === 'component') { // eslint-disable-next-line @typescript-eslint/restrict-plus-operands node.setAttribute('style', (node.getAttribute('style') ?? '') + rule); } else { data.attrs = data.attrs ?? {}; // eslint-disable-next-line @typescript-eslint/restrict-plus-operands data.attrs.style = (data.attrs.style ?? '') + rule; } } break; case 'model': data.domProps = data.domProps ?? {}; data.domProps.value = dir.value; break; default: // Do nothing } } } export function addDirectives( component: ComponentInterface, node: DirElement, data: VNodeData, directives?: VNodeDirective[] ): void { if (directives == null) { return; } const { unsafe: {$async: $a}, $options: {directives: store} } = component; if (store == null) { return; } node[$$.directives] = directives; const root = component.$root.unsafe; for (let o = directives, i = 0; i < o.length; i++) { const dir = o[i], dirParams = store[dir.name]; // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (dirParams == null) { continue; } const vnode = Object.create(node); vnode.context = component; if (Object.isFunction(dirParams)) { dirParams(Object.cast(node), Object.cast(dir), vnode, Object.cast(undefined)); } else { if (dirParams.bind) { dirParams.bind.call(undefined, node, dir, vnode); } if (dirParams.inserted) { const events = [ 'on-component-hook:mounted', `child-component-mounted:${component.componentId}` ]; dispatchHookFromEvents('inserted', events, dir, dirParams, vnode); } if (dirParams.unbind) { const events = [ 'on-component-hook:beforeDestroy', `child-component-destroyed:${component.componentId}` ]; dispatchHookFromEvents('unbind', events, dir, dirParams, vnode); } } } function dispatchHookFromEvents( hook: string, events: string[], dir: VNodeDirective, dirParams: DirectiveOptions, vnode: VNode ): void { const clear = () => { for (let i = 0; i < events.length; i++) { // eslint-disable-next-line @typescript-eslint/no-use-before-define root.$off(events[i], cb); } }; const cb = () => { clear(); const fn = dirParams[hook]; if (Object.isFunction(fn)) { return fn(node, dir, vnode); } }; for (let i = 0; i < events.length; i++) { root.$once(events[i], cb); } $a.worker(clear); } }