@dooboostore/simple-web-component 1.0.15 → 1.0.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +275 -91
- package/dist/cjs/decorators/addEventListener.js +2 -3
- package/dist/cjs/decorators/addEventListener.js.map +2 -2
- package/dist/cjs/decorators/elementDefine.js +161 -34
- package/dist/cjs/decorators/elementDefine.js.map +3 -3
- package/dist/cjs/decorators/emitCustomEvent.js +2 -14
- package/dist/cjs/decorators/emitCustomEvent.js.map +2 -2
- package/dist/cjs/decorators/query.js +47 -36
- package/dist/cjs/decorators/query.js.map +2 -2
- package/dist/cjs/decorators/queryAll.js +42 -36
- package/dist/cjs/decorators/queryAll.js.map +2 -2
- package/dist/cjs/elements/SwcAppEngine.js +2 -1
- package/dist/cjs/elements/SwcAppEngine.js.map +2 -2
- package/dist/cjs/elements/SwcAsync.js +188 -0
- package/dist/cjs/elements/SwcAsync.js.map +7 -0
- package/dist/cjs/elements/SwcChoose.js +155 -0
- package/dist/cjs/elements/SwcChoose.js.map +7 -0
- package/dist/cjs/elements/SwcIf.js +167 -0
- package/dist/cjs/elements/SwcIf.js.map +7 -0
- package/dist/cjs/elements/SwcLoop.js +246 -0
- package/dist/cjs/elements/SwcLoop.js.map +7 -0
- package/dist/cjs/elements/SwcSubTemplates.js +65 -0
- package/dist/cjs/elements/SwcSubTemplates.js.map +7 -0
- package/dist/cjs/elements/index.js +7 -0
- package/dist/cjs/elements/index.js.map +2 -2
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/index.js.map +2 -2
- package/dist/cjs/types.js +1 -0
- package/dist/cjs/types.js.map +2 -2
- package/dist/cjs/utils/Utils.js +92 -162
- package/dist/cjs/utils/Utils.js.map +3 -3
- package/dist/esm/decorators/addEventListener.js +2 -3
- package/dist/esm/decorators/addEventListener.js.map +2 -2
- package/dist/esm/decorators/elementDefine.js +161 -34
- package/dist/esm/decorators/elementDefine.js.map +3 -3
- package/dist/esm/decorators/emitCustomEvent.js +2 -14
- package/dist/esm/decorators/emitCustomEvent.js.map +2 -2
- package/dist/esm/decorators/query.js +47 -36
- package/dist/esm/decorators/query.js.map +2 -2
- package/dist/esm/decorators/queryAll.js +42 -36
- package/dist/esm/decorators/queryAll.js.map +2 -2
- package/dist/esm/elements/SwcAppEngine.js +2 -1
- package/dist/esm/elements/SwcAppEngine.js.map +2 -2
- package/dist/esm/elements/SwcAsync.js +169 -0
- package/dist/esm/elements/SwcAsync.js.map +7 -0
- package/dist/esm/elements/SwcChoose.js +136 -0
- package/dist/esm/elements/SwcChoose.js.map +7 -0
- package/dist/esm/elements/SwcIf.js +148 -0
- package/dist/esm/elements/SwcIf.js.map +7 -0
- package/dist/esm/elements/SwcLoop.js +227 -0
- package/dist/esm/elements/SwcLoop.js.map +7 -0
- package/dist/esm/elements/SwcSubTemplates.js +46 -0
- package/dist/esm/elements/SwcSubTemplates.js.map +7 -0
- package/dist/esm/elements/index.js +7 -0
- package/dist/esm/elements/index.js.map +2 -2
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/types.js +1 -0
- package/dist/esm/types.js.map +2 -2
- package/dist/esm/utils/Utils.js +92 -162
- package/dist/esm/utils/Utils.js.map +3 -3
- package/dist/esm-bundle/dooboostore-simple-web-component.esm.js +1150 -326
- package/dist/esm-bundle/dooboostore-simple-web-component.esm.js.map +4 -4
- package/dist/types/decorators/addEventListener.d.ts +10 -4
- package/dist/types/decorators/addEventListener.d.ts.map +1 -1
- package/dist/types/decorators/elementDefine.d.ts.map +1 -1
- package/dist/types/decorators/emitCustomEvent.d.ts.map +1 -1
- package/dist/types/decorators/query.d.ts +4 -5
- package/dist/types/decorators/query.d.ts.map +1 -1
- package/dist/types/decorators/queryAll.d.ts +4 -5
- package/dist/types/decorators/queryAll.d.ts.map +1 -1
- package/dist/types/elements/SwcAppEngine.d.ts.map +1 -1
- package/dist/types/elements/SwcAsync.d.ts +16 -0
- package/dist/types/elements/SwcAsync.d.ts.map +1 -0
- package/dist/types/elements/SwcChoose.d.ts +13 -0
- package/dist/types/elements/SwcChoose.d.ts.map +1 -0
- package/dist/types/elements/SwcIf.d.ts +15 -0
- package/dist/types/elements/SwcIf.d.ts.map +1 -0
- package/dist/types/elements/SwcLoop.d.ts +20 -0
- package/dist/types/elements/SwcLoop.d.ts.map +1 -0
- package/dist/types/elements/SwcSubTemplates.d.ts +13 -0
- package/dist/types/elements/SwcSubTemplates.d.ts.map +1 -0
- package/dist/types/elements/index.d.ts +7 -0
- package/dist/types/elements/index.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/types.d.ts +11 -1
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/utils/Utils.d.ts +19 -21
- package/dist/types/utils/Utils.d.ts.map +1 -1
- package/dist/umd-bundle/dooboostore-simple-web-component.umd.js +1150 -326
- package/dist/umd-bundle/dooboostore-simple-web-component.umd.js.map +4 -4
- package/package.json +10 -3
- package/src/decorators/addEventListener.ts +11 -7
- package/src/decorators/elementDefine.ts +165 -39
- package/src/decorators/emitCustomEvent.ts +5 -21
- package/src/decorators/query.ts +46 -41
- package/src/decorators/queryAll.ts +45 -41
- package/src/elements/SwcAppEngine.ts +2 -1
- package/src/elements/SwcAsync.ts +163 -0
- package/src/elements/SwcChoose.ts +127 -0
- package/src/elements/SwcIf.ts +134 -0
- package/src/elements/SwcLoop.ts +215 -0
- package/src/elements/SwcSubTemplates.ts +19 -0
- package/src/elements/index.ts +8 -3
- package/src/index.ts +1 -1
- package/src/types.ts +15 -5
- package/src/utils/Utils.ts +111 -197
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dooboostore/simple-web-component",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.17",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/cjs/index.js",
|
|
@@ -14,6 +14,13 @@
|
|
|
14
14
|
}
|
|
15
15
|
},
|
|
16
16
|
"description": "Simple Web Component library based on dom-render philosophy",
|
|
17
|
+
"sideEffects": [
|
|
18
|
+
"./dist/esm/elements/*.js",
|
|
19
|
+
"./dist/esm/elements/is/*.js",
|
|
20
|
+
"./dist/cjs/elements/*.js",
|
|
21
|
+
"./dist/cjs/elements/is/*.js",
|
|
22
|
+
"*.ts"
|
|
23
|
+
],
|
|
17
24
|
"repository": {
|
|
18
25
|
"type": "git",
|
|
19
26
|
"url": "git+https://github.com/dooboostore-develop/packages.git",
|
|
@@ -82,8 +89,8 @@
|
|
|
82
89
|
"webpack-dev-server": "^5.2.0",
|
|
83
90
|
"@dooboostore/core": "1.0.28",
|
|
84
91
|
"@dooboostore/core-node": "1.0.10",
|
|
85
|
-
"@dooboostore/
|
|
86
|
-
"@dooboostore/
|
|
92
|
+
"@dooboostore/core-web": "1.0.9",
|
|
93
|
+
"@dooboostore/simple-boot": "1.0.53"
|
|
87
94
|
},
|
|
88
95
|
"scripts": {
|
|
89
96
|
"clean": "rm -rf dist",
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { ReflectUtils } from '@dooboostore/core/reflect/ReflectUtils';
|
|
2
2
|
|
|
3
3
|
export interface AddEventListenerOptions extends EventListenerOptions {
|
|
4
|
-
query?: string;
|
|
5
|
-
type: string;
|
|
6
4
|
root?: 'light' | 'shadow' | 'all' | 'auto';
|
|
7
5
|
capture?: boolean;
|
|
8
6
|
once?: boolean;
|
|
@@ -10,28 +8,34 @@ export interface AddEventListenerOptions extends EventListenerOptions {
|
|
|
10
8
|
stopPropagation?: boolean;
|
|
11
9
|
stopImmediatePropagation?: boolean;
|
|
12
10
|
preventDefault?: boolean;
|
|
11
|
+
delegate?: boolean;
|
|
12
|
+
removeOnDisconnected?: boolean;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
export interface AddEventListenerMetadata {
|
|
16
|
+
selector: string;
|
|
17
|
+
type: string;
|
|
16
18
|
options: AddEventListenerOptions;
|
|
17
19
|
propertyKey: string | symbol;
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
export const ADD_EVENT_LISTENER_METADATA_KEY = Symbol('simple-web-component:add-event-listener');
|
|
21
23
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
/**
|
|
25
|
+
* @addEventListener decorator to bind events to elements.
|
|
26
|
+
* Usage: @addEventListener('.btn', 'click') onClick() { ... }
|
|
27
|
+
* Usage: @addEventListener('', 'scroll', { passive: true }) onScroll() { ... }
|
|
28
|
+
*/
|
|
29
|
+
export function addEventListener(selector: string, type: string, options: AddEventListenerOptions = {}): MethodDecorator {
|
|
25
30
|
return (target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor) => {
|
|
26
31
|
const constructor = target.constructor;
|
|
27
|
-
const options: AddEventListenerOptions = typeof arg1 === 'string' ? { type: arg1, query: arg2 } : arg1;
|
|
28
32
|
|
|
29
33
|
let listeners = ReflectUtils.getMetadata<AddEventListenerMetadata[]>(ADD_EVENT_LISTENER_METADATA_KEY, constructor);
|
|
30
34
|
if (!listeners) {
|
|
31
35
|
listeners = [];
|
|
32
36
|
ReflectUtils.defineMetadata(ADD_EVENT_LISTENER_METADATA_KEY, listeners, constructor);
|
|
33
37
|
}
|
|
34
|
-
listeners.push({ options, propertyKey });
|
|
38
|
+
listeners.push({ selector, type, options, propertyKey });
|
|
35
39
|
};
|
|
36
40
|
}
|
|
37
41
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ReflectUtils } from '@dooboostore/core/reflect/ReflectUtils';
|
|
2
|
+
import { FunctionUtils } from '@dooboostore/core/function/FunctionUtils';
|
|
2
3
|
import { getInnerHtmlMetadataList } from './innerHtml';
|
|
3
4
|
import { getQueryMetadata } from './query';
|
|
4
5
|
import { getQueryAllMetadata } from './queryAll';
|
|
@@ -11,9 +12,11 @@ import { SwcUtils } from '../utils/Utils';
|
|
|
11
12
|
import { HTML_TAG_ENTRIES, DOM_EVENT_NAMES } from '../config/config';
|
|
12
13
|
import { SituationType, SituationTypeContainer, SituationTypeContainers } from '@dooboostore/simple-boot/decorators/inject/Inject';
|
|
13
14
|
import { ConstructorType } from '@dooboostore/core/types';
|
|
15
|
+
import { SimpleApplication } from '@dooboostore/simple-boot/SimpleApplication';
|
|
16
|
+
import { Router } from '@dooboostore/core-web/routers/Router';
|
|
14
17
|
import { InjectSituationType, HostSet, SwcAppInterface } from '../types';
|
|
15
18
|
|
|
16
|
-
// --- Core Interfaces & Types
|
|
19
|
+
// --- Core Interfaces & Types ---
|
|
17
20
|
|
|
18
21
|
export interface ElementConfig {
|
|
19
22
|
name: string;
|
|
@@ -25,6 +28,9 @@ export interface ElementConfig {
|
|
|
25
28
|
|
|
26
29
|
export const ELEMENT_CONFIG_KEY = Symbol('simple-web-component:element-config');
|
|
27
30
|
|
|
31
|
+
// Shared state for global delegation to prevent multiple listeners on the same root
|
|
32
|
+
const globalDelegatedRoots = new WeakSet<Node>();
|
|
33
|
+
|
|
28
34
|
// --- Environment Initialization Helper ---
|
|
29
35
|
|
|
30
36
|
function buildEnv(configWindow?: Window) {
|
|
@@ -36,14 +42,7 @@ function buildEnv(configWindow?: Window) {
|
|
|
36
42
|
if (win?.[cls]) builtInTagMap.set(win[cls], tag);
|
|
37
43
|
}
|
|
38
44
|
|
|
39
|
-
const domHelpers =
|
|
40
|
-
$d: doc,
|
|
41
|
-
$w: win,
|
|
42
|
-
$q: (sel: string, root?: Element | Document | ShadowRoot) => (root ?? doc).querySelector(sel),
|
|
43
|
-
$qa: (sel: string, root?: Element | Document | ShadowRoot) => Array.from((root ?? doc).querySelectorAll(sel)),
|
|
44
|
-
$qi: (id: string, root?: Document | ShadowRoot) => (root ?? doc).getElementById(id)
|
|
45
|
-
} as const;
|
|
46
|
-
|
|
45
|
+
const domHelpers = SwcUtils.getHelperSet(win);
|
|
47
46
|
return { win, doc, builtInTagMap, domHelpers };
|
|
48
47
|
}
|
|
49
48
|
|
|
@@ -58,8 +57,6 @@ export const elementDefine =
|
|
|
58
57
|
const config: ElementConfig = typeof inConfig === 'string' ? { name: inConfig } : inConfig;
|
|
59
58
|
const { win, doc, builtInTagMap: BUILT_IN_TAG_MAP, domHelpers: SWC_DOM_HELPERS } = buildEnv(config.window);
|
|
60
59
|
config.window = win;
|
|
61
|
-
const SWC_DOM_HELPER_KEYS = Object.keys(SWC_DOM_HELPERS) as (keyof typeof SWC_DOM_HELPERS)[];
|
|
62
|
-
const SWC_DOM_HELPER_VALS = () => SWC_DOM_HELPER_KEYS.map(k => (SWC_DOM_HELPERS as any)[k]);
|
|
63
60
|
|
|
64
61
|
let extendsTagName = config.extends;
|
|
65
62
|
if (!extendsTagName) {
|
|
@@ -86,15 +83,14 @@ export const elementDefine =
|
|
|
86
83
|
const NewClass = class extends (constructor as any) {
|
|
87
84
|
private _swcId = Math.random().toString(36).substring(2, 11);
|
|
88
85
|
private _emitHandlers = new Map<string, EventListener>();
|
|
86
|
+
private _boundListeners: Array<{ target: EventTarget; type: string; handler: EventListener; options?: any }> = [];
|
|
89
87
|
|
|
90
88
|
private _executeSwcScript(attrName: string, hostSet: HostSet, extraArgs: Record<string, any> = {}) {
|
|
91
89
|
const script = this.getAttribute(attrName);
|
|
92
90
|
if (script) {
|
|
93
91
|
try {
|
|
94
92
|
const args = { ...hostSet, ...SWC_DOM_HELPERS, ...extraArgs };
|
|
95
|
-
|
|
96
|
-
const argValues = Object.values(args);
|
|
97
|
-
new Function(...argNames, script).apply(this, argValues);
|
|
93
|
+
FunctionUtils.execute({ script, context: this, args });
|
|
98
94
|
} catch (e) {
|
|
99
95
|
console.error(`[SWC] Failed to execute ${attrName}:`, e);
|
|
100
96
|
}
|
|
@@ -123,9 +119,11 @@ export const elementDefine =
|
|
|
123
119
|
}
|
|
124
120
|
}
|
|
125
121
|
|
|
126
|
-
private _bindAttributeEvent(el: HTMLElement, attrName: string, script: string) {
|
|
127
|
-
if (!
|
|
128
|
-
|
|
122
|
+
private _bindAttributeEvent(el: HTMLElement, attrName: string, script: string, eventName?: string) {
|
|
123
|
+
if (!eventName) {
|
|
124
|
+
if (!attrName.startsWith('swc-on-')) return;
|
|
125
|
+
eventName = attrName.substring(7);
|
|
126
|
+
}
|
|
129
127
|
if (swcLifecycleAttributes.includes(attrName)) return;
|
|
130
128
|
|
|
131
129
|
const elHandlers = getHandlers(this);
|
|
@@ -140,9 +138,13 @@ export const elementDefine =
|
|
|
140
138
|
|
|
141
139
|
const handler = (event: any) => {
|
|
142
140
|
const hostSet = SwcUtils.getHostSet(el);
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
|
|
141
|
+
const args = {
|
|
142
|
+
event,
|
|
143
|
+
$data: (event as CustomEvent).detail,
|
|
144
|
+
...hostSet,
|
|
145
|
+
...SWC_DOM_HELPERS
|
|
146
|
+
};
|
|
147
|
+
FunctionUtils.execute({ script, context: el, args });
|
|
146
148
|
};
|
|
147
149
|
|
|
148
150
|
el.addEventListener(eventName, handler);
|
|
@@ -164,6 +166,10 @@ export const elementDefine =
|
|
|
164
166
|
}
|
|
165
167
|
|
|
166
168
|
async connectedCallback() {
|
|
169
|
+
const conf = getElementConfig(this);
|
|
170
|
+
const currentWin = conf?.window || window;
|
|
171
|
+
const currentDoc = currentWin.document;
|
|
172
|
+
|
|
167
173
|
const hostSet = SwcUtils.getHostSet(this as any);
|
|
168
174
|
|
|
169
175
|
this._executeSwcScript('swc-on-before-connected', hostSet);
|
|
@@ -188,21 +194,117 @@ export const elementDefine =
|
|
|
188
194
|
if (lContent) this.innerHTML = lContent;
|
|
189
195
|
}
|
|
190
196
|
|
|
191
|
-
// ---
|
|
197
|
+
// --- Optimized Host Delegation ---
|
|
198
|
+
const createDelegationHandler = (delegateHost: any) => (event: Event) => {
|
|
199
|
+
if ((event as any).__swc_handled) return;
|
|
200
|
+
|
|
201
|
+
const path = event.composedPath();
|
|
202
|
+
const type = event.type;
|
|
203
|
+
const attrName = `swc-on-${type}`;
|
|
204
|
+
|
|
205
|
+
for (const node of path) {
|
|
206
|
+
if (node === delegateHost) break;
|
|
207
|
+
if (!(node instanceof HTMLElement)) continue;
|
|
208
|
+
|
|
209
|
+
const script = node.getAttribute?.(attrName);
|
|
210
|
+
if (script && !getElementConfig(node)) {
|
|
211
|
+
const host = SwcUtils.findNearestSwcHost(node);
|
|
212
|
+
if (host) {
|
|
213
|
+
const childHostSet = SwcUtils.getHostSet(node);
|
|
214
|
+
const args = {
|
|
215
|
+
event,
|
|
216
|
+
$data: (event as CustomEvent).detail,
|
|
217
|
+
...childHostSet,
|
|
218
|
+
...SWC_DOM_HELPERS
|
|
219
|
+
};
|
|
220
|
+
FunctionUtils.execute({ script, context: node, args });
|
|
221
|
+
(event as any).__swc_handled = true;
|
|
222
|
+
break;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
// 1. Local Delegation (To itself)
|
|
229
|
+
DOM_EVENT_NAMES.forEach(type => {
|
|
230
|
+
this.addEventListener(type, createDelegationHandler(this));
|
|
231
|
+
this._boundListeners.push({ target: this as any, type, handler: createDelegationHandler(this) });
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
// 2. Structural/Global Delegation (If no parent host exists)
|
|
235
|
+
const hasParentHost = SwcUtils.findAllSwcHosts(this as any).length > 0;
|
|
236
|
+
if (!hasParentHost) {
|
|
237
|
+
const root = this.getRootNode();
|
|
238
|
+
if (!globalDelegatedRoots.has(root)) {
|
|
239
|
+
DOM_EVENT_NAMES.forEach(type => {
|
|
240
|
+
const handler = createDelegationHandler(root);
|
|
241
|
+
root.addEventListener(type, handler);
|
|
242
|
+
});
|
|
243
|
+
globalDelegatedRoots.add(root);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// --- @addEventListener binding (Direct/Delegate/Global) ---
|
|
192
248
|
addEventListenerList.forEach(meta => {
|
|
193
|
-
const {
|
|
194
|
-
const
|
|
249
|
+
const { selector, type, options } = meta;
|
|
250
|
+
const { root, capture, once, passive, stopPropagation, stopImmediatePropagation, preventDefault, delegate, removeOnDisconnected } = options;
|
|
251
|
+
const opts = { capture, once, passive };
|
|
252
|
+
|
|
253
|
+
const bindTargets: EventTarget[] = [];
|
|
254
|
+
|
|
255
|
+
if (selector === ':window') {
|
|
256
|
+
bindTargets.push(currentWin);
|
|
257
|
+
} else if (selector === ':document') {
|
|
258
|
+
bindTargets.push(currentDoc);
|
|
259
|
+
} else if (selector === ':parentHost') {
|
|
260
|
+
if (hostSet.$parentHost) bindTargets.push(hostSet.$parentHost);
|
|
261
|
+
} else if (selector === ':appHost') {
|
|
262
|
+
if (hostSet.$appHost) bindTargets.push(hostSet.$appHost);
|
|
263
|
+
} else if (selector === ':firstHost') {
|
|
264
|
+
if (hostSet.$firstHost) bindTargets.push(hostSet.$firstHost);
|
|
265
|
+
} else if (selector === ':lastHost') {
|
|
266
|
+
if (hostSet.$lastHost) bindTargets.push(hostSet.$lastHost);
|
|
267
|
+
} else if (selector === ':firstAppHost') {
|
|
268
|
+
if (hostSet.$firstAppHost) bindTargets.push(hostSet.$firstAppHost);
|
|
269
|
+
} else if (selector === ':lastAppHost') {
|
|
270
|
+
if (hostSet.$lastAppHost) bindTargets.push(hostSet.$lastAppHost);
|
|
271
|
+
} else if (!selector || selector === ':host') {
|
|
272
|
+
bindTargets.push(this as any);
|
|
273
|
+
} else if (delegate) {
|
|
274
|
+
const bindRoots: (HTMLElement | ShadowRoot)[] = [];
|
|
275
|
+
const r = root || 'auto';
|
|
276
|
+
if (r === 'auto') bindRoots.push(this.shadowRoot || (this as any));
|
|
277
|
+
else if (r === 'light') bindRoots.push(this as any);
|
|
278
|
+
else if (r === 'shadow') {
|
|
279
|
+
if (this.shadowRoot) bindRoots.push(this.shadowRoot as any);
|
|
280
|
+
} else if (r === 'all') {
|
|
281
|
+
bindRoots.push(this as any);
|
|
282
|
+
if (this.shadowRoot) bindRoots.push(this.shadowRoot as any);
|
|
283
|
+
}
|
|
195
284
|
|
|
196
|
-
|
|
197
|
-
|
|
285
|
+
bindRoots.forEach(br => {
|
|
286
|
+
const handler = (event: Event) => {
|
|
287
|
+
const matchedEl = (event.target as HTMLElement).closest(selector);
|
|
288
|
+
if (matchedEl && (br as any).contains(matchedEl)) {
|
|
289
|
+
if (stopPropagation) event.stopPropagation();
|
|
290
|
+
if (stopImmediatePropagation) event.stopImmediatePropagation();
|
|
291
|
+
if (preventDefault) event.preventDefault();
|
|
292
|
+
const currentHostSet = SwcUtils.getHostSet(this as any);
|
|
293
|
+
this._invokeLifecycleMethod(meta.propertyKey, currentHostSet, [event, currentHostSet, matchedEl]);
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
br.addEventListener(type, handler, opts);
|
|
297
|
+
const self: any = this;
|
|
298
|
+
if (removeOnDisconnected === true || (br !== self && br !== self.shadowRoot)) {
|
|
299
|
+
this._boundListeners.push({ target: br, type, handler, options: opts });
|
|
300
|
+
}
|
|
301
|
+
});
|
|
198
302
|
} else {
|
|
199
303
|
const searchRoots: (HTMLElement | ShadowRoot)[] = [];
|
|
200
304
|
const r = root || 'auto';
|
|
201
|
-
if (r === 'auto')
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
searchRoots.push(this as any);
|
|
205
|
-
} else if (r === 'shadow') {
|
|
305
|
+
if (r === 'auto') searchRoots.push((this.shadowRoot || (this as any)) as any);
|
|
306
|
+
else if (r === 'light') searchRoots.push(this as any);
|
|
307
|
+
else if (r === 'shadow') {
|
|
206
308
|
if (this.shadowRoot) searchRoots.push(this.shadowRoot as any);
|
|
207
309
|
} else if (r === 'all') {
|
|
208
310
|
searchRoots.push(this as any);
|
|
@@ -210,22 +312,22 @@ export const elementDefine =
|
|
|
210
312
|
}
|
|
211
313
|
|
|
212
314
|
searchRoots.forEach(sr => {
|
|
213
|
-
|
|
214
|
-
found.forEach(el => targetEls.push(el as any));
|
|
315
|
+
sr.querySelectorAll(selector).forEach(el => bindTargets.push(el));
|
|
215
316
|
});
|
|
216
317
|
}
|
|
217
318
|
|
|
218
|
-
|
|
319
|
+
bindTargets.forEach(target => {
|
|
219
320
|
const handler = (event: Event) => {
|
|
220
321
|
if (stopPropagation) event.stopPropagation();
|
|
221
322
|
if (stopImmediatePropagation) event.stopImmediatePropagation();
|
|
222
323
|
if (preventDefault) event.preventDefault();
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
this._invokeLifecycleMethod(meta.propertyKey, hostSet, [event, hostSet]);
|
|
324
|
+
const currentHostSet = SwcUtils.getHostSet(this as any);
|
|
325
|
+
this._invokeLifecycleMethod(meta.propertyKey, currentHostSet, [event, currentHostSet]);
|
|
226
326
|
};
|
|
227
|
-
|
|
228
|
-
|
|
327
|
+
target.addEventListener(type, handler, opts);
|
|
328
|
+
if (removeOnDisconnected === true || target === currentWin || target === currentDoc) {
|
|
329
|
+
this._boundListeners.push({ target, type, handler, options: opts });
|
|
330
|
+
}
|
|
229
331
|
});
|
|
230
332
|
});
|
|
231
333
|
|
|
@@ -245,6 +347,9 @@ export const elementDefine =
|
|
|
245
347
|
const bMethods = findAllLifecycleMetadata(this, ON_BEFORE_DISCONNECTED_METADATA_KEY);
|
|
246
348
|
if (bMethods) bMethods.forEach(m => this._invokeLifecycleMethod(m, hostSet));
|
|
247
349
|
|
|
350
|
+
this._boundListeners.forEach(l => l.target.removeEventListener(l.type, l.handler, l.options));
|
|
351
|
+
this._boundListeners = [];
|
|
352
|
+
|
|
248
353
|
if (super.disconnectedCallback) super.disconnectedCallback();
|
|
249
354
|
|
|
250
355
|
const aMethods = findAllLifecycleMetadata(this, ON_AFTER_DISCONNECTED_METADATA_KEY);
|
|
@@ -272,7 +377,9 @@ export const elementDefine =
|
|
|
272
377
|
|
|
273
378
|
if (name.startsWith('swc-on-') && !swcLifecycleAttributes.includes(name)) {
|
|
274
379
|
if (newVal !== null) {
|
|
275
|
-
|
|
380
|
+
const customEventMeta = emitCustomEventList.find(it => it.options.attributeName === name);
|
|
381
|
+
const eventName = customEventMeta ? customEventMeta.options.type : name.substring(7);
|
|
382
|
+
this._bindAttributeEvent(this as any, name, newVal, eventName);
|
|
276
383
|
}
|
|
277
384
|
}
|
|
278
385
|
|
|
@@ -299,6 +406,25 @@ export const elementDefine =
|
|
|
299
406
|
}
|
|
300
407
|
});
|
|
301
408
|
|
|
409
|
+
// --- @emitCustomEvent wrapping logic ---
|
|
410
|
+
emitCustomEventList.forEach(meta => {
|
|
411
|
+
const { propertyKey, options } = meta;
|
|
412
|
+
const original = NewClass.prototype[propertyKey as any];
|
|
413
|
+
if (typeof original === 'function') {
|
|
414
|
+
NewClass.prototype[propertyKey as any] = function (...args: any[]) {
|
|
415
|
+
const detail = original.apply(this, args);
|
|
416
|
+
const event = new CustomEvent(options.type, {
|
|
417
|
+
detail,
|
|
418
|
+
bubbles: options.bubbles,
|
|
419
|
+
composed: options.composed,
|
|
420
|
+
cancelable: options.cancelable
|
|
421
|
+
});
|
|
422
|
+
this.dispatchEvent(event);
|
|
423
|
+
return detail;
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
});
|
|
427
|
+
|
|
302
428
|
const originalProto = constructor.prototype;
|
|
303
429
|
const newProto = NewClass.prototype;
|
|
304
430
|
Object.getOwnPropertyNames(originalProto).forEach(name => {
|
|
@@ -2,9 +2,9 @@ import { ReflectUtils } from '@dooboostore/core/reflect/ReflectUtils';
|
|
|
2
2
|
|
|
3
3
|
export interface EmitCustomEventOptions {
|
|
4
4
|
type: string;
|
|
5
|
-
attributeName?: string;
|
|
6
|
-
bubbles?: boolean;
|
|
7
|
-
composed?: boolean;
|
|
5
|
+
attributeName?: string;
|
|
6
|
+
bubbles?: boolean;
|
|
7
|
+
composed?: boolean;
|
|
8
8
|
cancelable?: boolean;
|
|
9
9
|
}
|
|
10
10
|
|
|
@@ -16,12 +16,12 @@ export interface EmitCustomEventMetadata {
|
|
|
16
16
|
export const EMIT_CUSTOM_EVENT_METADATA_KEY = Symbol('simple-web-component:emit-custom-event');
|
|
17
17
|
|
|
18
18
|
export function emitCustomEvent(typeOrOptions: string | EmitCustomEventOptions): MethodDecorator {
|
|
19
|
-
return (target: Object, propertyKey: string | symbol
|
|
19
|
+
return (target: Object, propertyKey: string | symbol) => {
|
|
20
20
|
const constructor = target.constructor;
|
|
21
21
|
const options: EmitCustomEventOptions = typeof typeOrOptions === 'string' ? { type: typeOrOptions } : typeOrOptions;
|
|
22
22
|
|
|
23
23
|
if (!options.attributeName) {
|
|
24
|
-
options.attributeName = `on
|
|
24
|
+
options.attributeName = `swc-on-${options.type}`;
|
|
25
25
|
}
|
|
26
26
|
if (options.bubbles === undefined) options.bubbles = true;
|
|
27
27
|
if (options.composed === undefined) options.composed = true;
|
|
@@ -32,22 +32,6 @@ export function emitCustomEvent(typeOrOptions: string | EmitCustomEventOptions):
|
|
|
32
32
|
ReflectUtils.defineMetadata(EMIT_CUSTOM_EVENT_METADATA_KEY, list, constructor);
|
|
33
33
|
}
|
|
34
34
|
list.push({ propertyKey, options });
|
|
35
|
-
|
|
36
|
-
// Wrap the original method to dispatch event after execution
|
|
37
|
-
const originalMethod = descriptor.value;
|
|
38
|
-
descriptor.value = function (this: HTMLElement, ...args: any[]) {
|
|
39
|
-
const detail = originalMethod.apply(this, args);
|
|
40
|
-
|
|
41
|
-
const event = new CustomEvent(options.type, {
|
|
42
|
-
detail,
|
|
43
|
-
bubbles: options.bubbles,
|
|
44
|
-
composed: options.composed,
|
|
45
|
-
cancelable: options.cancelable
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
this.dispatchEvent(event);
|
|
49
|
-
return detail;
|
|
50
|
-
};
|
|
51
35
|
};
|
|
52
36
|
}
|
|
53
37
|
|
package/src/decorators/query.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { ReflectUtils } from '@dooboostore/core/reflect/ReflectUtils';
|
|
2
|
+
import { getElementConfig } from './elementDefine';
|
|
3
|
+
import { SwcUtils } from '../utils/Utils';
|
|
2
4
|
|
|
3
5
|
export interface QueryOptions {
|
|
4
|
-
selector?: string;
|
|
5
6
|
root?: 'light' | 'shadow' | 'all' | 'auto';
|
|
6
7
|
}
|
|
7
8
|
|
|
@@ -9,61 +10,65 @@ export interface QueryMetadata {
|
|
|
9
10
|
selector: string;
|
|
10
11
|
options: QueryOptions;
|
|
11
12
|
propertyKey: string | symbol;
|
|
12
|
-
isMethod: boolean;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
export const QUERY_METADATA_KEY = Symbol('simple-web-component:query');
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
export function query(
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
17
|
+
/**
|
|
18
|
+
* @query decorator to inject a single element into a class property.
|
|
19
|
+
*/
|
|
20
|
+
export function query(selector: string, options: QueryOptions = {}): PropertyDecorator {
|
|
21
|
+
return (target: Object, propertyKey: string | symbol, descriptor?: never): void => {
|
|
22
|
+
if (descriptor !== undefined) {
|
|
23
|
+
throw new Error(`@query decorator cannot be used on methods. (Method: ${String(propertyKey)})`);
|
|
24
|
+
}
|
|
24
25
|
|
|
26
|
+
const constructor = target.constructor;
|
|
25
27
|
let queries = ReflectUtils.getMetadata<QueryMetadata[]>(QUERY_METADATA_KEY, constructor);
|
|
26
28
|
if (!queries) {
|
|
27
29
|
queries = [];
|
|
28
30
|
ReflectUtils.defineMetadata(QUERY_METADATA_KEY, queries, constructor);
|
|
29
31
|
}
|
|
30
|
-
queries.push({ selector, options, propertyKey
|
|
32
|
+
queries.push({ selector, options, propertyKey });
|
|
31
33
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
34
|
+
Object.defineProperty(target, propertyKey, {
|
|
35
|
+
get(this: HTMLElement) {
|
|
36
|
+
// --- Special Selectors: HostSet ---
|
|
37
|
+
const hostSet = SwcUtils.getHostSet(this);
|
|
38
|
+
if (selector === ':parentHost') return hostSet.$parentHost;
|
|
39
|
+
if (selector === ':appHost') return hostSet.$appHost;
|
|
40
|
+
if (selector === ':firstHost') return hostSet.$firstHost;
|
|
41
|
+
if (selector === ':lastHost') return hostSet.$lastHost;
|
|
42
|
+
if (selector === ':firstAppHost') return hostSet.$firstAppHost;
|
|
43
|
+
if (selector === ':lastAppHost') return hostSet.$lastAppHost;
|
|
40
44
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
// Case: @query (parameterless decorator)
|
|
50
|
-
if (arg1 && typeof arg2 === 'string') {
|
|
51
|
-
return decorator('', {}, arg1 as Object, arg2 as string | symbol);
|
|
52
|
-
}
|
|
45
|
+
// --- Special Selectors: Env ---
|
|
46
|
+
const config = getElementConfig(this);
|
|
47
|
+
const win = config?.window || window;
|
|
48
|
+
const doc = win.document;
|
|
53
49
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
let options: QueryOptions = {};
|
|
50
|
+
if (selector === ':window') return win;
|
|
51
|
+
if (selector === ':document') return doc;
|
|
52
|
+
if (!selector || selector === ':host') return this;
|
|
58
53
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
} else if (typeof arg1 === 'object') {
|
|
62
|
-
options = arg1 as QueryOptions;
|
|
63
|
-
selector = options.selector || '';
|
|
64
|
-
}
|
|
54
|
+
// --- Standard Selectors ---
|
|
55
|
+
const r = options.root || 'auto';
|
|
65
56
|
|
|
66
|
-
|
|
57
|
+
if (r === 'shadow') {
|
|
58
|
+
return this.shadowRoot ? this.shadowRoot.querySelector(selector) : null;
|
|
59
|
+
}
|
|
60
|
+
if (r === 'light') {
|
|
61
|
+
return this.querySelector(selector);
|
|
62
|
+
}
|
|
63
|
+
if (r === 'all') {
|
|
64
|
+
return this.shadowRoot?.querySelector(selector) || this.querySelector(selector);
|
|
65
|
+
}
|
|
66
|
+
// auto
|
|
67
|
+
return (this.shadowRoot || this).querySelector(selector);
|
|
68
|
+
},
|
|
69
|
+
enumerable: true,
|
|
70
|
+
configurable: true
|
|
71
|
+
});
|
|
67
72
|
};
|
|
68
73
|
}
|
|
69
74
|
|