@dooboostore/simple-web-component 1.0.29 → 1.0.31
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 +651 -27
- package/dist/cjs/decorators/elementDefine.js +5 -3
- package/dist/cjs/decorators/elementDefine.js.map +2 -2
- package/dist/cjs/decorators/emitCustomEvent.js.map +2 -2
- package/dist/cjs/decorators/index.js +1 -0
- package/dist/cjs/decorators/index.js.map +2 -2
- package/dist/cjs/decorators/setProperty.js +97 -0
- package/dist/cjs/decorators/setProperty.js.map +7 -0
- package/dist/cjs/elements/SwcAppEngine.js +3 -3
- package/dist/cjs/elements/SwcAppEngine.js.map +2 -2
- package/dist/cjs/elements/SwcAppMixin.js +1 -8
- package/dist/cjs/elements/SwcAppMixin.js.map +2 -2
- package/dist/cjs/elements/SwcAsync.js +37 -53
- package/dist/cjs/elements/SwcAsync.js.map +2 -2
- package/dist/cjs/elements/SwcChoose.js +55 -60
- package/dist/cjs/elements/SwcChoose.js.map +2 -2
- package/dist/cjs/elements/SwcIf.js +31 -60
- package/dist/cjs/elements/SwcIf.js.map +2 -2
- package/dist/cjs/elements/SwcLoop.js +20 -43
- package/dist/cjs/elements/SwcLoop.js.map +2 -2
- package/dist/cjs/elements/SwcSubTemplates.js +0 -27
- package/dist/cjs/elements/SwcSubTemplates.js.map +2 -2
- package/dist/cjs/elements/register.js +216 -128
- package/dist/cjs/elements/register.js.map +2 -2
- package/dist/cjs/index.js +2 -1
- package/dist/cjs/index.js.map +2 -2
- package/dist/cjs/router/ElementRouter.js +1 -8
- package/dist/cjs/router/ElementRouter.js.map +2 -2
- package/dist/cjs/types.js.map +1 -1
- package/dist/cjs/utils/Utils.js +26 -10
- package/dist/cjs/utils/Utils.js.map +2 -2
- package/dist/esm/decorators/elementDefine.js +5 -3
- package/dist/esm/decorators/elementDefine.js.map +2 -2
- package/dist/esm/decorators/emitCustomEvent.js.map +2 -2
- package/dist/esm/decorators/index.js +1 -0
- package/dist/esm/decorators/index.js.map +2 -2
- package/dist/esm/decorators/setProperty.js +78 -0
- package/dist/esm/decorators/setProperty.js.map +7 -0
- package/dist/esm/elements/SwcAppEngine.js +3 -3
- package/dist/esm/elements/SwcAppEngine.js.map +2 -2
- package/dist/esm/elements/SwcAppMixin.js +1 -8
- package/dist/esm/elements/SwcAppMixin.js.map +2 -2
- package/dist/esm/elements/SwcAsync.js +37 -34
- package/dist/esm/elements/SwcAsync.js.map +2 -2
- package/dist/esm/elements/SwcChoose.js +55 -41
- package/dist/esm/elements/SwcChoose.js.map +2 -2
- package/dist/esm/elements/SwcIf.js +31 -41
- package/dist/esm/elements/SwcIf.js.map +2 -2
- package/dist/esm/elements/SwcLoop.js +20 -24
- package/dist/esm/elements/SwcLoop.js.map +2 -2
- package/dist/esm/elements/SwcSubTemplates.js +0 -8
- package/dist/esm/elements/SwcSubTemplates.js.map +2 -2
- package/dist/esm/elements/register.js +216 -128
- package/dist/esm/elements/register.js.map +2 -2
- package/dist/esm/index.js +2 -1
- package/dist/esm/index.js.map +2 -2
- package/dist/esm/router/ElementRouter.js +1 -8
- package/dist/esm/router/ElementRouter.js.map +2 -2
- package/dist/esm/types.js.map +1 -1
- package/dist/esm/utils/Utils.js +26 -10
- package/dist/esm/utils/Utils.js.map +2 -2
- package/dist/esm-bundle/dooboostore-simple-web-component.esm.js +105 -13
- package/dist/esm-bundle/dooboostore-simple-web-component.esm.js.map +4 -4
- package/dist/types/decorators/elementDefine.d.ts.map +1 -1
- package/dist/types/decorators/emitCustomEvent.d.ts.map +1 -1
- package/dist/types/decorators/index.d.ts +1 -0
- package/dist/types/decorators/index.d.ts.map +1 -1
- package/dist/types/decorators/setProperty.d.ts +13 -0
- package/dist/types/decorators/setProperty.d.ts.map +1 -0
- package/dist/types/elements/SwcAppMixin.d.ts.map +1 -1
- package/dist/types/elements/SwcAsync.d.ts +1 -11
- package/dist/types/elements/SwcAsync.d.ts.map +1 -1
- package/dist/types/elements/SwcChoose.d.ts +1 -13
- package/dist/types/elements/SwcChoose.d.ts.map +1 -1
- package/dist/types/elements/SwcIf.d.ts +1 -13
- package/dist/types/elements/SwcIf.d.ts.map +1 -1
- package/dist/types/elements/SwcLoop.d.ts +1 -13
- package/dist/types/elements/SwcLoop.d.ts.map +1 -1
- package/dist/types/elements/SwcSubTemplates.d.ts +6 -6
- package/dist/types/elements/SwcSubTemplates.d.ts.map +1 -1
- package/dist/types/elements/register.d.ts +3 -1
- package/dist/types/elements/register.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/router/ElementRouter.d.ts.map +1 -1
- package/dist/types/types.d.ts +3 -0
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/utils/Utils.d.ts +10 -0
- package/dist/types/utils/Utils.d.ts.map +1 -1
- package/dist/umd-bundle/dooboostore-simple-web-component.umd.js +105 -13
- package/dist/umd-bundle/dooboostore-simple-web-component.umd.js.map +4 -4
- package/package.json +3 -3
- package/src/decorators/elementDefine.ts +10 -3
- package/src/decorators/emitCustomEvent.ts +1 -0
- package/src/decorators/index.ts +1 -0
- package/src/decorators/setProperty.ts +97 -0
- package/src/elements/SwcAppEngine.ts +3 -3
- package/src/elements/SwcAppMixin.ts +7 -28
- package/src/elements/SwcAsync.ts +46 -30
- package/src/elements/SwcChoose.ts +82 -38
- package/src/elements/SwcIf.ts +38 -38
- package/src/elements/SwcLoop.ts +24 -19
- package/src/elements/SwcSubTemplates.ts +6 -6
- package/src/elements/register.ts +261 -121
- package/src/index.ts +3 -1
- package/src/router/ElementRouter.ts +1 -12
- package/src/types.ts +3 -1
- package/src/utils/Utils.ts +40 -19
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.31",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/cjs/index.js",
|
|
@@ -93,9 +93,9 @@
|
|
|
93
93
|
"webpack-cli": "^5.1.4",
|
|
94
94
|
"webpack-dev-server": "^5.2.0",
|
|
95
95
|
"@dooboostore/core": "1.0.30",
|
|
96
|
-
"@dooboostore/
|
|
96
|
+
"@dooboostore/core-node": "1.0.12",
|
|
97
97
|
"@dooboostore/core-web": "1.0.10",
|
|
98
|
-
"@dooboostore/
|
|
98
|
+
"@dooboostore/simple-boot": "1.0.54"
|
|
99
99
|
},
|
|
100
100
|
"scripts": {
|
|
101
101
|
"clean": "rm -rf dist",
|
|
@@ -91,6 +91,7 @@ export const ensureInit = (inst: any) => {
|
|
|
91
91
|
|
|
92
92
|
// Call constructor script if present
|
|
93
93
|
const hostSet = SwcUtils.getHostSet(inst);
|
|
94
|
+
const appHosts = SwcUtils.findAllParentAppHostsDirect(inst);
|
|
94
95
|
inst._executeSwcScript?.('swc-on-constructor', hostSet);
|
|
95
96
|
|
|
96
97
|
// Call @onInitialize lifecycle methods
|
|
@@ -176,14 +177,14 @@ const setupPrototype = (proto: any) => {
|
|
|
176
177
|
ensureInit(this);
|
|
177
178
|
if (typeof (this as any)[methodName] !== 'function') return;
|
|
178
179
|
const useHostSet = hostSet ?? SwcUtils.getHostSet(this);
|
|
179
|
-
const app = useHostSet?.$appHost
|
|
180
|
+
const app = useHostSet?.$appHost?.simpleApplication;
|
|
180
181
|
// console.log('---->hh',app, this, methodName);
|
|
181
182
|
if (app) {
|
|
182
183
|
const otherStorage = new Map<any, any>();
|
|
183
184
|
const situations = new SituationTypeContainers([new SituationTypeContainer({ situationType: InjectSituationType.HOST_SET, data: useHostSet }), new SituationTypeContainer({ situationType: InjectSituationType.APP_HOST, data: useHostSet.$appHost }), new SituationTypeContainer({ situationType: InjectSituationType.APP_HOSTS, data: useHostSet.$appHosts }), new SituationTypeContainer({ situationType: InjectSituationType.HOST, data: useHostSet.$host }), new SituationTypeContainer({ situationType: InjectSituationType.HOSTS, data: useHostSet.$hosts }), new SituationTypeContainer({ situationType: InjectSituationType.FIRST_HOST, data: useHostSet.$firstHost }), new SituationTypeContainer({ situationType: InjectSituationType.LAST_HOST, data: useHostSet.$lastHost }), new SituationTypeContainer({ situationType: InjectSituationType.FIRST_APP_HOST, data: useHostSet.$firstAppHost }), new SituationTypeContainer({ situationType: InjectSituationType.LAST_APP_HOST, data: useHostSet.$lastAppHost })]);
|
|
184
185
|
otherStorage.set(SituationTypeContainers, situations);
|
|
185
186
|
|
|
186
|
-
return app.simstanceManager.
|
|
187
|
+
return app.simstanceManager.executeBindParameterSim(
|
|
187
188
|
{
|
|
188
189
|
target: this,
|
|
189
190
|
targetKey: methodName,
|
|
@@ -331,7 +332,10 @@ export const elementDefine =
|
|
|
331
332
|
let sContent = '',
|
|
332
333
|
lContent = '';
|
|
333
334
|
for (const meta of connectedInnerHtmlList) {
|
|
334
|
-
const res = (this as any)[meta.propertyKey]();
|
|
335
|
+
// const res = (this as any)[meta.propertyKey]();
|
|
336
|
+
const res = (this as any)._invokeLifecycleMethod(meta.propertyKey, hostSet);
|
|
337
|
+
// console.log('------res0', res);
|
|
338
|
+
// console.log('------res1', res1);
|
|
335
339
|
if (res !== undefined) {
|
|
336
340
|
if (meta.options.useShadow) sContent += res;
|
|
337
341
|
else lContent += res;
|
|
@@ -494,6 +498,9 @@ export const elementDefine =
|
|
|
494
498
|
(this as any).__swc_connected = true;
|
|
495
499
|
|
|
496
500
|
// Register to appHost LAST when all lifecycle completed
|
|
501
|
+
// console.log('!!!!!!!!!!!!!!', this, appHost)
|
|
502
|
+
const appHostss = SwcUtils.findAllParentAppHostsDirect(this)
|
|
503
|
+
// const nAppHostSet = SwcUtils.getHostSet(this as any);
|
|
497
504
|
if (appHost && typeof (appHost as any)._connected === 'function') {
|
|
498
505
|
(appHost as any)._connected(this);
|
|
499
506
|
} else if (appHost && typeof (appHost as any)) {
|
|
@@ -52,6 +52,7 @@ export function emitCustomEvent(selectorOrTarget: string, type: string, options:
|
|
|
52
52
|
const hostSet = SwcUtils.getHostSet(this as any);
|
|
53
53
|
const conf = getElementConfig(this);
|
|
54
54
|
const currentWin = (this as any)._resolveWindow?.(conf) || ((typeof window !== 'undefined' ? window : undefined) as Window);
|
|
55
|
+
// console.log('emitEEEEEEEEEEE', hostSet);
|
|
55
56
|
|
|
56
57
|
const eventTargets: EventTarget[] = [];
|
|
57
58
|
if (selectorOrTarget === ':window') eventTargets.push(currentWin);
|
package/src/decorators/index.ts
CHANGED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { ReflectUtils } from '@dooboostore/core';
|
|
2
|
+
import { ensureInit } from './elementDefine';
|
|
3
|
+
import { SwcUtils } from '../utils/Utils';
|
|
4
|
+
import { SwcQueryOptions } from '../types';
|
|
5
|
+
|
|
6
|
+
export interface PropertySetOptions extends SwcQueryOptions {
|
|
7
|
+
name?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface PropertySetMetadata {
|
|
11
|
+
propertyKey: string | symbol;
|
|
12
|
+
selector: string;
|
|
13
|
+
options: PropertySetOptions;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const PROPERTY_SET_METADATA_KEY = Symbol.for('simple-web-component:property-set');
|
|
17
|
+
|
|
18
|
+
export const setProperty = (selector: string, propertyNameOrOptions?: string | PropertySetOptions, options: PropertySetOptions = {}): MethodDecorator => {
|
|
19
|
+
return (target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor) => {
|
|
20
|
+
const constructor = target.constructor;
|
|
21
|
+
let metaList = ReflectUtils.getOwnMetadata(PROPERTY_SET_METADATA_KEY, constructor) as Map<string | symbol, PropertySetMetadata>;
|
|
22
|
+
if (!metaList) {
|
|
23
|
+
metaList = new Map<string | symbol, PropertySetMetadata>();
|
|
24
|
+
ReflectUtils.defineMetadata(PROPERTY_SET_METADATA_KEY, metaList, constructor);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
let propertyName = typeof propertyNameOrOptions === 'string' ? propertyNameOrOptions : undefined;
|
|
28
|
+
let finalOptions = options;
|
|
29
|
+
|
|
30
|
+
if (typeof propertyNameOrOptions === 'object' && propertyNameOrOptions !== null) {
|
|
31
|
+
finalOptions = propertyNameOrOptions as PropertySetOptions;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
metaList.set(propertyKey, {
|
|
35
|
+
propertyKey,
|
|
36
|
+
selector,
|
|
37
|
+
options: { ...finalOptions, name: propertyName }
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const original = descriptor.value;
|
|
41
|
+
descriptor.value = async function (...args: any[]) {
|
|
42
|
+
ensureInit(this);
|
|
43
|
+
const res = await (original as any).apply(this, args);
|
|
44
|
+
if (res !== undefined) {
|
|
45
|
+
const win = (this as any).ownerDocument?.defaultView || window;
|
|
46
|
+
const host = this as unknown as HTMLElement;
|
|
47
|
+
const hostSet = { ...SwcUtils.getHelperAndHostSet(win, host), $this: this };
|
|
48
|
+
const r = finalOptions.root || 'auto';
|
|
49
|
+
const targetEls: HTMLElement[] = [];
|
|
50
|
+
|
|
51
|
+
const applyRoot = (t: any) => {
|
|
52
|
+
if (!t || !(t instanceof win.HTMLElement)) return;
|
|
53
|
+
targetEls.push(t);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
if (selector === ':parentHost') applyRoot(hostSet.$parentHost);
|
|
57
|
+
else if (selector === ':appHost') applyRoot(hostSet.$appHost);
|
|
58
|
+
else if (selector === ':firstHost') applyRoot(hostSet.$firstHost);
|
|
59
|
+
else if (selector === ':lastHost') applyRoot(hostSet.$lastHost);
|
|
60
|
+
else if (selector === ':firstAppHost') applyRoot(hostSet.$firstAppHost);
|
|
61
|
+
else if (selector === ':lastAppHost') applyRoot(hostSet.$lastAppHost);
|
|
62
|
+
else if (selector === ':hosts') hostSet.$hosts.forEach(applyRoot);
|
|
63
|
+
else if (selector === ':appHosts') hostSet.$appHosts.forEach(applyRoot);
|
|
64
|
+
else if (selector === ':host' || !selector) applyRoot(host);
|
|
65
|
+
else {
|
|
66
|
+
const targetRoot = r === 'auto' ? (host.shadowRoot || host) : (r === 'shadow' ? host.shadowRoot : host);
|
|
67
|
+
if (targetRoot) {
|
|
68
|
+
const found = targetRoot.querySelectorAll(selector);
|
|
69
|
+
if (found) targetEls.push(...(Array.from(found) as HTMLElement[]));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
targetEls.forEach(targetEl => {
|
|
74
|
+
const resolvedRes = typeof res === 'function' ? (res as any)(targetEl, hostSet) : res;
|
|
75
|
+
|
|
76
|
+
if (propertyName) {
|
|
77
|
+
// Case: @setProperty(selector, 'propertyName')
|
|
78
|
+
(targetEl as any)[propertyName] = resolvedRes;
|
|
79
|
+
} else if (typeof resolvedRes === 'object' && resolvedRes !== null && !Array.isArray(resolvedRes)) {
|
|
80
|
+
// Case: @setProperty(selector) with Object return - assign multiple properties
|
|
81
|
+
Object.entries(resolvedRes as any).forEach(([prop, val]) => {
|
|
82
|
+
const finalVal = typeof val === 'function' ? val(targetEl, hostSet) : val;
|
|
83
|
+
(targetEl as any)[prop] = finalVal;
|
|
84
|
+
});
|
|
85
|
+
} else {
|
|
86
|
+
// Simple value - assign to 'value' property by default
|
|
87
|
+
(targetEl as any).value = resolvedRes;
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
};
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
export const setPropertyHost = (propertyNameOrOptions?: string | PropertySetOptions, options: PropertySetOptions = {}): MethodDecorator => {
|
|
96
|
+
return setProperty(':host', propertyNameOrOptions, options);
|
|
97
|
+
};
|
|
@@ -42,11 +42,11 @@ export class SwcAppEngine {
|
|
|
42
42
|
|
|
43
43
|
// Router Selection
|
|
44
44
|
if (this._config.routeType === 'path') {
|
|
45
|
-
this.router = new PathRouter({ window: this._config.window });
|
|
45
|
+
this.router = new PathRouter({ window: this._config.window, firstUrl: this._config.path });
|
|
46
46
|
} else if (this._config.routeType === 'hash') {
|
|
47
|
-
this.router = new HashRouter({ window: this._config.window });
|
|
47
|
+
this.router = new HashRouter({ window: this._config.window, firstUrl: this._config.path });
|
|
48
48
|
} else if (this._config.routeType === 'element') {
|
|
49
|
-
this.router = new ElementRouter({ window: this._config.window });
|
|
49
|
+
this.router = new ElementRouter({ window: this._config.window, firstUrl: this._config.path });
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
// console.log('vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv', this.router)
|
|
@@ -28,11 +28,6 @@ export function SwcAppMixin<T extends { new (...args: any[]): HTMLElement }>(Bas
|
|
|
28
28
|
return this.__swc_engine.router;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
// constructor() {
|
|
32
|
-
// super();
|
|
33
|
-
// console.log('cccccccccccccccccccccc');
|
|
34
|
-
// }
|
|
35
|
-
|
|
36
31
|
_invokeRouteChangeSubscribers(instance: any, re: RouterEventType) {
|
|
37
32
|
const routeChangeSubscribers = getSubscribeSwcAppRouteChangeMetadata(instance);
|
|
38
33
|
if (routeChangeSubscribers && Array.isArray(routeChangeSubscribers)) {
|
|
@@ -70,14 +65,14 @@ export function SwcAppMixin<T extends { new (...args: any[]): HTMLElement }>(Bas
|
|
|
70
65
|
|
|
71
66
|
// Invoke method if conditions pass
|
|
72
67
|
if (pathMatched && filterPassed) {
|
|
73
|
-
instance[methodName]?.(re, pathData
|
|
68
|
+
instance[methodName]?.({...re, pathData: pathData});
|
|
74
69
|
}
|
|
75
70
|
});
|
|
76
71
|
}
|
|
77
72
|
}
|
|
78
73
|
|
|
79
74
|
_connected(instance: any) {
|
|
80
|
-
// console.log('appmixin--> connected', instance);
|
|
75
|
+
// console.log('appmixin--> connected', this, instance);
|
|
81
76
|
if (instance) {
|
|
82
77
|
this._swc_connected_instance.add(instance);
|
|
83
78
|
if (this.simpleApplication) {
|
|
@@ -153,35 +148,19 @@ export function SwcAppMixin<T extends { new (...args: any[]): HTMLElement }>(Bas
|
|
|
153
148
|
this._invokeOnConnectedSwcAppStarted(instance);
|
|
154
149
|
});
|
|
155
150
|
|
|
156
|
-
// Safari fix: Manually scan DOM tree and register any SWC components that couldn't find parent
|
|
157
|
-
// This handles the case where is="" elements get upgraded in child-first order
|
|
158
|
-
// const allElements = (this as any).querySelectorAll('*');
|
|
159
|
-
// for (const el of allElements) {
|
|
160
|
-
// const isConnected = (el as any).__swc_connected;
|
|
161
|
-
// if (!isConnected && typeof (el as any)._resolveWindow === 'function') {
|
|
162
|
-
// // Element is an SWC component but hasn't registered with app yet
|
|
163
|
-
// try {
|
|
164
|
-
// this._connected(el);
|
|
165
|
-
// if ((el as any)._invokeOnConnectedSwcAppStarted) {
|
|
166
|
-
// this._invokeOnConnectedSwcAppStarted(el);
|
|
167
|
-
// }
|
|
168
|
-
// } catch (e) {
|
|
169
|
-
// console.warn('[SWC] Error registering discovered element:', el.tagName, e);
|
|
170
|
-
// }
|
|
171
|
-
// }
|
|
172
|
-
// }
|
|
173
151
|
|
|
174
152
|
// Subscribe to router changes after connect
|
|
175
153
|
if (!this._routerSubscription && this.router) {
|
|
176
154
|
this._routerSubscription = this.router.observable.subscribe((route: RouterEventType) => {
|
|
177
|
-
|
|
155
|
+
//@ts-ignore
|
|
156
|
+
// console.log('Route changed:', route, this._swcId);
|
|
178
157
|
this._handleRouteChange(route);
|
|
179
158
|
});
|
|
180
159
|
// Navigate to initial path if specified
|
|
181
160
|
const config = this.__swc_engine.config;
|
|
182
|
-
if (config?.path) {
|
|
183
|
-
|
|
184
|
-
}
|
|
161
|
+
// if (config?.path) {
|
|
162
|
+
// this.router.go(config.path);
|
|
163
|
+
// }
|
|
185
164
|
}
|
|
186
165
|
}
|
|
187
166
|
|
package/src/elements/SwcAsync.ts
CHANGED
|
@@ -2,39 +2,42 @@ import { changedAttributeHost } from '../decorators/changedAttributeHost';
|
|
|
2
2
|
import { SwcUtils } from '../utils/Utils';
|
|
3
3
|
import { FunctionUtils } from '@dooboostore/core';
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
class SwcAsync extends HTMLTemplateElement {
|
|
6
6
|
private _nodes: Node[] = [];
|
|
7
7
|
private _value: any = null;
|
|
8
8
|
|
|
9
|
-
private _resolvePortal(): Element | null {
|
|
10
|
-
const portalScript = this.getAttribute('on-get-portal');
|
|
11
|
-
if (!portalScript) return this.parentElement;
|
|
12
|
-
|
|
13
|
-
const win = this.ownerDocument?.defaultView || window;
|
|
14
|
-
const res = FunctionUtils.executeReturn({
|
|
15
|
-
script: portalScript,
|
|
16
|
-
context: this,
|
|
17
|
-
args: SwcUtils.getHelperAndHostSet(win, this)
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
if (res instanceof HTMLElement) return res;
|
|
21
|
-
if (typeof res === 'string') return win.document.querySelector(res);
|
|
22
|
-
return this.parentElement;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
9
|
private _executeCloneCallback(attr: string, args: any) {
|
|
26
10
|
const script = this.getAttribute(attr);
|
|
27
11
|
if (!script) return;
|
|
28
12
|
FunctionUtils.execute({ script, context: this, args });
|
|
29
13
|
}
|
|
30
14
|
|
|
31
|
-
@changedAttributeHost('
|
|
32
|
-
private refresh() {
|
|
33
|
-
|
|
34
|
-
if (!
|
|
35
|
-
|
|
36
|
-
|
|
15
|
+
@changedAttributeHost('value')
|
|
16
|
+
private refresh(valueAttr?: string | null) {
|
|
17
|
+
valueAttr ??= this.getAttribute('value');
|
|
18
|
+
if (!valueAttr) {
|
|
19
|
+
this.render('default');
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
37
22
|
|
|
23
|
+
// {{ }} 브레이스로 감싸진 값은 스크립트 실행
|
|
24
|
+
if (valueAttr.startsWith('{{') && valueAttr.endsWith('}}')) {
|
|
25
|
+
const script = valueAttr.slice(2, -2).trim();
|
|
26
|
+
const win = this.ownerDocument?.defaultView || window;
|
|
27
|
+
const result = FunctionUtils.executeReturn({
|
|
28
|
+
script,
|
|
29
|
+
context: this,
|
|
30
|
+
args: SwcUtils.getHelperAndHostSet(win, this)
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
this.executeAsync(result);
|
|
34
|
+
} else {
|
|
35
|
+
// 일반 문자열 값 또는 직접 Promise 속성
|
|
36
|
+
this.executeAsync(valueAttr);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
private executeAsync(result: any) {
|
|
38
41
|
if (result instanceof Promise) {
|
|
39
42
|
this.render('loading');
|
|
40
43
|
result
|
|
@@ -51,7 +54,7 @@ export class SwcAsync extends HTMLTemplateElement {
|
|
|
51
54
|
|
|
52
55
|
private render(state: string) {
|
|
53
56
|
this.cleanup();
|
|
54
|
-
const portal = this.
|
|
57
|
+
const portal = this.parentElement;
|
|
55
58
|
if (!portal) return;
|
|
56
59
|
|
|
57
60
|
const win = this.ownerDocument?.defaultView || window;
|
|
@@ -64,18 +67,31 @@ export class SwcAsync extends HTMLTemplateElement {
|
|
|
64
67
|
const elements = this._nodes.filter(n => n.nodeType === 1) as HTMLElement[];
|
|
65
68
|
const groupArgs = { ...helperSet, $value: this._value, $nodes: this._nodes, $elements: elements, $firstElement: elements[0], $state: state };
|
|
66
69
|
|
|
70
|
+
// 부모 체인을 정상적으로 유지하기 위해 __swc_host 할당 제거
|
|
67
71
|
this._nodes.forEach((n, nodeIdx) => {
|
|
68
|
-
if (n instanceof HTMLElement) (n as any).__swc_host = this;
|
|
72
|
+
// if (n instanceof HTMLElement) (n as any).__swc_host = this; // 제거: 부모 체인 유지
|
|
69
73
|
this._executeCloneCallback('on-clone-node', { ...groupArgs, $node: n, $nodeIndex: nodeIdx, $isElement: n.nodeType === 1 });
|
|
74
|
+
|
|
75
|
+
// Attribute {{ }} 치환 - SwcChoose처럼 동적 치환
|
|
76
|
+
if (n instanceof HTMLElement) {
|
|
77
|
+
Array.from(n.attributes).forEach(attr => {
|
|
78
|
+
const value = attr.value || '';
|
|
79
|
+
if (value.startsWith('{{') && value.endsWith('}}')) {
|
|
80
|
+
const script = value.slice(2, -2).trim();
|
|
81
|
+
const result = FunctionUtils.executeReturn({
|
|
82
|
+
script,
|
|
83
|
+
context: n,
|
|
84
|
+
args: groupArgs
|
|
85
|
+
});
|
|
86
|
+
n.setAttribute(attr.name, String(result));
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
}
|
|
70
90
|
});
|
|
71
91
|
|
|
72
92
|
this._executeCloneCallback('on-clone-nodes', groupArgs);
|
|
73
93
|
|
|
74
|
-
|
|
75
|
-
portal.insertBefore(clone, this.nextSibling);
|
|
76
|
-
} else {
|
|
77
|
-
portal.appendChild(clone);
|
|
78
|
-
}
|
|
94
|
+
portal.insertBefore(clone, this.nextSibling);
|
|
79
95
|
}
|
|
80
96
|
}
|
|
81
97
|
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { changedAttributeHost } from '../decorators/changedAttributeHost';
|
|
2
2
|
import { SwcUtils } from '../utils/Utils';
|
|
3
3
|
import { FunctionUtils } from '@dooboostore/core';
|
|
4
|
+
import { SwcChooseInterface } from '../types';
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
class SwcChoose extends HTMLTemplateElement implements SwcChooseInterface {
|
|
6
7
|
private _nodes: Node[] = [];
|
|
7
8
|
private _value: any = null;
|
|
9
|
+
private _previousTemplate: HTMLTemplateElement | null = null;
|
|
8
10
|
|
|
9
11
|
get value(): any {
|
|
10
12
|
return this._value;
|
|
@@ -14,76 +16,118 @@ export class SwcChoose extends HTMLTemplateElement {
|
|
|
14
16
|
this.render();
|
|
15
17
|
}
|
|
16
18
|
|
|
17
|
-
private _resolvePortal(): Element | null {
|
|
18
|
-
const portalScript = this.getAttribute('on-get-portal');
|
|
19
|
-
if (!portalScript) return this.parentElement;
|
|
20
|
-
|
|
21
|
-
const win = this.ownerDocument?.defaultView || window;
|
|
22
|
-
const res = FunctionUtils.executeReturn({
|
|
23
|
-
script: portalScript,
|
|
24
|
-
context: this,
|
|
25
|
-
args: SwcUtils.getHelperAndHostSet(win, this)
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
if (res instanceof HTMLElement) return res;
|
|
29
|
-
if (typeof res === 'string') return win.document.querySelector(res);
|
|
30
|
-
return this.parentElement;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
19
|
private _executeCloneCallback(attr: string, args: any) {
|
|
34
20
|
const script = this.getAttribute(attr);
|
|
35
21
|
if (!script) return;
|
|
36
22
|
FunctionUtils.execute({ script, context: this, args });
|
|
37
23
|
}
|
|
38
24
|
|
|
39
|
-
@changedAttributeHost('
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
if (
|
|
25
|
+
@changedAttributeHost('value')
|
|
26
|
+
refresh(valueAttr?: string | null) {
|
|
27
|
+
valueAttr ??= this.getAttribute('value');
|
|
28
|
+
if (valueAttr === null) {
|
|
43
29
|
this.value = null;
|
|
44
30
|
return;
|
|
45
31
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
32
|
+
|
|
33
|
+
// {{ }} 브레이스로 감싸진 값은 스크립트 실행
|
|
34
|
+
if (valueAttr.startsWith('{{') && valueAttr.endsWith('}}')) {
|
|
35
|
+
const script = valueAttr.slice(2, -2).trim();
|
|
36
|
+
const win = this.ownerDocument?.defaultView || window;
|
|
37
|
+
const helperAndHostSet = SwcUtils.getHelperAndHostSet(win, SwcUtils.findNearestSwcHost(this));
|
|
38
|
+
// console.log('vvvvvvvvvvvvvvvvvv323', helperAndHostSet);
|
|
39
|
+
const result = FunctionUtils.executeReturn({ script, context: this, args: helperAndHostSet });
|
|
40
|
+
this.value = result;
|
|
41
|
+
// console.log('----------vv', this.value);
|
|
42
|
+
} else {
|
|
43
|
+
// 일반 문자열 값
|
|
44
|
+
this.value = valueAttr;
|
|
45
|
+
}
|
|
49
46
|
}
|
|
50
47
|
|
|
51
48
|
private render() {
|
|
52
|
-
this.
|
|
53
|
-
const portal = this._resolvePortal();
|
|
54
|
-
if (!portal) return;
|
|
55
|
-
|
|
49
|
+
// console.log('-------------', this.value);
|
|
56
50
|
const win = this.ownerDocument?.defaultView || window;
|
|
57
51
|
const helperSet = SwcUtils.getHelperAndHostSet(win, this);
|
|
58
|
-
|
|
59
52
|
const templates = Array.from(this.content.querySelectorAll('template'));
|
|
60
53
|
let selected: HTMLTemplateElement | null = null;
|
|
54
|
+
|
|
61
55
|
for (const t of templates) {
|
|
62
|
-
if (t.getAttribute('is') === 'swc-when'
|
|
63
|
-
|
|
64
|
-
|
|
56
|
+
if (t.getAttribute('is') === 'swc-when') {
|
|
57
|
+
let matches = false;
|
|
58
|
+
const value = t.getAttribute('value') || '';
|
|
59
|
+
|
|
60
|
+
// {{ }} 브레이스로 감싸진 값은 스크립트 실행
|
|
61
|
+
if (value.startsWith('{{') && value.endsWith('}}')) {
|
|
62
|
+
const script = value.slice(2, -2).trim();
|
|
63
|
+
const result = FunctionUtils.executeReturn({
|
|
64
|
+
script,
|
|
65
|
+
context: t,
|
|
66
|
+
args: { ...helperSet, $value: this._value }
|
|
67
|
+
});
|
|
68
|
+
// console.log('rrrrrrrrrrrrrr', result);
|
|
69
|
+
matches = result === true;
|
|
70
|
+
} else {
|
|
71
|
+
// 일반 문자열 비교 (기존 방식)
|
|
72
|
+
matches = String(value) === String(this._value);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (matches) {
|
|
76
|
+
selected = t;
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
65
79
|
}
|
|
66
80
|
}
|
|
81
|
+
|
|
67
82
|
const target = selected || (this.content.querySelector('template[is="swc-otherwise"]') as HTMLTemplateElement);
|
|
68
83
|
|
|
84
|
+
// skip-if-same attribute가 있고 이전과 같은 template이면 렌더링 스킵
|
|
85
|
+
// 최상위 skip-if-same 또는 각 when/otherwise의 skip-if-same 체크
|
|
86
|
+
const targetHasSkipIfSame = target?.hasAttribute('skip-if-same');
|
|
87
|
+
const shouldSkip = (this.hasAttribute('skip-if-same') || targetHasSkipIfSame) && this._previousTemplate === target;
|
|
88
|
+
if (shouldSkip) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
this.cleanup();
|
|
93
|
+
this._previousTemplate = target;
|
|
94
|
+
|
|
69
95
|
if (target) {
|
|
70
96
|
const clone = target.content.cloneNode(true) as DocumentFragment;
|
|
71
97
|
this._nodes = Array.from(clone.childNodes);
|
|
72
98
|
const elements = this._nodes.filter(n => n.nodeType === 1) as HTMLElement[];
|
|
73
99
|
const groupArgs = { ...helperSet, $value: this._value, $nodes: this._nodes, $elements: elements, $firstElement: elements[0] };
|
|
74
100
|
|
|
101
|
+
// 복제된 최상위 요소들은 SwcChoose의 직접 자식이 될 것이므로 __swc_host 설정 안 함
|
|
75
102
|
this._nodes.forEach((n, nodeIdx) => {
|
|
76
|
-
if (n instanceof HTMLElement) (n as any).__swc_host = this;
|
|
103
|
+
// if (n instanceof HTMLElement) (n as any).__swc_host = this; // 제거: 부모 체인 유지
|
|
77
104
|
this._executeCloneCallback('on-clone-node', { ...groupArgs, $node: n, $nodeIndex: nodeIdx, $isElement: n.nodeType === 1 });
|
|
105
|
+
|
|
106
|
+
// Attribute {{ }} 치환 - HTML string 차원에서 동적 치환
|
|
107
|
+
if (n instanceof HTMLElement) {
|
|
108
|
+
Array.from(n.attributes).forEach(attr => {
|
|
109
|
+
const value = attr.value || '';
|
|
110
|
+
if (value.startsWith('{{') && value.endsWith('}}')) {
|
|
111
|
+
const script = value.slice(2, -2).trim();
|
|
112
|
+
const result = FunctionUtils.executeReturn({
|
|
113
|
+
script,
|
|
114
|
+
context: n,
|
|
115
|
+
args: { ...groupArgs, $value: this._value }
|
|
116
|
+
});
|
|
117
|
+
n.setAttribute(attr.name, String(result));
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
}
|
|
78
121
|
});
|
|
79
122
|
|
|
80
123
|
this._executeCloneCallback('on-clone-nodes', groupArgs);
|
|
81
124
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
125
|
+
this.parentElement?.insertBefore(clone, this.nextSibling);
|
|
126
|
+
// if (portal === this.parentElement) {
|
|
127
|
+
// portal.insertBefore(clone, this.nextSibling);
|
|
128
|
+
// } else {
|
|
129
|
+
// portal.appendChild(clone);
|
|
130
|
+
// }
|
|
87
131
|
}
|
|
88
132
|
}
|
|
89
133
|
|
package/src/elements/SwcIf.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { changedAttributeHost } from '../decorators/changedAttributeHost';
|
|
|
2
2
|
import { SwcUtils } from '../utils/Utils';
|
|
3
3
|
import { FunctionUtils } from '@dooboostore/core';
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
class SwcIf extends HTMLTemplateElement {
|
|
6
6
|
private _value = false;
|
|
7
7
|
private _nodes: Node[] = [];
|
|
8
8
|
|
|
@@ -14,52 +14,39 @@ export class SwcIf extends HTMLTemplateElement {
|
|
|
14
14
|
this.render();
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
private _resolvePortal(): Element | null {
|
|
18
|
-
const portalScript = this.getAttribute('on-get-portal');
|
|
19
|
-
if (!portalScript) return this.parentElement;
|
|
20
|
-
|
|
21
|
-
const win = this.ownerDocument?.defaultView || window;
|
|
22
|
-
const res = FunctionUtils.executeReturn({
|
|
23
|
-
script: portalScript,
|
|
24
|
-
context: this,
|
|
25
|
-
args: SwcUtils.getHelperAndHostSet(win, this)
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
if (res instanceof HTMLElement) return res;
|
|
29
|
-
if (typeof res === 'string') return win.document.querySelector(res);
|
|
30
|
-
return this.parentElement;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
17
|
private _executeCloneCallback(attr: string, args: any) {
|
|
34
18
|
const script = this.getAttribute(attr);
|
|
35
19
|
if (!script) return;
|
|
36
20
|
FunctionUtils.execute({ script, context: this, args });
|
|
37
21
|
}
|
|
38
22
|
|
|
39
|
-
@changedAttributeHost('
|
|
40
|
-
private refreshValue() {
|
|
41
|
-
|
|
42
|
-
if (
|
|
23
|
+
@changedAttributeHost('value')
|
|
24
|
+
private refreshValue(valueAttr?: string | null) {
|
|
25
|
+
valueAttr ??= this.getAttribute('value');
|
|
26
|
+
if (valueAttr === null) {
|
|
43
27
|
this.value = false;
|
|
44
28
|
return;
|
|
45
29
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
30
|
+
|
|
31
|
+
// {{ }} 브레이스로 감싸진 값은 스크립트 실행
|
|
32
|
+
if (valueAttr.startsWith('{{') && valueAttr.endsWith('}}')) {
|
|
33
|
+
const script = valueAttr.slice(2, -2).trim();
|
|
34
|
+
const win = this.ownerDocument?.defaultView || window;
|
|
35
|
+
const result = FunctionUtils.executeReturn({
|
|
36
|
+
script,
|
|
37
|
+
context: this,
|
|
38
|
+
args: SwcUtils.getHelperAndHostSet(win, this)
|
|
39
|
+
});
|
|
40
|
+
this.value = result;
|
|
55
41
|
} else {
|
|
56
|
-
|
|
42
|
+
// 일반 문자열 값
|
|
43
|
+
this.value = Boolean(valueAttr);
|
|
57
44
|
}
|
|
58
45
|
}
|
|
59
46
|
|
|
60
47
|
private render() {
|
|
61
48
|
this.cleanup();
|
|
62
|
-
const portal = this.
|
|
49
|
+
const portal = this.parentElement;
|
|
63
50
|
if (!portal) return;
|
|
64
51
|
|
|
65
52
|
const win = this.ownerDocument?.defaultView || window;
|
|
@@ -83,18 +70,31 @@ export class SwcIf extends HTMLTemplateElement {
|
|
|
83
70
|
const elements = this._nodes.filter(n => n.nodeType === 1) as HTMLElement[];
|
|
84
71
|
const groupArgs = { ...helperSet, $value: this._value, $nodes: this._nodes, $elements: elements, $firstElement: elements[0] };
|
|
85
72
|
|
|
73
|
+
// 부모 체인을 정상적으로 유지하기 위해 __swc_host 할당 제거
|
|
86
74
|
this._nodes.forEach((node, nodeIdx) => {
|
|
87
|
-
if (node instanceof HTMLElement) (node as any).__swc_host = this;
|
|
75
|
+
// if (node instanceof HTMLElement) (node as any).__swc_host = this; // 제거: 부모 체인 유지
|
|
88
76
|
this._executeCloneCallback('on-clone-node', { ...groupArgs, $node: node, $nodeIndex: nodeIdx, $isElement: node.nodeType === 1 });
|
|
77
|
+
|
|
78
|
+
// Attribute {{ }} 치환 - SwcChoose처럼 동적 치환
|
|
79
|
+
if (node instanceof HTMLElement) {
|
|
80
|
+
Array.from(node.attributes).forEach(attr => {
|
|
81
|
+
const value = attr.value || '';
|
|
82
|
+
if (value.startsWith('{{') && value.endsWith('}}')) {
|
|
83
|
+
const script = value.slice(2, -2).trim();
|
|
84
|
+
const result = FunctionUtils.executeReturn({
|
|
85
|
+
script,
|
|
86
|
+
context: node,
|
|
87
|
+
args: { ...groupArgs, $value: this._value }
|
|
88
|
+
});
|
|
89
|
+
node.setAttribute(attr.name, String(result));
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
}
|
|
89
93
|
});
|
|
90
94
|
|
|
91
95
|
this._executeCloneCallback('on-clone-nodes', groupArgs);
|
|
92
96
|
|
|
93
|
-
|
|
94
|
-
portal.insertBefore(clone, this.nextSibling);
|
|
95
|
-
} else {
|
|
96
|
-
portal.appendChild(clone);
|
|
97
|
-
}
|
|
97
|
+
portal.insertBefore(clone, this.nextSibling);
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
100
|
|