@dooboostore/simple-web-component 1.0.34 → 1.0.36

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 (170) hide show
  1. package/README.md +44 -51
  2. package/dist/cjs/decorators/addEventListener.js +12 -4
  3. package/dist/cjs/decorators/addEventListener.js.map +2 -2
  4. package/dist/cjs/decorators/applyAttribute.js +19 -18
  5. package/dist/cjs/decorators/applyAttribute.js.map +2 -2
  6. package/dist/cjs/decorators/applyClass.js +23 -22
  7. package/dist/cjs/decorators/applyClass.js.map +2 -2
  8. package/dist/cjs/decorators/applyNode.js +22 -17
  9. package/dist/cjs/decorators/applyNode.js.map +2 -2
  10. package/dist/cjs/decorators/applyStyle.js +20 -19
  11. package/dist/cjs/decorators/applyStyle.js.map +2 -2
  12. package/dist/cjs/decorators/attribute.js +17 -16
  13. package/dist/cjs/decorators/attribute.js.map +2 -2
  14. package/dist/cjs/decorators/{changedAttributeHost.js → changedAttributeThis.js} +6 -6
  15. package/dist/cjs/decorators/changedAttributeThis.js.map +7 -0
  16. package/dist/cjs/decorators/elementDefine.js +234 -188
  17. package/dist/cjs/decorators/elementDefine.js.map +2 -2
  18. package/dist/cjs/decorators/emitCustomEvent.js +24 -16
  19. package/dist/cjs/decorators/emitCustomEvent.js.map +2 -2
  20. package/dist/cjs/decorators/index.js +1 -1
  21. package/dist/cjs/decorators/index.js.map +1 -1
  22. package/dist/cjs/decorators/lifecycles.js +15 -17
  23. package/dist/cjs/decorators/lifecycles.js.map +2 -2
  24. package/dist/cjs/decorators/query.js +38 -60
  25. package/dist/cjs/decorators/query.js.map +2 -2
  26. package/dist/cjs/decorators/queryAll.js +31 -63
  27. package/dist/cjs/decorators/queryAll.js.map +2 -2
  28. package/dist/cjs/decorators/setProperty.js +13 -12
  29. package/dist/cjs/decorators/setProperty.js.map +2 -2
  30. package/dist/cjs/decorators/subscribeSwcAppRouteChangeWhileConnected.js +2 -2
  31. package/dist/cjs/decorators/subscribeSwcAppRouteChangeWhileConnected.js.map +2 -2
  32. package/dist/cjs/elements/SwcAppEngine.js +8 -2
  33. package/dist/cjs/elements/SwcAppEngine.js.map +2 -2
  34. package/dist/cjs/elements/SwcAppMixin.js +120 -65
  35. package/dist/cjs/elements/SwcAppMixin.js.map +2 -2
  36. package/dist/cjs/elements/SwcAsync.js +31 -21
  37. package/dist/cjs/elements/SwcAsync.js.map +2 -2
  38. package/dist/cjs/elements/SwcChoose.js +53 -34
  39. package/dist/cjs/elements/SwcChoose.js.map +2 -2
  40. package/dist/cjs/elements/SwcIf.js +54 -32
  41. package/dist/cjs/elements/SwcIf.js.map +2 -2
  42. package/dist/cjs/elements/SwcLoop.js +111 -37
  43. package/dist/cjs/elements/SwcLoop.js.map +2 -2
  44. package/dist/cjs/elements/register.js +249 -122
  45. package/dist/cjs/elements/register.js.map +2 -2
  46. package/dist/cjs/router/ElementRouter.js +4 -2
  47. package/dist/cjs/router/ElementRouter.js.map +1 -1
  48. package/dist/cjs/types.js.map +1 -1
  49. package/dist/cjs/utils/Utils.js +31 -17
  50. package/dist/cjs/utils/Utils.js.map +2 -2
  51. package/dist/esm/decorators/addEventListener.js +12 -4
  52. package/dist/esm/decorators/addEventListener.js.map +2 -2
  53. package/dist/esm/decorators/applyAttribute.js +19 -18
  54. package/dist/esm/decorators/applyAttribute.js.map +2 -2
  55. package/dist/esm/decorators/applyClass.js +23 -22
  56. package/dist/esm/decorators/applyClass.js.map +2 -2
  57. package/dist/esm/decorators/applyNode.js +22 -17
  58. package/dist/esm/decorators/applyNode.js.map +2 -2
  59. package/dist/esm/decorators/applyStyle.js +20 -19
  60. package/dist/esm/decorators/applyStyle.js.map +2 -2
  61. package/dist/esm/decorators/attribute.js +17 -16
  62. package/dist/esm/decorators/attribute.js.map +2 -2
  63. package/dist/esm/decorators/{changedAttributeHost.js → changedAttributeThis.js} +3 -3
  64. package/dist/esm/decorators/changedAttributeThis.js.map +7 -0
  65. package/dist/esm/decorators/elementDefine.js +233 -187
  66. package/dist/esm/decorators/elementDefine.js.map +2 -2
  67. package/dist/esm/decorators/emitCustomEvent.js +24 -16
  68. package/dist/esm/decorators/emitCustomEvent.js.map +2 -2
  69. package/dist/esm/decorators/index.js +1 -1
  70. package/dist/esm/decorators/index.js.map +1 -1
  71. package/dist/esm/decorators/lifecycles.js +15 -17
  72. package/dist/esm/decorators/lifecycles.js.map +2 -2
  73. package/dist/esm/decorators/query.js +38 -60
  74. package/dist/esm/decorators/query.js.map +2 -2
  75. package/dist/esm/decorators/queryAll.js +31 -63
  76. package/dist/esm/decorators/queryAll.js.map +2 -2
  77. package/dist/esm/decorators/setProperty.js +13 -12
  78. package/dist/esm/decorators/setProperty.js.map +2 -2
  79. package/dist/esm/decorators/subscribeSwcAppRouteChangeWhileConnected.js +2 -2
  80. package/dist/esm/decorators/subscribeSwcAppRouteChangeWhileConnected.js.map +2 -2
  81. package/dist/esm/elements/SwcAppEngine.js +9 -2
  82. package/dist/esm/elements/SwcAppEngine.js.map +2 -2
  83. package/dist/esm/elements/SwcAppMixin.js +122 -66
  84. package/dist/esm/elements/SwcAppMixin.js.map +2 -2
  85. package/dist/esm/elements/SwcAsync.js +32 -22
  86. package/dist/esm/elements/SwcAsync.js.map +2 -2
  87. package/dist/esm/elements/SwcChoose.js +54 -35
  88. package/dist/esm/elements/SwcChoose.js.map +2 -2
  89. package/dist/esm/elements/SwcIf.js +55 -33
  90. package/dist/esm/elements/SwcIf.js.map +2 -2
  91. package/dist/esm/elements/SwcLoop.js +112 -38
  92. package/dist/esm/elements/SwcLoop.js.map +2 -2
  93. package/dist/esm/elements/register.js +250 -123
  94. package/dist/esm/elements/register.js.map +2 -2
  95. package/dist/esm/router/ElementRouter.js +5 -2
  96. package/dist/esm/router/ElementRouter.js.map +1 -1
  97. package/dist/esm/types.js.map +1 -1
  98. package/dist/esm/utils/Utils.js +31 -17
  99. package/dist/esm/utils/Utils.js.map +2 -2
  100. package/dist/esm-bundle/dooboostore-simple-web-component.esm.js +563 -557
  101. package/dist/esm-bundle/dooboostore-simple-web-component.esm.js.map +4 -4
  102. package/dist/types/decorators/addEventListener.d.ts +5 -7
  103. package/dist/types/decorators/addEventListener.d.ts.map +1 -1
  104. package/dist/types/decorators/applyAttribute.d.ts +4 -4
  105. package/dist/types/decorators/applyAttribute.d.ts.map +1 -1
  106. package/dist/types/decorators/applyClass.d.ts +6 -6
  107. package/dist/types/decorators/applyClass.d.ts.map +1 -1
  108. package/dist/types/decorators/applyNode.d.ts +7 -6
  109. package/dist/types/decorators/applyNode.d.ts.map +1 -1
  110. package/dist/types/decorators/applyStyle.d.ts +4 -4
  111. package/dist/types/decorators/applyStyle.d.ts.map +1 -1
  112. package/dist/types/decorators/attribute.d.ts +5 -5
  113. package/dist/types/decorators/attribute.d.ts.map +1 -1
  114. package/dist/types/decorators/changedAttributeThis.d.ts +18 -0
  115. package/dist/types/decorators/changedAttributeThis.d.ts.map +1 -0
  116. package/dist/types/decorators/elementDefine.d.ts.map +1 -1
  117. package/dist/types/decorators/emitCustomEvent.d.ts +11 -12
  118. package/dist/types/decorators/emitCustomEvent.d.ts.map +1 -1
  119. package/dist/types/decorators/index.d.ts +1 -1
  120. package/dist/types/decorators/lifecycles.d.ts +10 -7
  121. package/dist/types/decorators/lifecycles.d.ts.map +1 -1
  122. package/dist/types/decorators/query.d.ts +17 -63
  123. package/dist/types/decorators/query.d.ts.map +1 -1
  124. package/dist/types/decorators/queryAll.d.ts +6 -63
  125. package/dist/types/decorators/queryAll.d.ts.map +1 -1
  126. package/dist/types/decorators/setProperty.d.ts +1 -1
  127. package/dist/types/decorators/setProperty.d.ts.map +1 -1
  128. package/dist/types/decorators/subscribeSwcAppRouteChangeWhileConnected.d.ts +4 -5
  129. package/dist/types/decorators/subscribeSwcAppRouteChangeWhileConnected.d.ts.map +1 -1
  130. package/dist/types/elements/SwcAppEngine.d.ts +4 -1
  131. package/dist/types/elements/SwcAppEngine.d.ts.map +1 -1
  132. package/dist/types/elements/SwcAppMixin.d.ts +11 -8
  133. package/dist/types/elements/SwcAppMixin.d.ts.map +1 -1
  134. package/dist/types/elements/register.d.ts.map +1 -1
  135. package/dist/types/types.d.ts +3 -2
  136. package/dist/types/types.d.ts.map +1 -1
  137. package/dist/types/utils/Utils.d.ts +11 -8
  138. package/dist/types/utils/Utils.d.ts.map +1 -1
  139. package/dist/umd-bundle/dooboostore-simple-web-component.umd.js +563 -557
  140. package/dist/umd-bundle/dooboostore-simple-web-component.umd.js.map +4 -4
  141. package/package.json +3 -3
  142. package/src/decorators/addEventListener.ts +11 -9
  143. package/src/decorators/applyAttribute.ts +16 -15
  144. package/src/decorators/applyClass.ts +18 -17
  145. package/src/decorators/applyNode.ts +34 -21
  146. package/src/decorators/applyStyle.ts +17 -16
  147. package/src/decorators/attribute.ts +21 -20
  148. package/src/decorators/{changedAttributeHost.ts → changedAttributeThis.ts} +13 -14
  149. package/src/decorators/elementDefine.ts +402 -358
  150. package/src/decorators/emitCustomEvent.ts +40 -31
  151. package/src/decorators/index.ts +1 -1
  152. package/src/decorators/lifecycles.ts +11 -10
  153. package/src/decorators/query.ts +51 -124
  154. package/src/decorators/queryAll.ts +30 -127
  155. package/src/decorators/setProperty.ts +12 -11
  156. package/src/decorators/subscribeSwcAppRouteChangeWhileConnected.ts +10 -10
  157. package/src/elements/SwcAppEngine.ts +10 -3
  158. package/src/elements/SwcAppMixin.ts +177 -97
  159. package/src/elements/SwcAsync.ts +32 -21
  160. package/src/elements/SwcChoose.ts +74 -37
  161. package/src/elements/SwcIf.ts +60 -23
  162. package/src/elements/SwcLoop.ts +141 -28
  163. package/src/elements/register.ts +300 -102
  164. package/src/types.ts +6 -5
  165. package/src/utils/Utils.ts +40 -23
  166. package/tsconfig.json +2 -2
  167. package/dist/cjs/decorators/changedAttributeHost.js.map +0 -7
  168. package/dist/esm/decorators/changedAttributeHost.js.map +0 -7
  169. package/dist/types/decorators/changedAttributeHost.d.ts +0 -19
  170. package/dist/types/decorators/changedAttributeHost.d.ts.map +0 -1
@@ -1,18 +1,17 @@
1
- import { changedAttributeHost } from '../decorators/changedAttributeHost';
1
+ import { changedAttributeThis } from '../decorators/changedAttributeThis';
2
2
  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, getAttributeValue, getElementConfig } from '../decorators';
6
+ import { attributeThis, getAttributeValue, getElementConfig } from '../decorators';
7
7
 
8
8
  class SwcChoose extends HTMLTemplateElement implements SwcChooseInterface {
9
- // @attributeHost('value')
10
- // fetchValue: any;
11
-
12
9
  _value: any;
13
10
 
14
- @attributeHost('replace-wrap')
15
- _replaceWrap: string | null;
11
+ @attributeThis('replace-wrap-start')
12
+ _replaceWrapStart: string | null;
13
+ @attributeThis('replace-wrap-end')
14
+ _replaceWrapEnd: string | null;
16
15
 
17
16
  private _nodes: Node[] = [];
18
17
  private _previousTemplate: HTMLTemplateElement | null = null;
@@ -40,26 +39,34 @@ class SwcChoose extends HTMLTemplateElement implements SwcChooseInterface {
40
39
  // console.log('-----------------choooooooose attr origin', na, o, n);
41
40
  }
42
41
 
43
- @changedAttributeHost('value')
42
+ @changedAttributeThis('value')
44
43
  private setValue(value: any, o: string, n: string) {
45
- // console.log('-----------------choooooooose attr setValue', value, o, n);
44
+ console.log('chooooooose value', value)
46
45
  this._value = value;
47
46
  this.refresh(this._value);
48
47
  }
49
48
 
50
- @changedAttributeHost('replace-wrap')
51
- private setReplaceWrap(replaceWrapAttr?: string | null) {
52
- this.refresh();
53
- }
49
+ // @changedAttributeThis('replace-wrap')
50
+ // private setReplaceWrap(replaceWrapAttr?: string | null) {
51
+ // this.refresh();
52
+ // }
54
53
 
55
- private replaceExpressions(html: string, groupArgs: any, context: any, wrap?: string | null): string {
56
- const config = wrap ? { wrapExpression: wrap } : undefined;
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
+ }
57
65
  const ae = new ActionExpression(html, config);
58
- const filterOptions = wrap ? { type: 'replace' as const, wrap } : 'replace';
59
66
  const win = this.ownerDocument?.defaultView || window;
60
67
 
61
68
  // @ts-ignore
62
- for (const expr of ae.getExpressions(filterOptions)) {
69
+ for (const expr of ae.getExpressions('replace')) {
63
70
  try {
64
71
  const value = FunctionUtils.executeReturn({
65
72
  script: ConvertUtils.decodeHtmlEntity(expr.script, win.document),
@@ -76,37 +83,48 @@ class SwcChoose extends HTMLTemplateElement implements SwcChooseInterface {
76
83
 
77
84
  refresh(value?: any) {
78
85
  value = getAttributeValue(this, 'value') ?? this._value;
79
- // console.log('chooooooooo refresh->', value);
80
- const portal = this.parentElement;
81
- if (!portal) return;
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
+ }
82
92
 
83
93
  const win = (getElementConfig(this).window ?? this.ownerDocument?.defaultView) || window;
84
94
  const helperSet = SwcUtils.getHelperAndHostSet(win, this);
85
- const groupArgs = { ...helperSet, $value: value };
95
+ const groupArgs = { ...helperSet, $value: value, $templateHost: this };
86
96
 
87
97
  const templates = Array.from(this.content.querySelectorAll('template'));
88
98
  let selected: HTMLTemplateElement | null = null;
89
99
 
90
100
  for (const t of templates) {
91
101
  if (t.getAttribute('is') === 'swc-when') {
92
- const attrVal = t.getAttribute('value') || '';
102
+ const attrVal = t.getAttribute('test') || '';
93
103
  let matches = false;
94
104
 
105
+
106
+
107
+ const result = FunctionUtils.executeReturn({
108
+ script: attrVal,
109
+ context: t,
110
+ args: groupArgs
111
+ });
112
+ matches = !!result;
95
113
  // Use ActionExpression to parse and extract script from value attribute
96
- const ae = new ActionExpression(attrVal);
97
- const expr = ae.getFirstExpression('replace');
98
-
99
- if (expr) {
100
- const decodedScript = ConvertUtils.decodeHtmlEntity(expr.script, win.document);
101
- const result = FunctionUtils.executeReturn({
102
- script: decodedScript,
103
- context: t,
104
- args: groupArgs
105
- });
106
- matches = !!result;
107
- } else {
108
- matches = String(attrVal) === String(value);
109
- }
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
+ // }
110
128
 
111
129
  if (matches) {
112
130
  selected = t;
@@ -131,14 +149,33 @@ class SwcChoose extends HTMLTemplateElement implements SwcChooseInterface {
131
149
 
132
150
  if (target) {
133
151
  let htmlString = target.innerHTML;
134
- htmlString = this.replaceExpressions(htmlString, groupArgs, this, this._replaceWrap);
152
+ htmlString = this.replaceExpressions(htmlString, groupArgs, this, {start:this._replaceWrapStart ?? '{#{', end: this._replaceWrapEnd ?? '}#}'});
135
153
 
136
154
  const result = this.ownerDocument?.createElement('template') || document.createElement('template');
137
155
  result.innerHTML = htmlString;
138
156
  const processedFragment = result.content;
139
157
 
158
+ // console.log('[DEBUG] SwcChoose processedFragment children:', processedFragment._childNodesInternal?.length);
140
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}`);
141
177
  portal.insertBefore(processedFragment, this.nextSibling);
178
+ console.log('[DEBUG] SwcChoose after insertBefore');
142
179
  }
143
180
  }
144
181
 
@@ -1,16 +1,22 @@
1
- import { changedAttributeHost } from '../decorators/changedAttributeHost';
1
+ import { changedAttributeThis } from '../decorators/changedAttributeThis';
2
2
  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 { attributeThis, getAttributeValue } from '../decorators';
7
7
 
8
8
  class SwcIf extends HTMLTemplateElement implements SwcIfInterface {
9
- @attributeHost('value')
9
+ // id =
10
+ // @attributeThis('value')
10
11
  _value: any;
11
12
 
12
- @attributeHost('replace-wrap')
13
- _replaceWrap: string | null;
13
+ @attributeThis('replace-wrap-start')
14
+ _replaceWrapStart: string | null;
15
+ @attributeThis('replace-wrap-end')
16
+ _replaceWrapEnd: string | null;
17
+
18
+ @attributeThis('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
+ @changedAttributeThis('value')
37
+ private setValue(value: any, o: string, n: string) {
38
+ this._value = value;
39
+ this.refresh(this._value);
40
+ }
41
+
42
+ // @changedAttributeThis('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
 
@@ -1,47 +1,68 @@
1
- import { changedAttributeHost } from '../decorators/changedAttributeHost';
1
+ import { changedAttributeThis } from '../decorators/changedAttributeThis';
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 { attributeThis, 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
+ @attributeThis('replace-wrap-start')
11
+ _replaceWrapStart: string | null;
12
+ @attributeThis('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
+ @changedAttributeThis('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
+ // @changedAttributeThis('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
  }