From 31edf748a4884e36747aae8024abfe2783d802bd Mon Sep 17 00:00:00 2001 From: sunhao Date: Tue, 9 Jul 2024 08:45:03 +0800 Subject: [PATCH] * core: support for creating multiple component instances with zui-create. --- lib/core/src/helpers/zui-creator.ts | 102 +++++++++++++++++++++++----- 1 file changed, 85 insertions(+), 17 deletions(-) diff --git a/lib/core/src/helpers/zui-creator.ts b/lib/core/src/helpers/zui-creator.ts index 434e0a6699..3d83fc5ce8 100644 --- a/lib/core/src/helpers/zui-creator.ts +++ b/lib/core/src/helpers/zui-creator.ts @@ -2,7 +2,10 @@ import {$, Cash, Selector} from '../cash'; import {takeData} from './data'; import {getZData} from './z'; -type ZUIComponentOptions = Record; +type ZUIComponentOptions = { + $update?: boolean; + [x: string]: unknown; +}; declare class ZUIComponentClass { gid: number; @@ -39,18 +42,30 @@ export function getComponent(name?: string): ZUIComponent | undefined { return name ? componentsMap.get(name.toLowerCase()) : undefined; } -export function create(name: string, element: HTMLElement, options: ZUIComponentOptions) { +export function create(name: string, element: HTMLElement, options: ZUIComponentOptions = {}) { const Component = getComponent(name); if (!Component) { return null; } - if (!Component.MULTI_INSTANCE && Component.get(element)) { - console.error(`[ZUI] cannot create component "${name}" on element which already has a component instance.`, {element, options}); - return null; + if (!Component.MULTI_INSTANCE) { + const component = Component.get(element); + if (component) { + if (options.$update) { + delete options.$update; + component.render(options); + } else { + console.warn(`[ZUI] cannot create component "${name}" on element which already has a component instance.`, {element, options}); + } + return component; + } } return new Component(element, options); } +function createInAnimationFrame(name: string, element: HTMLElement, options: ZUIComponentOptions = {}) { + requestAnimationFrame(() => create(name, element, options)); +} + export function defineFn(name?: string) { if (name) { const Component = getComponent(name); @@ -74,21 +89,74 @@ declare module 'cash-dom' { } } +/** + * Create zui component instance from elements which match [zui-create], [data-zui], [data-zui] is deprecated, use [zui-create] instead. + * 为匹配 [zui-create], [data-zui] 的元素创建 zui 组件实例,[data-zui] 被弃用,优先使用 [zui-create]。 + * + * @param element - The element to create zui components. 要创建 zui 组件的元素。 + * @param options - The options. 选项。 + * + * @example + * ```html + *
+ * + * Create multiple components + * + *
Deprecated usage
+ * ``` + */ +function initCreators(element: HTMLElement, options: {update?: boolean} = {}): void { + const $element = $(element); + let createNames = $element.attr('zui-create'); + const $update = options.update; + const defaultCreateOptions = { + $update, + $optionsFromDataset: false, + }; + if (typeof createNames === 'string') { + createNames = createNames.trim(); + const names = createNames.length ? createNames.split(',').map((name) => name.trim()) : []; + const createOptionsMap = getZData(element, {prefix: 'zui-create-', evalValue: true})!; + const createOptionsNames = Object.keys(createOptionsMap); + if (!createOptionsNames.length && names.length === 1) { + createInAnimationFrame(names[0], element, { + ...defaultCreateOptions, + ...$element.dataset(), + }); + } else { + const initedNames = new Set(); + [...names, ...createOptionsNames].forEach(name => { + if (initedNames.has(name)) { + return; + } + const createOptions = createOptionsMap[name] as ZUIComponentOptions | undefined; + createInAnimationFrame(name, element, { + ...defaultCreateOptions, + ...createOptions, + }); + delete createOptionsMap[name]; + initedNames.add(name); + }); + } + } else { + const initOptions = $element.dataset(); + const name = initOptions?.zui as string; + if (!name) { + return; + } + console.warn('[ZUI] create component instance with [data-zui] is deprecated, use [zui-create] instead.', {element, options}); + delete initOptions!.zui; + createInAnimationFrame(name, element, { + ...defaultCreateOptions, + ...initOptions, + }); + } +} + /** Define the $.fn.zuiInit method. */ $.fn.zuiInit = function (this: Cash) { this.find('[zui-create],[data-zui]').each(function () { - const $element = $(this); - let options = getZData($element, 'data-')!; - const [name, optionsName] = ((options.zui || $element.attr('zui-create')) as string).split(':'); - if ($element.zui(name)) { - return; - } - if (optionsName) { - options = $.share[optionsName] as Record; - } else { - delete options.zui; - } - requestAnimationFrame(() => create(name, this, options)); + initCreators(this); }); this.find('[zui-init]').each(function () { const $element = $(this);