@dooboostore/simple-web-component 1.0.42 → 1.0.43
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 +423 -68
- package/dist/cjs/decorators/addEventListener.js +10 -0
- package/dist/cjs/decorators/addEventListener.js.map +2 -2
- package/dist/cjs/decorators/applyAttribute.js +11 -2
- package/dist/cjs/decorators/applyAttribute.js.map +2 -2
- package/dist/cjs/decorators/applyClass.js +9 -2
- package/dist/cjs/decorators/applyClass.js.map +2 -2
- package/dist/cjs/decorators/applyNode.js +11 -2
- package/dist/cjs/decorators/applyNode.js.map +2 -2
- package/dist/cjs/decorators/applyProperty.js +10 -2
- 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 +9 -2
- package/dist/cjs/decorators/applyStyle.js.map +2 -2
- package/dist/cjs/decorators/emitCustomEvent.js +2 -0
- package/dist/cjs/decorators/emitCustomEvent.js.map +2 -2
- package/dist/cjs/decorators/publishSwcAppMessage.js +37 -23
- package/dist/cjs/decorators/publishSwcAppMessage.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 +11 -2
- package/dist/esm/decorators/applyAttribute.js.map +2 -2
- package/dist/esm/decorators/applyClass.js +9 -2
- package/dist/esm/decorators/applyClass.js.map +2 -2
- package/dist/esm/decorators/applyNode.js +11 -2
- package/dist/esm/decorators/applyNode.js.map +2 -2
- package/dist/esm/decorators/applyProperty.js +10 -2
- 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 +9 -2
- package/dist/esm/decorators/applyStyle.js.map +2 -2
- package/dist/esm/decorators/emitCustomEvent.js +2 -0
- package/dist/esm/decorators/emitCustomEvent.js.map +2 -2
- package/dist/esm/decorators/publishSwcAppMessage.js +37 -23
- package/dist/esm/decorators/publishSwcAppMessage.js.map +2 -2
- package/dist/esm-bundle/dooboostore-simple-web-component.esm.js +146 -71
- package/dist/esm-bundle/dooboostore-simple-web-component.esm.js.map +3 -3
- package/dist/types/decorators/addEventListener.d.ts +5 -0
- package/dist/types/decorators/addEventListener.d.ts.map +1 -1
- package/dist/types/decorators/applyAttribute.d.ts +26 -0
- package/dist/types/decorators/applyAttribute.d.ts.map +1 -1
- package/dist/types/decorators/applyClass.d.ts +6 -0
- package/dist/types/decorators/applyClass.d.ts.map +1 -1
- package/dist/types/decorators/applyNode.d.ts +6 -0
- package/dist/types/decorators/applyNode.d.ts.map +1 -1
- package/dist/types/decorators/applyProperty.d.ts +26 -0
- 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 +6 -0
- package/dist/types/decorators/applyStyle.d.ts.map +1 -1
- package/dist/types/decorators/emitCustomEvent.d.ts +17 -0
- package/dist/types/decorators/emitCustomEvent.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/umd-bundle/dooboostore-simple-web-component.umd.js +146 -71
- package/dist/umd-bundle/dooboostore-simple-web-component.umd.js.map +3 -3
- package/package.json +7 -9
- package/src/decorators/addEventListener.ts +7 -0
- package/src/decorators/applyAttribute.ts +58 -2
- package/src/decorators/applyClass.ts +24 -2
- package/src/decorators/applyNode.ts +26 -2
- package/src/decorators/applyProperty.ts +57 -2
- package/src/decorators/applySlot.ts +24 -10
- package/src/decorators/applyStyle.ts +24 -2
- package/src/decorators/emitCustomEvent.ts +19 -0
- package/src/decorators/publishSwcAppMessage.ts +71 -27
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')
|
|
@@ -421,28 +478,204 @@ onconstructor() {
|
|
|
421
478
|
}
|
|
422
479
|
```
|
|
423
480
|
|
|
424
|
-
### 7. **
|
|
481
|
+
### 7. **DOM Manipulation with @applyNode**
|
|
482
|
+
|
|
483
|
+
Surgically add, replace, or remove nodes in the DOM with fine-grained control.
|
|
425
484
|
|
|
426
485
|
```typescript
|
|
427
|
-
@
|
|
428
|
-
|
|
429
|
-
|
|
486
|
+
@elementDefine('content-updater')
|
|
487
|
+
class ContentUpdater extends HTMLElement {
|
|
488
|
+
@addEventListener('button', 'click')
|
|
489
|
+
@replaceChildrenLightNodeThis
|
|
490
|
+
updateContent() {
|
|
491
|
+
return `<div>New content</div>`;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
@addEventListener('.append-btn', 'click')
|
|
495
|
+
@beforeEndLightNodeThis
|
|
496
|
+
appendContent() {
|
|
497
|
+
return `<p>Appended content</p>`;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
@addEventListener('.prepend-btn', 'click')
|
|
501
|
+
@afterBeginLightNodeThis
|
|
502
|
+
prependContent() {
|
|
503
|
+
return `<p>Prepended content</p>`;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
@onConnectedLight
|
|
507
|
+
render() {
|
|
508
|
+
return `
|
|
509
|
+
<div>
|
|
510
|
+
<button class="append-btn">Append</button>
|
|
511
|
+
<button class="prepend-btn">Prepend</button>
|
|
512
|
+
<button>Replace</button>
|
|
513
|
+
</div>
|
|
514
|
+
`;
|
|
515
|
+
}
|
|
430
516
|
}
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
**@applyNode Decorator Variants:**
|
|
520
|
+
- `replaceChildrenNodeThis()` - Replace all children
|
|
521
|
+
- `replaceChildrenLightNodeThis()` - Replace children in light DOM
|
|
522
|
+
- `beforeEndNodeThis()` - Append to end
|
|
523
|
+
- `beforeEndLightNodeThis()` - Append to light DOM end
|
|
524
|
+
- `afterBeginNodeThis()` - Prepend to beginning
|
|
525
|
+
- `afterBeginLightNodeThis()` - Prepend to light DOM beginning
|
|
526
|
+
- `innerHtmlNodeThis()` - Set innerHTML
|
|
527
|
+
- `innerHtmlLightNodeThis()` - Set innerHTML in light DOM
|
|
528
|
+
- `innerTextNodeThis()` - Set innerText
|
|
529
|
+
|
|
530
|
+
**Position Options:**
|
|
531
|
+
```typescript
|
|
532
|
+
export type ApplyNodePosition =
|
|
533
|
+
| 'beforeBegin' // Before element
|
|
534
|
+
| 'afterBegin' // After element opens
|
|
535
|
+
| 'beforeEnd' // Before element closes
|
|
536
|
+
| 'afterEnd' // After element closes
|
|
537
|
+
| 'replace' // Replace element
|
|
538
|
+
| 'replaceChildren' // Replace children
|
|
539
|
+
| 'innerHtml' // Set innerHTML
|
|
540
|
+
| 'innerText' // Set innerText
|
|
541
|
+
| 'remove'; // Remove element
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
### 7.5 **Style & Class Management**
|
|
545
|
+
|
|
546
|
+
Apply styles and classes dynamically with `@applyStyle` and `@applyClass`.
|
|
547
|
+
|
|
548
|
+
```typescript
|
|
549
|
+
@elementDefine('styled-component')
|
|
550
|
+
class StyledComponent extends HTMLElement {
|
|
551
|
+
@state('isActive')
|
|
552
|
+
isActive = false;
|
|
553
|
+
|
|
554
|
+
@addEventListener('button', 'click')
|
|
555
|
+
@updateStyleThis
|
|
556
|
+
toggleStyle() {
|
|
557
|
+
return {
|
|
558
|
+
color: this.isActive ? 'green' : 'red',
|
|
559
|
+
fontSize: '16px'
|
|
560
|
+
};
|
|
561
|
+
}
|
|
431
562
|
|
|
432
|
-
@
|
|
433
|
-
|
|
434
|
-
|
|
563
|
+
@addEventListener('.toggle-btn', 'click')
|
|
564
|
+
@updateClassThis
|
|
565
|
+
toggleClass() {
|
|
566
|
+
return {
|
|
567
|
+
'active': this.isActive,
|
|
568
|
+
'disabled': !this.isActive
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
@onConnectedLight
|
|
573
|
+
render() {
|
|
574
|
+
return `
|
|
575
|
+
<div>
|
|
576
|
+
<button class="toggle-btn">Toggle</button>
|
|
577
|
+
<button>Update Style</button>
|
|
578
|
+
</div>
|
|
579
|
+
`;
|
|
580
|
+
}
|
|
435
581
|
}
|
|
582
|
+
```
|
|
583
|
+
|
|
584
|
+
**@applyStyle Variants:**
|
|
585
|
+
- `setStyleThis()` - Clear and set styles
|
|
586
|
+
- `updateStyleThis()` - Update/merge styles
|
|
587
|
+
- `removeStyleThis()` - Remove specific styles
|
|
588
|
+
|
|
589
|
+
**@applyClass Variants:**
|
|
590
|
+
- `setClassThis()` - Replace all classes
|
|
591
|
+
- `updateClassThis()` - Toggle classes
|
|
592
|
+
- `addClassThis()` - Add classes
|
|
593
|
+
- `removeClassThis()` - Remove classes
|
|
594
|
+
- `toggleClassThis()` - Toggle classes
|
|
595
|
+
|
|
596
|
+
### 7.6 **Lifecycle Hooks**
|
|
597
|
+
|
|
598
|
+
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.
|
|
599
|
+
|
|
600
|
+
```typescript
|
|
601
|
+
@elementDefine('my-component')
|
|
602
|
+
class MyComponent extends HTMLElement {
|
|
603
|
+
@onInitialize({ order: 0 })
|
|
604
|
+
onInit() {
|
|
605
|
+
// Called during component construction
|
|
606
|
+
console.log('Component initialized');
|
|
607
|
+
}
|
|
436
608
|
|
|
609
|
+
@onConnectedBefore({ order: 0 })
|
|
610
|
+
beforeConnected() {
|
|
611
|
+
// Called before element connects to DOM
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
@onConnectedAfter({ order: 1 })
|
|
615
|
+
afterConnected() {
|
|
616
|
+
// Called after element connects to DOM
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
@onConnected
|
|
620
|
+
onConnected() {
|
|
621
|
+
// Called when element enters DOM
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
@onDisconnectedBefore
|
|
625
|
+
beforeDisconnected() {
|
|
626
|
+
// Called before element disconnects from DOM
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
@onDisconnected
|
|
630
|
+
onDisconnected() {
|
|
631
|
+
// Called when element leaves DOM
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
@onAdoptedBefore
|
|
635
|
+
beforeAdopted() {
|
|
636
|
+
// Called before element is adopted into new document
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
@onAdopted
|
|
640
|
+
onAdopted() {
|
|
641
|
+
// Called when element is adopted into new document
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
@onConnectedSwcApp({ order: 0 })
|
|
645
|
+
onSwcAppConnected() {
|
|
646
|
+
// Called after SwcApp.connect() completes
|
|
647
|
+
// Full DI support available through hostSet
|
|
648
|
+
}
|
|
437
649
|
|
|
438
|
-
@
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
console.log('Application initialization complete!');
|
|
650
|
+
@onConnectedCompleted({ order: 0 })
|
|
651
|
+
onConnectedCompleted() {
|
|
652
|
+
// Called after all connected lifecycle hooks complete
|
|
653
|
+
}
|
|
443
654
|
}
|
|
444
655
|
```
|
|
445
656
|
|
|
657
|
+
**Lifecycle Execution Order:**
|
|
658
|
+
- `@onInitialize` - Component construction
|
|
659
|
+
- `@onConnectedBefore` - Before DOM connection
|
|
660
|
+
- `@onConnectedAfter` - After DOM connection
|
|
661
|
+
- `@onConnected` - DOM connection (with HTML rendering)
|
|
662
|
+
- `@onConnectedSwcApp` - After SwcApp initialization
|
|
663
|
+
- `@onConnectedCompleted` - All connected hooks done
|
|
664
|
+
- `@onDisconnectedBefore` - Before DOM disconnection
|
|
665
|
+
- `@onDisconnected` - DOM disconnection
|
|
666
|
+
- `@onAdoptedBefore` - Before document adoption
|
|
667
|
+
- `@onAdopted` - Document adoption
|
|
668
|
+
|
|
669
|
+
**Order Parameter:**
|
|
670
|
+
All lifecycle decorators support `order?: number` for controlling execution sequence:
|
|
671
|
+
```typescript
|
|
672
|
+
@onConnectedBefore({ order: 0 }) // Runs first
|
|
673
|
+
onFirst() { }
|
|
674
|
+
|
|
675
|
+
@onConnectedBefore({ order: 1 }) // Runs second
|
|
676
|
+
onSecond() { }
|
|
677
|
+
```
|
|
678
|
+
|
|
446
679
|
### 8. **Structural Directives**
|
|
447
680
|
|
|
448
681
|
Structural Directives allow declarative conditional rendering, list iteration, async handling, and routing with automatic attribute substitution and dynamic expressions.
|
|
@@ -528,6 +761,28 @@ class LoginForm extends HTMLElement {
|
|
|
528
761
|
}
|
|
529
762
|
```
|
|
530
763
|
|
|
764
|
+
**@publishSwcAppMessage Decorator Variants:**
|
|
765
|
+
- `publishSwcAppMessage()` - Publish without message type
|
|
766
|
+
- `publishSwcAppMessage(messageType)` - Publish with specific message type
|
|
767
|
+
- `publishSwcAppMessage(messageType, { valueKey: 'customKey' })` - Publish with custom value extraction
|
|
768
|
+
- `publishSwcAppMessage({ messageType: 'type', valueKey: 'customKey' })` - Publish with options object
|
|
769
|
+
- `publish()` - Short alias
|
|
770
|
+
- `publish(messageType)` - Short alias with message type
|
|
771
|
+
- `publish(messageType, options)` - Short alias with options
|
|
772
|
+
|
|
773
|
+
**Using valueKey for Multiple Decorators:**
|
|
774
|
+
|
|
775
|
+
```typescript
|
|
776
|
+
@publishSwcAppMessage('event1', { valueKey: 'detail1' })
|
|
777
|
+
@publishSwcAppMessage('event2', { valueKey: 'detail2' })
|
|
778
|
+
handleMultipleEvents() {
|
|
779
|
+
return {
|
|
780
|
+
detail1: { type: 'event1', data: 'value1' },
|
|
781
|
+
detail2: { type: 'event2', data: 'value2' }
|
|
782
|
+
};
|
|
783
|
+
}
|
|
784
|
+
```
|
|
785
|
+
|
|
531
786
|
**SwcAppMessage Structure:**
|
|
532
787
|
```typescript
|
|
533
788
|
type SwcAppMessage<T = any> = {
|
|
@@ -647,17 +902,15 @@ w.document.addEventListener('DOMContentLoaded', async () => {
|
|
|
647
902
|
|
|
648
903
|
```typescript
|
|
649
904
|
// Component factory
|
|
650
|
-
import {defineSwcApp} from "./SwcApp";
|
|
651
|
-
|
|
652
905
|
export default (w: Window) => {
|
|
653
906
|
const tagName = 'my-component';
|
|
654
907
|
const existing = w.customElements.get(tagName);
|
|
655
908
|
if (existing) return tagName;
|
|
656
909
|
|
|
657
|
-
@elementDefine(tagName, {window: w})
|
|
910
|
+
@elementDefine(tagName, { window: w })
|
|
658
911
|
class MyComponent extends w.HTMLElement {
|
|
659
912
|
@onInitialize
|
|
660
|
-
onconstructor(service: MyService) {
|
|
913
|
+
onconstructor(@inject(MyService.SYMBOL) service: MyService) {
|
|
661
914
|
this.service = service;
|
|
662
915
|
}
|
|
663
916
|
}
|
|
@@ -672,7 +925,7 @@ export default (w: Window, container: symbol) => {
|
|
|
672
925
|
|
|
673
926
|
// Entry point
|
|
674
927
|
const container = Symbol('app');
|
|
675
|
-
await defineSwcAppBody(w)
|
|
928
|
+
await defineSwcAppBody(w);
|
|
676
929
|
const appElement = w.document.querySelector('#app') as SwcAppInterface;
|
|
677
930
|
appElement.connect({
|
|
678
931
|
container,
|
|
@@ -684,7 +937,7 @@ appElement.connect({
|
|
|
684
937
|
});
|
|
685
938
|
```
|
|
686
939
|
|
|
687
|
-
|
|
940
|
+
#### 11. **Routing with @subscribeSwcAppRouteChangeWhileConnected**
|
|
688
941
|
|
|
689
942
|
SWC provides declarative routing with automatic path matching, order-based execution, and optional propagation control.
|
|
690
943
|
|
|
@@ -723,10 +976,11 @@ class RootRouter extends HTMLElement {
|
|
|
723
976
|
#### Route Handler Features
|
|
724
977
|
|
|
725
978
|
**1. Path Matching**
|
|
726
|
-
-
|
|
727
|
-
-
|
|
728
|
-
-
|
|
729
|
-
-
|
|
979
|
+
- **No path pattern** (omit path): `@subscribeSwcAppRouteChangeWhileConnected({ order: -1 })` - matches all routes
|
|
980
|
+
- **Exact match**: `['', '/']` - matches home route only
|
|
981
|
+
- **Prefix match**: `['/products']` - matches `/products` and `/products/...`
|
|
982
|
+
- **Dynamic segments**: `['/product/{id}']` - captures `id` parameter
|
|
983
|
+
- **Wildcard**: `['/{tail:.*}']` - matches any remaining path (use for 404)
|
|
730
984
|
|
|
731
985
|
**2. Order-Based Execution**
|
|
732
986
|
Routes are executed in order of `order` value (lowest first). First matching route with a return value stops propagation:
|
|
@@ -750,7 +1004,7 @@ handleAdmin(routerPathSet: RouterEventType) {
|
|
|
750
1004
|
}
|
|
751
1005
|
|
|
752
1006
|
// This handler continues propagation (no return value)
|
|
753
|
-
@subscribeSwcAppRouteChangeWhileConnected(
|
|
1007
|
+
@subscribeSwcAppRouteChangeWhileConnected({ order: -1 })
|
|
754
1008
|
onRouteChange(routerPathSet: RouterEventType) {
|
|
755
1009
|
console.log('Route changed:', routerPathSet.path);
|
|
756
1010
|
// No return value → continues to next handler
|
|
@@ -762,8 +1016,8 @@ onRouteChange(routerPathSet: RouterEventType) {
|
|
|
762
1016
|
```typescript
|
|
763
1017
|
@elementDefine('accommodation-router')
|
|
764
1018
|
class AccommodationRouter extends HTMLElement {
|
|
765
|
-
// Global route logger (order: -1 runs first, no
|
|
766
|
-
@subscribeSwcAppRouteChangeWhileConnected(
|
|
1019
|
+
// Global route logger (order: -1 runs first, no path pattern = matches all routes)
|
|
1020
|
+
@subscribeSwcAppRouteChangeWhileConnected({ order: -1 })
|
|
767
1021
|
onRouteChange(routerPathSet: RouterEventType) {
|
|
768
1022
|
console.log('[Route Change]', {
|
|
769
1023
|
path: routerPathSet.path,
|
|
@@ -851,6 +1105,97 @@ URL: /product/123
|
|
|
851
1105
|
pathData: { id: '123' }
|
|
852
1106
|
```
|
|
853
1107
|
|
|
1108
|
+
#### Multiple Decorators with Shared Return Value
|
|
1109
|
+
|
|
1110
|
+
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.
|
|
1111
|
+
|
|
1112
|
+
**How It Works:**
|
|
1113
|
+
1. Each decorator has a default symbol key (e.g., `ATTRIBUTE_METADATA_KEY`, `PROPERTY_METADATA_KEY`)
|
|
1114
|
+
2. When a method returns an object, each decorator checks if its key exists in the object
|
|
1115
|
+
3. If the key exists, the decorator uses that value; otherwise uses the entire return value
|
|
1116
|
+
4. This allows multiple decorators to extract different values from the same return object
|
|
1117
|
+
|
|
1118
|
+
**Method 1: Using Default Symbol Keys**
|
|
1119
|
+
|
|
1120
|
+
```typescript
|
|
1121
|
+
import {
|
|
1122
|
+
ATTRIBUTE_METADATA_KEY,
|
|
1123
|
+
PROPERTY_METADATA_KEY,
|
|
1124
|
+
STYLE_METADATA_KEY,
|
|
1125
|
+
CLASS_METADATA_KEY
|
|
1126
|
+
} from '@dooboostore/simple-web-component';
|
|
1127
|
+
|
|
1128
|
+
@elementDefine('multi-decorator-example')
|
|
1129
|
+
class MultiDecoratorExample extends HTMLElement {
|
|
1130
|
+
@setAttribute('selector')
|
|
1131
|
+
@setProperty('selector')
|
|
1132
|
+
@updateStyleThis
|
|
1133
|
+
@updateClassThis
|
|
1134
|
+
handleUpdate() {
|
|
1135
|
+
return {
|
|
1136
|
+
[ATTRIBUTE_METADATA_KEY]: 'attribute-value',
|
|
1137
|
+
[PROPERTY_METADATA_KEY]: 'property-value',
|
|
1138
|
+
[STYLE_METADATA_KEY]: { color: 'red', fontSize: '16px' },
|
|
1139
|
+
[CLASS_METADATA_KEY]: { 'active': true, 'disabled': false }
|
|
1140
|
+
};
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
```
|
|
1144
|
+
|
|
1145
|
+
**Method 2: Using Custom valueKey Option**
|
|
1146
|
+
|
|
1147
|
+
For more readable code, use the `valueKey` option to specify custom keys:
|
|
1148
|
+
|
|
1149
|
+
```typescript
|
|
1150
|
+
@elementDefine('custom-key-example')
|
|
1151
|
+
class CustomKeyExample extends HTMLElement {
|
|
1152
|
+
@setAttribute('selector', { valueKey: 'attrValue' })
|
|
1153
|
+
@setProperty('selector', { valueKey: 'propValue' })
|
|
1154
|
+
@updateStyleThis({ valueKey: 'styleValue' })
|
|
1155
|
+
@updateClassThis({ valueKey: 'classValue' })
|
|
1156
|
+
handleUpdate() {
|
|
1157
|
+
return {
|
|
1158
|
+
attrValue: 'attribute-value',
|
|
1159
|
+
propValue: 'property-value',
|
|
1160
|
+
styleValue: { color: 'red', fontSize: '16px' },
|
|
1161
|
+
classValue: { 'active': true, 'disabled': false }
|
|
1162
|
+
};
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
```
|
|
1166
|
+
|
|
1167
|
+
**Method 3: Mixing Symbol Keys and Custom Keys**
|
|
1168
|
+
|
|
1169
|
+
You can mix both approaches in the same method:
|
|
1170
|
+
|
|
1171
|
+
```typescript
|
|
1172
|
+
@elementDefine('mixed-keys-example')
|
|
1173
|
+
class MixedKeysExample extends HTMLElement {
|
|
1174
|
+
@setAttribute('selector') // Uses default ATTRIBUTE_METADATA_KEY
|
|
1175
|
+
@setProperty('selector', { valueKey: 'customProp' }) // Uses custom key
|
|
1176
|
+
@updateStyleThis({ valueKey: 'styles' }) // Uses custom key
|
|
1177
|
+
handleUpdate() {
|
|
1178
|
+
return {
|
|
1179
|
+
[ATTRIBUTE_METADATA_KEY]: 'attr-value',
|
|
1180
|
+
customProp: 'prop-value',
|
|
1181
|
+
styles: { color: 'blue' }
|
|
1182
|
+
};
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
```
|
|
1186
|
+
|
|
1187
|
+
**Supported Decorators with valueKey:**
|
|
1188
|
+
- `@applyAttribute` / `@setAttribute` / `@setAttributeThis`
|
|
1189
|
+
- `@applyProperty` / `@setProperty` / `@setPropertyThis`
|
|
1190
|
+
- `@applySlot` / `@clearSlot` / `@appendHtmlSlot` / etc.
|
|
1191
|
+
- `@applyNode` / `@replaceChildrenNodeThis` / etc.
|
|
1192
|
+
- `@applyStyle` / `@updateStyleThis` / etc.
|
|
1193
|
+
- `@applyClass` / `@updateClassThis` / etc.
|
|
1194
|
+
- `@emitCustomEvent` / `@emit` / `@emitCustomEventThis`
|
|
1195
|
+
- `@publishSwcAppMessage` / `@publish`
|
|
1196
|
+
|
|
1197
|
+
Each decorator will use only its corresponding value from the return object, preventing conflicts and allowing clean separation of concerns.
|
|
1198
|
+
|
|
854
1199
|
---
|
|
855
1200
|
|
|
856
1201
|
### 12. **Accommodation Pattern**
|
|
@@ -858,29 +1203,39 @@ Factory-based component registration with explicit DI.
|
|
|
858
1203
|
|
|
859
1204
|
```typescript
|
|
860
1205
|
// 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
1206
|
export default (w: Window) => {
|
|
1207
|
+
const tagName = 'my-component';
|
|
870
1208
|
const existing = w.customElements.get(tagName);
|
|
871
|
-
if (existing) return
|
|
1209
|
+
if (existing) return tagName;
|
|
872
1210
|
|
|
873
1211
|
@elementDefine(tagName, { window: w })
|
|
874
|
-
class
|
|
875
|
-
value: string = 'value';
|
|
1212
|
+
class MyComponent extends w.HTMLElement {
|
|
876
1213
|
@onInitialize
|
|
877
|
-
onconstructor(service: MyService) {
|
|
1214
|
+
onconstructor(@inject(MyService.SYMBOL) service: MyService) {
|
|
878
1215
|
this.service = service;
|
|
879
1216
|
}
|
|
880
1217
|
}
|
|
881
|
-
|
|
1218
|
+
|
|
1219
|
+
return tagName;
|
|
1220
|
+
};
|
|
1221
|
+
|
|
1222
|
+
// Boot factory
|
|
1223
|
+
export default (w: Window, container: symbol) => {
|
|
1224
|
+
serviceFactories.forEach(s => s(container));
|
|
882
1225
|
};
|
|
883
1226
|
|
|
1227
|
+
// Entry point
|
|
1228
|
+
const container = Symbol('app');
|
|
1229
|
+
await defineSwcAppBody(w);
|
|
1230
|
+
const appElement = w.document.querySelector('#app') as SwcAppInterface;
|
|
1231
|
+
appElement.connect({
|
|
1232
|
+
container,
|
|
1233
|
+
window: w,
|
|
1234
|
+
onStartedLazyDefineComponent: [...pageFactories, ...componentFactories],
|
|
1235
|
+
onEngineStarted: () => {
|
|
1236
|
+
appElement.innerHTML = '<root-router></root-router>';
|
|
1237
|
+
}
|
|
1238
|
+
});
|
|
884
1239
|
```
|
|
885
1240
|
---
|
|
886
1241
|
|
|
@@ -27,6 +27,11 @@ __export(addEventListener_exports, {
|
|
|
27
27
|
addEventListenerDocument: () => addEventListenerDocument,
|
|
28
28
|
addEventListenerThis: () => addEventListenerThis,
|
|
29
29
|
addEventListenerWindow: () => addEventListenerWindow,
|
|
30
|
+
event: () => event,
|
|
31
|
+
eventDelegate: () => eventDelegate,
|
|
32
|
+
eventDelegateAllDom: () => eventDelegateAllDom,
|
|
33
|
+
eventDelegateLightDom: () => eventDelegateLightDom,
|
|
34
|
+
eventDelegateShadowDom: () => eventDelegateShadowDom,
|
|
30
35
|
getAddEventListenerMetadata: () => getAddEventListenerMetadata
|
|
31
36
|
});
|
|
32
37
|
module.exports = __toCommonJS(addEventListener_exports);
|
|
@@ -55,6 +60,11 @@ function addEventListenerDelegateAllDom(selector, type, options) {
|
|
|
55
60
|
function addEventListenerDelegate(selector, type, options) {
|
|
56
61
|
return addEventListener(selector, type, { ...options ?? {}, root: "auto", delegate: true });
|
|
57
62
|
}
|
|
63
|
+
const event = addEventListener;
|
|
64
|
+
const eventDelegateLightDom = addEventListenerDelegateLightDom;
|
|
65
|
+
const eventDelegateShadowDom = addEventListenerDelegateShadowDom;
|
|
66
|
+
const eventDelegateAllDom = addEventListenerDelegateAllDom;
|
|
67
|
+
const eventDelegate = addEventListenerDelegate;
|
|
58
68
|
function addEventListenerThis(type, options) {
|
|
59
69
|
return addEventListener("$this", type, options);
|
|
60
70
|
}
|