@dooboostore/simple-web-component 1.0.31 → 1.0.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/dist/cjs/decorators/applyStyle.js +1 -1
  2. package/dist/cjs/decorators/applyStyle.js.map +2 -2
  3. package/dist/cjs/decorators/attribute.js +17 -1
  4. package/dist/cjs/decorators/attribute.js.map +2 -2
  5. package/dist/cjs/decorators/elementDefine.js +26 -9
  6. package/dist/cjs/decorators/elementDefine.js.map +3 -3
  7. package/dist/cjs/elements/SwcAppMixin.js +1 -1
  8. package/dist/cjs/elements/SwcAppMixin.js.map +2 -2
  9. package/dist/cjs/elements/SwcAsync.js +69 -57
  10. package/dist/cjs/elements/SwcAsync.js.map +2 -2
  11. package/dist/cjs/elements/SwcChoose.js +66 -60
  12. package/dist/cjs/elements/SwcChoose.js.map +2 -2
  13. package/dist/cjs/elements/SwcIf.js +51 -66
  14. package/dist/cjs/elements/SwcIf.js.map +2 -2
  15. package/dist/cjs/elements/SwcLoop.js +58 -78
  16. package/dist/cjs/elements/SwcLoop.js.map +2 -2
  17. package/dist/cjs/elements/register.js +310 -335
  18. package/dist/cjs/elements/register.js.map +2 -2
  19. package/dist/cjs/types.js.map +1 -1
  20. package/dist/cjs/utils/Utils.js +7 -13
  21. package/dist/cjs/utils/Utils.js.map +2 -2
  22. package/dist/esm/decorators/applyStyle.js +1 -1
  23. package/dist/esm/decorators/applyStyle.js.map +2 -2
  24. package/dist/esm/decorators/attribute.js +18 -2
  25. package/dist/esm/decorators/attribute.js.map +2 -2
  26. package/dist/esm/decorators/elementDefine.js +24 -7
  27. package/dist/esm/decorators/elementDefine.js.map +3 -3
  28. package/dist/esm/elements/SwcAppMixin.js +1 -1
  29. package/dist/esm/elements/SwcAppMixin.js.map +2 -2
  30. package/dist/esm/elements/SwcAsync.js +70 -58
  31. package/dist/esm/elements/SwcAsync.js.map +2 -2
  32. package/dist/esm/elements/SwcChoose.js +67 -61
  33. package/dist/esm/elements/SwcChoose.js.map +2 -2
  34. package/dist/esm/elements/SwcIf.js +52 -67
  35. package/dist/esm/elements/SwcIf.js.map +2 -2
  36. package/dist/esm/elements/SwcLoop.js +59 -79
  37. package/dist/esm/elements/SwcLoop.js.map +2 -2
  38. package/dist/esm/elements/register.js +239 -264
  39. package/dist/esm/elements/register.js.map +2 -2
  40. package/dist/esm/types.js.map +1 -1
  41. package/dist/esm/utils/Utils.js +7 -13
  42. package/dist/esm/utils/Utils.js.map +2 -2
  43. package/dist/esm-bundle/dooboostore-simple-web-component.esm.js +2623 -2422
  44. package/dist/esm-bundle/dooboostore-simple-web-component.esm.js.map +4 -4
  45. package/dist/types/decorators/attribute.d.ts.map +1 -1
  46. package/dist/types/decorators/elementDefine.d.ts.map +1 -1
  47. package/dist/types/elements/register.d.ts.map +1 -1
  48. package/dist/types/types.d.ts +8 -1
  49. package/dist/types/types.d.ts.map +1 -1
  50. package/dist/types/utils/Utils.d.ts.map +1 -1
  51. package/dist/umd-bundle/dooboostore-simple-web-component.umd.js +2623 -2422
  52. package/dist/umd-bundle/dooboostore-simple-web-component.umd.js.map +4 -4
  53. package/package.json +1 -1
  54. package/src/decorators/applyStyle.ts +1 -1
  55. package/src/decorators/attribute.ts +35 -30
  56. package/src/decorators/elementDefine.ts +26 -7
  57. package/src/elements/SwcAppMixin.ts +1 -1
  58. package/src/elements/SwcAsync.ts +75 -69
  59. package/src/elements/SwcChoose.ts +67 -80
  60. package/src/elements/SwcIf.ts +51 -76
  61. package/src/elements/SwcLoop.ts +56 -87
  62. package/src/elements/register.ts +235 -307
  63. package/src/types.ts +6 -1
  64. package/src/utils/Utils.ts +11 -17
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dooboostore/simple-web-component",
3
- "version": "1.0.31",
3
+ "version": "1.0.32",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/cjs/index.js",
@@ -34,7 +34,7 @@ function createStyleDecorator(action: StyleAction) {
34
34
  if (res !== undefined) {
35
35
  const win = (this as any).ownerDocument?.defaultView || window;
36
36
  const host = this as unknown as HTMLElement;
37
- const hostSet = { ...SwcUtils.getHelperAndHostSet(win, host), $this: this };
37
+ const hostSet = SwcUtils.getHelperAndHostSet(win, host);
38
38
  const r = options.root || 'auto';
39
39
  const targetEls: HTMLElement[] = [];
40
40
 
@@ -1,7 +1,8 @@
1
- import {ReflectUtils} from '@dooboostore/core';
1
+ import {ReflectUtils, FunctionUtils, ActionExpression} from '@dooboostore/core';
2
2
  import {ensureInit, getElementConfig} from './elementDefine';
3
3
  import {SwcUtils} from '../utils/Utils';
4
4
  import {SpecialSelector, SwcQueryOptions} from '../types';
5
+ import { ConvertUtils } from '@dooboostore/core-web';
5
6
  export interface AttributeFieldOptions extends SwcQueryOptions {
6
7
  name?: string;
7
8
  // connectedInitialize?: boolean;
@@ -113,38 +114,42 @@ export function attribute(selector: string, options: AttributeFieldOptions = {})
113
114
 
114
115
  Object.defineProperty(target, propertyKey, {
115
116
  get(this: any) {
116
- // console.log('------>??gettttt', this, propertyKey);
117
- // try {
118
- const attrName = options.name || String(propertyKey);
119
- const conf = getElementConfig(this);
120
- const currentWin = this._resolveWindow?.(conf) || ((typeof window !== 'undefined' ? window : undefined) as any);
121
- const targetType = options.type || designType;
122
- //
123
- // connectedInitialize: true이면 privateKey 값만 반환 (DOM 무시)
124
- // if (options.connectedInitialize === true && (this as any).__swc_syncing_init) {
125
- // console.log('cccccccc2');
126
- // return this[privateKey];
127
- // // return convertValue(this[privateKey], targetType);
128
- // }
129
-
130
- const targets = resolveTargetEls(this, selector, options, currentWin);
131
- const primaryTarget = targets[0];
132
-
133
- if (primaryTarget && typeof (primaryTarget as any).hasAttribute === 'function') {
134
- if ((primaryTarget as any).hasAttribute(attrName)) {
135
- const domVal = (primaryTarget as any).getAttribute(attrName);
136
- return convertValue(domVal, targetType);
117
+ const attrName = options.name || String(propertyKey);
118
+ const conf = getElementConfig(this);
119
+ const currentWin = this._resolveWindow?.(conf) || ((typeof window !== 'undefined' ? window : undefined) as any);
120
+ const targetType = options.type || designType;
121
+
122
+ const targets = resolveTargetEls(this, selector, options, currentWin);
123
+ const primaryTarget = targets[0];
124
+
125
+ if (primaryTarget && typeof (primaryTarget as any).hasAttribute === 'function') {
126
+ if ((primaryTarget as any).hasAttribute(attrName)) {
127
+ let domVal = (primaryTarget as any).getAttribute(attrName);
128
+
129
+ // 표현식 체크
130
+ const ae = new ActionExpression(domVal);
131
+ const expr = ae.getFirstExpression('call-return');
132
+ if (expr) {
133
+ try {
134
+ const result = FunctionUtils.executeReturn({
135
+ script: ConvertUtils.decodeHtmlEntity(expr.script, currentWin.document),
136
+ context: this,
137
+ args: SwcUtils.getHelperAndHostSet(currentWin, this)
138
+ });
139
+ return convertValue(result, targetType);
140
+ } catch (e) {
141
+ console.error(`[SWC] Failed to execute directive {{= ${expr.script} }}:`, e);
142
+ return convertValue(domVal, targetType);
143
+ }
137
144
  }
138
- return null;
145
+
146
+ return convertValue(domVal, targetType);
139
147
  }
148
+ return null;
149
+ }
140
150
 
141
- const data = convertValue(this[privateKey], targetType);
142
- // console.log('get rrr', data);
143
- return data;
144
- // } catch (err) {
145
- // console.error('Error in getter:', err);
146
- // throw err;
147
- // }
151
+ const data = convertValue(this[privateKey], targetType);
152
+ return data;
148
153
  },
149
154
  set(this: any, nv: any) {
150
155
  // this[propertyKey] = nv;
@@ -1,5 +1,4 @@
1
- import { ReflectUtils } from '@dooboostore/core';
2
- import { FunctionUtils } from '@dooboostore/core';
1
+ import { ReflectUtils, FunctionUtils, ActionExpression } from '@dooboostore/core';
3
2
  import { getAddEventListenerMetadata } from './addEventListener';
4
3
  import { ON_INITIALIZE_METADATA_KEY, ON_BEFORE_CONNECTED_METADATA_KEY, ON_AFTER_CONNECTED_METADATA_KEY, ON_BEFORE_DISCONNECTED_METADATA_KEY, ON_AFTER_DISCONNECTED_METADATA_KEY, ON_BEFORE_ADOPTED_METADATA_KEY, ON_AFTER_ADOPTED_METADATA_KEY, findAllLifecycleMetadata, getOnConnectedInnerHtmlMetadata } from './lifecycles';
5
4
  import { getEmitCustomEventMetadataList } from './emitCustomEvent';
@@ -12,6 +11,7 @@ import { SwcUtils } from '../utils/Utils';
12
11
  import { HTML_TAG_ENTRIES, DOM_EVENT_NAMES } from '../config/config';
13
12
  import { SituationTypeContainer, SituationTypeContainers } from '@dooboostore/simple-boot/decorators/inject/Inject';
14
13
  import { InjectSituationType, HostSet } from '../types';
14
+ import { ConvertUtils } from '@dooboostore/core-web';
15
15
 
16
16
  // --- Core Interfaces & Types ---
17
17
 
@@ -214,8 +214,7 @@ const setupPrototype = (proto: any) => {
214
214
  if (oldHandler) el.removeEventListener(eventName, oldHandler);
215
215
 
216
216
  const handler = async (event: any) => {
217
- const parent = SwcUtils.findNearestSwcHost(el);
218
- const hostSet = SwcUtils.getHostSet(parent || el);
217
+ const hostSet = SwcUtils.getHostSet(el);
219
218
  const conf = getElementConfig(this);
220
219
  const currentWin = (this as any)._resolveWindow(conf);
221
220
  const helpers = SwcUtils.getHelperSet(currentWin);
@@ -237,8 +236,7 @@ const setupPrototype = (proto: any) => {
237
236
  proto.__swc_executeAttributeEvent = async function (el: HTMLElement, attrName: string, script: string, event: Event) {
238
237
  ensureInit(this);
239
238
 
240
- const parent = SwcUtils.findNearestSwcHost(el);
241
- const hostSet = SwcUtils.getHostSet(parent || el);
239
+ const hostSet = SwcUtils.getHostSet(el);
242
240
  const conf = getElementConfig(this);
243
241
  const currentWin = (this as any)._resolveWindow(conf);
244
242
  const currentHelpers = SwcUtils.getHelperSet(currentWin);
@@ -565,6 +563,27 @@ export const elementDefine =
565
563
 
566
564
  const hSet = SwcUtils.getHostSet(this as any);
567
565
 
566
+ // Process expression directive before passing to handlers
567
+ let processedVal = newVal;
568
+ if (newVal !== null) {
569
+ const ae = new ActionExpression(newVal);
570
+ const expr = ae.getFirstExpression('call-return');
571
+ if (expr) {
572
+ const win = (this as any)._resolveWindow?.() || ((typeof window !== 'undefined' ? window : undefined) as any);
573
+ try {
574
+ const result = FunctionUtils.executeReturn({
575
+ script: ConvertUtils.decodeHtmlEntity(expr.script, win.document),
576
+ context: this,
577
+ args: SwcUtils.getHelperAndHostSet(win,this as any)
578
+ });
579
+ processedVal = String(result);
580
+ } catch (e) {
581
+ console.error(`[SWC] Failed to execute directive {{= ${expr.script} }} on attribute ${name}:`, e);
582
+ processedVal = newVal;
583
+ }
584
+ }
585
+ }
586
+
568
587
  if (name.startsWith('swc-on-') && !swcLifecycleAttributes.includes(name)) {
569
588
  if (newVal !== null) {
570
589
  const eventName = name.substring(7);
@@ -579,7 +598,7 @@ export const elementDefine =
579
598
 
580
599
  const mKeys = attrChangeMap.get(name);
581
600
  if (mKeys && Array.isArray(mKeys)) {
582
- for (const key of mKeys) (this as any)[key](newVal, old, name, hSet);
601
+ for (const key of mKeys) (this as any)[key](processedVal, old, name, hSet);
583
602
  }
584
603
  };
585
604
 
@@ -185,7 +185,7 @@ export function SwcAppMixin<T extends { new (...args: any[]): HTMLElement }>(Bas
185
185
  const userConfig = FunctionUtils.executeReturn({
186
186
  script: configStr,
187
187
  context: this,
188
- args: SwcUtils.getHelperAndHostSet(win, SwcUtils.findNearestSwcHost(this))
188
+ args: SwcUtils.getHelperAndHostSet(win, this)
189
189
  });
190
190
  this.connect(userConfig);
191
191
  } catch (e) {
@@ -1,58 +1,86 @@
1
1
  import { changedAttributeHost } from '../decorators/changedAttributeHost';
2
2
  import { SwcUtils } from '../utils/Utils';
3
- import { FunctionUtils } from '@dooboostore/core';
3
+ import { FunctionUtils, ActionExpression } from '@dooboostore/core';
4
+ import { ConvertUtils } from '@dooboostore/core-web';
5
+ import { attributeHost } from '../decorators';
4
6
 
5
7
  class SwcAsync extends HTMLTemplateElement {
6
- private _nodes: Node[] = [];
7
- private _value: any = null;
8
+ @attributeHost('value')
9
+ _value: any;
10
+
11
+ @attributeHost('replace-wrap')
12
+ _replaceWrap: string | null;
13
+
14
+ resultValue?: any;
8
15
 
9
- private _executeCloneCallback(attr: string, args: any) {
10
- const script = this.getAttribute(attr);
11
- if (!script) return;
12
- FunctionUtils.execute({ script, context: this, args });
16
+ get value(): boolean {
17
+ return this._value;
13
18
  }
14
19
 
20
+ set value(nv: any) {
21
+ this.executeAsync(nv);
22
+ }
23
+
24
+ private _nodes: Node[] = [];
25
+
15
26
  @changedAttributeHost('value')
16
- private refresh(valueAttr?: string | null) {
17
- valueAttr ??= this.getAttribute('value');
18
- if (!valueAttr) {
19
- this.render('default');
20
- return;
21
- }
27
+ private setValue(valueAttr?: string | null) {
28
+ this.executeAsync(this._value);
29
+ }
22
30
 
23
- // {{ }} 브레이스로 감싸진 값은 스크립트 실행
24
- if (valueAttr.startsWith('{{') && valueAttr.endsWith('}}')) {
25
- const script = valueAttr.slice(2, -2).trim();
26
- const win = this.ownerDocument?.defaultView || window;
27
- const result = FunctionUtils.executeReturn({
28
- script,
29
- context: this,
30
- args: SwcUtils.getHelperAndHostSet(win, this)
31
- });
32
-
33
- this.executeAsync(result);
34
- } else {
35
- // 일반 문자열 값 또는 직접 Promise 속성
36
- this.executeAsync(valueAttr);
37
- }
31
+ @changedAttributeHost('replace-wrap')
32
+ private setReplaceWrap() {
33
+ this.executeAsync(this._value);
38
34
  }
39
35
 
40
- private executeAsync(result: any) {
36
+ private executeAsync(result: any = this._value) {
37
+ if (result === undefined || result === null) {
38
+ this.refresh('default');
39
+ return;
40
+ }
41
+
41
42
  if (result instanceof Promise) {
42
- this.render('loading');
43
+ this.refresh('loading');
43
44
  result
44
45
  .then(v => {
45
- this._value = v;
46
- this.render('success');
46
+ this.resultValue = v;
47
+ this.refresh('success');
47
48
  })
48
- .catch(e => this.render('error'));
49
+ .catch(e => {
50
+ console.error('[SWC-ASYNC] Promise rejected:', e);
51
+ this.resultValue = e;
52
+ this.refresh('error');
53
+ });
49
54
  } else {
50
- this._value = result;
51
- this.render('success');
55
+ this.resultValue = result;
56
+ this.refresh('success');
57
+ }
58
+ }
59
+
60
+ private replaceExpressions(html: string, groupArgs: any, context: any, wrap?: string | null): string {
61
+ const config = wrap ? { wrapExpression: wrap } : undefined;
62
+ const ae = new ActionExpression(html, config);
63
+ const filterOptions = wrap ? { type: 'replace' as const, wrap } : 'replace';
64
+ const win = this.ownerDocument?.defaultView || window;
65
+
66
+ // @ts-ignore
67
+ for (const expr of ae.getExpressions(filterOptions)) {
68
+ try {
69
+ const value = FunctionUtils.executeReturn({
70
+ script: ConvertUtils.decodeHtmlEntity(expr.script, win.document),
71
+ context,
72
+ args: groupArgs
73
+ });
74
+ ae.replace(expr, String(value));
75
+ } catch (e) {
76
+ console.error(`[SWC] Failed to execute ${expr.original}:`, e);
77
+ }
52
78
  }
79
+ return ae.getUnprocessedTemplate();
53
80
  }
54
81
 
55
- private render(state: string) {
82
+ refresh(state: string = 'default') {
83
+ console.log('rrrrrrrrrrrrrrrrr', state);
56
84
  this.cleanup();
57
85
  const portal = this.parentElement;
58
86
  if (!portal) return;
@@ -62,36 +90,17 @@ class SwcAsync extends HTMLTemplateElement {
62
90
 
63
91
  const t = this.content.querySelector(`template[is="swc-${state}"]`) as HTMLTemplateElement;
64
92
  if (t) {
65
- const clone = t.content.cloneNode(true) as DocumentFragment;
66
- this._nodes = Array.from(clone.childNodes);
67
- const elements = this._nodes.filter(n => n.nodeType === 1) as HTMLElement[];
68
- const groupArgs = { ...helperSet, $value: this._value, $nodes: this._nodes, $elements: elements, $firstElement: elements[0], $state: state };
69
-
70
- // 부모 체인을 정상적으로 유지하기 위해 __swc_host 할당 제거
71
- this._nodes.forEach((n, nodeIdx) => {
72
- // if (n instanceof HTMLElement) (n as any).__swc_host = this; // 제거: 부모 체인 유지
73
- this._executeCloneCallback('on-clone-node', { ...groupArgs, $node: n, $nodeIndex: nodeIdx, $isElement: n.nodeType === 1 });
74
-
75
- // Attribute {{ }} 치환 - SwcChoose처럼 동적 치환
76
- if (n instanceof HTMLElement) {
77
- Array.from(n.attributes).forEach(attr => {
78
- const value = attr.value || '';
79
- if (value.startsWith('{{') && value.endsWith('}}')) {
80
- const script = value.slice(2, -2).trim();
81
- const result = FunctionUtils.executeReturn({
82
- script,
83
- context: n,
84
- args: groupArgs
85
- });
86
- n.setAttribute(attr.name, String(result));
87
- }
88
- });
89
- }
90
- });
91
-
92
- this._executeCloneCallback('on-clone-nodes', groupArgs);
93
-
94
- portal.insertBefore(clone, this.nextSibling);
93
+ let htmlString = t.innerHTML;
94
+ const groupArgs = { ...helperSet, $value: this._value, $state: state };
95
+
96
+ htmlString = this.replaceExpressions(htmlString, groupArgs, this, this._replaceWrap);
97
+
98
+ const temp = this.ownerDocument?.createElement('template') || document.createElement('template');
99
+ temp.innerHTML = htmlString;
100
+ const content = temp.content;
101
+
102
+ this._nodes = Array.from(content.childNodes);
103
+ portal.insertBefore(content, this.nextSibling);
95
104
  }
96
105
  }
97
106
 
@@ -100,9 +109,6 @@ class SwcAsync extends HTMLTemplateElement {
100
109
  this._nodes = [];
101
110
  }
102
111
 
103
- connectedCallback() {
104
- this.refresh();
105
- }
106
112
  disconnectedCallback() {
107
113
  this.cleanup();
108
114
  }
@@ -1,75 +1,90 @@
1
1
  import { changedAttributeHost } from '../decorators/changedAttributeHost';
2
2
  import { SwcUtils } from '../utils/Utils';
3
- import { FunctionUtils } from '@dooboostore/core';
3
+ import { FunctionUtils, ActionExpression } from '@dooboostore/core';
4
+ import { ConvertUtils } from '@dooboostore/core-web';
4
5
  import { SwcChooseInterface } from '../types';
6
+ import { attributeHost } from '../decorators';
5
7
 
6
8
  class SwcChoose extends HTMLTemplateElement implements SwcChooseInterface {
9
+ @attributeHost('value')
10
+ _value: any;
11
+
12
+ @attributeHost('replace-wrap')
13
+ _replaceWrap: string | null;
14
+
7
15
  private _nodes: Node[] = [];
8
- private _value: any = null;
9
16
  private _previousTemplate: HTMLTemplateElement | null = null;
10
17
 
11
18
  get value(): any {
12
19
  return this._value;
13
20
  }
21
+
14
22
  set value(nv: any) {
15
- this._value = nv;
16
- this.render();
23
+ this.refresh(nv);
17
24
  }
18
25
 
19
- private _executeCloneCallback(attr: string, args: any) {
20
- const script = this.getAttribute(attr);
21
- if (!script) return;
22
- FunctionUtils.execute({ script, context: this, args });
26
+ @changedAttributeHost('value')
27
+ private setValue() {
28
+ this.refresh();
23
29
  }
24
30
 
25
- @changedAttributeHost('value')
26
- refresh(valueAttr?: string | null) {
27
- valueAttr ??= this.getAttribute('value');
28
- if (valueAttr === null) {
29
- this.value = null;
30
- return;
31
- }
31
+ @changedAttributeHost('replace-wrap')
32
+ private setReplaceWrap(replaceWrapAttr?: string | null) {
33
+ this.refresh();
34
+ }
32
35
 
33
- // {{ }} 브레이스로 감싸진 값은 스크립트 실행
34
- if (valueAttr.startsWith('{{') && valueAttr.endsWith('}}')) {
35
- const script = valueAttr.slice(2, -2).trim();
36
- const win = this.ownerDocument?.defaultView || window;
37
- const helperAndHostSet = SwcUtils.getHelperAndHostSet(win, SwcUtils.findNearestSwcHost(this));
38
- // console.log('vvvvvvvvvvvvvvvvvv323', helperAndHostSet);
39
- const result = FunctionUtils.executeReturn({ script, context: this, args: helperAndHostSet });
40
- this.value = result;
41
- // console.log('----------vv', this.value);
42
- } else {
43
- // 일반 문자열 값
44
- this.value = valueAttr;
36
+ private replaceExpressions(html: string, groupArgs: any, context: any, wrap?: string | null): string {
37
+ const config = wrap ? { wrapExpression: wrap } : undefined;
38
+ const ae = new ActionExpression(html, config);
39
+ const filterOptions = wrap ? { type: 'replace' as const, wrap } : 'replace';
40
+ const win = this.ownerDocument?.defaultView || window;
41
+
42
+ // @ts-ignore
43
+ for (const expr of ae.getExpressions(filterOptions)) {
44
+ try {
45
+ const value = FunctionUtils.executeReturn({
46
+ script: ConvertUtils.decodeHtmlEntity(expr.script, win.document),
47
+ context,
48
+ args: groupArgs
49
+ });
50
+ ae.replace(expr, String(value));
51
+ } catch (e) {
52
+ console.error(`[SWC] Failed to execute ${expr.original}:`, e);
53
+ }
45
54
  }
55
+ return ae.getUnprocessedTemplate();
46
56
  }
47
57
 
48
- private render() {
49
- // console.log('-------------', this.value);
58
+ refresh(value = this._value) {
59
+ const portal = this.parentElement;
60
+ if (!portal) return;
61
+
50
62
  const win = this.ownerDocument?.defaultView || window;
51
63
  const helperSet = SwcUtils.getHelperAndHostSet(win, this);
64
+ const groupArgs = { ...helperSet, $value: value };
65
+
52
66
  const templates = Array.from(this.content.querySelectorAll('template'));
53
67
  let selected: HTMLTemplateElement | null = null;
54
68
 
55
69
  for (const t of templates) {
56
70
  if (t.getAttribute('is') === 'swc-when') {
71
+ const attrVal = t.getAttribute('value') || '';
57
72
  let matches = false;
58
- const value = t.getAttribute('value') || '';
59
73
 
60
- // {{ }} 브레이스로 감싸진 값은 스크립트 실행
61
- if (value.startsWith('{{') && value.endsWith('}}')) {
62
- const script = value.slice(2, -2).trim();
74
+ // Use ActionExpression to parse and extract script from value attribute
75
+ const ae = new ActionExpression(attrVal);
76
+ const expr = ae.getFirstExpression('replace');
77
+
78
+ if (expr) {
79
+ const decodedScript = ConvertUtils.decodeHtmlEntity(expr.script, win.document);
63
80
  const result = FunctionUtils.executeReturn({
64
- script,
81
+ script: decodedScript,
65
82
  context: t,
66
- args: { ...helperSet, $value: this._value }
83
+ args: groupArgs
67
84
  });
68
- // console.log('rrrrrrrrrrrrrr', result);
69
- matches = result === true;
85
+ matches = !!result;
70
86
  } else {
71
- // 일반 문자열 비교 (기존 방식)
72
- matches = String(value) === String(this._value);
87
+ matches = String(attrVal) === String(value);
73
88
  }
74
89
 
75
90
  if (matches) {
@@ -81,11 +96,12 @@ class SwcChoose extends HTMLTemplateElement implements SwcChooseInterface {
81
96
 
82
97
  const target = selected || (this.content.querySelector('template[is="swc-otherwise"]') as HTMLTemplateElement);
83
98
 
84
- // skip-if-same attribute가 있고 이전과 같은 template이면 렌더링 스킵
85
- // 최상위 skip-if-same 또는 각 when/otherwise의 skip-if-same 체크
86
- const targetHasSkipIfSame = target?.hasAttribute('skip-if-same');
87
- const shouldSkip = (this.hasAttribute('skip-if-same') || targetHasSkipIfSame) && this._previousTemplate === target;
88
- if (shouldSkip) {
99
+ const skipValue = target?.getAttribute?.('skip-if-same');
100
+ const skipValue2 = this._previousTemplate?.getAttribute?.('skip-if-same');
101
+ const hasSkipIfSame = target?.hasAttribute?.('skip-if-same');
102
+ const isSame = this._previousTemplate === target;
103
+ // console.log('---rrrr---', this._previousTemplate, target, hasSkipIfSame, isSame, skipValue, skipValue2);
104
+ if (hasSkipIfSame && isSame) {
89
105
  return;
90
106
  }
91
107
 
@@ -93,41 +109,15 @@ class SwcChoose extends HTMLTemplateElement implements SwcChooseInterface {
93
109
  this._previousTemplate = target;
94
110
 
95
111
  if (target) {
96
- const clone = target.content.cloneNode(true) as DocumentFragment;
97
- this._nodes = Array.from(clone.childNodes);
98
- const elements = this._nodes.filter(n => n.nodeType === 1) as HTMLElement[];
99
- const groupArgs = { ...helperSet, $value: this._value, $nodes: this._nodes, $elements: elements, $firstElement: elements[0] };
100
-
101
- // 복제된 최상위 요소들은 SwcChoose의 직접 자식이 될 것이므로 __swc_host 설정 안 함
102
- this._nodes.forEach((n, nodeIdx) => {
103
- // if (n instanceof HTMLElement) (n as any).__swc_host = this; // 제거: 부모 체인 유지
104
- this._executeCloneCallback('on-clone-node', { ...groupArgs, $node: n, $nodeIndex: nodeIdx, $isElement: n.nodeType === 1 });
105
-
106
- // Attribute {{ }} 치환 - HTML string 차원에서 동적 치환
107
- if (n instanceof HTMLElement) {
108
- Array.from(n.attributes).forEach(attr => {
109
- const value = attr.value || '';
110
- if (value.startsWith('{{') && value.endsWith('}}')) {
111
- const script = value.slice(2, -2).trim();
112
- const result = FunctionUtils.executeReturn({
113
- script,
114
- context: n,
115
- args: { ...groupArgs, $value: this._value }
116
- });
117
- n.setAttribute(attr.name, String(result));
118
- }
119
- });
120
- }
121
- });
112
+ let htmlString = target.innerHTML;
113
+ htmlString = this.replaceExpressions(htmlString, groupArgs, this, this._replaceWrap);
122
114
 
123
- this._executeCloneCallback('on-clone-nodes', groupArgs);
115
+ const result = this.ownerDocument?.createElement('template') || document.createElement('template');
116
+ result.innerHTML = htmlString;
117
+ const processedFragment = result.content;
124
118
 
125
- this.parentElement?.insertBefore(clone, this.nextSibling);
126
- // if (portal === this.parentElement) {
127
- // portal.insertBefore(clone, this.nextSibling);
128
- // } else {
129
- // portal.appendChild(clone);
130
- // }
119
+ this._nodes = Array.from(processedFragment.childNodes);
120
+ portal.insertBefore(processedFragment, this.nextSibling);
131
121
  }
132
122
  }
133
123
 
@@ -136,9 +126,6 @@ class SwcChoose extends HTMLTemplateElement implements SwcChooseInterface {
136
126
  this._nodes = [];
137
127
  }
138
128
 
139
- connectedCallback() {
140
- this.refresh();
141
- }
142
129
  disconnectedCallback() {
143
130
  this.cleanup();
144
131
  }