@dooboostore/simple-web-component 1.0.33 → 1.0.35

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 (150) hide show
  1. package/README.md +23 -30
  2. package/dist/cjs/decorators/addEventListener.js.map +1 -1
  3. package/dist/cjs/decorators/applyAttribute.js +67 -57
  4. package/dist/cjs/decorators/applyAttribute.js.map +2 -2
  5. package/dist/cjs/decorators/applyClass.js +80 -70
  6. package/dist/cjs/decorators/applyClass.js.map +2 -2
  7. package/dist/cjs/decorators/applyNode.js +5 -8
  8. package/dist/cjs/decorators/applyNode.js.map +2 -2
  9. package/dist/cjs/decorators/applyStyle.js +66 -56
  10. package/dist/cjs/decorators/applyStyle.js.map +2 -2
  11. package/dist/cjs/decorators/attribute.js +35 -29
  12. package/dist/cjs/decorators/attribute.js.map +2 -2
  13. package/dist/cjs/decorators/changedAttributeHost.js +32 -12
  14. package/dist/cjs/decorators/changedAttributeHost.js.map +2 -2
  15. package/dist/cjs/decorators/elementDefine.js +238 -180
  16. package/dist/cjs/decorators/elementDefine.js.map +2 -2
  17. package/dist/cjs/decorators/emitCustomEvent.js +67 -48
  18. package/dist/cjs/decorators/emitCustomEvent.js.map +2 -2
  19. package/dist/cjs/decorators/lifecycles.js +15 -17
  20. package/dist/cjs/decorators/lifecycles.js.map +2 -2
  21. package/dist/cjs/decorators/setProperty.js +50 -39
  22. package/dist/cjs/decorators/setProperty.js.map +2 -2
  23. package/dist/cjs/decorators/subscribeSwcAppMessageWhileConnected.js.map +1 -1
  24. package/dist/cjs/decorators/subscribeSwcAppRouteChangeWhileConnected.js +2 -2
  25. package/dist/cjs/decorators/subscribeSwcAppRouteChangeWhileConnected.js.map +2 -2
  26. package/dist/cjs/elements/SwcAppEngine.js +14 -8
  27. package/dist/cjs/elements/SwcAppEngine.js.map +3 -3
  28. package/dist/cjs/elements/SwcAppMixin.js +154 -81
  29. package/dist/cjs/elements/SwcAppMixin.js.map +2 -2
  30. package/dist/cjs/elements/SwcAsync.js +28 -18
  31. package/dist/cjs/elements/SwcAsync.js.map +2 -2
  32. package/dist/cjs/elements/SwcChoose.js +68 -45
  33. package/dist/cjs/elements/SwcChoose.js.map +2 -2
  34. package/dist/cjs/elements/SwcIf.js +52 -30
  35. package/dist/cjs/elements/SwcIf.js.map +2 -2
  36. package/dist/cjs/elements/SwcLoop.js +109 -35
  37. package/dist/cjs/elements/SwcLoop.js.map +2 -2
  38. package/dist/cjs/elements/register.js +263 -131
  39. package/dist/cjs/elements/register.js.map +2 -2
  40. package/dist/cjs/router/ElementRouter.js +4 -2
  41. package/dist/cjs/router/ElementRouter.js.map +1 -1
  42. package/dist/cjs/types.js.map +1 -1
  43. package/dist/cjs/utils/Utils.js +25 -7
  44. package/dist/cjs/utils/Utils.js.map +2 -2
  45. package/dist/esm/decorators/addEventListener.js.map +1 -1
  46. package/dist/esm/decorators/applyAttribute.js +67 -57
  47. package/dist/esm/decorators/applyAttribute.js.map +2 -2
  48. package/dist/esm/decorators/applyClass.js +80 -70
  49. package/dist/esm/decorators/applyClass.js.map +2 -2
  50. package/dist/esm/decorators/applyNode.js +5 -8
  51. package/dist/esm/decorators/applyNode.js.map +2 -2
  52. package/dist/esm/decorators/applyStyle.js +66 -56
  53. package/dist/esm/decorators/applyStyle.js.map +2 -2
  54. package/dist/esm/decorators/attribute.js +35 -29
  55. package/dist/esm/decorators/attribute.js.map +2 -2
  56. package/dist/esm/decorators/changedAttributeHost.js +32 -12
  57. package/dist/esm/decorators/changedAttributeHost.js.map +2 -2
  58. package/dist/esm/decorators/elementDefine.js +240 -182
  59. package/dist/esm/decorators/elementDefine.js.map +2 -2
  60. package/dist/esm/decorators/emitCustomEvent.js +67 -48
  61. package/dist/esm/decorators/emitCustomEvent.js.map +2 -2
  62. package/dist/esm/decorators/lifecycles.js +15 -17
  63. package/dist/esm/decorators/lifecycles.js.map +2 -2
  64. package/dist/esm/decorators/setProperty.js +50 -39
  65. package/dist/esm/decorators/setProperty.js.map +2 -2
  66. package/dist/esm/decorators/subscribeSwcAppMessageWhileConnected.js.map +1 -1
  67. package/dist/esm/decorators/subscribeSwcAppRouteChangeWhileConnected.js +2 -2
  68. package/dist/esm/decorators/subscribeSwcAppRouteChangeWhileConnected.js.map +2 -2
  69. package/dist/esm/elements/SwcAppEngine.js +14 -7
  70. package/dist/esm/elements/SwcAppEngine.js.map +2 -2
  71. package/dist/esm/elements/SwcAppMixin.js +156 -82
  72. package/dist/esm/elements/SwcAppMixin.js.map +2 -2
  73. package/dist/esm/elements/SwcAsync.js +28 -18
  74. package/dist/esm/elements/SwcAsync.js.map +2 -2
  75. package/dist/esm/elements/SwcChoose.js +69 -46
  76. package/dist/esm/elements/SwcChoose.js.map +2 -2
  77. package/dist/esm/elements/SwcIf.js +53 -31
  78. package/dist/esm/elements/SwcIf.js.map +2 -2
  79. package/dist/esm/elements/SwcLoop.js +110 -36
  80. package/dist/esm/elements/SwcLoop.js.map +2 -2
  81. package/dist/esm/elements/register.js +264 -132
  82. package/dist/esm/elements/register.js.map +2 -2
  83. package/dist/esm/router/ElementRouter.js +5 -2
  84. package/dist/esm/router/ElementRouter.js.map +1 -1
  85. package/dist/esm/types.js.map +1 -1
  86. package/dist/esm/utils/Utils.js +25 -7
  87. package/dist/esm/utils/Utils.js.map +2 -2
  88. package/dist/esm-bundle/dooboostore-simple-web-component.esm.js +728 -595
  89. package/dist/esm-bundle/dooboostore-simple-web-component.esm.js.map +4 -4
  90. package/dist/types/decorators/addEventListener.d.ts +4 -1
  91. package/dist/types/decorators/addEventListener.d.ts.map +1 -1
  92. package/dist/types/decorators/applyAttribute.d.ts +5 -1
  93. package/dist/types/decorators/applyAttribute.d.ts.map +1 -1
  94. package/dist/types/decorators/applyClass.d.ts +4 -0
  95. package/dist/types/decorators/applyClass.d.ts.map +1 -1
  96. package/dist/types/decorators/applyNode.d.ts +5 -1
  97. package/dist/types/decorators/applyNode.d.ts.map +1 -1
  98. package/dist/types/decorators/applyStyle.d.ts +5 -1
  99. package/dist/types/decorators/applyStyle.d.ts.map +1 -1
  100. package/dist/types/decorators/attribute.d.ts +5 -0
  101. package/dist/types/decorators/attribute.d.ts.map +1 -1
  102. package/dist/types/decorators/changedAttributeHost.d.ts +14 -3
  103. package/dist/types/decorators/changedAttributeHost.d.ts.map +1 -1
  104. package/dist/types/decorators/elementDefine.d.ts +1 -1
  105. package/dist/types/decorators/elementDefine.d.ts.map +1 -1
  106. package/dist/types/decorators/emitCustomEvent.d.ts +15 -7
  107. package/dist/types/decorators/emitCustomEvent.d.ts.map +1 -1
  108. package/dist/types/decorators/lifecycles.d.ts +10 -7
  109. package/dist/types/decorators/lifecycles.d.ts.map +1 -1
  110. package/dist/types/decorators/setProperty.d.ts +5 -1
  111. package/dist/types/decorators/setProperty.d.ts.map +1 -1
  112. package/dist/types/decorators/subscribeSwcAppMessageWhileConnected.d.ts +2 -2
  113. package/dist/types/decorators/subscribeSwcAppMessageWhileConnected.d.ts.map +1 -1
  114. package/dist/types/decorators/subscribeSwcAppRouteChangeWhileConnected.d.ts +5 -6
  115. package/dist/types/decorators/subscribeSwcAppRouteChangeWhileConnected.d.ts.map +1 -1
  116. package/dist/types/elements/SwcAppEngine.d.ts +9 -3
  117. package/dist/types/elements/SwcAppEngine.d.ts.map +1 -1
  118. package/dist/types/elements/SwcAppMixin.d.ts +12 -7
  119. package/dist/types/elements/SwcAppMixin.d.ts.map +1 -1
  120. package/dist/types/elements/register.d.ts.map +1 -1
  121. package/dist/types/types.d.ts +1 -0
  122. package/dist/types/types.d.ts.map +1 -1
  123. package/dist/types/utils/Utils.d.ts +4 -1
  124. package/dist/types/utils/Utils.d.ts.map +1 -1
  125. package/dist/umd-bundle/dooboostore-simple-web-component.umd.js +728 -595
  126. package/dist/umd-bundle/dooboostore-simple-web-component.umd.js.map +4 -4
  127. package/package.json +5 -5
  128. package/src/decorators/addEventListener.ts +1 -1
  129. package/src/decorators/applyAttribute.ts +74 -61
  130. package/src/decorators/applyClass.ts +91 -77
  131. package/src/decorators/applyNode.ts +16 -13
  132. package/src/decorators/applyStyle.ts +75 -61
  133. package/src/decorators/attribute.ts +55 -43
  134. package/src/decorators/changedAttributeHost.ts +49 -15
  135. package/src/decorators/elementDefine.ts +401 -340
  136. package/src/decorators/emitCustomEvent.ts +92 -62
  137. package/src/decorators/lifecycles.ts +11 -10
  138. package/src/decorators/setProperty.ts +58 -44
  139. package/src/decorators/subscribeSwcAppMessageWhileConnected.ts +2 -2
  140. package/src/decorators/subscribeSwcAppRouteChangeWhileConnected.ts +11 -11
  141. package/src/elements/SwcAppEngine.ts +19 -13
  142. package/src/elements/SwcAppMixin.ts +214 -102
  143. package/src/elements/SwcAsync.ts +28 -17
  144. package/src/elements/SwcChoose.ts +94 -36
  145. package/src/elements/SwcIf.ts +59 -22
  146. package/src/elements/SwcLoop.ts +140 -27
  147. package/src/elements/register.ts +319 -100
  148. package/src/types.ts +1 -0
  149. package/src/utils/Utils.ts +38 -10
  150. package/tsconfig.json +2 -2
@@ -3,14 +3,15 @@ import { SwcUtils } from '../utils/Utils';
3
3
  import { FunctionUtils, ActionExpression } from '@dooboostore/core';
4
4
  import { ConvertUtils } from '@dooboostore/core-web';
5
5
  import { SwcChooseInterface } from '../types';
6
- import { attributeHost } from '../decorators';
6
+ import { attributeHost, getAttributeValue, getElementConfig } from '../decorators';
7
7
 
8
8
  class SwcChoose extends HTMLTemplateElement implements SwcChooseInterface {
9
- @attributeHost('value')
10
9
  _value: any;
11
10
 
12
- @attributeHost('replace-wrap')
13
- _replaceWrap: string | null;
11
+ @attributeHost('replace-wrap-start')
12
+ _replaceWrapStart: string | null;
13
+ @attributeHost('replace-wrap-end')
14
+ _replaceWrapEnd: string | null;
14
15
 
15
16
  private _nodes: Node[] = [];
16
17
  private _previousTemplate: HTMLTemplateElement | null = null;
@@ -20,27 +21,52 @@ class SwcChoose extends HTMLTemplateElement implements SwcChooseInterface {
20
21
  }
21
22
 
22
23
  set value(nv: any) {
23
- this.refresh(nv);
24
+ this._value = nv;
25
+ this.refresh(this._value);
24
26
  }
25
27
 
26
- @changedAttributeHost('value')
27
- private setValue() {
28
- this.refresh();
28
+ constructor() {
29
+ super();
30
+ // console.log('swc chooooooooooooooooooooooooose constructor called!');
31
+ // console.log('swc cccccccss', this.getAttribute('value'));
29
32
  }
30
33
 
31
- @changedAttributeHost('replace-wrap')
32
- private setReplaceWrap(replaceWrapAttr?: string | null) {
33
- this.refresh();
34
+ // static get observedAttributes() {
35
+ // return ['value'];
36
+ // }
37
+
38
+ attributeChangedCallback(na, o, n) {
39
+ // console.log('-----------------choooooooose attr origin', na, o, n);
40
+ }
41
+
42
+ @changedAttributeHost('value')
43
+ private setValue(value: any, o: string, n: string) {
44
+ console.log('chooooooose value', value)
45
+ this._value = value;
46
+ this.refresh(this._value);
34
47
  }
35
48
 
36
- private replaceExpressions(html: string, groupArgs: any, context: any, wrap?: string | null): string {
37
- const config = wrap ? { wrapExpression: wrap } : undefined;
49
+ // @changedAttributeHost('replace-wrap')
50
+ // private setReplaceWrap(replaceWrapAttr?: string | null) {
51
+ // this.refresh();
52
+ // }
53
+
54
+ private replaceExpressions(html: string, groupArgs: any, context: any, wrap: {start: string, end: string}): string {
55
+ let config: any = undefined;
56
+ if (wrap) {
57
+ config = {
58
+ expression: {
59
+ replace: { start: `${wrap.start}`, end: `${wrap.end}` },
60
+ callReturn: { start: `${wrap.start}=`, end: `${wrap.end}` },
61
+ call: { start: `${wrap.start}@`, end: `${wrap.end}` }
62
+ }
63
+ };
64
+ }
38
65
  const ae = new ActionExpression(html, config);
39
- const filterOptions = wrap ? { type: 'replace' as const, wrap } : 'replace';
40
66
  const win = this.ownerDocument?.defaultView || window;
41
67
 
42
68
  // @ts-ignore
43
- for (const expr of ae.getExpressions(filterOptions)) {
69
+ for (const expr of ae.getExpressions('replace')) {
44
70
  try {
45
71
  const value = FunctionUtils.executeReturn({
46
72
  script: ConvertUtils.decodeHtmlEntity(expr.script, win.document),
@@ -55,37 +81,50 @@ class SwcChoose extends HTMLTemplateElement implements SwcChooseInterface {
55
81
  return ae.getUnprocessedTemplate();
56
82
  }
57
83
 
58
- refresh(value = this._value) {
59
- const portal = this.parentElement;
60
- if (!portal) return;
84
+ refresh(value?: any) {
85
+ value = getAttributeValue(this, 'value') ?? this._value;
86
+ console.log('[DEBUG] SwcChoose.refresh called, value:', value);
87
+ const portal = SwcUtils.getParentContainer(this);
88
+ if (!portal) {
89
+ console.log('[DEBUG] SwcChoose portal is null!');
90
+ return;
91
+ }
61
92
 
62
- const win = this.ownerDocument?.defaultView || window;
93
+ const win = (getElementConfig(this).window ?? this.ownerDocument?.defaultView) || window;
63
94
  const helperSet = SwcUtils.getHelperAndHostSet(win, this);
64
- const groupArgs = { ...helperSet, $value: value };
95
+ const groupArgs = { ...helperSet, $value: value, $templateHost: this };
65
96
 
66
97
  const templates = Array.from(this.content.querySelectorAll('template'));
67
98
  let selected: HTMLTemplateElement | null = null;
68
99
 
69
100
  for (const t of templates) {
70
101
  if (t.getAttribute('is') === 'swc-when') {
71
- const attrVal = t.getAttribute('value') || '';
102
+ const attrVal = t.getAttribute('test') || '';
72
103
  let matches = false;
73
104
 
105
+
106
+
107
+ const result = FunctionUtils.executeReturn({
108
+ script: attrVal,
109
+ context: t,
110
+ args: groupArgs
111
+ });
112
+ matches = !!result;
74
113
  // 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);
80
- const result = FunctionUtils.executeReturn({
81
- script: decodedScript,
82
- context: t,
83
- args: groupArgs
84
- });
85
- matches = !!result;
86
- } else {
87
- matches = String(attrVal) === String(value);
88
- }
114
+ // const ae = new ActionExpression(attrVal);
115
+ // const expr = ae.getFirstExpression('replace');
116
+
117
+ // if (expr) {
118
+ // const decodedScript = ConvertUtils.decodeHtmlEntity(expr.script, win.document);
119
+ // const result = FunctionUtils.executeReturn({
120
+ // script: decodedScript,
121
+ // context: t,
122
+ // args: groupArgs
123
+ // });
124
+ // matches = !!result;
125
+ // } else {
126
+ // matches = String(attrVal) === String(value);
127
+ // }
89
128
 
90
129
  if (matches) {
91
130
  selected = t;
@@ -110,14 +149,33 @@ class SwcChoose extends HTMLTemplateElement implements SwcChooseInterface {
110
149
 
111
150
  if (target) {
112
151
  let htmlString = target.innerHTML;
113
- htmlString = this.replaceExpressions(htmlString, groupArgs, this, this._replaceWrap);
152
+ htmlString = this.replaceExpressions(htmlString, groupArgs, this, {start:this._replaceWrapStart ?? '{#{', end: this._replaceWrapEnd ?? '}#}'});
114
153
 
115
154
  const result = this.ownerDocument?.createElement('template') || document.createElement('template');
116
155
  result.innerHTML = htmlString;
117
156
  const processedFragment = result.content;
118
157
 
158
+ // console.log('[DEBUG] SwcChoose processedFragment children:', processedFragment._childNodesInternal?.length);
119
159
  this._nodes = Array.from(processedFragment.childNodes);
160
+
161
+ const setTemplateHost = (element: HTMLElement) => {
162
+ (element as any).__swc_template_host = this;
163
+ Array.from(element.children).forEach((child: any) => {
164
+ if (child instanceof HTMLElement) {
165
+ setTemplateHost(child);
166
+ }
167
+ });
168
+ };
169
+
170
+ this._nodes.forEach((node: any) => {
171
+ if (node instanceof HTMLElement) {
172
+ setTemplateHost(node as HTMLElement);
173
+ }
174
+ });
175
+
176
+ console.log(`[DEBUG] SwcChoose before insertBefore, portal: ${portal?.tagName}, isConnected: ${(portal as any)?.isConnected}`);
120
177
  portal.insertBefore(processedFragment, this.nextSibling);
178
+ console.log('[DEBUG] SwcChoose after insertBefore');
121
179
  }
122
180
  }
123
181
 
@@ -3,14 +3,20 @@ import { SwcUtils } from '../utils/Utils';
3
3
  import { FunctionUtils, ActionExpression } from '@dooboostore/core';
4
4
  import { ConvertUtils } from '@dooboostore/core-web';
5
5
  import { SwcIfInterface } from '../types';
6
- import { attributeHost } from '../decorators';
6
+ import { attributeHost, getAttributeValue } from '../decorators';
7
7
 
8
8
  class SwcIf extends HTMLTemplateElement implements SwcIfInterface {
9
- @attributeHost('value')
9
+ // id =
10
+ // @attributeHost('value')
10
11
  _value: any;
11
12
 
12
- @attributeHost('replace-wrap')
13
- _replaceWrap: string | null;
13
+ @attributeHost('replace-wrap-start')
14
+ _replaceWrapStart: string | null;
15
+ @attributeHost('replace-wrap-end')
16
+ _replaceWrapEnd: string | null;
17
+
18
+ @attributeHost('skip-if-same')
19
+ _skipIfSame: string | null;
14
20
 
15
21
  private _nodes: Node[] = [];
16
22
 
@@ -23,14 +29,37 @@ class SwcIf extends HTMLTemplateElement implements SwcIfInterface {
23
29
  this.refresh(nv);
24
30
  }
25
31
 
26
- private replaceExpressions(html: string, groupArgs: any, context: any, wrap?: string | null): string {
27
- const config = wrap ? { wrapExpression: wrap } : undefined;
32
+ constructor() {
33
+ super();
34
+ }
35
+
36
+ @changedAttributeHost('value')
37
+ private setValue(value: any, o: string, n: string) {
38
+ this._value = value;
39
+ this.refresh(this._value);
40
+ }
41
+
42
+ // @changedAttributeHost('replace-wrap')
43
+ // private setReplaceWrap(replaceWrapAttr?: string | null) {
44
+ // this.refresh();
45
+ // }
46
+
47
+ private replaceExpressions(html: string, groupArgs: any, context: any, wrap: {start: string, end: string}): string {
48
+ let config: any = undefined;
49
+ if (wrap) {
50
+ config = {
51
+ expression: {
52
+ replace: { start: `${wrap.start}`, end: `${wrap.end}` },
53
+ callReturn: { start: `${wrap.start}=`, end: `${wrap.end}` },
54
+ call: { start: `${wrap.start}@`, end: `${wrap.end}` }
55
+ }
56
+ };
57
+ }
28
58
  const ae = new ActionExpression(html, config);
29
- const filterOptions = wrap ? { type: 'replace' as const, wrap } : 'replace';
30
59
  const win = this.ownerDocument?.defaultView || window;
31
-
60
+
32
61
  // @ts-ignore
33
- for (const expr of ae.getExpressions(filterOptions)) {
62
+ for (const expr of ae.getExpressions('replace')) {
34
63
  try {
35
64
  const value = FunctionUtils.executeReturn({
36
65
  script: ConvertUtils.decodeHtmlEntity(expr.script, win.document),
@@ -45,39 +74,47 @@ class SwcIf extends HTMLTemplateElement implements SwcIfInterface {
45
74
  return ae.getUnprocessedTemplate();
46
75
  }
47
76
 
48
- @changedAttributeHost('value')
49
- private setValue(valueAttr?: string | null) {
50
- this.refresh();
51
- }
77
+ refresh(value?: any) {
78
+ const targetValue = value !== undefined ? value : (getAttributeValue(this, 'value') ?? this._value);
52
79
 
53
- @changedAttributeHost('replace-wrap')
54
- private setReplaceWrap(replaceWrapAttr?: string | null) {
55
- this.refresh();
56
- }
80
+ // skip-if-same 속성이 존재하고(true/빈 문자열), 이전 값과 새로운 값이 완전히 동일하면 렌더링 스킵!
81
+ if (this.hasAttribute('skip-if-same') && this._value === targetValue && this._nodes.length > 0) {
82
+ return;
83
+ }
57
84
 
58
- refresh(value = this._value) {
59
85
  this.cleanup();
86
+ this._value = targetValue;
87
+
60
88
  const portal = this.parentElement;
61
- if (!portal || !value) return;
89
+ if (!portal || !this._value) return;
62
90
 
63
91
  const win = this.ownerDocument?.defaultView || window;
64
92
  const helperSet = SwcUtils.getHelperAndHostSet(win, this);
65
93
 
66
94
  let htmlString = this.innerHTML;
67
- const groupArgs = { ...helperSet, $value: value };
95
+ const groupArgs = { ...helperSet, $value: this._value, $templateHost: this };
68
96
 
69
- htmlString = this.replaceExpressions(htmlString, groupArgs, this, this._replaceWrap);
97
+ htmlString = this.replaceExpressions(htmlString, groupArgs, this, {start:this._replaceWrapStart ?? '{#{', end: this._replaceWrapEnd ?? '}#}'});
70
98
 
71
99
  const result = this.ownerDocument?.createElement('template') || document.createElement('template');
72
100
  result.innerHTML = htmlString;
73
101
  const processedFragment = result.content;
74
102
 
75
103
  this._nodes = Array.from(processedFragment.childNodes);
104
+ this._nodes.forEach((node: any) => {
105
+ if (node instanceof HTMLElement) (node as any).__swc_template_host = this;
106
+ });
107
+
76
108
  portal.insertBefore(processedFragment, this.nextSibling);
77
109
  }
78
110
 
79
111
  private cleanup() {
80
- this._nodes.forEach(n => n.parentElement?.removeChild(n));
112
+ this._nodes.forEach(n => {
113
+ const realNode = (n as any).__upgraded_instance || n;
114
+ if (realNode.parentElement) {
115
+ realNode.parentElement.removeChild(realNode);
116
+ }
117
+ });
81
118
  this._nodes = [];
82
119
  }
83
120
 
@@ -2,46 +2,67 @@ import { changedAttributeHost } from '../decorators/changedAttributeHost';
2
2
  import { SwcUtils } from '../utils/Utils';
3
3
  import { FunctionUtils, ActionExpression } from '@dooboostore/core';
4
4
  import { ConvertUtils } from '@dooboostore/core-web';
5
- import { attributeHost } from '../decorators';
5
+ import { attributeHost, getAttributeValue } from '../decorators';
6
6
 
7
7
  class SwcLoop extends HTMLTemplateElement {
8
- @attributeHost('value')
9
- _value: any[];
8
+ _value: any[] = [];
10
9
 
11
- @attributeHost('replace-wrap')
12
- _replaceWrap: string | null;
10
+ @attributeHost('replace-wrap-start')
11
+ _replaceWrapStart: string | null;
12
+ @attributeHost('replace-wrap-end')
13
+ _replaceWrapEnd: string | null;
13
14
 
14
15
  private _nodeGroups: Node[][] = [];
15
16
 
16
17
  get value(): any[] {
17
18
  return this._value;
18
19
  }
20
+
19
21
  set value(nv: any[]) {
20
- // this._value = Array.isArray(nv) ? [...nv] : [];
22
+ this._value = nv;
21
23
  this.refresh(nv);
22
24
  }
23
25
 
24
- @changedAttributeHost('value')
25
- private setValue() {
26
- this.refresh();
26
+ getValue(index: number) {
27
+ return this._value?.[index];
28
+ }
29
+
30
+ constructor() {
31
+ super();
32
+ // console.log('swc SwcLoop constructor called!');
33
+ // console.log('swc SwcLoop', this.getAttribute('value'));
27
34
  }
28
35
 
29
- @changedAttributeHost('replace-wrap')
30
- private setReplaceWrap() {
31
- this.refresh();
36
+ @changedAttributeHost('value')
37
+ private setValue(value: any, o: string, n: string) {
38
+ this._value = value;
39
+ this.refresh(this._value);
32
40
  }
33
41
 
34
- private replaceExpressions(html: string, groupArgs: any, context: any, wrap?: string | null): string {
35
- const config = wrap ? { wrapExpression: wrap } : undefined;
42
+ // @changedAttributeHost('replace-wrap')
43
+ // private setReplaceWrap(replaceWrapAttr?: string | null) {
44
+ // this.refresh();
45
+ // }
46
+
47
+ private replaceExpressions(html: string, groupArgs: any, context: any, wrap: {start: string, end: string}): string {
48
+ let config: any = undefined;
49
+ config = {
50
+ expression: {
51
+ replace: { start: `${wrap.start}`, end: `${wrap.end}` },
52
+ callReturn: { start: `${wrap.start}=`, end: `${wrap.end}` },
53
+ call: { start: `${wrap.start}@`, end: `${wrap.end}` }
54
+ }
55
+ };
36
56
  const ae = new ActionExpression(html, config);
37
- const filterOptions = wrap ? { type: 'replace' as const, wrap } : 'replace';
38
57
  const win = this.ownerDocument?.defaultView || window;
39
58
 
40
59
  // @ts-ignore
41
- for (const expr of ae.getExpressions(filterOptions)) {
60
+ const expressions = ae.getExpressions('replace');
61
+ for (const expr of expressions) {
62
+ const script = ConvertUtils.decodeHtmlEntity(expr.script, win.document);
42
63
  try {
43
64
  const value = FunctionUtils.executeReturn({
44
- script: ConvertUtils.decodeHtmlEntity(expr.script, win.document),
65
+ script: script,
45
66
  context,
46
67
  args: groupArgs
47
68
  });
@@ -53,35 +74,127 @@ class SwcLoop extends HTMLTemplateElement {
53
74
  return ae.getUnprocessedTemplate();
54
75
  }
55
76
 
56
- refresh(value = this._value) {
57
- this.cleanup();
58
- if (value?.length<=0) return;
59
- const portal = this.parentElement;
60
- if (!portal) return;
61
-
77
+ private createFragmentAndNodes(items: any[], helperSet: any, startIndex: number = 0): { fragment: DocumentFragment; newNodesGroup: Node[][] } {
62
78
  const win = this.ownerDocument?.defaultView || window;
63
- const helperSet = SwcUtils.getHelperAndHostSet(win, this);
64
79
  const fragment = win.document.createDocumentFragment();
80
+ const newNodesGroup: Node[][] = [];
65
81
 
66
- value.forEach((item, index) => {
82
+ items.forEach((item, i) => {
83
+ const index = startIndex + i;
67
84
  let htmlString = this.innerHTML;
68
85
  const groupArgs = { ...helperSet, $item: item, $index: index, $value: this._value };
69
86
 
70
- htmlString = this.replaceExpressions(htmlString, groupArgs, this, this._replaceWrap);
87
+ htmlString = this.replaceExpressions(htmlString, groupArgs, this, {start:this._replaceWrapStart ?? '{#{', end: this._replaceWrapEnd ?? '}#}'});
71
88
 
72
89
  const temp = this.ownerDocument?.createElement('template') || document.createElement('template');
73
90
  temp.innerHTML = htmlString;
74
91
 
75
92
  const content = temp.content;
76
93
  const nodes = Array.from(content.childNodes);
77
- this._nodeGroups.push(nodes);
94
+
95
+ const setTemplateHost = (element: HTMLElement) => {
96
+ (element as any).__swc_template_host = this;
97
+ Array.from(element.children).forEach((child: any) => {
98
+ if (child instanceof HTMLElement) {
99
+ setTemplateHost(child);
100
+ }
101
+ });
102
+ };
103
+
104
+ nodes.forEach((node: any) => {
105
+ if (node instanceof HTMLElement) {
106
+ setTemplateHost(node as HTMLElement);
107
+ }
108
+ });
109
+ newNodesGroup.push(nodes);
78
110
  fragment.appendChild(content);
79
111
  });
80
112
 
113
+ return { fragment, newNodesGroup };
114
+ }
115
+
116
+ appendItem(...items: any[]) {
117
+ if (!items || items.length <= 0) return;
118
+ const portal = SwcUtils.getParentContainer(this);
119
+ if (!portal) return;
120
+
121
+ const win = this.ownerDocument?.defaultView || window;
122
+ const helperSet = SwcUtils.getHelperAndHostSet(win, this);
123
+
124
+ const startIndex = this._value ? this._value.length : 0;
125
+
126
+ // 내부 상태(value) 업데이트 (기존 배열에 새 아이템 추가)
127
+ if (this._value !== items) {
128
+ this._value = [...(this._value || []), ...items];
129
+ }
130
+
131
+ const { fragment, newNodesGroup } = this.createFragmentAndNodes(items, helperSet, startIndex);
132
+ this._nodeGroups.push(...newNodesGroup);
133
+
134
+ console.log('뭐야 씨발');
135
+ // append 위치 계산: 기존에 그려진 마지막 노드 뒤에 삽입, 없으면 템플릿 바로 뒤에 삽입
136
+ let insertReference: Node | null = this.nextSibling;
137
+ if (this._nodeGroups.length > items.length) {
138
+ const lastExistingGroup = this._nodeGroups[this._nodeGroups.length - items.length - 1];
139
+ if (lastExistingGroup && lastExistingGroup.length > 0) {
140
+ const lastNode = lastExistingGroup[lastExistingGroup.length - 1];
141
+ insertReference = lastNode.nextSibling;
142
+ }
143
+ }
144
+
145
+ portal.insertBefore(fragment, insertReference);
146
+ }
147
+
148
+ async prependItem(...items: any[]) {
149
+ if (!items || items.length <= 0) return;
150
+ const portal = SwcUtils.getParentContainer(this);
151
+ if (!portal) return;
152
+
153
+ const win = this.ownerDocument?.defaultView || window;
154
+ const helperSet = SwcUtils.getHelperAndHostSet(win, this);
155
+
156
+ // 내부 상태(value) 업데이트 (기존 배열의 앞쪽에 새 아이템 추가)
157
+ if (this._value !== items) {
158
+ this._value = [...items, ...(this._value || [])];
159
+ }
160
+
161
+ const { fragment, newNodesGroup } = this.createFragmentAndNodes(items, helperSet, 0);
162
+
163
+ // 기존의 nodeGroups 배열 맨 앞에 새로운 그룹들을 추가
164
+ this._nodeGroups = [...newNodesGroup, ...this._nodeGroups];
165
+
166
+ // prepend 위치: 이 템플릿(SwcLoop) 노드의 바로 다음 위치 (가장 위)
81
167
  portal.insertBefore(fragment, this.nextSibling);
82
168
  }
83
169
 
170
+ refresh(value?: any) {
171
+ // 1. 그릴 대상 확정
172
+ const targetValue = value !== undefined ? value : (getAttributeValue(this, 'value') ?? this._value ?? []);
173
+
174
+ // 2. 화면 정리
175
+ this.cleanup();
176
+
177
+ // 3. 상태 완전 교체 (이어붙이지 않음!)
178
+ this._value = targetValue;
179
+
180
+ if (!this._value || this._value.length <= 0) return;
181
+
182
+ // 4. 화면 다시 그리기 (appendItem을 쓰지 않고 바로 그리기)
183
+ const win = this.ownerDocument?.defaultView || window;
184
+ const helperSet = SwcUtils.getHelperAndHostSet(win, this);
185
+ helperSet.$templateHost = this;
186
+ const { fragment, newNodesGroup } = this.createFragmentAndNodes(this._value, helperSet, 0);
187
+
188
+ this._nodeGroups = newNodesGroup;
189
+ const portal = SwcUtils.getParentContainer(this);
190
+ if (portal) {
191
+ // console.log('append????!~~~~~!?',fragment)
192
+ portal.insertBefore(fragment, this.nextSibling);
193
+ }
194
+ }
195
+
84
196
  private cleanup() {
197
+ console.log('누가 cleanup');
85
198
  this._nodeGroups.forEach(nodes => nodes.forEach(n => n.parentElement?.removeChild(n)));
86
199
  this._nodeGroups = [];
87
200
  }