@dooboostore/simple-web-component 1.0.16 → 1.0.18
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 +109 -48
- package/dist/cjs/decorators/addEventListener.js.map +2 -2
- package/dist/cjs/decorators/appendChild.js +48 -0
- package/dist/cjs/decorators/appendChild.js.map +7 -0
- package/dist/cjs/decorators/elementDefine.js +216 -67
- package/dist/cjs/decorators/elementDefine.js.map +3 -3
- package/dist/cjs/decorators/index.js +2 -1
- package/dist/cjs/decorators/index.js.map +2 -2
- package/dist/cjs/decorators/{innerHtml.js → onConnectedInnerHtml.js} +8 -8
- package/dist/cjs/decorators/onConnectedInnerHtml.js.map +7 -0
- package/dist/cjs/decorators/query.js +38 -0
- package/dist/cjs/decorators/query.js.map +2 -2
- package/dist/cjs/decorators/queryAll.js +32 -0
- package/dist/cjs/decorators/queryAll.js.map +2 -2
- package/dist/cjs/decorators/replaceChildren.js +53 -0
- package/dist/cjs/decorators/replaceChildren.js.map +7 -0
- 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 +12 -2
- 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/appendChild.js +29 -0
- package/dist/esm/decorators/appendChild.js.map +7 -0
- package/dist/esm/decorators/elementDefine.js +216 -67
- package/dist/esm/decorators/elementDefine.js.map +3 -3
- package/dist/esm/decorators/index.js +2 -1
- package/dist/esm/decorators/index.js.map +2 -2
- package/dist/esm/decorators/{innerHtml.js → onConnectedInnerHtml.js} +5 -5
- package/dist/esm/decorators/onConnectedInnerHtml.js.map +7 -0
- package/dist/esm/decorators/query.js +38 -0
- package/dist/esm/decorators/query.js.map +2 -2
- package/dist/esm/decorators/queryAll.js +32 -0
- package/dist/esm/decorators/queryAll.js.map +2 -2
- package/dist/esm/decorators/replaceChildren.js +34 -0
- package/dist/esm/decorators/replaceChildren.js.map +7 -0
- 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 +8 -2
- package/dist/esm/index.js.map +2 -2
- 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 +1226 -149
- 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/appendChild.d.ts +17 -0
- package/dist/types/decorators/appendChild.d.ts.map +1 -0
- package/dist/types/decorators/elementDefine.d.ts.map +1 -1
- package/dist/types/decorators/index.d.ts +2 -1
- package/dist/types/decorators/index.d.ts.map +1 -1
- package/dist/types/decorators/onConnectedInnerHtml.d.ts +15 -0
- package/dist/types/decorators/onConnectedInnerHtml.d.ts.map +1 -0
- 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/decorators/replaceChildren.d.ts +9 -0
- package/dist/types/decorators/replaceChildren.d.ts.map +1 -0
- 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 +5 -2
- 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 +1226 -149
- 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/appendChild.ts +42 -0
- package/src/decorators/elementDefine.ts +225 -71
- package/src/decorators/index.ts +2 -1
- package/src/decorators/{innerHtml.ts → onConnectedInnerHtml.ts} +7 -6
- package/src/decorators/query.ts +32 -2
- package/src/decorators/queryAll.ts +32 -2
- package/src/decorators/replaceChildren.ts +35 -0
- 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 +5 -2
- package/src/types.ts +9 -1
- package/src/utils/Utils.ts +86 -36
- package/dist/cjs/decorators/innerHtml.js.map +0 -7
- package/dist/esm/decorators/innerHtml.js.map +0 -7
- package/dist/types/decorators/innerHtml.d.ts +0 -12
- package/dist/types/decorators/innerHtml.d.ts.map +0 -1
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.18",
|
|
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",
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { ReflectUtils } from '@dooboostore/core/reflect/ReflectUtils';
|
|
2
|
+
|
|
3
|
+
export type AppendChildPosition = 'firstChild' | 'lastChild' | 'nextSibling' | 'previousSibling';
|
|
4
|
+
|
|
5
|
+
export interface AppendChildOptions {
|
|
6
|
+
position?: AppendChildPosition;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface AppendChildMetadata {
|
|
10
|
+
propertyKey: string | symbol;
|
|
11
|
+
selector: string;
|
|
12
|
+
options: AppendChildOptions;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const APPEND_CHILD_METADATA_KEY = Symbol('simple-web-component:append-child');
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @appendChild decorator to surgically add nodes to a target element.
|
|
19
|
+
* Usage: @appendChild('#list') addItem() { return '<li>New Item</li>'; }
|
|
20
|
+
*/
|
|
21
|
+
export function appendChild(selector: string, options: AppendChildOptions = { position: 'lastChild' }): MethodDecorator {
|
|
22
|
+
return (target: Object, propertyKey: string | symbol) => {
|
|
23
|
+
const constructor = target.constructor;
|
|
24
|
+
let meta = ReflectUtils.getOwnMetadata(APPEND_CHILD_METADATA_KEY, constructor) as Map<string | symbol, AppendChildMetadata>;
|
|
25
|
+
if (!meta) {
|
|
26
|
+
meta = new Map<string | symbol, AppendChildMetadata>();
|
|
27
|
+
ReflectUtils.defineMetadata(APPEND_CHILD_METADATA_KEY, meta, constructor);
|
|
28
|
+
}
|
|
29
|
+
meta.set(propertyKey, { propertyKey, selector, options });
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const findAllAppendChildMetadata = (target: any): Map<string | symbol, AppendChildMetadata> => {
|
|
34
|
+
const result = new Map<string | symbol, AppendChildMetadata>();
|
|
35
|
+
const maps = ReflectUtils.findAllMetadata<Map<string | symbol, AppendChildMetadata>>(APPEND_CHILD_METADATA_KEY, target);
|
|
36
|
+
maps.forEach(map => {
|
|
37
|
+
if (map instanceof Map) {
|
|
38
|
+
map.forEach((v, k) => result.set(k, v));
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
return result;
|
|
42
|
+
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ReflectUtils } from '@dooboostore/core/reflect/ReflectUtils';
|
|
2
|
-
import {
|
|
2
|
+
import { FunctionUtils } from '@dooboostore/core/function/FunctionUtils';
|
|
3
|
+
import { getOnConnectedInnerHtmlMetadata } from './onConnectedInnerHtml';
|
|
3
4
|
import { getQueryMetadata } from './query';
|
|
4
5
|
import { getQueryAllMetadata } from './queryAll';
|
|
5
6
|
import { getAddEventListenerMetadata } from './addEventListener';
|
|
@@ -7,13 +8,17 @@ import { ON_BEFORE_CONNECTED_METADATA_KEY, ON_AFTER_CONNECTED_METADATA_KEY, ON_B
|
|
|
7
8
|
import { getEmitCustomEventMetadataList } from './emitCustomEvent';
|
|
8
9
|
import { changedAttribute, findAllAttributeChangedMetadata } from './changedAttribute';
|
|
9
10
|
import { setAttribute, findAllSetAttributeMetadata } from './setAttribute';
|
|
11
|
+
import { findAllReplaceChildrenMetadata } from './replaceChildren';
|
|
12
|
+
import { findAllAppendChildMetadata } from './appendChild';
|
|
10
13
|
import { SwcUtils } from '../utils/Utils';
|
|
11
14
|
import { HTML_TAG_ENTRIES, DOM_EVENT_NAMES } from '../config/config';
|
|
12
15
|
import { SituationType, SituationTypeContainer, SituationTypeContainers } from '@dooboostore/simple-boot/decorators/inject/Inject';
|
|
13
16
|
import { ConstructorType } from '@dooboostore/core/types';
|
|
17
|
+
import { SimpleApplication } from '@dooboostore/simple-boot/SimpleApplication';
|
|
18
|
+
import { Router } from '@dooboostore/core-web/routers/Router';
|
|
14
19
|
import { InjectSituationType, HostSet, SwcAppInterface } from '../types';
|
|
15
20
|
|
|
16
|
-
// --- Core Interfaces & Types
|
|
21
|
+
// --- Core Interfaces & Types ---
|
|
17
22
|
|
|
18
23
|
export interface ElementConfig {
|
|
19
24
|
name: string;
|
|
@@ -25,6 +30,9 @@ export interface ElementConfig {
|
|
|
25
30
|
|
|
26
31
|
export const ELEMENT_CONFIG_KEY = Symbol('simple-web-component:element-config');
|
|
27
32
|
|
|
33
|
+
// Shared state for global delegation to prevent multiple listeners on the same root
|
|
34
|
+
const globalDelegatedRoots = new WeakSet<Node>();
|
|
35
|
+
|
|
28
36
|
// --- Environment Initialization Helper ---
|
|
29
37
|
|
|
30
38
|
function buildEnv(configWindow?: Window) {
|
|
@@ -36,14 +44,7 @@ function buildEnv(configWindow?: Window) {
|
|
|
36
44
|
if (win?.[cls]) builtInTagMap.set(win[cls], tag);
|
|
37
45
|
}
|
|
38
46
|
|
|
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
|
-
|
|
47
|
+
const domHelpers = SwcUtils.getHelperSet(win);
|
|
47
48
|
return { win, doc, builtInTagMap, domHelpers };
|
|
48
49
|
}
|
|
49
50
|
|
|
@@ -58,8 +59,6 @@ export const elementDefine =
|
|
|
58
59
|
const config: ElementConfig = typeof inConfig === 'string' ? { name: inConfig } : inConfig;
|
|
59
60
|
const { win, doc, builtInTagMap: BUILT_IN_TAG_MAP, domHelpers: SWC_DOM_HELPERS } = buildEnv(config.window);
|
|
60
61
|
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
62
|
|
|
64
63
|
let extendsTagName = config.extends;
|
|
65
64
|
if (!extendsTagName) {
|
|
@@ -76,25 +75,26 @@ export const elementDefine =
|
|
|
76
75
|
const addEventListenerList = getAddEventListenerMetadata(constructor) || [];
|
|
77
76
|
const attrChangeMap = findAllAttributeChangedMetadata(constructor);
|
|
78
77
|
const setAttrMap = findAllSetAttributeMetadata(constructor);
|
|
78
|
+
const replaceChildrenMap = findAllReplaceChildrenMetadata(constructor);
|
|
79
|
+
const appendChildMap = findAllAppendChildMetadata(constructor);
|
|
79
80
|
|
|
80
81
|
const swcLifecycleAttributes = ['swc-on-constructor', 'swc-on-connected', 'swc-on-disconnected', 'swc-on-before-connected', 'swc-on-after-connected', 'swc-on-before-disconnected', 'swc-on-after-disconnected', 'swc-on-before-adopted', 'swc-on-after-adopted', 'swc-on-attribute-changed'];
|
|
81
82
|
const swcOnEvents = DOM_EVENT_NAMES.map(e => `swc-on-${e}`);
|
|
82
83
|
|
|
83
84
|
const mergedObservedAttributes = [...new Set([...(config.observedAttributes ?? []), ...attrChangeMap.keys(), ...setAttrMap.keys(), ...emitCustomEventList.map(it => it.options.attributeName!), ...swcLifecycleAttributes, ...swcOnEvents])];
|
|
84
|
-
const
|
|
85
|
+
const connectedInnerHtmlList = getOnConnectedInnerHtmlMetadata(constructor) || [];
|
|
85
86
|
|
|
86
87
|
const NewClass = class extends (constructor as any) {
|
|
87
88
|
private _swcId = Math.random().toString(36).substring(2, 11);
|
|
88
89
|
private _emitHandlers = new Map<string, EventListener>();
|
|
90
|
+
private _boundListeners: Array<{ target: EventTarget; type: string; handler: EventListener; options?: any }> = [];
|
|
89
91
|
|
|
90
92
|
private _executeSwcScript(attrName: string, hostSet: HostSet, extraArgs: Record<string, any> = {}) {
|
|
91
93
|
const script = this.getAttribute(attrName);
|
|
92
94
|
if (script) {
|
|
93
95
|
try {
|
|
94
96
|
const args = { ...hostSet, ...SWC_DOM_HELPERS, ...extraArgs };
|
|
95
|
-
|
|
96
|
-
const argValues = Object.values(args);
|
|
97
|
-
new Function(...argNames, script).apply(this, argValues);
|
|
97
|
+
FunctionUtils.execute({ script, context: this, args });
|
|
98
98
|
} catch (e) {
|
|
99
99
|
console.error(`[SWC] Failed to execute ${attrName}:`, e);
|
|
100
100
|
}
|
|
@@ -142,9 +142,13 @@ export const elementDefine =
|
|
|
142
142
|
|
|
143
143
|
const handler = (event: any) => {
|
|
144
144
|
const hostSet = SwcUtils.getHostSet(el);
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
|
|
145
|
+
const args = {
|
|
146
|
+
event,
|
|
147
|
+
$data: (event as CustomEvent).detail,
|
|
148
|
+
...hostSet,
|
|
149
|
+
...SWC_DOM_HELPERS
|
|
150
|
+
};
|
|
151
|
+
FunctionUtils.execute({ script, context: el, args });
|
|
148
152
|
};
|
|
149
153
|
|
|
150
154
|
el.addEventListener(eventName, handler);
|
|
@@ -156,7 +160,7 @@ export const elementDefine =
|
|
|
156
160
|
const hostSet = SwcUtils.getHostSet(this as any);
|
|
157
161
|
this._executeSwcScript('swc-on-constructor', hostSet);
|
|
158
162
|
|
|
159
|
-
if (
|
|
163
|
+
if (connectedInnerHtmlList.some(it => it.options.useShadow === true) && !this.shadowRoot) {
|
|
160
164
|
this.attachShadow({ mode: 'open' });
|
|
161
165
|
}
|
|
162
166
|
}
|
|
@@ -166,6 +170,10 @@ export const elementDefine =
|
|
|
166
170
|
}
|
|
167
171
|
|
|
168
172
|
async connectedCallback() {
|
|
173
|
+
const conf = getElementConfig(this);
|
|
174
|
+
const currentWin = conf?.window || window;
|
|
175
|
+
const currentDoc = currentWin.document;
|
|
176
|
+
|
|
169
177
|
const hostSet = SwcUtils.getHostSet(this as any);
|
|
170
178
|
|
|
171
179
|
this._executeSwcScript('swc-on-before-connected', hostSet);
|
|
@@ -176,10 +184,10 @@ export const elementDefine =
|
|
|
176
184
|
|
|
177
185
|
if (typeof (this as any).initCore === 'function') (this as any).initCore();
|
|
178
186
|
|
|
179
|
-
if (
|
|
187
|
+
if (connectedInnerHtmlList.length > 0) {
|
|
180
188
|
let sContent = '',
|
|
181
189
|
lContent = '';
|
|
182
|
-
for (const meta of
|
|
190
|
+
for (const meta of connectedInnerHtmlList) {
|
|
183
191
|
const res = await (this as any)[meta.propertyKey]();
|
|
184
192
|
if (res !== undefined) {
|
|
185
193
|
if (meta.options.useShadow) sContent += res;
|
|
@@ -190,77 +198,142 @@ export const elementDefine =
|
|
|
190
198
|
if (lContent) this.innerHTML = lContent;
|
|
191
199
|
}
|
|
192
200
|
|
|
193
|
-
// ---
|
|
201
|
+
// --- Optimized Host Delegation ---
|
|
202
|
+
const createDelegationHandler = (delegateHost: any) => (event: Event) => {
|
|
203
|
+
if ((event as any).__swc_handled) return;
|
|
204
|
+
|
|
205
|
+
const path = event.composedPath();
|
|
206
|
+
const type = event.type;
|
|
207
|
+
const attrName = `swc-on-${type}`;
|
|
208
|
+
|
|
209
|
+
for (const node of path) {
|
|
210
|
+
if (node === delegateHost) break;
|
|
211
|
+
if (!(node instanceof HTMLElement)) continue;
|
|
212
|
+
|
|
213
|
+
const script = node.getAttribute?.(attrName);
|
|
214
|
+
if (script && !getElementConfig(node)) {
|
|
215
|
+
const host = SwcUtils.findNearestSwcHost(node);
|
|
216
|
+
if (host) {
|
|
217
|
+
const childHostSet = SwcUtils.getHostSet(node);
|
|
218
|
+
const args = {
|
|
219
|
+
event,
|
|
220
|
+
$data: (event as CustomEvent).detail,
|
|
221
|
+
...childHostSet,
|
|
222
|
+
...SWC_DOM_HELPERS
|
|
223
|
+
};
|
|
224
|
+
FunctionUtils.execute({ script, context: node, args });
|
|
225
|
+
(event as any).__swc_handled = true;
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
// 1. Local Delegation (To itself)
|
|
233
|
+
DOM_EVENT_NAMES.forEach(type => {
|
|
234
|
+
const handler = createDelegationHandler(this);
|
|
235
|
+
this.addEventListener(type, handler);
|
|
236
|
+
this._boundListeners.push({ target: this as any, type, handler });
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
// 2. Structural/Global Delegation (If no parent host exists)
|
|
240
|
+
const hasParentHost = SwcUtils.findAllSwcHosts(this as any).length > 0;
|
|
241
|
+
if (!hasParentHost) {
|
|
242
|
+
const root = this.getRootNode();
|
|
243
|
+
if (!globalDelegatedRoots.has(root)) {
|
|
244
|
+
DOM_EVENT_NAMES.forEach(type => {
|
|
245
|
+
const handler = createDelegationHandler(root);
|
|
246
|
+
root.addEventListener(type, handler);
|
|
247
|
+
});
|
|
248
|
+
globalDelegatedRoots.add(root);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// --- @addEventListener binding (Direct/Delegate/Global) ---
|
|
194
253
|
addEventListenerList.forEach(meta => {
|
|
195
254
|
const { selector, type, options } = meta;
|
|
196
|
-
const { root, capture, once, passive, stopPropagation, stopImmediatePropagation, preventDefault, delegate } = options;
|
|
197
|
-
|
|
198
|
-
|
|
255
|
+
const { root, capture, once, passive, stopPropagation, stopImmediatePropagation, preventDefault, delegate, removeOnDisconnected } = options;
|
|
256
|
+
const opts = { capture, once, passive };
|
|
257
|
+
|
|
258
|
+
const bindTargets: EventTarget[] = [];
|
|
259
|
+
|
|
260
|
+
if (selector === ':window') {
|
|
261
|
+
bindTargets.push(currentWin);
|
|
262
|
+
} else if (selector === ':document') {
|
|
263
|
+
bindTargets.push(currentDoc);
|
|
264
|
+
} else if (selector === ':parentHost') {
|
|
265
|
+
if (hostSet.$parentHost) bindTargets.push(hostSet.$parentHost);
|
|
266
|
+
} else if (selector === ':appHost') {
|
|
267
|
+
if (hostSet.$appHost) bindTargets.push(hostSet.$appHost);
|
|
268
|
+
} else if (selector === ':firstHost') {
|
|
269
|
+
if (hostSet.$firstHost) bindTargets.push(hostSet.$firstHost);
|
|
270
|
+
} else if (selector === ':lastHost') {
|
|
271
|
+
if (hostSet.$lastHost) bindTargets.push(hostSet.$lastHost);
|
|
272
|
+
} else if (selector === ':firstAppHost') {
|
|
273
|
+
if (hostSet.$firstAppHost) bindTargets.push(hostSet.$firstAppHost);
|
|
274
|
+
} else if (selector === ':lastAppHost') {
|
|
275
|
+
if (hostSet.$lastAppHost) bindTargets.push(hostSet.$lastAppHost);
|
|
276
|
+
} else if (!selector || selector === ':host') {
|
|
277
|
+
bindTargets.push(this as any);
|
|
278
|
+
} else if (delegate) {
|
|
199
279
|
const bindRoots: (HTMLElement | ShadowRoot)[] = [];
|
|
200
280
|
const r = root || 'auto';
|
|
201
|
-
if (r === 'auto')
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
bindRoots.push(this as any);
|
|
205
|
-
} else if (r === 'shadow') {
|
|
281
|
+
if (r === 'auto') bindRoots.push(this.shadowRoot || (this as any));
|
|
282
|
+
else if (r === 'light') bindRoots.push(this as any);
|
|
283
|
+
else if (r === 'shadow') {
|
|
206
284
|
if (this.shadowRoot) bindRoots.push(this.shadowRoot as any);
|
|
207
285
|
} else if (r === 'all') {
|
|
208
286
|
bindRoots.push(this as any);
|
|
209
287
|
if (this.shadowRoot) bindRoots.push(this.shadowRoot as any);
|
|
210
288
|
}
|
|
211
289
|
|
|
212
|
-
bindRoots.forEach(
|
|
290
|
+
bindRoots.forEach(br => {
|
|
213
291
|
const handler = (event: Event) => {
|
|
214
|
-
const
|
|
215
|
-
|
|
216
|
-
if (matchedEl && (bindRoot as any).contains(matchedEl)) {
|
|
292
|
+
const matchedEl = (event.target as HTMLElement).closest(selector);
|
|
293
|
+
if (matchedEl && (br as any).contains(matchedEl)) {
|
|
217
294
|
if (stopPropagation) event.stopPropagation();
|
|
218
295
|
if (stopImmediatePropagation) event.stopImmediatePropagation();
|
|
219
296
|
if (preventDefault) event.preventDefault();
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
this._invokeLifecycleMethod(meta.propertyKey, hostSet, [event, hostSet, matchedEl]);
|
|
297
|
+
const currentHostSet = SwcUtils.getHostSet(this as any);
|
|
298
|
+
this._invokeLifecycleMethod(meta.propertyKey, currentHostSet, [event, currentHostSet, matchedEl]);
|
|
223
299
|
}
|
|
224
300
|
};
|
|
225
|
-
|
|
301
|
+
br.addEventListener(type, handler, opts);
|
|
302
|
+
const self: any = this;
|
|
303
|
+
if (removeOnDisconnected === true || (br !== self && br !== self.shadowRoot)) {
|
|
304
|
+
this._boundListeners.push({ target: br, type, handler, options: opts });
|
|
305
|
+
}
|
|
226
306
|
});
|
|
227
307
|
} 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
|
-
});
|
|
308
|
+
const searchRoots: (HTMLElement | ShadowRoot)[] = [];
|
|
309
|
+
const r = root || 'auto';
|
|
310
|
+
if (r === 'auto') searchRoots.push((this.shadowRoot || (this as any)) as any);
|
|
311
|
+
else if (r === 'light') searchRoots.push(this as any);
|
|
312
|
+
else if (r === 'shadow') {
|
|
313
|
+
if (this.shadowRoot) searchRoots.push(this.shadowRoot as any);
|
|
314
|
+
} else if (r === 'all') {
|
|
315
|
+
searchRoots.push(this as any);
|
|
316
|
+
if (this.shadowRoot) searchRoots.push(this.shadowRoot as any);
|
|
249
317
|
}
|
|
250
318
|
|
|
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);
|
|
319
|
+
searchRoots.forEach(sr => {
|
|
320
|
+
sr.querySelectorAll(selector).forEach(el => bindTargets.push(el));
|
|
262
321
|
});
|
|
263
322
|
}
|
|
323
|
+
|
|
324
|
+
bindTargets.forEach(target => {
|
|
325
|
+
const handler = (event: Event) => {
|
|
326
|
+
if (stopPropagation) event.stopPropagation();
|
|
327
|
+
if (stopImmediatePropagation) event.stopImmediatePropagation();
|
|
328
|
+
if (preventDefault) event.preventDefault();
|
|
329
|
+
const currentHostSet = SwcUtils.getHostSet(this as any);
|
|
330
|
+
this._invokeLifecycleMethod(meta.propertyKey, currentHostSet, [event, currentHostSet]);
|
|
331
|
+
};
|
|
332
|
+
target.addEventListener(type, handler, opts);
|
|
333
|
+
if (removeOnDisconnected === true || target === currentWin || target === currentDoc) {
|
|
334
|
+
this._boundListeners.push({ target, type, handler, options: opts });
|
|
335
|
+
}
|
|
336
|
+
});
|
|
264
337
|
});
|
|
265
338
|
|
|
266
339
|
if (super.connectedCallback) await super.connectedCallback();
|
|
@@ -279,6 +352,9 @@ export const elementDefine =
|
|
|
279
352
|
const bMethods = findAllLifecycleMetadata(this, ON_BEFORE_DISCONNECTED_METADATA_KEY);
|
|
280
353
|
if (bMethods) bMethods.forEach(m => this._invokeLifecycleMethod(m, hostSet));
|
|
281
354
|
|
|
355
|
+
this._boundListeners.forEach(l => l.target.removeEventListener(l.type, l.handler, l.options));
|
|
356
|
+
this._boundListeners = [];
|
|
357
|
+
|
|
282
358
|
if (super.disconnectedCallback) super.disconnectedCallback();
|
|
283
359
|
|
|
284
360
|
const aMethods = findAllLifecycleMetadata(this, ON_AFTER_DISCONNECTED_METADATA_KEY);
|
|
@@ -354,6 +430,84 @@ export const elementDefine =
|
|
|
354
430
|
}
|
|
355
431
|
});
|
|
356
432
|
|
|
433
|
+
// --- @replaceChildren wrapping logic ---
|
|
434
|
+
replaceChildrenMap.forEach((selector, methodName) => {
|
|
435
|
+
const original = NewClass.prototype[methodName as any];
|
|
436
|
+
if (typeof original === 'function') {
|
|
437
|
+
NewClass.prototype[methodName as any] = function (...args: any[]) {
|
|
438
|
+
const res = original.apply(this, args);
|
|
439
|
+
if (res !== undefined) {
|
|
440
|
+
const conf = getElementConfig(this);
|
|
441
|
+
const currentWin = conf?.window || window;
|
|
442
|
+
const currentDoc = currentWin.document;
|
|
443
|
+
|
|
444
|
+
let targetEl: Element | null = null;
|
|
445
|
+
if (selector === ':host' || !selector) {
|
|
446
|
+
targetEl = this;
|
|
447
|
+
} else {
|
|
448
|
+
targetEl = (this.shadowRoot || this).querySelector(selector);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
if (targetEl) {
|
|
452
|
+
const nodes: (Node | string)[] = Array.isArray(res) ? res : [res];
|
|
453
|
+
const normalizedNodes = nodes.map(n => {
|
|
454
|
+
if (typeof n === 'string') {
|
|
455
|
+
const t = currentDoc.createElement('template');
|
|
456
|
+
t.innerHTML = n;
|
|
457
|
+
return t.content;
|
|
458
|
+
}
|
|
459
|
+
return n as Node;
|
|
460
|
+
});
|
|
461
|
+
(targetEl as any).replaceChildren(...normalizedNodes);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
return res;
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
// --- @appendChild wrapping logic ---
|
|
470
|
+
appendChildMap.forEach((meta, methodName) => {
|
|
471
|
+
const { selector, options } = meta;
|
|
472
|
+
const original = NewClass.prototype[methodName as any];
|
|
473
|
+
if (typeof original === 'function') {
|
|
474
|
+
NewClass.prototype[methodName as any] = function (...args: any[]) {
|
|
475
|
+
const res = original.apply(this, args);
|
|
476
|
+
if (res !== undefined) {
|
|
477
|
+
const conf = getElementConfig(this);
|
|
478
|
+
const currentWin = conf?.window || window;
|
|
479
|
+
const currentDoc = currentWin.document;
|
|
480
|
+
|
|
481
|
+
let targetEl: Element | null = null;
|
|
482
|
+
if (selector === ':host' || !selector) {
|
|
483
|
+
targetEl = this;
|
|
484
|
+
} else {
|
|
485
|
+
targetEl = (this.shadowRoot || this).querySelector(selector);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
if (targetEl) {
|
|
489
|
+
const nodes: (Node | string)[] = Array.isArray(res) ? res : [res];
|
|
490
|
+
const normalizedNodes = nodes.map(n => {
|
|
491
|
+
if (typeof n === 'string') {
|
|
492
|
+
const t = currentDoc.createElement('template');
|
|
493
|
+
t.innerHTML = n;
|
|
494
|
+
return t.content;
|
|
495
|
+
}
|
|
496
|
+
return n as Node;
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
const pos = options.position || 'lastChild';
|
|
500
|
+
if (pos === 'lastChild') (targetEl as any).append(...normalizedNodes);
|
|
501
|
+
else if (pos === 'firstChild') (targetEl as any).prepend(...normalizedNodes);
|
|
502
|
+
else if (pos === 'nextSibling') (targetEl as any).after(...normalizedNodes);
|
|
503
|
+
else if (pos === 'previousSibling') (targetEl as any).before(...normalizedNodes);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
return res;
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
});
|
|
510
|
+
|
|
357
511
|
const originalProto = constructor.prototype;
|
|
358
512
|
const newProto = NewClass.prototype;
|
|
359
513
|
Object.getOwnPropertyNames(originalProto).forEach(name => {
|
package/src/decorators/index.ts
CHANGED
|
@@ -3,7 +3,8 @@ export * from './elementDefine';
|
|
|
3
3
|
export * from './changedAttribute';
|
|
4
4
|
export * from './setAttribute';
|
|
5
5
|
export * from './emitCustomEvent';
|
|
6
|
-
export * from './
|
|
6
|
+
export * from './replaceChildren';
|
|
7
|
+
export * from './appendChild';
|
|
7
8
|
export * from './lifecycles';
|
|
8
9
|
export * from './query';
|
|
9
10
|
export * from './queryAll';
|
|
@@ -11,9 +11,12 @@ export interface InnerHtmlMetadata {
|
|
|
11
11
|
|
|
12
12
|
export const INNER_HTML_METADATA_KEY = Symbol('simple-web-component:inner-html');
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
/**
|
|
15
|
+
* @onConnectedInnerHtml decorator to define the initial HTML content when connected.
|
|
16
|
+
*/
|
|
17
|
+
export function onConnectedInnerHtml(options: InnerHtmlOptions): MethodDecorator;
|
|
18
|
+
export function onConnectedInnerHtml(target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor): void;
|
|
19
|
+
export function onConnectedInnerHtml(arg1?: InnerHtmlOptions | Object, arg2?: string | symbol, arg3?: PropertyDescriptor): MethodDecorator | void {
|
|
17
20
|
const decorator = (options: InnerHtmlOptions, target: Object, propertyKey: string | symbol) => {
|
|
18
21
|
const constructor = target.constructor;
|
|
19
22
|
let list = ReflectUtils.getMetadata<InnerHtmlMetadata[]>(INNER_HTML_METADATA_KEY, constructor);
|
|
@@ -25,17 +28,15 @@ export function innerHtml(arg1?: InnerHtmlOptions | Object, arg2?: string | symb
|
|
|
25
28
|
};
|
|
26
29
|
|
|
27
30
|
if (arg2) {
|
|
28
|
-
// Used as @innerHtml
|
|
29
31
|
return decorator({}, arg1!, arg2);
|
|
30
32
|
}
|
|
31
33
|
|
|
32
|
-
// Used as @innerHtml(options)
|
|
33
34
|
return (target: Object, propertyKey: string | symbol) => {
|
|
34
35
|
decorator((arg1 as InnerHtmlOptions) || {}, target, propertyKey);
|
|
35
36
|
};
|
|
36
37
|
}
|
|
37
38
|
|
|
38
|
-
export const
|
|
39
|
+
export const getOnConnectedInnerHtmlMetadata = (target: any): InnerHtmlMetadata[] | undefined => {
|
|
39
40
|
const constructor = target instanceof Function ? target : target.constructor;
|
|
40
41
|
return ReflectUtils.getMetadata(INNER_HTML_METADATA_KEY, constructor);
|
|
41
42
|
};
|
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') {
|
|
@@ -48,6 +66,18 @@ export function query(selector: string, options: QueryOptions = {}): PropertyDec
|
|
|
48
66
|
// auto
|
|
49
67
|
return (this.shadowRoot || this).querySelector(selector);
|
|
50
68
|
},
|
|
69
|
+
set(this: HTMLElement, nv: any) {
|
|
70
|
+
if (nv === null || nv === undefined) {
|
|
71
|
+
const r = options.root || 'auto';
|
|
72
|
+
let targetEl: Element | null = null;
|
|
73
|
+
if (r === 'shadow') targetEl = this.shadowRoot ? this.shadowRoot.querySelector(selector) : null;
|
|
74
|
+
else if (r === 'light') targetEl = this.querySelector(selector);
|
|
75
|
+
else if (r === 'all') targetEl = this.shadowRoot?.querySelector(selector) || this.querySelector(selector);
|
|
76
|
+
else targetEl = (this.shadowRoot || this).querySelector(selector);
|
|
77
|
+
|
|
78
|
+
if (targetEl) targetEl.remove();
|
|
79
|
+
}
|
|
80
|
+
},
|
|
51
81
|
enumerable: true,
|
|
52
82
|
configurable: true
|
|
53
83
|
});
|