@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
@@ -1,4 +1,4 @@
1
- import { elementDefine, attributeHost, changedAttributeHost } from '../decorators';
1
+ import { getElementConfig, elementDefine, attributeHost, changedAttributeHost, getAttributeValue } from '../decorators';
2
2
  import { SwcAppMixin } from './SwcAppMixin';
3
3
  import { SwcUtils } from '../utils/Utils';
4
4
  import { FunctionUtils,ActionExpression } from '@dooboostore/core';
@@ -148,11 +148,17 @@ export const registerAllElements = (w: any): Record<string, Constructor<HTMLElem
148
148
 
149
149
  @elementDefine('swc-if', { extends: 'template', window: w })
150
150
  class SwcIf extends HTMLTemplateElement {
151
- @attributeHost('value')
151
+ // id =
152
+ // @attributeHost('value')
152
153
  _value: any;
153
154
 
154
- @attributeHost('replace-wrap')
155
- _replaceWrap: string | null;
155
+ @attributeHost('replace-wrap-start')
156
+ _replaceWrapStart: string | null;
157
+ @attributeHost('replace-wrap-end')
158
+ _replaceWrapEnd: string | null;
159
+
160
+ @attributeHost('skip-if-same')
161
+ _skipIfSame: string | null;
156
162
 
157
163
  private _nodes: Node[] = [];
158
164
 
@@ -165,14 +171,37 @@ export const registerAllElements = (w: any): Record<string, Constructor<HTMLElem
165
171
  this.refresh(nv);
166
172
  }
167
173
 
168
- private replaceExpressions(html: string, groupArgs: any, context: any, wrap?: string | null): string {
169
- const config = wrap ? { wrapExpression: wrap } : undefined;
174
+ constructor() {
175
+ super();
176
+ }
177
+
178
+ @changedAttributeHost('value')
179
+ private setValue(value: any, o: string, n: string) {
180
+ this._value = value;
181
+ this.refresh(this._value);
182
+ }
183
+
184
+ // @changedAttributeHost('replace-wrap')
185
+ // private setReplaceWrap(replaceWrapAttr?: string | null) {
186
+ // this.refresh();
187
+ // }
188
+
189
+ private replaceExpressions(html: string, groupArgs: any, context: any, wrap: {start: string, end: string}): string {
190
+ let config: any = undefined;
191
+ if (wrap) {
192
+ config = {
193
+ expression: {
194
+ replace: { start: `${wrap.start}`, end: `${wrap.end}` },
195
+ callReturn: { start: `${wrap.start}=`, end: `${wrap.end}` },
196
+ call: { start: `${wrap.start}@`, end: `${wrap.end}` }
197
+ }
198
+ };
199
+ }
170
200
  const ae = new ActionExpression(html, config);
171
- const filterOptions = wrap ? { type: 'replace' as const, wrap } : 'replace';
172
201
  const win = this.ownerDocument?.defaultView || window;
173
-
202
+
174
203
  // @ts-ignore
175
- for (const expr of ae.getExpressions(filterOptions)) {
204
+ for (const expr of ae.getExpressions('replace')) {
176
205
  try {
177
206
  const value = FunctionUtils.executeReturn({
178
207
  script: ConvertUtils.decodeHtmlEntity(expr.script, win.document),
@@ -187,39 +216,47 @@ export const registerAllElements = (w: any): Record<string, Constructor<HTMLElem
187
216
  return ae.getUnprocessedTemplate();
188
217
  }
189
218
 
190
- @changedAttributeHost('value')
191
- private setValue(valueAttr?: string | null) {
192
- this.refresh();
193
- }
219
+ refresh(value?: any) {
220
+ const targetValue = value !== undefined ? value : (getAttributeValue(this, 'value') ?? this._value);
194
221
 
195
- @changedAttributeHost('replace-wrap')
196
- private setReplaceWrap(replaceWrapAttr?: string | null) {
197
- this.refresh();
198
- }
222
+ // skip-if-same 속성이 존재하고(true/빈 문자열), 이전 값과 새로운 값이 완전히 동일하면 렌더링 스킵!
223
+ if (this.hasAttribute('skip-if-same') && this._value === targetValue && this._nodes.length > 0) {
224
+ return;
225
+ }
199
226
 
200
- refresh(value = this._value) {
201
227
  this.cleanup();
228
+ this._value = targetValue;
229
+
202
230
  const portal = this.parentElement;
203
- if (!portal || !value) return;
231
+ if (!portal || !this._value) return;
204
232
 
205
233
  const win = this.ownerDocument?.defaultView || window;
206
234
  const helperSet = SwcUtils.getHelperAndHostSet(win, this);
207
235
 
208
236
  let htmlString = this.innerHTML;
209
- const groupArgs = { ...helperSet, $value: value };
237
+ const groupArgs = { ...helperSet, $value: this._value, $templateHost: this };
210
238
 
211
- htmlString = this.replaceExpressions(htmlString, groupArgs, this, this._replaceWrap);
239
+ htmlString = this.replaceExpressions(htmlString, groupArgs, this, {start:this._replaceWrapStart ?? '{#{', end: this._replaceWrapEnd ?? '}#}'});
212
240
 
213
241
  const result = this.ownerDocument?.createElement('template') || document.createElement('template');
214
242
  result.innerHTML = htmlString;
215
243
  const processedFragment = result.content;
216
244
 
217
245
  this._nodes = Array.from(processedFragment.childNodes);
246
+ this._nodes.forEach((node: any) => {
247
+ if (node instanceof HTMLElement) (node as any).__swc_template_host = this;
248
+ });
249
+
218
250
  portal.insertBefore(processedFragment, this.nextSibling);
219
251
  }
220
252
 
221
253
  private cleanup() {
222
- this._nodes.forEach(n => n.parentElement?.removeChild(n));
254
+ this._nodes.forEach(n => {
255
+ const realNode = (n as any).__upgraded_instance || n;
256
+ if (realNode.parentElement) {
257
+ realNode.parentElement.removeChild(realNode);
258
+ }
259
+ });
223
260
  this._nodes = [];
224
261
  }
225
262
 
@@ -230,43 +267,64 @@ export const registerAllElements = (w: any): Record<string, Constructor<HTMLElem
230
267
 
231
268
  @elementDefine('swc-loop', { extends: 'template', window: w })
232
269
  class SwcLoop extends HTMLTemplateElement {
233
- @attributeHost('value')
234
- _value: any[];
270
+ _value: any[] = [];
235
271
 
236
- @attributeHost('replace-wrap')
237
- _replaceWrap: string | null;
272
+ @attributeHost('replace-wrap-start')
273
+ _replaceWrapStart: string | null;
274
+ @attributeHost('replace-wrap-end')
275
+ _replaceWrapEnd: string | null;
238
276
 
239
277
  private _nodeGroups: Node[][] = [];
240
278
 
241
279
  get value(): any[] {
242
280
  return this._value;
243
281
  }
282
+
244
283
  set value(nv: any[]) {
245
- // this._value = Array.isArray(nv) ? [...nv] : [];
284
+ this._value = nv;
246
285
  this.refresh(nv);
247
286
  }
248
287
 
249
- @changedAttributeHost('value')
250
- private setValue() {
251
- this.refresh();
288
+ getValue(index: number) {
289
+ return this._value?.[index];
252
290
  }
253
291
 
254
- @changedAttributeHost('replace-wrap')
255
- private setReplaceWrap() {
256
- this.refresh();
292
+ constructor() {
293
+ super();
294
+ // console.log('swc SwcLoop constructor called!');
295
+ // console.log('swc SwcLoop', this.getAttribute('value'));
257
296
  }
258
297
 
259
- private replaceExpressions(html: string, groupArgs: any, context: any, wrap?: string | null): string {
260
- const config = wrap ? { wrapExpression: wrap } : undefined;
298
+ @changedAttributeHost('value')
299
+ private setValue(value: any, o: string, n: string) {
300
+ this._value = value;
301
+ this.refresh(this._value);
302
+ }
303
+
304
+ // @changedAttributeHost('replace-wrap')
305
+ // private setReplaceWrap(replaceWrapAttr?: string | null) {
306
+ // this.refresh();
307
+ // }
308
+
309
+ private replaceExpressions(html: string, groupArgs: any, context: any, wrap: {start: string, end: string}): string {
310
+ let config: any = undefined;
311
+ config = {
312
+ expression: {
313
+ replace: { start: `${wrap.start}`, end: `${wrap.end}` },
314
+ callReturn: { start: `${wrap.start}=`, end: `${wrap.end}` },
315
+ call: { start: `${wrap.start}@`, end: `${wrap.end}` }
316
+ }
317
+ };
261
318
  const ae = new ActionExpression(html, config);
262
- const filterOptions = wrap ? { type: 'replace' as const, wrap } : 'replace';
263
319
  const win = this.ownerDocument?.defaultView || window;
264
320
 
265
321
  // @ts-ignore
266
- for (const expr of ae.getExpressions(filterOptions)) {
322
+ const expressions = ae.getExpressions('replace');
323
+ for (const expr of expressions) {
324
+ const script = ConvertUtils.decodeHtmlEntity(expr.script, win.document);
267
325
  try {
268
326
  const value = FunctionUtils.executeReturn({
269
- script: ConvertUtils.decodeHtmlEntity(expr.script, win.document),
327
+ script: script,
270
328
  context,
271
329
  args: groupArgs
272
330
  });
@@ -278,35 +336,127 @@ export const registerAllElements = (w: any): Record<string, Constructor<HTMLElem
278
336
  return ae.getUnprocessedTemplate();
279
337
  }
280
338
 
281
- refresh(value = this._value) {
282
- this.cleanup();
283
- if (value?.length<=0) return;
284
- const portal = this.parentElement;
285
- if (!portal) return;
286
-
339
+ private createFragmentAndNodes(items: any[], helperSet: any, startIndex: number = 0): { fragment: DocumentFragment; newNodesGroup: Node[][] } {
287
340
  const win = this.ownerDocument?.defaultView || window;
288
- const helperSet = SwcUtils.getHelperAndHostSet(win, this);
289
341
  const fragment = win.document.createDocumentFragment();
342
+ const newNodesGroup: Node[][] = [];
290
343
 
291
- value.forEach((item, index) => {
344
+ items.forEach((item, i) => {
345
+ const index = startIndex + i;
292
346
  let htmlString = this.innerHTML;
293
347
  const groupArgs = { ...helperSet, $item: item, $index: index, $value: this._value };
294
348
 
295
- htmlString = this.replaceExpressions(htmlString, groupArgs, this, this._replaceWrap);
349
+ htmlString = this.replaceExpressions(htmlString, groupArgs, this, {start:this._replaceWrapStart ?? '{#{', end: this._replaceWrapEnd ?? '}#}'});
296
350
 
297
351
  const temp = this.ownerDocument?.createElement('template') || document.createElement('template');
298
352
  temp.innerHTML = htmlString;
299
353
 
300
354
  const content = temp.content;
301
355
  const nodes = Array.from(content.childNodes);
302
- this._nodeGroups.push(nodes);
356
+
357
+ const setTemplateHost = (element: HTMLElement) => {
358
+ (element as any).__swc_template_host = this;
359
+ Array.from(element.children).forEach((child: any) => {
360
+ if (child instanceof HTMLElement) {
361
+ setTemplateHost(child);
362
+ }
363
+ });
364
+ };
365
+
366
+ nodes.forEach((node: any) => {
367
+ if (node instanceof HTMLElement) {
368
+ setTemplateHost(node as HTMLElement);
369
+ }
370
+ });
371
+ newNodesGroup.push(nodes);
303
372
  fragment.appendChild(content);
304
373
  });
305
374
 
375
+ return { fragment, newNodesGroup };
376
+ }
377
+
378
+ appendItem(...items: any[]) {
379
+ if (!items || items.length <= 0) return;
380
+ const portal = SwcUtils.getParentContainer(this);
381
+ if (!portal) return;
382
+
383
+ const win = this.ownerDocument?.defaultView || window;
384
+ const helperSet = SwcUtils.getHelperAndHostSet(win, this);
385
+
386
+ const startIndex = this._value ? this._value.length : 0;
387
+
388
+ // 내부 상태(value) 업데이트 (기존 배열에 새 아이템 추가)
389
+ if (this._value !== items) {
390
+ this._value = [...(this._value || []), ...items];
391
+ }
392
+
393
+ const { fragment, newNodesGroup } = this.createFragmentAndNodes(items, helperSet, startIndex);
394
+ this._nodeGroups.push(...newNodesGroup);
395
+
396
+ console.log('뭐야 씨발');
397
+ // append 위치 계산: 기존에 그려진 마지막 노드 뒤에 삽입, 없으면 템플릿 바로 뒤에 삽입
398
+ let insertReference: Node | null = this.nextSibling;
399
+ if (this._nodeGroups.length > items.length) {
400
+ const lastExistingGroup = this._nodeGroups[this._nodeGroups.length - items.length - 1];
401
+ if (lastExistingGroup && lastExistingGroup.length > 0) {
402
+ const lastNode = lastExistingGroup[lastExistingGroup.length - 1];
403
+ insertReference = lastNode.nextSibling;
404
+ }
405
+ }
406
+
407
+ portal.insertBefore(fragment, insertReference);
408
+ }
409
+
410
+ async prependItem(...items: any[]) {
411
+ if (!items || items.length <= 0) return;
412
+ const portal = SwcUtils.getParentContainer(this);
413
+ if (!portal) return;
414
+
415
+ const win = this.ownerDocument?.defaultView || window;
416
+ const helperSet = SwcUtils.getHelperAndHostSet(win, this);
417
+
418
+ // 내부 상태(value) 업데이트 (기존 배열의 앞쪽에 새 아이템 추가)
419
+ if (this._value !== items) {
420
+ this._value = [...items, ...(this._value || [])];
421
+ }
422
+
423
+ const { fragment, newNodesGroup } = this.createFragmentAndNodes(items, helperSet, 0);
424
+
425
+ // 기존의 nodeGroups 배열 맨 앞에 새로운 그룹들을 추가
426
+ this._nodeGroups = [...newNodesGroup, ...this._nodeGroups];
427
+
428
+ // prepend 위치: 이 템플릿(SwcLoop) 노드의 바로 다음 위치 (가장 위)
306
429
  portal.insertBefore(fragment, this.nextSibling);
307
430
  }
308
431
 
432
+ refresh(value?: any) {
433
+ // 1. 그릴 대상 확정
434
+ const targetValue = value !== undefined ? value : (getAttributeValue(this, 'value') ?? this._value ?? []);
435
+
436
+ // 2. 화면 정리
437
+ this.cleanup();
438
+
439
+ // 3. 상태 완전 교체 (이어붙이지 않음!)
440
+ this._value = targetValue;
441
+
442
+ if (!this._value || this._value.length <= 0) return;
443
+
444
+ // 4. 화면 다시 그리기 (appendItem을 쓰지 않고 바로 그리기)
445
+ const win = this.ownerDocument?.defaultView || window;
446
+ const helperSet = SwcUtils.getHelperAndHostSet(win, this);
447
+ helperSet.$templateHost = this;
448
+ const { fragment, newNodesGroup } = this.createFragmentAndNodes(this._value, helperSet, 0);
449
+
450
+ this._nodeGroups = newNodesGroup;
451
+ const portal = SwcUtils.getParentContainer(this);
452
+ if (portal) {
453
+ // console.log('append????!~~~~~!?',fragment)
454
+ portal.insertBefore(fragment, this.nextSibling);
455
+ }
456
+ }
457
+
309
458
  private cleanup() {
459
+ console.log('누가 cleanup');
310
460
  this._nodeGroups.forEach(nodes => nodes.forEach(n => n.parentElement?.removeChild(n)));
311
461
  this._nodeGroups = [];
312
462
  }
@@ -318,11 +468,12 @@ export const registerAllElements = (w: any): Record<string, Constructor<HTMLElem
318
468
 
319
469
  @elementDefine('swc-choose', { extends: 'template', window: w })
320
470
  class SwcChoose extends HTMLTemplateElement {
321
- @attributeHost('value')
322
471
  _value: any;
323
472
 
324
- @attributeHost('replace-wrap')
325
- _replaceWrap: string | null;
473
+ @attributeHost('replace-wrap-start')
474
+ _replaceWrapStart: string | null;
475
+ @attributeHost('replace-wrap-end')
476
+ _replaceWrapEnd: string | null;
326
477
 
327
478
  private _nodes: Node[] = [];
328
479
  private _previousTemplate: HTMLTemplateElement | null = null;
@@ -332,27 +483,52 @@ export const registerAllElements = (w: any): Record<string, Constructor<HTMLElem
332
483
  }
333
484
 
334
485
  set value(nv: any) {
335
- this.refresh(nv);
486
+ this._value = nv;
487
+ this.refresh(this._value);
336
488
  }
337
489
 
338
- @changedAttributeHost('value')
339
- private setValue() {
340
- this.refresh();
490
+ constructor() {
491
+ super();
492
+ // console.log('swc chooooooooooooooooooooooooose constructor called!');
493
+ // console.log('swc cccccccss', this.getAttribute('value'));
341
494
  }
342
495
 
343
- @changedAttributeHost('replace-wrap')
344
- private setReplaceWrap(replaceWrapAttr?: string | null) {
345
- this.refresh();
496
+ // static get observedAttributes() {
497
+ // return ['value'];
498
+ // }
499
+
500
+ attributeChangedCallback(na, o, n) {
501
+ // console.log('-----------------choooooooose attr origin', na, o, n);
346
502
  }
347
503
 
348
- private replaceExpressions(html: string, groupArgs: any, context: any, wrap?: string | null): string {
349
- const config = wrap ? { wrapExpression: wrap } : undefined;
504
+ @changedAttributeHost('value')
505
+ private setValue(value: any, o: string, n: string) {
506
+ console.log('chooooooose value', value)
507
+ this._value = value;
508
+ this.refresh(this._value);
509
+ }
510
+
511
+ // @changedAttributeHost('replace-wrap')
512
+ // private setReplaceWrap(replaceWrapAttr?: string | null) {
513
+ // this.refresh();
514
+ // }
515
+
516
+ private replaceExpressions(html: string, groupArgs: any, context: any, wrap: {start: string, end: string}): string {
517
+ let config: any = undefined;
518
+ if (wrap) {
519
+ config = {
520
+ expression: {
521
+ replace: { start: `${wrap.start}`, end: `${wrap.end}` },
522
+ callReturn: { start: `${wrap.start}=`, end: `${wrap.end}` },
523
+ call: { start: `${wrap.start}@`, end: `${wrap.end}` }
524
+ }
525
+ };
526
+ }
350
527
  const ae = new ActionExpression(html, config);
351
- const filterOptions = wrap ? { type: 'replace' as const, wrap } : 'replace';
352
528
  const win = this.ownerDocument?.defaultView || window;
353
529
 
354
530
  // @ts-ignore
355
- for (const expr of ae.getExpressions(filterOptions)) {
531
+ for (const expr of ae.getExpressions('replace')) {
356
532
  try {
357
533
  const value = FunctionUtils.executeReturn({
358
534
  script: ConvertUtils.decodeHtmlEntity(expr.script, win.document),
@@ -367,37 +543,50 @@ export const registerAllElements = (w: any): Record<string, Constructor<HTMLElem
367
543
  return ae.getUnprocessedTemplate();
368
544
  }
369
545
 
370
- refresh(value = this._value) {
371
- const portal = this.parentElement;
372
- if (!portal) return;
546
+ refresh(value?: any) {
547
+ value = getAttributeValue(this, 'value') ?? this._value;
548
+ console.log('[DEBUG] SwcChoose.refresh called, value:', value);
549
+ const portal = SwcUtils.getParentContainer(this);
550
+ if (!portal) {
551
+ console.log('[DEBUG] SwcChoose portal is null!');
552
+ return;
553
+ }
373
554
 
374
- const win = this.ownerDocument?.defaultView || window;
555
+ const win = (getElementConfig(this).window ?? this.ownerDocument?.defaultView) || window;
375
556
  const helperSet = SwcUtils.getHelperAndHostSet(win, this);
376
- const groupArgs = { ...helperSet, $value: value };
557
+ const groupArgs = { ...helperSet, $value: value, $templateHost: this };
377
558
 
378
559
  const templates = Array.from(this.content.querySelectorAll('template'));
379
560
  let selected: HTMLTemplateElement | null = null;
380
561
 
381
562
  for (const t of templates) {
382
563
  if (t.getAttribute('is') === 'swc-when') {
383
- const attrVal = t.getAttribute('value') || '';
564
+ const attrVal = t.getAttribute('test') || '';
384
565
  let matches = false;
385
566
 
567
+
568
+
569
+ const result = FunctionUtils.executeReturn({
570
+ script: attrVal,
571
+ context: t,
572
+ args: groupArgs
573
+ });
574
+ matches = !!result;
386
575
  // Use ActionExpression to parse and extract script from value attribute
387
- const ae = new ActionExpression(attrVal);
388
- const expr = ae.getFirstExpression('replace');
389
-
390
- if (expr) {
391
- const decodedScript = ConvertUtils.decodeHtmlEntity(expr.script, win.document);
392
- const result = FunctionUtils.executeReturn({
393
- script: decodedScript,
394
- context: t,
395
- args: groupArgs
396
- });
397
- matches = !!result;
398
- } else {
399
- matches = String(attrVal) === String(value);
400
- }
576
+ // const ae = new ActionExpression(attrVal);
577
+ // const expr = ae.getFirstExpression('replace');
578
+
579
+ // if (expr) {
580
+ // const decodedScript = ConvertUtils.decodeHtmlEntity(expr.script, win.document);
581
+ // const result = FunctionUtils.executeReturn({
582
+ // script: decodedScript,
583
+ // context: t,
584
+ // args: groupArgs
585
+ // });
586
+ // matches = !!result;
587
+ // } else {
588
+ // matches = String(attrVal) === String(value);
589
+ // }
401
590
 
402
591
  if (matches) {
403
592
  selected = t;
@@ -422,14 +611,33 @@ export const registerAllElements = (w: any): Record<string, Constructor<HTMLElem
422
611
 
423
612
  if (target) {
424
613
  let htmlString = target.innerHTML;
425
- htmlString = this.replaceExpressions(htmlString, groupArgs, this, this._replaceWrap);
614
+ htmlString = this.replaceExpressions(htmlString, groupArgs, this, {start:this._replaceWrapStart ?? '{#{', end: this._replaceWrapEnd ?? '}#}'});
426
615
 
427
616
  const result = this.ownerDocument?.createElement('template') || document.createElement('template');
428
617
  result.innerHTML = htmlString;
429
618
  const processedFragment = result.content;
430
619
 
620
+ // console.log('[DEBUG] SwcChoose processedFragment children:', processedFragment._childNodesInternal?.length);
431
621
  this._nodes = Array.from(processedFragment.childNodes);
622
+
623
+ const setTemplateHost = (element: HTMLElement) => {
624
+ (element as any).__swc_template_host = this;
625
+ Array.from(element.children).forEach((child: any) => {
626
+ if (child instanceof HTMLElement) {
627
+ setTemplateHost(child);
628
+ }
629
+ });
630
+ };
631
+
632
+ this._nodes.forEach((node: any) => {
633
+ if (node instanceof HTMLElement) {
634
+ setTemplateHost(node as HTMLElement);
635
+ }
636
+ });
637
+
638
+ console.log(`[DEBUG] SwcChoose before insertBefore, portal: ${portal?.tagName}, isConnected: ${(portal as any)?.isConnected}`);
432
639
  portal.insertBefore(processedFragment, this.nextSibling);
640
+ console.log('[DEBUG] SwcChoose after insertBefore');
433
641
  }
434
642
  }
435
643
 
@@ -447,11 +655,10 @@ export const registerAllElements = (w: any): Record<string, Constructor<HTMLElem
447
655
  class SwcAsync extends HTMLTemplateElement {
448
656
  @attributeHost('value')
449
657
  _value: any;
450
-
451
- @attributeHost('replace-wrap')
452
- _replaceWrap: string | null;
453
-
454
- resultValue?: any;
658
+ @attributeHost('replace-wrap-start')
659
+ _replaceWrapStart: string | null;
660
+ @attributeHost('replace-wrap-end')
661
+ _replaceWrapEnd: string | null; resultValue?: any;
455
662
 
456
663
  get value(): boolean {
457
664
  return this._value;
@@ -468,10 +675,10 @@ export const registerAllElements = (w: any): Record<string, Constructor<HTMLElem
468
675
  this.executeAsync(this._value);
469
676
  }
470
677
 
471
- @changedAttributeHost('replace-wrap')
472
- private setReplaceWrap() {
473
- this.executeAsync(this._value);
474
- }
678
+ // @changedAttributeHost('replace-wrap')
679
+ // private setReplaceWrap() {
680
+ // this.executeAsync(this._value);
681
+ // }
475
682
 
476
683
  private executeAsync(result: any = this._value) {
477
684
  if (result === undefined || result === null) {
@@ -497,14 +704,22 @@ export const registerAllElements = (w: any): Record<string, Constructor<HTMLElem
497
704
  }
498
705
  }
499
706
 
500
- private replaceExpressions(html: string, groupArgs: any, context: any, wrap?: string | null): string {
501
- const config = wrap ? { wrapExpression: wrap } : undefined;
707
+ private replaceExpressions(html: string, groupArgs: any, context: any, wrap: {start: string, end: string}): string {
708
+ let config: any = undefined;
709
+ if (wrap) {
710
+ config = {
711
+ expression: {
712
+ replace: { start: `${wrap.start}`, end: `${wrap.end}` },
713
+ callReturn: { start: `${wrap.start}=`, end: `${wrap.end}` },
714
+ call: { start: `${wrap.start}@`, end: `${wrap.end}` }
715
+ }
716
+ };
717
+ }
502
718
  const ae = new ActionExpression(html, config);
503
- const filterOptions = wrap ? { type: 'replace' as const, wrap } : 'replace';
504
719
  const win = this.ownerDocument?.defaultView || window;
505
720
 
506
721
  // @ts-ignore
507
- for (const expr of ae.getExpressions(filterOptions)) {
722
+ for (const expr of ae.getExpressions('replace')) {
508
723
  try {
509
724
  const value = FunctionUtils.executeReturn({
510
725
  script: ConvertUtils.decodeHtmlEntity(expr.script, win.document),
@@ -520,9 +735,9 @@ export const registerAllElements = (w: any): Record<string, Constructor<HTMLElem
520
735
  }
521
736
 
522
737
  refresh(state: string = 'default') {
523
- console.log('rrrrrrrrrrrrrrrrr', state);
738
+ // console.log('rrrrrrrrrrrrrrrrr', state);
524
739
  this.cleanup();
525
- const portal = this.parentElement;
740
+ const portal = SwcUtils.getParentContainer(this);
526
741
  if (!portal) return;
527
742
 
528
743
  const win = this.ownerDocument?.defaultView || window;
@@ -531,15 +746,19 @@ export const registerAllElements = (w: any): Record<string, Constructor<HTMLElem
531
746
  const t = this.content.querySelector(`template[is="swc-${state}"]`) as HTMLTemplateElement;
532
747
  if (t) {
533
748
  let htmlString = t.innerHTML;
534
- const groupArgs = { ...helperSet, $value: this._value, $state: state };
749
+ const groupArgs = { ...helperSet, $value: this._value, $state: state, $templateHost: this };
535
750
 
536
- htmlString = this.replaceExpressions(htmlString, groupArgs, this, this._replaceWrap);
751
+ htmlString = this.replaceExpressions(htmlString, groupArgs, this, {start:this._replaceWrapStart ?? '{#{', end: this._replaceWrapEnd ?? '}#}'});
537
752
 
538
753
  const temp = this.ownerDocument?.createElement('template') || document.createElement('template');
539
754
  temp.innerHTML = htmlString;
540
755
  const content = temp.content;
541
756
 
542
757
  this._nodes = Array.from(content.childNodes);
758
+ this._nodes.forEach((node: any) => {
759
+ if (node instanceof HTMLElement) (node as any).__template_swc_host = this;
760
+ });
761
+
543
762
  portal.insertBefore(content, this.nextSibling);
544
763
  }
545
764
  }
package/src/types.ts CHANGED
@@ -35,6 +35,7 @@ export type HostSet = {
35
35
  $hosts: HTMLElement[]; // [root, ..., parent, self]
36
36
  $firstHost: HTMLElement | null; // Top-most SWC ancestor
37
37
  $lastHost: HTMLElement | null; // Same as $parentHost (for backward compatibility or clarity)
38
+ $templateHost: HTMLTemplateElement | null | undefined;
38
39
  $appHost: SwcAppInterface | null;
39
40
  $appHosts: SwcAppInterface[];
40
41
  $firstAppHost: SwcAppInterface | null;