@dooboostore/simple-web-component 1.0.37 → 1.0.39

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 (157) hide show
  1. package/README.md +280 -791
  2. package/dist/cjs/SwcAppEngine.js +3 -0
  3. package/dist/cjs/SwcAppEngine.js.map +2 -2
  4. package/dist/cjs/decorators/addEventListener.js +20 -4
  5. package/dist/cjs/decorators/addEventListener.js.map +2 -2
  6. package/dist/cjs/decorators/applyAttribute.js.map +1 -1
  7. package/dist/cjs/decorators/applyClass.js.map +1 -1
  8. package/dist/cjs/decorators/applyNode.js +104 -35
  9. package/dist/cjs/decorators/applyNode.js.map +2 -2
  10. package/dist/cjs/decorators/applySlot.js +69 -57
  11. package/dist/cjs/decorators/applySlot.js.map +3 -3
  12. package/dist/cjs/decorators/applyStyle.js.map +2 -2
  13. package/dist/cjs/decorators/attribute.js.map +1 -1
  14. package/dist/cjs/decorators/elementDefine.js +65 -42
  15. package/dist/cjs/decorators/elementDefine.js.map +2 -2
  16. package/dist/cjs/decorators/emitCustomEvent.js.map +1 -1
  17. package/dist/cjs/decorators/index.js +1 -0
  18. package/dist/cjs/decorators/index.js.map +2 -2
  19. package/dist/cjs/decorators/lifecycles.js +47 -31
  20. package/dist/cjs/decorators/lifecycles.js.map +3 -3
  21. package/dist/cjs/decorators/publishSwcAppMessage.js.map +2 -2
  22. package/dist/cjs/decorators/query.js +1 -1
  23. package/dist/cjs/decorators/query.js.map +2 -2
  24. package/dist/cjs/decorators/queryAll.js.map +1 -1
  25. package/dist/cjs/decorators/setProperty.js.map +1 -1
  26. package/dist/cjs/decorators/state.js +83 -0
  27. package/dist/cjs/decorators/state.js.map +7 -0
  28. package/dist/cjs/decorators/subscribeSwcAppMessageWhileConnected.js.map +1 -1
  29. package/dist/cjs/decorators/subscribeSwcAppRouteChangeWhileConnected.js.map +2 -2
  30. package/dist/cjs/elements/SwcApp.js +193 -305
  31. package/dist/cjs/elements/SwcApp.js.map +3 -3
  32. package/dist/cjs/elements/SwcAppMixin.js +291 -0
  33. package/dist/cjs/elements/SwcAppMixin.js.map +7 -0
  34. package/dist/cjs/elements/index.js +3 -21
  35. package/dist/cjs/elements/index.js.map +3 -3
  36. package/dist/cjs/index.js +1 -3
  37. package/dist/cjs/index.js.map +2 -2
  38. package/dist/cjs/{types.js → types/index.js} +1 -1
  39. package/dist/cjs/types/index.js.map +7 -0
  40. package/dist/cjs/types/window.d.js.map +1 -1
  41. package/dist/cjs/utils/Utils.js +159 -20
  42. package/dist/cjs/utils/Utils.js.map +3 -3
  43. package/dist/esm/SwcAppEngine.js +3 -0
  44. package/dist/esm/SwcAppEngine.js.map +2 -2
  45. package/dist/esm/decorators/addEventListener.js +20 -4
  46. package/dist/esm/decorators/addEventListener.js.map +2 -2
  47. package/dist/esm/decorators/applyAttribute.js.map +1 -1
  48. package/dist/esm/decorators/applyClass.js.map +1 -1
  49. package/dist/esm/decorators/applyNode.js +104 -35
  50. package/dist/esm/decorators/applyNode.js.map +2 -2
  51. package/dist/esm/decorators/applySlot.js +69 -57
  52. package/dist/esm/decorators/applySlot.js.map +3 -3
  53. package/dist/esm/decorators/applyStyle.js.map +2 -2
  54. package/dist/esm/decorators/attribute.js.map +1 -1
  55. package/dist/esm/decorators/elementDefine.js +69 -46
  56. package/dist/esm/decorators/elementDefine.js.map +2 -2
  57. package/dist/esm/decorators/emitCustomEvent.js.map +1 -1
  58. package/dist/esm/decorators/index.js +1 -0
  59. package/dist/esm/decorators/index.js.map +2 -2
  60. package/dist/esm/decorators/lifecycles.js +47 -31
  61. package/dist/esm/decorators/lifecycles.js.map +3 -3
  62. package/dist/esm/decorators/publishSwcAppMessage.js.map +2 -2
  63. package/dist/esm/decorators/query.js +1 -1
  64. package/dist/esm/decorators/query.js.map +2 -2
  65. package/dist/esm/decorators/queryAll.js.map +1 -1
  66. package/dist/esm/decorators/setProperty.js.map +1 -1
  67. package/dist/esm/decorators/state.js +64 -0
  68. package/dist/esm/decorators/state.js.map +7 -0
  69. package/dist/esm/decorators/subscribeSwcAppMessageWhileConnected.js.map +1 -1
  70. package/dist/esm/decorators/subscribeSwcAppRouteChangeWhileConnected.js.map +2 -2
  71. package/dist/esm/elements/SwcApp.js +195 -307
  72. package/dist/esm/elements/SwcApp.js.map +3 -3
  73. package/dist/esm/elements/SwcAppMixin.js +273 -0
  74. package/dist/esm/elements/SwcAppMixin.js.map +7 -0
  75. package/dist/esm/elements/index.js +2 -7
  76. package/dist/esm/elements/index.js.map +2 -2
  77. package/dist/esm/index.js +2 -4
  78. package/dist/esm/index.js.map +2 -2
  79. package/dist/esm/{types.js → types/index.js} +1 -1
  80. package/dist/esm/types/index.js.map +7 -0
  81. package/dist/esm/utils/Utils.js +159 -20
  82. package/dist/esm/utils/Utils.js.map +3 -3
  83. package/dist/esm-bundle/dooboostore-simple-web-component.esm.js +3448 -2584
  84. package/dist/esm-bundle/dooboostore-simple-web-component.esm.js.map +4 -4
  85. package/dist/types/SwcAppEngine.d.ts +1 -1
  86. package/dist/types/SwcAppEngine.d.ts.map +1 -1
  87. package/dist/types/decorators/addEventListener.d.ts +5 -2
  88. package/dist/types/decorators/addEventListener.d.ts.map +1 -1
  89. package/dist/types/decorators/applyAttribute.d.ts.map +1 -1
  90. package/dist/types/decorators/applyClass.d.ts.map +1 -1
  91. package/dist/types/decorators/applyNode.d.ts +23 -7
  92. package/dist/types/decorators/applyNode.d.ts.map +1 -1
  93. package/dist/types/decorators/applySlot.d.ts +14 -12
  94. package/dist/types/decorators/applySlot.d.ts.map +1 -1
  95. package/dist/types/decorators/applyStyle.d.ts.map +1 -1
  96. package/dist/types/decorators/attribute.d.ts.map +1 -1
  97. package/dist/types/decorators/elementDefine.d.ts.map +1 -1
  98. package/dist/types/decorators/emitCustomEvent.d.ts.map +1 -1
  99. package/dist/types/decorators/index.d.ts +1 -0
  100. package/dist/types/decorators/index.d.ts.map +1 -1
  101. package/dist/types/decorators/lifecycles.d.ts +10 -14
  102. package/dist/types/decorators/lifecycles.d.ts.map +1 -1
  103. package/dist/types/decorators/publishSwcAppMessage.d.ts.map +1 -1
  104. package/dist/types/decorators/query.d.ts +1 -1
  105. package/dist/types/decorators/query.d.ts.map +1 -1
  106. package/dist/types/decorators/queryAll.d.ts.map +1 -1
  107. package/dist/types/decorators/setProperty.d.ts +1 -1
  108. package/dist/types/decorators/setProperty.d.ts.map +1 -1
  109. package/dist/types/decorators/state.d.ts +9 -0
  110. package/dist/types/decorators/state.d.ts.map +1 -0
  111. package/dist/types/decorators/subscribeSwcAppMessageWhileConnected.d.ts.map +1 -1
  112. package/dist/types/decorators/subscribeSwcAppRouteChangeWhileConnected.d.ts +5 -1
  113. package/dist/types/decorators/subscribeSwcAppRouteChangeWhileConnected.d.ts.map +1 -1
  114. package/dist/types/elements/SwcApp.d.ts +35 -6
  115. package/dist/types/elements/SwcApp.d.ts.map +1 -1
  116. package/dist/types/elements/SwcAppMixin.d.ts +405 -0
  117. package/dist/types/elements/SwcAppMixin.d.ts.map +1 -0
  118. package/dist/types/elements/index.d.ts +2 -2
  119. package/dist/types/elements/index.d.ts.map +1 -1
  120. package/dist/types/index.d.ts +1 -1
  121. package/dist/types/index.d.ts.map +1 -1
  122. package/dist/types/{types.d.ts → types/index.d.ts} +11 -11
  123. package/dist/types/types/index.d.ts.map +1 -0
  124. package/dist/types/utils/Utils.d.ts +14 -2
  125. package/dist/types/utils/Utils.d.ts.map +1 -1
  126. package/dist/umd-bundle/dooboostore-simple-web-component.umd.js +3431 -2567
  127. package/dist/umd-bundle/dooboostore-simple-web-component.umd.js.map +4 -4
  128. package/package.json +4 -4
  129. package/src/SwcAppEngine.ts +6 -2
  130. package/src/decorators/addEventListener.ts +21 -5
  131. package/src/decorators/applyAttribute.ts +1 -1
  132. package/src/decorators/applyClass.ts +1 -1
  133. package/src/decorators/applyNode.ts +183 -73
  134. package/src/decorators/applySlot.ts +99 -128
  135. package/src/decorators/applyStyle.ts +4 -4
  136. package/src/decorators/attribute.ts +1 -1
  137. package/src/decorators/elementDefine.ts +112 -60
  138. package/src/decorators/emitCustomEvent.ts +1 -1
  139. package/src/decorators/index.ts +2 -0
  140. package/src/decorators/lifecycles.ts +50 -26
  141. package/src/decorators/publishSwcAppMessage.ts +2 -3
  142. package/src/decorators/query.ts +4 -4
  143. package/src/decorators/queryAll.ts +1 -1
  144. package/src/decorators/setProperty.ts +1 -1
  145. package/src/decorators/state.ts +83 -0
  146. package/src/decorators/subscribeSwcAppMessageWhileConnected.ts +1 -1
  147. package/src/decorators/subscribeSwcAppRouteChangeWhileConnected.ts +4 -3
  148. package/src/elements/SwcApp.ts +239 -441
  149. package/src/elements/SwcAppMixin.ts +328 -0
  150. package/src/elements/index.ts +2 -9
  151. package/src/index.ts +2 -4
  152. package/src/{types.ts → types/index.ts} +13 -10
  153. package/src/types/window.d.ts +3 -0
  154. package/src/utils/Utils.ts +205 -20
  155. package/dist/cjs/types.js.map +0 -7
  156. package/dist/esm/types.js.map +0 -7
  157. package/dist/types/types.d.ts.map +0 -1
package/README.md CHANGED
@@ -30,7 +30,7 @@ Inject services into Web Components using the `@onInitialize` decorator.
30
30
  class Dashboard extends HTMLElement {
31
31
  @onInitialize
32
32
  onconstructor(
33
- @Inject({ symbol: UserService.SYMBOL }) userService: UserService
33
+ @inject(UserService.SYMBOL) userService: UserService
34
34
  ) {
35
35
  this.userService = userService;
36
36
  }
@@ -43,7 +43,7 @@ class Dashboard extends HTMLElement {
43
43
  Render HTML automatically when component connects to DOM.
44
44
 
45
45
  ```typescript
46
- @onConnectedInnerHtml({ useShadow: true })
46
+ @onConnectedShadowDom
47
47
  render() {
48
48
  return `<div>Hello, <span>${this.name}</span>!</div>`;
49
49
  }
@@ -53,7 +53,7 @@ render() {
53
53
  Apply HTML to specific host element.
54
54
 
55
55
  ```typescript
56
- @applyNodeThisInnerHtml({ root: 'light' })
56
+ @applyThisInnerHtmlNodeLightDom
57
57
  updateContent() {
58
58
  return `<p>Updated content</p>`;
59
59
  }
@@ -63,48 +63,204 @@ updateContent() {
63
63
  Replace child nodes with new content.
64
64
 
65
65
  ```typescript
66
- @applyNodeThisReplaceChildren({ root: 'light' })
66
+ @applyNodeThisReplaceChildrenLightDom
67
67
  replaceChildren(node: Node) {
68
68
  return node;
69
69
  }
70
70
  ```
71
+ ### 3.5 **Slot Management with @applySlot**
72
+
73
+ Manage dynamic content insertion into named slots using the `@applySlot` decorator. Slots are placeholders in templates that can be updated dynamically.
71
74
 
72
- ### slot
73
75
  ```typescript
74
- @applySlot('zzz')
75
- nodeSlot!: NodeSlot;
76
+ @elementDefine('content-manager')
77
+ class ContentManager extends HTMLElement {
78
+
76
79
 
77
- @addEventListener('.test-btn', 'click' )
78
- @applySlotReplaceChildren('zzz')
79
- ttt() {
80
- return '<div>zzzzzzzzzz</div>'
81
- }
80
+ // Method: Replace children in slot with HTML
81
+ @addEventListener('.update-btn', 'click')
82
+ @applySlotReplaceChildren('main-content')
83
+ updateContent() {
84
+ return '<div>Updated content here</div>';
85
+ }
82
86
 
83
- @addEventListener('.test-clear-btn', 'click' )
84
- // @applySlot('zzz', {position: 'clear'})
85
- @applySlotClear('zzz')
86
- ttct() {
87
- return true;
88
- }
87
+ // Method: Append HTML to slot
88
+ @applySlotAppendHtml('main-content')
89
+ addMoreContent() {
90
+ return '<p>Additional content</p>';
91
+ }
92
+
93
+ // Method: Append text to slot
94
+ @applySlotAppendText('sidebar')
95
+ addSidebarText() {
96
+ return 'Sidebar text: ' + new Date().toISOString();
97
+ }
89
98
 
90
- @onConnectedAfter
91
- ttttt() {
92
- console.log('vvvvvvvvvvvvvvv??', this.nodeSlot)
93
- this.nodeSlot.appendText('zzz22zzz'+new Date().toISOString())
99
+ // Method: Clear slot content
100
+ @addEventListener('.clear-btn', 'click')
101
+ @applySlotClear('main-content')
102
+ clearContent() {
103
+ return true;
104
+ }
105
+
106
+
107
+
108
+ @onConnectedInnerHtml
109
+ render() {
110
+ return `
111
+ <div>
112
+ <h2>Main Content</h2>
113
+ <!--[[ main-content ]]-->
114
+
115
+ <aside>
116
+ <h3>Sidebar</h3>
117
+ <!--[[ sidebar ]]-->
118
+ </aside>
119
+
120
+ <button class="update-btn">Update Content</button>
121
+ <button class="clear-btn">Clear Content</button>
122
+ </div>
123
+ `;
124
+ }
94
125
  }
95
- @onConnectedInnerHtml
96
- render()
97
- {
98
-
99
- return `
100
- [[ zzz ]]
101
- <button class="test-btn">test222222222222222</button>
102
- <button class="test-clear-btn">clear</button>
103
- [[ zzz ]]
104
- `
126
+ ```
127
+
128
+ **Slot Syntax in Templates:**
129
+ - `<!--[[ slot-id ]]-->` - Named slot placeholder (HTML comment syntax)
130
+
131
+ **@applySlot Decorator Variants:**
132
+ - `@applySlot(id, options)` - Generic slot operation with position option
133
+ - `@applySlotReplaceChildren(id)` - Replace all children with HTML
134
+ - `@applySlotReplaceChildrenText(id)` - Replace all children with text
135
+ - `@applySlotAppendHtml(id)` - Append HTML to slot
136
+ - `@applySlotAppendText(id)` - Append text to slot
137
+ - `@applySlotPrependHtml(id)` - Prepend HTML to slot
138
+ - `@applySlotPrependText(id)` - Prepend text to slot
139
+ - `@applySlotClear(id)` - Clear slot content
140
+
141
+ **Position Options:**
142
+ ```typescript
143
+ export type ApplySlotPosition =
144
+ | 'prependHtml' // Add HTML at beginning
145
+ | 'prependText' // Add text at beginning
146
+ | 'appendHtml' // Add HTML at end
147
+ | 'appendText' // Add text at end
148
+ | 'replaceChildrenHtml' // Replace all with HTML
149
+ | 'replaceChildrenText' // Replace all with text
150
+ | 'clear'; // Clear all content
151
+ ```
152
+
153
+
154
+ ### 3.6 **State Management with @state**
155
+
156
+ Reactive state management with automatic DOM updates when state changes. State values are accessible in templates and scripts using special syntax.
157
+
158
+ ```typescript
159
+ @elementDefine('counter-app')
160
+ class CounterApp extends HTMLElement {
161
+ // Declare reactive state
162
+ @state('count')
163
+ count: number = 0;
164
+
165
+ @state('message')
166
+ message: string = 'Hello';
167
+
168
+ @state('isActive')
169
+ isActive: boolean = false;
170
+
171
+ @addEventListener('button', 'click')
172
+ increment() {
173
+ this.count++; // Triggers automatic DOM update
174
+ }
175
+
176
+ @onConnectedInnerHtml
177
+ render() {
178
+ return `
179
+ <div>
180
+ <!-- HTML directive: render HTML content -->
181
+ <!--[html @message@ ]-->
182
+
183
+ <!-- Text directive: render as text -->
184
+ <!--[text Count: @count@ ]-->
185
+
186
+ <!-- Attribute binding with a: prefix -->
187
+ <div a:title="Count is @count@"></div>
188
+
189
+ <!-- Event binding with e: prefix -->
190
+ <button e:click="@increment@()">Increment (@count@)</button>
191
+
192
+ <!-- Conditional rendering -->
193
+ <div a:style="@isActive@ ? 'color: green' : 'color: red'">
194
+ Status: @isActive@
195
+ </div>
196
+ </div>
197
+ `;
198
+ }
105
199
  }
106
200
  ```
107
201
 
202
+ **State Syntax in Templates:**
203
+
204
+ **Reading State Values:**
205
+ - `@stateName@` - Read state value as text/attribute
206
+ - `@stateName@()` - Call state property as function (if it's a function)
207
+ - `@expression@` - Evaluate expressions with state variables
208
+
209
+ **Template Directives:**
210
+ - `<!--[html @state@ ]-->` - Render state value as HTML
211
+ - `<!--[text @state@ ]-->` - Render state value as text
212
+ - `a:attributeName="@state@"` - Bind state to HTML attributes
213
+ - `e:eventName="@methodName@()"` - Bind events to methods
214
+
215
+ **How @state Works:**
216
+ 1. Declare property with `@state('stateName')`
217
+ 2. When property value changes via `this.count++`, setter is triggered
218
+ 3. Setter automatically applies state context to templates
219
+ 4. All `@stateName@` expressions in templates are re-evaluated
220
+ 5. DOM updates automatically with new values
221
+
222
+ **State Context Available:**
223
+ - `@propertyName@` - Access state properties
224
+ - `@methodName@()` - Call methods
225
+ - `@expression@` - Evaluate JavaScript expressions
226
+ - All state variables available in template expressions
227
+
228
+ **Advanced Example:**
229
+ ```typescript
230
+ @elementDefine('user-profile')
231
+ class UserProfile extends HTMLElement {
232
+ @state('user')
233
+ user = { name: 'Alice', age: 30, email: 'alice@example.com' };
234
+
235
+ @state('isEditing')
236
+ isEditing = false;
237
+
238
+ @addEventListener('.edit-btn', 'click')
239
+ toggleEdit() {
240
+ this.isEditing = !this.isEditing;
241
+ }
242
+
243
+ @onConnectedShadowDom
244
+ render() {
245
+ return `
246
+ <div>
247
+ <!-- Display user info -->
248
+ <h2><!--[text @user.name@ ]--></h2>
249
+ <p a:title="Email: @user.email@">Age: @user.age@</p>
250
+
251
+ <!-- Conditional rendering based on state -->
252
+ <div a:style="@isEditing@ ? 'border: 1px solid blue' : 'border: none'">
253
+ <!--[html @isEditing@ ? '<input type="text" />' : '<span>View Mode</span>' ]-->
254
+ </div>
255
+
256
+ <button e:click="toggleEdit">
257
+ @isEditing@ ? 'Save' : 'Edit'
258
+ </button>
259
+ </div>
260
+ `;
261
+ }
262
+ }
263
+ ```
108
264
 
109
265
  ### 4. **Event Handling**
110
266
 
@@ -211,7 +367,7 @@ listItems?: HTMLLIElement[];
211
367
  ```
212
368
 
213
369
  #### @queryThis
214
- Query the component element itself.
370
+ Query the component element itself. == this
215
371
 
216
372
  ```typescript
217
373
  @queryThis
@@ -222,18 +378,6 @@ printTag() {
222
378
  }
223
379
  ```
224
380
 
225
- #### @queryAllThis
226
- Query all direct children of the component.
227
-
228
- ```typescript
229
- @queryAllThis
230
- children?: HTMLElement[];
231
-
232
- getChildCount() {
233
- return this.children?.length ?? 0;
234
- }
235
- ```
236
-
237
381
  ### 6. **Attribute Binding**
238
382
 
239
383
  #### @attribute
@@ -317,378 +461,6 @@ SWC supports two types of expression syntax for dynamic value evaluation:
317
461
 
318
462
  ---
319
463
 
320
- #### 8.1 **SwcChoose - Multi-Condition Rendering**
321
-
322
- Switch-case style conditional rendering with `swc-when` and `swc-otherwise` sub-templates.
323
-
324
- ```typescript
325
- // Component with conditional rendering logic
326
- @elementDefine('status-display')
327
- class StatusDisplay extends HTMLElement {
328
- @attributeThis('status')
329
- status: string = 'pending';
330
-
331
- @setProperty('#status-template', 'value')
332
- getStatus() {
333
- return this.status;
334
- }
335
-
336
- @onConnectedInnerHtml
337
- render() {
338
- return `
339
- <template id="status-template" is="swc-choose">
340
- <template is="swc-when" test="$value === 'pending'">
341
- <div style="color: orange;">⏳ Processing...</div>
342
- </template>
343
-
344
- <template is="swc-when" test="$value === 'success'">
345
- <div style="color: green;">✓ Completed</div>
346
- </template>
347
-
348
- <template is="swc-when" test="$value === 'error'">
349
- <div style="color: red;">✗ Failed</div>
350
- </template>
351
-
352
- <!-- Fallback -->
353
- <template is="swc-otherwise">
354
- <div style="color: gray;">Unknown status</div>
355
- </template>
356
- </template>
357
- `;
358
- }
359
- }
360
- ```
361
-
362
- **Expression Features:**
363
- - `$value` contains the value passed to `swc-choose`
364
- - `skip-if-same` attribute prevents re-render when same template selected
365
- - **Attribute substitution with `{{= }}`**: `attribute-name="{{= expression }}"` automatically evaluated
366
-
367
- **Attribute Substitution Example ({{= }} usage):**
368
- ```html
369
- <template is="swc-choose">
370
- <template is="swc-when" test="$value?.type === 'user'">
371
- <user-card
372
- data-username="{{= $value?.name }}"
373
- data-email="{{= $value?.email }}"
374
- data-id="{{= $value?.id }}"
375
- ></user-card>
376
- </template>
377
-
378
- <template is="swc-when" value="$value?.type === 'product'">
379
- <product-card
380
- data-product-name="{{= $value?.title }}"
381
- data-price="{{= $value?.price }}"
382
- ></product-card>
383
- </template>
384
- </template>
385
- ```
386
-
387
- ---
388
-
389
- #### 8.2 **SwcIf - Binary Conditional**
390
-
391
- If-then-else rendering with optional `swc-default` fallback.
392
-
393
- ```typescript
394
- @elementDefine('user-profile')
395
- class UserProfile extends HTMLElement {
396
- private isLoggedIn = false;
397
- private userData = { userName: 'Alice', userId: 123 };
398
-
399
- @setProperty('#content', 'value')
400
- checkAuth() {
401
- return this.isLoggedIn;
402
- }
403
-
404
- @onConnectedInnerHtml
405
- render() {
406
- return `
407
- <!-- True condition: renders main template using {{ }} -->
408
- <template id="content" is="swc-if" value="{{ $host.isLoggedIn }}">
409
- <!-- Attribute substitution with {{= }} expressions -->
410
- <user-card
411
- data-username="{{= $host.userData.userName }}"
412
- data-userid="{{= $host.userData.userId }}"
413
- ></user-card>
414
- <div>
415
- <h2>Welcome, {{ $host.userData.userName }}</h2>
416
- <button on-click="logout">Logout</button>
417
- </div>
418
-
419
- <!-- Fallback template: renders if condition is false -->
420
- <template is="swc-default">
421
- <div>
422
- <p>Please log in</p>
423
- <button on-click="login">Login</button>
424
- </div>
425
- </template>
426
- </template>
427
- `;
428
- }
429
- }
430
- ```
431
-
432
- **Expression Features:**
433
- - Binary true/false logic with `{{ }} condition`
434
- - Main template renders when `value` is truthy
435
- - `swc-default` sub-template renders when value is falsy
436
- - Dynamic attribute substitution with `{{= }}` supported
437
- - Access `$host`, `$value`, and other context variables
438
- - `{{= functionCall() }}` executes functions and returns results for attributes
439
-
440
- ---
441
-
442
- #### 8.3 **SwcAsync - Promise/Loading State**
443
-
444
- Automatically handle async operations with loading, success, and error states.
445
-
446
- ```typescript
447
- @elementDefine('data-loader')
448
- class DataLoader extends HTMLElement {
449
- private data: any = null;
450
-
451
- async fetchData() {
452
- return new Promise((resolve, reject) => {
453
- setTimeout(() => {
454
- resolve({ id: 1, name: 'Sample Data', description: 'API response' });
455
- }, 2000);
456
- });
457
- }
458
-
459
- @setProperty('#async-content', 'value')
460
- loadData() {
461
- return this.fetchData();
462
- }
463
-
464
- @onConnectedInnerHtml
465
- render() {
466
- return `
467
- <template id="async-content" is="swc-async" value="{{= $host.fetchData() }}">
468
- <!-- Loading State: Shows while Promise is pending -->
469
- <template is="swc-loading">
470
- <div>🔄 Loading data...</div>
471
- </template>
472
-
473
- <!-- Success State: Shows when Promise resolves -->
474
- <!-- Using {{= }} to extract data from resolved Promise -->
475
- <template is="swc-success">
476
- <data-card
477
- data-id="{{= $value.id }}"
478
- data-name="{{= $value.name }}"
479
- ></data-card>
480
- <div>
481
- <p>Data ID: {{ $value.id }}</p>
482
- <p>Data Name: {{ $value.name }}</p>
483
- <p>Description: {{= $value.description }}</p>
484
- </div>
485
- </template>
486
-
487
- <!-- Error State: Shows if Promise rejects -->
488
- <template is="swc-error">
489
- <div style="color: red;">❌ Failed to load data</div>
490
- </template>
491
- </template>
492
- `;
493
- }
494
- }
495
- ```
496
-
497
- **Expression Features:**
498
- - Promise lifecycle management with `{{ }} conditions`
499
- - `{{= $host.fetchData() }}` - function call expression that executes and returns Promise
500
- - `{{= $value.propertyName }}` - extract properties from resolved value
501
- - `swc-loading` - displays during pending state
502
- - `swc-success` - displays on successful resolution, `$value` contains resolved result
503
- - `swc-error` - displays on Promise rejection, `$value` contains error
504
- - Dynamic attribute substitution with `{{= }}` in all states
505
- - `on-clone-node` and `on-clone-nodes` callbacks for DOM manipulation
506
-
507
- ---
508
-
509
- #### 8.4 **SwcLoop - List Rendering**
510
-
511
- Iterate over arrays and render templates for each item.
512
-
513
- ```typescript
514
- @elementDefine('product-list')
515
- class ProductList extends HTMLElement {
516
- private products = [
517
- { id: 1, name: 'Laptop', price: 1200, category: 'electronics' },
518
- { id: 2, name: 'Phone', price: 800, category: 'electronics' },
519
- { id: 3, name: 'Tablet', price: 500, category: 'electronics' }
520
- ];
521
-
522
- @setProperty('#products-list', 'value')
523
- getProducts() {
524
- return this.products;
525
- }
526
-
527
- @onConnectedInnerHtml
528
- render() {
529
- return `
530
- <template id="products-list" is="swc-loop" value="{{= $host.products }}">
531
- <!-- Loop iteration with {{ }} for display and {{= }} for attributes -->
532
- <div style="margin: 10px; padding: 10px; border: 1px solid #ddd;">
533
- <!-- Using {{ }} for template content rendering -->
534
- <h3>{{ $item.name }}</h3>
535
- <p>Price: {{ $item.price }}</p>
536
- <p>ID: {{ $item.id }} (Index: {{ $index }})</p>
537
-
538
- <!-- Using {{= }} for dynamic attribute substitution -->
539
- <product-card
540
- data-product-id="{{= $item.id }}"
541
- data-product-name="{{= $item.name }}"
542
- data-product-price="{{= $item.price }}"
543
- data-product-category="{{= $item.category }}"
544
- ></product-card>
545
-
546
- <!-- Computed expressions with {{= }} -->
547
- <button data-id="{{= $item.id }}" data-price="{{= $item.price }}">
548
- Add to Cart - {{ $item.price }}
549
- </button>
550
- </div>
551
-
552
- <!-- Empty fallback: renders when array is empty -->
553
- <template is="swc-default">
554
- <p>No products available</p>
555
- </template>
556
- </template>
557
- `;
558
- }
559
- }
560
- ```
561
-
562
- **Context Variables (Available in loop):**
563
- - `$item` - current item in iteration
564
- - `$index` - zero-based index (0, 1, 2, ...)
565
- - `$value` - entire array being iterated
566
- - `$nodes` - cloned DOM nodes
567
- - `$elements` - cloned HTML elements
568
- - `$firstElement` - first element in cloned nodes
569
-
570
- **Expression Features:**
571
- - `{{= $host.products }}` - function call expression that returns the array to iterate
572
- - `{{ $item.propertyName }}` - display item properties using standard `{{ }}`
573
- - `{{= $item.propertyName }}` - extract item properties for attributes using `{{= }}`
574
- - `{{= computedValue }}` - compute values in attributes (e.g., calculations, method calls)
575
- - `swc-default` fallback renders when array is empty
576
- - All cloned attributes support `{{ }}` and `{{= }}` substitution
577
- - `on-clone-node` callback fires for each cloned node
578
- - `on-clone-nodes` callback fires after all nodes cloned
579
-
580
- **Common Patterns:**
581
- ```html
582
- <!-- Template functions with {{= }} -->
583
- <template is="swc-loop" value="{{= filterProducts($host.products) }}">
584
- <!-- Dynamic calculations -->
585
- <span>Total: {{= $item.price * $item.quantity }}</span>
586
-
587
- <!-- Conditional attributes -->
588
- <div class="{{= $item.inStock ? 'available' : 'unavailable' }}"></div>
589
-
590
- <!-- Method calls as attributes -->
591
- <button data-formatted="{{= formatPrice($item.price) }}">
592
- Buy - {{ formatPrice($item.price) }}
593
- </button>
594
- </template>
595
- ```
596
-
597
- **Advanced: replace-wrap-start replace-wrap-end for Custom Expression Syntax**
598
-
599
- Override default `{{ }}` and `{{= }}` syntax with custom wrappers:
600
-
601
- ```typescript
602
- @elementDefine('custom-loop')
603
- class CustomLoop extends HTMLElement {
604
- private items = [{ id: 1, name: 'Item 1' }];
605
-
606
- @onConnectedInnerHtml
607
- render() {
608
- return `
609
- <!-- Using custom wrapper syntax [(= ... )] instead of {{= ... }} -->
610
- <template id="items" is="swc-loop" replace-wrap-start="[(" replace-wrap-end=")]" value="{{= $host.items }}">
611
- <div>
612
- <p>Name: [( $item.name )]</p>
613
- <button data-id="[( $item.id )]">Click me</button>
614
- </div>
615
- </template>
616
- `;
617
- }
618
- }
619
- ```
620
-
621
- **replace-wrap attribute options:**
622
- - Change expression wrapper from default `{#{ }#}` to custom pattern
623
- - Supported on: `SwcLoop`, `SwcChoose`, `SwcIf`, `SwcAsync`, `register()` directives
624
-
625
- ---
626
-
627
- #### 8.5 **Declarative Routing with swc-choose**
628
-
629
- Combine `@subscribeSwcAppRouteChangeWhileConnected` with `swc-choose` for clean, declarative routing without boilerplate.
630
-
631
- ```typescript
632
- @elementDefine('root-router')
633
- class RootRouter extends HTMLElement {
634
- private router: Router;
635
-
636
- @onInitialize
637
- onconstructor(router: Router) {
638
- this.router = router;
639
- }
640
-
641
- @setProperty('#router', 'value')
642
- @subscribeSwcAppRouteChangeWhileConnected(['/', '/users', '/users/{id}', '/products/{id}'])
643
- routeChanged(route: RouterEventType) {
644
- return route;
645
- }
646
-
647
- navigate(path: string): void {
648
- this.router?.go(path);
649
- }
650
-
651
- @onConnectedInnerHtml
652
- render() {
653
- return `
654
- <template id="router" is="swc-choose">
655
- <template is="swc-when" test="['', '/'].includes($value?.path)">
656
- <home-page/>
657
- </template>
658
-
659
- <!-- Users List Page -->
660
- <template is="swc-when" test="$value?.path === '/users'">
661
- <users-list-page/>
662
- </template>
663
-
664
- <!-- User Detail Page: Using {{= }} for dynamic attributes from route params -->
665
- <template is="swc-when" test="$value?.path.startsWith('/users/')">
666
- <user-detail-page user-id="{{= $value?.pathData?.id }}"></user-detail-page>
667
- </template>
668
-
669
- <!-- Product Detail Page: Using {{= }} for dynamic route parameters -->
670
- <template is="swc-when" test="$value?.path.startsWith('/products/')">
671
- <product-detail-page product-id="{{= $value?.pathData?.id }}"></product-detail-page>
672
- </template>
673
-
674
- <!-- Not Found -->
675
- <template is="swc-otherwise">
676
- <not-found-page/>
677
- </template>
678
- </template>
679
- `;
680
- }
681
- }
682
- ```
683
-
684
- **Benefits:**
685
- - ✅ **No imperative route methods** - routes are declarative templates
686
- - ✅ **Automatic re-rendering** - attributes like `user-id` update automatically
687
- - ✅ **Performance optimization** - use `skip-if-same` on templates to prevent re-renders
688
- - ✅ **Dynamic path parameters** - use `{{= $value?.pathData?.id }}` syntax to inject route params with `{{= }}`
689
- - ✅ **Cleaner DX** - single unified routing pattern
690
- - ✅ **Full expression support** - use `{{= }}` for complex parameter transformations
691
-
692
464
  ### 9. **Component Communication - Message Bus**
693
465
 
694
466
  SWC provides a powerful message bus system for inter-component communication through SwcApp. Components can publish and subscribe to typed messages while connected.
@@ -699,7 +471,7 @@ Subscribe to messages while component is connected to DOM.
699
471
  ```typescript
700
472
  @elementDefine('notification-panel')
701
473
  class NotificationPanel extends HTMLElement {
702
- @subscribeSwcAppMessageWhileConnected()
474
+ @subscribeSwcAppMessageWhileConnected
703
475
  onAnyMessage(message: SwcAppMessage) {
704
476
  console.log('Received message:', message);
705
477
  }
@@ -783,363 +555,50 @@ class ProductDetail extends HTMLElement {
783
555
 
784
556
  ---
785
557
 
786
- ### 10. **Accommodation Pattern**
787
- Factory-based component registration with explicit DI.
558
+ ### 10. **SwcApp - Multiple Element Types with Mixin Pattern**
788
559
 
789
- ```typescript
790
- // Component factory
791
- export default (w: Window) => {
792
- const tagName = 'my-component';
793
- const existing = w.customElements.get(tagName);
794
- if (existing) return tagName;
560
+ SWC provides a flexible Mixin-based architecture for creating SwcApp elements that extend different HTMLElement types. This enables building SPAs with any semantic HTML element as the root.
795
561
 
796
- @elementDefine(tagName, { window: w })
797
- class MyComponent extends w.HTMLElement {
798
- @onInitialize
799
- onconstructor(service: MyService) {
800
- this.service = service;
801
- }
802
- }
803
-
804
- return tagName;
805
- };
806
-
807
- // Boot factory
808
- export default (w: Window, container: symbol) => {
809
- serviceFactories.forEach(s => s(container));
810
- register(w, [...pageFactories, ...componentFactories]);
811
- };
562
+ #### Available SwcApp Variants
812
563
 
813
- // Entry point
814
- const container = Symbol('app');
815
- bootFactory(w, container);
816
- appElement.connect({
817
- container,
818
- window: w,
819
- onEngineStarted: () => {
820
- appElement.innerHTML = '<root-router></root-router>';
821
- }
822
- });
564
+ ```typescript
565
+ import {
566
+ SwcApp, // swc-app (HTMLElement)
567
+ SwcAppBody, // swc-app-body (HTMLBodyElement, is="body")
568
+ SwcAppDiv, // swc-app-div (HTMLDivElement, is="div")
569
+ SwcAppSection, // swc-app-section (HTMLElement, is="section")
570
+ SwcAppMain, // swc-app-main (HTMLElement, is="main")
571
+ SwcAppArticle, // swc-app-article (HTMLElement, is="article")
572
+ SwcAppHeader, // swc-app-header (HTMLElement, is="header")
573
+ SwcAppFooter, // swc-app-footer (HTMLElement, is="footer")
574
+ SwcAppNav, // swc-app-nav (HTMLElement, is="nav")
575
+ SwcAppAside, // swc-app-aside (HTMLElement, is="aside")
576
+ defineSwcAppAll, // Register all SwcApp variants at once
577
+ swcAppFactories // Array of all definition functions
578
+ } from '@dooboostore/simple-web-component';
823
579
  ```
824
580
 
825
- ### 11. **Quickstart - Basic SPA Setup**
581
+ #### Using SwcAppBody (Recommended for SPAs)
826
582
 
827
- Complete example of setting up a production SPA with routing, services, and components.
828
-
829
- #### Step 1. HTML Entry Point
830
583
  ```html
831
584
  <!DOCTYPE html>
832
585
  <html>
833
586
  <head>
834
587
  <meta charset="UTF-8">
835
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
836
588
  <title>My SPA</title>
837
- <!-- Web Components polyfill for older browsers -->
838
- <script src="https://unpkg.com/@ungap/custom-elements"></script>
839
589
  </head>
590
+ <!-- Use is="swc-app-body" for semantic HTML -->
840
591
  <body id="app" is="swc-app-body">
841
- <!-- Root router will render pages here -->
842
592
  <root-router></root-router>
843
593
  </body>
844
594
  <script src="bundle.js"></script>
845
595
  </html>
846
596
  ```
847
597
 
848
- #### Step 2. Services (Dependency Injection)
849
- ```typescript
850
- // services/UserService.ts
851
- import { Sim } from '@dooboostore/simple-boot';
852
- import { ConstructorType } from '@dooboostore/core';
853
-
854
- export namespace UserService {
855
- export const SYMBOL = Symbol('UserService');
856
-
857
- export interface User {
858
- id: number;
859
- name: string;
860
- email: string;
861
- }
862
-
863
- const users: User[] = [
864
- { id: 1, name: 'Alice', email: 'alice@example.com' },
865
- { id: 2, name: 'Bob', email: 'bob@example.com' }
866
- ];
867
- }
868
-
869
- export interface UserService {
870
- getUser(id: number): UserService.User | undefined;
871
- getAllUsers(): UserService.User[];
872
- }
873
-
874
- // Factory function: accepts container and returns service class
875
- export default (container: symbol): ConstructorType<any> => {
876
- @Sim({ symbol: UserService.SYMBOL, container: container })
877
- class UserServiceImpl implements UserService {
878
- getUser(id: number) {
879
- return UserService.users.find(u => u.id === id);
880
- }
881
-
882
- getAllUsers() {
883
- return [...UserService.users];
884
- }
885
- }
886
-
887
- return UserServiceImpl;
888
- };
889
- ```
890
-
891
- ```typescript
892
- // services/index.ts
893
- import userServiceFactory from './UserService';
894
-
895
- export const serviceFactories = [userServiceFactory];
896
- ```
897
-
898
- #### Step 3. Router Configuration (Declarative with swc-choose)
899
- ```typescript
900
- // pages/RootRouter.ts
901
- import { elementDefine, subscribeSwcAppRouteChangeWhileConnected, setProperty, onConnectedInnerHtml, onInitialize } from '@dooboostore/simple-web-component';
902
- import { Router, RouterEventType } from '@dooboostore/core-web';
903
-
904
- const rootRouterFactory = (w: Window) => {
905
- const tagName = 'root-router';
906
- const existing = w.customElements.get(tagName);
907
- if (existing) return tagName;
908
-
909
- @elementDefine(tagName, { window: w })
910
- class RootRouter extends w.HTMLElement {
911
- private router: Router;
912
-
913
- @onInitialize
914
- onconstructor(router: Router) {
915
- this.router = router;
916
- }
917
-
918
- @setProperty('#router', 'value')
919
- @subscribeSwcAppRouteChangeWhileConnected(['/', '/users', '/users/{id}'])
920
- routeChanged(route: RouterEventType) {
921
- return route;
922
- }
923
-
924
- navigate(path: string): void {
925
- this.router?.go(path);
926
- }
927
-
928
- @onConnectedInnerHtml
929
- render() {
930
- return `
931
- <template id="router" is="swc-choose">
932
- <!-- Home Page -->
933
- <template is="swc-when" test="['', '/'].includes($value?.path)">
934
- <home-page/>
935
- </template>
936
-
937
- <!-- Users List -->
938
- <template is="swc-when" test="$value?.path === '/users'">
939
- <users-list-page/>
940
- </template>
941
-
942
- <!-- User Detail -->
943
- <template is="swc-when" test="$value?.path.startsWith('/users/')">
944
- <user-detail-page user-id="{{$value?.pathData?.id}}"/>
945
- </template>
946
-
947
- <!-- Not Found -->
948
- <template is="swc-otherwise">
949
- <not-found-page/>
950
- </template>
951
- </template>
952
- `;
953
- }
954
- }
955
-
956
- return tagName;
957
- };
958
-
959
- export default rootRouterFactory;
960
- ```
961
-
962
- #### Step 4. Page Components
963
- ```typescript
964
- // pages/HomePage.ts
965
- import { elementDefine, onConnectedInnerHtml } from '@dooboostore/simple-web-component';
966
-
967
- const HomePage = (w: Window) => {
968
- const tagName = 'home-page';
969
- const existing = w.customElements.get(tagName);
970
- if (existing) return tagName;
971
-
972
- @elementDefine(tagName, { window: w })
973
- class HomePage extends w.HTMLElement {
974
- @onConnectedInnerHtml({ useShadow: true })
975
- render() {
976
- return `
977
- <style>
978
- :host { display: block; padding: 20px; }
979
- h1 { color: #333; }
980
- a { color: #0066cc; text-decoration: none; margin-right: 20px; }
981
- </style>
982
- <h1>Welcome to My SPA</h1>
983
- <nav>
984
- <a href="/users">View All Users</a>
985
- </nav>
986
- `;
987
- }
988
- }
989
-
990
- return tagName;
991
- };
992
-
993
- export default HomePage;
994
- ```
995
-
996
- ```typescript
997
- // pages/UsersListPage.ts
998
- import { elementDefine, onConnectedInnerHtml, onConnectedBefore } from '@dooboostore/simple-web-component';
999
- import { Inject } from '@dooboostore/simple-boot';
1000
- import { UserService } from '../services/UserService';
1001
-
1002
- const UsersListPage = (w: Window) => {
1003
- const tagName = 'users-list-page';
1004
- const existing = w.customElements.get(tagName);
1005
- if (existing) return tagName;
1006
-
1007
- @elementDefine(tagName, { window: w })
1008
- class UsersListPage extends w.HTMLElement {
1009
- private users: any[] = [];
1010
-
1011
- @onConnectedBefore
1012
- onconstructor(@Inject({ symbol: UserService.SYMBOL }) userService: UserService) {
1013
- this.users = userService.getAllUsers();
1014
- }
1015
-
1016
- @onConnectedInnerHtml({ useShadow: true })
1017
- render() {
1018
- const usersList = this.users
1019
- .map(u => `<li><a href="/users/${u.id}">${u.name}</a></li>`)
1020
- .join('');
1021
-
1022
- return `
1023
- <style>
1024
- :host { display: block; padding: 20px; }
1025
- h1 { color: #333; }
1026
- ul { list-style: none; padding: 0; }
1027
- li { margin: 10px 0; }
1028
- a { color: #0066cc; text-decoration: none; }
1029
- </style>
1030
- <h1>Users</h1>
1031
- <ul>${usersList}</ul>
1032
- <a href="/">Back to Home</a>
1033
- `;
1034
- }
1035
- }
1036
-
1037
- return tagName;
1038
- };
1039
-
1040
- export default UsersListPage;
1041
- ```
1042
-
1043
- #### Step 4-1. Reusable Components
1044
- ```typescript
1045
- // components/UserCard.ts
1046
- import { elementDefine, onConnectedInnerHtml, attributeHost } from '@dooboostore/simple-web-component';
1047
-
1048
- const UserCard = (w: Window) => {
1049
- const tagName = 'user-card';
1050
- const existing = w.customElements.get(tagName);
1051
- if (existing) return tagName;
1052
-
1053
- @elementDefine(tagName, { window: w })
1054
- class UserCard extends w.HTMLElement {
1055
- @attributeThis('data-username')
1056
- username: string;
1057
-
1058
- @attributeThis('data-email')
1059
- email: string;
1060
-
1061
- @onConnectedInnerHtml({ useShadow: true })
1062
- render() {
1063
- return `
1064
- <style>
1065
- :host { display: block; }
1066
- .card { border: 1px solid #ddd; border-radius: 4px; padding: 12px; }
1067
- .name { font-weight: bold; font-size: 14px; }
1068
- .email { color: #666; font-size: 12px; }
1069
- </style>
1070
- <div class="card">
1071
- <div class="name">${this.username}</div>
1072
- <div class="email">${this.email}</div>
1073
- </div>
1074
- `;
1075
- }
1076
- }
1077
-
1078
- return tagName;
1079
- };
1080
-
1081
- export default UserCard;
1082
- ```
1083
-
1084
- #### Step 5. Export Factories
1085
-
1086
- **services/index.ts** - Export all service factories
1087
- ```typescript
1088
- // services/index.ts
1089
- import userServiceFactory from './UserService';
1090
-
1091
- export const serviceFactories = [userServiceFactory];
1092
- ```
1093
-
1094
- **components/index.ts** - Export all component factories
1095
- ```typescript
1096
- // components/index.ts
1097
- import UserCard from './UserCard';
1098
-
1099
- export const componentFactories = [
1100
- UserCard,
1101
- ];
1102
- ```
1103
-
1104
- **pages/index.ts** - Export all page factories with router
1105
- ```typescript
1106
- // pages/index.ts
1107
- import rootRouterFactory from './RootRouter';
1108
- import HomePage from './HomePage';
1109
- import UsersListPage from './UsersListPage';
1110
- import UserDetailPage from './UserDetailPage';
1111
-
1112
- export const pageFactories = [
1113
- rootRouterFactory, // Router first
1114
- HomePage,
1115
- UsersListPage,
1116
- UserDetailPage
1117
- ];
1118
- ```
1119
-
1120
- #### Step 6. Application Bootstrap
1121
- ```typescript
1122
- // bootFactory.ts
1123
- import { register } from '@dooboostore/simple-web-component';
1124
- import { serviceFactories } from './services';
1125
- import { componentFactories } from './components';
1126
- import { pageFactories } from './pages';
1127
-
1128
- export default (w: Window, container: symbol) => {
1129
- // Initialize services with container (dependency injection)
1130
- // Each factory receives the container symbol and registers its service in the DI container
1131
- serviceFactories.forEach(serviceFactory => serviceFactory(container));
1132
-
1133
- // Register pages and components
1134
- register(w, [...pageFactories, ...componentFactories]);
1135
- };
1136
- ```
1137
-
1138
- #### Step 7. Application Entry Point
1139
598
  ```typescript
1140
599
  // index.ts
1141
600
  import 'reflect-metadata';
1142
- import { SwcAppInterface } from '@dooboostore/simple-web-component';
601
+ import { defineSwcAppBody, SwcAppInterface, defineSwcAppAll } from '@dooboostore/simple-web-component';
1143
602
  import bootFactory from './bootFactory';
1144
603
 
1145
604
  const w = window;
@@ -1148,64 +607,94 @@ w.document.addEventListener('DOMContentLoaded', async () => {
1148
607
  const container = Symbol('app');
1149
608
 
1150
609
  // Initialize services and components
1151
- bootFactory(w, container);
610
+ await defineSwcAppBody(w);
1152
611
 
1153
- // Wait for customized built-in element to be defined
1154
- await w.customElements.whenDefined('swc-app-body');
1155
612
 
1156
613
  // Get app root element
1157
614
  const appElement = w.document.querySelector('#app') as SwcAppInterface;
1158
615
 
1159
616
  if (appElement && typeof appElement.connect === 'function') {
1160
- // Connect SPA with routing and DI
1161
617
  appElement.connect({
1162
618
  path: '/',
1163
619
  routeType: 'path',
1164
620
  container: container,
621
+ onStartedLazyDefineComponent: [yourComponentFactory1, yourComponentFactor2],
1165
622
  window: w,
1166
623
  onEngineStarted: () => {
1167
624
  console.log('🚀 Application started successfully');
1168
- // Root router automatically initialized via @elementDefine
1169
625
  }
1170
626
  });
1171
- } else {
1172
- console.error('Failed to initialize SPA');
1173
627
  }
1174
628
  });
1175
629
  ```
1176
630
 
1177
- #### Resulting Application Structure
1178
- ```
1179
- src/
1180
- ├── index.ts # Entry point
1181
- ├── index.html # HTML root with <root-router>
1182
- ├── bootFactory.ts # Bootstrap services & components via register()
1183
- ├── services/
1184
- │ ├── UserService.ts # Service factory (export default)
1185
- │ └── index.ts # Export serviceFactories
1186
- ├── pages/
1187
- │ ├── RootRouter.ts # Router factory (export default)
1188
- │ ├── HomePage.ts # Page factory (export default)
1189
- │ ├── UsersListPage.ts # Page factory (export default)
1190
- │ ├── UserDetailPage.ts # Page factory (export default)
1191
- │ └── index.ts # Export pageFactories
1192
- └── components/
1193
- ├── UserCard.ts # Component factory (export default)
1194
- └── index.ts # Export componentFactories
631
+ #### Factory Pattern - Component Registration
632
+
633
+ ```typescript
634
+ // Component factory
635
+ export default (w: Window) => {
636
+ const tagName = 'my-component';
637
+ const existing = w.customElements.get(tagName);
638
+ if (existing) return tagName;
639
+
640
+ @elementDefine(tagName, { window: w })
641
+ class MyComponent extends w.HTMLElement {
642
+ @onInitialize
643
+ onconstructor(service: MyService) {
644
+ this.service = service;
645
+ }
646
+ }
647
+
648
+ return tagName;
649
+ };
650
+
651
+ // Boot factory
652
+ export default (w: Window, container: symbol) => {
653
+ serviceFactories.forEach(s => s(container));
654
+ register(w, [...pageFactories, ...componentFactories]);
655
+ };
656
+
657
+ // Entry point
658
+ const container = Symbol('app');
659
+ bootFactory(w, container);
660
+ appElement.connect({
661
+ container,
662
+ window: w,
663
+ onEngineStarted: () => {
664
+ appElement.innerHTML = '<root-router></root-router>';
665
+ }
666
+ });
1195
667
  ```
1196
668
 
1197
- **Key Flow:**
1198
- 1. HTML loads → `<root-router>` element already in DOM
1199
- 2. `index.ts` executes → calls `bootFactory`
1200
- 3. `bootFactory` registers services and components (including `rootRouterFactory`)
1201
- 4. App waits for `swc-app-body` to be defined
1202
- 5. `appElement.connect()` initializes routing & DI
1203
- 6. `<root-router>` becomes active with `@subscribeSwcAppRouteChangeWhileConnected` + `swc-choose` template
1204
- 7. Route changes automatically trigger `swc-choose` re-evaluation and template re-rendering
1205
- 8. Template attributes like `user-id="{{$value?.pathData?.id}}"` are dynamically populated with route params
1206
- 9. All components have access to injected services via `@onInitialize`
669
+ ### 11. **Accommodation Pattern**
670
+ Factory-based component registration with explicit DI.
671
+
672
+ ```typescript
673
+ // Component factory
674
+ import {SwcUtils, createElement, CreateElementConfig} from '@dooboostore/simple-web-component';
675
+ const tagName = 'my-component';
676
+ export interface MyComponent extends HTMLElement {
677
+ value: string;
678
+ }
679
+ export const MyComponent = (w: Window|Document, data?: CreateElementConfig) => {
680
+ return createElement<HTMLElement>(w, tagName, data);
681
+ };
682
+ export default (w: Window) => {
683
+ const existing = w.customElements.get(tagName);
684
+ if (existing) return existing;
1207
685
 
686
+ @elementDefine(tagName, { window: w })
687
+ class MyComponentImp extends w.HTMLElement implements MyComponent{
688
+ value: string = 'value';
689
+ @onInitialize
690
+ onconstructor(service: MyService) {
691
+ this.service = service;
692
+ }
693
+ }
694
+ return w.customElements.whenDefined(swcAppTagName);
695
+ };
1208
696
 
697
+ ```
1209
698
  ---
1210
699
 
1211
700
  ## ⚠️ Critical Rules
@@ -1227,17 +716,17 @@ class UserWidget extends w.HTMLElement { }
1227
716
  class UserWidget extends w.HTMLElement { }
1228
717
  ```
1229
718
 
1230
- ### Factory Always Returns tagName (String)
719
+ ### Factory Always Returns
1231
720
  ```typescript
1232
721
  export default (w: Window) => {
1233
722
  const tagName = 'my-element';
1234
723
  const existing = w.customElements.get(tagName);
1235
- if (existing) return tagName; // ← Return string!
724
+ if (existing) return existing;
1236
725
 
1237
726
  @elementDefine(tagName, { window: w })
1238
727
  class MyElement extends w.HTMLElement { }
1239
728
 
1240
- return tagName; // ← Always return tagName, never class
729
+ return existing;
1241
730
  };
1242
731
  ```
1243
732
 
@@ -1245,7 +734,7 @@ export default (w: Window) => {
1245
734
  ```typescript
1246
735
  // ✅ CORRECT
1247
736
  @onInitialize
1248
- onconstructor(@Inject({ symbol: Service.SYMBOL }) service: Service) {
737
+ onconstructor(@Inject(symbol: Service.SYMBOL) service: Service) {
1249
738
  this.service = service;
1250
739
  }
1251
740