@dooboostore/simple-web-component 1.0.21 → 1.0.22

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 (151) hide show
  1. package/README.md +771 -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/{appendChild.js → applyNode.js} +25 -17
  5. package/dist/cjs/decorators/applyNode.js.map +7 -0
  6. package/dist/cjs/decorators/attribute.js +31 -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 +11 -1
  11. package/dist/cjs/decorators/classList.js.map +2 -2
  12. package/dist/cjs/decorators/elementDefine.js +106 -55
  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 -3
  17. package/dist/cjs/decorators/index.js.map +2 -2
  18. package/dist/cjs/decorators/lifecycles.js +4 -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/elements/SwcAppEngine.js.map +2 -2
  27. package/dist/cjs/elements/SwcAppMixin.js.map +2 -2
  28. package/dist/cjs/elements/SwcAsync.js +2 -2
  29. package/dist/cjs/elements/SwcAsync.js.map +2 -2
  30. package/dist/cjs/elements/SwcChoose.js +2 -2
  31. package/dist/cjs/elements/SwcChoose.js.map +2 -2
  32. package/dist/cjs/elements/SwcIf.js +2 -2
  33. package/dist/cjs/elements/SwcIf.js.map +2 -2
  34. package/dist/cjs/elements/SwcLoop.js +2 -2
  35. package/dist/cjs/elements/SwcLoop.js.map +2 -2
  36. package/dist/cjs/elements/register.js +6 -5
  37. package/dist/cjs/elements/register.js.map +2 -2
  38. package/dist/cjs/index.js +6 -18
  39. package/dist/cjs/index.js.map +2 -2
  40. package/dist/cjs/types.js.map +2 -2
  41. package/dist/esm/decorators/addEventListener.js +4 -0
  42. package/dist/esm/decorators/addEventListener.js.map +2 -2
  43. package/dist/esm/decorators/{appendChild.js → applyNode.js} +22 -14
  44. package/dist/esm/decorators/applyNode.js.map +7 -0
  45. package/dist/esm/decorators/attribute.js +31 -10
  46. package/dist/esm/decorators/attribute.js.map +3 -3
  47. package/dist/esm/decorators/{changedAttribute.js → changedAttributeHost.js} +3 -3
  48. package/dist/esm/decorators/changedAttributeHost.js.map +7 -0
  49. package/dist/esm/decorators/classList.js +11 -1
  50. package/dist/esm/decorators/classList.js.map +2 -2
  51. package/dist/esm/decorators/elementDefine.js +106 -55
  52. package/dist/esm/decorators/elementDefine.js.map +2 -2
  53. package/dist/esm/decorators/emitCustomEvent.js +4 -3
  54. package/dist/esm/decorators/emitCustomEvent.js.map +2 -2
  55. package/dist/esm/decorators/index.js +2 -3
  56. package/dist/esm/decorators/index.js.map +2 -2
  57. package/dist/esm/decorators/lifecycles.js +4 -0
  58. package/dist/esm/decorators/lifecycles.js.map +2 -2
  59. package/dist/esm/decorators/query.js +5 -1
  60. package/dist/esm/decorators/query.js.map +2 -2
  61. package/dist/esm/decorators/queryAll.js +5 -1
  62. package/dist/esm/decorators/queryAll.js.map +2 -2
  63. package/dist/esm/decorators/setAttribute.js +5 -1
  64. package/dist/esm/decorators/setAttribute.js.map +2 -2
  65. package/dist/esm/elements/SwcAppEngine.js.map +2 -2
  66. package/dist/esm/elements/SwcAppMixin.js.map +2 -2
  67. package/dist/esm/elements/SwcAsync.js +2 -2
  68. package/dist/esm/elements/SwcAsync.js.map +2 -2
  69. package/dist/esm/elements/SwcChoose.js +2 -2
  70. package/dist/esm/elements/SwcChoose.js.map +2 -2
  71. package/dist/esm/elements/SwcIf.js +2 -2
  72. package/dist/esm/elements/SwcIf.js.map +2 -2
  73. package/dist/esm/elements/SwcLoop.js +2 -2
  74. package/dist/esm/elements/SwcLoop.js.map +2 -2
  75. package/dist/esm/elements/register.js +6 -5
  76. package/dist/esm/elements/register.js.map +2 -2
  77. package/dist/esm/index.js +6 -18
  78. package/dist/esm/index.js.map +2 -2
  79. package/dist/esm/types.js.map +2 -2
  80. package/dist/esm-bundle/dooboostore-simple-web-component.esm.js +195 -139
  81. package/dist/esm-bundle/dooboostore-simple-web-component.esm.js.map +4 -4
  82. package/dist/types/decorators/addEventListener.d.ts +6 -0
  83. package/dist/types/decorators/addEventListener.d.ts.map +1 -1
  84. package/dist/types/decorators/applyNode.d.ts +38 -0
  85. package/dist/types/decorators/applyNode.d.ts.map +1 -0
  86. package/dist/types/decorators/attribute.d.ts +6 -0
  87. package/dist/types/decorators/attribute.d.ts.map +1 -1
  88. package/dist/types/decorators/{changedAttribute.d.ts → changedAttributeHost.d.ts} +6 -2
  89. package/dist/types/decorators/changedAttributeHost.d.ts.map +1 -0
  90. package/dist/types/decorators/classList.d.ts +25 -0
  91. package/dist/types/decorators/classList.d.ts.map +1 -1
  92. package/dist/types/decorators/elementDefine.d.ts.map +1 -1
  93. package/dist/types/decorators/emitCustomEvent.d.ts +14 -2
  94. package/dist/types/decorators/emitCustomEvent.d.ts.map +1 -1
  95. package/dist/types/decorators/index.d.ts +2 -3
  96. package/dist/types/decorators/index.d.ts.map +1 -1
  97. package/dist/types/decorators/lifecycles.d.ts +2 -0
  98. package/dist/types/decorators/lifecycles.d.ts.map +1 -1
  99. package/dist/types/decorators/query.d.ts +6 -0
  100. package/dist/types/decorators/query.d.ts.map +1 -1
  101. package/dist/types/decorators/queryAll.d.ts +6 -0
  102. package/dist/types/decorators/queryAll.d.ts.map +1 -1
  103. package/dist/types/decorators/setAttribute.d.ts +5 -0
  104. package/dist/types/decorators/setAttribute.d.ts.map +1 -1
  105. package/dist/types/elements/SwcAppEngine.d.ts.map +1 -1
  106. package/dist/types/elements/SwcAppMixin.d.ts.map +1 -1
  107. package/dist/types/elements/register.d.ts +1 -1
  108. package/dist/types/elements/register.d.ts.map +1 -1
  109. package/dist/types/index.d.ts +3 -15
  110. package/dist/types/index.d.ts.map +1 -1
  111. package/dist/types/types.d.ts +2 -1
  112. package/dist/types/types.d.ts.map +1 -1
  113. package/dist/umd-bundle/dooboostore-simple-web-component.umd.js +195 -139
  114. package/dist/umd-bundle/dooboostore-simple-web-component.umd.js.map +4 -4
  115. package/package.json +3 -3
  116. package/src/decorators/addEventListener.ts +9 -0
  117. package/src/decorators/applyNode.ts +127 -0
  118. package/src/decorators/attribute.ts +46 -12
  119. package/src/decorators/{changedAttribute.ts → changedAttributeHost.ts} +5 -1
  120. package/src/decorators/classList.ts +32 -0
  121. package/src/decorators/elementDefine.ts +130 -62
  122. package/src/decorators/emitCustomEvent.ts +13 -5
  123. package/src/decorators/index.ts +2 -3
  124. package/src/decorators/lifecycles.ts +3 -1
  125. package/src/decorators/query.ts +9 -0
  126. package/src/decorators/queryAll.ts +9 -0
  127. package/src/decorators/setAttribute.ts +8 -0
  128. package/src/elements/SwcAppEngine.ts +1 -0
  129. package/src/elements/SwcAppMixin.ts +2 -1
  130. package/src/elements/SwcAsync.ts +2 -2
  131. package/src/elements/SwcChoose.ts +2 -2
  132. package/src/elements/SwcIf.ts +2 -2
  133. package/src/elements/SwcLoop.ts +2 -2
  134. package/src/elements/register.ts +7 -5
  135. package/src/index.ts +8 -17
  136. package/src/types.ts +2 -1
  137. package/dist/cjs/decorators/appendChild.js.map +0 -7
  138. package/dist/cjs/decorators/changedAttribute.js.map +0 -7
  139. package/dist/cjs/decorators/replaceChildren.js +0 -79
  140. package/dist/cjs/decorators/replaceChildren.js.map +0 -7
  141. package/dist/esm/decorators/appendChild.js.map +0 -7
  142. package/dist/esm/decorators/changedAttribute.js.map +0 -7
  143. package/dist/esm/decorators/replaceChildren.js +0 -60
  144. package/dist/esm/decorators/replaceChildren.js.map +0 -7
  145. package/dist/types/decorators/appendChild.d.ts +0 -17
  146. package/dist/types/decorators/appendChild.d.ts.map +0 -1
  147. package/dist/types/decorators/changedAttribute.d.ts.map +0 -1
  148. package/dist/types/decorators/replaceChildren.d.ts +0 -9
  149. package/dist/types/decorators/replaceChildren.d.ts.map +0 -1
  150. package/src/decorators/appendChild.ts +0 -86
  151. package/src/decorators/replaceChildren.ts +0 -64
package/README.md CHANGED
@@ -30,67 +30,715 @@ 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.
161
+
162
+ ```typescript
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.
47
232
 
48
233
  ```typescript
49
- @elementDefine('my-el')
50
- class MyEl extends HTMLElement {
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
+ `;
54
251
  }
55
252
  }
56
253
  ```
57
254
 
58
- ### 3. Modern Attribute Management
59
- SWC uses a **Method-Based Reflection** approach. You control exactly how data flows.
255
+ **Options:**
256
+ - `useShadow: true` - Render to Shadow DOM (style encapsulation)
257
+ - `useShadow: false` - Render to Light DOM (default)
258
+
259
+ ---
60
260
 
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.
261
+ ### Attribute & Property Decorators
66
262
 
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.
263
+ #### 10. `@attributeHost(name, options?)`
264
+ Bidirectional attribute binding with automatic type conversion.
69
265
 
70
266
  ```typescript
71
- @elementDefine('my-counter')
72
- class MyCounter extends HTMLElement {
73
- private count = 0;
74
- @query('#display') displayEl?: HTMLElement;
75
- @query('#shadow-input', { root: 'shadow' }) shadowInput?: HTMLInputElement;
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;
76
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(name, options?)`
316
+ Reflect method's return value to a 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
+
323
+ // Reflect status to DOM attribute
324
+ @setAttribute('data-status')
325
+ getStatus() {
326
+ return this.status;
327
+ }
328
+
329
+ // Reflect computed data to attribute
77
330
  @setAttribute('count')
78
- updateCount(val: number) { return val; } // Sets 'count' attribute in DOM
331
+ getItemCount() {
332
+ return this.getItems().length.toString();
333
+ }
79
334
 
80
- @setAttribute('shadow-value', { root: 'shadow' })
81
- updateShadowValue(val: string) { return val; } // Sets attribute on shadow DOM element
335
+ // Reflect to Shadow DOM element
336
+ @setAttribute('aria-label', { root: 'shadow' })
337
+ getAriaLabel() {
338
+ return `Status is ${this.status}`;
339
+ }
340
+
341
+ private getItems() {
342
+ return [];
343
+ }
344
+
345
+ @onConnectedInnerHtml
346
+ render() {
347
+ return `
348
+ <div class="badge" data-status="${this.status}">
349
+ Status: ${this.status}
350
+ </div>
351
+ `;
352
+ }
82
353
 
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;
354
+ // Change status and update DOM
355
+ setStatus(newStatus: typeof this.status) {
356
+ this.status = newStatus;
357
+ this.getStatus(); // Triggers @setAttribute update
88
358
  }
89
359
  }
90
360
  ```
91
361
 
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.
362
+ **Key Differences:**
363
+ - **`@attributeHost`** - Two-way binding: DOM ↔ Class (Bidirectional)
364
+ - **`@setAttribute`** - One-way binding: Class → DOM (Method return reflects to attribute)
365
+ - **`@changedAttributeHost`** - Watch DOM attribute changes (Reactive handler)
366
+
367
+ **Options:**
368
+ - `root: 'light'` - Set attribute on light DOM element
369
+ - `root: 'shadow'` - Set attribute on shadow DOM element
370
+ - `root: 'auto'` - Auto-detect (default)
371
+
372
+ ---
373
+
374
+ ### Query & DOM Selection Decorators
375
+
376
+ #### 13. `@query(selector, options?)`
377
+ Inject a single element into a property.
378
+
379
+ ```typescript
380
+ @elementDefine('form-handler')
381
+ class FormHandler extends HTMLElement {
382
+ @query('#submit-btn') submitBtn?: HTMLButtonElement;
383
+ @query('.form-field', { root: 'shadow' }) formField?: HTMLElement;
384
+ @query(':host') self?: HTMLElement;
385
+
386
+ @onConnectedInnerHtml({ useShadow: true })
387
+ render() {
388
+ return `
389
+ <div class="form-field">
390
+ <input type="text" placeholder="Enter name">
391
+ <button id="submit-btn">Submit</button>
392
+ </div>
393
+ `;
394
+ }
395
+ }
396
+ ```
397
+
398
+ **Root Options:**
399
+ - `'auto'` (default) - Search shadow DOM first, then light DOM
400
+ - `'light'` - Search only light DOM
401
+ - `'shadow'` - Search only shadow DOM
402
+ - `'all'` - Search both, return `[lightElements, shadowElements]`
403
+
404
+ **Special Targets:**
405
+ ```typescript
406
+ @query(':host') self?: HTMLElement; // Element itself
407
+ @query(':parentHost') parent?: HTMLElement; // Parent SWC component
408
+ @query(':appHost') app?: SwcAppInterface; // Nearest <swc-app>
409
+ @query(':window') win?: Window; // Global window
410
+ @query(':document') doc?: Document; // Global document
411
+ ```
412
+
413
+ #### 14. `@queryAll(selector, options?)`
414
+ Inject multiple elements as an array.
415
+
416
+ ```typescript
417
+ @elementDefine('list-renderer')
418
+ class ListRenderer extends HTMLElement {
419
+ @queryAll('.item') items?: HTMLElement[];
420
+ @queryAll('button', { root: 'shadow' }) buttons?: HTMLButtonElement[];
421
+
422
+ getItemCount() {
423
+ return this.items?.length ?? 0;
424
+ }
425
+ }
426
+ ```
427
+
428
+ ---
429
+
430
+ ### Event & Messaging Decorators
431
+
432
+ #### 15. `@addEventListener(selector, event, options?)`
433
+ Declarative event binding with automatic delegation.
434
+
435
+ ```typescript
436
+ @elementDefine('todo-app')
437
+ class TodoApp extends HTMLElement {
438
+ private todos: any[] = [];
439
+
440
+ // Direct binding to specific element
441
+ @addEventListener('#add-btn', 'click')
442
+ onAddTodo(event: MouseEvent) {
443
+ console.log('Add button clicked');
444
+ }
445
+
446
+ // Event delegation for dynamic elements
447
+ @addEventListener('.todo-item', 'click', { delegate: true })
448
+ onTodoClick(event: MouseEvent, hostSet: any, target: HTMLElement) {
449
+ const itemId = target.getAttribute('data-id');
450
+ console.log('Clicked todo:', itemId);
451
+ }
452
+
453
+ // Global window events
454
+ @addEventListener(':window', 'resize', { removeOnDisconnected: true })
455
+ onWindowResize(event: Event) {
456
+ console.log('Window resized');
457
+ }
458
+
459
+ // Parent app events
460
+ @addEventListener(':appHost', 'custom-event')
461
+ onCustomEvent(event: CustomEvent) {
462
+ console.log('Event received:', event.detail);
463
+ }
464
+
465
+ @onConnectedInnerHtml
466
+ render() {
467
+ return `
468
+ <div>
469
+ <input type="text" id="todo-input" placeholder="Add todo...">
470
+ <button id="add-btn">Add</button>
471
+ <ul class="todo-list">
472
+ ${this.todos.map(t => `
473
+ <li class="todo-item" data-id="${t.id}">${t.text}</li>
474
+ `).join('')}
475
+ </ul>
476
+ </div>
477
+ `;
478
+ }
479
+ }
480
+ ```
481
+
482
+ **Features:**
483
+ - ✅ Direct binding to static selectors
484
+ - ✅ Event delegation for dynamic elements
485
+ - ✅ Global events (`:window`, `:document`)
486
+ - ✅ Parent communication (`:parentHost`, `:appHost`)
487
+ - ✅ Auto-cleanup on `disconnectedCallback`
488
+
489
+ #### 16. `@emitCustomEvent(target, type, options?)`
490
+ Emit custom events to component hierarchy or global targets.
491
+
492
+ ```typescript
493
+ @elementDefine('auth-panel')
494
+ class AuthPanel extends HTMLElement {
495
+ constructor(private authService: AuthService) { super(); }
496
+
497
+ // Emit to parent app when user logs in
498
+ @emitCustomEvent(':appHost', 'user-logged-in')
499
+ async onLogin() {
500
+ const user = await this.authService.login();
501
+ return { user, timestamp: Date.now() };
502
+ }
503
+
504
+ // Emit globally when logout happens
505
+ @emitCustomEvent(':window', 'user-logged-out')
506
+ async onLogout() {
507
+ await this.authService.logout();
508
+ return { timestamp: Date.now() };
509
+ }
510
+
511
+ // Emit to direct parent
512
+ @emitCustomEvent(':parentHost', 'child-ready')
513
+ notifyParent() {
514
+ return { childElement: this };
515
+ }
516
+
517
+ @onConnectedInnerHtml
518
+ render() {
519
+ return `
520
+ <div>
521
+ <button swc-on-click="$host.onLogin()">Login</button>
522
+ <button swc-on-click="$host.onLogout()">Logout</button>
523
+ </div>
524
+ `;
525
+ }
526
+ }
527
+
528
+ // Listen to the emitted event
529
+ @elementDefine('user-widget')
530
+ class UserWidget extends HTMLElement {
531
+ private user: any = null;
532
+
533
+ @addEventListener(':appHost', 'user-logged-in')
534
+ onUserLoggedIn(event: CustomEvent) {
535
+ this.user = event.detail.user;
536
+ this.render();
537
+ }
538
+
539
+ @onConnectedInnerHtml
540
+ render() {
541
+ return `<p>User: ${this.user?.name ?? 'Anonymous'}</p>`;
542
+ }
543
+ }
544
+ ```
545
+
546
+ **Emission Targets:**
547
+ - `':appHost'` - Nearest parent `<swc-app>` (contained within app)
548
+ - `':parentHost'` - Direct parent SWC component
549
+ - `':firstHost'` / `':lastHost'` - First/last component in hierarchy
550
+ - `':firstAppHost'` / `':lastAppHost'` - First/last `<swc-app>` in hierarchy
551
+ - `':hosts'` - All SWC ancestors
552
+ - `':appHosts'` - All `<swc-app>` ancestors
553
+ - `':window'` - Global window (all apps can listen)
554
+ - `':document'` - Global document
555
+ - `selector` - CSS selector (emit to matching elements)
556
+
557
+ **Options:**
558
+ - `bubbles: true` (default) - Event bubbles up
559
+ - `composed: true` (default) - Event crosses Shadow DOM boundary
560
+ - `cancelable: true` - Event can be canceled
561
+ - `root: 'auto' | 'light' | 'shadow'` - Where to find target elements
562
+
563
+ ---
564
+
565
+ ### DOM Manipulation Decorators
566
+
567
+ #### 17. `@applyNode(selector, options?)`
568
+ Surgically add, update, or replace DOM nodes at specific positions.
569
+
570
+ ```typescript
571
+ @elementDefine('counter-display')
572
+ class CounterDisplay extends HTMLElement {
573
+ private count = 0;
574
+
575
+ // Replace all children with new content
576
+ @applyNode('#display', { position: 'replaceChildren' })
577
+ updateDisplayCount() {
578
+ return `<span>${this.count}</span>`;
579
+ }
580
+
581
+ // Append to container
582
+ @applyNode('#history', { position: 'beforeEnd' })
583
+ addHistoryEntry() {
584
+ return `<p>Count was: ${this.count}</p>`;
585
+ }
586
+
587
+ // Set HTML content
588
+ @applyNode('#status', { position: 'innerHtml' })
589
+ setStatus() {
590
+ return `<strong>Status: Active</strong>`;
591
+ }
592
+
593
+ // Set text content
594
+ @applyNode('#counter', { position: 'innerText' })
595
+ updateCounter() {
596
+ return this.count.toString();
597
+ }
598
+
599
+ @onConnectedInnerHtml
600
+ render() {
601
+ return `
602
+ <div id="display">0</div>
603
+ <div id="counter">0</div>
604
+ <div id="status"></div>
605
+ <button swc-on-click="$host.count++; $host.updateDisplayCount?.()">+</button>
606
+ <div id="history"></div>
607
+ `;
608
+ }
609
+ }
610
+ ```
611
+
612
+ **Position Options:**
613
+ - `'beforeEnd'` (default) - Append to end
614
+ - `'afterBegin'` - Prepend to start
615
+ - `'beforeBegin'` - Insert before element
616
+ - `'afterEnd'` - Insert after element
617
+ - `'replace'` - Replace entire element
618
+ - `'replaceChildren'` - Replace all children
619
+ - `'innerHtml'` - Set `innerHTML`
620
+ - `'innerText'` - Set `innerText`
621
+
622
+ ---
623
+
624
+ ### Dependency Injection & Singleton
625
+
626
+ #### 18. `@Sim`
627
+ Marks a class as a **Singleton** in the `<swc-app>` DI container for **constructor injection**.
628
+
629
+ **Two DI Mechanisms:**
630
+
631
+ 1. **Lifecycle DI** (`@onBeforeConnected`, etc.) - ❌ NO `@Sim` needed
632
+ - Works on any `@elementDefine` component
633
+ - Services injected via method parameters
634
+ - Can be used outside `<swc-app>`
635
+
636
+ 2. **Constructor DI** - ✅ `@Sim` REQUIRED
637
+ - Only works inside `<swc-app>` boundary
638
+ - Both provider AND consumer need `@Sim`
639
+ - Singleton pattern within the app
640
+
641
+ **Example: Both Approaches**
642
+
643
+ ```typescript
644
+ // ============ SERVICE ============
645
+ @Sim
646
+ class CartService {
647
+ private items: any[] = [];
648
+
649
+ addItem(item: any) {
650
+ this.items.push(item);
651
+ }
652
+
653
+ getItems() {
654
+ return this.items;
655
+ }
656
+ }
657
+
658
+ // ============ APPROACH 1: Lifecycle DI (NO @Sim needed) ============
659
+ @elementDefine('cart-header') // ← NO @Sim
660
+ class CartHeader extends HTMLElement {
661
+ #cartService: CartService;
662
+
663
+ @onBeforeConnected
664
+ async setup(cartService: CartService) { // ← Injected here
665
+ this.#cartService = cartService;
666
+ await this.#cartService.load?.();
667
+ }
668
+
669
+ @onConnectedInnerHtml
670
+ render() {
671
+ return `<div>Cart: ${this.#cartService?.getItems().length}</div>`;
672
+ }
673
+ }
674
+
675
+ // ============ APPROACH 2: Constructor DI (NEEDS @Sim) ============
676
+ @Sim // ← Required for constructor injection
677
+ @elementDefine('cart-component')
678
+ class CartComponent extends HTMLElement {
679
+ // Constructor injection - CartService is auto-injected
680
+ constructor(private cartService: CartService) { super(); }
681
+
682
+ @onConnectedInnerHtml
683
+ render() {
684
+ const itemCount = this.cartService.getItems().length;
685
+ return `<div>Items: ${itemCount}</div>`;
686
+ }
687
+ }
688
+
689
+ // ============ ROUTING & BOOTSTRAP ============
690
+ @Sim
691
+ @Router({
692
+ path: '/',
693
+ route: {
694
+ '/home': HomePage,
695
+ '/cart': CartComponent // Constructor DI works here
696
+ }
697
+ })
698
+ @elementDefine('app-router')
699
+ class AppRouter extends HTMLElement {
700
+ @onConnectedInnerHtml
701
+ render() {
702
+ return `<div><slot></slot></div>`;
703
+ }
704
+ }
705
+
706
+ // ============ HTML ============
707
+ <!-- Lifecycle DI works OUTSIDE <swc-app> -->
708
+ <cart-header></cart-header>
709
+
710
+ <!-- Constructor DI works INSIDE <swc-app> -->
711
+ <swc-app swc-get-application-config="appConfig()">
712
+ <app-router></app-router>
713
+ </swc-app>
714
+
715
+ <script>
716
+ window.appConfig = () => ({
717
+ rootRouter: AppRouter,
718
+ routeType: 'element',
719
+ path: '/'
720
+ });
721
+ </script>
722
+ ```
723
+
724
+ **When to Use Each:**
725
+
726
+ | Scenario | Use | Mark |
727
+ |----------|-----|------|
728
+ | Component needs services anywhere | Lifecycle DI in `@onBeforeConnected` | NO `@Sim` |
729
+ | Component inside `<swc-app>` route | Constructor DI | YES `@Sim` |
730
+ | Service/Singleton registration | Register in DI container | YES `@Sim` |
731
+ | Standalone component | Lifecycle DI | NO `@Sim` |
732
+
733
+ **Key Points:**
734
+ - `@Sim` = Mark as singleton for **constructor injection inside `<swc-app>`**
735
+ - Lifecycle DI = Inject via method parameters (works everywhere, no `@Sim`)
736
+ - Each `<swc-app>` has its own isolated DI container
737
+ - Services are local to their `<swc-app>` instance (for constructor DI)
738
+
739
+ ---
740
+
741
+ ## Core Decorators
94
742
 
95
743
  #### Root Options
96
744
  The `root` option controls which DOM tree is searched:
@@ -117,7 +765,7 @@ Standard CSS selectors support precise root control.
117
765
  @query('.btn', { root: 'shadow' }) shadowBtn!: HTMLButtonElement;
118
766
  ```
119
767
 
120
- ### 5. Smart Event Binding (`@addEventListener`)
768
+ ### 5. Event Binding (`@addEventListener`)
121
769
 
122
770
  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
771
 
@@ -128,6 +776,43 @@ Bind events declaratively with built-in **Event Delegation**, **Auto-Cleanup**,
128
776
  - **Context Injection**: Receive `$host`, `$parentHost`, `$appHost` automatically
129
777
  - **Special Targets**: `:window`, `:document`, `:parentHost`, `:appHost`
130
778
 
779
+ #### 6. Surgical DOM Manipulation (`@applyNode`)
780
+
781
+ Surgically add, prepend, insert, or replace nodes with a single decorator. Perfect for dynami content updates without imperatively manipulating the DOM.
782
+
783
+ ```typescript
784
+ @elementDefine('list-manager')
785
+ class ListManager extends HTMLElement {
786
+ @applyNode('#list') addItem() {
787
+ return '<li>New Item</li>';
788
+ }
789
+
790
+ @applyNode('#header', { position: 'afterBegin' }) prependTitle() {
791
+ return '<h1>Title</h1>';
792
+ }
793
+
794
+ @applyNode('#item', { position: 'replace' }) replaceItem() {
795
+ return '<div>Replaced Content</div>';
796
+ }
797
+ }
798
+ ```
799
+
800
+ **Positions:**
801
+ - `'beforeEnd'` (default): Append to end of target
802
+ - `'afterBegin'`: Prepend to start of target
803
+ - `'beforeBegin'`: Insert before target
804
+ - `'afterEnd'`: Insert after target
805
+ - `'replace'`: Replace entire target element
806
+ - `'replaceChildren'`: Replace all children of target element
807
+ - `'innerHtml'`: Set target's `innerHTML` with string content
808
+ - `'innerText'`: Set target's `innerText` with string content
809
+
810
+ ---
811
+
812
+ ### 6. DOM Manipulation (`@applyNode`)
813
+
814
+ Surgically add, prepend, insert, or replace nodes. Perfect for reactive updates without full re-renders.
815
+
131
816
  #### Pattern 1: Direct Binding & Static Selectors
132
817
 
133
818
  ```typescript
@@ -257,17 +942,11 @@ class ResponsiveLayout extends HTMLElement {
257
942
  }
258
943
  ```
259
944
 
260
- ### 6. `@replaceChildren(selector)`
261
-
262
- Surgically replaces the children of a target element. Perfect for dynamic content updates without full-page re-renders.
945
+ ### 7. Dynamic Content Updates with `@applyNode`
263
946
 
264
- **Use Cases:**
265
- - Search results updating without page reload
266
- - Filtering product lists
267
- - Content swaps in tabbed interfaces
268
- - Live data refreshes
947
+ #### Advanced Patterns
269
948
 
270
- #### Pattern 1: Simple Content Replacement
949
+ **Pattern 1: Simple Content Replacement with `replaceChildren`**
271
950
 
272
951
  ```typescript
273
952
  @Sim
@@ -326,7 +1005,21 @@ class SearchResults extends HTMLElement {
326
1005
  }
327
1006
  ```
328
1007
 
329
- #### Pattern 2: Tab Content Switching
1008
+ **Pattern 2: Setting HTML Content**
1009
+
1010
+ ```typescript
1011
+ @applyNode('#message', { position: 'innerHtml' })
1012
+ displayMessage(text: string) {
1013
+ return `<p style="color: blue;">${text}</p>`;
1014
+ }
1015
+
1016
+ @applyNode('#count', { position: 'innerText' })
1017
+ updateCounter() {
1018
+ return this.count.toString();
1019
+ }
1020
+ ```
1021
+
1022
+ **Pattern 3: Tab Content Switching
330
1023
 
331
1024
  ```typescript
332
1025
  @Sim
@@ -370,7 +1063,7 @@ class TabbedPanel extends HTMLElement {
370
1063
  }
371
1064
  ```
372
1065
 
373
- ### 7. `@appendChild(selector, options?)`
1066
+ ### 8. `@appendChild(selector, options?)`
374
1067
 
375
1068
  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
1069
 
@@ -767,66 +1460,49 @@ Unlike traditional SPAs that force a global router and monolithic state tree, SW
767
1460
  - **Decentralized DI**: Service dependencies are resolved within app scope, enabling natural code splitting and lazy loading
768
1461
  - **Island Architecture**: Apps boot only when needed, reducing initial JavaScript payload
769
1462
 
1463
+ ### ✨ Complete Commerce App Example
1464
+
1465
+ See the **[Commerce Example](examples/commerce/)** for a production-ready SPA implementation featuring:
1466
+
1467
+ - **Multi-page routing** (Home, Product Detail, Cart, Orders)
1468
+ - **Service layer** (ProductService, CartService, OrderService) with localStorage persistence
1469
+ - **Reactive state management** using BehaviorSubject
1470
+ - **Modern UI design** with gradient backgrounds, responsive layouts, and sticky header
1471
+ - **Product images** from dynamic CDN (picsum.photos)
1472
+ - **DI integration** with `@onBeforeConnected` lifecycle hooks
1473
+ - **Shadow DOM** for style encapsulation
1474
+ - **Real-time updates** with cart count synchronization
1475
+
1476
+ ```bash
1477
+ cd packages/@dooboostore/simple-web-component/examples/commerce
1478
+ pnpm install
1479
+ pnpm build
1480
+ ```
1481
+
1482
+ This example demonstrates all decorators working together in a real-world e-commerce scenario.
1483
+
770
1484
  ---
771
1485
 
772
- ### 1. Isolated App Scopes & Micro-Frontend Pattern
1486
+ ### Advanced Patterns
1487
+
1488
+ #### Isolated App Scopes & Micro-Frontend Pattern
773
1489
 
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.
1490
+ Create self-contained apps with `<swc-app>` where routing and DI are completely isolated:
775
1491
 
776
1492
  ```html
777
- <!-- Example: Multi-tenant dashboard with independent feature modules -->
778
1493
  <div class="dashboard">
779
- <!-- User Management Module -->
780
1494
  <swc-app swc-get-application-config="createUserModule()"></swc-app>
781
-
782
- <!-- Reports Module -->
783
1495
  <swc-app swc-get-application-config="createReportsModule()"></swc-app>
784
-
785
- <!-- Settings Module -->
786
- <swc-app swc-get-application-config="createSettingsModule()"></swc-app>
787
1496
  </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
1497
  ```
819
1498
 
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**
1499
+ Each app is independent: state, services, and routes don't cross boundaries.
824
1500
 
825
1501
  ---
826
1502
 
827
- ### 2. Advanced DI-Powered Routing with Lifecycle Hooks
1503
+ #### DI-Powered Routing with Lifecycle Hooks
828
1504
 
829
- SWC routing leverages **Dependency Injection** to inject services directly into components, enabling dynamic route guards, permission checks, and data preloading.
1505
+ SWC routing injects services directly into components using `canActivate` lifecycle hook for permission checks and data preloading:
830
1506
 
831
1507
  ```typescript
832
1508
  // ============================================