@dooboostore/simple-web-component 1.0.21 → 1.0.23

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 (174) hide show
  1. package/README.md +1113 -95
  2. package/dist/cjs/decorators/addEventListener.js +4 -0
  3. package/dist/cjs/decorators/addEventListener.js.map +2 -2
  4. package/dist/cjs/decorators/applyNode.js +174 -0
  5. package/dist/cjs/decorators/applyNode.js.map +7 -0
  6. package/dist/cjs/decorators/attribute.js +30 -10
  7. package/dist/cjs/decorators/attribute.js.map +3 -3
  8. package/dist/cjs/decorators/{changedAttribute.js → changedAttributeHost.js} +6 -6
  9. package/dist/cjs/decorators/changedAttributeHost.js.map +7 -0
  10. package/dist/cjs/decorators/classList.js +45 -15
  11. package/dist/cjs/decorators/classList.js.map +2 -2
  12. package/dist/cjs/decorators/elementDefine.js +107 -57
  13. package/dist/cjs/decorators/elementDefine.js.map +2 -2
  14. package/dist/cjs/decorators/emitCustomEvent.js +4 -3
  15. package/dist/cjs/decorators/emitCustomEvent.js.map +2 -2
  16. package/dist/cjs/decorators/index.js +2 -4
  17. package/dist/cjs/decorators/index.js.map +2 -2
  18. package/dist/cjs/decorators/lifecycles.js +29 -0
  19. package/dist/cjs/decorators/lifecycles.js.map +2 -2
  20. package/dist/cjs/decorators/query.js +5 -1
  21. package/dist/cjs/decorators/query.js.map +2 -2
  22. package/dist/cjs/decorators/queryAll.js +5 -1
  23. package/dist/cjs/decorators/queryAll.js.map +2 -2
  24. package/dist/cjs/decorators/setAttribute.js +5 -1
  25. package/dist/cjs/decorators/setAttribute.js.map +2 -2
  26. package/dist/cjs/decorators/style.js +39 -13
  27. package/dist/cjs/decorators/style.js.map +2 -2
  28. package/dist/cjs/elements/SwcAppEngine.js.map +2 -2
  29. package/dist/cjs/elements/SwcAppMixin.js.map +2 -2
  30. package/dist/cjs/elements/SwcAsync.js +2 -2
  31. package/dist/cjs/elements/SwcAsync.js.map +2 -2
  32. package/dist/cjs/elements/SwcChoose.js +2 -2
  33. package/dist/cjs/elements/SwcChoose.js.map +2 -2
  34. package/dist/cjs/elements/SwcIf.js +2 -2
  35. package/dist/cjs/elements/SwcIf.js.map +2 -2
  36. package/dist/cjs/elements/SwcLoop.js +2 -2
  37. package/dist/cjs/elements/SwcLoop.js.map +2 -2
  38. package/dist/cjs/elements/register.js +6 -5
  39. package/dist/cjs/elements/register.js.map +2 -2
  40. package/dist/cjs/index.js +6 -18
  41. package/dist/cjs/index.js.map +2 -2
  42. package/dist/cjs/types.js.map +2 -2
  43. package/dist/cjs/utils/TemplateUtils.js +6 -0
  44. package/dist/cjs/utils/TemplateUtils.js.map +2 -2
  45. package/dist/esm/decorators/addEventListener.js +4 -0
  46. package/dist/esm/decorators/addEventListener.js.map +2 -2
  47. package/dist/esm/decorators/applyNode.js +155 -0
  48. package/dist/esm/decorators/applyNode.js.map +7 -0
  49. package/dist/esm/decorators/attribute.js +30 -10
  50. package/dist/esm/decorators/attribute.js.map +3 -3
  51. package/dist/esm/decorators/{changedAttribute.js → changedAttributeHost.js} +3 -3
  52. package/dist/esm/decorators/changedAttributeHost.js.map +7 -0
  53. package/dist/esm/decorators/classList.js +45 -15
  54. package/dist/esm/decorators/classList.js.map +2 -2
  55. package/dist/esm/decorators/elementDefine.js +106 -56
  56. package/dist/esm/decorators/elementDefine.js.map +2 -2
  57. package/dist/esm/decorators/emitCustomEvent.js +4 -3
  58. package/dist/esm/decorators/emitCustomEvent.js.map +2 -2
  59. package/dist/esm/decorators/index.js +2 -4
  60. package/dist/esm/decorators/index.js.map +2 -2
  61. package/dist/esm/decorators/lifecycles.js +29 -0
  62. package/dist/esm/decorators/lifecycles.js.map +2 -2
  63. package/dist/esm/decorators/query.js +5 -1
  64. package/dist/esm/decorators/query.js.map +2 -2
  65. package/dist/esm/decorators/queryAll.js +5 -1
  66. package/dist/esm/decorators/queryAll.js.map +2 -2
  67. package/dist/esm/decorators/setAttribute.js +5 -1
  68. package/dist/esm/decorators/setAttribute.js.map +2 -2
  69. package/dist/esm/decorators/style.js +39 -13
  70. package/dist/esm/decorators/style.js.map +2 -2
  71. package/dist/esm/elements/SwcAppEngine.js.map +2 -2
  72. package/dist/esm/elements/SwcAppMixin.js.map +2 -2
  73. package/dist/esm/elements/SwcAsync.js +2 -2
  74. package/dist/esm/elements/SwcAsync.js.map +2 -2
  75. package/dist/esm/elements/SwcChoose.js +2 -2
  76. package/dist/esm/elements/SwcChoose.js.map +2 -2
  77. package/dist/esm/elements/SwcIf.js +2 -2
  78. package/dist/esm/elements/SwcIf.js.map +2 -2
  79. package/dist/esm/elements/SwcLoop.js +2 -2
  80. package/dist/esm/elements/SwcLoop.js.map +2 -2
  81. package/dist/esm/elements/register.js +6 -5
  82. package/dist/esm/elements/register.js.map +2 -2
  83. package/dist/esm/index.js +6 -18
  84. package/dist/esm/index.js.map +2 -2
  85. package/dist/esm/types.js.map +2 -2
  86. package/dist/esm/utils/TemplateUtils.js +6 -0
  87. package/dist/esm/utils/TemplateUtils.js.map +2 -2
  88. package/dist/esm-bundle/dooboostore-simple-web-component.esm.js +385 -206
  89. package/dist/esm-bundle/dooboostore-simple-web-component.esm.js.map +4 -4
  90. package/dist/types/decorators/addEventListener.d.ts +6 -0
  91. package/dist/types/decorators/addEventListener.d.ts.map +1 -1
  92. package/dist/types/decorators/applyNode.d.ts +121 -0
  93. package/dist/types/decorators/applyNode.d.ts.map +1 -0
  94. package/dist/types/decorators/attribute.d.ts +6 -0
  95. package/dist/types/decorators/attribute.d.ts.map +1 -1
  96. package/dist/types/decorators/{changedAttribute.d.ts → changedAttributeHost.d.ts} +6 -2
  97. package/dist/types/decorators/changedAttributeHost.d.ts.map +1 -0
  98. package/dist/types/decorators/classList.d.ts +32 -5
  99. package/dist/types/decorators/classList.d.ts.map +1 -1
  100. package/dist/types/decorators/elementDefine.d.ts.map +1 -1
  101. package/dist/types/decorators/emitCustomEvent.d.ts +14 -2
  102. package/dist/types/decorators/emitCustomEvent.d.ts.map +1 -1
  103. package/dist/types/decorators/index.d.ts +2 -4
  104. package/dist/types/decorators/index.d.ts.map +1 -1
  105. package/dist/types/decorators/lifecycles.d.ts +16 -0
  106. package/dist/types/decorators/lifecycles.d.ts.map +1 -1
  107. package/dist/types/decorators/query.d.ts +6 -3
  108. package/dist/types/decorators/query.d.ts.map +1 -1
  109. package/dist/types/decorators/queryAll.d.ts +6 -0
  110. package/dist/types/decorators/queryAll.d.ts.map +1 -1
  111. package/dist/types/decorators/setAttribute.d.ts +5 -0
  112. package/dist/types/decorators/setAttribute.d.ts.map +1 -1
  113. package/dist/types/decorators/style.d.ts +28 -3
  114. package/dist/types/decorators/style.d.ts.map +1 -1
  115. package/dist/types/elements/SwcAppEngine.d.ts.map +1 -1
  116. package/dist/types/elements/SwcAppMixin.d.ts.map +1 -1
  117. package/dist/types/elements/register.d.ts +1 -1
  118. package/dist/types/elements/register.d.ts.map +1 -1
  119. package/dist/types/index.d.ts +3 -15
  120. package/dist/types/index.d.ts.map +1 -1
  121. package/dist/types/types.d.ts +2 -1
  122. package/dist/types/types.d.ts.map +1 -1
  123. package/dist/types/utils/TemplateUtils.d.ts +5 -0
  124. package/dist/types/utils/TemplateUtils.d.ts.map +1 -1
  125. package/dist/umd-bundle/dooboostore-simple-web-component.umd.js +385 -206
  126. package/dist/umd-bundle/dooboostore-simple-web-component.umd.js.map +4 -4
  127. package/package.json +3 -3
  128. package/src/decorators/addEventListener.ts +9 -0
  129. package/src/decorators/applyNode.ts +290 -0
  130. package/src/decorators/attribute.ts +46 -12
  131. package/src/decorators/{changedAttribute.ts → changedAttributeHost.ts} +5 -1
  132. package/src/decorators/classList.ts +71 -16
  133. package/src/decorators/elementDefine.ts +130 -63
  134. package/src/decorators/emitCustomEvent.ts +13 -5
  135. package/src/decorators/index.ts +2 -4
  136. package/src/decorators/lifecycles.ts +43 -1
  137. package/src/decorators/query.ts +9 -2
  138. package/src/decorators/queryAll.ts +9 -0
  139. package/src/decorators/setAttribute.ts +8 -0
  140. package/src/decorators/style.ts +64 -13
  141. package/src/elements/SwcAppEngine.ts +1 -0
  142. package/src/elements/SwcAppMixin.ts +2 -1
  143. package/src/elements/SwcAsync.ts +2 -2
  144. package/src/elements/SwcChoose.ts +2 -2
  145. package/src/elements/SwcIf.ts +2 -2
  146. package/src/elements/SwcLoop.ts +2 -2
  147. package/src/elements/register.ts +7 -5
  148. package/src/index.ts +8 -17
  149. package/src/types.ts +2 -1
  150. package/src/utils/TemplateUtils.ts +9 -0
  151. package/dist/cjs/decorators/appendChild.js +0 -92
  152. package/dist/cjs/decorators/appendChild.js.map +0 -7
  153. package/dist/cjs/decorators/changedAttribute.js.map +0 -7
  154. package/dist/cjs/decorators/onConnectedInnerHtml.js +0 -48
  155. package/dist/cjs/decorators/onConnectedInnerHtml.js.map +0 -7
  156. package/dist/cjs/decorators/replaceChildren.js +0 -79
  157. package/dist/cjs/decorators/replaceChildren.js.map +0 -7
  158. package/dist/esm/decorators/appendChild.js +0 -73
  159. package/dist/esm/decorators/appendChild.js.map +0 -7
  160. package/dist/esm/decorators/changedAttribute.js.map +0 -7
  161. package/dist/esm/decorators/onConnectedInnerHtml.js +0 -29
  162. package/dist/esm/decorators/onConnectedInnerHtml.js.map +0 -7
  163. package/dist/esm/decorators/replaceChildren.js +0 -60
  164. package/dist/esm/decorators/replaceChildren.js.map +0 -7
  165. package/dist/types/decorators/appendChild.d.ts +0 -17
  166. package/dist/types/decorators/appendChild.d.ts.map +0 -1
  167. package/dist/types/decorators/changedAttribute.d.ts.map +0 -1
  168. package/dist/types/decorators/onConnectedInnerHtml.d.ts +0 -15
  169. package/dist/types/decorators/onConnectedInnerHtml.d.ts.map +0 -1
  170. package/dist/types/decorators/replaceChildren.d.ts +0 -9
  171. package/dist/types/decorators/replaceChildren.d.ts.map +0 -1
  172. package/src/decorators/appendChild.ts +0 -86
  173. package/src/decorators/onConnectedInnerHtml.ts +0 -42
  174. package/src/decorators/replaceChildren.ts +0 -64
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dooboostore/simple-web-component",
3
- "version": "1.0.21",
3
+ "version": "1.0.23",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/cjs/index.js",
@@ -92,10 +92,10 @@
92
92
  "webpack": "^5.93.0",
93
93
  "webpack-cli": "^5.1.4",
94
94
  "webpack-dev-server": "^5.2.0",
95
- "@dooboostore/core": "1.0.30",
95
+ "@dooboostore/core-web": "1.0.10",
96
96
  "@dooboostore/core-node": "1.0.12",
97
97
  "@dooboostore/simple-boot": "1.0.54",
98
- "@dooboostore/core-web": "1.0.10"
98
+ "@dooboostore/core": "1.0.30"
99
99
  },
100
100
  "scripts": {
101
101
  "clean": "rm -rf dist",
@@ -44,3 +44,12 @@ export const getAddEventListenerMetadata = (target: any): AddEventListenerMetada
44
44
  const constructor = target instanceof Function ? target : target.constructor;
45
45
  return ReflectUtils.getMetadata(ADD_EVENT_LISTENER_METADATA_KEY, constructor);
46
46
  };
47
+
48
+ /**
49
+ * @addEventListenerHost decorator - simplified version of @addEventListener for :host selector
50
+ * Usage: @addEventListenerHost('click')
51
+ * Usage: @addEventListenerHost('click', { stopPropagation: true })
52
+ */
53
+ export function addEventListenerHost(type: string, options?: AddEventListenerBaseOptions & SwcQueryOptions): MethodDecorator {
54
+ return addEventListener(':host', type, options);
55
+ }
@@ -0,0 +1,290 @@
1
+ import { ReflectUtils } from '@dooboostore/core';
2
+ import { ensureInit, getElementConfig } from './elementDefine';
3
+ import { SwcRootType } from '../types';
4
+
5
+ export type ApplyNodePosition = 'beforeBegin' | 'afterBegin' | 'beforeEnd' | 'afterEnd' | 'replace' | 'replaceChildren' | 'innerHtml' | 'innerText' | 'remove';
6
+
7
+ export interface ApplyNodeOptions {
8
+ position?: ApplyNodePosition;
9
+ root?: SwcRootType;
10
+ }
11
+
12
+ export interface ApplyNodeMetadata {
13
+ propertyKey: string | symbol;
14
+ selector: string;
15
+ options: ApplyNodeOptions;
16
+ }
17
+
18
+ export const APPLY_NODE_METADATA_KEY = Symbol('simple-web-component:apply-node');
19
+
20
+ const normalizeNodes = (res: any, doc: Document): Node[] => {
21
+ const items = Array.isArray(res) ? res : [res];
22
+ return items.map(it => {
23
+ if (it instanceof Node) return it;
24
+ return doc.createTextNode(it !== undefined && it !== null ? String(it) : '');
25
+ });
26
+ };
27
+
28
+ /**
29
+ * @applyNode decorator to surgically add/replace nodes to a target element.
30
+ *
31
+ * Usage examples:
32
+ * @applyNode('#list') addItem() { return '<li>New Item</li>'; } // append at end
33
+ * @applyNode('#header', { position: 'afterBegin' }) prependItem() { return '<h1>Title</h1>'; } // prepend at start
34
+ * @applyNode('#item', { position: 'replace' }) replaceItem() { return '<div>Replaced</div>'; } // replace element
35
+ * @applyNode('#list', { position: 'replaceChildren' }) updateList() { return [item1, item2]; } // replace all children
36
+ * @applyNode('#notification', { position: 'remove' }) dismissNotification() { return shouldDismiss; } // remove if truthy
37
+ * @applyNode('.items', { position: 'remove', root: 'shadow' }) clearItems() { return true; } // remove shadow DOM elements
38
+ *
39
+ * Positions:
40
+ * - 'beforeEnd' (default): Append at end of target
41
+ * - 'afterBegin': Prepend at start of target
42
+ * - 'beforeBegin': Insert before target
43
+ * - 'afterEnd': Insert after target
44
+ * - 'replace': Replace the entire target element
45
+ * - 'replaceChildren': Replace all children of target element
46
+ * - 'innerHtml': Set target's innerHTML
47
+ * - 'innerText': Set target's innerText
48
+ * - 'remove': Remove target element if returned value is truthy
49
+ *
50
+ * Root Options:
51
+ * - 'auto' (default): Search shadow DOM first, then light DOM
52
+ * - 'shadow': Search only in shadow DOM
53
+ * - 'light': Search only in light DOM
54
+ * - 'all': Use both (shadow preferred for query, both for remove)
55
+ */
56
+ export function applyNode(selector: string, options: ApplyNodeOptions = { position: 'beforeEnd' }): MethodDecorator {
57
+ return (target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor) => {
58
+ const constructor = target.constructor;
59
+ let metaList = ReflectUtils.getOwnMetadata(APPLY_NODE_METADATA_KEY, constructor) as Map<string | symbol, ApplyNodeMetadata>;
60
+ if (!metaList) {
61
+ metaList = new Map<string | symbol, ApplyNodeMetadata>();
62
+ ReflectUtils.defineMetadata(APPLY_NODE_METADATA_KEY, metaList, constructor);
63
+ }
64
+ metaList.set(propertyKey, { propertyKey, selector, options });
65
+
66
+ const original = descriptor.value;
67
+ descriptor.value = async function (...args: any[]) {
68
+ ensureInit(this);
69
+ const res = await original.apply(this, args);
70
+ if (res !== undefined) {
71
+ const conf = getElementConfig(this);
72
+ const currentWin = (this as any)._resolveWindow?.(conf) || ((typeof window !== 'undefined' ? window : undefined) as Window);
73
+ const currentDoc = currentWin.document;
74
+
75
+ const pos = options.position || 'beforeEnd';
76
+ const r = options.root || 'auto';
77
+
78
+ const getTargetRoot = () => {
79
+ if (r === 'shadow') return this.shadowRoot;
80
+ if (r === 'light') return this as any;
81
+ if (r === 'all') return this.shadowRoot || (this as any);
82
+ return this.shadowRoot || (this as any); // auto
83
+ };
84
+
85
+ if (pos === 'remove') {
86
+ // Remove element(s) if returned value is truthy
87
+ if (res) {
88
+ if (selector === ':host' || !selector) {
89
+ if (r === 'shadow' && this.shadowRoot) {
90
+ // Remove all children from shadow DOM
91
+ this.shadowRoot.replaceChildren();
92
+ } else {
93
+ // Remove the component itself
94
+ (this as any).remove();
95
+ }
96
+ } else {
97
+ const targetRoot = getTargetRoot();
98
+ if (targetRoot) {
99
+ const targets = targetRoot.querySelectorAll(selector);
100
+ targets.forEach((el: Element) => el.remove());
101
+ }
102
+ }
103
+ }
104
+ } else {
105
+ let targetEl: any = null;
106
+ if (selector === ':host' || !selector) {
107
+ if (r === 'shadow') {
108
+ targetEl = this.shadowRoot; // Target shadow DOM root
109
+ } else {
110
+ targetEl = this as any; // Target the component itself
111
+ }
112
+ } else {
113
+ const targetRoot = getTargetRoot();
114
+ targetEl = targetRoot ? targetRoot.querySelector(selector) : null;
115
+ }
116
+
117
+ if (targetEl) {
118
+ const isShadowRoot = targetEl instanceof ShadowRoot;
119
+ const nodes = normalizeNodes(res, currentDoc);
120
+
121
+ if (pos === 'replace') {
122
+ // ShadowRoot doesn't support replaceWith
123
+ if (!isShadowRoot) {
124
+ targetEl.replaceWith(...nodes);
125
+ }
126
+ } else if (pos === 'replaceChildren') {
127
+ // Replace all children of target element
128
+ targetEl.replaceChildren(...nodes);
129
+ } else if (pos === 'innerHtml') {
130
+ // Set innerHTML
131
+ targetEl.innerHTML = res !== undefined && res !== null ? String(res) : '';
132
+ } else if (pos === 'innerText') {
133
+ // Set innerText
134
+ if (targetEl.innerText !== undefined) {
135
+ targetEl.innerText = res !== undefined && res !== null ? String(res) : '';
136
+ }
137
+ } else {
138
+ /*
139
+ <!-- beforebegin -->
140
+ <p>
141
+ <!-- afterbegin -->
142
+ foo
143
+ <!-- beforeend -->
144
+ </p>
145
+ <!-- afterend -->
146
+ */
147
+ if (pos === 'beforeEnd') targetEl.append(...nodes);
148
+ else if (pos === 'afterBegin') targetEl.prepend(...nodes);
149
+ else if (pos === 'beforeBegin' && !isShadowRoot) targetEl.before(...nodes);
150
+ else if (pos === 'afterEnd' && !isShadowRoot) targetEl.after(...nodes);
151
+ }
152
+ }
153
+ }
154
+ }
155
+ return res;
156
+ };
157
+ };
158
+ }
159
+
160
+ export const findAllApplyNodeMetadata = (target: any): Map<string | symbol, ApplyNodeMetadata> => {
161
+ const result = new Map<string | symbol, ApplyNodeMetadata>();
162
+ const maps = ReflectUtils.findAllMetadata<Map<string | symbol, ApplyNodeMetadata>>(APPLY_NODE_METADATA_KEY, target);
163
+ maps.forEach(map => {
164
+ if (map instanceof Map) {
165
+ map.forEach((v, k) => result.set(k, v));
166
+ }
167
+ });
168
+ return result;
169
+ };
170
+
171
+ /**
172
+ * @applyNodeHost decorator - simplified version of @applyNode for :host selector
173
+ * Usage: @applyNodeHost() addItem() { return '<li>New Item</li>'; }
174
+ * Usage: @applyNodeHost({ position: 'afterBegin' }) prependItem() { return '<h1>Title</h1>'; }
175
+ */
176
+ export function applyNodeHost(options: ApplyNodeOptions = { position: 'beforeEnd' }): MethodDecorator {
177
+ return applyNode(':host', options);
178
+ }
179
+
180
+ // Convenience decorators for specific positions
181
+
182
+ /**
183
+ * @applyBeforeBeginNode - Insert before target element
184
+ */
185
+ export const applyBeforeBeginNode = (selector: string, options: ApplyNodeOptions = {}) =>
186
+ applyNode(selector, { ...options, position: 'beforeBegin' });
187
+
188
+ /**
189
+ * @applyAfterBeginNode - Prepend to target element
190
+ */
191
+ export const applyAfterBeginNode = (selector: string, options: ApplyNodeOptions = {}) =>
192
+ applyNode(selector, { ...options, position: 'afterBegin' });
193
+
194
+ /**
195
+ * @applyBeforeEndNode - Append to target element (default)
196
+ */
197
+ export const applyBeforeEndNode = (selector: string, options: ApplyNodeOptions = {}) =>
198
+ applyNode(selector, { ...options, position: 'beforeEnd' });
199
+
200
+ /**
201
+ * @applyAfterEndNode - Insert after target element
202
+ */
203
+ export const applyAfterEndNode = (selector: string, options: ApplyNodeOptions = {}) =>
204
+ applyNode(selector, { ...options, position: 'afterEnd' });
205
+
206
+ /**
207
+ * @applyReplaceNode - Replace entire target element
208
+ */
209
+ export const applyReplaceNode = (selector: string, options: ApplyNodeOptions = {}) =>
210
+ applyNode(selector, { ...options, position: 'replace' });
211
+
212
+ /**
213
+ * @applyReplaceChildrenNode - Replace all children of target element
214
+ */
215
+ export const applyReplaceChildrenNode = (selector: string, options: ApplyNodeOptions = {}) =>
216
+ applyNode(selector, { ...options, position: 'replaceChildren' });
217
+
218
+ /**
219
+ * @applyInnerHtmlNode - Set target's innerHTML
220
+ */
221
+ export const applyInnerHtmlNode = (selector: string, options: ApplyNodeOptions = {}) =>
222
+ applyNode(selector, { ...options, position: 'innerHtml' });
223
+
224
+ /**
225
+ * @applyInnerTextNode - Set target's innerText
226
+ */
227
+ export const applyInnerTextNode = (selector: string, options: ApplyNodeOptions = {}) =>
228
+ applyNode(selector, { ...options, position: 'innerText' });
229
+
230
+ /**
231
+ * @applyRemoveNode - Remove target element if truthy
232
+ */
233
+ export const applyRemoveNode = (selector: string, options: ApplyNodeOptions = {}) =>
234
+ applyNode(selector, { ...options, position: 'remove' });
235
+
236
+ // Host versions
237
+
238
+ /**
239
+ * @applyBeforeBeginNodeHost - Insert before :host
240
+ */
241
+ export const applyBeforeBeginNodeHost = (options: ApplyNodeOptions = {}) =>
242
+ applyNode(':host', { ...options, position: 'beforeBegin' });
243
+
244
+ /**
245
+ * @applyAfterBeginNodeHost - Prepend to :host
246
+ */
247
+ export const applyAfterBeginNodeHost = (options: ApplyNodeOptions = {}) =>
248
+ applyNode(':host', { ...options, position: 'afterBegin' });
249
+
250
+ /**
251
+ * @applyBeforeEndNodeHost - Append to :host
252
+ */
253
+ export const applyBeforeEndNodeHost = (options: ApplyNodeOptions = {}) =>
254
+ applyNode(':host', { ...options, position: 'beforeEnd' });
255
+
256
+ /**
257
+ * @applyAfterEndNodeHost - Insert after :host
258
+ */
259
+ export const applyAfterEndNodeHost = (options: ApplyNodeOptions = {}) =>
260
+ applyNode(':host', { ...options, position: 'afterEnd' });
261
+
262
+ /**
263
+ * @applyReplaceNodeHost - Replace :host
264
+ */
265
+ export const applyReplaceNodeHost = (options: ApplyNodeOptions = {}) =>
266
+ applyNode(':host', { ...options, position: 'replace' });
267
+
268
+ /**
269
+ * @applyReplaceChildrenNodeHost - Replace children of :host
270
+ */
271
+ export const applyReplaceChildrenNodeHost = (options: ApplyNodeOptions = {}) =>
272
+ applyNode(':host', { ...options, position: 'replaceChildren' });
273
+
274
+ /**
275
+ * @applyInnerHtmlNodeHost - Set :host innerHTML
276
+ */
277
+ export const applyInnerHtmlNodeHost = (options: ApplyNodeOptions = {}) =>
278
+ applyNode(':host', { ...options, position: 'innerHtml' });
279
+
280
+ /**
281
+ * @applyInnerTextNodeHost - Set :host innerText
282
+ */
283
+ export const applyInnerTextNodeHost = (options: ApplyNodeOptions = {}) =>
284
+ applyNode(':host', { ...options, position: 'innerText' });
285
+
286
+ /**
287
+ * @applyRemoveNodeHost - Remove :host if truthy
288
+ */
289
+ export const applyRemoveNodeHost = (options: ApplyNodeOptions = {}) =>
290
+ applyNode(':host', { ...options, position: 'remove' });
@@ -99,6 +99,22 @@ export function attribute(selector: string, options: AttributeOptions = {}): Pro
99
99
  }
100
100
  metaList.push({ propertyKey, selector, options, privateKey });
101
101
 
102
+ // Helper function to apply setAttribute to targets
103
+ const applySetAttribute = (inst: any, targets: HTMLElement[], attrName: string, nv: any) => {
104
+ targets.forEach(it => {
105
+ console.log('----->', it)
106
+ if (it && typeof (it as any).setAttribute === 'function') {
107
+ if (nv === null) {
108
+ (it as any).removeAttribute(attrName);
109
+ } else {
110
+ (it as any).setAttribute(attrName, String(nv));
111
+ }
112
+ } else {
113
+ inst[privateKey] = nv;
114
+ }
115
+ });
116
+ };
117
+
102
118
  Object.defineProperty(target, propertyKey, {
103
119
  get(this: any) {
104
120
  const attrName = options.name || String(propertyKey);
@@ -127,25 +143,34 @@ export function attribute(selector: string, options: AttributeOptions = {}): Pro
127
143
  },
128
144
  set(this: any, nv: any) {
129
145
  const targetType = options.type || designType;
130
- // const convertedValue = convertValue(nv, targetType);
131
- // console.log('---set', propertyKey, targetType, nv, convertedValue);
146
+ // console.log('---set', propertyKey, targetType, nv, this, this.__swc_syncing_init);
132
147
 
133
148
  const attrName = options.name || String(propertyKey);
134
149
  const conf = getElementConfig(this);
135
150
  const currentWin = this._resolveWindow?.(conf) || ((typeof window !== 'undefined' ? window : undefined) as any);
136
- const targets = resolveTargetEls(this, selector, options, currentWin);
137
- targets.forEach(it => {
138
- console.log('----->', it)
139
- if (it && typeof (it as any).setAttribute === 'function') {
140
- if (nv === null) {
141
- (it as any).removeAttribute(attrName);
142
- } else {
143
- (it as any).setAttribute(attrName, String(nv));
144
- }
151
+
152
+ // During initialization/syncing phase
153
+ if (this.__swc_syncing_init) {
154
+ // If connectedInitialize is enabled, force setAttribute (JS -> DOM)
155
+ if (options.connectedInitialize === true) {
156
+ const targets = resolveTargetEls(this, selector, options, currentWin);
157
+ applySetAttribute(this, targets, attrName, nv);
145
158
  } else {
159
+ // Otherwise just update memory
146
160
  this[privateKey] = nv;
147
161
  }
148
- })
162
+ return;
163
+ }
164
+
165
+ // Before connected, save to memory only
166
+ if (!this.__swc_connected) {
167
+ this[privateKey] = nv;
168
+ return;
169
+ }
170
+
171
+ // After connected, normal setAttribute
172
+ const targets = resolveTargetEls(this, selector, options, currentWin);
173
+ applySetAttribute(this, targets, attrName, nv);
149
174
  },
150
175
  enumerable: true,
151
176
  configurable: true
@@ -156,3 +181,12 @@ export function attribute(selector: string, options: AttributeOptions = {}): Pro
156
181
  export const findAllAttributeMetadata = (target: any): AttributeMetadata[] => {
157
182
  return ReflectUtils.findAllMetadata<AttributeMetadata[]>(ATTRIBUTE_METADATA_KEY, target).flat();
158
183
  };
184
+
185
+ /**
186
+ * @hostAttribute decorator - simplified version of @attribute for :host selector
187
+ * Usage: @hostAttribute('data-product-id')
188
+ * Usage: @hostAttribute('disabled', { type: Boolean })
189
+ */
190
+ export function attributeHost(attrName: string, options: AttributeOptions = {}): PropertyDecorator {
191
+ return attribute(':host', { ...options, name: attrName });
192
+ }
@@ -2,7 +2,11 @@ import { ReflectUtils } from '@dooboostore/core';
2
2
 
3
3
  export const ON_ATTRIBUTE_CHANGED_METADATA_KEY = Symbol('simple-web-component:on-attribute-changed');
4
4
 
5
- export function changedAttribute(attributeName?: string): MethodDecorator {
5
+ /**
6
+ * @changedAttributeHost decorator - fires when any attribute on :host changes
7
+ * Usage: @changedAttributeHost('data-product-id')
8
+ */
9
+ export function changedAttributeHost(attributeName?: string): MethodDecorator {
6
10
  return (target: Object, propertyKey: string | symbol) => {
7
11
  const constructor = target.constructor;
8
12
  let meta = ReflectUtils.getOwnMetadata(ON_ATTRIBUTE_CHANGED_METADATA_KEY, constructor) as Map<string, (string | symbol)[]>;
@@ -1,6 +1,7 @@
1
1
  import { ReflectUtils } from '@dooboostore/core';
2
2
  import { ensureInit } from './elementDefine';
3
3
  import { SwcUtils } from '../utils/Utils';
4
+ import { SwcQueryOptions } from '../types';
4
5
 
5
6
  export const CLASS_LIST_METADATA_KEY = Symbol('simple-web-component:class-list');
6
7
 
@@ -10,10 +11,11 @@ export interface ClassListMetadata {
10
11
  propertyKey: string | symbol;
11
12
  selector: string;
12
13
  action: ClassListAction;
14
+ options: SwcQueryOptions;
13
15
  }
14
16
 
15
17
  function createClassListDecorator(action: ClassListAction) {
16
- return (selector: string): MethodDecorator => {
18
+ return (selector: string, options: SwcQueryOptions = {}): MethodDecorator => {
17
19
  return (target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor) => {
18
20
  const constructor = target.constructor;
19
21
  let metaList = ReflectUtils.getOwnMetadata(CLASS_LIST_METADATA_KEY, constructor) as Map<string | symbol, ClassListMetadata>;
@@ -21,22 +23,43 @@ function createClassListDecorator(action: ClassListAction) {
21
23
  metaList = new Map<string | symbol, ClassListMetadata>();
22
24
  ReflectUtils.defineMetadata(CLASS_LIST_METADATA_KEY, metaList, constructor);
23
25
  }
24
- metaList.set(propertyKey, { propertyKey, selector, action });
26
+ metaList.set(propertyKey, { propertyKey, selector, action, options });
25
27
 
26
28
  const original = descriptor.value;
27
29
  descriptor.value = async function (...args: any[]) {
28
30
  ensureInit(this);
29
31
  const res = await original.apply(this, args);
30
32
  if (res !== undefined) {
33
+ const r = options.root || 'auto';
31
34
  const hostSet = SwcUtils.getHostSet(this as any);
35
+ const targetEls: HTMLElement[] = [];
32
36
 
33
- let targetEl: HTMLElement | null = null;
34
- if (selector === ':host' || !selector) targetEl = this as any;
35
- else if (selector === ':parentHost') targetEl = hostSet.$parentHost;
36
- else if (selector === ':appHost') targetEl = hostSet.$appHost;
37
- else targetEl = (this.shadowRoot || (this as any)).querySelector(selector);
37
+ if (selector === ':host' || !selector) {
38
+ targetEls.push(this as any);
39
+ } else if (selector === ':parentHost') {
40
+ if (hostSet.$parentHost) targetEls.push(hostSet.$parentHost);
41
+ } else if (selector === ':appHost') {
42
+ if (hostSet.$appHost) targetEls.push(hostSet.$appHost);
43
+ } else {
44
+ if (r === 'shadow') {
45
+ const found = this.shadowRoot?.querySelectorAll(selector);
46
+ if (found) targetEls.push(...found);
47
+ } else if (r === 'light') {
48
+ const found = (this as any).querySelectorAll(selector);
49
+ if (found) targetEls.push(...found);
50
+ } else if (r === 'all') {
51
+ const sFound = this.shadowRoot?.querySelectorAll(selector);
52
+ const lFound = (this as any).querySelectorAll(selector);
53
+ if (sFound) targetEls.push(...sFound);
54
+ if (lFound) targetEls.push(...lFound);
55
+ } else {
56
+ // auto
57
+ const found = (this.shadowRoot || (this as any)).querySelectorAll(selector);
58
+ if (found) targetEls.push(...found);
59
+ }
60
+ }
38
61
 
39
- if (targetEl) {
62
+ targetEls.forEach(targetEl => {
40
63
  if (action === 'set') {
41
64
  let classes = '';
42
65
  if (typeof res === 'object' && res !== null && !Array.isArray(res)) {
@@ -51,7 +74,7 @@ function createClassListDecorator(action: ClassListAction) {
51
74
  } else if (action === 'update') {
52
75
  if (typeof res === 'object' && res !== null && !Array.isArray(res)) {
53
76
  Object.entries(res).forEach(([cls, force]) => {
54
- targetEl!.classList.toggle(cls, !!force);
77
+ targetEl.classList.toggle(cls, !!force);
55
78
  });
56
79
  } else if (res !== undefined && res !== null) {
57
80
  const classes = Array.isArray(res) ? res.map(String) : [String(res)];
@@ -65,9 +88,9 @@ function createClassListDecorator(action: ClassListAction) {
65
88
  targetEl.classList.remove(...classes);
66
89
  } else if (action === 'toggle') {
67
90
  const classes = Array.isArray(res) ? res.map(String) : [String(res)];
68
- classes.forEach(c => targetEl!.classList.toggle(c));
91
+ classes.forEach(c => targetEl.classList.toggle(c));
69
92
  }
70
- }
93
+ });
71
94
  }
72
95
  return res;
73
96
  };
@@ -79,31 +102,63 @@ function createClassListDecorator(action: ClassListAction) {
79
102
  * @setClassList decorator to surgically replace all classes on a target element.
80
103
  * Returns: string | string[]
81
104
  */
82
- export const setClassList = createClassListDecorator('set');
105
+ export const setClassList = (selector: string, options: SwcQueryOptions = {}) => createClassListDecorator('set')(selector, options);
83
106
 
84
107
  /**
85
108
  * @updateClassList decorator to surgically update specific classes.
86
109
  * Returns: Record<string, boolean> (toggle with force) | string | string[] (add)
87
110
  */
88
- export const updateClassList = createClassListDecorator('update');
111
+ export const updateClassList = (selector: string, options: SwcQueryOptions = {}) => createClassListDecorator('update')(selector, options);
89
112
 
90
113
  /**
91
114
  * @addClassList decorator to surgically add classes to a target element.
92
115
  * Returns: string | string[]
93
116
  */
94
- export const addClassList = createClassListDecorator('add');
117
+ export const addClassList = (selector: string, options: SwcQueryOptions = {}) => createClassListDecorator('add')(selector, options);
95
118
 
96
119
  /**
97
120
  * @removeClassList decorator to surgically remove classes from a target element.
98
121
  * Returns: string | string[]
99
122
  */
100
- export const removeClassList = createClassListDecorator('remove');
123
+ export const removeClassList = (selector: string, options: SwcQueryOptions = {}) => createClassListDecorator('remove')(selector, options);
101
124
 
102
125
  /**
103
126
  * @toggleClassList decorator to surgically toggle classes on a target element.
104
127
  * Returns: string | string[]
105
128
  */
106
- export const toggleClassList = createClassListDecorator('toggle');
129
+ export const toggleClassList = (selector: string, options: SwcQueryOptions = {}) => createClassListDecorator('toggle')(selector, options);
130
+
131
+ // Host-specific versions (for :host selector)
132
+
133
+ /**
134
+ * @setClassListHost decorator to set classes on :host element.
135
+ * Returns: string | string[]
136
+ */
137
+ export const setClassListHost = (options: SwcQueryOptions = {}) => createClassListDecorator('set')(':host', options);
138
+
139
+ /**
140
+ * @updateClassListHost decorator to update specific classes on :host.
141
+ * Returns: Record<string, boolean> (toggle with force) | string | string[] (add)
142
+ */
143
+ export const updateClassListHost = (options: SwcQueryOptions = {}) => createClassListDecorator('update')(':host', options);
144
+
145
+ /**
146
+ * @addClassListHost decorator to add classes to :host element.
147
+ * Returns: string | string[]
148
+ */
149
+ export const addClassListHost = (options: SwcQueryOptions = {}) => createClassListDecorator('add')(':host', options);
150
+
151
+ /**
152
+ * @removeClassListHost decorator to remove classes from :host element.
153
+ * Returns: string | string[]
154
+ */
155
+ export const removeClassListHost = (options: SwcQueryOptions = {}) => createClassListDecorator('remove')(':host', options);
156
+
157
+ /**
158
+ * @toggleClassListHost decorator to toggle classes on :host element.
159
+ * Returns: string | string[]
160
+ */
161
+ export const toggleClassListHost = (options: SwcQueryOptions = {}) => createClassListDecorator('toggle')(':host', options);
107
162
 
108
163
  export const findAllClassListMetadata = (target: any): Map<string | symbol, ClassListMetadata> => {
109
164
  const result = new Map<string | symbol, ClassListMetadata>();