@dooboostore/simple-web-component 1.0.42 → 1.0.44
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.
- package/README.md +689 -85
- package/dist/cjs/SwcAppEngine.js +3 -1
- package/dist/cjs/SwcAppEngine.js.map +2 -2
- package/dist/cjs/decorators/addEventListener.js +10 -0
- package/dist/cjs/decorators/addEventListener.js.map +2 -2
- package/dist/cjs/decorators/applyAttribute.js +41 -30
- package/dist/cjs/decorators/applyAttribute.js.map +2 -2
- package/dist/cjs/decorators/applyClass.js +66 -23
- package/dist/cjs/decorators/applyClass.js.map +3 -3
- package/dist/cjs/decorators/applyNode.js +176 -107
- package/dist/cjs/decorators/applyNode.js.map +2 -2
- package/dist/cjs/decorators/applyProperty.js +21 -30
- package/dist/cjs/decorators/applyProperty.js.map +2 -2
- package/dist/cjs/decorators/applySlot.js +11 -2
- package/dist/cjs/decorators/applySlot.js.map +2 -2
- package/dist/cjs/decorators/applyStyle.js +48 -17
- package/dist/cjs/decorators/applyStyle.js.map +3 -3
- package/dist/cjs/decorators/{changedAttributeThis.js → changedAttribute.js} +6 -6
- package/dist/cjs/decorators/changedAttribute.js.map +7 -0
- package/dist/cjs/decorators/elementDefine.js +37 -6
- package/dist/cjs/decorators/elementDefine.js.map +3 -3
- package/dist/cjs/decorators/emitCustomEvent.js +58 -42
- package/dist/cjs/decorators/emitCustomEvent.js.map +2 -2
- package/dist/cjs/decorators/index.js +1 -1
- package/dist/cjs/decorators/index.js.map +2 -2
- package/dist/cjs/decorators/publishSwcAppMessage.js +37 -23
- package/dist/cjs/decorators/publishSwcAppMessage.js.map +3 -3
- package/dist/cjs/decorators/query.js +96 -145
- package/dist/cjs/decorators/query.js.map +3 -3
- package/dist/cjs/decorators/queryAll.js +104 -131
- package/dist/cjs/decorators/queryAll.js.map +3 -3
- package/dist/cjs/decorators/subscribeSwcAppMessageWhileConnected.js +2 -0
- package/dist/cjs/decorators/subscribeSwcAppMessageWhileConnected.js.map +2 -2
- package/dist/cjs/decorators/subscribeSwcAppRouteChangeWhileConnected.js +2 -0
- package/dist/cjs/decorators/subscribeSwcAppRouteChangeWhileConnected.js.map +2 -2
- package/dist/cjs/elements/SwcAppMixin.js +17 -2
- package/dist/cjs/elements/SwcAppMixin.js.map +2 -2
- package/dist/cjs/examples/RxJSOperatorsExample.js +285 -0
- package/dist/cjs/examples/RxJSOperatorsExample.js.map +7 -0
- package/dist/cjs/index.js +1 -0
- package/dist/cjs/index.js.map +2 -2
- package/dist/cjs/router/ElementRouter.js +4 -0
- package/dist/cjs/router/ElementRouter.js.map +2 -2
- package/dist/esm/SwcAppEngine.js +3 -1
- package/dist/esm/SwcAppEngine.js.map +2 -2
- package/dist/esm/decorators/addEventListener.js +10 -0
- package/dist/esm/decorators/addEventListener.js.map +2 -2
- package/dist/esm/decorators/applyAttribute.js +41 -30
- package/dist/esm/decorators/applyAttribute.js.map +2 -2
- package/dist/esm/decorators/applyClass.js +66 -23
- package/dist/esm/decorators/applyClass.js.map +3 -3
- package/dist/esm/decorators/applyNode.js +176 -107
- package/dist/esm/decorators/applyNode.js.map +2 -2
- package/dist/esm/decorators/applyProperty.js +21 -30
- package/dist/esm/decorators/applyProperty.js.map +2 -2
- package/dist/esm/decorators/applySlot.js +11 -2
- package/dist/esm/decorators/applySlot.js.map +2 -2
- package/dist/esm/decorators/applyStyle.js +48 -17
- package/dist/esm/decorators/applyStyle.js.map +3 -3
- package/dist/esm/decorators/{changedAttributeThis.js → changedAttribute.js} +3 -3
- package/dist/esm/decorators/changedAttribute.js.map +7 -0
- package/dist/esm/decorators/elementDefine.js +35 -4
- package/dist/esm/decorators/elementDefine.js.map +2 -2
- package/dist/esm/decorators/emitCustomEvent.js +58 -42
- package/dist/esm/decorators/emitCustomEvent.js.map +2 -2
- package/dist/esm/decorators/index.js +1 -1
- package/dist/esm/decorators/index.js.map +1 -1
- package/dist/esm/decorators/publishSwcAppMessage.js +37 -23
- package/dist/esm/decorators/publishSwcAppMessage.js.map +3 -3
- package/dist/esm/decorators/query.js +96 -145
- package/dist/esm/decorators/query.js.map +3 -3
- package/dist/esm/decorators/queryAll.js +104 -131
- package/dist/esm/decorators/queryAll.js.map +3 -3
- package/dist/esm/decorators/subscribeSwcAppMessageWhileConnected.js +2 -0
- package/dist/esm/decorators/subscribeSwcAppMessageWhileConnected.js.map +2 -2
- package/dist/esm/decorators/subscribeSwcAppRouteChangeWhileConnected.js +2 -0
- package/dist/esm/decorators/subscribeSwcAppRouteChangeWhileConnected.js.map +2 -2
- package/dist/esm/elements/SwcAppMixin.js +18 -3
- package/dist/esm/elements/SwcAppMixin.js.map +2 -2
- package/dist/esm/examples/RxJSOperatorsExample.js +266 -0
- package/dist/esm/examples/RxJSOperatorsExample.js.map +7 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +2 -2
- package/dist/esm/router/ElementRouter.js +4 -0
- package/dist/esm/router/ElementRouter.js.map +2 -2
- package/dist/esm-bundle/dooboostore-simple-web-component.esm.js +900 -578
- package/dist/esm-bundle/dooboostore-simple-web-component.esm.js.map +4 -4
- package/dist/types/SwcAppEngine.d.ts.map +1 -1
- package/dist/types/decorators/addEventListener.d.ts +22 -14
- package/dist/types/decorators/addEventListener.d.ts.map +1 -1
- package/dist/types/decorators/applyAttribute.d.ts +61 -20
- package/dist/types/decorators/applyAttribute.d.ts.map +1 -1
- package/dist/types/decorators/applyClass.d.ts +22 -36
- package/dist/types/decorators/applyClass.d.ts.map +1 -1
- package/dist/types/decorators/applyNode.d.ts +178 -30
- package/dist/types/decorators/applyNode.d.ts.map +1 -1
- package/dist/types/decorators/applyProperty.d.ts +53 -18
- package/dist/types/decorators/applyProperty.d.ts.map +1 -1
- package/dist/types/decorators/applySlot.d.ts +6 -0
- package/dist/types/decorators/applySlot.d.ts.map +1 -1
- package/dist/types/decorators/applyStyle.d.ts +13 -7
- package/dist/types/decorators/applyStyle.d.ts.map +1 -1
- package/dist/types/decorators/{changedAttributeThis.d.ts → changedAttribute.d.ts} +2 -2
- package/dist/types/decorators/changedAttribute.d.ts.map +1 -0
- package/dist/types/decorators/elementDefine.d.ts.map +1 -1
- package/dist/types/decorators/emitCustomEvent.d.ts +20 -10
- package/dist/types/decorators/emitCustomEvent.d.ts.map +1 -1
- package/dist/types/decorators/index.d.ts +1 -1
- package/dist/types/decorators/index.d.ts.map +1 -1
- package/dist/types/decorators/publishSwcAppMessage.d.ts +19 -0
- package/dist/types/decorators/publishSwcAppMessage.d.ts.map +1 -1
- package/dist/types/decorators/query.d.ts +3 -26
- package/dist/types/decorators/query.d.ts.map +1 -1
- package/dist/types/decorators/queryAll.d.ts +3 -20
- package/dist/types/decorators/queryAll.d.ts.map +1 -1
- package/dist/types/decorators/subscribeSwcAppMessageWhileConnected.d.ts +1 -0
- package/dist/types/decorators/subscribeSwcAppMessageWhileConnected.d.ts.map +1 -1
- package/dist/types/decorators/subscribeSwcAppRouteChangeWhileConnected.d.ts +2 -0
- package/dist/types/decorators/subscribeSwcAppRouteChangeWhileConnected.d.ts.map +1 -1
- package/dist/types/elements/SwcAppMixin.d.ts +4 -4
- package/dist/types/elements/SwcAppMixin.d.ts.map +1 -1
- package/dist/types/examples/RxJSOperatorsExample.d.ts +94 -0
- package/dist/types/examples/RxJSOperatorsExample.d.ts.map +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/router/ElementRouter.d.ts.map +1 -1
- package/dist/umd-bundle/dooboostore-simple-web-component.umd.js +900 -578
- package/dist/umd-bundle/dooboostore-simple-web-component.umd.js.map +4 -4
- package/package.json +7 -9
- package/src/SwcAppEngine.ts +3 -1
- package/src/decorators/addEventListener.ts +35 -24
- package/src/decorators/applyAttribute.ts +167 -65
- package/src/decorators/applyClass.ts +113 -17
- package/src/decorators/applyNode.ts +383 -102
- package/src/decorators/applyProperty.ts +99 -65
- package/src/decorators/applySlot.ts +24 -10
- package/src/decorators/applyStyle.ts +83 -13
- package/src/decorators/{changedAttributeThis.ts → changedAttribute.ts} +1 -1
- package/src/decorators/elementDefine.ts +47 -4
- package/src/decorators/emitCustomEvent.ts +62 -35
- package/src/decorators/index.ts +1 -1
- package/src/decorators/publishSwcAppMessage.ts +71 -27
- package/src/decorators/query.ts +90 -159
- package/src/decorators/queryAll.ts +92 -134
- package/src/decorators/subscribeSwcAppMessageWhileConnected.ts +1 -0
- package/src/decorators/subscribeSwcAppRouteChangeWhileConnected.ts +3 -1
- package/src/elements/SwcAppMixin.ts +43 -24
- package/src/examples/RxJSOperatorsExample.ts +279 -0
- package/src/index.ts +2 -0
- package/src/router/ElementRouter.ts +4 -0
- package/dist/cjs/decorators/changedAttributeThis.js.map +0 -7
- package/dist/esm/decorators/changedAttributeThis.js.map +0 -7
- package/dist/types/decorators/changedAttributeThis.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -70,18 +70,14 @@ replaceChildren(node: Node) {
|
|
|
70
70
|
```
|
|
71
71
|
### 3.5 **Slot Management with @applySlot**
|
|
72
72
|
|
|
73
|
-
Manage dynamic content insertion into named slots using
|
|
73
|
+
Manage dynamic content insertion into named slots using slot decorators. Slots are placeholders in templates that can be updated dynamically.
|
|
74
74
|
|
|
75
75
|
```typescript
|
|
76
|
-
import {clearSlot} from "./applySlot";
|
|
77
|
-
|
|
78
76
|
@elementDefine('content-manager')
|
|
79
77
|
class ContentManager extends HTMLElement {
|
|
80
|
-
|
|
81
|
-
|
|
82
78
|
// Method: Replace children in slot with HTML
|
|
83
79
|
@addEventListener('.update-btn', 'click')
|
|
84
|
-
@
|
|
80
|
+
@replaceChildrenHtmlSlot('main-content')
|
|
85
81
|
updateContent() {
|
|
86
82
|
return '<div>Updated content here</div>';
|
|
87
83
|
}
|
|
@@ -105,7 +101,6 @@ class ContentManager extends HTMLElement {
|
|
|
105
101
|
return true;
|
|
106
102
|
}
|
|
107
103
|
|
|
108
|
-
|
|
109
104
|
@onConnectedLight
|
|
110
105
|
render() {
|
|
111
106
|
return `
|
|
@@ -130,17 +125,13 @@ class ContentManager extends HTMLElement {
|
|
|
130
125
|
- `<!--[[ slot-id ]]-->` - Named slot placeholder (HTML comment syntax)
|
|
131
126
|
|
|
132
127
|
**@applySlot Decorator Variants:**
|
|
133
|
-
- clearSlot
|
|
134
|
-
- prependHtmlSlot
|
|
135
|
-
-
|
|
136
|
-
-
|
|
137
|
-
-
|
|
138
|
-
-
|
|
139
|
-
-
|
|
140
|
-
- replaceChildrenHtmlSlot
|
|
141
|
-
- replaceChildrenSlot
|
|
142
|
-
- replaceChildrenTextSlot
|
|
143
|
-
|
|
128
|
+
- `clearSlot(slotId)` - Clear all content
|
|
129
|
+
- `prependHtmlSlot(slotId)` - Add HTML at beginning
|
|
130
|
+
- `prependTextSlot(slotId)` - Add text at beginning
|
|
131
|
+
- `appendHtmlSlot(slotId)` - Add HTML at end
|
|
132
|
+
- `appendTextSlot(slotId)` - Add text at end
|
|
133
|
+
- `replaceChildrenHtmlSlot(slotId)` - Replace all with HTML
|
|
134
|
+
- `replaceChildrenTextSlot(slotId)` - Replace all with text
|
|
144
135
|
|
|
145
136
|
**Position Options:**
|
|
146
137
|
```typescript
|
|
@@ -207,7 +198,7 @@ class CounterApp extends HTMLElement {
|
|
|
207
198
|
|
|
208
199
|
**State Syntax in Templates:**
|
|
209
200
|
|
|
210
|
-
**Reading State Values:**
|
|
201
|
+
**Reading State Values(read only):**
|
|
211
202
|
- `@stateName@` - Read state value as text/attribute
|
|
212
203
|
- `@stateName@()` - Call state property as function (if it's a function)
|
|
213
204
|
- `@expression@` - Evaluate expressions with state variables
|
|
@@ -231,6 +222,23 @@ class CounterApp extends HTMLElement {
|
|
|
231
222
|
- `@expression@` - Evaluate JavaScript expressions
|
|
232
223
|
- All state variables available in template expressions
|
|
233
224
|
|
|
225
|
+
**Important: State Properties are Read-Only in Templates**
|
|
226
|
+
State values accessed in templates via `@stateName@` syntax are read-only. You cannot assign values to state properties from within template expressions. State must be updated from component methods:
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
// ✅ CORRECT - Update state from method
|
|
230
|
+
@addEventListener('button', 'click')
|
|
231
|
+
increment() {
|
|
232
|
+
this.count++; // Direct property assignment in code
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// ❌ WRONG - Cannot assign in template
|
|
236
|
+
<!--[text @count = 5@ ]--> // This won't work - read-only in templates
|
|
237
|
+
|
|
238
|
+
// ✅ CORRECT - Call method from template
|
|
239
|
+
<button e:click="@increment@()">Increment</button>
|
|
240
|
+
```
|
|
241
|
+
|
|
234
242
|
**Advanced Example:**
|
|
235
243
|
```typescript
|
|
236
244
|
@elementDefine('user-profile')
|
|
@@ -288,16 +296,21 @@ onCriticalClick(event: Event) {
|
|
|
288
296
|
}
|
|
289
297
|
```
|
|
290
298
|
|
|
291
|
-
|
|
292
|
-
-
|
|
293
|
-
-
|
|
294
|
-
-
|
|
295
|
-
-
|
|
296
|
-
-
|
|
297
|
-
-
|
|
298
|
-
-
|
|
299
|
-
-
|
|
300
|
-
|
|
299
|
+
**@addEventListener Decorator Variants:**
|
|
300
|
+
- `addEventListener(selector, type, options)` - Full form
|
|
301
|
+
- `event(selector, type, options)` - Short alias
|
|
302
|
+
- `addEventListenerThis(type, options)` - Listen on $this element
|
|
303
|
+
- `addEventListenerAppHost(type, options)` - Listen on $appHost
|
|
304
|
+
- `addEventListenerWindow(type, options)` - Listen on window
|
|
305
|
+
- `addEventListenerDocument(type, options)` - Listen on document
|
|
306
|
+
- `addEventListenerDelegate(selector, type, options)` - Event delegation
|
|
307
|
+
- `eventDelegate(selector, type, options)` - Short alias
|
|
308
|
+
- `addEventListenerDelegateLightDom(selector, type, options)` - Delegate in light DOM
|
|
309
|
+
- `eventDelegateLightDom(selector, type, options)` - Short alias
|
|
310
|
+
- `addEventListenerDelegateShadowDom(selector, type, options)` - Delegate in shadow DOM
|
|
311
|
+
- `eventDelegateShadowDom(selector, type, options)` - Short alias
|
|
312
|
+
- `addEventListenerDelegateAllDom(selector, type, options)` - Delegate in all DOM
|
|
313
|
+
- `eventDelegateAllDom(selector, type, options)` - Short alias
|
|
301
314
|
|
|
302
315
|
#### @emitCustomEvent
|
|
303
316
|
Emit custom events with data.
|
|
@@ -310,6 +323,50 @@ async onLogin() {
|
|
|
310
323
|
}
|
|
311
324
|
```
|
|
312
325
|
|
|
326
|
+
**@emitCustomEvent Decorator Variants:**
|
|
327
|
+
- `emitCustomEvent(target, type, options)` - Full form
|
|
328
|
+
- `emit(target, type, options)` - Short alias
|
|
329
|
+
- `emitCustomEventThis(type, options)` - Emit from $this element
|
|
330
|
+
|
|
331
|
+
#### @publishSwcAppMessage
|
|
332
|
+
Publish messages through the message bus when method completes.
|
|
333
|
+
|
|
334
|
+
```typescript
|
|
335
|
+
@publishSwcAppMessage()
|
|
336
|
+
async onLogin() {
|
|
337
|
+
const user = await this.authService.login();
|
|
338
|
+
return user; // Published as message with data: user
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
@publishSwcAppMessage('user-profile-updated')
|
|
342
|
+
updateProfile() {
|
|
343
|
+
const profile = { name: this.name, email: this.email };
|
|
344
|
+
return profile; // Published as message with type: 'user-profile-updated'
|
|
345
|
+
}
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
**@publishSwcAppMessage Decorator Variants:**
|
|
349
|
+
- `publishSwcAppMessage()` - Publish without message type
|
|
350
|
+
- `publishSwcAppMessage(messageType)` - Publish with specific message type
|
|
351
|
+
- `publishSwcAppMessage(messageType, { valueKey: 'customKey' })` - Publish with custom value extraction
|
|
352
|
+
- `publishSwcAppMessage({ messageType: 'type', valueKey: 'customKey' })` - Publish with options object
|
|
353
|
+
- `publish()` - Short alias
|
|
354
|
+
- `publish(messageType)` - Short alias with message type
|
|
355
|
+
- `publish(messageType, options)` - Short alias with options
|
|
356
|
+
|
|
357
|
+
**Using valueKey for Multiple Decorators:**
|
|
358
|
+
|
|
359
|
+
```typescript
|
|
360
|
+
@publishSwcAppMessage('event1', { valueKey: 'detail1' })
|
|
361
|
+
@publishSwcAppMessage('event2', { valueKey: 'detail2' })
|
|
362
|
+
handleMultipleEvents() {
|
|
363
|
+
return {
|
|
364
|
+
detail1: { type: 'event1', data: 'value1' },
|
|
365
|
+
detail2: { type: 'event2', data: 'value2' }
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
```
|
|
369
|
+
|
|
313
370
|
#### @emitCustomEventThis
|
|
314
371
|
Emit events from a method with custom event name mapping.
|
|
315
372
|
|
|
@@ -321,7 +378,7 @@ onNavClick(e: any) {
|
|
|
321
378
|
```
|
|
322
379
|
|
|
323
380
|
#### @addEventListenerThis
|
|
324
|
-
Listen to events on the component element itself (
|
|
381
|
+
Listen to events on the component element itself (`$this` selector).
|
|
325
382
|
|
|
326
383
|
```typescript
|
|
327
384
|
@addEventListenerThis('click')
|
|
@@ -395,55 +452,366 @@ printTag() {
|
|
|
395
452
|
|
|
396
453
|
### 6. **Attribute Binding**
|
|
397
454
|
|
|
398
|
-
#### @attribute
|
|
399
|
-
Bind HTML attributes to properties
|
|
455
|
+
#### @attribute (Field Decorator)
|
|
456
|
+
Bind HTML attributes to properties with automatic read/write synchronization.
|
|
457
|
+
|
|
458
|
+
Intelligently distinguishes between attribute names and CSS selectors:
|
|
400
459
|
|
|
401
460
|
```typescript
|
|
402
|
-
@
|
|
403
|
-
|
|
461
|
+
@elementDefine('product-card')
|
|
462
|
+
class ProductCard extends HTMLElement {
|
|
463
|
+
// Attribute name on $this (auto-detected)
|
|
464
|
+
@attribute('product-id')
|
|
465
|
+
productId: string;
|
|
404
466
|
|
|
405
|
-
|
|
406
|
-
|
|
467
|
+
// Attribute name on selector
|
|
468
|
+
@attribute('#user', 'data-id')
|
|
469
|
+
userId: string;
|
|
470
|
+
|
|
471
|
+
// Bare decorator (uses field name as attribute)
|
|
472
|
+
@attribute
|
|
473
|
+
title: string;
|
|
474
|
+
|
|
475
|
+
@onInitialize
|
|
476
|
+
onconstructor() {
|
|
477
|
+
if (this.productId) {
|
|
478
|
+
this.loadProduct(this.productId);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
}
|
|
407
482
|
```
|
|
408
483
|
|
|
409
|
-
#### @
|
|
410
|
-
|
|
484
|
+
#### @setAttribute (Method Decorator)
|
|
485
|
+
Set element attributes from method return values.
|
|
411
486
|
|
|
412
487
|
```typescript
|
|
413
|
-
@
|
|
414
|
-
|
|
488
|
+
@elementDefine('status-updater')
|
|
489
|
+
class StatusUpdater extends HTMLElement {
|
|
490
|
+
// Set attribute on $this
|
|
491
|
+
@setAttribute('data-status')
|
|
492
|
+
updateStatus() {
|
|
493
|
+
return this.isActive ? 'active' : 'inactive';
|
|
494
|
+
}
|
|
415
495
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
this.
|
|
496
|
+
// Set attribute on selector
|
|
497
|
+
@setAttribute('#user', 'data-name')
|
|
498
|
+
setUserName() {
|
|
499
|
+
return this.userName;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// Bare decorator (uses method name as attribute)
|
|
503
|
+
@setAttribute
|
|
504
|
+
setValue() {
|
|
505
|
+
return this.computedValue;
|
|
420
506
|
}
|
|
421
507
|
}
|
|
422
508
|
```
|
|
423
509
|
|
|
424
|
-
|
|
510
|
+
**Usage Patterns:**
|
|
511
|
+
- `@attribute('attr-name')` - Attribute name on $this (auto-detected)
|
|
512
|
+
- `@attribute('#selector', 'attr-name')` - Attribute name on selector
|
|
513
|
+
- `@attribute('attr-name', options)` - Attribute name on $this with options
|
|
514
|
+
- `@attribute('#selector', 'attr-name', options)` - Attribute name on selector with options
|
|
515
|
+
- `@attribute` - Bare decorator (uses field name as attribute on $this)
|
|
516
|
+
|
|
517
|
+
**@setAttribute Patterns:**
|
|
518
|
+
- `@setAttribute('attr-name')` - Set attribute on $this
|
|
519
|
+
- `@setAttribute('#selector', 'attr-name')` - Set attribute on selector
|
|
520
|
+
- `@setAttribute('attr-name', options)` - Set attribute on $this with options
|
|
521
|
+
- `@setAttribute('#selector', 'attr-name', options)` - Set attribute on selector with options
|
|
522
|
+
- `@setAttribute` - Bare decorator (uses method name as attribute on $this)
|
|
523
|
+
|
|
524
|
+
#### @changedAttribute (Method Decorator)
|
|
525
|
+
Listen for attribute changes on the component this element.
|
|
425
526
|
|
|
426
527
|
```typescript
|
|
427
|
-
@
|
|
428
|
-
|
|
429
|
-
|
|
528
|
+
@elementDefine('reactive-component')
|
|
529
|
+
class ReactiveComponent extends HTMLElement {
|
|
530
|
+
@changedAttribute('product-id')
|
|
531
|
+
onProductIdChanged(newValue: string, oldValue: string) {
|
|
532
|
+
console.log(`Product changed from ${oldValue} to ${newValue}`);
|
|
533
|
+
this.loadProduct(newValue);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
@changedAttribute('data-status', { type: Boolean })
|
|
537
|
+
onStatusChanged(newValue: boolean) {
|
|
538
|
+
console.log(`Status is now: ${newValue}`);
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
// Listen to any attribute change
|
|
542
|
+
@changedAttribute()
|
|
543
|
+
onAnyAttributeChanged(newValue: any, oldValue: any) {
|
|
544
|
+
console.log('An attribute changed');
|
|
545
|
+
}
|
|
430
546
|
}
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
**@changedAttribute Options:**
|
|
550
|
+
- `attributeName` - Attribute name to listen for (optional, defaults to method name)
|
|
551
|
+
- `type` - Type converter: `Number`, `Boolean`, or `String`
|
|
552
|
+
- `while` - Execution condition: `'connected'` (only while connected to DOM)
|
|
431
553
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
554
|
+
### 6.5 **Property Binding**
|
|
555
|
+
|
|
556
|
+
#### @property (Field Decorator)
|
|
557
|
+
Bind element properties to component fields with automatic read/write synchronization.
|
|
558
|
+
|
|
559
|
+
```typescript
|
|
560
|
+
@elementDefine('input-wrapper')
|
|
561
|
+
class InputWrapper extends HTMLElement {
|
|
562
|
+
// Property on $this (auto-detected)
|
|
563
|
+
@property('value')
|
|
564
|
+
inputValue: string;
|
|
565
|
+
|
|
566
|
+
// Property on selector
|
|
567
|
+
@property('#submit-btn', 'disabled')
|
|
568
|
+
isSubmitDisabled: boolean;
|
|
569
|
+
|
|
570
|
+
// Bare decorator (uses field name as property)
|
|
571
|
+
@property
|
|
572
|
+
checked: boolean;
|
|
435
573
|
}
|
|
574
|
+
```
|
|
436
575
|
|
|
576
|
+
#### @setProperty (Method Decorator)
|
|
577
|
+
Set element properties from method return values.
|
|
437
578
|
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
//
|
|
442
|
-
|
|
579
|
+
```typescript
|
|
580
|
+
@elementDefine('form-handler')
|
|
581
|
+
class FormHandler extends HTMLElement {
|
|
582
|
+
// Set property on $this
|
|
583
|
+
@setProperty('disabled')
|
|
584
|
+
disableForm() {
|
|
585
|
+
return !this.isValid();
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
// Set property on selector
|
|
589
|
+
@setProperty('#submit-btn', 'disabled')
|
|
590
|
+
updateSubmitState() {
|
|
591
|
+
return this.hasErrors;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
// Bare decorator (uses method name as property)
|
|
595
|
+
@setProperty
|
|
596
|
+
setValue() {
|
|
597
|
+
return this.computedValue;
|
|
598
|
+
}
|
|
443
599
|
}
|
|
444
600
|
```
|
|
445
601
|
|
|
446
|
-
|
|
602
|
+
**Usage Patterns:**
|
|
603
|
+
- `@property('propertyName')` - Property on $this
|
|
604
|
+
- `@property('#selector', 'propertyName')` - Property on selector
|
|
605
|
+
- `@property('propertyName', options)` - Property on $this with options
|
|
606
|
+
- `@property('#selector', 'propertyName', options)` - Property on selector with options
|
|
607
|
+
- `@property` - Bare decorator (uses field name as property on $this)
|
|
608
|
+
|
|
609
|
+
**@setProperty Patterns:**
|
|
610
|
+
- `@setProperty('propertyName')` - Set property on $this
|
|
611
|
+
- `@setProperty('#selector', 'propertyName')` - Set property on selector
|
|
612
|
+
- `@setProperty('propertyName', options)` - Set property on $this with options
|
|
613
|
+
- `@setProperty('#selector', 'propertyName', options)` - Set property on selector with options
|
|
614
|
+
- `@setProperty` - Bare decorator (uses method name as property on $this)
|
|
615
|
+
|
|
616
|
+
### 7. **DOM Manipulation with applyNode**
|
|
617
|
+
|
|
618
|
+
Surgically add, replace, or remove nodes in the DOM with fine-grained control.
|
|
619
|
+
|
|
620
|
+
```typescript
|
|
621
|
+
@elementDefine('content-updater')
|
|
622
|
+
class ContentUpdater extends HTMLElement {
|
|
623
|
+
@addEventListener('button', 'click')
|
|
624
|
+
@replaceChildrenNode()
|
|
625
|
+
updateContent() {
|
|
626
|
+
return `<div>New content</div>`;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
@addEventListener('.append-btn', 'click')
|
|
630
|
+
@beforeEndNode()
|
|
631
|
+
appendContent() {
|
|
632
|
+
return `<p>Appended content</p>`;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
@addEventListener('.prepend-btn', 'click')
|
|
636
|
+
@afterBeginNode()
|
|
637
|
+
prependContent() {
|
|
638
|
+
return `<p>Prepended content</p>`;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
@onConnectedLight
|
|
642
|
+
render() {
|
|
643
|
+
return `
|
|
644
|
+
<div>
|
|
645
|
+
<button class="append-btn">Append</button>
|
|
646
|
+
<button class="prepend-btn">Prepend</button>
|
|
647
|
+
<button>Replace</button>
|
|
648
|
+
</div>
|
|
649
|
+
`;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
```
|
|
653
|
+
|
|
654
|
+
**@applyNode Decorator Variants:**
|
|
655
|
+
- `replaceChildrenNode()` - Replace all children
|
|
656
|
+
- `replaceChildrenLightNode()` - Replace children in light DOM
|
|
657
|
+
- `beforeEndNode()` - Append to end
|
|
658
|
+
- `beforeEndLightNode()` - Append to light DOM end
|
|
659
|
+
- `afterBeginNode()` - Prepend to beginning
|
|
660
|
+
- `afterBeginLightNode()` - Prepend to light DOM beginning
|
|
661
|
+
- `innerHtmlNode()` - Set innerHTML
|
|
662
|
+
- `innerHtmlLightNode()` - Set innerHTML in light DOM
|
|
663
|
+
- `innerTextNode()` - Set innerText
|
|
664
|
+
|
|
665
|
+
**Position Options:**
|
|
666
|
+
```typescript
|
|
667
|
+
export type ApplyNodePosition =
|
|
668
|
+
| 'beforeBegin' // Before element
|
|
669
|
+
| 'afterBegin' // After element opens
|
|
670
|
+
| 'beforeEnd' // Before element closes
|
|
671
|
+
| 'afterEnd' // After element closes
|
|
672
|
+
| 'replace' // Replace element
|
|
673
|
+
| 'replaceChildren' // Replace children
|
|
674
|
+
| 'innerHtml' // Set innerHTML
|
|
675
|
+
| 'innerText' // Set innerText
|
|
676
|
+
| 'remove'; // Remove element
|
|
677
|
+
```
|
|
678
|
+
|
|
679
|
+
### 8. **Style & Class Management**
|
|
680
|
+
|
|
681
|
+
Apply styles and classes dynamically with `@applyStyle` and `@applyClass`.
|
|
682
|
+
|
|
683
|
+
```typescript
|
|
684
|
+
@elementDefine('styled-component')
|
|
685
|
+
class StyledComponent extends HTMLElement {
|
|
686
|
+
@state('isActive')
|
|
687
|
+
isActive = false;
|
|
688
|
+
|
|
689
|
+
@addEventListener('button', 'click')
|
|
690
|
+
@updateStyle()
|
|
691
|
+
toggleStyle() {
|
|
692
|
+
return {
|
|
693
|
+
color: this.isActive ? 'green' : 'red',
|
|
694
|
+
fontSize: '16px'
|
|
695
|
+
};
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
@addEventListener('.toggle-btn', 'click')
|
|
699
|
+
@updateClass()
|
|
700
|
+
toggleClass() {
|
|
701
|
+
return {
|
|
702
|
+
'active': this.isActive,
|
|
703
|
+
'disabled': !this.isActive
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
@onConnectedLight
|
|
708
|
+
render() {
|
|
709
|
+
return `
|
|
710
|
+
<div>
|
|
711
|
+
<button class="toggle-btn">Toggle</button>
|
|
712
|
+
<button>Update Style</button>
|
|
713
|
+
</div>
|
|
714
|
+
`;
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
```
|
|
718
|
+
|
|
719
|
+
**@applyStyle Variants:**
|
|
720
|
+
- `setStyleThis()` - Clear and set styles
|
|
721
|
+
- `updateStyleThis()` - Update/merge styles
|
|
722
|
+
- `removeStyleThis()` - Remove specific styles
|
|
723
|
+
|
|
724
|
+
**@applyClass Variants:**
|
|
725
|
+
- `setClassThis()` - Replace all classes
|
|
726
|
+
- `updateClassThis()` - Toggle classes
|
|
727
|
+
- `addClassThis()` - Add classes
|
|
728
|
+
- `removeClassThis()` - Remove classes
|
|
729
|
+
- `toggleClassThis()` - Toggle classes
|
|
730
|
+
|
|
731
|
+
### 8.5 **Lifecycle Hooks**
|
|
732
|
+
|
|
733
|
+
Lifecycle hooks allow you to execute code at specific points in a component's lifecycle. All lifecycle decorators support optional `order` parameter for execution ordering.
|
|
734
|
+
|
|
735
|
+
```typescript
|
|
736
|
+
@elementDefine('my-component')
|
|
737
|
+
class MyComponent extends HTMLElement {
|
|
738
|
+
@onInitialize({ order: 0 })
|
|
739
|
+
onInit() {
|
|
740
|
+
// Called during component construction
|
|
741
|
+
console.log('Component initialized');
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
@onConnectedBefore({ order: 0 })
|
|
745
|
+
beforeConnected() {
|
|
746
|
+
// Called before element connects to DOM
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
@onConnectedAfter({ order: 1 })
|
|
750
|
+
afterConnected() {
|
|
751
|
+
// Called after element connects to DOM
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
@onConnected
|
|
755
|
+
onConnected() {
|
|
756
|
+
// Called when element enters DOM
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
@onDisconnectedBefore
|
|
760
|
+
beforeDisconnected() {
|
|
761
|
+
// Called before element disconnects from DOM
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
@onDisconnected
|
|
765
|
+
onDisconnected() {
|
|
766
|
+
// Called when element leaves DOM
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
@onAdoptedBefore
|
|
770
|
+
beforeAdopted() {
|
|
771
|
+
// Called before element is adopted into new document
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
@onAdopted
|
|
775
|
+
onAdopted() {
|
|
776
|
+
// Called when element is adopted into new document
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
@onConnectedSwcApp({ order: 0 })
|
|
780
|
+
onSwcAppConnected() {
|
|
781
|
+
// Called after SwcApp.connect() completes
|
|
782
|
+
// Full DI support available through hostSet
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
@onConnectedCompleted({ order: 0 })
|
|
786
|
+
onConnectedCompleted() {
|
|
787
|
+
// Called after all connected lifecycle hooks complete
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
```
|
|
791
|
+
|
|
792
|
+
**Lifecycle Execution Order:**
|
|
793
|
+
- `@onInitialize` - Component construction
|
|
794
|
+
- `@onConnectedBefore` - Before DOM connection
|
|
795
|
+
- `@onConnectedAfter` - After DOM connection
|
|
796
|
+
- `@onConnected` - DOM connection (with HTML rendering)
|
|
797
|
+
- `@onConnectedSwcApp` - After SwcApp initialization
|
|
798
|
+
- `@onConnectedCompleted` - All connected hooks done
|
|
799
|
+
- `@onDisconnectedBefore` - Before DOM disconnection
|
|
800
|
+
- `@onDisconnected` - DOM disconnection
|
|
801
|
+
- `@onAdoptedBefore` - Before document adoption
|
|
802
|
+
- `@onAdopted` - Document adoption
|
|
803
|
+
|
|
804
|
+
**Order Parameter:**
|
|
805
|
+
All lifecycle decorators support `order?: number` for controlling execution sequence:
|
|
806
|
+
```typescript
|
|
807
|
+
@onConnectedBefore({ order: 0 }) // Runs first
|
|
808
|
+
onFirst() { }
|
|
809
|
+
|
|
810
|
+
@onConnectedBefore({ order: 1 }) // Runs second
|
|
811
|
+
onSecond() { }
|
|
812
|
+
```
|
|
813
|
+
|
|
814
|
+
### 9. **Structural Directives**
|
|
447
815
|
|
|
448
816
|
Structural Directives allow declarative conditional rendering, list iteration, async handling, and routing with automatic attribute substitution and dynamic expressions.
|
|
449
817
|
|
|
@@ -528,6 +896,28 @@ class LoginForm extends HTMLElement {
|
|
|
528
896
|
}
|
|
529
897
|
```
|
|
530
898
|
|
|
899
|
+
**@publishSwcAppMessage Decorator Variants:**
|
|
900
|
+
- `publishSwcAppMessage()` - Publish without message type
|
|
901
|
+
- `publishSwcAppMessage(messageType)` - Publish with specific message type
|
|
902
|
+
- `publishSwcAppMessage(messageType, { valueKey: 'customKey' })` - Publish with custom value extraction
|
|
903
|
+
- `publishSwcAppMessage({ messageType: 'type', valueKey: 'customKey' })` - Publish with options object
|
|
904
|
+
- `publishMessage()` - Short alias
|
|
905
|
+
- `publishMessage(messageType)` - Short alias with message type
|
|
906
|
+
- `publishMessage(messageType, options)` - Short alias with options
|
|
907
|
+
|
|
908
|
+
**Using valueKey for Multiple Decorators:**
|
|
909
|
+
|
|
910
|
+
```typescript
|
|
911
|
+
@publishSwcAppMessage('event1', { valueKey: 'detail1' })
|
|
912
|
+
@publishSwcAppMessage('event2', { valueKey: 'detail2' })
|
|
913
|
+
handleMultipleEvents() {
|
|
914
|
+
return {
|
|
915
|
+
detail1: { type: 'event1', data: 'value1' },
|
|
916
|
+
detail2: { type: 'event2', data: 'value2' }
|
|
917
|
+
};
|
|
918
|
+
}
|
|
919
|
+
```
|
|
920
|
+
|
|
531
921
|
**SwcAppMessage Structure:**
|
|
532
922
|
```typescript
|
|
533
923
|
type SwcAppMessage<T = any> = {
|
|
@@ -570,7 +960,7 @@ class ProductDetail extends HTMLElement {
|
|
|
570
960
|
|
|
571
961
|
---
|
|
572
962
|
|
|
573
|
-
###
|
|
963
|
+
### 11. **SwcApp - Multiple Element Types with Mixin Pattern**
|
|
574
964
|
|
|
575
965
|
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.
|
|
576
966
|
|
|
@@ -647,17 +1037,15 @@ w.document.addEventListener('DOMContentLoaded', async () => {
|
|
|
647
1037
|
|
|
648
1038
|
```typescript
|
|
649
1039
|
// Component factory
|
|
650
|
-
import {defineSwcApp} from "./SwcApp";
|
|
651
|
-
|
|
652
1040
|
export default (w: Window) => {
|
|
653
1041
|
const tagName = 'my-component';
|
|
654
1042
|
const existing = w.customElements.get(tagName);
|
|
655
1043
|
if (existing) return tagName;
|
|
656
1044
|
|
|
657
|
-
@elementDefine(tagName, {window: w})
|
|
1045
|
+
@elementDefine(tagName, { window: w })
|
|
658
1046
|
class MyComponent extends w.HTMLElement {
|
|
659
1047
|
@onInitialize
|
|
660
|
-
onconstructor(service: MyService) {
|
|
1048
|
+
onconstructor(@inject(MyService.SYMBOL) service: MyService) {
|
|
661
1049
|
this.service = service;
|
|
662
1050
|
}
|
|
663
1051
|
}
|
|
@@ -672,7 +1060,7 @@ export default (w: Window, container: symbol) => {
|
|
|
672
1060
|
|
|
673
1061
|
// Entry point
|
|
674
1062
|
const container = Symbol('app');
|
|
675
|
-
await defineSwcAppBody(w)
|
|
1063
|
+
await defineSwcAppBody(w);
|
|
676
1064
|
const appElement = w.document.querySelector('#app') as SwcAppInterface;
|
|
677
1065
|
appElement.connect({
|
|
678
1066
|
container,
|
|
@@ -684,7 +1072,7 @@ appElement.connect({
|
|
|
684
1072
|
});
|
|
685
1073
|
```
|
|
686
1074
|
|
|
687
|
-
|
|
1075
|
+
#### 12. **Routing with @subscribeSwcAppRouteChangeWhileConnected**
|
|
688
1076
|
|
|
689
1077
|
SWC provides declarative routing with automatic path matching, order-based execution, and optional propagation control.
|
|
690
1078
|
|
|
@@ -723,10 +1111,11 @@ class RootRouter extends HTMLElement {
|
|
|
723
1111
|
#### Route Handler Features
|
|
724
1112
|
|
|
725
1113
|
**1. Path Matching**
|
|
726
|
-
-
|
|
727
|
-
-
|
|
728
|
-
-
|
|
729
|
-
-
|
|
1114
|
+
- **No path pattern** (omit path): `@subscribeSwcAppRouteChangeWhileConnected({ order: -1 })` - matches all routes
|
|
1115
|
+
- **Exact match**: `['', '/']` - matches home route only
|
|
1116
|
+
- **Prefix match**: `['/products']` - matches `/products` and `/products/...`
|
|
1117
|
+
- **Dynamic segments**: `['/product/{id}']` - captures `id` parameter
|
|
1118
|
+
- **Wildcard**: `['/{tail:.*}']` - matches any remaining path (use for 404)
|
|
730
1119
|
|
|
731
1120
|
**2. Order-Based Execution**
|
|
732
1121
|
Routes are executed in order of `order` value (lowest first). First matching route with a return value stops propagation:
|
|
@@ -750,7 +1139,7 @@ handleAdmin(routerPathSet: RouterEventType) {
|
|
|
750
1139
|
}
|
|
751
1140
|
|
|
752
1141
|
// This handler continues propagation (no return value)
|
|
753
|
-
@subscribeSwcAppRouteChangeWhileConnected(
|
|
1142
|
+
@subscribeSwcAppRouteChangeWhileConnected({ order: -1 })
|
|
754
1143
|
onRouteChange(routerPathSet: RouterEventType) {
|
|
755
1144
|
console.log('Route changed:', routerPathSet.path);
|
|
756
1145
|
// No return value → continues to next handler
|
|
@@ -762,8 +1151,8 @@ onRouteChange(routerPathSet: RouterEventType) {
|
|
|
762
1151
|
```typescript
|
|
763
1152
|
@elementDefine('accommodation-router')
|
|
764
1153
|
class AccommodationRouter extends HTMLElement {
|
|
765
|
-
// Global route logger (order: -1 runs first, no
|
|
766
|
-
@subscribeSwcAppRouteChangeWhileConnected(
|
|
1154
|
+
// Global route logger (order: -1 runs first, no path pattern = matches all routes)
|
|
1155
|
+
@subscribeSwcAppRouteChangeWhileConnected({ order: -1 })
|
|
767
1156
|
onRouteChange(routerPathSet: RouterEventType) {
|
|
768
1157
|
console.log('[Route Change]', {
|
|
769
1158
|
path: routerPathSet.path,
|
|
@@ -851,37 +1240,252 @@ URL: /product/123
|
|
|
851
1240
|
pathData: { id: '123' }
|
|
852
1241
|
```
|
|
853
1242
|
|
|
1243
|
+
#### Multiple Decorators with Shared Return Value
|
|
1244
|
+
|
|
1245
|
+
When using multiple decorators on the same method, each decorator can extract its own value from the return object using its symbol key or custom `valueKey` option.
|
|
1246
|
+
|
|
1247
|
+
**How It Works:**
|
|
1248
|
+
1. Each decorator has a default symbol key (e.g., `ATTRIBUTE_METADATA_KEY`, `PROPERTY_METADATA_KEY`)
|
|
1249
|
+
2. When a method returns an object, each decorator checks if its key exists in the object
|
|
1250
|
+
3. If the key exists, the decorator uses that value; otherwise uses the entire return value
|
|
1251
|
+
4. This allows multiple decorators to extract different values from the same return object
|
|
1252
|
+
|
|
1253
|
+
**Method 1: Using Default Symbol Keys**
|
|
1254
|
+
|
|
1255
|
+
```typescript
|
|
1256
|
+
import {
|
|
1257
|
+
ATTRIBUTE_METADATA_KEY,
|
|
1258
|
+
PROPERTY_METADATA_KEY,
|
|
1259
|
+
STYLE_METADATA_KEY,
|
|
1260
|
+
CLASS_METADATA_KEY
|
|
1261
|
+
} from '@dooboostore/simple-web-component';
|
|
1262
|
+
|
|
1263
|
+
@elementDefine('multi-decorator-example')
|
|
1264
|
+
class MultiDecoratorExample extends HTMLElement {
|
|
1265
|
+
@setAttribute('selector')
|
|
1266
|
+
@setProperty('selector')
|
|
1267
|
+
@updateStyle()
|
|
1268
|
+
@updateClass()
|
|
1269
|
+
handleUpdate() {
|
|
1270
|
+
return {
|
|
1271
|
+
[ATTRIBUTE_METADATA_KEY]: 'attribute-value',
|
|
1272
|
+
[PROPERTY_METADATA_KEY]: 'property-value',
|
|
1273
|
+
[STYLE_METADATA_KEY]: { color: 'red', fontSize: '16px' },
|
|
1274
|
+
[CLASS_METADATA_KEY]: { 'active': true, 'disabled': false }
|
|
1275
|
+
};
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
```
|
|
1279
|
+
|
|
1280
|
+
**Method 2: Using Custom valueKey Option**
|
|
1281
|
+
|
|
1282
|
+
For more readable code, use the `valueKey` option to specify custom keys:
|
|
1283
|
+
|
|
1284
|
+
```typescript
|
|
1285
|
+
@elementDefine('custom-key-example')
|
|
1286
|
+
class CustomKeyExample extends HTMLElement {
|
|
1287
|
+
@setAttribute('selector', { valueKey: 'attrValue' })
|
|
1288
|
+
@setProperty('selector', { valueKey: 'propValue' })
|
|
1289
|
+
@updateStyle({ valueKey: 'styleValue' })
|
|
1290
|
+
@updateClass({ valueKey: 'classValue' })
|
|
1291
|
+
handleUpdate() {
|
|
1292
|
+
return {
|
|
1293
|
+
attrValue: 'attribute-value',
|
|
1294
|
+
propValue: 'property-value',
|
|
1295
|
+
styleValue: { color: 'red', fontSize: '16px' },
|
|
1296
|
+
classValue: { 'active': true, 'disabled': false }
|
|
1297
|
+
};
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
```
|
|
1301
|
+
|
|
1302
|
+
**Method 3: Mixing Symbol Keys and Custom Keys**
|
|
1303
|
+
|
|
1304
|
+
You can mix both approaches in the same method:
|
|
1305
|
+
|
|
1306
|
+
```typescript
|
|
1307
|
+
@elementDefine('mixed-keys-example')
|
|
1308
|
+
class MixedKeysExample extends HTMLElement {
|
|
1309
|
+
@setAttribute('selector') // Uses default ATTRIBUTE_METADATA_KEY
|
|
1310
|
+
@setProperty('selector', { valueKey: 'customProp' }) // Uses custom key
|
|
1311
|
+
@updateStyle({ valueKey: 'styles' }) // Uses custom key
|
|
1312
|
+
handleUpdate() {
|
|
1313
|
+
return {
|
|
1314
|
+
[ATTRIBUTE_METADATA_KEY]: 'attr-value',
|
|
1315
|
+
customProp: 'prop-value',
|
|
1316
|
+
styles: { color: 'blue' }
|
|
1317
|
+
};
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
```
|
|
1321
|
+
|
|
1322
|
+
**Supported Decorators with valueKey:**
|
|
1323
|
+
- `@applyAttribute` / `@setAttribute`
|
|
1324
|
+
- `@applyProperty` / `@setProperty`
|
|
1325
|
+
- `@applySlot` / `@clearSlot` / `@appendHtmlSlot` / etc.
|
|
1326
|
+
- `@applyNode` / `@replaceChildrenNode` / etc.
|
|
1327
|
+
- `@applyStyle` / `@updateStyle` / etc.
|
|
1328
|
+
- `@applyClass` / `@updateClass` / etc.
|
|
1329
|
+
- `@emitCustomEvent` / `@emit`
|
|
1330
|
+
- `@publishSwcAppMessage` / `@publish`
|
|
1331
|
+
|
|
1332
|
+
Each decorator will use only its corresponding value from the return object, preventing conflicts and allowing clean separation of concerns.
|
|
1333
|
+
|
|
854
1334
|
---
|
|
855
1335
|
|
|
856
|
-
###
|
|
1336
|
+
### 13. **Accommodation Pattern**
|
|
857
1337
|
Factory-based component registration with explicit DI.
|
|
858
1338
|
|
|
859
1339
|
```typescript
|
|
860
1340
|
// Component factory
|
|
861
|
-
import {SwcUtils, createElement, CreateElementConfig} from '@dooboostore/simple-web-component';
|
|
862
|
-
const tagName = 'my-component';
|
|
863
|
-
export interface MyComponent extends HTMLElement {
|
|
864
|
-
value: string;
|
|
865
|
-
}
|
|
866
|
-
export const MyComponent = (w: Window|Document, data?: CreateElementConfig) => {
|
|
867
|
-
return createElement<HTMLElement>(w, tagName, data);
|
|
868
|
-
};
|
|
869
1341
|
export default (w: Window) => {
|
|
1342
|
+
const tagName = 'my-component';
|
|
870
1343
|
const existing = w.customElements.get(tagName);
|
|
871
|
-
if (existing) return
|
|
1344
|
+
if (existing) return tagName;
|
|
872
1345
|
|
|
873
1346
|
@elementDefine(tagName, { window: w })
|
|
874
|
-
class
|
|
875
|
-
value: string = 'value';
|
|
1347
|
+
class MyComponent extends w.HTMLElement {
|
|
876
1348
|
@onInitialize
|
|
877
|
-
onconstructor(service: MyService) {
|
|
1349
|
+
onconstructor(@inject(MyService.SYMBOL) service: MyService) {
|
|
878
1350
|
this.service = service;
|
|
879
1351
|
}
|
|
880
1352
|
}
|
|
881
|
-
|
|
1353
|
+
|
|
1354
|
+
return tagName;
|
|
1355
|
+
};
|
|
1356
|
+
|
|
1357
|
+
// Boot factory
|
|
1358
|
+
export default (w: Window, container: symbol) => {
|
|
1359
|
+
serviceFactories.forEach(s => s(container));
|
|
882
1360
|
};
|
|
883
1361
|
|
|
1362
|
+
// Entry point
|
|
1363
|
+
const container = Symbol('app');
|
|
1364
|
+
await defineSwcAppBody(w);
|
|
1365
|
+
const appElement = w.document.querySelector('#app') as SwcAppInterface;
|
|
1366
|
+
appElement.connect({
|
|
1367
|
+
container,
|
|
1368
|
+
window: w,
|
|
1369
|
+
onStartedLazyDefineComponent: [...pageFactories, ...componentFactories],
|
|
1370
|
+
onEngineStarted: () => {
|
|
1371
|
+
appElement.innerHTML = '<root-router></root-router>';
|
|
1372
|
+
}
|
|
1373
|
+
});
|
|
1374
|
+
```
|
|
1375
|
+
---
|
|
1376
|
+
|
|
1377
|
+
## 🔄 Decorator API - Consolidated Pattern (v1.0.43+)
|
|
1378
|
+
|
|
1379
|
+
### Simplified Decorator API
|
|
1380
|
+
|
|
1381
|
+
The decorator API uses a consolidated pattern where all decorators support **optional selector parameter** that defaults to `$this` when omitted. This eliminates redundant `*This` functions.
|
|
1382
|
+
|
|
1383
|
+
#### Current Usage Pattern
|
|
1384
|
+
```typescript
|
|
1385
|
+
// Single function with optional selector
|
|
1386
|
+
@applyClass('selector', 'update')
|
|
1387
|
+
method() { ... }
|
|
1388
|
+
|
|
1389
|
+
@applyClass('update') // Selector defaults to $this
|
|
1390
|
+
method() { ... }
|
|
1391
|
+
|
|
1392
|
+
@applyClass // Bare decorator - selector defaults to $this
|
|
1393
|
+
method() { ... }
|
|
1394
|
+
```
|
|
1395
|
+
|
|
1396
|
+
#### Decorator Functions (Current API)
|
|
1397
|
+
|
|
1398
|
+
**applyClass.ts:**
|
|
1399
|
+
- `applyClass(selector?, options?)` - Set/update/add/remove/toggle classes
|
|
1400
|
+
- `setClass(selector?, options?)` - Replace all classes
|
|
1401
|
+
- `updateClass(selector?, options?)` - Toggle classes
|
|
1402
|
+
- `addClass(selector?, options?)` - Add classes
|
|
1403
|
+
- `removeClass(selector?, options?)` - Remove classes
|
|
1404
|
+
- `toggleClass(selector?, options?)` - Toggle classes
|
|
1405
|
+
|
|
1406
|
+
**applyStyle.ts:**
|
|
1407
|
+
- `applyStyle(selector?, options?)` - Set/update/remove styles
|
|
1408
|
+
- `setStyle(selector?, options?)` - Clear and set styles
|
|
1409
|
+
- `updateStyle(selector?, options?)` - Update/merge styles
|
|
1410
|
+
- `removeStyle(selector?, options?)` - Remove specific styles
|
|
1411
|
+
|
|
1412
|
+
**applyProperty.ts:**
|
|
1413
|
+
- `property(selector?, options?)` - Get/set element properties
|
|
1414
|
+
- `setProperty(selector?, options?)` - Set element properties
|
|
1415
|
+
|
|
1416
|
+
**applyAttribute.ts:**
|
|
1417
|
+
- `attribute(selector?, options?)` - Get/set element attributes
|
|
1418
|
+
- `setAttribute(selector?, options?)` - Set element attributes
|
|
1419
|
+
|
|
1420
|
+
**query.ts:**
|
|
1421
|
+
- `query(selector?, options?)` - Query single element (supports $this, $host, $appHost, etc.)
|
|
1422
|
+
|
|
1423
|
+
**queryAll.ts:**
|
|
1424
|
+
- `queryAll(selector?, options?)` - Query multiple elements
|
|
1425
|
+
|
|
1426
|
+
**emitCustomEvent.ts:**
|
|
1427
|
+
- `emitCustomEvent(target, type, options?)` - Emit custom events
|
|
1428
|
+
- `emit(target, type, options?)` - Short alias
|
|
1429
|
+
|
|
1430
|
+
#### Usage Examples
|
|
1431
|
+
|
|
1432
|
+
```typescript
|
|
1433
|
+
@elementDefine('my-component')
|
|
1434
|
+
class MyComponent extends HTMLElement {
|
|
1435
|
+
// Class management - all default to $this
|
|
1436
|
+
@updateClass()
|
|
1437
|
+
toggleActive() {
|
|
1438
|
+
return { 'active': this.isActive };
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1441
|
+
// Style management - all default to $this
|
|
1442
|
+
@updateStyle()
|
|
1443
|
+
applyTheme() {
|
|
1444
|
+
return { color: this.theme.color, fontSize: '16px' };
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
// Property binding - all default to $this
|
|
1448
|
+
@setProperty()
|
|
1449
|
+
updateValue() {
|
|
1450
|
+
return this.computedValue;
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1453
|
+
// Attribute binding - all default to $this
|
|
1454
|
+
@setAttribute()
|
|
1455
|
+
updateId() {
|
|
1456
|
+
return this.elementId;
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
// Query elements
|
|
1460
|
+
@query('#input')
|
|
1461
|
+
inputElement?: HTMLInputElement;
|
|
1462
|
+
|
|
1463
|
+
@queryAll('li')
|
|
1464
|
+
listItems?: HTMLLIElement[];
|
|
1465
|
+
|
|
1466
|
+
// Emit custom events
|
|
1467
|
+
@addEventListener('button', 'click')
|
|
1468
|
+
@emitCustomEvent('$this', 'item-selected')
|
|
1469
|
+
onItemSelect() {
|
|
1470
|
+
return { itemId: this.selectedId };
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
// With selector
|
|
1474
|
+
@updateClass('.card', 'update')
|
|
1475
|
+
updateCardClass() {
|
|
1476
|
+
return { 'highlighted': true };
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
884
1479
|
```
|
|
1480
|
+
|
|
1481
|
+
#### Key Features
|
|
1482
|
+
|
|
1483
|
+
1. **Reduced API Surface** - Consolidated decorators eliminate redundant `*This` functions
|
|
1484
|
+
2. **Cleaner Code** - Single function name with multiple overloads
|
|
1485
|
+
3. **Better Discoverability** - Intuitive parameter patterns
|
|
1486
|
+
4. **Flexible Usage** - Supports bare decorators, with options, and with selectors
|
|
1487
|
+
5. **Type Safe** - Full TypeScript support with proper type inference
|
|
1488
|
+
|
|
885
1489
|
---
|
|
886
1490
|
|
|
887
1491
|
## ⚠️ Critical Rules
|