@dooboostore/simple-web-component 1.0.22 → 1.0.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/README.md +568 -32
  2. package/dist/cjs/decorators/applyNode.js +195 -26
  3. package/dist/cjs/decorators/applyNode.js.map +2 -2
  4. package/dist/cjs/decorators/attribute.js +21 -3
  5. package/dist/cjs/decorators/attribute.js.map +2 -2
  6. package/dist/cjs/decorators/changedAttributeHost.js.map +2 -2
  7. package/dist/cjs/decorators/classList.js +82 -19
  8. package/dist/cjs/decorators/classList.js.map +2 -2
  9. package/dist/cjs/decorators/elementDefine.js +1 -2
  10. package/dist/cjs/decorators/elementDefine.js.map +2 -2
  11. package/dist/cjs/decorators/index.js +0 -1
  12. package/dist/cjs/decorators/index.js.map +2 -2
  13. package/dist/cjs/decorators/lifecycles.js +25 -0
  14. package/dist/cjs/decorators/lifecycles.js.map +2 -2
  15. package/dist/cjs/decorators/query.js +77 -2
  16. package/dist/cjs/decorators/query.js.map +2 -2
  17. package/dist/cjs/decorators/queryAll.js +77 -2
  18. package/dist/cjs/decorators/queryAll.js.map +2 -2
  19. package/dist/cjs/decorators/setAttribute.js +4 -4
  20. package/dist/cjs/decorators/setAttribute.js.map +2 -2
  21. package/dist/cjs/decorators/style.js +70 -13
  22. package/dist/cjs/decorators/style.js.map +2 -2
  23. package/dist/cjs/index.js +4 -4
  24. package/dist/cjs/index.js.map +2 -2
  25. package/dist/cjs/utils/Utils.js +140 -1
  26. package/dist/cjs/utils/Utils.js.map +2 -2
  27. package/dist/esm/decorators/applyNode.js +195 -26
  28. package/dist/esm/decorators/applyNode.js.map +2 -2
  29. package/dist/esm/decorators/attribute.js +21 -3
  30. package/dist/esm/decorators/attribute.js.map +2 -2
  31. package/dist/esm/decorators/changedAttributeHost.js.map +2 -2
  32. package/dist/esm/decorators/classList.js +82 -19
  33. package/dist/esm/decorators/classList.js.map +2 -2
  34. package/dist/esm/decorators/elementDefine.js +1 -2
  35. package/dist/esm/decorators/elementDefine.js.map +2 -2
  36. package/dist/esm/decorators/index.js +0 -1
  37. package/dist/esm/decorators/index.js.map +2 -2
  38. package/dist/esm/decorators/lifecycles.js +25 -0
  39. package/dist/esm/decorators/lifecycles.js.map +2 -2
  40. package/dist/esm/decorators/query.js +77 -2
  41. package/dist/esm/decorators/query.js.map +2 -2
  42. package/dist/esm/decorators/queryAll.js +77 -2
  43. package/dist/esm/decorators/queryAll.js.map +2 -2
  44. package/dist/esm/decorators/setAttribute.js +4 -4
  45. package/dist/esm/decorators/setAttribute.js.map +2 -2
  46. package/dist/esm/decorators/style.js +70 -13
  47. package/dist/esm/decorators/style.js.map +2 -2
  48. package/dist/esm/index.js +4 -4
  49. package/dist/esm/index.js.map +2 -2
  50. package/dist/esm/utils/Utils.js +140 -1
  51. package/dist/esm/utils/Utils.js.map +2 -2
  52. package/dist/esm-bundle/dooboostore-simple-web-component.esm.js +678 -168
  53. package/dist/esm-bundle/dooboostore-simple-web-component.esm.js.map +4 -4
  54. package/dist/types/decorators/applyNode.d.ts +107 -2
  55. package/dist/types/decorators/applyNode.d.ts.map +1 -1
  56. package/dist/types/decorators/attribute.d.ts +11 -4
  57. package/dist/types/decorators/attribute.d.ts.map +1 -1
  58. package/dist/types/decorators/classList.d.ts +22 -10
  59. package/dist/types/decorators/classList.d.ts.map +1 -1
  60. package/dist/types/decorators/elementDefine.d.ts.map +1 -1
  61. package/dist/types/decorators/index.d.ts +0 -1
  62. package/dist/types/decorators/index.d.ts.map +1 -1
  63. package/dist/types/decorators/lifecycles.d.ts +14 -0
  64. package/dist/types/decorators/lifecycles.d.ts.map +1 -1
  65. package/dist/types/decorators/query.d.ts +68 -7
  66. package/dist/types/decorators/query.d.ts.map +1 -1
  67. package/dist/types/decorators/queryAll.d.ts +70 -1
  68. package/dist/types/decorators/queryAll.d.ts.map +1 -1
  69. package/dist/types/decorators/setAttribute.d.ts.map +1 -1
  70. package/dist/types/decorators/style.d.ts +37 -3
  71. package/dist/types/decorators/style.d.ts.map +1 -1
  72. package/dist/types/index.d.ts +1 -2
  73. package/dist/types/index.d.ts.map +1 -1
  74. package/dist/types/utils/Utils.d.ts +49 -0
  75. package/dist/types/utils/Utils.d.ts.map +1 -1
  76. package/dist/umd-bundle/dooboostore-simple-web-component.umd.js +678 -168
  77. package/dist/umd-bundle/dooboostore-simple-web-component.umd.js.map +4 -4
  78. package/package.json +2 -2
  79. package/src/decorators/applyNode.ts +319 -37
  80. package/src/decorators/attribute.ts +44 -11
  81. package/src/decorators/changedAttributeHost.ts +1 -1
  82. package/src/decorators/classList.ts +107 -22
  83. package/src/decorators/elementDefine.ts +1 -2
  84. package/src/decorators/index.ts +0 -1
  85. package/src/decorators/lifecycles.ts +40 -0
  86. package/src/decorators/query.ts +147 -7
  87. package/src/decorators/queryAll.ts +148 -2
  88. package/src/decorators/setAttribute.ts +6 -5
  89. package/src/decorators/style.ts +109 -13
  90. package/src/index.ts +6 -6
  91. package/src/utils/Utils.ts +144 -0
  92. package/dist/cjs/decorators/onConnectedInnerHtml.js +0 -48
  93. package/dist/cjs/decorators/onConnectedInnerHtml.js.map +0 -7
  94. package/dist/cjs/utils/TemplateUtils.js +0 -103
  95. package/dist/cjs/utils/TemplateUtils.js.map +0 -7
  96. package/dist/esm/decorators/onConnectedInnerHtml.js +0 -29
  97. package/dist/esm/decorators/onConnectedInnerHtml.js.map +0 -7
  98. package/dist/esm/utils/TemplateUtils.js +0 -84
  99. package/dist/esm/utils/TemplateUtils.js.map +0 -7
  100. package/dist/types/decorators/onConnectedInnerHtml.d.ts +0 -15
  101. package/dist/types/decorators/onConnectedInnerHtml.d.ts.map +0 -1
  102. package/dist/types/utils/TemplateUtils.d.ts +0 -54
  103. package/dist/types/utils/TemplateUtils.d.ts.map +0 -1
  104. package/src/decorators/onConnectedInnerHtml.ts +0 -42
  105. package/src/utils/TemplateUtils.ts +0 -83
package/README.md CHANGED
@@ -260,19 +260,26 @@ class StyledCard extends HTMLElement {
260
260
 
261
261
  ### Attribute & Property Decorators
262
262
 
263
- #### 10. `@attributeHost(name, options?)`
264
- Bidirectional attribute binding with automatic type conversion.
263
+ #### 10. `@attributeHost`
264
+ Bidirectional attribute binding with automatic type conversion on `:host` element.
265
+
266
+ All usage patterns support automatic field-name-to-attribute-name conversion:
267
+ - Without parentheses: `@attributeHost` - uses field name as attribute name
268
+ - Empty parentheses: `@attributeHost()` - uses field name as attribute name
269
+ - Options only: `@attributeHost({ connectedInitialize: true })` - uses field name as attribute name
270
+ - Custom name: `@attributeHost('custom-name')` - explicit attribute name
271
+ - Custom name + options: `@attributeHost('custom-name', { type: Boolean })` - both specified
265
272
 
266
273
  ```typescript
267
274
  @elementDefine('input-field')
268
275
  class InputField extends HTMLElement {
269
- @attributeHost('value', { connectedInitialize: true })
276
+ @attributeHost
270
277
  value: string = '';
271
278
 
272
279
  @attributeHost('disabled')
273
280
  disabled: boolean = false;
274
281
 
275
- @attributeHost('count')
282
+ @attributeHost({ type: Number })
276
283
  count: number = 0;
277
284
 
278
285
  @onConnectedInnerHtml
@@ -312,56 +319,119 @@ class ReactiveInput extends HTMLElement {
312
319
  }
313
320
  ```
314
321
 
315
- #### 12. `@setAttribute(name, options?)`
316
- Reflect method's return value to a DOM attribute. One-way binding (class → DOM).
322
+ #### 12. `@setAttribute(selector, name, options?)`
323
+ Reflect method's return value to a target element's DOM attribute. One-way binding (class → DOM).
317
324
 
318
325
  ```typescript
319
326
  @elementDefine('status-badge')
320
327
  class StatusBadge extends HTMLElement {
321
328
  private status: 'active' | 'inactive' | 'pending' = 'pending';
329
+ private items: any[] = [];
322
330
 
323
- // Reflect status to DOM attribute
324
- @setAttribute('data-status')
331
+ // Reflect status to host element's data-status attribute
332
+ @setAttribute(':host', 'data-status')
325
333
  getStatus() {
326
334
  return this.status;
327
335
  }
328
336
 
329
- // Reflect computed data to attribute
330
- @setAttribute('count')
337
+ // Reflect computed data to specific element
338
+ @setAttribute('#count-display', 'data-count')
331
339
  getItemCount() {
332
- return this.getItems().length.toString();
340
+ return this.items.length.toString();
333
341
  }
334
342
 
335
- // Reflect to Shadow DOM element
336
- @setAttribute('aria-label', { root: 'shadow' })
343
+ // Reflect to Shadow DOM element's aria-label
344
+ @setAttribute('.badge-label', 'aria-label', { root: 'shadow' })
337
345
  getAriaLabel() {
338
346
  return `Status is ${this.status}`;
339
347
  }
340
348
 
341
- private getItems() {
342
- return [];
343
- }
344
-
345
349
  @onConnectedInnerHtml
346
350
  render() {
347
351
  return `
348
- <div class="badge" data-status="${this.status}">
349
- Status: ${this.status}
352
+ <div data-status="${this.status}">
353
+ <span class="badge-label">Status: ${this.status}</span>
354
+ <span id="count-display" data-count="${this.items.length}">Items: ${this.items.length}</span>
350
355
  </div>
351
356
  `;
352
357
  }
353
358
 
354
- // Change status and update DOM
359
+ // Change status and update DOM attributes
355
360
  setStatus(newStatus: typeof this.status) {
356
361
  this.status = newStatus;
357
- this.getStatus(); // Triggers @setAttribute update
362
+ this.getStatus(); // Updates :host[data-status]
363
+ this.getItemCount(); // Updates #count-display[data-count]
364
+ this.getAriaLabel(); // Updates .badge-label[aria-label]
365
+ }
366
+ }
367
+ ```
368
+
369
+ **Signature:**
370
+ - `@setAttribute(selector, attributeName, options?)`
371
+ - Returns method's value → reflects to target's attribute
372
+
373
+ **Selectors:**
374
+ - `':host'` - The component itself
375
+ - `'#element-id'` - Element by ID
376
+ - `'.class-name'` - Element by class
377
+ - `selector` - Any CSS selector
378
+
379
+ #### 12b. `@setAttributeHost(name, options?)` - Shorthand for `:host`
380
+ Simplified version of `@setAttribute` that always targets `:host`.
381
+
382
+ ```typescript
383
+ @elementDefine('counter')
384
+ class Counter extends HTMLElement {
385
+ private count = 0;
386
+
387
+ // Equivalent to @setAttribute(':host', 'aria-label')
388
+ @setAttributeHost('aria-label')
389
+ getAriaLabel() {
390
+ return `Count is ${this.count}`;
391
+ }
392
+
393
+ @setAttributeHost('data-count')
394
+ getCountValue() {
395
+ return this.count.toString();
396
+ }
397
+
398
+ @setAttributeHost('aria-disabled')
399
+ getDisabledState() {
400
+ return (this.count >= 10).toString();
401
+ }
402
+
403
+ @onConnectedInnerHtml
404
+ render() {
405
+ return `
406
+ <div>
407
+ <p>Count: ${this.count}</p>
408
+ <button swc-on-click="$host.increment()">+</button>
409
+ </div>
410
+ `;
411
+ }
412
+
413
+ increment() {
414
+ this.count++;
415
+ this.getAriaLabel(); // Updates :host[aria-label]
416
+ this.getCountValue(); // Updates :host[data-count]
417
+ this.getDisabledState(); // Updates :host[aria-disabled]
358
418
  }
359
419
  }
360
420
  ```
361
421
 
422
+ **Usage:**
423
+ ```typescript
424
+ // Full form
425
+ @setAttribute(':host', 'data-status')
426
+ // vs
427
+ // Shorthand for :host
428
+ @setAttributeHost('data-status')
429
+ ```
430
+
362
431
  **Key Differences:**
363
432
  - **`@attributeHost`** - Two-way binding: DOM ↔ Class (Bidirectional)
364
- - **`@setAttribute`** - One-way binding: Class → DOM (Method return reflects to attribute)
433
+ - **`@setAttribute`** - One-way binding: Class → DOM (Any selector)
434
+ - **`@setAttributeHost`** - One-way binding: Class → DOM (`:host` only)
365
435
  - **`@changedAttributeHost`** - Watch DOM attribute changes (Reactive handler)
366
436
 
367
437
  **Options:**
@@ -425,11 +495,110 @@ class ListRenderer extends HTMLElement {
425
495
  }
426
496
  ```
427
497
 
498
+ #### 14b. Removing Elements via `null` or `undefined`
499
+ Both `@query` and `@queryAll` support element removal by setting the property to `null` or `undefined`. This triggers automatic removal of the matching element(s).
500
+
501
+ ```typescript
502
+ @elementDefine('todo-list')
503
+ class TodoList extends HTMLElement {
504
+ @query('.notification') notification?: HTMLElement;
505
+ @queryAll('.todo-item') todoItems?: HTMLElement[];
506
+
507
+ removeNotification() {
508
+ // Setting to null triggers automatic removal
509
+ this.notification = null;
510
+ }
511
+
512
+ removeTodoItem(id: string) {
513
+ // Find and remove a specific todo item
514
+ const item = this.querySelector(`[data-id="${id}"]`);
515
+ if (item) {
516
+ // Setting @query to null removes the element from DOM
517
+ this.notification = null;
518
+ }
519
+ }
520
+
521
+ clearAllTodos() {
522
+ // Setting @queryAll to null/undefined removes all matching elements
523
+ this.todoItems = null; // Removes all .todo-item elements
524
+ }
525
+
526
+ @onConnectedInnerHtml
527
+ render() {
528
+ return `
529
+ <div class="notification">New todos available!</div>
530
+ <ul>
531
+ <li class="todo-item" data-id="1">Task 1</li>
532
+ <li class="todo-item" data-id="2">Task 2</li>
533
+ <li class="todo-item" data-id="3">Task 3</li>
534
+ </ul>
535
+ <button swc-on-click="$host.removeNotification()">Clear Notification</button>
536
+ <button swc-on-click="$host.clearAllTodos()">Clear All Tasks</button>
537
+ `;
538
+ }
539
+ }
540
+ ```
541
+
542
+ **Removal Rules:**
543
+ - `@query`: Setting to `null` or `undefined` removes the single element
544
+ - `@queryAll`: Setting to `null` or `undefined` removes all matching elements
545
+ - Special selectors (`:host`, `:window`, `:document`, etc.) are **NOT removed** (protected)
546
+ - Only affects Light DOM by default; use `{ root: 'shadow' }` to remove from Shadow DOM
547
+
548
+ **Use Cases:**
549
+ - Close notification banners
550
+ - Clear dynamic list items
551
+ - Remove temporary overlays
552
+ - Clean up modal dialogs
553
+
554
+ #### 15. `@queryHost` - Query :host Element
555
+ Specialized query for the component element itself. Supports automatic or explicit usage.
556
+
557
+ All usage patterns automatically set the element reference:
558
+ - Without parentheses: `@queryHost` self?: HTMLElement;
559
+ - Empty parentheses: `@queryHost()` self?: HTMLElement;
560
+ - With options: `@queryHost({ root: 'shadow' })` self?: HTMLElement;
561
+
562
+ ```typescript
563
+ @elementDefine('custom-card')
564
+ class CustomCard extends HTMLElement {
565
+ @queryHost
566
+ self?: HTMLElement;
567
+
568
+ @queryHost()
569
+ selfExplicit?: HTMLElement;
570
+
571
+ printTag() {
572
+ console.log(this.self?.tagName); // Logs: CUSTOM-CARD
573
+ }
574
+ }
575
+ ```
576
+
577
+ #### 16. `@queryAllHost` - Query All :host Children
578
+ Returns all children of the component element. Useful for accessing direct children.
579
+
580
+ All usage patterns automatically set the children array:
581
+ - Without parentheses: `@queryAllHost` children?: HTMLElement[];
582
+ - Empty parentheses: `@queryAllHost()` children?: HTMLElement[];
583
+ - With options: `@queryAllHost({ root: 'shadow' })` children?: HTMLElement[];
584
+
585
+ ```typescript
586
+ @elementDefine('card-container')
587
+ class CardContainer extends HTMLElement {
588
+ @queryAllHost
589
+ allChildren?: HTMLElement[];
590
+
591
+ getChildCount() {
592
+ return this.allChildren?.length ?? 0;
593
+ }
594
+ }
595
+ ```
596
+
428
597
  ---
429
598
 
430
599
  ### Event & Messaging Decorators
431
600
 
432
- #### 15. `@addEventListener(selector, event, options?)`
601
+ #### 17. `@addEventListener(selector, event, options?)`
433
602
  Declarative event binding with automatic delegation.
434
603
 
435
604
  ```typescript
@@ -486,7 +655,7 @@ class TodoApp extends HTMLElement {
486
655
  - ✅ Parent communication (`:parentHost`, `:appHost`)
487
656
  - ✅ Auto-cleanup on `disconnectedCallback`
488
657
 
489
- #### 16. `@emitCustomEvent(target, type, options?)`
658
+ #### 18. `@emitCustomEvent(target, type, options?)`
490
659
  Emit custom events to component hierarchy or global targets.
491
660
 
492
661
  ```typescript
@@ -564,38 +733,47 @@ class UserWidget extends HTMLElement {
564
733
 
565
734
  ### DOM Manipulation Decorators
566
735
 
567
- #### 17. `@applyNode(selector, options?)`
736
+ #### 19. `@applyNode(selector, options?)`
568
737
  Surgically add, update, or replace DOM nodes at specific positions.
569
738
 
570
739
  ```typescript
740
+ import { htmlDivElement, htmlFragment, htmlSpanElement } from '@dooboostore/simple-web-component';
741
+
571
742
  @elementDefine('counter-display')
572
743
  class CounterDisplay extends HTMLElement {
573
744
  private count = 0;
745
+ private errorMessage = '';
574
746
 
575
- // Replace all children with new content
747
+ // Replace all children with new content (return Node)
576
748
  @applyNode('#display', { position: 'replaceChildren' })
577
749
  updateDisplayCount() {
578
- return `<span>${this.count}</span>`;
750
+ return htmlSpanElement(`${this.count}`);
579
751
  }
580
752
 
581
- // Append to container
753
+ // Append to container (return Node)
582
754
  @applyNode('#history', { position: 'beforeEnd' })
583
755
  addHistoryEntry() {
584
- return `<p>Count was: ${this.count}</p>`;
756
+ return htmlFragment(`<p>Count was: ${this.count}</p>`);
585
757
  }
586
758
 
587
- // Set HTML content
759
+ // Set HTML content (return string)
588
760
  @applyNode('#status', { position: 'innerHtml' })
589
761
  setStatus() {
590
762
  return `<strong>Status: Active</strong>`;
591
763
  }
592
764
 
593
- // Set text content
765
+ // Set text content (return string)
594
766
  @applyNode('#counter', { position: 'innerText' })
595
767
  updateCounter() {
596
768
  return this.count.toString();
597
769
  }
598
770
 
771
+ // Remove element if condition is truthy (return boolean)
772
+ @applyNode('#error-banner', { position: 'remove' })
773
+ dismissError() {
774
+ return this.errorMessage === ''; // Remove if no error
775
+ }
776
+
599
777
  @onConnectedInnerHtml
600
778
  render() {
601
779
  return `
@@ -604,6 +782,7 @@ class CounterDisplay extends HTMLElement {
604
782
  <div id="status"></div>
605
783
  <button swc-on-click="$host.count++; $host.updateDisplayCount?.()">+</button>
606
784
  <div id="history"></div>
785
+ <div id="error-banner" style="display: ${this.errorMessage ? 'block' : 'none'};"></div>
607
786
  `;
608
787
  }
609
788
  }
@@ -618,12 +797,349 @@ class CounterDisplay extends HTMLElement {
618
797
  - `'replaceChildren'` - Replace all children
619
798
  - `'innerHtml'` - Set `innerHTML`
620
799
  - `'innerText'` - Set `innerText`
800
+ - `'remove'` - Remove element if returned value is truthy
801
+
802
+ **Root Option:**
803
+ Control which DOM tree is searched with the `root` option:
804
+ - `'auto'` (default) - Search shadow DOM first, then light DOM
805
+ - `'shadow'` - Search only shadow DOM
806
+ - `'light'` - Search only light DOM
807
+ - `'all'` - Search all ancestors
808
+
809
+ #### Convenience Decorators
810
+
811
+ Shorthand decorators for common position-specific operations:
812
+
813
+ **Position-Specific Variants:**
814
+
815
+ Return **DOM Nodes** for position-based insertion. Use DOM creation functions:
816
+
817
+ ```typescript
818
+ import {
819
+ htmlFragment, htmlDivElement, htmlSpanElement,
820
+ createElement, textNode
821
+ } from '@dooboostore/simple-web-component';
822
+
823
+ @elementDefine('my-component')
824
+ class MyComponent extends HTMLElement {
825
+ // Method 1: Using htmlFragment (DocumentFragment)
826
+ @applyBeforeBeginNode('#target') prependTarget() {
827
+ return htmlFragment('<div>Before</div>');
828
+ }
829
+
830
+ // Method 2: Using htmlDivElement (single element)
831
+ @applyAfterBeginNode('#target') afterBeginTarget() {
832
+ return htmlDivElement('After Begin');
833
+ }
834
+
835
+ // Method 3: Manual createElement (full control)
836
+ @applyBeforeEndNode('#target') beforeEndTarget() {
837
+ return htmlDivElement('Before End');
838
+ }
839
+
840
+ @applyAfterEndNode('#target') afterEndTarget() {
841
+ const el = htmlDivElement('After');
842
+ el.classList.add('highlight');
843
+ return el;
844
+ }
845
+
846
+ @applyReplaceNode('#target') replaceTarget() {
847
+ return htmlFragment('<div class="replaced">Replaced</div>');
848
+ }
849
+
850
+ // Multiple nodes: return array
851
+ @applyReplaceChildrenNode('#target') replaceChildrenTarget() {
852
+ return [
853
+ htmlDivElement('Item 1'),
854
+ htmlDivElement('Item 2'),
855
+ htmlDivElement('Item 3')
856
+ ];
857
+ }
858
+
859
+ // String return for innerHtml
860
+ @applyInnerHtmlNode('#target') setInnerHtml() {
861
+ return `<b>Bold</b> <i>Italic</i>`;
862
+ }
863
+
864
+ // String return for innerText
865
+ @applyInnerTextNode('#target') setInnerText() {
866
+ return 'Plain text';
867
+ }
868
+
869
+ // Boolean return for remove
870
+ @applyRemoveNode('#target') removeTarget() {
871
+ return this.shouldHideTarget;
872
+ }
873
+ }
874
+ ```
875
+
876
+ **SSR Support (with custom Document):**
877
+
878
+ ```typescript
879
+ // Browser
880
+ return htmlDivElement('Content'); // Uses global document
881
+
882
+ // SSR (e.g. Node.js with jsdom)
883
+ const ssrDocument = new JSDOM().window.document;
884
+ return htmlDivElement('Content', ssrDocument); // Pass custom doc
885
+ ```
886
+
887
+ **Host-Targeted Variants (`:host` selector):**
888
+
889
+ All Host variants support three usage patterns:
890
+ - Without parentheses: `@applyBeforeBeginNodeHost` prependHost() { ... }
891
+ - With empty parentheses: `@applyBeforeBeginNodeHost()` prependHost() { ... }
892
+ - With options: `@applyBeforeBeginNodeHost({ ... })` prependHost() { ... }
893
+
894
+ ```typescript
895
+ @elementDefine('my-component')
896
+ class MyComponent extends HTMLElement {
897
+ @applyBeforeBeginNodeHost
898
+ prependHost() {
899
+ return htmlFragment('<div>Before Host</div>');
900
+ }
901
+
902
+ @applyAfterBeginNodeHost() afterBeginHost() {
903
+ return htmlSpanElement('After Begin');
904
+ }
905
+
906
+ @applyBeforeEndNodeHost beforeEndHost() {
907
+ const el = htmlDivElement('Before End');
908
+ return el;
909
+ }
910
+
911
+ @applyAfterEndNodeHost afterEndHost() {
912
+ return htmlFragment('After Host');
913
+ }
914
+
915
+ @applyReplaceNodeHost replaceHost() {
916
+ return htmlFragment('<div>Replaced Host</div>');
917
+ }
918
+
919
+ // Multiple children in shadow DOM
920
+ @applyReplaceChildrenNodeHost() replaceChildrenHost() {
921
+ return [
922
+ htmlDivElement('Shadow Child 1'),
923
+ htmlDivElement('Shadow Child 2')
924
+ ];
925
+ }
926
+
927
+ @applyInnerHtmlNodeHost setHostInnerHtml() {
928
+ return `<h1>Host Content</h1>`;
929
+ }
930
+
931
+ @applyInnerTextNodeHost() setHostInnerText() {
932
+ return 'Host text content';
933
+ }
934
+
935
+ @applyRemoveNodeHost removeHost() {
936
+ return this.isRemoved;
937
+ }
938
+ }
939
+ ```
940
+
941
+ **Return Types by Position:**
942
+ - `beforeBegin`, `afterBegin`, `beforeEnd`, `afterEnd`: **Node | Node[]**
943
+ - `replace`: **Node**
944
+ - `replaceChildren`: **Node | Node[]**
945
+ - `innerHtml`: **string**
946
+ - `innerText`: **string**
947
+ - `remove`: **boolean** (truthy = remove)
948
+
949
+ **Available DOM Functions:**
950
+ - `htmlFragment(html, doc?)` - Create DocumentFragment from HTML
951
+ - `textNode(text, doc?)` - Create Text node
952
+ - `commentNode(comment, doc?)` - Create Comment node
953
+ - `createElement(tagName, options?, doc?)` - Create any element with attributes
954
+ - `htmlDivElement(html, doc?)` - Create div element
955
+ - `htmlSpanElement(html, doc?)` - Create span element
956
+ - `htmlButtonElement(html, doc?)` - Create button element
957
+ - `htmlAElement(html, href?, doc?)` - Create anchor element
958
+ - `htmlPElement(html, doc?)` - Create paragraph element
959
+ - `htmlLiElement(html, doc?)` - Create list item element
960
+ - `htmlUlElement(doc?)` - Create unordered list
961
+ - `htmlHeadingElement(level, html, doc?)` - Create h1-h6
962
+ - `htmlSectionElement(doc?)` - Create section
963
+ - `htmlArticleElement(doc?)` - Create article
964
+
965
+ **All functions accept optional `doc` parameter for SSR support.**
966
+
967
+ #### 20. `@setStyle(selector, options?)` / `@updateStyle(selector, options?)` / `@removeStyle(selector, options?)`
968
+
969
+ Manipulate CSS styles on elements with full Shadow DOM support.
970
+
971
+ **Core Features:**
972
+ - `@setStyle` - Set inline style (replaces all)
973
+ - `@updateStyle` - Merge with existing styles
974
+ - `@removeStyle` - Remove specific properties
975
+ - `root` option for Shadow DOM / Light DOM targeting
976
+ - Support for multiple elements with `querySelectorAll`
977
+
978
+ ```typescript
979
+ @elementDefine('styled-component')
980
+ class StyledComponent extends HTMLElement {
981
+ private isActive = false;
982
+
983
+ @setStyle('.button', { root: 'shadow' })
984
+ setPrimaryButtonStyle() {
985
+ return {
986
+ backgroundColor: '#0066cc',
987
+ color: 'white',
988
+ padding: '10px 20px',
989
+ border: 'none',
990
+ borderRadius: '4px'
991
+ };
992
+ }
993
+
994
+ @updateStyle('.button', { root: 'shadow' })
995
+ highlightButton() {
996
+ return {
997
+ boxShadow: '0 0 10px rgba(0, 102, 204, 0.5)',
998
+ transform: 'scale(1.05)'
999
+ };
1000
+ }
1001
+
1002
+ @removeStyle('.button', { root: 'shadow' })
1003
+ clearHighlight() {
1004
+ return ['boxShadow', 'transform'];
1005
+ }
1006
+
1007
+ @onConnectedInnerHtml({ useShadow: true })
1008
+ render() {
1009
+ return `
1010
+ <style>
1011
+ .button { cursor: pointer; transition: all 0.2s; }
1012
+ </style>
1013
+ <button class="button">Styled</button>
1014
+ `;
1015
+ }
1016
+ }
1017
+ ```
1018
+
1019
+ **Host-Targeted Variants:**
1020
+
1021
+ Note: `:host` selector targets the component itself (boundary), so `root` option is ignored.
1022
+
1023
+ All Host variants support three usage patterns:
1024
+ - Without parentheses: `@setStyleHost` setHostStyle() { ... }
1025
+ - With empty parentheses: `@setStyleHost()` setHostStyle() { ... }
1026
+ - With options: `@setStyleHost({ ... })` setHostStyle() { ... }
1027
+
1028
+ ```typescript
1029
+ @setStyleHost
1030
+ setHostStyle() {
1031
+ return { display: 'flex', gap: '10px' };
1032
+ }
1033
+
1034
+ @updateStyleHost()
1035
+ updateHostStyle() {
1036
+ return { backgroundColor: '#f0f0f0' };
1037
+ }
1038
+
1039
+ @removeStyleHost()
1040
+ clearHostStyle() {
1041
+ return ['backgroundColor'];
1042
+ }
1043
+ ```
1044
+
1045
+ ---
1046
+
1047
+ #### 21. `@setClassList(selector, options?)` / `@addClassList(selector, options?)` / `@removeClassList(selector, options?)` / `@toggleClassList(selector, options?)` / `@updateClassList(selector, options?)`
1048
+
1049
+ Manipulate CSS classes on elements declaratively with Shadow DOM support.
1050
+
1051
+ **Core Features:**
1052
+ - `@addClassList` - Add classes
1053
+ - `@removeClassList` - Remove classes
1054
+ - `@toggleClassList` - Toggle classes (returns boolean or bool array)
1055
+ - `@setClassList` - Replace all classes
1056
+ - `@updateClassList` - Merge with existing classes
1057
+ - `root` option for DOM layer targeting
1058
+ - Support for multiple elements
1059
+
1060
+ ```typescript
1061
+ @elementDefine('tab-component')
1062
+ class TabComponent extends HTMLElement {
1063
+ activeTab = 'home';
1064
+
1065
+ @addClassList('.tab', { root: 'shadow' })
1066
+ addTabClasses() {
1067
+ return ['interactive', 'focusable'];
1068
+ }
1069
+
1070
+ @toggleClassList('.tab', { root: 'shadow' })
1071
+ toggleActiveTab() {
1072
+ return this.activeTab === 'home'; // Returns boolean for all matched elements
1073
+ }
1074
+
1075
+ @removeClassList('.tab-inactive', { root: 'shadow' })
1076
+ removeInactiveClass() {
1077
+ return ['disabled'];
1078
+ }
1079
+
1080
+ @setClassList('.tab-pane', { root: 'shadow' })
1081
+ updateTabPane() {
1082
+ return [`pane-${this.activeTab}`, 'content'];
1083
+ }
1084
+
1085
+ @onConnectedInnerHtml({ useShadow: true })
1086
+ render() {
1087
+ return `
1088
+ <style>
1089
+ .tab { cursor: pointer; padding: 10px; }
1090
+ .tab.active { border-bottom: 2px solid blue; }
1091
+ </style>
1092
+ <div class="tabs">
1093
+ <div class="tab" swc-on-click="$host.activeTab='home'; $host.toggleActiveTab?.();">Home</div>
1094
+ <div class="tab" swc-on-click="$host.activeTab='about'; $host.toggleActiveTab?.();">About</div>
1095
+ </div>
1096
+ <div class="tab-pane">Content here</div>
1097
+ `;
1098
+ }
1099
+ }
1100
+ ```
1101
+
1102
+ **Host-Targeted Variants:**
1103
+
1104
+ Note: `:host` selector targets the component itself (boundary), so `root` option is ignored.
1105
+
1106
+ All Host variants support three usage patterns:
1107
+ - Without parentheses: `@setClassListHost` setHostClasses() { ... }
1108
+ - With empty parentheses: `@setClassListHost()` setHostClasses() { ... }
1109
+ - With options: `@setClassListHost({ ... })` setHostClasses() { ... }
1110
+
1111
+ ```typescript
1112
+ @addClassListHost
1113
+ addHostClasses() {
1114
+ return ['component', 'interactive'];
1115
+ }
1116
+
1117
+ @removeClassListHost()
1118
+ removeHostClasses() {
1119
+ return ['loading'];
1120
+ }
1121
+
1122
+ @toggleClassListHost()
1123
+ toggleHostClass() {
1124
+ return this.isOpen; // Apply 'open' class if true
1125
+ }
1126
+
1127
+ @setClassListHost
1128
+ setHostClasses() {
1129
+ return this.isOpen ? ['open', 'expanded'] : ['closed'];
1130
+ }
1131
+
1132
+ @updateClassListHost()
1133
+ updateHostClasses() {
1134
+ return ['visible'];
1135
+ }
1136
+ ```
621
1137
 
622
1138
  ---
623
1139
 
624
1140
  ### Dependency Injection & Singleton
625
1141
 
626
- #### 18. `@Sim`
1142
+ #### 22. `@Sim`
627
1143
  Marks a class as a **Singleton** in the `<swc-app>` DI container for **constructor injection**.
628
1144
 
629
1145
  **Two DI Mechanisms:**
@@ -1270,6 +1786,26 @@ class TextRenderer extends HTMLElement {
1270
1786
  }
1271
1787
  ```
1272
1788
 
1789
+ ### 2b. `commentNode` - HTML Comments
1790
+
1791
+ Create HTML comment nodes (useful for debugging, markers, or template documentation).
1792
+
1793
+ ```typescript
1794
+ @elementDefine('debug-component')
1795
+ class DebugComponent extends HTMLElement {
1796
+ @appendChild('#debug-area')
1797
+ addDebugComment(message: string) {
1798
+ // Create HTML comment node
1799
+ return commentNode`DEBUG: ${message} at ${new Date().toISOString()}`;
1800
+ }
1801
+
1802
+ @onConnectedInnerHtml
1803
+ render() {
1804
+ return `<div id="debug-area"></div>`;
1805
+ }
1806
+ }
1807
+ ```
1808
+
1273
1809
  ### 3. Element Factories (`html***Element`)
1274
1810
 
1275
1811
  Specific factories for common tags. Each returns a **typed element instance**.