@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/README.md CHANGED
@@ -30,67 +30,1037 @@ For **Safari** support of the `is` attribute, add this to your `<head>`:
30
30
 
31
31
  ---
32
32
 
33
- ## 💡 Core Decorators
33
+ ## 💡 Complete Decorator Reference
34
34
 
35
- ### 1. `@elementDefine(config)`
36
- Registers the class as a custom element.
35
+ ### Lifecycle Decorators
37
36
 
38
- | Option | Type | Description |
39
- |---|---|---|
40
- | `name` | `string` | The custom element tag name (e.g., `'my-el'`). |
41
- | `extends` | `string` | (Optional) The native tag to extend. |
42
- | `observedAttributes` | `string[]` | (Optional) Extra attributes to observe. |
43
- | `window` | `Window` | (Optional) Custom window object (useful for SSR). |
37
+ #### 1. `@onConstructor()`
38
+ Called during component construction (in `constructor`). Perfect for dependency injection setup.
44
39
 
45
- ### 2. `@onConnectedInnerHtml(options?)`
46
- Defines the template for the component. Executed when the component is connected to the DOM.
40
+ ```typescript
41
+ @elementDefine('my-component')
42
+ class MyComponent extends HTMLElement {
43
+ private dataService: DataService;
44
+
45
+ @onConstructor
46
+ setupDI(dataService: DataService) {
47
+ this.dataService = dataService;
48
+ console.log('Component constructed with DI');
49
+ }
50
+ }
51
+ ```
52
+
53
+ **Execution Order**: First - during `new MyComponent()`
54
+
55
+ #### 2. `@onBeforeConnected()`
56
+ **Dependency Injection & Pre-connection Setup**
57
+
58
+ Called before the component connects to the DOM. Perfect for initializing services via DI.
59
+
60
+ **⚠️ Important**: Lifecycle DI works **WITHOUT `@Sim`** - regular `@elementDefine` components can receive services here!
61
+
62
+ ```typescript
63
+ @elementDefine('my-component') // ← NO @Sim needed for lifecycle DI
64
+ class MyComponent extends HTMLElement {
65
+ #cartService: CartService;
66
+ #subscription?: Subscription;
67
+
68
+ // Lifecycle DI injection - works even without @Sim
69
+ @onBeforeConnected
70
+ async setupComponent(cartService: CartService) {
71
+ this.#cartService = cartService;
72
+
73
+ // Can be async - waits before rendering
74
+ await this.#cartService.load();
75
+
76
+ // Subscribe to reactive updates
77
+ this.#subscription = this.#cartService.store.subscribe(state => {
78
+ this.updateUI();
79
+ });
80
+ }
81
+
82
+ disconnectedCallback() {
83
+ this.#subscription?.unsubscribe();
84
+ }
85
+
86
+ @onConnectedInnerHtml
87
+ render() {
88
+ return `<div>Component ready</div>`;
89
+ }
90
+ }
91
+ ```
92
+
93
+ **Key Features:**
94
+ - ✅ Services automatically injected via lifecycle decorators
95
+ - ✅ **NO `@Sim` required** for lifecycle DI injection
96
+ - ✅ Can be async - waits before rendering
97
+ - ✅ Called once, before `connectedCallback`
98
+ - ✅ Perfect for initialization and subscriptions
99
+
100
+ **DI Injection Methods:**
101
+
102
+ | Method | Requires `@Sim`? | Location |
103
+ |--------|-----------------|----------|
104
+ | Lifecycle (`@onBeforeConnected`) | ❌ NO | Lifecycle decorators |
105
+ | Constructor Injection | ✅ YES | Inside `<swc-app>` only |
106
+ | Static Registration | ✅ YES | `<swc-app>` DI container |
107
+
108
+ **Execution Order**: Before `connectedCallback()`
109
+
110
+ #### 3. `@onAfterConnected()` / `@onConnected()`
111
+ Called after the component connects to the DOM. For post-render operations.
112
+
113
+ ```typescript
114
+ @elementDefine('my-element')
115
+ class MyElement extends HTMLElement {
116
+ @onAfterConnected // or @onConnected (alias)
117
+ initializeAfterConnect() {
118
+ console.log('Element fully connected to DOM');
119
+ this.focus(); // Can interact with DOM now
120
+
121
+ // Measure element size after render
122
+ const rect = this.getBoundingClientRect();
123
+ console.log('Size:', rect.width, rect.height);
124
+ }
125
+ }
126
+ ```
127
+
128
+ **Execution Order**: After `connectedCallback()`
129
+
130
+ #### 4. `@onBeforeDisconnected()`
131
+ Called before the component disconnects from the DOM. For cleanup preparation.
132
+
133
+ ```typescript
134
+ @elementDefine('event-listener-component')
135
+ class EventListenerComponent extends HTMLElement {
136
+ private resizeObserver: ResizeObserver;
137
+
138
+ @onBeforeDisconnected
139
+ prepareForDisconnect() {
140
+ console.log('About to disconnect, saving state...');
141
+ // Save state, cancel pending requests, etc.
142
+ const state = {
143
+ scrollPosition: window.scrollY,
144
+ formData: this.getFormData()
145
+ };
146
+ localStorage.setItem('component-state', JSON.stringify(state));
147
+ }
148
+
149
+ @onAfterDisconnected
150
+ cleanup() {
151
+ this.resizeObserver?.disconnect();
152
+ console.log('Cleanup completed');
153
+ }
154
+ }
155
+ ```
156
+
157
+ **Execution Order**: Before `disconnectedCallback()`
158
+
159
+ #### 5. `@onAfterDisconnected()` / `@onDisconnected()`
160
+ Called after the component disconnects from the DOM. Final cleanup.
47
161
 
48
162
  ```typescript
49
- @elementDefine('my-el')
50
- class MyEl extends HTMLElement {
163
+ @elementDefine('resource-component')
164
+ class ResourceComponent extends HTMLElement {
165
+ private timerId: number;
166
+ private subscription: Subscription;
167
+
168
+ @onAfterDisconnected // or @onDisconnected (alias)
169
+ cleanup() {
170
+ // Cancel timers
171
+ clearTimeout(this.timerId);
172
+
173
+ // Unsubscribe from observables
174
+ this.subscription?.unsubscribe();
175
+
176
+ console.log('Resources cleaned up');
177
+ }
178
+ }
179
+ ```
180
+
181
+ **Execution Order**: After `disconnectedCallback()`
182
+
183
+ #### 6. `@onBeforeAdopted()`
184
+ Called before the element is adopted by a new document (useful for SSR/iframe scenarios).
185
+
186
+ ```typescript
187
+ @elementDefine('document-aware')
188
+ class DocumentAware extends HTMLElement {
189
+ @onBeforeAdopted
190
+ prepareForAdoption() {
191
+ console.log('Adopting to new document...');
192
+ // Prepare for document change
193
+ }
194
+ }
195
+ ```
196
+
197
+ #### 7. `@onAfterAdopted()` / `@onAdopted()`
198
+ Called after the element is adopted by a new document.
199
+
200
+ ```typescript
201
+ @elementDefine('iframe-component')
202
+ class IframeComponent extends HTMLElement {
203
+ @onAfterAdopted // or @onAdopted (alias)
204
+ onDocumentAdopted() {
205
+ console.log('Now in new document context');
206
+ // Re-attach to new document's resources
207
+ this.initializeInNewDocument();
208
+ }
209
+
210
+ private initializeInNewDocument() {
211
+ // Setup for new document
212
+ }
213
+ }
214
+ ```
215
+
216
+ #### 8. `@onAddEventListener()`
217
+ Called when an event listener is added to the element. For event tracking/analytics.
218
+
219
+ ```typescript
220
+ @elementDefine('event-tracked')
221
+ class EventTracked extends HTMLElement {
222
+ @onAddEventListener
223
+ onListenerAdded(type: string, listener: EventListener, options?: AddEventListenerOptions) {
224
+ console.log(`Event listener added: ${type}`);
225
+ // Track event listeners for debugging
226
+ }
227
+ }
228
+ ```
229
+
230
+ #### 9. `@onConnectedInnerHtml(options?)`
231
+ Defines the component's template. Renders to Shadow DOM or Light DOM based on `useShadow` option.
232
+
233
+ ```typescript
234
+ @elementDefine('styled-card')
235
+ class StyledCard extends HTMLElement {
51
236
  @onConnectedInnerHtml({ useShadow: true })
52
237
  render() {
53
- return `<style>:host { color: blue; }</style><div>Hello World</div>`;
238
+ return `
239
+ <style>
240
+ :host {
241
+ display: block;
242
+ padding: 20px;
243
+ border-radius: 8px;
244
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
245
+ }
246
+ </style>
247
+ <div class="card-content">
248
+ <slot></slot>
249
+ </div>
250
+ `;
251
+ }
252
+ }
253
+ ```
254
+
255
+ **Options:**
256
+ - `useShadow: true` - Render to Shadow DOM (style encapsulation)
257
+ - `useShadow: false` - Render to Light DOM (default)
258
+
259
+ ---
260
+
261
+ ### Attribute & Property Decorators
262
+
263
+ #### 10. `@attributeHost(name, options?)`
264
+ Bidirectional attribute binding with automatic type conversion.
265
+
266
+ ```typescript
267
+ @elementDefine('input-field')
268
+ class InputField extends HTMLElement {
269
+ @attributeHost('value', { connectedInitialize: true })
270
+ value: string = '';
271
+
272
+ @attributeHost('disabled')
273
+ disabled: boolean = false;
274
+
275
+ @attributeHost('count')
276
+ count: number = 0;
277
+
278
+ @onConnectedInnerHtml
279
+ render() {
280
+ return `
281
+ <input type="text" value="${this.value}" ${this.disabled ? 'disabled' : ''}>
282
+ <p>Count: ${this.count}</p>
283
+ `;
284
+ }
285
+ }
286
+ ```
287
+
288
+ **Options:**
289
+ - `connectedInitialize: true` - Push class value to DOM on connect
290
+ - `connectedInitialize: false` - Pull DOM value on connect (default)
291
+ - Auto-converts: `String`, `Number`, `Boolean`
292
+
293
+ #### 11. `@changedAttributeHost(name)`
294
+ Reactive handler triggered when attribute changes.
295
+
296
+ ```typescript
297
+ @elementDefine('reactive-input')
298
+ class ReactiveInput extends HTMLElement {
299
+ private internalValue = '';
300
+
301
+ @changedAttributeHost('value')
302
+ onValueChange(newVal: string, oldVal: string) {
303
+ console.log(`Value changed: ${oldVal} → ${newVal}`);
304
+ this.internalValue = newVal;
305
+ this.updateDisplay();
306
+ }
307
+
308
+ updateDisplay() {
309
+ const input = this.querySelector('input') as HTMLInputElement;
310
+ if (input) input.value = this.internalValue;
311
+ }
312
+ }
313
+ ```
314
+
315
+ #### 12. `@setAttribute(selector, name, options?)`
316
+ Reflect method's return value to a target element's DOM attribute. One-way binding (class → DOM).
317
+
318
+ ```typescript
319
+ @elementDefine('status-badge')
320
+ class StatusBadge extends HTMLElement {
321
+ private status: 'active' | 'inactive' | 'pending' = 'pending';
322
+ private items: any[] = [];
323
+
324
+ // Reflect status to host element's data-status attribute
325
+ @setAttribute(':host', 'data-status')
326
+ getStatus() {
327
+ return this.status;
328
+ }
329
+
330
+ // Reflect computed data to specific element
331
+ @setAttribute('#count-display', 'data-count')
332
+ getItemCount() {
333
+ return this.items.length.toString();
334
+ }
335
+
336
+ // Reflect to Shadow DOM element's aria-label
337
+ @setAttribute('.badge-label', 'aria-label', { root: 'shadow' })
338
+ getAriaLabel() {
339
+ return `Status is ${this.status}`;
340
+ }
341
+
342
+ @onConnectedInnerHtml
343
+ render() {
344
+ return `
345
+ <div data-status="${this.status}">
346
+ <span class="badge-label">Status: ${this.status}</span>
347
+ <span id="count-display" data-count="${this.items.length}">Items: ${this.items.length}</span>
348
+ </div>
349
+ `;
350
+ }
351
+
352
+ // Change status and update DOM attributes
353
+ setStatus(newStatus: typeof this.status) {
354
+ this.status = newStatus;
355
+ this.getStatus(); // Updates :host[data-status]
356
+ this.getItemCount(); // Updates #count-display[data-count]
357
+ this.getAriaLabel(); // Updates .badge-label[aria-label]
358
+ }
359
+ }
360
+ ```
361
+
362
+ **Signature:**
363
+ - `@setAttribute(selector, attributeName, options?)`
364
+ - Returns method's value → reflects to target's attribute
365
+
366
+ **Selectors:**
367
+ - `':host'` - The component itself
368
+ - `'#element-id'` - Element by ID
369
+ - `'.class-name'` - Element by class
370
+ - `selector` - Any CSS selector
371
+
372
+ #### 12b. `@setAttributeHost(name, options?)` - Shorthand for `:host`
373
+ Simplified version of `@setAttribute` that always targets `:host`.
374
+
375
+ ```typescript
376
+ @elementDefine('counter')
377
+ class Counter extends HTMLElement {
378
+ private count = 0;
379
+
380
+ // Equivalent to @setAttribute(':host', 'aria-label')
381
+ @setAttributeHost('aria-label')
382
+ getAriaLabel() {
383
+ return `Count is ${this.count}`;
384
+ }
385
+
386
+ @setAttributeHost('data-count')
387
+ getCountValue() {
388
+ return this.count.toString();
389
+ }
390
+
391
+ @setAttributeHost('aria-disabled')
392
+ getDisabledState() {
393
+ return (this.count >= 10).toString();
394
+ }
395
+
396
+ @onConnectedInnerHtml
397
+ render() {
398
+ return `
399
+ <div>
400
+ <p>Count: ${this.count}</p>
401
+ <button swc-on-click="$host.increment()">+</button>
402
+ </div>
403
+ `;
404
+ }
405
+
406
+ increment() {
407
+ this.count++;
408
+ this.getAriaLabel(); // Updates :host[aria-label]
409
+ this.getCountValue(); // Updates :host[data-count]
410
+ this.getDisabledState(); // Updates :host[aria-disabled]
411
+ }
412
+ }
413
+ ```
414
+
415
+ **Usage:**
416
+ ```typescript
417
+ // Full form
418
+ @setAttribute(':host', 'data-status')
419
+ // vs
420
+ // Shorthand for :host
421
+ @setAttributeHost('data-status')
422
+ ```
423
+
424
+ **Key Differences:**
425
+ - **`@attributeHost`** - Two-way binding: DOM ↔ Class (Bidirectional)
426
+ - **`@setAttribute`** - One-way binding: Class → DOM (Any selector)
427
+ - **`@setAttributeHost`** - One-way binding: Class → DOM (`:host` only)
428
+ - **`@changedAttributeHost`** - Watch DOM attribute changes (Reactive handler)
429
+
430
+ **Options:**
431
+ - `root: 'light'` - Set attribute on light DOM element
432
+ - `root: 'shadow'` - Set attribute on shadow DOM element
433
+ - `root: 'auto'` - Auto-detect (default)
434
+
435
+ ---
436
+
437
+ ### Query & DOM Selection Decorators
438
+
439
+ #### 13. `@query(selector, options?)`
440
+ Inject a single element into a property.
441
+
442
+ ```typescript
443
+ @elementDefine('form-handler')
444
+ class FormHandler extends HTMLElement {
445
+ @query('#submit-btn') submitBtn?: HTMLButtonElement;
446
+ @query('.form-field', { root: 'shadow' }) formField?: HTMLElement;
447
+ @query(':host') self?: HTMLElement;
448
+
449
+ @onConnectedInnerHtml({ useShadow: true })
450
+ render() {
451
+ return `
452
+ <div class="form-field">
453
+ <input type="text" placeholder="Enter name">
454
+ <button id="submit-btn">Submit</button>
455
+ </div>
456
+ `;
457
+ }
458
+ }
459
+ ```
460
+
461
+ **Root Options:**
462
+ - `'auto'` (default) - Search shadow DOM first, then light DOM
463
+ - `'light'` - Search only light DOM
464
+ - `'shadow'` - Search only shadow DOM
465
+ - `'all'` - Search both, return `[lightElements, shadowElements]`
466
+
467
+ **Special Targets:**
468
+ ```typescript
469
+ @query(':host') self?: HTMLElement; // Element itself
470
+ @query(':parentHost') parent?: HTMLElement; // Parent SWC component
471
+ @query(':appHost') app?: SwcAppInterface; // Nearest <swc-app>
472
+ @query(':window') win?: Window; // Global window
473
+ @query(':document') doc?: Document; // Global document
474
+ ```
475
+
476
+ #### 14. `@queryAll(selector, options?)`
477
+ Inject multiple elements as an array.
478
+
479
+ ```typescript
480
+ @elementDefine('list-renderer')
481
+ class ListRenderer extends HTMLElement {
482
+ @queryAll('.item') items?: HTMLElement[];
483
+ @queryAll('button', { root: 'shadow' }) buttons?: HTMLButtonElement[];
484
+
485
+ getItemCount() {
486
+ return this.items?.length ?? 0;
487
+ }
488
+ }
489
+ ```
490
+
491
+ #### 14b. Removing Elements via `null` or `undefined`
492
+ Both `@query` and `@queryAll` support element removal by setting the property to `null` or `undefined`. This triggers automatic removal of the matching element(s).
493
+
494
+ ```typescript
495
+ @elementDefine('todo-list')
496
+ class TodoList extends HTMLElement {
497
+ @query('.notification') notification?: HTMLElement;
498
+ @queryAll('.todo-item') todoItems?: HTMLElement[];
499
+
500
+ removeNotification() {
501
+ // Setting to null triggers automatic removal
502
+ this.notification = null;
503
+ }
504
+
505
+ removeTodoItem(id: string) {
506
+ // Find and remove a specific todo item
507
+ const item = this.querySelector(`[data-id="${id}"]`);
508
+ if (item) {
509
+ // Setting @query to null removes the element from DOM
510
+ this.notification = null;
511
+ }
512
+ }
513
+
514
+ clearAllTodos() {
515
+ // Setting @queryAll to null/undefined removes all matching elements
516
+ this.todoItems = null; // Removes all .todo-item elements
517
+ }
518
+
519
+ @onConnectedInnerHtml
520
+ render() {
521
+ return `
522
+ <div class="notification">New todos available!</div>
523
+ <ul>
524
+ <li class="todo-item" data-id="1">Task 1</li>
525
+ <li class="todo-item" data-id="2">Task 2</li>
526
+ <li class="todo-item" data-id="3">Task 3</li>
527
+ </ul>
528
+ <button swc-on-click="$host.removeNotification()">Clear Notification</button>
529
+ <button swc-on-click="$host.clearAllTodos()">Clear All Tasks</button>
530
+ `;
531
+ }
532
+ }
533
+ ```
534
+
535
+ **Removal Rules:**
536
+ - `@query`: Setting to `null` or `undefined` removes the single element
537
+ - `@queryAll`: Setting to `null` or `undefined` removes all matching elements
538
+ - Special selectors (`:host`, `:window`, `:document`, etc.) are **NOT removed** (protected)
539
+ - Only affects Light DOM by default; use `{ root: 'shadow' }` to remove from Shadow DOM
540
+
541
+ **Use Cases:**
542
+ - Close notification banners
543
+ - Clear dynamic list items
544
+ - Remove temporary overlays
545
+ - Clean up modal dialogs
546
+
547
+ ---
548
+
549
+ ### Event & Messaging Decorators
550
+
551
+ #### 15. `@addEventListener(selector, event, options?)`
552
+ Declarative event binding with automatic delegation.
553
+
554
+ ```typescript
555
+ @elementDefine('todo-app')
556
+ class TodoApp extends HTMLElement {
557
+ private todos: any[] = [];
558
+
559
+ // Direct binding to specific element
560
+ @addEventListener('#add-btn', 'click')
561
+ onAddTodo(event: MouseEvent) {
562
+ console.log('Add button clicked');
563
+ }
564
+
565
+ // Event delegation for dynamic elements
566
+ @addEventListener('.todo-item', 'click', { delegate: true })
567
+ onTodoClick(event: MouseEvent, hostSet: any, target: HTMLElement) {
568
+ const itemId = target.getAttribute('data-id');
569
+ console.log('Clicked todo:', itemId);
570
+ }
571
+
572
+ // Global window events
573
+ @addEventListener(':window', 'resize', { removeOnDisconnected: true })
574
+ onWindowResize(event: Event) {
575
+ console.log('Window resized');
576
+ }
577
+
578
+ // Parent app events
579
+ @addEventListener(':appHost', 'custom-event')
580
+ onCustomEvent(event: CustomEvent) {
581
+ console.log('Event received:', event.detail);
582
+ }
583
+
584
+ @onConnectedInnerHtml
585
+ render() {
586
+ return `
587
+ <div>
588
+ <input type="text" id="todo-input" placeholder="Add todo...">
589
+ <button id="add-btn">Add</button>
590
+ <ul class="todo-list">
591
+ ${this.todos.map(t => `
592
+ <li class="todo-item" data-id="${t.id}">${t.text}</li>
593
+ `).join('')}
594
+ </ul>
595
+ </div>
596
+ `;
597
+ }
598
+ }
599
+ ```
600
+
601
+ **Features:**
602
+ - ✅ Direct binding to static selectors
603
+ - ✅ Event delegation for dynamic elements
604
+ - ✅ Global events (`:window`, `:document`)
605
+ - ✅ Parent communication (`:parentHost`, `:appHost`)
606
+ - ✅ Auto-cleanup on `disconnectedCallback`
607
+
608
+ #### 16. `@emitCustomEvent(target, type, options?)`
609
+ Emit custom events to component hierarchy or global targets.
610
+
611
+ ```typescript
612
+ @elementDefine('auth-panel')
613
+ class AuthPanel extends HTMLElement {
614
+ constructor(private authService: AuthService) { super(); }
615
+
616
+ // Emit to parent app when user logs in
617
+ @emitCustomEvent(':appHost', 'user-logged-in')
618
+ async onLogin() {
619
+ const user = await this.authService.login();
620
+ return { user, timestamp: Date.now() };
621
+ }
622
+
623
+ // Emit globally when logout happens
624
+ @emitCustomEvent(':window', 'user-logged-out')
625
+ async onLogout() {
626
+ await this.authService.logout();
627
+ return { timestamp: Date.now() };
628
+ }
629
+
630
+ // Emit to direct parent
631
+ @emitCustomEvent(':parentHost', 'child-ready')
632
+ notifyParent() {
633
+ return { childElement: this };
634
+ }
635
+
636
+ @onConnectedInnerHtml
637
+ render() {
638
+ return `
639
+ <div>
640
+ <button swc-on-click="$host.onLogin()">Login</button>
641
+ <button swc-on-click="$host.onLogout()">Logout</button>
642
+ </div>
643
+ `;
644
+ }
645
+ }
646
+
647
+ // Listen to the emitted event
648
+ @elementDefine('user-widget')
649
+ class UserWidget extends HTMLElement {
650
+ private user: any = null;
651
+
652
+ @addEventListener(':appHost', 'user-logged-in')
653
+ onUserLoggedIn(event: CustomEvent) {
654
+ this.user = event.detail.user;
655
+ this.render();
656
+ }
657
+
658
+ @onConnectedInnerHtml
659
+ render() {
660
+ return `<p>User: ${this.user?.name ?? 'Anonymous'}</p>`;
54
661
  }
55
662
  }
56
663
  ```
57
664
 
58
- ### 3. Modern Attribute Management
59
- SWC uses a **Method-Based Reflection** approach. You control exactly how data flows.
665
+ **Emission Targets:**
666
+ - `':appHost'` - Nearest parent `<swc-app>` (contained within app)
667
+ - `':parentHost'` - Direct parent SWC component
668
+ - `':firstHost'` / `':lastHost'` - First/last component in hierarchy
669
+ - `':firstAppHost'` / `':lastAppHost'` - First/last `<swc-app>` in hierarchy
670
+ - `':hosts'` - All SWC ancestors
671
+ - `':appHosts'` - All `<swc-app>` ancestors
672
+ - `':window'` - Global window (all apps can listen)
673
+ - `':document'` - Global document
674
+ - `selector` - CSS selector (emit to matching elements)
675
+
676
+ **Options:**
677
+ - `bubbles: true` (default) - Event bubbles up
678
+ - `composed: true` (default) - Event crosses Shadow DOM boundary
679
+ - `cancelable: true` - Event can be canceled
680
+ - `root: 'auto' | 'light' | 'shadow'` - Where to find target elements
681
+
682
+ ---
60
683
 
61
- **Attribute sync rules**
62
- - Initial sync honors DOM-first semantics: if a markup attribute already exists (e.g., `<my-el count="11">`), the first write keeps that DOM value even when the class defines `count = 0`.
63
- - `connectedInitialize: true` flips the direction for that property so the in-memory value is pushed to the DOM during `connectedCallback`.
64
- - Type conversion is automatic through `convertValue` so `Boolean`, `Number`, and `String` fields always expose typed values while still storing the serialized attribute on the element.
65
- - Getters return `null` when the resolved target exists but the attribute is absent, giving you an explicit signal that the DOM is missing that state.
684
+ ### DOM Manipulation Decorators
66
685
 
67
- - **`@setAttribute(name?, options?)`**: Reflects the method's **return value** to the DOM attribute. Supports `SwcQueryOptions` for root-aware target resolution (light DOM, shadow DOM, or auto-detect).
68
- - **`@changedAttribute(name?)`**: A handler triggered when the attribute changes.
686
+ #### 17. `@applyNode(selector, options?)`
687
+ Surgically add, update, or replace DOM nodes at specific positions.
69
688
 
70
689
  ```typescript
71
- @elementDefine('my-counter')
72
- class MyCounter extends HTMLElement {
690
+ @elementDefine('counter-display')
691
+ class CounterDisplay extends HTMLElement {
73
692
  private count = 0;
74
- @query('#display') displayEl?: HTMLElement;
75
- @query('#shadow-input', { root: 'shadow' }) shadowInput?: HTMLInputElement;
693
+ private errorMessage = '';
694
+
695
+ // Replace all children with new content
696
+ @applyNode('#display', { position: 'replaceChildren' })
697
+ updateDisplayCount() {
698
+ return `<span>${this.count}</span>`;
699
+ }
700
+
701
+ // Append to container
702
+ @applyNode('#history', { position: 'beforeEnd' })
703
+ addHistoryEntry() {
704
+ return `<p>Count was: ${this.count}</p>`;
705
+ }
76
706
 
77
- @setAttribute('count')
78
- updateCount(val: number) { return val; } // Sets 'count' attribute in DOM
707
+ // Set HTML content
708
+ @applyNode('#status', { position: 'innerHtml' })
709
+ setStatus() {
710
+ return `<strong>Status: Active</strong>`;
711
+ }
79
712
 
80
- @setAttribute('shadow-value', { root: 'shadow' })
81
- updateShadowValue(val: string) { return val; } // Sets attribute on shadow DOM element
713
+ // Set text content
714
+ @applyNode('#counter', { position: 'innerText' })
715
+ updateCounter() {
716
+ return this.count.toString();
717
+ }
82
718
 
83
- @changedAttribute('count')
84
- onCountChange(newVal: string, oldVal: string, name: string) {
85
- this.count = Number(newVal);
86
- // Surgical Update: Only update the specific node
87
- if (this.displayEl) this.displayEl.textContent = newVal;
719
+ // Remove element if condition is truthy
720
+ @applyNode('#error-banner', { position: 'remove' })
721
+ dismissError() {
722
+ return this.errorMessage === ''; // Remove if no error
723
+ }
724
+
725
+ @onConnectedInnerHtml
726
+ render() {
727
+ return `
728
+ <div id="display">0</div>
729
+ <div id="counter">0</div>
730
+ <div id="status"></div>
731
+ <button swc-on-click="$host.count++; $host.updateDisplayCount?.()">+</button>
732
+ <div id="history"></div>
733
+ <div id="error-banner" style="display: ${this.errorMessage ? 'block' : 'none'};"></div>
734
+ `;
88
735
  }
89
736
  }
90
737
  ```
91
738
 
92
- ### 4. Powerful Queries (`@query`, `@queryAll`)
93
- Inject internal elements or host instances directly into properties. Assigning `null` or `[]` to these properties will surgically remove the target elements from the DOM.
739
+ **Position Options:**
740
+ - `'beforeEnd'` (default) - Append to end
741
+ - `'afterBegin'` - Prepend to start
742
+ - `'beforeBegin'` - Insert before element
743
+ - `'afterEnd'` - Insert after element
744
+ - `'replace'` - Replace entire element
745
+ - `'replaceChildren'` - Replace all children
746
+ - `'innerHtml'` - Set `innerHTML`
747
+ - `'innerText'` - Set `innerText`
748
+ - `'remove'` - Remove element if returned value is truthy
749
+
750
+ **Root Option:**
751
+ Control which DOM tree is searched with the `root` option:
752
+ - `'auto'` (default) - Search shadow DOM first, then light DOM
753
+ - `'shadow'` - Search only shadow DOM
754
+ - `'light'` - Search only light DOM
755
+ - `'all'` - Search all ancestors
756
+
757
+ #### Convenience Decorators
758
+
759
+ Shorthand decorators for common position-specific operations:
760
+
761
+ **Position-Specific Variants:**
762
+ ```typescript
763
+ @applyBeforeBeginNode('#target') prependTarget() { return `<div>Before</div>`; }
764
+ @applyAfterBeginNode('#target') afterBeginTarget() { return `<div>After Begin</div>`; }
765
+ @applyBeforeEndNode('#target') beforeEndTarget() { return `<div>Before End</div>`; }
766
+ @applyAfterEndNode('#target') afterEndTarget() { return `<div>After</div>`; }
767
+ @applyReplaceNode('#target') replaceTarget() { return `<div>Replaced</div>`; }
768
+ @applyReplaceChildrenNode('#target') replaceChildrenTarget() { return `<div>New Children</div>`; }
769
+ @applyInnerHtmlNode('#target') setInnerHtml() { return `<b>Bold</b>`; }
770
+ @applyInnerTextNode('#target') setInnerText() { return 'Plain text'; }
771
+ @applyRemoveNode('#target') removeTarget() { return true; }
772
+ ```
773
+
774
+ **Host-Targeted Variants (`:host` selector):**
775
+ ```typescript
776
+ @applyBeforeBeginNodeHost() prependHost() { return `<div>Before Host</div>`; }
777
+ @applyAfterBeginNodeHost() afterBeginHost() { return `<div>After Begin</div>`; }
778
+ @applyBeforeEndNodeHost() beforeEndHost() { return `<div>Before End</div>`; }
779
+ @applyAfterEndNodeHost({ root: 'shadow' }) afterEndHost() { return `<div>After Host</div>`; }
780
+ @applyReplaceNodeHost() replaceHost() { return `<div>Replaced Host</div>`; }
781
+ @applyReplaceChildrenNodeHost() replaceChildrenHost() { return `<div>Shadow Children</div>`; }
782
+ @applyInnerHtmlNodeHost() setHostInnerHtml() { return `<b>Host Content</b>`; }
783
+ @applyInnerTextNodeHost() setHostInnerText() { return 'Host text'; }
784
+ @applyRemoveNodeHost() removeHost() { return this.shouldRemove; }
785
+ ```
786
+
787
+ ---
788
+
789
+ #### 18. `@setStyle(selector, options?)` / `@updateStyle(selector, options?)` / `@removeStyle(selector, options?)`
790
+
791
+ Manipulate CSS styles on elements with full Shadow DOM support.
792
+
793
+ **Core Features:**
794
+ - `@setStyle` - Set inline style (replaces all)
795
+ - `@updateStyle` - Merge with existing styles
796
+ - `@removeStyle` - Remove specific properties
797
+ - `root` option for Shadow DOM / Light DOM targeting
798
+ - Support for multiple elements with `querySelectorAll`
799
+
800
+ ```typescript
801
+ @elementDefine('styled-component')
802
+ class StyledComponent extends HTMLElement {
803
+ private isActive = false;
804
+
805
+ @setStyle('.button', { root: 'shadow' })
806
+ setPrimaryButtonStyle() {
807
+ return {
808
+ backgroundColor: '#0066cc',
809
+ color: 'white',
810
+ padding: '10px 20px',
811
+ border: 'none',
812
+ borderRadius: '4px'
813
+ };
814
+ }
815
+
816
+ @updateStyle('.button', { root: 'shadow' })
817
+ highlightButton() {
818
+ return {
819
+ boxShadow: '0 0 10px rgba(0, 102, 204, 0.5)',
820
+ transform: 'scale(1.05)'
821
+ };
822
+ }
823
+
824
+ @removeStyle('.button', { root: 'shadow' })
825
+ clearHighlight() {
826
+ return ['boxShadow', 'transform'];
827
+ }
828
+
829
+ @onConnectedInnerHtml({ useShadow: true })
830
+ render() {
831
+ return `
832
+ <style>
833
+ .button { cursor: pointer; transition: all 0.2s; }
834
+ </style>
835
+ <button class="button">Styled</button>
836
+ `;
837
+ }
838
+ }
839
+ ```
840
+
841
+ **Host-Targeted Variants:**
842
+ ```typescript
843
+ @setStyleHost({ root: 'shadow' })
844
+ setHostStyle() {
845
+ return { display: 'flex', gap: '10px' };
846
+ }
847
+
848
+ @updateStyleHost({ root: 'shadow' })
849
+ updateHostStyle() {
850
+ return { backgroundColor: '#f0f0f0' };
851
+ }
852
+
853
+ @removeStyleHost({ root: 'shadow' })
854
+ clearHostStyle() {
855
+ return ['backgroundColor'];
856
+ }
857
+ ```
858
+
859
+ ---
860
+
861
+ #### 19. `@setClassList(selector, options?)` / `@addClassList(selector, options?)` / `@removeClassList(selector, options?)` / `@toggleClassList(selector, options?)` / `@updateClassList(selector, options?)`
862
+
863
+ Manipulate CSS classes on elements declaratively with Shadow DOM support.
864
+
865
+ **Core Features:**
866
+ - `@addClassList` - Add classes
867
+ - `@removeClassList` - Remove classes
868
+ - `@toggleClassList` - Toggle classes (returns boolean or bool array)
869
+ - `@setClassList` - Replace all classes
870
+ - `@updateClassList` - Merge with existing classes
871
+ - `root` option for DOM layer targeting
872
+ - Support for multiple elements
873
+
874
+ ```typescript
875
+ @elementDefine('tab-component')
876
+ class TabComponent extends HTMLElement {
877
+ activeTab = 'home';
878
+
879
+ @addClassList('.tab', { root: 'shadow' })
880
+ addTabClasses() {
881
+ return ['interactive', 'focusable'];
882
+ }
883
+
884
+ @toggleClassList('.tab', { root: 'shadow' })
885
+ toggleActiveTab() {
886
+ return this.activeTab === 'home'; // Returns boolean for all matched elements
887
+ }
888
+
889
+ @removeClassList('.tab-inactive', { root: 'shadow' })
890
+ removeInactiveClass() {
891
+ return ['disabled'];
892
+ }
893
+
894
+ @setClassList('.tab-pane', { root: 'shadow' })
895
+ updateTabPane() {
896
+ return [`pane-${this.activeTab}`, 'content'];
897
+ }
898
+
899
+ @onConnectedInnerHtml({ useShadow: true })
900
+ render() {
901
+ return `
902
+ <style>
903
+ .tab { cursor: pointer; padding: 10px; }
904
+ .tab.active { border-bottom: 2px solid blue; }
905
+ </style>
906
+ <div class="tabs">
907
+ <div class="tab" swc-on-click="$host.activeTab='home'; $host.toggleActiveTab?.();">Home</div>
908
+ <div class="tab" swc-on-click="$host.activeTab='about'; $host.toggleActiveTab?.();">About</div>
909
+ </div>
910
+ <div class="tab-pane">Content here</div>
911
+ `;
912
+ }
913
+ }
914
+ ```
915
+
916
+ **Host-Targeted Variants:**
917
+ ```typescript
918
+ @addClassListHost({ root: 'shadow' })
919
+ addHostClasses() {
920
+ return ['component', 'interactive'];
921
+ }
922
+
923
+ @removeClassListHost({ root: 'shadow' })
924
+ removeHostClasses() {
925
+ return ['loading'];
926
+ }
927
+
928
+ @toggleClassListHost({ root: 'shadow' })
929
+ toggleHostClass() {
930
+ return this.isOpen; // Apply 'open' class if true
931
+ }
932
+
933
+ @setClassListHost({ root: 'shadow' })
934
+ setHostClasses() {
935
+ return this.isOpen ? ['open', 'expanded'] : ['closed'];
936
+ }
937
+
938
+ @updateClassListHost({ root: 'shadow' })
939
+ updateHostClasses() {
940
+ return ['visible'];
941
+ }
942
+ ```
943
+
944
+ ---
945
+
946
+ ### Dependency Injection & Singleton
947
+
948
+ #### 20. `@Sim`
949
+ Marks a class as a **Singleton** in the `<swc-app>` DI container for **constructor injection**.
950
+
951
+ **Two DI Mechanisms:**
952
+
953
+ 1. **Lifecycle DI** (`@onBeforeConnected`, etc.) - ❌ NO `@Sim` needed
954
+ - Works on any `@elementDefine` component
955
+ - Services injected via method parameters
956
+ - Can be used outside `<swc-app>`
957
+
958
+ 2. **Constructor DI** - ✅ `@Sim` REQUIRED
959
+ - Only works inside `<swc-app>` boundary
960
+ - Both provider AND consumer need `@Sim`
961
+ - Singleton pattern within the app
962
+
963
+ **Example: Both Approaches**
964
+
965
+ ```typescript
966
+ // ============ SERVICE ============
967
+ @Sim
968
+ class CartService {
969
+ private items: any[] = [];
970
+
971
+ addItem(item: any) {
972
+ this.items.push(item);
973
+ }
974
+
975
+ getItems() {
976
+ return this.items;
977
+ }
978
+ }
979
+
980
+ // ============ APPROACH 1: Lifecycle DI (NO @Sim needed) ============
981
+ @elementDefine('cart-header') // ← NO @Sim
982
+ class CartHeader extends HTMLElement {
983
+ #cartService: CartService;
984
+
985
+ @onBeforeConnected
986
+ async setup(cartService: CartService) { // ← Injected here
987
+ this.#cartService = cartService;
988
+ await this.#cartService.load?.();
989
+ }
990
+
991
+ @onConnectedInnerHtml
992
+ render() {
993
+ return `<div>Cart: ${this.#cartService?.getItems().length}</div>`;
994
+ }
995
+ }
996
+
997
+ // ============ APPROACH 2: Constructor DI (NEEDS @Sim) ============
998
+ @Sim // ← Required for constructor injection
999
+ @elementDefine('cart-component')
1000
+ class CartComponent extends HTMLElement {
1001
+ // Constructor injection - CartService is auto-injected
1002
+ constructor(private cartService: CartService) { super(); }
1003
+
1004
+ @onConnectedInnerHtml
1005
+ render() {
1006
+ const itemCount = this.cartService.getItems().length;
1007
+ return `<div>Items: ${itemCount}</div>`;
1008
+ }
1009
+ }
1010
+
1011
+ // ============ ROUTING & BOOTSTRAP ============
1012
+ @Sim
1013
+ @Router({
1014
+ path: '/',
1015
+ route: {
1016
+ '/home': HomePage,
1017
+ '/cart': CartComponent // Constructor DI works here
1018
+ }
1019
+ })
1020
+ @elementDefine('app-router')
1021
+ class AppRouter extends HTMLElement {
1022
+ @onConnectedInnerHtml
1023
+ render() {
1024
+ return `<div><slot></slot></div>`;
1025
+ }
1026
+ }
1027
+
1028
+ // ============ HTML ============
1029
+ <!-- Lifecycle DI works OUTSIDE <swc-app> -->
1030
+ <cart-header></cart-header>
1031
+
1032
+ <!-- Constructor DI works INSIDE <swc-app> -->
1033
+ <swc-app swc-get-application-config="appConfig()">
1034
+ <app-router></app-router>
1035
+ </swc-app>
1036
+
1037
+ <script>
1038
+ window.appConfig = () => ({
1039
+ rootRouter: AppRouter,
1040
+ routeType: 'element',
1041
+ path: '/'
1042
+ });
1043
+ </script>
1044
+ ```
1045
+
1046
+ **When to Use Each:**
1047
+
1048
+ | Scenario | Use | Mark |
1049
+ |----------|-----|------|
1050
+ | Component needs services anywhere | Lifecycle DI in `@onBeforeConnected` | NO `@Sim` |
1051
+ | Component inside `<swc-app>` route | Constructor DI | YES `@Sim` |
1052
+ | Service/Singleton registration | Register in DI container | YES `@Sim` |
1053
+ | Standalone component | Lifecycle DI | NO `@Sim` |
1054
+
1055
+ **Key Points:**
1056
+ - `@Sim` = Mark as singleton for **constructor injection inside `<swc-app>`**
1057
+ - Lifecycle DI = Inject via method parameters (works everywhere, no `@Sim`)
1058
+ - Each `<swc-app>` has its own isolated DI container
1059
+ - Services are local to their `<swc-app>` instance (for constructor DI)
1060
+
1061
+ ---
1062
+
1063
+ ## Core Decorators
94
1064
 
95
1065
  #### Root Options
96
1066
  The `root` option controls which DOM tree is searched:
@@ -117,7 +1087,7 @@ Standard CSS selectors support precise root control.
117
1087
  @query('.btn', { root: 'shadow' }) shadowBtn!: HTMLButtonElement;
118
1088
  ```
119
1089
 
120
- ### 5. Smart Event Binding (`@addEventListener`)
1090
+ ### 5. Event Binding (`@addEventListener`)
121
1091
 
122
1092
  Bind events declaratively with built-in **Event Delegation**, **Auto-Cleanup**, and **Context Injection**. Events flow through the component hierarchy with full access to parent scopes.
123
1093
 
@@ -128,6 +1098,43 @@ Bind events declaratively with built-in **Event Delegation**, **Auto-Cleanup**,
128
1098
  - **Context Injection**: Receive `$host`, `$parentHost`, `$appHost` automatically
129
1099
  - **Special Targets**: `:window`, `:document`, `:parentHost`, `:appHost`
130
1100
 
1101
+ #### 6. Surgical DOM Manipulation (`@applyNode`)
1102
+
1103
+ Surgically add, prepend, insert, or replace nodes with a single decorator. Perfect for dynami content updates without imperatively manipulating the DOM.
1104
+
1105
+ ```typescript
1106
+ @elementDefine('list-manager')
1107
+ class ListManager extends HTMLElement {
1108
+ @applyNode('#list') addItem() {
1109
+ return '<li>New Item</li>';
1110
+ }
1111
+
1112
+ @applyNode('#header', { position: 'afterBegin' }) prependTitle() {
1113
+ return '<h1>Title</h1>';
1114
+ }
1115
+
1116
+ @applyNode('#item', { position: 'replace' }) replaceItem() {
1117
+ return '<div>Replaced Content</div>';
1118
+ }
1119
+ }
1120
+ ```
1121
+
1122
+ **Positions:**
1123
+ - `'beforeEnd'` (default): Append to end of target
1124
+ - `'afterBegin'`: Prepend to start of target
1125
+ - `'beforeBegin'`: Insert before target
1126
+ - `'afterEnd'`: Insert after target
1127
+ - `'replace'`: Replace entire target element
1128
+ - `'replaceChildren'`: Replace all children of target element
1129
+ - `'innerHtml'`: Set target's `innerHTML` with string content
1130
+ - `'innerText'`: Set target's `innerText` with string content
1131
+
1132
+ ---
1133
+
1134
+ ### 6. DOM Manipulation (`@applyNode`)
1135
+
1136
+ Surgically add, prepend, insert, or replace nodes. Perfect for reactive updates without full re-renders.
1137
+
131
1138
  #### Pattern 1: Direct Binding & Static Selectors
132
1139
 
133
1140
  ```typescript
@@ -257,17 +1264,11 @@ class ResponsiveLayout extends HTMLElement {
257
1264
  }
258
1265
  ```
259
1266
 
260
- ### 6. `@replaceChildren(selector)`
1267
+ ### 7. Dynamic Content Updates with `@applyNode`
261
1268
 
262
- Surgically replaces the children of a target element. Perfect for dynamic content updates without full-page re-renders.
263
-
264
- **Use Cases:**
265
- - Search results updating without page reload
266
- - Filtering product lists
267
- - Content swaps in tabbed interfaces
268
- - Live data refreshes
1269
+ #### Advanced Patterns
269
1270
 
270
- #### Pattern 1: Simple Content Replacement
1271
+ **Pattern 1: Simple Content Replacement with `replaceChildren`**
271
1272
 
272
1273
  ```typescript
273
1274
  @Sim
@@ -326,7 +1327,21 @@ class SearchResults extends HTMLElement {
326
1327
  }
327
1328
  ```
328
1329
 
329
- #### Pattern 2: Tab Content Switching
1330
+ **Pattern 2: Setting HTML Content**
1331
+
1332
+ ```typescript
1333
+ @applyNode('#message', { position: 'innerHtml' })
1334
+ displayMessage(text: string) {
1335
+ return `<p style="color: blue;">${text}</p>`;
1336
+ }
1337
+
1338
+ @applyNode('#count', { position: 'innerText' })
1339
+ updateCounter() {
1340
+ return this.count.toString();
1341
+ }
1342
+ ```
1343
+
1344
+ **Pattern 3: Tab Content Switching
330
1345
 
331
1346
  ```typescript
332
1347
  @Sim
@@ -370,7 +1385,7 @@ class TabbedPanel extends HTMLElement {
370
1385
  }
371
1386
  ```
372
1387
 
373
- ### 7. `@appendChild(selector, options?)`
1388
+ ### 8. `@appendChild(selector, options?)`
374
1389
 
375
1390
  Appends or inserts the method's return value relative to a target element. Perfect for dynamically building lists, stacking notifications, or adding children without replacing existing content.
376
1391
 
@@ -577,6 +1592,26 @@ class TextRenderer extends HTMLElement {
577
1592
  }
578
1593
  ```
579
1594
 
1595
+ ### 2b. `commentNode` - HTML Comments
1596
+
1597
+ Create HTML comment nodes (useful for debugging, markers, or template documentation).
1598
+
1599
+ ```typescript
1600
+ @elementDefine('debug-component')
1601
+ class DebugComponent extends HTMLElement {
1602
+ @appendChild('#debug-area')
1603
+ addDebugComment(message: string) {
1604
+ // Create HTML comment node
1605
+ return commentNode`DEBUG: ${message} at ${new Date().toISOString()}`;
1606
+ }
1607
+
1608
+ @onConnectedInnerHtml
1609
+ render() {
1610
+ return `<div id="debug-area"></div>`;
1611
+ }
1612
+ }
1613
+ ```
1614
+
580
1615
  ### 3. Element Factories (`html***Element`)
581
1616
 
582
1617
  Specific factories for common tags. Each returns a **typed element instance**.
@@ -767,66 +1802,49 @@ Unlike traditional SPAs that force a global router and monolithic state tree, SW
767
1802
  - **Decentralized DI**: Service dependencies are resolved within app scope, enabling natural code splitting and lazy loading
768
1803
  - **Island Architecture**: Apps boot only when needed, reducing initial JavaScript payload
769
1804
 
1805
+ ### ✨ Complete Commerce App Example
1806
+
1807
+ See the **[Commerce Example](examples/commerce/)** for a production-ready SPA implementation featuring:
1808
+
1809
+ - **Multi-page routing** (Home, Product Detail, Cart, Orders)
1810
+ - **Service layer** (ProductService, CartService, OrderService) with localStorage persistence
1811
+ - **Reactive state management** using BehaviorSubject
1812
+ - **Modern UI design** with gradient backgrounds, responsive layouts, and sticky header
1813
+ - **Product images** from dynamic CDN (picsum.photos)
1814
+ - **DI integration** with `@onBeforeConnected` lifecycle hooks
1815
+ - **Shadow DOM** for style encapsulation
1816
+ - **Real-time updates** with cart count synchronization
1817
+
1818
+ ```bash
1819
+ cd packages/@dooboostore/simple-web-component/examples/commerce
1820
+ pnpm install
1821
+ pnpm build
1822
+ ```
1823
+
1824
+ This example demonstrates all decorators working together in a real-world e-commerce scenario.
1825
+
770
1826
  ---
771
1827
 
772
- ### 1. Isolated App Scopes & Micro-Frontend Pattern
1828
+ ### Advanced Patterns
1829
+
1830
+ #### Isolated App Scopes & Micro-Frontend Pattern
773
1831
 
774
- Create a self-contained universe with `<swc-app>`. The tag acts as a **boundary** where routing, DI, and state are completely isolated from sibling apps.
1832
+ Create self-contained apps with `<swc-app>` where routing and DI are completely isolated:
775
1833
 
776
1834
  ```html
777
- <!-- Example: Multi-tenant dashboard with independent feature modules -->
778
1835
  <div class="dashboard">
779
- <!-- User Management Module -->
780
1836
  <swc-app swc-get-application-config="createUserModule()"></swc-app>
781
-
782
- <!-- Reports Module -->
783
1837
  <swc-app swc-get-application-config="createReportsModule()"></swc-app>
784
-
785
- <!-- Settings Module -->
786
- <swc-app swc-get-application-config="createSettingsModule()"></swc-app>
787
1838
  </div>
788
-
789
- <script>
790
- // Each module is bootstrapped independently
791
- window.createUserModule = (host) => ({
792
- rootRouter: UserManagementRouter, // Entry point router
793
- routeType: 'element', // 'element' (in-memory), 'hash', or 'path'
794
- path: '/dashboard', // Initial route
795
- window: window // Optional: SSR virtual window
796
- });
797
-
798
- window.createReportsModule = (host) => ({
799
- rootRouter: ReportsRouter,
800
- routeType: 'element',
801
- path: '/overview',
802
- window: window
803
- });
804
-
805
- // External programmatic navigation (from parent scope)
806
- function switchUserModule(userId) {
807
- const userApp = document.querySelectorAll('swc-app')[0];
808
- userApp.routing(`/user/${userId}`); // Navigate within the isolated app
809
- }
810
-
811
- // Inter-app communication (if needed)
812
- function notifyModules(eventName, data) {
813
- document.querySelectorAll('swc-app').forEach(app => {
814
- app.dispatchEvent(new CustomEvent(eventName, { detail: data }));
815
- });
816
- }
817
- </script>
818
1839
  ```
819
1840
 
820
- **Key Insights:**
821
- - Each `<swc-app>` is **completely isolated**: one app's router state doesn't affect another
822
- - No global namespace pollution: services, routes, and components live within app scope
823
- - Perfect for **dashboard layouts, plugin architectures, and federated frontends**
1841
+ Each app is independent: state, services, and routes don't cross boundaries.
824
1842
 
825
1843
  ---
826
1844
 
827
- ### 2. Advanced DI-Powered Routing with Lifecycle Hooks
1845
+ #### DI-Powered Routing with Lifecycle Hooks
828
1846
 
829
- SWC routing leverages **Dependency Injection** to inject services directly into components, enabling dynamic route guards, permission checks, and data preloading.
1847
+ SWC routing injects services directly into components using `canActivate` lifecycle hook for permission checks and data preloading:
830
1848
 
831
1849
  ```typescript
832
1850
  // ============================================