@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
@@ -1,101 +1,79 @@
1
1
  import { changedAttributeHost } from '../decorators/changedAttributeHost';
2
2
  import { SwcUtils } from '../utils/Utils';
3
- import { FunctionUtils } from '@dooboostore/core';
3
+ import { FunctionUtils, ActionExpression } from '@dooboostore/core';
4
+ import { ConvertUtils } from '@dooboostore/core-web';
5
+ import { SwcIfInterface } from '../types';
6
+ import { attributeHost } from '../decorators';
7
+
8
+ class SwcIf extends HTMLTemplateElement implements SwcIfInterface {
9
+ @attributeHost('value')
10
+ _value: any;
11
+
12
+ @attributeHost('replace-wrap')
13
+ _replaceWrap: string | null;
4
14
 
5
- class SwcIf extends HTMLTemplateElement {
6
- private _value = false;
7
15
  private _nodes: Node[] = [];
8
16
 
9
17
  get value(): boolean {
10
18
  return this._value;
11
19
  }
20
+
12
21
  set value(nv: any) {
13
- this._value = Boolean(nv);
14
- this.render();
22
+ // this._value = nv;
23
+ this.refresh(nv);
15
24
  }
16
25
 
17
- private _executeCloneCallback(attr: string, args: any) {
18
- const script = this.getAttribute(attr);
19
- if (!script) return;
20
- FunctionUtils.execute({ script, context: this, args });
26
+ private replaceExpressions(html: string, groupArgs: any, context: any, wrap?: string | null): string {
27
+ const config = wrap ? { wrapExpression: wrap } : undefined;
28
+ const ae = new ActionExpression(html, config);
29
+ const filterOptions = wrap ? { type: 'replace' as const, wrap } : 'replace';
30
+ const win = this.ownerDocument?.defaultView || window;
31
+
32
+ // @ts-ignore
33
+ for (const expr of ae.getExpressions(filterOptions)) {
34
+ try {
35
+ const value = FunctionUtils.executeReturn({
36
+ script: ConvertUtils.decodeHtmlEntity(expr.script, win.document),
37
+ context,
38
+ args: groupArgs
39
+ });
40
+ ae.replace(expr, String(value));
41
+ } catch (e) {
42
+ console.error(`[SWC] Failed to execute ${expr.original}:`, e);
43
+ }
44
+ }
45
+ return ae.getUnprocessedTemplate();
21
46
  }
22
47
 
23
48
  @changedAttributeHost('value')
24
- private refreshValue(valueAttr?: string | null) {
25
- valueAttr ??= this.getAttribute('value');
26
- if (valueAttr === null) {
27
- this.value = false;
28
- return;
29
- }
49
+ private setValue(valueAttr?: string | null) {
50
+ this.refresh();
51
+ }
30
52
 
31
- // {{ }} 브레이스로 감싸진 값은 스크립트 실행
32
- if (valueAttr.startsWith('{{') && valueAttr.endsWith('}}')) {
33
- const script = valueAttr.slice(2, -2).trim();
34
- const win = this.ownerDocument?.defaultView || window;
35
- const result = FunctionUtils.executeReturn({
36
- script,
37
- context: this,
38
- args: SwcUtils.getHelperAndHostSet(win, this)
39
- });
40
- this.value = result;
41
- } else {
42
- // 일반 문자열 값
43
- this.value = Boolean(valueAttr);
44
- }
53
+ @changedAttributeHost('replace-wrap')
54
+ private setReplaceWrap(replaceWrapAttr?: string | null) {
55
+ this.refresh();
45
56
  }
46
57
 
47
- private render() {
58
+ refresh(value = this._value) {
48
59
  this.cleanup();
49
60
  const portal = this.parentElement;
50
- if (!portal) return;
61
+ if (!portal || !value) return;
51
62
 
52
63
  const win = this.ownerDocument?.defaultView || window;
53
64
  const helperSet = SwcUtils.getHelperAndHostSet(win, this);
54
65
 
55
- let targetTpl: HTMLTemplateElement | null = null;
56
- if (this._value) {
57
- targetTpl = this;
58
- } else {
59
- targetTpl = this.content.querySelector('template[is="swc-default"]') as HTMLTemplateElement;
60
- }
66
+ let htmlString = this.innerHTML;
67
+ const groupArgs = { ...helperSet, $value: value };
61
68
 
62
- if (targetTpl) {
63
- const clone = targetTpl.content.cloneNode(true) as DocumentFragment;
64
- // If it's the host template, remove sub-templates from clone
65
- if (targetTpl === this) {
66
- clone.querySelectorAll('template[is^="swc-"]').forEach(t => t.remove());
67
- }
69
+ htmlString = this.replaceExpressions(htmlString, groupArgs, this, this._replaceWrap);
68
70
 
69
- this._nodes = Array.from(clone.childNodes);
70
- const elements = this._nodes.filter(n => n.nodeType === 1) as HTMLElement[];
71
- const groupArgs = { ...helperSet, $value: this._value, $nodes: this._nodes, $elements: elements, $firstElement: elements[0] };
72
-
73
- // 부모 체인을 정상적으로 유지하기 위해 __swc_host 할당 제거
74
- this._nodes.forEach((node, nodeIdx) => {
75
- // if (node instanceof HTMLElement) (node as any).__swc_host = this; // 제거: 부모 체인 유지
76
- this._executeCloneCallback('on-clone-node', { ...groupArgs, $node: node, $nodeIndex: nodeIdx, $isElement: node.nodeType === 1 });
77
-
78
- // Attribute {{ }} 치환 - SwcChoose처럼 동적 치환
79
- if (node instanceof HTMLElement) {
80
- Array.from(node.attributes).forEach(attr => {
81
- const value = attr.value || '';
82
- if (value.startsWith('{{') && value.endsWith('}}')) {
83
- const script = value.slice(2, -2).trim();
84
- const result = FunctionUtils.executeReturn({
85
- script,
86
- context: node,
87
- args: { ...groupArgs, $value: this._value }
88
- });
89
- node.setAttribute(attr.name, String(result));
90
- }
91
- });
92
- }
93
- });
94
-
95
- this._executeCloneCallback('on-clone-nodes', groupArgs);
96
-
97
- portal.insertBefore(clone, this.nextSibling);
98
- }
71
+ const result = this.ownerDocument?.createElement('template') || document.createElement('template');
72
+ result.innerHTML = htmlString;
73
+ const processedFragment = result.content;
74
+
75
+ this._nodes = Array.from(processedFragment.childNodes);
76
+ portal.insertBefore(processedFragment, this.nextSibling);
99
77
  }
100
78
 
101
79
  private cleanup() {
@@ -103,9 +81,6 @@ class SwcIf extends HTMLTemplateElement {
103
81
  this._nodes = [];
104
82
  }
105
83
 
106
- connectedCallback() {
107
- this.refreshValue();
108
- }
109
84
  disconnectedCallback() {
110
85
  this.cleanup();
111
86
  }
@@ -1,59 +1,61 @@
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 SwcLoop extends HTMLTemplateElement {
6
- private _value: any[] = [];
8
+ @attributeHost('value')
9
+ _value: any[];
10
+
11
+ @attributeHost('replace-wrap')
12
+ _replaceWrap: string | null;
13
+
7
14
  private _nodeGroups: Node[][] = [];
8
15
 
9
16
  get value(): any[] {
10
17
  return this._value;
11
18
  }
12
19
  set value(nv: any[]) {
13
- this._value = Array.isArray(nv) ? [...nv] : [];
14
- this.render();
20
+ // this._value = Array.isArray(nv) ? [...nv] : [];
21
+ this.refresh(nv);
15
22
  }
16
23
 
17
-
18
-
19
- private _executeCloneCallback(attr: string, args: any) {
20
- const script = this.getAttribute(attr);
21
- if (!script) return;
22
- FunctionUtils.execute({ script, context: this, args });
24
+ @changedAttributeHost('value')
25
+ private setValue() {
26
+ this.refresh();
23
27
  }
24
28
 
25
- private _cleanup() {
26
- this._nodeGroups.forEach(nodes => nodes.forEach(n => n.parentElement?.removeChild(n)));
27
- this._nodeGroups = [];
29
+ @changedAttributeHost('replace-wrap')
30
+ private setReplaceWrap() {
31
+ this.refresh();
28
32
  }
29
33
 
30
- @changedAttributeHost('value')
31
- private refreshValue() {
32
- let script = this.getAttribute('value');
33
- if (!script) {
34
- this.value = [];
35
- return;
36
- }
37
- // Parse {{ }} braces
38
- if (script.startsWith('{{') && script.endsWith('}}')) {
39
- script = script.slice(2, -2).trim();
40
- }
34
+ private replaceExpressions(html: string, groupArgs: any, context: any, wrap?: string | null): string {
35
+ const config = wrap ? { wrapExpression: wrap } : undefined;
36
+ const ae = new ActionExpression(html, config);
37
+ const filterOptions = wrap ? { type: 'replace' as const, wrap } : 'replace';
41
38
  const win = this.ownerDocument?.defaultView || window;
42
- const res = FunctionUtils.executeReturn({
43
- script,
44
- context: this,
45
- args: SwcUtils.getHelperAndHostSet(win, this)
46
- });
47
39
 
48
- if (res instanceof Promise) {
49
- res.then(v => (this.value = v)).catch(() => (this.value = []));
50
- } else {
51
- this.value = res;
40
+ // @ts-ignore
41
+ for (const expr of ae.getExpressions(filterOptions)) {
42
+ try {
43
+ const value = FunctionUtils.executeReturn({
44
+ script: ConvertUtils.decodeHtmlEntity(expr.script, win.document),
45
+ context,
46
+ args: groupArgs
47
+ });
48
+ ae.replace(expr, String(value));
49
+ } catch (e) {
50
+ console.error(`[SWC] Failed to execute ${expr.original}:`, e);
51
+ }
52
52
  }
53
+ return ae.getUnprocessedTemplate();
53
54
  }
54
55
 
55
- render() {
56
- this._cleanup();
56
+ refresh(value = this._value) {
57
+ this.cleanup();
58
+ if (value?.length<=0) return;
57
59
  const portal = this.parentElement;
58
60
  if (!portal) return;
59
61
 
@@ -61,63 +63,30 @@ class SwcLoop extends HTMLTemplateElement {
61
63
  const helperSet = SwcUtils.getHelperAndHostSet(win, this);
62
64
  const fragment = win.document.createDocumentFragment();
63
65
 
64
- if (this._value.length === 0) {
65
- const defaultTpl = this.content.querySelector('template[is="swc-default"]') as HTMLTemplateElement;
66
- if (defaultTpl) {
67
- const clone = defaultTpl.content.cloneNode(true);
68
- const nodes = Array.from(clone.childNodes);
69
- this._nodeGroups.push(nodes);
70
- fragment.appendChild(clone);
71
- }
72
- } else {
73
- this._value.forEach((item, index) => {
74
- const clone = this.content.cloneNode(true) as DocumentFragment;
75
- // Filter out sub-templates
76
- const subTemplates = clone.querySelectorAll('template[is^="swc-"]');
77
- subTemplates.forEach(t => t.remove());
78
-
79
- const nodes = Array.from(clone.childNodes);
80
- const elements = nodes.filter(n => n.nodeType === 1) as HTMLElement[];
81
-
82
- const groupArgs = { ...helperSet, $item: item, $index: index, $value: this._value, $nodes: nodes, $elements: elements, $firstElement: elements[0] };
83
-
84
- nodes.forEach((node, nodeIdx) => {
85
- this._executeCloneCallback('on-clone-node', { ...groupArgs, $node: node, $nodeIndex: nodeIdx, $isElement: node.nodeType === 1 });
86
-
87
- // Attribute {{ }} 치환 - SwcChoose처럼 동적 치환
88
- if (node instanceof HTMLElement) {
89
- Array.from(node.attributes).forEach(attr => {
90
- const value = attr.value || '';
91
- if (value.startsWith('{{') && value.endsWith('}}')) {
92
- const script = value.slice(2, -2).trim();
93
- const result = FunctionUtils.executeReturn({
94
- script,
95
- context: node,
96
- args: groupArgs
97
- });
98
- node.setAttribute(attr.name, String(result));
99
- }
100
- });
101
- }
102
- });
66
+ value.forEach((item, index) => {
67
+ let htmlString = this.innerHTML;
68
+ const groupArgs = { ...helperSet, $item: item, $index: index, $value: this._value };
103
69
 
104
- this._executeCloneCallback('on-clone-nodes', groupArgs);
105
- this._nodeGroups.push(nodes);
106
- fragment.appendChild(clone);
107
- });
108
- }
70
+ htmlString = this.replaceExpressions(htmlString, groupArgs, this, this._replaceWrap);
109
71
 
110
- if (portal === this.parentElement) {
111
- portal.insertBefore(fragment, this.nextSibling);
112
- } else {
113
- portal.appendChild(fragment);
114
- }
72
+ const temp = this.ownerDocument?.createElement('template') || document.createElement('template');
73
+ temp.innerHTML = htmlString;
74
+
75
+ const content = temp.content;
76
+ const nodes = Array.from(content.childNodes);
77
+ this._nodeGroups.push(nodes);
78
+ fragment.appendChild(content);
79
+ });
80
+
81
+ portal.insertBefore(fragment, this.nextSibling);
115
82
  }
116
83
 
117
- connectedCallback() {
118
- this.refreshValue();
84
+ private cleanup() {
85
+ this._nodeGroups.forEach(nodes => nodes.forEach(n => n.parentElement?.removeChild(n)));
86
+ this._nodeGroups = [];
119
87
  }
88
+
120
89
  disconnectedCallback() {
121
- this._cleanup();
90
+ this.cleanup();
122
91
  }
123
92
  }