@dooboostore/simple-web-component 1.0.16 → 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 +93 -0
- package/dist/cjs/decorators/addEventListener.js.map +2 -2
- package/dist/cjs/decorators/elementDefine.js +133 -62
- package/dist/cjs/decorators/elementDefine.js.map +3 -3
- package/dist/cjs/decorators/query.js +22 -0
- package/dist/cjs/decorators/query.js.map +2 -2
- package/dist/cjs/decorators/queryAll.js +14 -0
- 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.map +1 -1
- package/dist/cjs/utils/Utils.js +74 -37
- package/dist/cjs/utils/Utils.js.map +3 -3
- package/dist/esm/decorators/addEventListener.js.map +2 -2
- package/dist/esm/decorators/elementDefine.js +133 -62
- package/dist/esm/decorators/elementDefine.js.map +3 -3
- package/dist/esm/decorators/query.js +22 -0
- package/dist/esm/decorators/query.js.map +2 -2
- package/dist/esm/decorators/queryAll.js +14 -0
- 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.map +1 -1
- package/dist/esm/utils/Utils.js +74 -37
- package/dist/esm/utils/Utils.js.map +3 -3
- package/dist/esm-bundle/dooboostore-simple-web-component.esm.js +1047 -140
- package/dist/esm-bundle/dooboostore-simple-web-component.esm.js.map +4 -4
- package/dist/types/decorators/addEventListener.d.ts +1 -0
- package/dist/types/decorators/addEventListener.d.ts.map +1 -1
- package/dist/types/decorators/elementDefine.d.ts.map +1 -1
- package/dist/types/decorators/query.d.ts +0 -2
- package/dist/types/decorators/query.d.ts.map +1 -1
- package/dist/types/decorators/queryAll.d.ts +0 -2
- 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 +8 -0
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/utils/Utils.d.ts +16 -10
- package/dist/types/utils/Utils.d.ts.map +1 -1
- package/dist/umd-bundle/dooboostore-simple-web-component.umd.js +1047 -140
- package/dist/umd-bundle/dooboostore-simple-web-component.umd.js.map +4 -4
- package/package.json +10 -3
- package/src/decorators/addEventListener.ts +1 -0
- package/src/decorators/elementDefine.ts +137 -66
- package/src/decorators/query.ts +20 -2
- package/src/decorators/queryAll.ts +16 -2
- 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 +9 -1
- package/src/utils/Utils.ts +86 -36
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",
|
|
@@ -81,9 +88,9 @@
|
|
|
81
88
|
"webpack-cli": "^5.1.4",
|
|
82
89
|
"webpack-dev-server": "^5.2.0",
|
|
83
90
|
"@dooboostore/core": "1.0.28",
|
|
84
|
-
"@dooboostore/
|
|
91
|
+
"@dooboostore/core-node": "1.0.10",
|
|
85
92
|
"@dooboostore/core-web": "1.0.9",
|
|
86
|
-
"@dooboostore/
|
|
93
|
+
"@dooboostore/simple-boot": "1.0.53"
|
|
87
94
|
},
|
|
88
95
|
"scripts": {
|
|
89
96
|
"clean": "rm -rf dist",
|
|
@@ -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
|
}
|
|
@@ -142,9 +138,13 @@ export const elementDefine =
|
|
|
142
138
|
|
|
143
139
|
const handler = (event: any) => {
|
|
144
140
|
const hostSet = SwcUtils.getHostSet(el);
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
|
|
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 });
|
|
148
148
|
};
|
|
149
149
|
|
|
150
150
|
el.addEventListener(eventName, handler);
|
|
@@ -166,6 +166,10 @@ export const elementDefine =
|
|
|
166
166
|
}
|
|
167
167
|
|
|
168
168
|
async connectedCallback() {
|
|
169
|
+
const conf = getElementConfig(this);
|
|
170
|
+
const currentWin = conf?.window || window;
|
|
171
|
+
const currentDoc = currentWin.document;
|
|
172
|
+
|
|
169
173
|
const hostSet = SwcUtils.getHostSet(this as any);
|
|
170
174
|
|
|
171
175
|
this._executeSwcScript('swc-on-before-connected', hostSet);
|
|
@@ -190,77 +194,141 @@ export const elementDefine =
|
|
|
190
194
|
if (lContent) this.innerHTML = lContent;
|
|
191
195
|
}
|
|
192
196
|
|
|
193
|
-
// ---
|
|
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) ---
|
|
194
248
|
addEventListenerList.forEach(meta => {
|
|
195
249
|
const { selector, type, options } = meta;
|
|
196
|
-
const { root, capture, once, passive, stopPropagation, stopImmediatePropagation, preventDefault, delegate } = options;
|
|
197
|
-
|
|
198
|
-
|
|
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) {
|
|
199
274
|
const bindRoots: (HTMLElement | ShadowRoot)[] = [];
|
|
200
275
|
const r = root || 'auto';
|
|
201
|
-
if (r === 'auto')
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
bindRoots.push(this as any);
|
|
205
|
-
} else if (r === 'shadow') {
|
|
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') {
|
|
206
279
|
if (this.shadowRoot) bindRoots.push(this.shadowRoot as any);
|
|
207
280
|
} else if (r === 'all') {
|
|
208
281
|
bindRoots.push(this as any);
|
|
209
282
|
if (this.shadowRoot) bindRoots.push(this.shadowRoot as any);
|
|
210
283
|
}
|
|
211
284
|
|
|
212
|
-
bindRoots.forEach(
|
|
285
|
+
bindRoots.forEach(br => {
|
|
213
286
|
const handler = (event: Event) => {
|
|
214
|
-
const
|
|
215
|
-
|
|
216
|
-
if (matchedEl && (bindRoot as any).contains(matchedEl)) {
|
|
287
|
+
const matchedEl = (event.target as HTMLElement).closest(selector);
|
|
288
|
+
if (matchedEl && (br as any).contains(matchedEl)) {
|
|
217
289
|
if (stopPropagation) event.stopPropagation();
|
|
218
290
|
if (stopImmediatePropagation) event.stopImmediatePropagation();
|
|
219
291
|
if (preventDefault) event.preventDefault();
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
this._invokeLifecycleMethod(meta.propertyKey, hostSet, [event, hostSet, matchedEl]);
|
|
292
|
+
const currentHostSet = SwcUtils.getHostSet(this as any);
|
|
293
|
+
this._invokeLifecycleMethod(meta.propertyKey, currentHostSet, [event, currentHostSet, matchedEl]);
|
|
223
294
|
}
|
|
224
295
|
};
|
|
225
|
-
|
|
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
|
+
}
|
|
226
301
|
});
|
|
227
302
|
} else {
|
|
228
|
-
const
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
searchRoots.push(this as any);
|
|
238
|
-
} else if (r === 'shadow') {
|
|
239
|
-
if (this.shadowRoot) searchRoots.push(this.shadowRoot as any);
|
|
240
|
-
} else if (r === 'all') {
|
|
241
|
-
searchRoots.push(this as any);
|
|
242
|
-
if (this.shadowRoot) searchRoots.push(this.shadowRoot as any);
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
searchRoots.forEach(sr => {
|
|
246
|
-
const found = sr.querySelectorAll(selector);
|
|
247
|
-
found.forEach(el => targetEls.push(el as any));
|
|
248
|
-
});
|
|
303
|
+
const searchRoots: (HTMLElement | ShadowRoot)[] = [];
|
|
304
|
+
const r = root || 'auto';
|
|
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') {
|
|
308
|
+
if (this.shadowRoot) searchRoots.push(this.shadowRoot as any);
|
|
309
|
+
} else if (r === 'all') {
|
|
310
|
+
searchRoots.push(this as any);
|
|
311
|
+
if (this.shadowRoot) searchRoots.push(this.shadowRoot as any);
|
|
249
312
|
}
|
|
250
313
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
if (stopPropagation) event.stopPropagation();
|
|
254
|
-
if (stopImmediatePropagation) event.stopImmediatePropagation();
|
|
255
|
-
if (preventDefault) event.preventDefault();
|
|
256
|
-
|
|
257
|
-
const hostSet = SwcUtils.getHostSet(this as any);
|
|
258
|
-
this._invokeLifecycleMethod(meta.propertyKey, hostSet, [event, hostSet]);
|
|
259
|
-
};
|
|
260
|
-
const opts = { capture, once, passive };
|
|
261
|
-
el.addEventListener(type, handler, opts);
|
|
314
|
+
searchRoots.forEach(sr => {
|
|
315
|
+
sr.querySelectorAll(selector).forEach(el => bindTargets.push(el));
|
|
262
316
|
});
|
|
263
317
|
}
|
|
318
|
+
|
|
319
|
+
bindTargets.forEach(target => {
|
|
320
|
+
const handler = (event: Event) => {
|
|
321
|
+
if (stopPropagation) event.stopPropagation();
|
|
322
|
+
if (stopImmediatePropagation) event.stopImmediatePropagation();
|
|
323
|
+
if (preventDefault) event.preventDefault();
|
|
324
|
+
const currentHostSet = SwcUtils.getHostSet(this as any);
|
|
325
|
+
this._invokeLifecycleMethod(meta.propertyKey, currentHostSet, [event, currentHostSet]);
|
|
326
|
+
};
|
|
327
|
+
target.addEventListener(type, handler, opts);
|
|
328
|
+
if (removeOnDisconnected === true || target === currentWin || target === currentDoc) {
|
|
329
|
+
this._boundListeners.push({ target, type, handler, options: opts });
|
|
330
|
+
}
|
|
331
|
+
});
|
|
264
332
|
});
|
|
265
333
|
|
|
266
334
|
if (super.connectedCallback) await super.connectedCallback();
|
|
@@ -279,6 +347,9 @@ export const elementDefine =
|
|
|
279
347
|
const bMethods = findAllLifecycleMetadata(this, ON_BEFORE_DISCONNECTED_METADATA_KEY);
|
|
280
348
|
if (bMethods) bMethods.forEach(m => this._invokeLifecycleMethod(m, hostSet));
|
|
281
349
|
|
|
350
|
+
this._boundListeners.forEach(l => l.target.removeEventListener(l.type, l.handler, l.options));
|
|
351
|
+
this._boundListeners = [];
|
|
352
|
+
|
|
282
353
|
if (super.disconnectedCallback) super.disconnectedCallback();
|
|
283
354
|
|
|
284
355
|
const aMethods = findAllLifecycleMetadata(this, ON_AFTER_DISCONNECTED_METADATA_KEY);
|
package/src/decorators/query.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
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
6
|
root?: 'light' | 'shadow' | 'all' | 'auto';
|
|
@@ -14,8 +16,6 @@ export const QUERY_METADATA_KEY = Symbol('simple-web-component:query');
|
|
|
14
16
|
|
|
15
17
|
/**
|
|
16
18
|
* @query decorator to inject a single element into a class property.
|
|
17
|
-
* Usage: @query('.my-class') myEl!: HTMLElement;
|
|
18
|
-
* Usage: @query('#my-id', { root: 'shadow' }) myEl!: HTMLElement;
|
|
19
19
|
*/
|
|
20
20
|
export function query(selector: string, options: QueryOptions = {}): PropertyDecorator {
|
|
21
21
|
return (target: Object, propertyKey: string | symbol, descriptor?: never): void => {
|
|
@@ -33,7 +33,25 @@ export function query(selector: string, options: QueryOptions = {}): PropertyDec
|
|
|
33
33
|
|
|
34
34
|
Object.defineProperty(target, propertyKey, {
|
|
35
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;
|
|
44
|
+
|
|
45
|
+
// --- Special Selectors: Env ---
|
|
46
|
+
const config = getElementConfig(this);
|
|
47
|
+
const win = config?.window || window;
|
|
48
|
+
const doc = win.document;
|
|
49
|
+
|
|
50
|
+
if (selector === ':window') return win;
|
|
51
|
+
if (selector === ':document') return doc;
|
|
36
52
|
if (!selector || selector === ':host') return this;
|
|
53
|
+
|
|
54
|
+
// --- Standard Selectors ---
|
|
37
55
|
const r = options.root || 'auto';
|
|
38
56
|
|
|
39
57
|
if (r === 'shadow') {
|
|
@@ -1,4 +1,6 @@
|
|
|
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 QueryAllOptions {
|
|
4
6
|
root?: 'light' | 'shadow' | 'all' | 'auto';
|
|
@@ -14,8 +16,6 @@ export const QUERY_ALL_METADATA_KEY = Symbol('simple-web-component:query-all');
|
|
|
14
16
|
|
|
15
17
|
/**
|
|
16
18
|
* @queryAll decorator to inject a list of elements into a class property.
|
|
17
|
-
* Usage: @queryAll('.items') list!: NodeListOf<HTMLElement>;
|
|
18
|
-
* Usage: @queryAll('li', { root: 'shadow' }) list!: HTMLElement[];
|
|
19
19
|
*/
|
|
20
20
|
export function queryAll(selector: string, options: QueryAllOptions = {}): PropertyDecorator {
|
|
21
21
|
return (target: Object, propertyKey: string | symbol, descriptor?: never): void => {
|
|
@@ -33,7 +33,21 @@ export function queryAll(selector: string, options: QueryAllOptions = {}): Prope
|
|
|
33
33
|
|
|
34
34
|
Object.defineProperty(target, propertyKey, {
|
|
35
35
|
get(this: HTMLElement) {
|
|
36
|
+
// --- Special Selectors: HostSet ---
|
|
37
|
+
const hostSet = SwcUtils.getHostSet(this);
|
|
38
|
+
if (selector === ':hosts') return hostSet.$hosts;
|
|
39
|
+
if (selector === ':appHosts') return hostSet.$appHosts;
|
|
40
|
+
|
|
41
|
+
// --- Special Selectors: Env ---
|
|
42
|
+
const config = getElementConfig(this);
|
|
43
|
+
const win = config?.window || window;
|
|
44
|
+
const doc = win.document;
|
|
45
|
+
|
|
46
|
+
if (selector === ':window') return [win];
|
|
47
|
+
if (selector === ':document') return [doc];
|
|
36
48
|
if (!selector || selector === ':host') return [this];
|
|
49
|
+
|
|
50
|
+
// --- Standard Selectors ---
|
|
37
51
|
const r = options.root || 'auto';
|
|
38
52
|
|
|
39
53
|
if (r === 'shadow') {
|
|
@@ -6,6 +6,7 @@ import { SimOption } from '@dooboostore/simple-boot/SimOption';
|
|
|
6
6
|
import { Router, RouterEventType } from '@dooboostore/core-web/routers/Router';
|
|
7
7
|
import { Subscription } from '@dooboostore/core/message/Subscription';
|
|
8
8
|
import { ConstructorType, Optional } from '@dooboostore/core/types';
|
|
9
|
+
import { FunctionUtils } from '@dooboostore/core/function/FunctionUtils';
|
|
9
10
|
|
|
10
11
|
export type SwcConfigType = {
|
|
11
12
|
rootRouter: ConstructorType<any>;
|
|
@@ -35,7 +36,7 @@ export class SwcAppEngine {
|
|
|
35
36
|
if (!configStr) return;
|
|
36
37
|
|
|
37
38
|
try {
|
|
38
|
-
const userConfig: SwcAttributeConfigType =
|
|
39
|
+
const userConfig: SwcAttributeConfigType = FunctionUtils.executeReturn({ script: configStr, context: this.host });
|
|
39
40
|
if (userConfig && userConfig.rootRouter) {
|
|
40
41
|
const _config: SwcConfigType = {
|
|
41
42
|
routeType: 'element',
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { elementDefine } from '../decorators/elementDefine';
|
|
2
|
+
import { changedAttribute } from '../decorators/changedAttribute';
|
|
3
|
+
import { SwcUtils } from '../utils/Utils';
|
|
4
|
+
import { FunctionUtils } from '@dooboostore/core/function/FunctionUtils';
|
|
5
|
+
import { SwcLoading, SwcError, SwcSuccess, SwcDefault } from './SwcSubTemplates';
|
|
6
|
+
|
|
7
|
+
@elementDefine({ name: 'swc-async', extends: 'template' })
|
|
8
|
+
export class SwcAsync extends HTMLTemplateElement {
|
|
9
|
+
private _nodes: Node[] = [];
|
|
10
|
+
private _currentPromise: Promise<any> | null = null;
|
|
11
|
+
private _value: any = null;
|
|
12
|
+
|
|
13
|
+
get value(): any {
|
|
14
|
+
return this._value;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
set value(nv: any) {
|
|
18
|
+
if (nv instanceof Promise) {
|
|
19
|
+
this.handlePromise(nv);
|
|
20
|
+
} else {
|
|
21
|
+
this._value = nv;
|
|
22
|
+
this.render(nv === null ? 'default' : 'success', nv);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
@changedAttribute('on-get-value')
|
|
27
|
+
private refresh(newValue?: string | null) {
|
|
28
|
+
const script = newValue !== undefined ? newValue : this.getAttribute('on-get-value');
|
|
29
|
+
if (!script) {
|
|
30
|
+
this.value = null; // Triggers default render
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
if (!this.parentElement) return;
|
|
34
|
+
|
|
35
|
+
const helperHostSet = SwcUtils.getHelperAndHostSet(window, this);
|
|
36
|
+
try {
|
|
37
|
+
const result = FunctionUtils.executeReturn({
|
|
38
|
+
script,
|
|
39
|
+
context: this,
|
|
40
|
+
args: helperHostSet
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
if (result instanceof Promise) {
|
|
44
|
+
this.handlePromise(result);
|
|
45
|
+
} else {
|
|
46
|
+
this.value = result;
|
|
47
|
+
}
|
|
48
|
+
} catch (e) {
|
|
49
|
+
this.render('error', e);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
private async handlePromise(promise: Promise<any>) {
|
|
54
|
+
this._currentPromise = promise;
|
|
55
|
+
this.render('loading');
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
const result = await promise;
|
|
59
|
+
if (this._currentPromise !== promise) return;
|
|
60
|
+
this._value = result;
|
|
61
|
+
this.render('success', result);
|
|
62
|
+
} catch (e) {
|
|
63
|
+
if (this._currentPromise !== promise) return;
|
|
64
|
+
this.render('error', e);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
private render(state: 'loading' | 'error' | 'success' | 'default', data?: any) {
|
|
69
|
+
this.cleanup();
|
|
70
|
+
if (!this.parentElement) return;
|
|
71
|
+
|
|
72
|
+
let fragment: DocumentFragment;
|
|
73
|
+
const ctx: any = {
|
|
74
|
+
$nodes: [],
|
|
75
|
+
$elements: [],
|
|
76
|
+
$state: state,
|
|
77
|
+
$value: null,
|
|
78
|
+
$error: null
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
if (state === 'default') {
|
|
82
|
+
const t = this.content.querySelector('template[is="swc-default"]') as HTMLTemplateElement;
|
|
83
|
+
fragment = t ? (t.content.cloneNode(true) as DocumentFragment) : document.createDocumentFragment();
|
|
84
|
+
} else if (state === 'loading') {
|
|
85
|
+
const t = this.content.querySelector('template[is="swc-loading"]') as HTMLTemplateElement;
|
|
86
|
+
fragment = t ? (t.content.cloneNode(true) as DocumentFragment) : document.createDocumentFragment();
|
|
87
|
+
} else if (state === 'error') {
|
|
88
|
+
const t = this.content.querySelector('template[is="swc-error"]') as HTMLTemplateElement;
|
|
89
|
+
ctx.$error = data;
|
|
90
|
+
fragment = t ? (t.content.cloneNode(true) as DocumentFragment) : document.createDocumentFragment();
|
|
91
|
+
} else {
|
|
92
|
+
// Success
|
|
93
|
+
const t = this.content.querySelector('template[is="swc-success"]') as HTMLTemplateElement;
|
|
94
|
+
ctx.$value = data;
|
|
95
|
+
if (t) {
|
|
96
|
+
fragment = t.content.cloneNode(true) as DocumentFragment;
|
|
97
|
+
} else {
|
|
98
|
+
const clone = this.content.cloneNode(true) as DocumentFragment;
|
|
99
|
+
Array.from(clone.querySelectorAll('template[is="swc-loading"], template[is="swc-error"], template[is="swc-success"], template[is="swc-default"]')).forEach(tmp => tmp.remove());
|
|
100
|
+
fragment = clone;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
this.mount(fragment, ctx);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
private mount(fragment: DocumentFragment, ctx: any) {
|
|
108
|
+
const nodes = Array.from(fragment.childNodes);
|
|
109
|
+
const elements = nodes.filter(n => n.nodeType === 1) as HTMLElement[];
|
|
110
|
+
|
|
111
|
+
Object.assign(ctx, {
|
|
112
|
+
$nodes: nodes,
|
|
113
|
+
$elements: elements,
|
|
114
|
+
$firstNode: nodes[0],
|
|
115
|
+
$lastNode: nodes[nodes.length - 1],
|
|
116
|
+
$firstElement: elements[0],
|
|
117
|
+
$lastElement: elements[elements.length - 1]
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
// Execution Callbacks
|
|
121
|
+
this._executeCallback('on-clone-nodes', ctx);
|
|
122
|
+
nodes.forEach((node, nodeIndex) => {
|
|
123
|
+
if (node instanceof HTMLElement) {
|
|
124
|
+
(node as any).__swc_host = this;
|
|
125
|
+
}
|
|
126
|
+
this._executeCallback('on-clone-node', {
|
|
127
|
+
...ctx,
|
|
128
|
+
$node: node,
|
|
129
|
+
$nodeIndex: nodeIndex,
|
|
130
|
+
$isElement: node.nodeType === 1
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
this._nodes = nodes;
|
|
135
|
+
this.parentElement?.insertBefore(fragment, this.nextSibling);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
private _executeCallback(attrName: string, extraArgs: Record<string, any>) {
|
|
139
|
+
const script = this.getAttribute(attrName);
|
|
140
|
+
if (!script) return;
|
|
141
|
+
const helperHostSet = SwcUtils.getHelperAndHostSet(window, this);
|
|
142
|
+
try {
|
|
143
|
+
FunctionUtils.execute({ script, context: this, args: { ...helperHostSet, ...extraArgs } });
|
|
144
|
+
} catch (e) {
|
|
145
|
+
console.error(`[SwcAsync] Error in ${attrName}:`, e);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
private cleanup() {
|
|
150
|
+
this._nodes.forEach(n => {
|
|
151
|
+
if (n.parentElement) n.parentElement.removeChild(n);
|
|
152
|
+
});
|
|
153
|
+
this._nodes = [];
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
connectedCallback() {
|
|
157
|
+
this.refresh();
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
disconnectedCallback() {
|
|
161
|
+
this.cleanup();
|
|
162
|
+
}
|
|
163
|
+
}
|