@dooboostore/simple-web-component 1.0.30 → 1.0.32

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 (93) hide show
  1. package/README.md +220 -1
  2. package/dist/cjs/decorators/applyStyle.js +1 -1
  3. package/dist/cjs/decorators/applyStyle.js.map +2 -2
  4. package/dist/cjs/decorators/attribute.js +17 -1
  5. package/dist/cjs/decorators/attribute.js.map +2 -2
  6. package/dist/cjs/decorators/elementDefine.js +29 -12
  7. package/dist/cjs/decorators/elementDefine.js.map +3 -3
  8. package/dist/cjs/elements/SwcAppEngine.js +3 -3
  9. package/dist/cjs/elements/SwcAppEngine.js.map +2 -2
  10. package/dist/cjs/elements/SwcAppMixin.js +1 -8
  11. package/dist/cjs/elements/SwcAppMixin.js.map +2 -2
  12. package/dist/cjs/elements/SwcAsync.js +71 -78
  13. package/dist/cjs/elements/SwcAsync.js.map +2 -2
  14. package/dist/cjs/elements/SwcChoose.js +66 -81
  15. package/dist/cjs/elements/SwcChoose.js.map +2 -2
  16. package/dist/cjs/elements/SwcIf.js +52 -96
  17. package/dist/cjs/elements/SwcIf.js.map +2 -2
  18. package/dist/cjs/elements/SwcLoop.js +60 -103
  19. package/dist/cjs/elements/SwcLoop.js.map +2 -2
  20. package/dist/cjs/elements/SwcSubTemplates.js +0 -27
  21. package/dist/cjs/elements/SwcSubTemplates.js.map +2 -2
  22. package/dist/cjs/elements/register.js +314 -343
  23. package/dist/cjs/elements/register.js.map +2 -2
  24. package/dist/cjs/router/ElementRouter.js +1 -8
  25. package/dist/cjs/router/ElementRouter.js.map +2 -2
  26. package/dist/cjs/types.js.map +1 -1
  27. package/dist/cjs/utils/Utils.js +31 -24
  28. package/dist/cjs/utils/Utils.js.map +2 -2
  29. package/dist/esm/decorators/applyStyle.js +1 -1
  30. package/dist/esm/decorators/applyStyle.js.map +2 -2
  31. package/dist/esm/decorators/attribute.js +18 -2
  32. package/dist/esm/decorators/attribute.js.map +2 -2
  33. package/dist/esm/decorators/elementDefine.js +27 -10
  34. package/dist/esm/decorators/elementDefine.js.map +3 -3
  35. package/dist/esm/elements/SwcAppEngine.js +3 -3
  36. package/dist/esm/elements/SwcAppEngine.js.map +2 -2
  37. package/dist/esm/elements/SwcAppMixin.js +1 -8
  38. package/dist/esm/elements/SwcAppMixin.js.map +2 -2
  39. package/dist/esm/elements/SwcAsync.js +72 -60
  40. package/dist/esm/elements/SwcAsync.js.map +2 -2
  41. package/dist/esm/elements/SwcChoose.js +67 -63
  42. package/dist/esm/elements/SwcChoose.js.map +2 -2
  43. package/dist/esm/elements/SwcIf.js +53 -78
  44. package/dist/esm/elements/SwcIf.js.map +2 -2
  45. package/dist/esm/elements/SwcLoop.js +61 -85
  46. package/dist/esm/elements/SwcLoop.js.map +2 -2
  47. package/dist/esm/elements/SwcSubTemplates.js +0 -8
  48. package/dist/esm/elements/SwcSubTemplates.js.map +2 -2
  49. package/dist/esm/elements/register.js +246 -275
  50. package/dist/esm/elements/register.js.map +2 -2
  51. package/dist/esm/router/ElementRouter.js +1 -8
  52. package/dist/esm/router/ElementRouter.js.map +2 -2
  53. package/dist/esm/types.js.map +1 -1
  54. package/dist/esm/utils/Utils.js +31 -24
  55. package/dist/esm/utils/Utils.js.map +2 -2
  56. package/dist/esm-bundle/dooboostore-simple-web-component.esm.js +2223 -2009
  57. package/dist/esm-bundle/dooboostore-simple-web-component.esm.js.map +4 -4
  58. package/dist/types/decorators/attribute.d.ts.map +1 -1
  59. package/dist/types/decorators/elementDefine.d.ts.map +1 -1
  60. package/dist/types/elements/SwcAppMixin.d.ts.map +1 -1
  61. package/dist/types/elements/SwcAsync.d.ts +1 -11
  62. package/dist/types/elements/SwcAsync.d.ts.map +1 -1
  63. package/dist/types/elements/SwcChoose.d.ts +1 -13
  64. package/dist/types/elements/SwcChoose.d.ts.map +1 -1
  65. package/dist/types/elements/SwcIf.d.ts +1 -13
  66. package/dist/types/elements/SwcIf.d.ts.map +1 -1
  67. package/dist/types/elements/SwcLoop.d.ts +1 -13
  68. package/dist/types/elements/SwcLoop.d.ts.map +1 -1
  69. package/dist/types/elements/SwcSubTemplates.d.ts +6 -6
  70. package/dist/types/elements/SwcSubTemplates.d.ts.map +1 -1
  71. package/dist/types/elements/register.d.ts.map +1 -1
  72. package/dist/types/router/ElementRouter.d.ts.map +1 -1
  73. package/dist/types/types.d.ts +11 -1
  74. package/dist/types/types.d.ts.map +1 -1
  75. package/dist/types/utils/Utils.d.ts +10 -0
  76. package/dist/types/utils/Utils.d.ts.map +1 -1
  77. package/dist/umd-bundle/dooboostore-simple-web-component.umd.js +2223 -2009
  78. package/dist/umd-bundle/dooboostore-simple-web-component.umd.js.map +4 -4
  79. package/package.json +3 -3
  80. package/src/decorators/applyStyle.ts +1 -1
  81. package/src/decorators/attribute.ts +35 -30
  82. package/src/decorators/elementDefine.ts +32 -10
  83. package/src/elements/SwcAppEngine.ts +3 -3
  84. package/src/elements/SwcAppMixin.ts +6 -27
  85. package/src/elements/SwcAsync.ts +75 -57
  86. package/src/elements/SwcChoose.ts +69 -77
  87. package/src/elements/SwcIf.ts +50 -75
  88. package/src/elements/SwcLoop.ts +56 -82
  89. package/src/elements/SwcSubTemplates.ts +6 -6
  90. package/src/elements/register.ts +234 -285
  91. package/src/router/ElementRouter.ts +1 -12
  92. package/src/types.ts +9 -2
  93. package/src/utils/Utils.ts +48 -37
@@ -1,70 +1,90 @@
1
1
  import { changedAttributeHost } from '../decorators/changedAttributeHost';
2
2
  import { SwcUtils } from '../utils/Utils';
3
- import { FunctionUtils } from '@dooboostore/core';
3
+ import { FunctionUtils, ActionExpression } from '@dooboostore/core';
4
+ import { ConvertUtils } from '@dooboostore/core-web';
5
+ import { SwcChooseInterface } from '../types';
6
+ import { attributeHost } from '../decorators';
7
+
8
+ class SwcChoose extends HTMLTemplateElement implements SwcChooseInterface {
9
+ @attributeHost('value')
10
+ _value: any;
11
+
12
+ @attributeHost('replace-wrap')
13
+ _replaceWrap: string | null;
4
14
 
5
- export class SwcChoose extends HTMLTemplateElement {
6
15
  private _nodes: Node[] = [];
7
- private _value: any = null;
8
16
  private _previousTemplate: HTMLTemplateElement | null = null;
9
17
 
10
18
  get value(): any {
11
19
  return this._value;
12
20
  }
21
+
13
22
  set value(nv: any) {
14
- this._value = nv;
15
- this.render();
23
+ this.refresh(nv);
16
24
  }
17
25
 
18
- private _executeCloneCallback(attr: string, args: any) {
19
- const script = this.getAttribute(attr);
20
- if (!script) return;
21
- FunctionUtils.execute({ script, context: this, args });
26
+ @changedAttributeHost('value')
27
+ private setValue() {
28
+ this.refresh();
22
29
  }
23
30
 
24
- @changedAttributeHost('value')
25
- private refresh(valueAttr?: string | null) {
26
- valueAttr ??= this.getAttribute('value');
27
- if (valueAttr === null) {
28
- this.value = null;
29
- return;
30
- }
31
+ @changedAttributeHost('replace-wrap')
32
+ private setReplaceWrap(replaceWrapAttr?: string | null) {
33
+ this.refresh();
34
+ }
31
35
 
32
- // {{ }} 브레이스로 감싸진 값은 스크립트 실행
33
- if (valueAttr.startsWith('{{') && valueAttr.endsWith('}}')) {
34
- const script = valueAttr.slice(2, -2).trim();
35
- const win = this.ownerDocument?.defaultView || window;
36
- const result = FunctionUtils.executeReturn({ script, context: this, args: SwcUtils.getHelperAndHostSet(win, this) });
37
- this.value = result;
38
- } else {
39
- // 일반 문자열 값
40
- this.value = valueAttr;
36
+ private replaceExpressions(html: string, groupArgs: any, context: any, wrap?: string | null): string {
37
+ const config = wrap ? { wrapExpression: wrap } : undefined;
38
+ const ae = new ActionExpression(html, config);
39
+ const filterOptions = wrap ? { type: 'replace' as const, wrap } : 'replace';
40
+ const win = this.ownerDocument?.defaultView || window;
41
+
42
+ // @ts-ignore
43
+ for (const expr of ae.getExpressions(filterOptions)) {
44
+ try {
45
+ const value = FunctionUtils.executeReturn({
46
+ script: ConvertUtils.decodeHtmlEntity(expr.script, win.document),
47
+ context,
48
+ args: groupArgs
49
+ });
50
+ ae.replace(expr, String(value));
51
+ } catch (e) {
52
+ console.error(`[SWC] Failed to execute ${expr.original}:`, e);
53
+ }
41
54
  }
55
+ return ae.getUnprocessedTemplate();
42
56
  }
43
57
 
44
- private render() {
58
+ refresh(value = this._value) {
59
+ const portal = this.parentElement;
60
+ if (!portal) return;
61
+
45
62
  const win = this.ownerDocument?.defaultView || window;
46
63
  const helperSet = SwcUtils.getHelperAndHostSet(win, this);
64
+ const groupArgs = { ...helperSet, $value: value };
65
+
47
66
  const templates = Array.from(this.content.querySelectorAll('template'));
48
67
  let selected: HTMLTemplateElement | null = null;
49
68
 
50
69
  for (const t of templates) {
51
70
  if (t.getAttribute('is') === 'swc-when') {
71
+ const attrVal = t.getAttribute('value') || '';
52
72
  let matches = false;
53
- const value = t.getAttribute('value') || '';
54
73
 
55
- // {{ }} 브레이스로 감싸진 값은 스크립트 실행
56
- if (value.startsWith('{{') && value.endsWith('}}')) {
57
- const script = value.slice(2, -2).trim();
74
+ // Use ActionExpression to parse and extract script from value attribute
75
+ const ae = new ActionExpression(attrVal);
76
+ const expr = ae.getFirstExpression('replace');
77
+
78
+ if (expr) {
79
+ const decodedScript = ConvertUtils.decodeHtmlEntity(expr.script, win.document);
58
80
  const result = FunctionUtils.executeReturn({
59
- script,
81
+ script: decodedScript,
60
82
  context: t,
61
- args: { ...helperSet, $value: this._value }
83
+ args: groupArgs
62
84
  });
63
- // console.log('rrrrrrrrrrrrrr', result);
64
- matches = result === true;
85
+ matches = !!result;
65
86
  } else {
66
- // 일반 문자열 비교 (기존 방식)
67
- matches = String(value) === String(this._value);
87
+ matches = String(attrVal) === String(value);
68
88
  }
69
89
 
70
90
  if (matches) {
@@ -76,11 +96,12 @@ export class SwcChoose extends HTMLTemplateElement {
76
96
 
77
97
  const target = selected || (this.content.querySelector('template[is="swc-otherwise"]') as HTMLTemplateElement);
78
98
 
79
- // skip-if-same attribute가 있고 이전과 같은 template이면 렌더링 스킵
80
- // 최상위 skip-if-same 또는 각 when/otherwise의 skip-if-same 체크
81
- const targetHasSkipIfSame = target?.hasAttribute('skip-if-same');
82
- const shouldSkip = (this.hasAttribute('skip-if-same') || targetHasSkipIfSame) && this._previousTemplate === target;
83
- if (shouldSkip) {
99
+ const skipValue = target?.getAttribute?.('skip-if-same');
100
+ const skipValue2 = this._previousTemplate?.getAttribute?.('skip-if-same');
101
+ const hasSkipIfSame = target?.hasAttribute?.('skip-if-same');
102
+ const isSame = this._previousTemplate === target;
103
+ // console.log('---rrrr---', this._previousTemplate, target, hasSkipIfSame, isSame, skipValue, skipValue2);
104
+ if (hasSkipIfSame && isSame) {
84
105
  return;
85
106
  }
86
107
 
@@ -88,41 +109,15 @@ export class SwcChoose extends HTMLTemplateElement {
88
109
  this._previousTemplate = target;
89
110
 
90
111
  if (target) {
91
- const clone = target.content.cloneNode(true) as DocumentFragment;
92
- this._nodes = Array.from(clone.childNodes);
93
- const elements = this._nodes.filter(n => n.nodeType === 1) as HTMLElement[];
94
- const groupArgs = { ...helperSet, $value: this._value, $nodes: this._nodes, $elements: elements, $firstElement: elements[0] };
95
-
96
- // 복제된 최상위 요소들은 SwcChoose의 직접 자식이 될 것이므로 __swc_host 설정 안 함
97
- this._nodes.forEach((n, nodeIdx) => {
98
- // if (n instanceof HTMLElement) (n as any).__swc_host = this; // 제거: 부모 체인 유지
99
- this._executeCloneCallback('on-clone-node', { ...groupArgs, $node: n, $nodeIndex: nodeIdx, $isElement: n.nodeType === 1 });
100
-
101
- // Attribute {{ }} 치환 - HTML string 차원에서 동적 치환
102
- if (n instanceof HTMLElement) {
103
- Array.from(n.attributes).forEach(attr => {
104
- const value = attr.value || '';
105
- if (value.startsWith('{{') && value.endsWith('}}')) {
106
- const script = value.slice(2, -2).trim();
107
- const result = FunctionUtils.executeReturn({
108
- script,
109
- context: n,
110
- args: { ...groupArgs, $value: this._value }
111
- });
112
- n.setAttribute(attr.name, String(result));
113
- }
114
- });
115
- }
116
- });
112
+ let htmlString = target.innerHTML;
113
+ htmlString = this.replaceExpressions(htmlString, groupArgs, this, this._replaceWrap);
117
114
 
118
- this._executeCloneCallback('on-clone-nodes', groupArgs);
115
+ const result = this.ownerDocument?.createElement('template') || document.createElement('template');
116
+ result.innerHTML = htmlString;
117
+ const processedFragment = result.content;
119
118
 
120
- this.parentElement?.insertBefore(clone, this.nextSibling);
121
- // if (portal === this.parentElement) {
122
- // portal.insertBefore(clone, this.nextSibling);
123
- // } else {
124
- // portal.appendChild(clone);
125
- // }
119
+ this._nodes = Array.from(processedFragment.childNodes);
120
+ portal.insertBefore(processedFragment, this.nextSibling);
126
121
  }
127
122
  }
128
123
 
@@ -131,9 +126,6 @@ export class SwcChoose extends HTMLTemplateElement {
131
126
  this._nodes = [];
132
127
  }
133
128
 
134
- connectedCallback() {
135
- this.refresh();
136
- }
137
129
  disconnectedCallback() {
138
130
  this.cleanup();
139
131
  }
@@ -1,101 +1,79 @@
1
1
  import { changedAttributeHost } from '../decorators/changedAttributeHost';
2
2
  import { SwcUtils } from '../utils/Utils';
3
- import { FunctionUtils } from '@dooboostore/core';
3
+ import { FunctionUtils, ActionExpression } from '@dooboostore/core';
4
+ import { ConvertUtils } from '@dooboostore/core-web';
5
+ import { SwcIfInterface } from '../types';
6
+ import { attributeHost } from '../decorators';
7
+
8
+ class SwcIf extends HTMLTemplateElement implements SwcIfInterface {
9
+ @attributeHost('value')
10
+ _value: any;
11
+
12
+ @attributeHost('replace-wrap')
13
+ _replaceWrap: string | null;
4
14
 
5
- export class SwcIf extends HTMLTemplateElement {
6
- private _value = false;
7
15
  private _nodes: Node[] = [];
8
16
 
9
17
  get value(): boolean {
10
18
  return this._value;
11
19
  }
20
+
12
21
  set value(nv: any) {
13
- this._value = Boolean(nv);
14
- this.render();
22
+ // this._value = nv;
23
+ this.refresh(nv);
15
24
  }
16
25
 
17
- private _resolvePortal(): Element | null {
18
- const portalScript = this.getAttribute('on-get-portal');
19
- if (!portalScript) return this.parentElement;
20
-
26
+ private replaceExpressions(html: string, groupArgs: any, context: any, wrap?: string | null): string {
27
+ const config = wrap ? { wrapExpression: wrap } : undefined;
28
+ const ae = new ActionExpression(html, config);
29
+ const filterOptions = wrap ? { type: 'replace' as const, wrap } : 'replace';
21
30
  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
+ // @ts-ignore
33
+ for (const expr of ae.getExpressions(filterOptions)) {
34
+ try {
35
+ const value = FunctionUtils.executeReturn({
36
+ script: ConvertUtils.decodeHtmlEntity(expr.script, win.document),
37
+ context,
38
+ args: groupArgs
39
+ });
40
+ ae.replace(expr, String(value));
41
+ } catch (e) {
42
+ console.error(`[SWC] Failed to execute ${expr.original}:`, e);
43
+ }
44
+ }
45
+ return ae.getUnprocessedTemplate();
31
46
  }
32
47
 
33
- private _executeCloneCallback(attr: string, args: any) {
34
- const script = this.getAttribute(attr);
35
- if (!script) return;
36
- FunctionUtils.execute({ script, context: this, args });
48
+ @changedAttributeHost('value')
49
+ private setValue(valueAttr?: string | null) {
50
+ this.refresh();
37
51
  }
38
52
 
39
- @changedAttributeHost('on-get-value')
40
- private refreshValue() {
41
- const script = this.getAttribute('on-get-value');
42
- if (!script) {
43
- this.value = false;
44
- return;
45
- }
46
- const win = this.ownerDocument?.defaultView || window;
47
- const res = FunctionUtils.executeReturn({
48
- script,
49
- context: this,
50
- args: SwcUtils.getHelperAndHostSet(win, this)
51
- });
52
-
53
- if (res instanceof Promise) {
54
- res.then(v => (this.value = v)).catch(() => (this.value = false));
55
- } else {
56
- this.value = res;
57
- }
53
+ @changedAttributeHost('replace-wrap')
54
+ private setReplaceWrap(replaceWrapAttr?: string | null) {
55
+ this.refresh();
58
56
  }
59
57
 
60
- private render() {
58
+ refresh(value = this._value) {
61
59
  this.cleanup();
62
- const portal = this._resolvePortal();
63
- if (!portal) return;
60
+ const portal = this.parentElement;
61
+ if (!portal || !value) return;
64
62
 
65
63
  const win = this.ownerDocument?.defaultView || window;
66
64
  const helperSet = SwcUtils.getHelperAndHostSet(win, this);
67
65
 
68
- let targetTpl: HTMLTemplateElement | null = null;
69
- if (this._value) {
70
- targetTpl = this;
71
- } else {
72
- targetTpl = this.content.querySelector('template[is="swc-default"]') as HTMLTemplateElement;
73
- }
74
-
75
- if (targetTpl) {
76
- const clone = targetTpl.content.cloneNode(true) as DocumentFragment;
77
- // If it's the host template, remove sub-templates from clone
78
- if (targetTpl === this) {
79
- clone.querySelectorAll('template[is^="swc-"]').forEach(t => t.remove());
80
- }
81
-
82
- this._nodes = Array.from(clone.childNodes);
83
- const elements = this._nodes.filter(n => n.nodeType === 1) as HTMLElement[];
84
- const groupArgs = { ...helperSet, $value: this._value, $nodes: this._nodes, $elements: elements, $firstElement: elements[0] };
66
+ let htmlString = this.innerHTML;
67
+ const groupArgs = { ...helperSet, $value: value };
85
68
 
86
- this._nodes.forEach((node, nodeIdx) => {
87
- if (node instanceof HTMLElement) (node as any).__swc_host = this;
88
- this._executeCloneCallback('on-clone-node', { ...groupArgs, $node: node, $nodeIndex: nodeIdx, $isElement: node.nodeType === 1 });
89
- });
69
+ htmlString = this.replaceExpressions(htmlString, groupArgs, this, this._replaceWrap);
90
70
 
91
- this._executeCloneCallback('on-clone-nodes', groupArgs);
71
+ const result = this.ownerDocument?.createElement('template') || document.createElement('template');
72
+ result.innerHTML = htmlString;
73
+ const processedFragment = result.content;
92
74
 
93
- if (portal === this.parentElement) {
94
- portal.insertBefore(clone, this.nextSibling);
95
- } else {
96
- portal.appendChild(clone);
97
- }
98
- }
75
+ this._nodes = Array.from(processedFragment.childNodes);
76
+ portal.insertBefore(processedFragment, this.nextSibling);
99
77
  }
100
78
 
101
79
  private cleanup() {
@@ -103,9 +81,6 @@ export class SwcIf extends HTMLTemplateElement {
103
81
  this._nodes = [];
104
82
  }
105
83
 
106
- connectedCallback() {
107
- this.refreshValue();
108
- }
109
84
  disconnectedCallback() {
110
85
  this.cleanup();
111
86
  }
@@ -1,118 +1,92 @@
1
1
  import { changedAttributeHost } from '../decorators/changedAttributeHost';
2
2
  import { SwcUtils } from '../utils/Utils';
3
- import { FunctionUtils } from '@dooboostore/core';
3
+ import { FunctionUtils, ActionExpression } from '@dooboostore/core';
4
+ import { ConvertUtils } from '@dooboostore/core-web';
5
+ import { attributeHost } from '../decorators';
6
+
7
+ class SwcLoop extends HTMLTemplateElement {
8
+ @attributeHost('value')
9
+ _value: any[];
10
+
11
+ @attributeHost('replace-wrap')
12
+ _replaceWrap: string | null;
4
13
 
5
- export class SwcLoop extends HTMLTemplateElement {
6
- private _value: any[] = [];
7
14
  private _nodeGroups: Node[][] = [];
8
15
 
9
16
  get value(): any[] {
10
17
  return this._value;
11
18
  }
12
19
  set value(nv: any[]) {
13
- this._value = Array.isArray(nv) ? [...nv] : [];
14
- this.render();
15
- }
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;
20
+ // this._value = Array.isArray(nv) ? [...nv] : [];
21
+ this.refresh(nv);
31
22
  }
32
23
 
33
- private _executeCloneCallback(attr: string, args: any) {
34
- const script = this.getAttribute(attr);
35
- if (!script) return;
36
- FunctionUtils.execute({ script, context: this, args });
24
+ @changedAttributeHost('value')
25
+ private setValue() {
26
+ this.refresh();
37
27
  }
38
28
 
39
- private _cleanup() {
40
- this._nodeGroups.forEach(nodes => nodes.forEach(n => n.parentElement?.removeChild(n)));
41
- this._nodeGroups = [];
29
+ @changedAttributeHost('replace-wrap')
30
+ private setReplaceWrap() {
31
+ this.refresh();
42
32
  }
43
33
 
44
- @changedAttributeHost('on-get-value')
45
- private refreshValue() {
46
- const script = this.getAttribute('on-get-value');
47
- if (!script) {
48
- this.value = [];
49
- return;
50
- }
34
+ private replaceExpressions(html: string, groupArgs: any, context: any, wrap?: string | null): string {
35
+ const config = wrap ? { wrapExpression: wrap } : undefined;
36
+ const ae = new ActionExpression(html, config);
37
+ const filterOptions = wrap ? { type: 'replace' as const, wrap } : 'replace';
51
38
  const win = this.ownerDocument?.defaultView || window;
52
- const res = FunctionUtils.executeReturn({
53
- script,
54
- context: this,
55
- args: SwcUtils.getHelperAndHostSet(win, this)
56
- });
57
39
 
58
- if (res instanceof Promise) {
59
- res.then(v => (this.value = v)).catch(() => (this.value = []));
60
- } else {
61
- this.value = res;
40
+ // @ts-ignore
41
+ for (const expr of ae.getExpressions(filterOptions)) {
42
+ try {
43
+ const value = FunctionUtils.executeReturn({
44
+ script: ConvertUtils.decodeHtmlEntity(expr.script, win.document),
45
+ context,
46
+ args: groupArgs
47
+ });
48
+ ae.replace(expr, String(value));
49
+ } catch (e) {
50
+ console.error(`[SWC] Failed to execute ${expr.original}:`, e);
51
+ }
62
52
  }
53
+ return ae.getUnprocessedTemplate();
63
54
  }
64
55
 
65
- render() {
66
- this._cleanup();
67
- const portal = this._resolvePortal();
56
+ refresh(value = this._value) {
57
+ this.cleanup();
58
+ if (value?.length<=0) return;
59
+ const portal = this.parentElement;
68
60
  if (!portal) return;
69
61
 
70
62
  const win = this.ownerDocument?.defaultView || window;
71
63
  const helperSet = SwcUtils.getHelperAndHostSet(win, this);
72
64
  const fragment = win.document.createDocumentFragment();
73
65
 
74
- if (this._value.length === 0) {
75
- const defaultTpl = this.content.querySelector('template[is="swc-default"]') as HTMLTemplateElement;
76
- if (defaultTpl) {
77
- const clone = defaultTpl.content.cloneNode(true);
78
- const nodes = Array.from(clone.childNodes);
79
- this._nodeGroups.push(nodes);
80
- fragment.appendChild(clone);
81
- }
82
- } else {
83
- this._value.forEach((item, index) => {
84
- const clone = this.content.cloneNode(true) as DocumentFragment;
85
- // Filter out sub-templates
86
- const subTemplates = clone.querySelectorAll('template[is^="swc-"]');
87
- subTemplates.forEach(t => t.remove());
88
-
89
- const nodes = Array.from(clone.childNodes);
90
- const elements = nodes.filter(n => n.nodeType === 1) as HTMLElement[];
66
+ value.forEach((item, index) => {
67
+ let htmlString = this.innerHTML;
68
+ const groupArgs = { ...helperSet, $item: item, $index: index, $value: this._value };
91
69
 
92
- const groupArgs = { ...helperSet, $item: item, $index: index, $value: this._value, $nodes: nodes, $elements: elements, $firstElement: elements[0] };
70
+ htmlString = this.replaceExpressions(htmlString, groupArgs, this, this._replaceWrap);
93
71
 
94
- nodes.forEach((node, nodeIdx) => {
95
- if (node instanceof HTMLElement) (node as any).__swc_host = this;
96
- this._executeCloneCallback('on-clone-node', { ...groupArgs, $node: node, $nodeIndex: nodeIdx, $isElement: node.nodeType === 1 });
97
- });
72
+ const temp = this.ownerDocument?.createElement('template') || document.createElement('template');
73
+ temp.innerHTML = htmlString;
98
74
 
99
- this._executeCloneCallback('on-clone-nodes', groupArgs);
100
- this._nodeGroups.push(nodes);
101
- fragment.appendChild(clone);
102
- });
103
- }
75
+ const content = temp.content;
76
+ const nodes = Array.from(content.childNodes);
77
+ this._nodeGroups.push(nodes);
78
+ fragment.appendChild(content);
79
+ });
104
80
 
105
- if (portal === this.parentElement) {
106
- portal.insertBefore(fragment, this.nextSibling);
107
- } else {
108
- portal.appendChild(fragment);
109
- }
81
+ portal.insertBefore(fragment, this.nextSibling);
110
82
  }
111
83
 
112
- connectedCallback() {
113
- this.refreshValue();
84
+ private cleanup() {
85
+ this._nodeGroups.forEach(nodes => nodes.forEach(n => n.parentElement?.removeChild(n)));
86
+ this._nodeGroups = [];
114
87
  }
88
+
115
89
  disconnectedCallback() {
116
- this._cleanup();
90
+ this.cleanup();
117
91
  }
118
92
  }
@@ -1,6 +1,6 @@
1
- export class SwcLoading extends HTMLTemplateElement {}
2
- export class SwcError extends HTMLTemplateElement {}
3
- export class SwcSuccess extends HTMLTemplateElement {}
4
- export class SwcWhen extends HTMLTemplateElement {}
5
- export class SwcOtherwise extends HTMLTemplateElement {}
6
- export class SwcDefault extends HTMLTemplateElement {}
1
+ class SwcLoading extends HTMLTemplateElement {}
2
+ class SwcError extends HTMLTemplateElement {}
3
+ class SwcSuccess extends HTMLTemplateElement {}
4
+ class SwcWhen extends HTMLTemplateElement {}
5
+ class SwcOtherwise extends HTMLTemplateElement {}
6
+ class SwcDefault extends HTMLTemplateElement {}