@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.
Files changed (130) hide show
  1. package/README.md +109 -48
  2. package/dist/cjs/decorators/addEventListener.js.map +2 -2
  3. package/dist/cjs/decorators/appendChild.js +48 -0
  4. package/dist/cjs/decorators/appendChild.js.map +7 -0
  5. package/dist/cjs/decorators/elementDefine.js +216 -67
  6. package/dist/cjs/decorators/elementDefine.js.map +3 -3
  7. package/dist/cjs/decorators/index.js +2 -1
  8. package/dist/cjs/decorators/index.js.map +2 -2
  9. package/dist/cjs/decorators/{innerHtml.js → onConnectedInnerHtml.js} +8 -8
  10. package/dist/cjs/decorators/onConnectedInnerHtml.js.map +7 -0
  11. package/dist/cjs/decorators/query.js +38 -0
  12. package/dist/cjs/decorators/query.js.map +2 -2
  13. package/dist/cjs/decorators/queryAll.js +32 -0
  14. package/dist/cjs/decorators/queryAll.js.map +2 -2
  15. package/dist/cjs/decorators/replaceChildren.js +53 -0
  16. package/dist/cjs/decorators/replaceChildren.js.map +7 -0
  17. package/dist/cjs/elements/SwcAppEngine.js +2 -1
  18. package/dist/cjs/elements/SwcAppEngine.js.map +2 -2
  19. package/dist/cjs/elements/SwcAsync.js +188 -0
  20. package/dist/cjs/elements/SwcAsync.js.map +7 -0
  21. package/dist/cjs/elements/SwcChoose.js +155 -0
  22. package/dist/cjs/elements/SwcChoose.js.map +7 -0
  23. package/dist/cjs/elements/SwcIf.js +167 -0
  24. package/dist/cjs/elements/SwcIf.js.map +7 -0
  25. package/dist/cjs/elements/SwcLoop.js +246 -0
  26. package/dist/cjs/elements/SwcLoop.js.map +7 -0
  27. package/dist/cjs/elements/SwcSubTemplates.js +65 -0
  28. package/dist/cjs/elements/SwcSubTemplates.js.map +7 -0
  29. package/dist/cjs/elements/index.js +7 -0
  30. package/dist/cjs/elements/index.js.map +2 -2
  31. package/dist/cjs/index.js +12 -2
  32. package/dist/cjs/index.js.map +2 -2
  33. package/dist/cjs/types.js.map +1 -1
  34. package/dist/cjs/utils/Utils.js +74 -37
  35. package/dist/cjs/utils/Utils.js.map +3 -3
  36. package/dist/esm/decorators/addEventListener.js.map +2 -2
  37. package/dist/esm/decorators/appendChild.js +29 -0
  38. package/dist/esm/decorators/appendChild.js.map +7 -0
  39. package/dist/esm/decorators/elementDefine.js +216 -67
  40. package/dist/esm/decorators/elementDefine.js.map +3 -3
  41. package/dist/esm/decorators/index.js +2 -1
  42. package/dist/esm/decorators/index.js.map +2 -2
  43. package/dist/esm/decorators/{innerHtml.js → onConnectedInnerHtml.js} +5 -5
  44. package/dist/esm/decorators/onConnectedInnerHtml.js.map +7 -0
  45. package/dist/esm/decorators/query.js +38 -0
  46. package/dist/esm/decorators/query.js.map +2 -2
  47. package/dist/esm/decorators/queryAll.js +32 -0
  48. package/dist/esm/decorators/queryAll.js.map +2 -2
  49. package/dist/esm/decorators/replaceChildren.js +34 -0
  50. package/dist/esm/decorators/replaceChildren.js.map +7 -0
  51. package/dist/esm/elements/SwcAppEngine.js +2 -1
  52. package/dist/esm/elements/SwcAppEngine.js.map +2 -2
  53. package/dist/esm/elements/SwcAsync.js +169 -0
  54. package/dist/esm/elements/SwcAsync.js.map +7 -0
  55. package/dist/esm/elements/SwcChoose.js +136 -0
  56. package/dist/esm/elements/SwcChoose.js.map +7 -0
  57. package/dist/esm/elements/SwcIf.js +148 -0
  58. package/dist/esm/elements/SwcIf.js.map +7 -0
  59. package/dist/esm/elements/SwcLoop.js +227 -0
  60. package/dist/esm/elements/SwcLoop.js.map +7 -0
  61. package/dist/esm/elements/SwcSubTemplates.js +46 -0
  62. package/dist/esm/elements/SwcSubTemplates.js.map +7 -0
  63. package/dist/esm/elements/index.js +7 -0
  64. package/dist/esm/elements/index.js.map +2 -2
  65. package/dist/esm/index.js +8 -2
  66. package/dist/esm/index.js.map +2 -2
  67. package/dist/esm/types.js.map +1 -1
  68. package/dist/esm/utils/Utils.js +74 -37
  69. package/dist/esm/utils/Utils.js.map +3 -3
  70. package/dist/esm-bundle/dooboostore-simple-web-component.esm.js +1226 -149
  71. package/dist/esm-bundle/dooboostore-simple-web-component.esm.js.map +4 -4
  72. package/dist/types/decorators/addEventListener.d.ts +1 -0
  73. package/dist/types/decorators/addEventListener.d.ts.map +1 -1
  74. package/dist/types/decorators/appendChild.d.ts +17 -0
  75. package/dist/types/decorators/appendChild.d.ts.map +1 -0
  76. package/dist/types/decorators/elementDefine.d.ts.map +1 -1
  77. package/dist/types/decorators/index.d.ts +2 -1
  78. package/dist/types/decorators/index.d.ts.map +1 -1
  79. package/dist/types/decorators/onConnectedInnerHtml.d.ts +15 -0
  80. package/dist/types/decorators/onConnectedInnerHtml.d.ts.map +1 -0
  81. package/dist/types/decorators/query.d.ts +0 -2
  82. package/dist/types/decorators/query.d.ts.map +1 -1
  83. package/dist/types/decorators/queryAll.d.ts +0 -2
  84. package/dist/types/decorators/queryAll.d.ts.map +1 -1
  85. package/dist/types/decorators/replaceChildren.d.ts +9 -0
  86. package/dist/types/decorators/replaceChildren.d.ts.map +1 -0
  87. package/dist/types/elements/SwcAppEngine.d.ts.map +1 -1
  88. package/dist/types/elements/SwcAsync.d.ts +16 -0
  89. package/dist/types/elements/SwcAsync.d.ts.map +1 -0
  90. package/dist/types/elements/SwcChoose.d.ts +13 -0
  91. package/dist/types/elements/SwcChoose.d.ts.map +1 -0
  92. package/dist/types/elements/SwcIf.d.ts +15 -0
  93. package/dist/types/elements/SwcIf.d.ts.map +1 -0
  94. package/dist/types/elements/SwcLoop.d.ts +20 -0
  95. package/dist/types/elements/SwcLoop.d.ts.map +1 -0
  96. package/dist/types/elements/SwcSubTemplates.d.ts +13 -0
  97. package/dist/types/elements/SwcSubTemplates.d.ts.map +1 -0
  98. package/dist/types/elements/index.d.ts +7 -0
  99. package/dist/types/elements/index.d.ts.map +1 -1
  100. package/dist/types/index.d.ts +5 -2
  101. package/dist/types/index.d.ts.map +1 -1
  102. package/dist/types/types.d.ts +8 -0
  103. package/dist/types/types.d.ts.map +1 -1
  104. package/dist/types/utils/Utils.d.ts +16 -10
  105. package/dist/types/utils/Utils.d.ts.map +1 -1
  106. package/dist/umd-bundle/dooboostore-simple-web-component.umd.js +1226 -149
  107. package/dist/umd-bundle/dooboostore-simple-web-component.umd.js.map +4 -4
  108. package/package.json +10 -3
  109. package/src/decorators/addEventListener.ts +1 -0
  110. package/src/decorators/appendChild.ts +42 -0
  111. package/src/decorators/elementDefine.ts +225 -71
  112. package/src/decorators/index.ts +2 -1
  113. package/src/decorators/{innerHtml.ts → onConnectedInnerHtml.ts} +7 -6
  114. package/src/decorators/query.ts +32 -2
  115. package/src/decorators/queryAll.ts +32 -2
  116. package/src/decorators/replaceChildren.ts +35 -0
  117. package/src/elements/SwcAppEngine.ts +2 -1
  118. package/src/elements/SwcAsync.ts +163 -0
  119. package/src/elements/SwcChoose.ts +127 -0
  120. package/src/elements/SwcIf.ts +134 -0
  121. package/src/elements/SwcLoop.ts +215 -0
  122. package/src/elements/SwcSubTemplates.ts +19 -0
  123. package/src/elements/index.ts +8 -3
  124. package/src/index.ts +5 -2
  125. package/src/types.ts +9 -1
  126. package/src/utils/Utils.ts +86 -36
  127. package/dist/cjs/decorators/innerHtml.js.map +0 -7
  128. package/dist/esm/decorators/innerHtml.js.map +0 -7
  129. package/dist/types/decorators/innerHtml.d.ts +0 -12
  130. package/dist/types/decorators/innerHtml.d.ts.map +0 -1
@@ -1,4 +1,6 @@
1
1
  import { ReflectUtils } from '@dooboostore/core/reflect/ReflectUtils';
2
+ import { getElementConfig } from './elementDefine';
3
+ import { SwcUtils } from '../utils/Utils';
2
4
 
3
5
  export interface QueryAllOptions {
4
6
  root?: 'light' | 'shadow' | 'all' | 'auto';
@@ -14,8 +16,6 @@ export const QUERY_ALL_METADATA_KEY = Symbol('simple-web-component:query-all');
14
16
 
15
17
  /**
16
18
  * @queryAll decorator to inject a list of elements into a class property.
17
- * Usage: @queryAll('.items') list!: NodeListOf<HTMLElement>;
18
- * Usage: @queryAll('li', { root: 'shadow' }) list!: HTMLElement[];
19
19
  */
20
20
  export function queryAll(selector: string, options: QueryAllOptions = {}): PropertyDecorator {
21
21
  return (target: Object, propertyKey: string | symbol, descriptor?: never): void => {
@@ -33,7 +33,21 @@ export function queryAll(selector: string, options: QueryAllOptions = {}): Prope
33
33
 
34
34
  Object.defineProperty(target, propertyKey, {
35
35
  get(this: HTMLElement) {
36
+ // --- Special Selectors: HostSet ---
37
+ const hostSet = SwcUtils.getHostSet(this);
38
+ if (selector === ':hosts') return hostSet.$hosts;
39
+ if (selector === ':appHosts') return hostSet.$appHosts;
40
+
41
+ // --- Special Selectors: Env ---
42
+ const config = getElementConfig(this);
43
+ const win = config?.window || window;
44
+ const doc = win.document;
45
+
46
+ if (selector === ':window') return [win];
47
+ if (selector === ':document') return [doc];
36
48
  if (!selector || selector === ':host') return [this];
49
+
50
+ // --- Standard Selectors ---
37
51
  const r = options.root || 'auto';
38
52
 
39
53
  if (r === 'shadow') {
@@ -51,6 +65,22 @@ export function queryAll(selector: string, options: QueryAllOptions = {}): Prope
51
65
  const root = this.shadowRoot || this;
52
66
  return root.querySelectorAll(selector);
53
67
  },
68
+ set(this: HTMLElement, nv: any) {
69
+ if (nv === null || nv === undefined || (Array.isArray(nv) && nv.length === 0)) {
70
+ const r = options.root || 'auto';
71
+ let targets: (Element | Node)[] = [];
72
+ if (r === 'shadow') targets = this.shadowRoot ? Array.from(this.shadowRoot.querySelectorAll(selector)) : [];
73
+ else if (r === 'light') targets = Array.from(this.querySelectorAll(selector));
74
+ else if (r === 'all') {
75
+ const sNodes = this.shadowRoot ? Array.from(this.shadowRoot.querySelectorAll(selector)) : [];
76
+ const lNodes = Array.from(this.querySelectorAll(selector));
77
+ targets = [...sNodes, ...lNodes];
78
+ } else {
79
+ targets = Array.from((this.shadowRoot || this).querySelectorAll(selector));
80
+ }
81
+ targets.forEach(t => (t as Element).remove());
82
+ }
83
+ },
54
84
  enumerable: true,
55
85
  configurable: true
56
86
  });
@@ -0,0 +1,35 @@
1
+ import { ReflectUtils } from '@dooboostore/core/reflect/ReflectUtils';
2
+
3
+ export const REPLACE_CHILDREN_METADATA_KEY = Symbol('simple-web-component:replace-children');
4
+
5
+ /**
6
+ * @replaceChildren decorator to surgically replace children of a target element.
7
+ * Usage: @replaceChildren('#container') renderPart() { return '<div>New Content</div>'; }
8
+ */
9
+ export function replaceChildren(selector: string): MethodDecorator {
10
+ return (target: Object, propertyKey: string | symbol) => {
11
+ const constructor = target.constructor;
12
+ let meta = ReflectUtils.getOwnMetadata(REPLACE_CHILDREN_METADATA_KEY, constructor) as Map<string | symbol, string>;
13
+ if (!meta) {
14
+ meta = new Map<string | symbol, string>();
15
+ ReflectUtils.defineMetadata(REPLACE_CHILDREN_METADATA_KEY, meta, constructor);
16
+ }
17
+ meta.set(propertyKey, selector);
18
+ };
19
+ }
20
+
21
+ export const getReplaceChildrenMetadata = (target: any): Map<string | symbol, string> | undefined => {
22
+ const constructor = target instanceof Function ? target : target.constructor;
23
+ return ReflectUtils.getMetadata(REPLACE_CHILDREN_METADATA_KEY, constructor);
24
+ };
25
+
26
+ export const findAllReplaceChildrenMetadata = (target: any): Map<string | symbol, string> => {
27
+ const result = new Map<string | symbol, string>();
28
+ const maps = ReflectUtils.findAllMetadata<Map<string | symbol, string>>(REPLACE_CHILDREN_METADATA_KEY, target);
29
+ maps.forEach(map => {
30
+ if (map instanceof Map) {
31
+ map.forEach((v, k) => result.set(k, v));
32
+ }
33
+ });
34
+ return result;
35
+ };
@@ -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
+ }