@dooboostore/simple-web-component 1.0.31 → 1.0.33
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 +280 -43
- package/dist/cjs/decorators/applyStyle.js +1 -1
- package/dist/cjs/decorators/applyStyle.js.map +2 -2
- package/dist/cjs/decorators/attribute.js +17 -1
- package/dist/cjs/decorators/attribute.js.map +2 -2
- package/dist/cjs/decorators/elementDefine.js +27 -10
- package/dist/cjs/decorators/elementDefine.js.map +3 -3
- package/dist/cjs/decorators/index.js +3 -1
- package/dist/cjs/decorators/index.js.map +2 -2
- package/dist/cjs/decorators/publishSwcAppMessage.js +86 -0
- package/dist/cjs/decorators/publishSwcAppMessage.js.map +7 -0
- package/dist/cjs/decorators/subscribeSwcAppMessageWhileConnected.js +62 -0
- package/dist/cjs/decorators/subscribeSwcAppMessageWhileConnected.js.map +7 -0
- package/dist/cjs/decorators/{subscribeSwcAppRouteChange.js → subscribeSwcAppRouteChangeWhileConnected.js} +16 -16
- package/dist/cjs/decorators/subscribeSwcAppRouteChangeWhileConnected.js.map +7 -0
- package/dist/cjs/elements/SwcAppMixin.js +32 -3
- package/dist/cjs/elements/SwcAppMixin.js.map +2 -2
- package/dist/cjs/elements/SwcAsync.js +69 -57
- package/dist/cjs/elements/SwcAsync.js.map +2 -2
- package/dist/cjs/elements/SwcChoose.js +66 -60
- package/dist/cjs/elements/SwcChoose.js.map +2 -2
- package/dist/cjs/elements/SwcIf.js +51 -66
- package/dist/cjs/elements/SwcIf.js.map +2 -2
- package/dist/cjs/elements/SwcLoop.js +58 -78
- package/dist/cjs/elements/SwcLoop.js.map +2 -2
- package/dist/cjs/elements/register.js +310 -335
- package/dist/cjs/elements/register.js.map +2 -2
- package/dist/cjs/types.js.map +1 -1
- package/dist/cjs/utils/Utils.js +7 -13
- package/dist/cjs/utils/Utils.js.map +2 -2
- package/dist/esm/decorators/applyStyle.js +1 -1
- package/dist/esm/decorators/applyStyle.js.map +2 -2
- package/dist/esm/decorators/attribute.js +18 -2
- package/dist/esm/decorators/attribute.js.map +2 -2
- package/dist/esm/decorators/elementDefine.js +25 -8
- package/dist/esm/decorators/elementDefine.js.map +3 -3
- package/dist/esm/decorators/index.js +3 -1
- package/dist/esm/decorators/index.js.map +2 -2
- package/dist/esm/decorators/publishSwcAppMessage.js +67 -0
- package/dist/esm/decorators/publishSwcAppMessage.js.map +7 -0
- package/dist/esm/decorators/subscribeSwcAppMessageWhileConnected.js +43 -0
- package/dist/esm/decorators/subscribeSwcAppMessageWhileConnected.js.map +7 -0
- package/dist/esm/decorators/{subscribeSwcAppRouteChange.js → subscribeSwcAppRouteChangeWhileConnected.js} +13 -13
- package/dist/esm/decorators/subscribeSwcAppRouteChangeWhileConnected.js.map +7 -0
- package/dist/esm/elements/SwcAppMixin.js +32 -3
- package/dist/esm/elements/SwcAppMixin.js.map +2 -2
- package/dist/esm/elements/SwcAsync.js +70 -58
- package/dist/esm/elements/SwcAsync.js.map +2 -2
- package/dist/esm/elements/SwcChoose.js +67 -61
- package/dist/esm/elements/SwcChoose.js.map +2 -2
- package/dist/esm/elements/SwcIf.js +52 -67
- package/dist/esm/elements/SwcIf.js.map +2 -2
- package/dist/esm/elements/SwcLoop.js +59 -79
- package/dist/esm/elements/SwcLoop.js.map +2 -2
- package/dist/esm/elements/register.js +239 -264
- package/dist/esm/elements/register.js.map +2 -2
- package/dist/esm/types.js.map +1 -1
- package/dist/esm/utils/Utils.js +7 -13
- package/dist/esm/utils/Utils.js.map +2 -2
- package/dist/esm-bundle/dooboostore-simple-web-component.esm.js +2804 -2498
- package/dist/esm-bundle/dooboostore-simple-web-component.esm.js.map +4 -4
- package/dist/types/decorators/attribute.d.ts.map +1 -1
- package/dist/types/decorators/elementDefine.d.ts +1 -0
- package/dist/types/decorators/elementDefine.d.ts.map +1 -1
- package/dist/types/decorators/index.d.ts +3 -1
- package/dist/types/decorators/index.d.ts.map +1 -1
- package/dist/types/decorators/publishSwcAppMessage.d.ts +13 -0
- package/dist/types/decorators/publishSwcAppMessage.d.ts.map +1 -0
- package/dist/types/decorators/subscribeSwcAppMessageWhileConnected.d.ts +16 -0
- package/dist/types/decorators/subscribeSwcAppMessageWhileConnected.d.ts.map +1 -0
- package/dist/types/decorators/subscribeSwcAppRouteChangeWhileConnected.d.ts +18 -0
- package/dist/types/decorators/subscribeSwcAppRouteChangeWhileConnected.d.ts.map +1 -0
- package/dist/types/elements/SwcAppMixin.d.ts +3 -0
- package/dist/types/elements/SwcAppMixin.d.ts.map +1 -1
- package/dist/types/elements/register.d.ts.map +1 -1
- package/dist/types/types.d.ts +14 -1
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/utils/Utils.d.ts.map +1 -1
- package/dist/umd-bundle/dooboostore-simple-web-component.umd.js +2804 -2498
- package/dist/umd-bundle/dooboostore-simple-web-component.umd.js.map +4 -4
- package/package.json +4 -4
- package/src/decorators/applyStyle.ts +1 -1
- package/src/decorators/attribute.ts +35 -30
- package/src/decorators/elementDefine.ts +28 -8
- package/src/decorators/index.ts +3 -1
- package/src/decorators/publishSwcAppMessage.ts +100 -0
- package/src/decorators/subscribeSwcAppMessageWhileConnected.ts +73 -0
- package/src/decorators/{subscribeSwcAppRouteChange.ts → subscribeSwcAppRouteChangeWhileConnected.ts} +19 -19
- package/src/elements/SwcAppMixin.ts +43 -4
- package/src/elements/SwcAsync.ts +75 -69
- package/src/elements/SwcChoose.ts +67 -80
- package/src/elements/SwcIf.ts +51 -76
- package/src/elements/SwcLoop.ts +56 -87
- package/src/elements/register.ts +235 -307
- package/src/types.ts +14 -1
- package/src/utils/Utils.ts +11 -17
- package/dist/cjs/decorators/subscribeSwcAppRouteChange.js.map +0 -7
- package/dist/esm/decorators/subscribeSwcAppRouteChange.js.map +0 -7
- package/dist/types/decorators/subscribeSwcAppRouteChange.d.ts +0 -18
- package/dist/types/decorators/subscribeSwcAppRouteChange.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -251,6 +251,35 @@ onAppReady(router: Router) {
|
|
|
251
251
|
|
|
252
252
|
Structural Directives allow declarative conditional rendering, list iteration, async handling, and routing with automatic attribute substitution and dynamic expressions.
|
|
253
253
|
|
|
254
|
+
#### 8.0 **Expression Syntax - Dynamic Evaluation**
|
|
255
|
+
|
|
256
|
+
SWC supports two types of expression syntax for dynamic value evaluation:
|
|
257
|
+
|
|
258
|
+
**`{{ }} - Standard Expression Evaluation**
|
|
259
|
+
- Used for boolean conditions and value comparisons
|
|
260
|
+
- Syntax: `{{ expression }}`
|
|
261
|
+
- Example: `{{ $value === 'pending' }}`, `{{ $host.isLoggedIn }}`
|
|
262
|
+
- Context variables: `$value`, `$item`, `$index`, `$host`, `$parentHost`, `$appHost`, etc.
|
|
263
|
+
|
|
264
|
+
**`{{= }} - Function Call & Return Expression**
|
|
265
|
+
- Evaluates JavaScript expressions and returns result
|
|
266
|
+
- Syntax: `{{= functionCall() }}` or `{{= computedValue }}`
|
|
267
|
+
- Automatically executes functions and captures return values
|
|
268
|
+
- Example: `{{= $item.name }}`, `{{= $parentHost.getData() }}`, `{{= formatDate($item.date) }}`
|
|
269
|
+
- Used in attributes for dynamic substitution
|
|
270
|
+
- Result is converted to string for DOM attributes
|
|
271
|
+
|
|
272
|
+
**Context Variables Available in All Directives:**
|
|
273
|
+
- `$value` - the value passed to the template
|
|
274
|
+
- `$host` - the current Web Component instance
|
|
275
|
+
- `$parentHost` - parent component
|
|
276
|
+
- `$appHost` - root application component
|
|
277
|
+
- `$item` - current item (in SwcLoop)
|
|
278
|
+
- `$index` - current index (in SwcLoop)
|
|
279
|
+
- Helper functions - utility methods from `SwcUtils.getHelperAndHostSet()`
|
|
280
|
+
|
|
281
|
+
---
|
|
282
|
+
|
|
254
283
|
#### 8.1 **SwcChoose - Multi-Condition Rendering**
|
|
255
284
|
|
|
256
285
|
Switch-case style conditional rendering with `swc-when` and `swc-otherwise` sub-templates.
|
|
@@ -271,17 +300,17 @@ class StatusDisplay extends HTMLElement {
|
|
|
271
300
|
render() {
|
|
272
301
|
return `
|
|
273
302
|
<template id="status-template" is="swc-choose">
|
|
274
|
-
<!-- Pending State -->
|
|
303
|
+
<!-- Pending State: Using {{ }} for condition -->
|
|
275
304
|
<template is="swc-when" value="{{ $value === 'pending' }}">
|
|
276
305
|
<div style="color: orange;">⏳ Processing...</div>
|
|
277
306
|
</template>
|
|
278
307
|
|
|
279
|
-
<!-- Success State -->
|
|
308
|
+
<!-- Success State: Using {{ }} for condition -->
|
|
280
309
|
<template is="swc-when" value="{{ $value === 'success' }}">
|
|
281
310
|
<div style="color: green;">✓ Completed</div>
|
|
282
311
|
</template>
|
|
283
312
|
|
|
284
|
-
<!-- Error State -->
|
|
313
|
+
<!-- Error State: Using {{ }} for condition -->
|
|
285
314
|
<template is="swc-when" value="{{ $value === 'error' }}">
|
|
286
315
|
<div style="color: red;">✗ Failed</div>
|
|
287
316
|
</template>
|
|
@@ -296,11 +325,31 @@ class StatusDisplay extends HTMLElement {
|
|
|
296
325
|
}
|
|
297
326
|
```
|
|
298
327
|
|
|
299
|
-
**Features:**
|
|
328
|
+
**Expression Features:**
|
|
300
329
|
- `{{ }} braces` for dynamic condition evaluation
|
|
301
330
|
- `$value` contains the value passed to `swc-choose`
|
|
302
331
|
- `skip-if-same` attribute prevents re-render when same template selected
|
|
303
|
-
- Attribute substitution
|
|
332
|
+
- **Attribute substitution with `{{= }}`**: `attribute-name="{{= expression }}"` automatically evaluated
|
|
333
|
+
|
|
334
|
+
**Attribute Substitution Example ({{= }} usage):**
|
|
335
|
+
```html
|
|
336
|
+
<template is="swc-choose">
|
|
337
|
+
<template is="swc-when" value="{{ $value?.type === 'user' }}">
|
|
338
|
+
<user-card
|
|
339
|
+
data-username="{{= $value?.name }}"
|
|
340
|
+
data-email="{{= $value?.email }}"
|
|
341
|
+
data-id="{{= $value?.id }}"
|
|
342
|
+
></user-card>
|
|
343
|
+
</template>
|
|
344
|
+
|
|
345
|
+
<template is="swc-when" value="{{ $value?.type === 'product' }}">
|
|
346
|
+
<product-card
|
|
347
|
+
data-product-name="{{= $value?.title }}"
|
|
348
|
+
data-price="{{= $value?.price }}"
|
|
349
|
+
></product-card>
|
|
350
|
+
</template>
|
|
351
|
+
</template>
|
|
352
|
+
```
|
|
304
353
|
|
|
305
354
|
---
|
|
306
355
|
|
|
@@ -312,6 +361,7 @@ If-then-else rendering with optional `swc-default` fallback.
|
|
|
312
361
|
@elementDefine('user-profile')
|
|
313
362
|
class UserProfile extends HTMLElement {
|
|
314
363
|
private isLoggedIn = false;
|
|
364
|
+
private userData = { userName: 'Alice', userId: 123 };
|
|
315
365
|
|
|
316
366
|
@setProperty('#content', 'value')
|
|
317
367
|
checkAuth() {
|
|
@@ -321,10 +371,15 @@ class UserProfile extends HTMLElement {
|
|
|
321
371
|
@onConnectedInnerHtml
|
|
322
372
|
render() {
|
|
323
373
|
return `
|
|
324
|
-
<!-- True condition: renders main template -->
|
|
374
|
+
<!-- True condition: renders main template using {{ }} -->
|
|
325
375
|
<template id="content" is="swc-if" value="{{ $host.isLoggedIn }}">
|
|
376
|
+
<!-- Attribute substitution with {{= }} expressions -->
|
|
377
|
+
<user-card
|
|
378
|
+
data-username="{{= $host.userData.userName }}"
|
|
379
|
+
data-userid="{{= $host.userData.userId }}"
|
|
380
|
+
></user-card>
|
|
326
381
|
<div>
|
|
327
|
-
<h2>Welcome, {{ $host.userName }}</h2>
|
|
382
|
+
<h2>Welcome, {{ $host.userData.userName }}</h2>
|
|
328
383
|
<button on-click="logout">Logout</button>
|
|
329
384
|
</div>
|
|
330
385
|
|
|
@@ -341,12 +396,13 @@ class UserProfile extends HTMLElement {
|
|
|
341
396
|
}
|
|
342
397
|
```
|
|
343
398
|
|
|
344
|
-
**Features:**
|
|
345
|
-
- Binary true/false logic
|
|
399
|
+
**Expression Features:**
|
|
400
|
+
- Binary true/false logic with `{{ }} condition`
|
|
346
401
|
- Main template renders when `value` is truthy
|
|
347
402
|
- `swc-default` sub-template renders when value is falsy
|
|
348
|
-
- Dynamic attribute substitution supported
|
|
403
|
+
- Dynamic attribute substitution with `{{= }}` supported
|
|
349
404
|
- Access `$host`, `$value`, and other context variables
|
|
405
|
+
- `{{= functionCall() }}` executes functions and returns results for attributes
|
|
350
406
|
|
|
351
407
|
---
|
|
352
408
|
|
|
@@ -362,7 +418,7 @@ class DataLoader extends HTMLElement {
|
|
|
362
418
|
async fetchData() {
|
|
363
419
|
return new Promise((resolve, reject) => {
|
|
364
420
|
setTimeout(() => {
|
|
365
|
-
resolve({ id: 1, name: 'Sample Data' });
|
|
421
|
+
resolve({ id: 1, name: 'Sample Data', description: 'API response' });
|
|
366
422
|
}, 2000);
|
|
367
423
|
});
|
|
368
424
|
}
|
|
@@ -375,17 +431,23 @@ class DataLoader extends HTMLElement {
|
|
|
375
431
|
@onConnectedInnerHtml
|
|
376
432
|
render() {
|
|
377
433
|
return `
|
|
378
|
-
<template id="async-content" is="swc-async" value="{{ $host.fetchData() }}">
|
|
434
|
+
<template id="async-content" is="swc-async" value="{{= $host.fetchData() }}">
|
|
379
435
|
<!-- Loading State: Shows while Promise is pending -->
|
|
380
436
|
<template is="swc-loading">
|
|
381
437
|
<div>🔄 Loading data...</div>
|
|
382
438
|
</template>
|
|
383
439
|
|
|
384
440
|
<!-- Success State: Shows when Promise resolves -->
|
|
441
|
+
<!-- Using {{= }} to extract data from resolved Promise -->
|
|
385
442
|
<template is="swc-success">
|
|
443
|
+
<data-card
|
|
444
|
+
data-id="{{= $value.id }}"
|
|
445
|
+
data-name="{{= $value.name }}"
|
|
446
|
+
></data-card>
|
|
386
447
|
<div>
|
|
387
448
|
<p>Data ID: {{ $value.id }}</p>
|
|
388
449
|
<p>Data Name: {{ $value.name }}</p>
|
|
450
|
+
<p>Description: {{= $value.description }}</p>
|
|
389
451
|
</div>
|
|
390
452
|
</template>
|
|
391
453
|
|
|
@@ -399,12 +461,14 @@ class DataLoader extends HTMLElement {
|
|
|
399
461
|
}
|
|
400
462
|
```
|
|
401
463
|
|
|
402
|
-
**Features:**
|
|
403
|
-
-
|
|
464
|
+
**Expression Features:**
|
|
465
|
+
- Promise lifecycle management with `{{ }} conditions`
|
|
466
|
+
- `{{= $host.fetchData() }}` - function call expression that executes and returns Promise
|
|
467
|
+
- `{{= $value.propertyName }}` - extract properties from resolved value
|
|
404
468
|
- `swc-loading` - displays during pending state
|
|
405
|
-
- `swc-success` - displays on successful resolution, `$value` contains result
|
|
406
|
-
- `swc-error` - displays on Promise rejection
|
|
407
|
-
- Dynamic attribute substitution in all states
|
|
469
|
+
- `swc-success` - displays on successful resolution, `$value` contains resolved result
|
|
470
|
+
- `swc-error` - displays on Promise rejection, `$value` contains error
|
|
471
|
+
- Dynamic attribute substitution with `{{= }}` in all states
|
|
408
472
|
- `on-clone-node` and `on-clone-nodes` callbacks for DOM manipulation
|
|
409
473
|
|
|
410
474
|
---
|
|
@@ -417,9 +481,9 @@ Iterate over arrays and render templates for each item.
|
|
|
417
481
|
@elementDefine('product-list')
|
|
418
482
|
class ProductList extends HTMLElement {
|
|
419
483
|
private products = [
|
|
420
|
-
{ id: 1, name: 'Laptop', price: 1200 },
|
|
421
|
-
{ id: 2, name: 'Phone', price: 800 },
|
|
422
|
-
{ id: 3, name: 'Tablet', price: 500 }
|
|
484
|
+
{ id: 1, name: 'Laptop', price: 1200, category: 'electronics' },
|
|
485
|
+
{ id: 2, name: 'Phone', price: 800, category: 'electronics' },
|
|
486
|
+
{ id: 3, name: 'Tablet', price: 500, category: 'electronics' }
|
|
423
487
|
];
|
|
424
488
|
|
|
425
489
|
@setProperty('#products-list', 'value')
|
|
@@ -430,12 +494,26 @@ class ProductList extends HTMLElement {
|
|
|
430
494
|
@onConnectedInnerHtml
|
|
431
495
|
render() {
|
|
432
496
|
return `
|
|
433
|
-
<template id="products-list" is="swc-loop" value="{{ $host.products }}">
|
|
497
|
+
<template id="products-list" is="swc-loop" value="{{= $host.products }}">
|
|
498
|
+
<!-- Loop iteration with {{ }} for display and {{= }} for attributes -->
|
|
434
499
|
<div style="margin: 10px; padding: 10px; border: 1px solid #ddd;">
|
|
500
|
+
<!-- Using {{ }} for template content rendering -->
|
|
435
501
|
<h3>{{ $item.name }}</h3>
|
|
436
502
|
<p>Price: ${{ $item.price }}</p>
|
|
437
503
|
<p>ID: {{ $item.id }} (Index: {{ $index }})</p>
|
|
438
|
-
|
|
504
|
+
|
|
505
|
+
<!-- Using {{= }} for dynamic attribute substitution -->
|
|
506
|
+
<product-card
|
|
507
|
+
data-product-id="{{= $item.id }}"
|
|
508
|
+
data-product-name="{{= $item.name }}"
|
|
509
|
+
data-product-price="{{= $item.price }}"
|
|
510
|
+
data-product-category="{{= $item.category }}"
|
|
511
|
+
></product-card>
|
|
512
|
+
|
|
513
|
+
<!-- Computed expressions with {{= }} -->
|
|
514
|
+
<button data-id="{{= $item.id }}" data-price="{{= $item.price }}">
|
|
515
|
+
Add to Cart - \${{= $item.price }}
|
|
516
|
+
</button>
|
|
439
517
|
</div>
|
|
440
518
|
|
|
441
519
|
<!-- Empty fallback: renders when array is empty -->
|
|
@@ -448,26 +526,76 @@ class ProductList extends HTMLElement {
|
|
|
448
526
|
}
|
|
449
527
|
```
|
|
450
528
|
|
|
451
|
-
**Context
|
|
529
|
+
**Context Variables (Available in loop):**
|
|
452
530
|
- `$item` - current item in iteration
|
|
453
|
-
- `$index` - zero-based index
|
|
454
|
-
- `$value` - entire array
|
|
531
|
+
- `$index` - zero-based index (0, 1, 2, ...)
|
|
532
|
+
- `$value` - entire array being iterated
|
|
455
533
|
- `$nodes` - cloned DOM nodes
|
|
456
534
|
- `$elements` - cloned HTML elements
|
|
457
535
|
- `$firstElement` - first element in cloned nodes
|
|
458
536
|
|
|
459
|
-
**Features:**
|
|
460
|
-
-
|
|
537
|
+
**Expression Features:**
|
|
538
|
+
- `{{= $host.products }}` - function call expression that returns the array to iterate
|
|
539
|
+
- `{{ $item.propertyName }}` - display item properties using standard `{{ }}`
|
|
540
|
+
- `{{= $item.propertyName }}` - extract item properties for attributes using `{{= }}`
|
|
541
|
+
- `{{= computedValue }}` - compute values in attributes (e.g., calculations, method calls)
|
|
461
542
|
- `swc-default` fallback renders when array is empty
|
|
462
|
-
- All cloned attributes support `{{ }}` substitution
|
|
543
|
+
- All cloned attributes support `{{ }}` and `{{= }}` substitution
|
|
463
544
|
- `on-clone-node` callback fires for each cloned node
|
|
464
545
|
- `on-clone-nodes` callback fires after all nodes cloned
|
|
465
546
|
|
|
547
|
+
**Common Patterns:**
|
|
548
|
+
```html
|
|
549
|
+
<!-- Template functions with {{= }} -->
|
|
550
|
+
<template is="swc-loop" value="{{= filterProducts($host.products) }}">
|
|
551
|
+
<!-- Dynamic calculations -->
|
|
552
|
+
<span>Total: {{= $item.price * $item.quantity }}</span>
|
|
553
|
+
|
|
554
|
+
<!-- Conditional attributes -->
|
|
555
|
+
<div class="{{= $item.inStock ? 'available' : 'unavailable' }}"></div>
|
|
556
|
+
|
|
557
|
+
<!-- Method calls as attributes -->
|
|
558
|
+
<button data-formatted="{{= formatPrice($item.price) }}">
|
|
559
|
+
Buy - {{ formatPrice($item.price) }}
|
|
560
|
+
</button>
|
|
561
|
+
</template>
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
**Advanced: replace-wrap for Custom Expression Syntax**
|
|
565
|
+
|
|
566
|
+
Override default `{{ }}` and `{{= }}` syntax with custom wrappers:
|
|
567
|
+
|
|
568
|
+
```typescript
|
|
569
|
+
@elementDefine('custom-loop')
|
|
570
|
+
class CustomLoop extends HTMLElement {
|
|
571
|
+
private items = [{ id: 1, name: 'Item 1' }];
|
|
572
|
+
|
|
573
|
+
@onConnectedInnerHtml
|
|
574
|
+
render() {
|
|
575
|
+
return `
|
|
576
|
+
<!-- Using custom wrapper syntax [(= ... )] instead of {{= ... }} -->
|
|
577
|
+
<template id="items" is="swc-loop" replace-wrap="[(= )]" value="[(= $host.items )]">
|
|
578
|
+
<div>
|
|
579
|
+
<p>Name: [(= $item.name )]</p>
|
|
580
|
+
<button data-id="[(= $item.id )]">Click me</button>
|
|
581
|
+
</div>
|
|
582
|
+
</template>
|
|
583
|
+
`;
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
```
|
|
587
|
+
|
|
588
|
+
**replace-wrap attribute options:**
|
|
589
|
+
- Change expression wrapper from default `{{= }}` to custom pattern
|
|
590
|
+
- Useful when `{{ }}` conflicts with template engines like EJS or Mustache
|
|
591
|
+
- Supported on: `SwcLoop`, `SwcChoose`, `SwcIf`, `SwcAsync`, `register()` directives
|
|
592
|
+
- Example patterns: `[(= )]`, `{[= ]}`, `<%= %>` (after escaping)
|
|
593
|
+
|
|
466
594
|
---
|
|
467
595
|
|
|
468
596
|
#### 8.5 **Declarative Routing with swc-choose**
|
|
469
597
|
|
|
470
|
-
Combine `@
|
|
598
|
+
Combine `@subscribeSwcAppRouteChangeWhileConnected` with `swc-choose` for clean, declarative routing without boilerplate.
|
|
471
599
|
|
|
472
600
|
```typescript
|
|
473
601
|
@elementDefine('root-router')
|
|
@@ -480,23 +608,37 @@ class RootRouter extends HTMLElement {
|
|
|
480
608
|
}
|
|
481
609
|
|
|
482
610
|
@setProperty('#router', 'value')
|
|
483
|
-
@
|
|
484
|
-
routeChanged(
|
|
485
|
-
return
|
|
611
|
+
@subscribeSwcAppRouteChangeWhileConnected(['/', '/users', '/users/{id}', '/products/{id}'])
|
|
612
|
+
routeChanged(route: RouterEventType) {
|
|
613
|
+
return route;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
navigate(path: string): void {
|
|
617
|
+
this.router?.go(path);
|
|
486
618
|
}
|
|
487
619
|
|
|
488
620
|
@onConnectedInnerHtml
|
|
489
621
|
render() {
|
|
490
622
|
return `
|
|
491
623
|
<template id="router" is="swc-choose">
|
|
492
|
-
<!-- Home -->
|
|
624
|
+
<!-- Home Page: Using {{ }} for condition -->
|
|
493
625
|
<template is="swc-when" value="{{ ['', '/'].includes($value?.path) }}">
|
|
494
626
|
<home-page/>
|
|
495
627
|
</template>
|
|
496
628
|
|
|
497
|
-
<!--
|
|
498
|
-
<template is="swc-when" value="{{ $value?.path
|
|
499
|
-
<
|
|
629
|
+
<!-- Users List Page -->
|
|
630
|
+
<template is="swc-when" value="{{ $value?.path === '/users' }}">
|
|
631
|
+
<users-list-page/>
|
|
632
|
+
</template>
|
|
633
|
+
|
|
634
|
+
<!-- User Detail Page: Using {{= }} for dynamic attributes from route params -->
|
|
635
|
+
<template is="swc-when" value="{{ $value?.path.startsWith('/users/') }}">
|
|
636
|
+
<user-detail-page user-id="{{= $value?.pathData?.id }}"></user-detail-page>
|
|
637
|
+
</template>
|
|
638
|
+
|
|
639
|
+
<!-- Product Detail Page: Using {{= }} for dynamic route parameters -->
|
|
640
|
+
<template is="swc-when" value="{{ $value?.path.startsWith('/products/') }}">
|
|
641
|
+
<product-detail-page product-id="{{= $value?.pathData?.id }}"></product-detail-page>
|
|
500
642
|
</template>
|
|
501
643
|
|
|
502
644
|
<!-- Not Found -->
|
|
@@ -511,12 +653,107 @@ class RootRouter extends HTMLElement {
|
|
|
511
653
|
|
|
512
654
|
**Benefits:**
|
|
513
655
|
- ✅ **No imperative route methods** - routes are declarative templates
|
|
514
|
-
- ✅ **Automatic re-rendering** - attributes like `
|
|
656
|
+
- ✅ **Automatic re-rendering** - attributes like `user-id` update automatically
|
|
515
657
|
- ✅ **Performance optimization** - use `skip-if-same` on templates to prevent re-renders
|
|
516
|
-
- ✅ **Dynamic path parameters** - use `{{$value?.pathData?.id}}` syntax to inject route params
|
|
658
|
+
- ✅ **Dynamic path parameters** - use `{{= $value?.pathData?.id }}` syntax to inject route params with `{{= }}`
|
|
517
659
|
- ✅ **Cleaner DX** - single unified routing pattern
|
|
660
|
+
- ✅ **Full expression support** - use `{{= }}` for complex parameter transformations
|
|
661
|
+
|
|
662
|
+
### 9. **Component Communication - Message Bus**
|
|
663
|
+
|
|
664
|
+
SWC provides a powerful message bus system for inter-component communication through SwcApp. Components can publish and subscribe to typed messages while connected.
|
|
665
|
+
|
|
666
|
+
#### @subscribeSwcAppMessageWhileConnected
|
|
667
|
+
Subscribe to messages while component is connected to DOM.
|
|
668
|
+
|
|
669
|
+
```typescript
|
|
670
|
+
@elementDefine('notification-panel')
|
|
671
|
+
class NotificationPanel extends HTMLElement {
|
|
672
|
+
@subscribeSwcAppMessageWhileConnected()
|
|
673
|
+
onAnyMessage(message: SwcAppMessage) {
|
|
674
|
+
console.log('Received message:', message);
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
@subscribeSwcAppMessageWhileConnected('user-login')
|
|
678
|
+
onUserLogin(message: SwcAppMessage<{ username: string }>) {
|
|
679
|
+
console.log(`Welcome ${message.data?.username}`);
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
@subscribeSwcAppMessageWhileConnected('user-login', {
|
|
683
|
+
filter: (msg) => msg.data?.username === 'admin'
|
|
684
|
+
})
|
|
685
|
+
onAdminLogin(message: SwcAppMessage) {
|
|
686
|
+
console.log('Admin logged in!');
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
```
|
|
690
|
+
|
|
691
|
+
#### @publishSwcAppMessage
|
|
692
|
+
Publish a message from a method's return value.
|
|
693
|
+
|
|
694
|
+
```typescript
|
|
695
|
+
@elementDefine('login-form')
|
|
696
|
+
class LoginForm extends HTMLElement {
|
|
697
|
+
@publishSwcAppMessage()
|
|
698
|
+
async onSubmit() {
|
|
699
|
+
const user = await this.authService.login(
|
|
700
|
+
this.username,
|
|
701
|
+
this.password
|
|
702
|
+
);
|
|
703
|
+
return user; // Published as message with data: user
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
@publishSwcAppMessage('user-profile-updated')
|
|
707
|
+
updateProfile() {
|
|
708
|
+
const profile = { name: this.name, email: this.email };
|
|
709
|
+
return profile; // Published as message with type: 'user-profile-updated'
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
```
|
|
713
|
+
|
|
714
|
+
**SwcAppMessage Structure:**
|
|
715
|
+
```typescript
|
|
716
|
+
type SwcAppMessage<T = any> = {
|
|
717
|
+
publisher?: any; // Component that published the message
|
|
718
|
+
data?: T; // Payload data
|
|
719
|
+
type?: string; // Optional message type/category
|
|
720
|
+
};
|
|
721
|
+
```
|
|
722
|
+
|
|
723
|
+
**Key Features:**
|
|
724
|
+
- ✅ **Lifecycle-aware** - subscriptions only active while component is connected
|
|
725
|
+
- ✅ **Type-safe** - specify message types for filtering
|
|
726
|
+
- ✅ **Filter support** - use custom filter functions to handle specific conditions
|
|
727
|
+
- ✅ **Auto-publishing** - return value automatically becomes message payload
|
|
728
|
+
- ✅ **Async support** - works with async methods (promises)
|
|
729
|
+
- ✅ **Centralized** - all messages routed through SwcApp host
|
|
730
|
+
- ✅ **Decoupled** - components don't need to know about each other
|
|
731
|
+
|
|
732
|
+
**Usage Example:**
|
|
733
|
+
```typescript
|
|
734
|
+
// Publisher component
|
|
735
|
+
@elementDefine('product-list')
|
|
736
|
+
class ProductList extends HTMLElement {
|
|
737
|
+
@publishSwcAppMessage('product-selected')
|
|
738
|
+
selectProduct(productId: string) {
|
|
739
|
+
const product = this.findProduct(productId);
|
|
740
|
+
return product;
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
// Subscriber component
|
|
745
|
+
@elementDefine('product-detail')
|
|
746
|
+
class ProductDetail extends HTMLElement {
|
|
747
|
+
@subscribeSwcAppMessageWhileConnected('product-selected')
|
|
748
|
+
onProductSelected(message: SwcAppMessage<Product>) {
|
|
749
|
+
this.displayProduct(message.data);
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
```
|
|
753
|
+
|
|
754
|
+
---
|
|
518
755
|
|
|
519
|
-
###
|
|
756
|
+
### 10. **Accommodation Pattern**
|
|
520
757
|
Factory-based component registration with explicit DI.
|
|
521
758
|
|
|
522
759
|
```typescript
|
|
@@ -555,7 +792,7 @@ appElement.connect({
|
|
|
555
792
|
});
|
|
556
793
|
```
|
|
557
794
|
|
|
558
|
-
###
|
|
795
|
+
### 11. **Quickstart - Basic SPA Setup**
|
|
559
796
|
|
|
560
797
|
Complete example of setting up a production SPA with routing, services, and components.
|
|
561
798
|
|
|
@@ -631,7 +868,7 @@ export const serviceFactories = [userServiceFactory];
|
|
|
631
868
|
#### Step 3. Router Configuration (Declarative with swc-choose)
|
|
632
869
|
```typescript
|
|
633
870
|
// pages/RootRouter.ts
|
|
634
|
-
import { elementDefine,
|
|
871
|
+
import { elementDefine, subscribeSwcAppRouteChangeWhileConnected, setProperty, onConnectedInnerHtml, onInitialize } from '@dooboostore/simple-web-component';
|
|
635
872
|
import { Router, RouterEventType } from '@dooboostore/core-web';
|
|
636
873
|
|
|
637
874
|
const rootRouterFactory = (w: Window) => {
|
|
@@ -649,7 +886,7 @@ const rootRouterFactory = (w: Window) => {
|
|
|
649
886
|
}
|
|
650
887
|
|
|
651
888
|
@setProperty('#router', 'value')
|
|
652
|
-
@
|
|
889
|
+
@subscribeSwcAppRouteChangeWhileConnected(['/', '/users', '/users/{id}'])
|
|
653
890
|
routeChanged(route: RouterEventType) {
|
|
654
891
|
return route;
|
|
655
892
|
}
|
|
@@ -933,7 +1170,7 @@ src/
|
|
|
933
1170
|
3. `bootFactory` registers services and components (including `rootRouterFactory`)
|
|
934
1171
|
4. App waits for `swc-app-body` to be defined
|
|
935
1172
|
5. `appElement.connect()` initializes routing & DI
|
|
936
|
-
6. `<root-router>` becomes active with `@
|
|
1173
|
+
6. `<root-router>` becomes active with `@subscribeSwcAppRouteChangeWhileConnected` + `swc-choose` template
|
|
937
1174
|
7. Route changes automatically trigger `swc-choose` re-evaluation and template re-rendering
|
|
938
1175
|
8. Template attributes like `user-id="{{$value?.pathData?.id}}"` are dynamically populated with route params
|
|
939
1176
|
9. All components have access to injected services via `@onInitialize` or `@onConnectedSwcApp`
|
|
@@ -50,7 +50,7 @@ function createStyleDecorator(action) {
|
|
|
50
50
|
if (res !== void 0) {
|
|
51
51
|
const win = this.ownerDocument?.defaultView || window;
|
|
52
52
|
const host = this;
|
|
53
|
-
const hostSet =
|
|
53
|
+
const hostSet = import_Utils.SwcUtils.getHelperAndHostSet(win, host);
|
|
54
54
|
const r = options.root || "auto";
|
|
55
55
|
const targetEls = [];
|
|
56
56
|
const applyRoot = (t) => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/decorators/applyStyle.ts"],
|
|
4
|
-
"sourcesContent": ["import { ReflectUtils } from '@dooboostore/core';\nimport { ensureInit } from './elementDefine';\nimport { SwcUtils } from '../utils/Utils';\nimport { SwcQueryOptions, HelperHostSet } from '../types';\n\nexport type StyleAction = 'set' | 'update' | 'remove';\n\nexport interface StyleApplyOptions extends SwcQueryOptions {}\n\nexport interface StyleApplyMetadata {\n propertyKey: string | symbol;\n selector: string;\n action: StyleAction;\n options: StyleApplyOptions;\n}\n\nexport const STYLE_METADATA_KEY = Symbol.for('simple-web-component:style');\n\nfunction createStyleDecorator(action: StyleAction) {\n return (selector: string, options: StyleApplyOptions = {}): MethodDecorator => {\n return (target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor) => {\n const constructor = target.constructor;\n let metaList = ReflectUtils.getOwnMetadata(STYLE_METADATA_KEY, constructor) as Map<string | symbol, StyleApplyMetadata>;\n if (!metaList) {\n metaList = new Map<string | symbol, StyleApplyMetadata>();\n ReflectUtils.defineMetadata(STYLE_METADATA_KEY, metaList, constructor);\n }\n metaList.set(propertyKey, { propertyKey, selector, action, options });\n\n const original = descriptor.value;\n descriptor.value = async function (...args: any[]) {\n ensureInit(this);\n const res = await (original as any).apply(this, args);\n if (res !== undefined) {\n const win = (this as any).ownerDocument?.defaultView || window;\n const host = this as unknown as HTMLElement;\n const hostSet =
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kBAA6B;AAC7B,2BAA2B;AAC3B,mBAAyB;AAclB,MAAM,qBAAqB,OAAO,IAAI,4BAA4B;AAEzE,SAAS,qBAAqB,QAAqB;AACjD,SAAO,CAAC,UAAkB,UAA6B,CAAC,MAAuB;AAC7E,WAAO,CAAC,QAAgB,aAA8B,eAAmC;AACvF,YAAM,cAAc,OAAO;AAC3B,UAAI,WAAW,yBAAa,eAAe,oBAAoB,WAAW;AAC1E,UAAI,CAAC,UAAU;AACb,mBAAW,oBAAI,IAAyC;AACxD,iCAAa,eAAe,oBAAoB,UAAU,WAAW;AAAA,MACvE;AACA,eAAS,IAAI,aAAa,EAAE,aAAa,UAAU,QAAQ,QAAQ,CAAC;AAEpE,YAAM,WAAW,WAAW;AAC5B,iBAAW,QAAQ,kBAAmB,MAAa;AACjD,6CAAW,IAAI;AACf,cAAM,MAAM,MAAO,SAAiB,MAAM,MAAM,IAAI;AACpD,YAAI,QAAQ,QAAW;AACrB,gBAAM,MAAO,KAAa,eAAe,eAAe;AACxD,gBAAM,OAAO;AACb,gBAAM,UAAU,
|
|
4
|
+
"sourcesContent": ["import { ReflectUtils } from '@dooboostore/core';\nimport { ensureInit } from './elementDefine';\nimport { SwcUtils } from '../utils/Utils';\nimport { SwcQueryOptions, HelperHostSet } from '../types';\n\nexport type StyleAction = 'set' | 'update' | 'remove';\n\nexport interface StyleApplyOptions extends SwcQueryOptions {}\n\nexport interface StyleApplyMetadata {\n propertyKey: string | symbol;\n selector: string;\n action: StyleAction;\n options: StyleApplyOptions;\n}\n\nexport const STYLE_METADATA_KEY = Symbol.for('simple-web-component:style');\n\nfunction createStyleDecorator(action: StyleAction) {\n return (selector: string, options: StyleApplyOptions = {}): MethodDecorator => {\n return (target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor) => {\n const constructor = target.constructor;\n let metaList = ReflectUtils.getOwnMetadata(STYLE_METADATA_KEY, constructor) as Map<string | symbol, StyleApplyMetadata>;\n if (!metaList) {\n metaList = new Map<string | symbol, StyleApplyMetadata>();\n ReflectUtils.defineMetadata(STYLE_METADATA_KEY, metaList, constructor);\n }\n metaList.set(propertyKey, { propertyKey, selector, action, options });\n\n const original = descriptor.value;\n descriptor.value = async function (...args: any[]) {\n ensureInit(this);\n const res = await (original as any).apply(this, args);\n if (res !== undefined) {\n const win = (this as any).ownerDocument?.defaultView || window;\n const host = this as unknown as HTMLElement;\n const hostSet = SwcUtils.getHelperAndHostSet(win, host);\n const r = options.root || 'auto';\n const targetEls: HTMLElement[] = [];\n\n const applyRoot = (t: any) => {\n if (!t || !(t instanceof win.HTMLElement)) return;\n targetEls.push(t);\n };\n\n if (selector === ':parentHost') applyRoot(hostSet.$parentHost);\n else if (selector === ':appHost') applyRoot(hostSet.$appHost);\n else if (selector === ':firstHost') applyRoot(hostSet.$firstHost);\n else if (selector === ':lastHost') applyRoot(hostSet.$lastHost);\n else if (selector === ':firstAppHost') applyRoot(hostSet.$firstAppHost);\n else if (selector === ':lastAppHost') applyRoot(hostSet.$lastAppHost);\n else if (selector === ':hosts') hostSet.$hosts.forEach(applyRoot);\n else if (selector === ':appHosts') hostSet.$appHosts.forEach(applyRoot);\n else if (selector === ':host' || !selector) applyRoot(host);\n else {\n const targetRoot = r === 'auto' ? (host.shadowRoot || host) : (r === 'shadow' ? host.shadowRoot : host);\n if (targetRoot) {\n const found = targetRoot.querySelectorAll(selector);\n if (found) targetEls.push(...(Array.from(found) as HTMLElement[]));\n }\n }\n\n targetEls.forEach(targetEl => {\n const resolvedRes = typeof res === 'function' ? (res as any)(targetEl, hostSet) : res;\n\n if (action === 'set' || action === 'update') {\n if (action === 'set') targetEl.style.cssText = '';\n \n if (typeof resolvedRes === 'object' && resolvedRes !== null && !Array.isArray(resolvedRes)) {\n Object.entries(resolvedRes as any).forEach(([prop, val]) => {\n const finalVal = typeof val === 'function' ? val(targetEl, hostSet) : val;\n if (finalVal === null || finalVal === undefined) {\n targetEl.style.removeProperty(prop);\n } else {\n targetEl.style[prop as any] = String(finalVal);\n }\n });\n } else if (typeof resolvedRes === 'string') {\n targetEl.style.cssText = resolvedRes;\n }\n } else if (action === 'remove') {\n if (typeof resolvedRes === 'object' && resolvedRes !== null && !Array.isArray(resolvedRes)) {\n Object.entries(resolvedRes as any).forEach(([prop, val]) => {\n const shouldRemove = typeof val === 'function' ? val(targetEl, hostSet) : !!val;\n if (shouldRemove) {\n targetEl.style.removeProperty(prop);\n }\n });\n } else {\n const props = Array.isArray(resolvedRes) ? resolvedRes : [resolvedRes];\n props.forEach(p => targetEl.style.removeProperty(String(p)));\n }\n }\n });\n }\n return res;\n };\n return descriptor;\n };\n };\n}\n\nexport function applyStyle(selector: string, action: StyleAction = 'update', options: StyleApplyOptions = {}): MethodDecorator {\n return createStyleDecorator(action)(selector, options);\n}\n\nexport const setStyle = (selector: string, options: StyleApplyOptions = {}) => createStyleDecorator('set')(selector, options);\nexport const updateStyle = (selector: string, options: StyleApplyOptions = {}) => createStyleDecorator('update')(selector, options);\nexport const removeStyle = (selector: string, options: StyleApplyOptions = {}) => createStyleDecorator('remove')(selector, options);\n\n// Host versions\nexport function applyStyleHost(action: StyleAction = 'update', options: StyleApplyOptions = {}): MethodDecorator {\n return createStyleDecorator(action)(':host', options);\n}\nexport const setStyleHost = (options: StyleApplyOptions = {}) => createStyleDecorator('set')(':host', options);\nexport const updateStyleHost = (options: StyleApplyOptions = {}) => createStyleDecorator('update')(':host', options);\nexport const removeStyleHost = (options: StyleApplyOptions = {}) => createStyleDecorator('remove')(':host', options);\n\nexport const findAllStyleMetadata = (target: any): Map<string | symbol, StyleApplyMetadata> => {\n const result = new Map<string | symbol, StyleApplyMetadata>();\n const maps = ReflectUtils.findAllMetadata<Map<string | symbol, StyleApplyMetadata>>(STYLE_METADATA_KEY, target);\n maps.forEach(map => {\n if (map instanceof Map) {\n map.forEach((v, k) => result.set(k, v));\n }\n });\n return result;\n};\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -27,6 +27,7 @@ module.exports = __toCommonJS(attribute_exports);
|
|
|
27
27
|
var import_core = require("@dooboostore/core");
|
|
28
28
|
var import_elementDefine = require("./elementDefine");
|
|
29
29
|
var import_Utils = require("../utils/Utils");
|
|
30
|
+
var import_core_web = require("@dooboostore/core-web");
|
|
30
31
|
const ATTRIBUTE_FIELD_METADATA_KEY = Symbol.for("simple-web-component:attribute-field");
|
|
31
32
|
const convertValue = (val, type) => {
|
|
32
33
|
if (val === null || val === void 0) return val;
|
|
@@ -115,7 +116,22 @@ function attribute(selector, options = {}) {
|
|
|
115
116
|
const primaryTarget = targets[0];
|
|
116
117
|
if (primaryTarget && typeof primaryTarget.hasAttribute === "function") {
|
|
117
118
|
if (primaryTarget.hasAttribute(attrName)) {
|
|
118
|
-
|
|
119
|
+
let domVal = primaryTarget.getAttribute(attrName);
|
|
120
|
+
const ae = new import_core.ActionExpression(domVal);
|
|
121
|
+
const expr = ae.getFirstExpression("call-return");
|
|
122
|
+
if (expr) {
|
|
123
|
+
try {
|
|
124
|
+
const result = import_core.FunctionUtils.executeReturn({
|
|
125
|
+
script: import_core_web.ConvertUtils.decodeHtmlEntity(expr.script, currentWin.document),
|
|
126
|
+
context: this,
|
|
127
|
+
args: import_Utils.SwcUtils.getHelperAndHostSet(currentWin, this)
|
|
128
|
+
});
|
|
129
|
+
return convertValue(result, targetType);
|
|
130
|
+
} catch (e) {
|
|
131
|
+
console.error(`[SWC] Failed to execute directive {{= ${expr.script} }}:`, e);
|
|
132
|
+
return convertValue(domVal, targetType);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
119
135
|
return convertValue(domVal, targetType);
|
|
120
136
|
}
|
|
121
137
|
return null;
|