diff --git a/lib/core/src/helpers/global-event.ts b/lib/core/src/helpers/global-event.ts
index f9c3a2bbdd..4950acf719 100644
--- a/lib/core/src/helpers/global-event.ts
+++ b/lib/core/src/helpers/global-event.ts
@@ -1,26 +1,27 @@
-import {$, Cash} from '@zui/core';
+import {$, Cash} from '../cash';
+import {evalValue} from './raw-data';
+import {deepGet} from '@zui/helpers/src/object/deep-get';
+import {getZData} from './z';
export type GlobalEventOptions = {
on: string;
- selector: string;
+ selector?: string;
prevent?: boolean;
stop?: boolean;
- call?: string;
+ call?: string | ((...args: unknown[]) => void);
self?: boolean;
- params?: unknown[];
+ params?: unknown[] | string;
once?: boolean;
- if?: string;
- do?: string;
+ if?: string | ((...args: unknown[]) => boolean);
+ do?: string | ((...args: unknown[]) => void);
[option: string]: unknown;
};
-function handleGlobalEvent(this: Cash, event: Event) {
- const $this = $(this);
- const options = $this.dataset()!;
+function processGlobalEvent($element: Cash, event: Event, options: GlobalEventOptions) {
if (!(options.on as string || 'click').split(' ').includes(event.type)) {
return;
}
- const $target = options.selector ? $(event.target as HTMLElement).closest(options.selector as string) : $this;
+ const $target = options.selector ? $(event.target as HTMLElement).closest(options.selector as string) : $element;
if (!$target.length) {
return;
}
@@ -40,7 +41,7 @@ function handleGlobalEvent(this: Cash, event: Event) {
if (options.onceCalled) {
return;
}
- $this.dataset('once-called', true);
+ $element.dataset('once-called', true);
}
if (parseBool(options.prevent)) {
event.preventDefault();
@@ -52,46 +53,99 @@ function handleGlobalEvent(this: Cash, event: Event) {
return;
}
- const runParams: [string, unknown][] = [['$element', $this], ['event', event], ['options', options], ['$target', $target]];
- if (options.if && !$.runJS(options.if as string, ...runParams)) {
+ const runParams: [string, unknown][] = [['$element', $element], ['event', event], ['options', options], ['$target', $target]];
+ const runCode = (code: string | ((...args: unknown[]) => boolean)) => {
+ if (typeof code === 'function') {
+ return code(...runParams);
+ }
+ return $.runJS(code as string, ...runParams);
+ };
+ if (options.if !== undefined && !runCode(options.if)) {
return;
}
- const call = options.call as string;
+ const call = options.call;
if (call) {
- let callback = window[call as keyof Window];
- const isFuncName = /^[$A-Z_][0-9A-Z_$.]*$/i.test(call as string);
- if (!callback) {
- callback = $.runJS(call as string, ...runParams);
- }
- if (!isFuncName || !$.isFunction(callback)) {
- return;
+ let callback: unknown;
+ if (typeof call === 'string') {
+ const isFuncName = /^[$A-Z_][0-9A-Z_$.]*$/i.test(call);
+ callback = isFuncName ? deepGet(window, call) : runCode(call);
+ } else {
+ callback = call;
}
- const params: unknown[] = [];
- const paramsOption = options.params;
- options.params = params;
- if (typeof paramsOption === 'string' && paramsOption.length) {
- if (paramsOption[0] === '[') {
- params.push(...(parseVal(paramsOption) as unknown[]));
+ if (typeof callback === 'function') {
+ const params: unknown[] = [];
+ const paramsOption = options.params;
+ options.params = params;
+ if (typeof paramsOption === 'string' && paramsOption.length) {
+ if (paramsOption[0] === '[') {
+ params.push(...(parseVal(paramsOption) as unknown[]));
+ } else {
+ params.push(...paramsOption.split(', ').map(x => {
+ x = x.trim();
+ if (x === '$element') return $element;
+ if (x === 'event') return event;
+ if (x === 'options') return options;
+ if (x.startsWith('$element.') || x.startsWith('event.') || x.startsWith('options.')) return runCode(x);
+ return parseVal(x);
+ }));
+ }
+ } else if (Array.isArray(paramsOption)) {
+ params.push(...paramsOption);
} else {
- params.push(...paramsOption.split(', ').map(x => {
- x = x.trim();
- if (x === '$element') return $this;
- if (x === 'event') return event;
- if (x === 'options') return options;
- if (x.startsWith('$element.') || x.startsWith('event.') || x.startsWith('options.')) return $.runJS(x, ...runParams);
- return parseVal(x);
- }));
+ params.push(paramsOption);
}
- } else {
- params.push(paramsOption);
+ callback(...params);
}
- callback(...params);
}
if (options.do) {
- $.runJS(options.do as string, ...runParams);
+ runCode(options.do as string);
}
}
-$(document).on('click.zui.global change.zui.global inited.zui.global', '[data-on]', handleGlobalEvent);
+/**
+ * Handle global event.
+ * @param event The event object
+ * @example
+ * ```html
+ * Click or changed
+ * Click or changed
+ * Click
+ * Click or changed
+ * Click or changed
+ *
+ * [data-on] is deprecated
+ * ```
+ */
+function handleGlobalEvent(this: Cash, event: Event) {
+ const $element = $(this);
+ const type = event.type;
+ const zuiOn = $element.attr('zui-on');
+ if (zuiOn) {
+ const [events, code] = zuiOn.split('~').map(x => x.trim());
+ if (events && code) {
+ processGlobalEvent($element, event, $.extend({
+ on: events,
+ }, code.startsWith('{') ? evalValue(code) : {do: code}));
+ }
+ }
+ const zuiOnEvent = $element.attr(`zui-on-${type}`);
+ if (zuiOnEvent) {
+ processGlobalEvent($element, event, $.extend({
+ on: type,
+ }, zuiOnEvent.startsWith('{') ? evalValue(zuiOnEvent) : {do: zuiOnEvent}));
+ }
+
+ const dataOn = $element.attr('data-on');
+ if (dataOn) {
+ processGlobalEvent($element, event, getZData($element, {prefix: 'data-', evalValue: ['call', 'if', 'do']}) as GlobalEventOptions);
+ console.warn(`[ZUI] Use [zui-on-${type}] instead of [data-on="${type}"] on element: `, $element[0]);
+ }
+}
+
+export function registerGlobalListener(events: string[]) {
+ $(document).off('.zui.global').on(events.map(event => `${event}.zui.global`).join(' '), `[zui-on],${events.map(x => `[zui-on-${x}]`)},[data-on]`, handleGlobalEvent);
+}
+
+registerGlobalListener(['click', 'change', 'inited']);