@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.
Files changed (108) hide show
  1. package/README.md +275 -91
  2. package/dist/cjs/decorators/addEventListener.js +2 -3
  3. package/dist/cjs/decorators/addEventListener.js.map +2 -2
  4. package/dist/cjs/decorators/elementDefine.js +161 -34
  5. package/dist/cjs/decorators/elementDefine.js.map +3 -3
  6. package/dist/cjs/decorators/emitCustomEvent.js +2 -14
  7. package/dist/cjs/decorators/emitCustomEvent.js.map +2 -2
  8. package/dist/cjs/decorators/query.js +47 -36
  9. package/dist/cjs/decorators/query.js.map +2 -2
  10. package/dist/cjs/decorators/queryAll.js +42 -36
  11. package/dist/cjs/decorators/queryAll.js.map +2 -2
  12. package/dist/cjs/elements/SwcAppEngine.js +2 -1
  13. package/dist/cjs/elements/SwcAppEngine.js.map +2 -2
  14. package/dist/cjs/elements/SwcAsync.js +188 -0
  15. package/dist/cjs/elements/SwcAsync.js.map +7 -0
  16. package/dist/cjs/elements/SwcChoose.js +155 -0
  17. package/dist/cjs/elements/SwcChoose.js.map +7 -0
  18. package/dist/cjs/elements/SwcIf.js +167 -0
  19. package/dist/cjs/elements/SwcIf.js.map +7 -0
  20. package/dist/cjs/elements/SwcLoop.js +246 -0
  21. package/dist/cjs/elements/SwcLoop.js.map +7 -0
  22. package/dist/cjs/elements/SwcSubTemplates.js +65 -0
  23. package/dist/cjs/elements/SwcSubTemplates.js.map +7 -0
  24. package/dist/cjs/elements/index.js +7 -0
  25. package/dist/cjs/elements/index.js.map +2 -2
  26. package/dist/cjs/index.js +1 -1
  27. package/dist/cjs/index.js.map +2 -2
  28. package/dist/cjs/types.js +1 -0
  29. package/dist/cjs/types.js.map +2 -2
  30. package/dist/cjs/utils/Utils.js +92 -162
  31. package/dist/cjs/utils/Utils.js.map +3 -3
  32. package/dist/esm/decorators/addEventListener.js +2 -3
  33. package/dist/esm/decorators/addEventListener.js.map +2 -2
  34. package/dist/esm/decorators/elementDefine.js +161 -34
  35. package/dist/esm/decorators/elementDefine.js.map +3 -3
  36. package/dist/esm/decorators/emitCustomEvent.js +2 -14
  37. package/dist/esm/decorators/emitCustomEvent.js.map +2 -2
  38. package/dist/esm/decorators/query.js +47 -36
  39. package/dist/esm/decorators/query.js.map +2 -2
  40. package/dist/esm/decorators/queryAll.js +42 -36
  41. package/dist/esm/decorators/queryAll.js.map +2 -2
  42. package/dist/esm/elements/SwcAppEngine.js +2 -1
  43. package/dist/esm/elements/SwcAppEngine.js.map +2 -2
  44. package/dist/esm/elements/SwcAsync.js +169 -0
  45. package/dist/esm/elements/SwcAsync.js.map +7 -0
  46. package/dist/esm/elements/SwcChoose.js +136 -0
  47. package/dist/esm/elements/SwcChoose.js.map +7 -0
  48. package/dist/esm/elements/SwcIf.js +148 -0
  49. package/dist/esm/elements/SwcIf.js.map +7 -0
  50. package/dist/esm/elements/SwcLoop.js +227 -0
  51. package/dist/esm/elements/SwcLoop.js.map +7 -0
  52. package/dist/esm/elements/SwcSubTemplates.js +46 -0
  53. package/dist/esm/elements/SwcSubTemplates.js.map +7 -0
  54. package/dist/esm/elements/index.js +7 -0
  55. package/dist/esm/elements/index.js.map +2 -2
  56. package/dist/esm/index.js +1 -1
  57. package/dist/esm/index.js.map +1 -1
  58. package/dist/esm/types.js +1 -0
  59. package/dist/esm/types.js.map +2 -2
  60. package/dist/esm/utils/Utils.js +92 -162
  61. package/dist/esm/utils/Utils.js.map +3 -3
  62. package/dist/esm-bundle/dooboostore-simple-web-component.esm.js +1150 -326
  63. package/dist/esm-bundle/dooboostore-simple-web-component.esm.js.map +4 -4
  64. package/dist/types/decorators/addEventListener.d.ts +10 -4
  65. package/dist/types/decorators/addEventListener.d.ts.map +1 -1
  66. package/dist/types/decorators/elementDefine.d.ts.map +1 -1
  67. package/dist/types/decorators/emitCustomEvent.d.ts.map +1 -1
  68. package/dist/types/decorators/query.d.ts +4 -5
  69. package/dist/types/decorators/query.d.ts.map +1 -1
  70. package/dist/types/decorators/queryAll.d.ts +4 -5
  71. package/dist/types/decorators/queryAll.d.ts.map +1 -1
  72. package/dist/types/elements/SwcAppEngine.d.ts.map +1 -1
  73. package/dist/types/elements/SwcAsync.d.ts +16 -0
  74. package/dist/types/elements/SwcAsync.d.ts.map +1 -0
  75. package/dist/types/elements/SwcChoose.d.ts +13 -0
  76. package/dist/types/elements/SwcChoose.d.ts.map +1 -0
  77. package/dist/types/elements/SwcIf.d.ts +15 -0
  78. package/dist/types/elements/SwcIf.d.ts.map +1 -0
  79. package/dist/types/elements/SwcLoop.d.ts +20 -0
  80. package/dist/types/elements/SwcLoop.d.ts.map +1 -0
  81. package/dist/types/elements/SwcSubTemplates.d.ts +13 -0
  82. package/dist/types/elements/SwcSubTemplates.d.ts.map +1 -0
  83. package/dist/types/elements/index.d.ts +7 -0
  84. package/dist/types/elements/index.d.ts.map +1 -1
  85. package/dist/types/index.d.ts +1 -1
  86. package/dist/types/index.d.ts.map +1 -1
  87. package/dist/types/types.d.ts +11 -1
  88. package/dist/types/types.d.ts.map +1 -1
  89. package/dist/types/utils/Utils.d.ts +19 -21
  90. package/dist/types/utils/Utils.d.ts.map +1 -1
  91. package/dist/umd-bundle/dooboostore-simple-web-component.umd.js +1150 -326
  92. package/dist/umd-bundle/dooboostore-simple-web-component.umd.js.map +4 -4
  93. package/package.json +10 -3
  94. package/src/decorators/addEventListener.ts +11 -7
  95. package/src/decorators/elementDefine.ts +165 -39
  96. package/src/decorators/emitCustomEvent.ts +5 -21
  97. package/src/decorators/query.ts +46 -41
  98. package/src/decorators/queryAll.ts +45 -41
  99. package/src/elements/SwcAppEngine.ts +2 -1
  100. package/src/elements/SwcAsync.ts +163 -0
  101. package/src/elements/SwcChoose.ts +127 -0
  102. package/src/elements/SwcIf.ts +134 -0
  103. package/src/elements/SwcLoop.ts +215 -0
  104. package/src/elements/SwcSubTemplates.ts +19 -0
  105. package/src/elements/index.ts +8 -3
  106. package/src/index.ts +1 -1
  107. package/src/types.ts +15 -5
  108. 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
- export function queryAll(selector: string): any;
18
- export function queryAll(options: QueryAllOptions): any;
19
- export function queryAll(target: Object, propertyKey: string | symbol): void;
20
- export function queryAll(arg1?: string | QueryAllOptions | Object, arg2?: string | symbol): any {
21
- const decorator = (selector: string, options: QueryAllOptions, target: Object, propertyKey: string | symbol, descriptor?: PropertyDescriptor) => {
22
- const isMethod = !!descriptor;
23
- const constructor = target.constructor;
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, isMethod });
32
+ queries.push({ selector, options, propertyKey });
31
33
 
32
- if (!isMethod) {
33
- Object.defineProperty(target, propertyKey, {
34
- get(this: HTMLElement) {
35
- if (!selector) return [this];
36
- let searchRoot: Node = this;
37
- if (options.root === 'shadow') searchRoot = this.shadowRoot || this;
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
- return (searchRoot as HTMLElement).querySelectorAll(selector);
42
- },
43
- enumerable: true,
44
- configurable: true
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
- // Case: @queryAll('selector') or @queryAll({ selector: '...', ... })
55
- return (target: Object, propertyKey: string | symbol, descriptor?: PropertyDescriptor) => {
56
- let selector = '';
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
- if (typeof arg1 === 'string') {
60
- selector = arg1;
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
- decorator(selector, options, target, propertyKey, descriptor);
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 = new Function(`return ${configStr}`).apply(this.host);
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
+ }