@dooboostore/simple-web-component 1.0.15 → 1.0.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +275 -91
- package/dist/cjs/decorators/addEventListener.js +2 -3
- package/dist/cjs/decorators/addEventListener.js.map +2 -2
- package/dist/cjs/decorators/elementDefine.js +161 -34
- package/dist/cjs/decorators/elementDefine.js.map +3 -3
- package/dist/cjs/decorators/emitCustomEvent.js +2 -14
- package/dist/cjs/decorators/emitCustomEvent.js.map +2 -2
- package/dist/cjs/decorators/query.js +47 -36
- package/dist/cjs/decorators/query.js.map +2 -2
- package/dist/cjs/decorators/queryAll.js +42 -36
- package/dist/cjs/decorators/queryAll.js.map +2 -2
- package/dist/cjs/elements/SwcAppEngine.js +2 -1
- package/dist/cjs/elements/SwcAppEngine.js.map +2 -2
- package/dist/cjs/elements/SwcAsync.js +188 -0
- package/dist/cjs/elements/SwcAsync.js.map +7 -0
- package/dist/cjs/elements/SwcChoose.js +155 -0
- package/dist/cjs/elements/SwcChoose.js.map +7 -0
- package/dist/cjs/elements/SwcIf.js +167 -0
- package/dist/cjs/elements/SwcIf.js.map +7 -0
- package/dist/cjs/elements/SwcLoop.js +246 -0
- package/dist/cjs/elements/SwcLoop.js.map +7 -0
- package/dist/cjs/elements/SwcSubTemplates.js +65 -0
- package/dist/cjs/elements/SwcSubTemplates.js.map +7 -0
- package/dist/cjs/elements/index.js +7 -0
- package/dist/cjs/elements/index.js.map +2 -2
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/index.js.map +2 -2
- package/dist/cjs/types.js +1 -0
- package/dist/cjs/types.js.map +2 -2
- package/dist/cjs/utils/Utils.js +92 -162
- package/dist/cjs/utils/Utils.js.map +3 -3
- package/dist/esm/decorators/addEventListener.js +2 -3
- package/dist/esm/decorators/addEventListener.js.map +2 -2
- package/dist/esm/decorators/elementDefine.js +161 -34
- package/dist/esm/decorators/elementDefine.js.map +3 -3
- package/dist/esm/decorators/emitCustomEvent.js +2 -14
- package/dist/esm/decorators/emitCustomEvent.js.map +2 -2
- package/dist/esm/decorators/query.js +47 -36
- package/dist/esm/decorators/query.js.map +2 -2
- package/dist/esm/decorators/queryAll.js +42 -36
- package/dist/esm/decorators/queryAll.js.map +2 -2
- package/dist/esm/elements/SwcAppEngine.js +2 -1
- package/dist/esm/elements/SwcAppEngine.js.map +2 -2
- package/dist/esm/elements/SwcAsync.js +169 -0
- package/dist/esm/elements/SwcAsync.js.map +7 -0
- package/dist/esm/elements/SwcChoose.js +136 -0
- package/dist/esm/elements/SwcChoose.js.map +7 -0
- package/dist/esm/elements/SwcIf.js +148 -0
- package/dist/esm/elements/SwcIf.js.map +7 -0
- package/dist/esm/elements/SwcLoop.js +227 -0
- package/dist/esm/elements/SwcLoop.js.map +7 -0
- package/dist/esm/elements/SwcSubTemplates.js +46 -0
- package/dist/esm/elements/SwcSubTemplates.js.map +7 -0
- package/dist/esm/elements/index.js +7 -0
- package/dist/esm/elements/index.js.map +2 -2
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/types.js +1 -0
- package/dist/esm/types.js.map +2 -2
- package/dist/esm/utils/Utils.js +92 -162
- package/dist/esm/utils/Utils.js.map +3 -3
- package/dist/esm-bundle/dooboostore-simple-web-component.esm.js +1150 -326
- package/dist/esm-bundle/dooboostore-simple-web-component.esm.js.map +4 -4
- package/dist/types/decorators/addEventListener.d.ts +10 -4
- package/dist/types/decorators/addEventListener.d.ts.map +1 -1
- package/dist/types/decorators/elementDefine.d.ts.map +1 -1
- package/dist/types/decorators/emitCustomEvent.d.ts.map +1 -1
- package/dist/types/decorators/query.d.ts +4 -5
- package/dist/types/decorators/query.d.ts.map +1 -1
- package/dist/types/decorators/queryAll.d.ts +4 -5
- package/dist/types/decorators/queryAll.d.ts.map +1 -1
- package/dist/types/elements/SwcAppEngine.d.ts.map +1 -1
- package/dist/types/elements/SwcAsync.d.ts +16 -0
- package/dist/types/elements/SwcAsync.d.ts.map +1 -0
- package/dist/types/elements/SwcChoose.d.ts +13 -0
- package/dist/types/elements/SwcChoose.d.ts.map +1 -0
- package/dist/types/elements/SwcIf.d.ts +15 -0
- package/dist/types/elements/SwcIf.d.ts.map +1 -0
- package/dist/types/elements/SwcLoop.d.ts +20 -0
- package/dist/types/elements/SwcLoop.d.ts.map +1 -0
- package/dist/types/elements/SwcSubTemplates.d.ts +13 -0
- package/dist/types/elements/SwcSubTemplates.d.ts.map +1 -0
- package/dist/types/elements/index.d.ts +7 -0
- package/dist/types/elements/index.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/types.d.ts +11 -1
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/utils/Utils.d.ts +19 -21
- package/dist/types/utils/Utils.d.ts.map +1 -1
- package/dist/umd-bundle/dooboostore-simple-web-component.umd.js +1150 -326
- package/dist/umd-bundle/dooboostore-simple-web-component.umd.js.map +4 -4
- package/package.json +10 -3
- package/src/decorators/addEventListener.ts +11 -7
- package/src/decorators/elementDefine.ts +165 -39
- package/src/decorators/emitCustomEvent.ts +5 -21
- package/src/decorators/query.ts +46 -41
- package/src/decorators/queryAll.ts +45 -41
- package/src/elements/SwcAppEngine.ts +2 -1
- package/src/elements/SwcAsync.ts +163 -0
- package/src/elements/SwcChoose.ts +127 -0
- package/src/elements/SwcIf.ts +134 -0
- package/src/elements/SwcLoop.ts +215 -0
- package/src/elements/SwcSubTemplates.ts +19 -0
- package/src/elements/index.ts +8 -3
- package/src/index.ts +1 -1
- package/src/types.ts +15 -5
- package/src/utils/Utils.ts +111 -197
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { ReflectUtils } from '@dooboostore/core/reflect/ReflectUtils';
|
|
2
|
+
import { getElementConfig } from './elementDefine';
|
|
3
|
+
import { SwcUtils } from '../utils/Utils';
|
|
2
4
|
|
|
3
5
|
export interface QueryAllOptions {
|
|
4
|
-
selector?: string;
|
|
5
6
|
root?: 'light' | 'shadow' | 'all' | 'auto';
|
|
6
7
|
}
|
|
7
8
|
|
|
@@ -9,61 +10,64 @@ export interface QueryAllMetadata {
|
|
|
9
10
|
selector: string;
|
|
10
11
|
options: QueryAllOptions;
|
|
11
12
|
propertyKey: string | symbol;
|
|
12
|
-
isMethod: boolean;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
export const QUERY_ALL_METADATA_KEY = Symbol('simple-web-component:query-all');
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
export function queryAll(
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
17
|
+
/**
|
|
18
|
+
* @queryAll decorator to inject a list of elements into a class property.
|
|
19
|
+
*/
|
|
20
|
+
export function queryAll(selector: string, options: QueryAllOptions = {}): PropertyDecorator {
|
|
21
|
+
return (target: Object, propertyKey: string | symbol, descriptor?: never): void => {
|
|
22
|
+
if (descriptor !== undefined) {
|
|
23
|
+
throw new Error(`@queryAll decorator cannot be used on methods. (Method: ${String(propertyKey)})`);
|
|
24
|
+
}
|
|
24
25
|
|
|
26
|
+
const constructor = target.constructor;
|
|
25
27
|
let queries = ReflectUtils.getMetadata<QueryAllMetadata[]>(QUERY_ALL_METADATA_KEY, constructor);
|
|
26
28
|
if (!queries) {
|
|
27
29
|
queries = [];
|
|
28
30
|
ReflectUtils.defineMetadata(QUERY_ALL_METADATA_KEY, queries, constructor);
|
|
29
31
|
}
|
|
30
|
-
queries.push({ selector, options, propertyKey
|
|
32
|
+
queries.push({ selector, options, propertyKey });
|
|
31
33
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
else if (options.root === 'light') searchRoot = this;
|
|
39
|
-
else searchRoot = this.shadowRoot || this; // Default: Auto (Shadow-First)
|
|
34
|
+
Object.defineProperty(target, propertyKey, {
|
|
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
40
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
// Case: @queryAll (parameterless decorator)
|
|
50
|
-
if (arg1 && typeof arg2 === 'string') {
|
|
51
|
-
return decorator('', {}, arg1 as Object, arg2 as string | symbol);
|
|
52
|
-
}
|
|
41
|
+
// --- Special Selectors: Env ---
|
|
42
|
+
const config = getElementConfig(this);
|
|
43
|
+
const win = config?.window || window;
|
|
44
|
+
const doc = win.document;
|
|
53
45
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
let options: QueryAllOptions = {};
|
|
46
|
+
if (selector === ':window') return [win];
|
|
47
|
+
if (selector === ':document') return [doc];
|
|
48
|
+
if (!selector || selector === ':host') return [this];
|
|
58
49
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
} else if (typeof arg1 === 'object') {
|
|
62
|
-
options = arg1 as QueryAllOptions;
|
|
63
|
-
selector = options.selector || '';
|
|
64
|
-
}
|
|
50
|
+
// --- Standard Selectors ---
|
|
51
|
+
const r = options.root || 'auto';
|
|
65
52
|
|
|
66
|
-
|
|
53
|
+
if (r === 'shadow') {
|
|
54
|
+
return this.shadowRoot ? this.shadowRoot.querySelectorAll(selector) : [];
|
|
55
|
+
}
|
|
56
|
+
if (r === 'light') {
|
|
57
|
+
return this.querySelectorAll(selector);
|
|
58
|
+
}
|
|
59
|
+
if (r === 'all') {
|
|
60
|
+
const sNodes = this.shadowRoot ? Array.from(this.shadowRoot.querySelectorAll(selector)) : [];
|
|
61
|
+
const lNodes = Array.from(this.querySelectorAll(selector));
|
|
62
|
+
return [...sNodes, ...lNodes];
|
|
63
|
+
}
|
|
64
|
+
// auto
|
|
65
|
+
const root = this.shadowRoot || this;
|
|
66
|
+
return root.querySelectorAll(selector);
|
|
67
|
+
},
|
|
68
|
+
enumerable: true,
|
|
69
|
+
configurable: true
|
|
70
|
+
});
|
|
67
71
|
};
|
|
68
72
|
}
|
|
69
73
|
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
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 { SwcWhen, SwcOtherwise } from './SwcSubTemplates';
|
|
6
|
+
|
|
7
|
+
@elementDefine({ name: 'swc-choose', extends: 'template' })
|
|
8
|
+
export class SwcChoose extends HTMLTemplateElement {
|
|
9
|
+
private _nodes: Node[] = [];
|
|
10
|
+
private _value: any = null;
|
|
11
|
+
|
|
12
|
+
get value(): any {
|
|
13
|
+
return this._value;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
set value(nv: any) {
|
|
17
|
+
this._value = nv;
|
|
18
|
+
this.render();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
private _executeCallback(attrName: string, extraArgs: Record<string, any>) {
|
|
22
|
+
const script = this.getAttribute(attrName);
|
|
23
|
+
if (!script) return;
|
|
24
|
+
const helperHostSet = SwcUtils.getHelperAndHostSet(window, this);
|
|
25
|
+
try {
|
|
26
|
+
FunctionUtils.execute({ script, context: this, args: { ...helperHostSet, ...extraArgs } });
|
|
27
|
+
} catch (e) {
|
|
28
|
+
console.error(`[SwcChoose] Error in ${attrName}:`, e);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
@changedAttribute('on-get-value')
|
|
33
|
+
private refresh(newValue?: string | null) {
|
|
34
|
+
const script = newValue !== undefined ? newValue : this.getAttribute('on-get-value');
|
|
35
|
+
if (!script) {
|
|
36
|
+
this.value = null;
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (!this.parentElement) return;
|
|
40
|
+
|
|
41
|
+
const helperHostSet = SwcUtils.getHelperAndHostSet(window, this);
|
|
42
|
+
try {
|
|
43
|
+
const result = FunctionUtils.executeReturn({
|
|
44
|
+
script,
|
|
45
|
+
context: this,
|
|
46
|
+
args: helperHostSet
|
|
47
|
+
});
|
|
48
|
+
this.value = result;
|
|
49
|
+
} catch (e) {
|
|
50
|
+
console.error('[SwcChoose] Error in on-get-value:', e);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
private render() {
|
|
55
|
+
this.cleanup();
|
|
56
|
+
if (!this.parentElement) return;
|
|
57
|
+
|
|
58
|
+
const templates = Array.from(this.content.querySelectorAll('template'));
|
|
59
|
+
let selected: HTMLTemplateElement | null = null;
|
|
60
|
+
let otherwise: HTMLTemplateElement | null = null;
|
|
61
|
+
let defaultTmp: HTMLTemplateElement | null = null;
|
|
62
|
+
|
|
63
|
+
for (const t of templates) {
|
|
64
|
+
const is = t.getAttribute('is');
|
|
65
|
+
if (is === 'swc-when') {
|
|
66
|
+
const whenVal = t.getAttribute('value');
|
|
67
|
+
if (String(whenVal) === String(this._value)) {
|
|
68
|
+
selected = t;
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
} else if (is === 'swc-otherwise') {
|
|
72
|
+
otherwise = t;
|
|
73
|
+
} else if (is === 'swc-default') {
|
|
74
|
+
defaultTmp = t;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const targetTemplate = (this._value == null ? defaultTmp : selected) || otherwise || defaultTmp;
|
|
79
|
+
if (targetTemplate) {
|
|
80
|
+
const clone = targetTemplate.content.cloneNode(true) as DocumentFragment;
|
|
81
|
+
const nodes = Array.from(clone.childNodes);
|
|
82
|
+
const elements = nodes.filter(n => n.nodeType === 1) as HTMLElement[];
|
|
83
|
+
|
|
84
|
+
const ctx = {
|
|
85
|
+
$value: this._value,
|
|
86
|
+
$nodes: nodes,
|
|
87
|
+
$elements: elements,
|
|
88
|
+
$firstNode: nodes[0],
|
|
89
|
+
$lastNode: nodes[nodes.length - 1],
|
|
90
|
+
$firstElement: elements[0],
|
|
91
|
+
$lastElement: elements[elements.length - 1]
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
// Execution Callbacks
|
|
95
|
+
this._executeCallback('on-clone-nodes', ctx);
|
|
96
|
+
nodes.forEach((node, nodeIndex) => {
|
|
97
|
+
if (node instanceof HTMLElement) {
|
|
98
|
+
(node as any).__swc_host = this;
|
|
99
|
+
}
|
|
100
|
+
this._executeCallback('on-clone-node', {
|
|
101
|
+
...ctx,
|
|
102
|
+
$node: node,
|
|
103
|
+
$nodeIndex: nodeIndex,
|
|
104
|
+
$isElement: node.nodeType === 1
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
this._nodes = nodes;
|
|
109
|
+
this.parentElement.insertBefore(clone, this.nextSibling);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
private cleanup() {
|
|
114
|
+
this._nodes.forEach(n => {
|
|
115
|
+
if (n.parentElement) n.parentElement.removeChild(n);
|
|
116
|
+
});
|
|
117
|
+
this._nodes = [];
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
connectedCallback() {
|
|
121
|
+
this.refresh();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
disconnectedCallback() {
|
|
125
|
+
this.cleanup();
|
|
126
|
+
}
|
|
127
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
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
|
+
|
|
6
|
+
@elementDefine({ name: 'swc-if', extends: 'template' })
|
|
7
|
+
export class SwcIf extends HTMLTemplateElement {
|
|
8
|
+
private _nodes: Node[] = [];
|
|
9
|
+
private _value: any = null;
|
|
10
|
+
|
|
11
|
+
get value(): any {
|
|
12
|
+
return this._value;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
set value(nv: any) {
|
|
16
|
+
this._value = nv;
|
|
17
|
+
this.render();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
private _getTarget(): Element | DocumentFragment | null {
|
|
21
|
+
const script = this.getAttribute('on-get-portal');
|
|
22
|
+
if (!script) return null;
|
|
23
|
+
try {
|
|
24
|
+
const res = FunctionUtils.executeReturn({
|
|
25
|
+
script,
|
|
26
|
+
context: this,
|
|
27
|
+
args: SwcUtils.getHelperAndHostSet(window, this)
|
|
28
|
+
});
|
|
29
|
+
if (typeof res === 'string') return document.querySelector(res);
|
|
30
|
+
if (res instanceof Node) return res as any;
|
|
31
|
+
} catch (e) {
|
|
32
|
+
console.error('[SwcIf] Portal target error:', e);
|
|
33
|
+
}
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
private _executeCallback(attrName: string, extraArgs: Record<string, any>) {
|
|
38
|
+
const script = this.getAttribute(attrName);
|
|
39
|
+
if (!script) return;
|
|
40
|
+
const helperHostSet = SwcUtils.getHelperAndHostSet(window, this);
|
|
41
|
+
try {
|
|
42
|
+
FunctionUtils.execute({ script, context: this, args: { ...helperHostSet, ...extraArgs } });
|
|
43
|
+
} catch (e) {
|
|
44
|
+
console.error(`[SwcIf] Error in ${attrName}:`, e);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
@changedAttribute('on-get-value')
|
|
49
|
+
private refresh(newValue?: string | null) {
|
|
50
|
+
const script = newValue !== undefined ? newValue : this.getAttribute('on-get-value');
|
|
51
|
+
if (!script) {
|
|
52
|
+
this.value = null;
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (!this.parentElement) return;
|
|
56
|
+
|
|
57
|
+
const helperHostSet = SwcUtils.getHelperAndHostSet(window, this);
|
|
58
|
+
try {
|
|
59
|
+
const result = FunctionUtils.executeReturn({
|
|
60
|
+
script,
|
|
61
|
+
context: this,
|
|
62
|
+
args: helperHostSet
|
|
63
|
+
});
|
|
64
|
+
this.value = result;
|
|
65
|
+
} catch (e) {
|
|
66
|
+
console.error('[SwcIf] Error in on-get-value:', e);
|
|
67
|
+
this.value = null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
private render() {
|
|
72
|
+
this.cleanup();
|
|
73
|
+
if (!this.parentElement) return;
|
|
74
|
+
|
|
75
|
+
let fragment: DocumentFragment | null = null;
|
|
76
|
+
const ctx: any = { $value: this._value, $nodes: [], $elements: [] };
|
|
77
|
+
|
|
78
|
+
if (this._value) {
|
|
79
|
+
const clone = this.content.cloneNode(true) as DocumentFragment;
|
|
80
|
+
Array.from(clone.querySelectorAll('template[is="swc-default"]')).forEach(t => t.remove());
|
|
81
|
+
fragment = clone;
|
|
82
|
+
} else {
|
|
83
|
+
const t = this.content.querySelector('template[is="swc-default"]') as HTMLTemplateElement;
|
|
84
|
+
if (t) fragment = t.content.cloneNode(true) as DocumentFragment;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (fragment) {
|
|
88
|
+
this.mount(fragment, ctx);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
private mount(fragment: DocumentFragment, ctx: any) {
|
|
93
|
+
const nodes = Array.from(fragment.childNodes);
|
|
94
|
+
const elements = nodes.filter(n => n.nodeType === 1) as HTMLElement[];
|
|
95
|
+
|
|
96
|
+
Object.assign(ctx, {
|
|
97
|
+
$nodes: nodes,
|
|
98
|
+
$elements: elements,
|
|
99
|
+
$firstNode: nodes[0],
|
|
100
|
+
$lastNode: nodes[nodes.length - 1],
|
|
101
|
+
$firstElement: elements[0],
|
|
102
|
+
$lastElement: elements[elements.length - 1]
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
this._executeCallback('on-clone-nodes', ctx);
|
|
106
|
+
nodes.forEach((node, nodeIndex) => {
|
|
107
|
+
if (node instanceof HTMLElement) (node as any).__swc_host = this;
|
|
108
|
+
this._executeCallback('on-clone-node', { ...ctx, $node: node, $nodeIndex: nodeIndex, $isElement: node.nodeType === 1 });
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
this._nodes = nodes;
|
|
112
|
+
const target = this._getTarget();
|
|
113
|
+
if (target) {
|
|
114
|
+
target.appendChild(fragment);
|
|
115
|
+
} else {
|
|
116
|
+
this.parentElement?.insertBefore(fragment, this.nextSibling);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
private cleanup() {
|
|
121
|
+
this._nodes.forEach(n => {
|
|
122
|
+
if (n.parentElement) n.parentElement.removeChild(n);
|
|
123
|
+
});
|
|
124
|
+
this._nodes = [];
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
connectedCallback() {
|
|
128
|
+
this.refresh();
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
disconnectedCallback() {
|
|
132
|
+
this.cleanup();
|
|
133
|
+
}
|
|
134
|
+
}
|