@cdevhub/ngx-tw 0.5.0 → 0.6.1
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 +78 -15
- package/fesm2022/cdevhub-ngx-tw-accordion.mjs +16 -5
- package/fesm2022/cdevhub-ngx-tw-accordion.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-avatar.mjs +26 -15
- package/fesm2022/cdevhub-ngx-tw-avatar.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-badge.mjs +17 -5
- package/fesm2022/cdevhub-ngx-tw-badge.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-breadcrumbs.mjs +27 -6
- package/fesm2022/cdevhub-ngx-tw-breadcrumbs.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-button.mjs +23 -11
- package/fesm2022/cdevhub-ngx-tw-button.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-calendar-luxon.mjs +5 -5
- package/fesm2022/cdevhub-ngx-tw-calendar-luxon.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-calendar-testing.mjs +3 -3
- package/fesm2022/cdevhub-ngx-tw-calendar-testing.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-calendar.mjs +427 -122
- package/fesm2022/cdevhub-ngx-tw-calendar.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-card.mjs +21 -11
- package/fesm2022/cdevhub-ngx-tw-card.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-carousel.mjs +215 -96
- package/fesm2022/cdevhub-ngx-tw-carousel.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-checkbox.mjs +108 -64
- package/fesm2022/cdevhub-ngx-tw-checkbox.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-code-block.mjs +18 -6
- package/fesm2022/cdevhub-ngx-tw-code-block.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-collapsible.mjs +55 -30
- package/fesm2022/cdevhub-ngx-tw-collapsible.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-combobox-testing.mjs +218 -0
- package/fesm2022/cdevhub-ngx-tw-combobox-testing.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-combobox.mjs +236 -211
- package/fesm2022/cdevhub-ngx-tw-combobox.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-command-palette-testing.mjs +200 -0
- package/fesm2022/cdevhub-ngx-tw-command-palette-testing.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-command-palette.mjs +34 -7
- package/fesm2022/cdevhub-ngx-tw-command-palette.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-core.mjs +135 -15
- package/fesm2022/cdevhub-ngx-tw-core.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-date-picker-testing.mjs +203 -0
- package/fesm2022/cdevhub-ngx-tw-date-picker-testing.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-date-picker.mjs +160 -102
- package/fesm2022/cdevhub-ngx-tw-date-picker.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-date-range-picker-testing.mjs +209 -0
- package/fesm2022/cdevhub-ngx-tw-date-range-picker-testing.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-date-range-picker.mjs +182 -107
- package/fesm2022/cdevhub-ngx-tw-date-range-picker.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-dialog-testing.mjs +122 -0
- package/fesm2022/cdevhub-ngx-tw-dialog-testing.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-dialog.mjs +11 -2
- package/fesm2022/cdevhub-ngx-tw-dialog.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-empty-state.mjs +1 -1
- package/fesm2022/cdevhub-ngx-tw-empty-state.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-file-upload-testing.mjs +149 -0
- package/fesm2022/cdevhub-ngx-tw-file-upload-testing.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-file-upload.mjs +57 -67
- package/fesm2022/cdevhub-ngx-tw-file-upload.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-flip-card.mjs +42 -24
- package/fesm2022/cdevhub-ngx-tw-flip-card.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-form-field.mjs +114 -21
- package/fesm2022/cdevhub-ngx-tw-form-field.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-icon.mjs +16 -6
- package/fesm2022/cdevhub-ngx-tw-icon.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-input.mjs +52 -61
- package/fesm2022/cdevhub-ngx-tw-input.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-item.mjs +28 -4
- package/fesm2022/cdevhub-ngx-tw-item.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-menu-testing.mjs +168 -0
- package/fesm2022/cdevhub-ngx-tw-menu-testing.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-menu.mjs +58 -21
- package/fesm2022/cdevhub-ngx-tw-menu.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-number-input.mjs +34 -1
- package/fesm2022/cdevhub-ngx-tw-number-input.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-paginator.mjs +95 -45
- package/fesm2022/cdevhub-ngx-tw-paginator.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-popover.mjs +81 -32
- package/fesm2022/cdevhub-ngx-tw-popover.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-progress-bar.mjs +1 -1
- package/fesm2022/cdevhub-ngx-tw-progress-bar.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-radio.mjs +131 -98
- package/fesm2022/cdevhub-ngx-tw-radio.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-segmented-control.mjs +207 -42
- package/fesm2022/cdevhub-ngx-tw-segmented-control.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-select-testing.mjs +165 -0
- package/fesm2022/cdevhub-ngx-tw-select-testing.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-select.mjs +386 -210
- package/fesm2022/cdevhub-ngx-tw-select.mjs.map +1 -1
- package/fesm2022/{cdevhub-ngx-tw-sheet-sheet-renderer-ByyNluUd.mjs → cdevhub-ngx-tw-sheet-sheet-renderer-CqVJwgmp.mjs} +3 -3
- package/fesm2022/cdevhub-ngx-tw-sheet-sheet-renderer-CqVJwgmp.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-sheet-testing.mjs +129 -0
- package/fesm2022/cdevhub-ngx-tw-sheet-testing.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-sheet.mjs +23 -7
- package/fesm2022/cdevhub-ngx-tw-sheet.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-slider.mjs +89 -54
- package/fesm2022/cdevhub-ngx-tw-slider.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-sort.mjs +122 -17
- package/fesm2022/cdevhub-ngx-tw-sort.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-split.mjs +72 -19
- package/fesm2022/cdevhub-ngx-tw-split.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-stat.mjs +63 -25
- package/fesm2022/cdevhub-ngx-tw-stat.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-stepper.mjs +35 -8
- package/fesm2022/cdevhub-ngx-tw-stepper.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-switch.mjs +54 -48
- package/fesm2022/cdevhub-ngx-tw-switch.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-tab-nav.mjs +12 -15
- package/fesm2022/cdevhub-ngx-tw-tab-nav.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-table.mjs +74 -13
- package/fesm2022/cdevhub-ngx-tw-table.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-tabs.mjs +84 -30
- package/fesm2022/cdevhub-ngx-tw-tabs.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-tags-input-testing.mjs +172 -0
- package/fesm2022/cdevhub-ngx-tw-tags-input-testing.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-tags-input.mjs +107 -71
- package/fesm2022/cdevhub-ngx-tw-tags-input.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-textarea.mjs +41 -9
- package/fesm2022/cdevhub-ngx-tw-textarea.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-theme.mjs +257 -45
- package/fesm2022/cdevhub-ngx-tw-theme.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-time-picker-testing.mjs +201 -0
- package/fesm2022/cdevhub-ngx-tw-time-picker-testing.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-time-picker.mjs +184 -77
- package/fesm2022/cdevhub-ngx-tw-time-picker.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-timeline.mjs +110 -27
- package/fesm2022/cdevhub-ngx-tw-timeline.mjs.map +1 -1
- package/fesm2022/{cdevhub-ngx-tw-toast-toast-renderer-DSu4YoTy.mjs → cdevhub-ngx-tw-toast-toast-renderer-BlDxemsU.mjs} +107 -19
- package/fesm2022/cdevhub-ngx-tw-toast-toast-renderer-BlDxemsU.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-toast.mjs +21 -13
- package/fesm2022/cdevhub-ngx-tw-toast.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-tooltip.mjs +102 -15
- package/fesm2022/cdevhub-ngx-tw-tooltip.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-transfer-testing.mjs +219 -0
- package/fesm2022/cdevhub-ngx-tw-transfer-testing.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-transfer.mjs +88 -69
- package/fesm2022/cdevhub-ngx-tw-transfer.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-tree.mjs +68 -19
- package/fesm2022/cdevhub-ngx-tw-tree.mjs.map +1 -1
- package/index.json +1 -1
- package/package.json +63 -2
- package/theme/_base.css +13 -0
- package/theme/_dark.css +111 -36
- package/theme/_high-contrast-dark.css +367 -0
- package/theme/_high-contrast.css +13 -2
- package/theme/_light.css +332 -0
- package/theme/_semantic.css +60 -26
- package/theme/_typography.css +17 -0
- package/theme/index.css +86 -7
- package/types/cdevhub-ngx-tw-accordion.d.ts +12 -4
- package/types/cdevhub-ngx-tw-avatar.d.ts +10 -3
- package/types/cdevhub-ngx-tw-button.d.ts +1 -0
- package/types/cdevhub-ngx-tw-calendar-luxon.d.ts +2 -2
- package/types/cdevhub-ngx-tw-calendar-testing.d.ts +1 -1
- package/types/cdevhub-ngx-tw-calendar.d.ts +179 -73
- package/types/cdevhub-ngx-tw-card.d.ts +11 -3
- package/types/cdevhub-ngx-tw-carousel.d.ts +89 -20
- package/types/cdevhub-ngx-tw-checkbox.d.ts +40 -12
- package/types/cdevhub-ngx-tw-code-block.d.ts +11 -2
- package/types/cdevhub-ngx-tw-collapsible.d.ts +32 -5
- package/types/cdevhub-ngx-tw-combobox-testing.d.ts +157 -0
- package/types/cdevhub-ngx-tw-combobox.d.ts +94 -53
- package/types/cdevhub-ngx-tw-command-palette-testing.d.ts +136 -0
- package/types/cdevhub-ngx-tw-command-palette.d.ts +33 -5
- package/types/cdevhub-ngx-tw-core.d.ts +152 -21
- package/types/cdevhub-ngx-tw-date-picker-testing.d.ts +141 -0
- package/types/cdevhub-ngx-tw-date-picker.d.ts +37 -29
- package/types/cdevhub-ngx-tw-date-range-picker-testing.d.ts +148 -0
- package/types/cdevhub-ngx-tw-date-range-picker.d.ts +36 -29
- package/types/cdevhub-ngx-tw-dialog-testing.d.ts +98 -0
- package/types/cdevhub-ngx-tw-dialog.d.ts +12 -3
- package/types/cdevhub-ngx-tw-file-upload-testing.d.ts +120 -0
- package/types/cdevhub-ngx-tw-file-upload.d.ts +8 -10
- package/types/cdevhub-ngx-tw-flip-card.d.ts +18 -11
- package/types/cdevhub-ngx-tw-form-field.d.ts +19 -2
- package/types/cdevhub-ngx-tw-icon.d.ts +3 -3
- package/types/cdevhub-ngx-tw-input.d.ts +5 -8
- package/types/cdevhub-ngx-tw-item.d.ts +2 -0
- package/types/cdevhub-ngx-tw-menu-testing.d.ts +131 -0
- package/types/cdevhub-ngx-tw-menu.d.ts +2 -2
- package/types/cdevhub-ngx-tw-number-input.d.ts +20 -0
- package/types/cdevhub-ngx-tw-paginator.d.ts +58 -24
- package/types/cdevhub-ngx-tw-popover.d.ts +41 -10
- package/types/cdevhub-ngx-tw-progress-bar.d.ts +1 -1
- package/types/cdevhub-ngx-tw-radio.d.ts +68 -25
- package/types/cdevhub-ngx-tw-segmented-control.d.ts +79 -16
- package/types/cdevhub-ngx-tw-select-testing.d.ts +114 -0
- package/types/cdevhub-ngx-tw-select.d.ts +104 -33
- package/types/cdevhub-ngx-tw-sheet-testing.d.ts +104 -0
- package/types/cdevhub-ngx-tw-sheet.d.ts +19 -3
- package/types/cdevhub-ngx-tw-slider.d.ts +79 -25
- package/types/cdevhub-ngx-tw-sort.d.ts +45 -4
- package/types/cdevhub-ngx-tw-split.d.ts +32 -2
- package/types/cdevhub-ngx-tw-stat.d.ts +14 -4
- package/types/cdevhub-ngx-tw-stepper.d.ts +16 -2
- package/types/cdevhub-ngx-tw-switch.d.ts +35 -13
- package/types/cdevhub-ngx-tw-tab-nav.d.ts +7 -6
- package/types/cdevhub-ngx-tw-table.d.ts +53 -10
- package/types/cdevhub-ngx-tw-tabs.d.ts +41 -10
- package/types/cdevhub-ngx-tw-tags-input-testing.d.ts +135 -0
- package/types/cdevhub-ngx-tw-tags-input.d.ts +26 -11
- package/types/cdevhub-ngx-tw-textarea.d.ts +3 -2
- package/types/cdevhub-ngx-tw-theme.d.ts +245 -39
- package/types/cdevhub-ngx-tw-time-picker-testing.d.ts +129 -0
- package/types/cdevhub-ngx-tw-time-picker.d.ts +76 -19
- package/types/cdevhub-ngx-tw-timeline.d.ts +36 -12
- package/types/cdevhub-ngx-tw-toast.d.ts +20 -6
- package/types/cdevhub-ngx-tw-tooltip.d.ts +28 -6
- package/types/cdevhub-ngx-tw-transfer-testing.d.ts +152 -0
- package/types/cdevhub-ngx-tw-transfer.d.ts +21 -8
- package/types/cdevhub-ngx-tw-tree.d.ts +6 -1
- package/fesm2022/cdevhub-ngx-tw-sheet-sheet-renderer-ByyNluUd.mjs.map +0 -1
- package/fesm2022/cdevhub-ngx-tw-toast-toast-renderer-DSu4YoTy.mjs.map +0 -1
|
@@ -24,45 +24,66 @@ const breadcrumbs = tv({
|
|
|
24
24
|
'focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-500 cursor-pointer',
|
|
25
25
|
},
|
|
26
26
|
variants: {
|
|
27
|
+
// A breadcrumb row is inline text navigation, so its box is a `min-h-*`
|
|
28
|
+
// floor rather than a pinned height (docs/vertical-rhythm.md §2) — the
|
|
29
|
+
// trail must stay free to wrap onto a second line in a narrow container.
|
|
30
|
+
// The floor lands on the control scale (24 / 32 / 36 / 44 / 48) for two
|
|
31
|
+
// reasons:
|
|
32
|
+
// 1. The font ramp cannot separate all five steps on its own —
|
|
33
|
+
// `text-lg` is forbidden by CLAUDE.md, so `sm`/`md` both resolve to
|
|
34
|
+
// `text-sm` and `lg`/`xl` both to `text-base`. Without the floor,
|
|
35
|
+
// two of the five size steps are visually dead.
|
|
36
|
+
// 2. `overflowTrigger` is a square button (24 / 28 / 32 / 36 / 36) that
|
|
37
|
+
// is taller than the bare text line box at every size, so a trail
|
|
38
|
+
// that collapses would jump in height the moment the ellipsis
|
|
39
|
+
// appears. With the floor at or above the trigger square at every
|
|
40
|
+
// size, the row height no longer depends on whether it renders.
|
|
41
|
+
// The `lg`/`xl` trigger saturation at `size-9` is the codified CLAUDE.md
|
|
42
|
+
// note — deliberately left alone.
|
|
27
43
|
size: {
|
|
28
44
|
xs: {
|
|
29
45
|
list: 'gap-1',
|
|
46
|
+
item: 'min-h-6',
|
|
30
47
|
link: 'text-xs',
|
|
31
48
|
current: 'text-xs',
|
|
32
49
|
disabled: 'text-xs',
|
|
33
|
-
separator: 'text-xs',
|
|
50
|
+
separator: 'min-h-6 text-xs',
|
|
34
51
|
overflowTrigger: 'size-6 text-xs',
|
|
35
52
|
},
|
|
36
53
|
sm: {
|
|
37
54
|
list: 'gap-1',
|
|
55
|
+
item: 'min-h-8',
|
|
38
56
|
link: 'text-sm',
|
|
39
57
|
current: 'text-sm',
|
|
40
58
|
disabled: 'text-sm',
|
|
41
|
-
separator: 'text-sm',
|
|
59
|
+
separator: 'min-h-8 text-sm',
|
|
42
60
|
overflowTrigger: 'size-7 text-sm',
|
|
43
61
|
},
|
|
44
62
|
md: {
|
|
45
63
|
list: 'gap-1.5',
|
|
64
|
+
item: 'min-h-9',
|
|
46
65
|
link: 'text-sm',
|
|
47
66
|
current: 'text-sm',
|
|
48
67
|
disabled: 'text-sm',
|
|
49
|
-
separator: 'text-sm',
|
|
68
|
+
separator: 'min-h-9 text-sm',
|
|
50
69
|
overflowTrigger: 'size-8 text-sm',
|
|
51
70
|
},
|
|
52
71
|
lg: {
|
|
53
72
|
list: 'gap-2',
|
|
73
|
+
item: 'min-h-11',
|
|
54
74
|
link: 'text-base',
|
|
55
75
|
current: 'text-base',
|
|
56
76
|
disabled: 'text-base',
|
|
57
|
-
separator: 'text-base',
|
|
77
|
+
separator: 'min-h-11 text-base',
|
|
58
78
|
overflowTrigger: 'size-9 text-base',
|
|
59
79
|
},
|
|
60
80
|
xl: {
|
|
61
81
|
list: 'gap-2',
|
|
82
|
+
item: 'min-h-12',
|
|
62
83
|
link: 'text-base',
|
|
63
84
|
current: 'text-base',
|
|
64
85
|
disabled: 'text-base',
|
|
65
|
-
separator: 'text-base',
|
|
86
|
+
separator: 'min-h-12 text-base',
|
|
66
87
|
overflowTrigger: 'size-9 text-base',
|
|
67
88
|
},
|
|
68
89
|
},
|
|
@@ -392,7 +413,7 @@ class BreadcrumbsComponent {
|
|
|
392
413
|
}
|
|
393
414
|
</ol>
|
|
394
415
|
</nav>
|
|
395
|
-
`, isInline: true, dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: IconComponent, selector: "tw-icon", inputs: ["name", "img", "color", "size", "
|
|
416
|
+
`, isInline: true, dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: IconComponent, selector: "tw-icon", inputs: ["name", "img", "color", "size", "aria-label", "svg"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, deferBlockDependencies: [() => [/* @ts-ignore */
|
|
396
417
|
import('@cdevhub/ngx-tw/menu').then(m => m.MenuComponent), /* @ts-ignore */
|
|
397
418
|
import('@cdevhub/ngx-tw/menu').then(m => m.MenuItemDirective), /* @ts-ignore */
|
|
398
419
|
import('@cdevhub/ngx-tw/menu').then(m => m.MenuTriggerDirective)]] });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cdevhub-ngx-tw-breadcrumbs.mjs","sources":["../../../projects/ngx-tw/breadcrumbs/breadcrumbs.ts","../../../projects/ngx-tw/breadcrumbs/cdevhub-ngx-tw-breadcrumbs.ts"],"sourcesContent":["import { NgTemplateOutlet } from '@angular/common';\nimport {\n ChangeDetectionStrategy,\n Component,\n Directive,\n TemplateRef,\n computed,\n contentChild,\n forwardRef,\n inject,\n input,\n} from '@angular/core';\nimport { tv } from 'tailwind-variants';\nimport type { TwSize } from '@cdevhub/ngx-tw/core';\nimport { IconComponent } from '@cdevhub/ngx-tw/icon';\nimport {\n MenuComponent,\n MenuItemDirective,\n MenuTriggerDirective,\n} from '@cdevhub/ngx-tw/menu';\n\n// ── Public types ──\n\n/**\n * Describes a single hop in a breadcrumb trail. The last entry in `items` is\n * always treated as the current page (rendered with `aria-current=\"page\"`,\n * never as an anchor).\n *\n * The generic `T` types the optional `data` payload — useful for forwarding\n * routerLink commands or any other consumer-defined metadata to the custom\n * item template without unsafe casts.\n */\nexport interface TwBreadcrumbsItem<T = unknown> {\n /** Visible label shown for this hop. */\n label: string;\n /**\n * Optional href used to render the default anchor. Omit on the current item\n * (the last entry). Ignored when a custom `*twBreadcrumbsItem` template is\n * projected — the consumer owns the anchor in that case.\n */\n href?: string;\n /**\n * Opaque payload forwarded to the consumer's `*twBreadcrumbsItem` template\n * via the template context. Use this to carry router commands or any other\n * data your template needs.\n */\n data?: T;\n /**\n * When true, the item renders as muted text without an anchor (even if\n * `href` is set). Receives `aria-disabled=\"true\"`.\n */\n disabled?: boolean;\n}\n\n/**\n * Template context passed to `*twBreadcrumbsItem` for each rendered item.\n */\nexport interface TwBreadcrumbsItemContext<T = unknown> {\n /** The item record (also exposed via `let-item=\"$implicit\"`). */\n $implicit: TwBreadcrumbsItem<T>;\n /** Same as `$implicit`. Available via `let-item=\"item\"` for readability. */\n item: TwBreadcrumbsItem<T>;\n /** Zero-based index of this item in the original `items` input. */\n index: number;\n /** `true` only for the last entry — the current page. */\n isCurrent: boolean;\n}\n\n// ── tv() config ──\n\nconst breadcrumbs = tv(\n {\n slots: {\n // The host outer element (<tw-breadcrumbs>) is a custom element; we wrap\n // the actual landmark <nav> inside the template. The list is a real <ol>\n // so the trail keeps its semantic ordering for assistive tech.\n list: 'flex flex-wrap items-center m-0 p-0 list-none min-w-0',\n item: 'inline-flex items-center min-w-0',\n link:\n 'inline-flex items-center text-fg-muted hover:text-fg transition-colors duration-200 motion-reduce:transition-none ' +\n 'focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-500 rounded-md no-underline truncate',\n current: 'inline-flex items-center text-fg font-medium truncate',\n disabled:\n 'inline-flex items-center text-fg-subtle opacity-50 cursor-not-allowed truncate',\n // Separator carries rtl:rotate-180 so the chevron flips for RTL layouts;\n // requires a `dir=\"rtl\"` ancestor for Tailwind's rtl: variant to match.\n separator:\n 'inline-flex items-center text-fg-subtle shrink-0 select-none rtl:rotate-180',\n overflowTrigger:\n 'inline-flex items-center justify-center rounded-md text-fg-muted hover:text-fg hover:bg-surface-muted ' +\n 'transition-colors duration-200 motion-reduce:transition-none ' +\n 'focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-500 cursor-pointer',\n },\n variants: {\n size: {\n xs: {\n list: 'gap-1',\n link: 'text-xs',\n current: 'text-xs',\n disabled: 'text-xs',\n separator: 'text-xs',\n overflowTrigger: 'size-6 text-xs',\n },\n sm: {\n list: 'gap-1',\n link: 'text-sm',\n current: 'text-sm',\n disabled: 'text-sm',\n separator: 'text-sm',\n overflowTrigger: 'size-7 text-sm',\n },\n md: {\n list: 'gap-1.5',\n link: 'text-sm',\n current: 'text-sm',\n disabled: 'text-sm',\n separator: 'text-sm',\n overflowTrigger: 'size-8 text-sm',\n },\n lg: {\n list: 'gap-2',\n link: 'text-base',\n current: 'text-base',\n disabled: 'text-base',\n separator: 'text-base',\n overflowTrigger: 'size-9 text-base',\n },\n xl: {\n list: 'gap-2',\n link: 'text-base',\n current: 'text-base',\n disabled: 'text-base',\n separator: 'text-base',\n overflowTrigger: 'size-9 text-base',\n },\n },\n },\n defaultVariants: { size: 'md' },\n },\n { twMerge: true },\n);\n\n// Separator glyph size mirrors the menu-item icon scale: keep the chevron one\n// step below the text-size step so it reads as a quiet hint, not a glyph.\nconst ICON_SIZE_FOR_BREADCRUMB: Record<TwSize, TwSize> = {\n xs: 'xs',\n sm: 'sm',\n md: 'sm',\n lg: 'md',\n xl: 'md',\n};\n\n// ── Template slot directives ──\n\n/**\n * Structural directive applied to an `<ng-template>` projected into\n * `tw-breadcrumbs`. Replaces the default per-item rendering so consumers can\n * wire their own anchors (typically `routerLink`-bound). The template receives\n * `$implicit` (the item), `item`, `index`, and `isCurrent` in its context.\n */\n@Directive({\n selector: '[twBreadcrumbsItem]',\n})\nexport class BreadcrumbsItemTemplateDirective<T = unknown> {\n /** @internal */\n readonly templateRef = inject<TemplateRef<TwBreadcrumbsItemContext<T>>>(TemplateRef);\n\n /** Type-narrows the template context for `let-` destructuring in templates. */\n static ngTemplateContextGuard<T>(\n _dir: BreadcrumbsItemTemplateDirective<T>,\n _ctx: unknown,\n ): _ctx is TwBreadcrumbsItemContext<T> {\n return true;\n }\n}\n\n/**\n * Structural directive applied to an `<ng-template>` projected into\n * `tw-breadcrumbs`. Replaces the default chevron separator with custom\n * content (any element — icon, text, dot, etc.).\n */\n@Directive({\n selector: '[twBreadcrumbsSeparator]',\n})\nexport class BreadcrumbsSeparatorTemplateDirective {\n /** @internal */\n readonly templateRef = inject<TemplateRef<unknown>>(TemplateRef);\n}\n\n// ── Link styling directive ──\n\n/**\n * Applies the parent `tw-breadcrumbs` link / current / disabled styling to a\n * consumer-projected anchor (or span) inside `*twBreadcrumbsItem`. Without\n * this directive, projected content renders unstyled — only the layout `<li>`\n * is provided by the component.\n *\n * The directive sets `aria-current=\"page\"` when `current` is true. It does\n * NOT itself swap the element tag; the consumer should still pick `<a>` for\n * link items and `<span>` for the current item.\n */\n@Directive({\n selector: '[twBreadcrumbsLink]',\n host: {\n '[class]': 'classes()',\n '[attr.aria-current]': 'current() ? \"page\" : null',\n },\n})\nexport class BreadcrumbsLinkDirective {\n /** When true, this element is the current page: styled bold and gets `aria-current=\"page\"`. Defaults to `false`. */\n readonly current = input(false);\n\n /** When true, applies disabled styling (muted, not-allowed cursor). Defaults to `false`. */\n readonly disabled = input(false);\n\n private readonly parent = inject<BreadcrumbsComponent<unknown>>(\n forwardRef(() => BreadcrumbsComponent),\n );\n\n /** @internal Combined classes pulled from the parent component's slot tokens. */\n readonly classes = computed(() => {\n const slots = this.parent.slotResult();\n if (this.disabled()) return slots.disabled();\n if (this.current()) return slots.current();\n return slots.link();\n });\n}\n\n// ── Rendered entry types (internal) ──\n\ninterface RenderedItem<T> {\n readonly kind: 'item';\n readonly item: TwBreadcrumbsItem<T>;\n readonly index: number;\n readonly isCurrent: boolean;\n readonly key: string;\n}\n\ninterface RenderedSeparator {\n readonly kind: 'separator';\n readonly key: string;\n}\n\ninterface RenderedOverflow {\n readonly kind: 'overflow';\n readonly key: string;\n}\n\ntype RenderedEntry<T> = RenderedItem<T> | RenderedSeparator | RenderedOverflow;\n\n// ── BreadcrumbsComponent ──\n\n/**\n * Breadcrumb trail rendered as a `<nav>` landmark with an ordered list of\n * navigation hops. The last entry in `items` is the current page (gets\n * `aria-current=\"page\"`, never an anchor). Items beyond `maxItems` are\n * collapsed behind an ellipsis trigger that opens an overflow menu.\n *\n * Customize per-item rendering with `*twBreadcrumbsItem` (consumer projects\n * router-aware anchors), and the separator with `*twBreadcrumbsSeparator` or\n * the `separator` input (Lucide icon name).\n *\n * @example\n * ```html\n * <tw-breadcrumbs [items]=\"trail\" />\n *\n * <tw-breadcrumbs [items]=\"trail\" [maxItems]=\"3\" separator=\"slash\">\n * <ng-template twBreadcrumbsItem let-item let-isCurrent=\"isCurrent\">\n * @if (isCurrent) {\n * <span twBreadcrumbsLink [current]=\"true\">{{ item.label }}</span>\n * } @else {\n * <a twBreadcrumbsLink [routerLink]=\"item.data?.routerLink\">{{ item.label }}</a>\n * }\n * </ng-template>\n * </tw-breadcrumbs>\n * ```\n */\n@Component({\n selector: 'tw-breadcrumbs',\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [\n NgTemplateOutlet,\n IconComponent,\n MenuComponent,\n MenuItemDirective,\n MenuTriggerDirective,\n ],\n host: {\n // Custom elements default to inline; force a block layout so the trail\n // behaves predictably inside flex / grid containers.\n class: 'block min-w-0',\n },\n template: `\n <nav [attr.aria-label]=\"ariaLabel()\">\n <ol [class]=\"listClasses()\">\n @for (entry of renderedEntries(); track entry.key) {\n @if (entry.kind === 'item') {\n <li [class]=\"itemClasses()\">\n @if (itemTemplate(); as tpl) {\n <ng-container\n *ngTemplateOutlet=\"\n tpl.templateRef;\n context: {\n $implicit: entry.item,\n item: entry.item,\n index: entry.index,\n isCurrent: entry.isCurrent\n }\n \"\n />\n } @else {\n @if (entry.isCurrent) {\n <span [class]=\"currentClasses()\" aria-current=\"page\">{{ entry.item.label }}</span>\n } @else if (entry.item.disabled) {\n <span [class]=\"disabledClasses()\" aria-disabled=\"true\">{{ entry.item.label }}</span>\n } @else if (entry.item.href) {\n <a [class]=\"linkClasses()\" [attr.href]=\"entry.item.href\">{{ entry.item.label }}</a>\n } @else {\n <span [class]=\"linkClasses()\">{{ entry.item.label }}</span>\n }\n }\n </li>\n } @else if (entry.kind === 'separator') {\n <li [class]=\"separatorClasses()\" aria-hidden=\"true\">\n @if (separatorTemplate(); as sepTpl) {\n <ng-container *ngTemplateOutlet=\"sepTpl.templateRef\" />\n } @else {\n <tw-icon [name]=\"separator()\" [size]=\"iconSize()\" />\n }\n </li>\n } @else {\n <li [class]=\"itemClasses()\">\n <!--\n The overflow menu pulls in tw-menu + @angular/cdk/menu (overlay,\n focus-trap). @defer keeps that weight in a lazy chunk so a plain\n breadcrumb trail (the common case, maxItems=0) never bundles it;\n the chunk loads when the trigger scrolls into view and is\n prefetched on idle to keep the first click responsive. The\n @placeholder mirrors the trigger so layout and a11y are stable.\n -->\n @defer (on viewport; prefetch on idle) {\n <button\n type=\"button\"\n [class]=\"overflowTriggerClasses()\"\n [twMenuTrigger]=\"overflowMenuTpl\"\n aria-label=\"Show more breadcrumbs\"\n >\n <span aria-hidden=\"true\">…</span>\n </button>\n <ng-template #overflowMenuTpl>\n <tw-menu aria-label=\"Hidden breadcrumb items\">\n @for (hidden of collapsedItems(); track hidden) {\n @if (hidden.disabled || !hidden.href) {\n <span twMenuItem [attr.aria-disabled]=\"hidden.disabled ? 'true' : null\">\n {{ hidden.label }}\n </span>\n } @else {\n <a twMenuItem [attr.href]=\"hidden.href\">{{ hidden.label }}</a>\n }\n }\n </tw-menu>\n </ng-template>\n } @placeholder {\n <button\n type=\"button\"\n [class]=\"overflowTriggerClasses()\"\n aria-label=\"Show more breadcrumbs\"\n >\n <span aria-hidden=\"true\">…</span>\n </button>\n }\n </li>\n }\n }\n </ol>\n </nav>\n `,\n})\nexport class BreadcrumbsComponent<T = unknown> {\n /** The full breadcrumb trail. The last entry is treated as the current page. Defaults to `[]`. */\n readonly items = input<readonly TwBreadcrumbsItem<T>[]>([]);\n\n /** Density of the trail — drives font size, gap, icon size, and the overflow-button square. Defaults to `'md'`. */\n readonly size = input<TwSize>('md');\n\n /**\n * When greater than `0`, the trail collapses any middle items past this\n * threshold behind an overflow menu. The first item and the current (last)\n * item are always visible. Values `< 2` are clamped to `2`. Defaults to `0`\n * (no collapsing).\n */\n readonly maxItems = input<number>(0);\n\n /**\n * Lucide icon name used for the default separator between items. Ignored\n * when a `*twBreadcrumbsSeparator` template is projected. Defaults to\n * `'chevron-right'`. Requires the consumer to register the icon via\n * `provideTwLucideIcons({ ChevronRight, ... })`.\n */\n readonly separator = input<string>('chevron-right');\n\n /** Accessible label applied to the `<nav>` landmark. Defaults to `'Breadcrumb'`. Aliased as `aria-label`. */\n readonly ariaLabel = input<string>('Breadcrumb', { alias: 'aria-label' });\n\n /** Custom per-item template projected via `*twBreadcrumbsItem`. */\n readonly itemTemplate = contentChild(BreadcrumbsItemTemplateDirective<T>);\n\n /** Custom separator template projected via `*twBreadcrumbsSeparator`. */\n readonly separatorTemplate = contentChild(BreadcrumbsSeparatorTemplateDirective);\n\n /** @internal Materialised tv() slot result; exposed so `BreadcrumbsLinkDirective` can pull link/current/disabled classes. */\n readonly slotResult = computed(() => breadcrumbs({ size: this.size() }));\n\n /** @internal Icon size for the default chevron separator. */\n readonly iconSize = computed(() => ICON_SIZE_FOR_BREADCRUMB[this.size()]);\n\n /** @internal Class strings per slot, memoised. */\n readonly listClasses = computed(() => this.slotResult().list());\n readonly itemClasses = computed(() => this.slotResult().item());\n readonly linkClasses = computed(() => this.slotResult().link());\n readonly currentClasses = computed(() => this.slotResult().current());\n readonly disabledClasses = computed(() => this.slotResult().disabled());\n readonly separatorClasses = computed(() => this.slotResult().separator());\n readonly overflowTriggerClasses = computed(() => this.slotResult().overflowTrigger());\n\n /** @internal Items hidden behind the overflow menu. Empty when no collapsing is active. */\n readonly collapsedItems = computed<readonly TwBreadcrumbsItem<T>[]>(() => {\n const all = this.items();\n const max = this.maxItems();\n if (max <= 0 || all.length <= 2 || all.length <= max) return [];\n const effectiveMax = Math.max(max, 2);\n if (all.length <= effectiveMax) return [];\n const tailCount = effectiveMax - 1;\n const hiddenEnd = all.length - tailCount;\n return all.slice(1, hiddenEnd);\n });\n\n /** @internal Flat sequence of items + separators + overflow used by the template's @for. */\n readonly renderedEntries = computed<RenderedEntry<T>[]>(() => {\n const all = this.items();\n const max = this.maxItems();\n if (all.length === 0) return [];\n\n const entries: RenderedEntry<T>[] = [];\n const lastIndex = all.length - 1;\n const pushSeparator = (key: string) =>\n entries.push({ kind: 'separator', key: `sep-${key}` });\n\n const collapsing = max > 0 && all.length > 2 && all.length > max;\n const effectiveMax = Math.max(max, 2);\n const shouldCollapse = collapsing && all.length > effectiveMax;\n\n if (!shouldCollapse) {\n for (let i = 0; i < all.length; i++) {\n if (i > 0) pushSeparator(`${i}`);\n entries.push({\n kind: 'item',\n item: all[i],\n index: i,\n isCurrent: i === lastIndex,\n key: `item-${i}`,\n });\n }\n return entries;\n }\n\n const tailCount = effectiveMax - 1;\n const tailStart = all.length - tailCount;\n\n // First item.\n entries.push({\n kind: 'item',\n item: all[0],\n index: 0,\n isCurrent: lastIndex === 0,\n key: 'item-0',\n });\n pushSeparator('after-first');\n\n // Overflow trigger.\n entries.push({ kind: 'overflow', key: 'overflow' });\n pushSeparator('after-overflow');\n\n // Tail items.\n for (let i = tailStart; i < all.length; i++) {\n if (i > tailStart) pushSeparator(`${i}`);\n entries.push({\n kind: 'item',\n item: all[i],\n index: i,\n isCurrent: i === lastIndex,\n key: `item-${i}`,\n });\n }\n\n return entries;\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cdevhub-ngx-tw-breadcrumbs.mjs","sources":["../../../projects/ngx-tw/breadcrumbs/breadcrumbs.ts","../../../projects/ngx-tw/breadcrumbs/cdevhub-ngx-tw-breadcrumbs.ts"],"sourcesContent":["import { NgTemplateOutlet } from '@angular/common';\nimport {\n ChangeDetectionStrategy,\n Component,\n Directive,\n TemplateRef,\n computed,\n contentChild,\n forwardRef,\n inject,\n input,\n} from '@angular/core';\nimport { tv } from 'tailwind-variants';\nimport type { TwSize } from '@cdevhub/ngx-tw/core';\nimport { IconComponent } from '@cdevhub/ngx-tw/icon';\nimport {\n MenuComponent,\n MenuItemDirective,\n MenuTriggerDirective,\n} from '@cdevhub/ngx-tw/menu';\n\n// ── Public types ──\n\n/**\n * Describes a single hop in a breadcrumb trail. The last entry in `items` is\n * always treated as the current page (rendered with `aria-current=\"page\"`,\n * never as an anchor).\n *\n * The generic `T` types the optional `data` payload — useful for forwarding\n * routerLink commands or any other consumer-defined metadata to the custom\n * item template without unsafe casts.\n */\nexport interface TwBreadcrumbsItem<T = unknown> {\n /** Visible label shown for this hop. */\n label: string;\n /**\n * Optional href used to render the default anchor. Omit on the current item\n * (the last entry). Ignored when a custom `*twBreadcrumbsItem` template is\n * projected — the consumer owns the anchor in that case.\n */\n href?: string;\n /**\n * Opaque payload forwarded to the consumer's `*twBreadcrumbsItem` template\n * via the template context. Use this to carry router commands or any other\n * data your template needs.\n */\n data?: T;\n /**\n * When true, the item renders as muted text without an anchor (even if\n * `href` is set). Receives `aria-disabled=\"true\"`.\n */\n disabled?: boolean;\n}\n\n/**\n * Template context passed to `*twBreadcrumbsItem` for each rendered item.\n */\nexport interface TwBreadcrumbsItemContext<T = unknown> {\n /** The item record (also exposed via `let-item=\"$implicit\"`). */\n $implicit: TwBreadcrumbsItem<T>;\n /** Same as `$implicit`. Available via `let-item=\"item\"` for readability. */\n item: TwBreadcrumbsItem<T>;\n /** Zero-based index of this item in the original `items` input. */\n index: number;\n /** `true` only for the last entry — the current page. */\n isCurrent: boolean;\n}\n\n// ── tv() config ──\n\nconst breadcrumbs = tv(\n {\n slots: {\n // The host outer element (<tw-breadcrumbs>) is a custom element; we wrap\n // the actual landmark <nav> inside the template. The list is a real <ol>\n // so the trail keeps its semantic ordering for assistive tech.\n list: 'flex flex-wrap items-center m-0 p-0 list-none min-w-0',\n item: 'inline-flex items-center min-w-0',\n link:\n 'inline-flex items-center text-fg-muted hover:text-fg transition-colors duration-200 motion-reduce:transition-none ' +\n 'focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-500 rounded-md no-underline truncate',\n current: 'inline-flex items-center text-fg font-medium truncate',\n disabled:\n 'inline-flex items-center text-fg-subtle opacity-50 cursor-not-allowed truncate',\n // Separator carries rtl:rotate-180 so the chevron flips for RTL layouts;\n // requires a `dir=\"rtl\"` ancestor for Tailwind's rtl: variant to match.\n separator:\n 'inline-flex items-center text-fg-subtle shrink-0 select-none rtl:rotate-180',\n overflowTrigger:\n 'inline-flex items-center justify-center rounded-md text-fg-muted hover:text-fg hover:bg-surface-muted ' +\n 'transition-colors duration-200 motion-reduce:transition-none ' +\n 'focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-500 cursor-pointer',\n },\n variants: {\n // A breadcrumb row is inline text navigation, so its box is a `min-h-*`\n // floor rather than a pinned height (docs/vertical-rhythm.md §2) — the\n // trail must stay free to wrap onto a second line in a narrow container.\n // The floor lands on the control scale (24 / 32 / 36 / 44 / 48) for two\n // reasons:\n // 1. The font ramp cannot separate all five steps on its own —\n // `text-lg` is forbidden by CLAUDE.md, so `sm`/`md` both resolve to\n // `text-sm` and `lg`/`xl` both to `text-base`. Without the floor,\n // two of the five size steps are visually dead.\n // 2. `overflowTrigger` is a square button (24 / 28 / 32 / 36 / 36) that\n // is taller than the bare text line box at every size, so a trail\n // that collapses would jump in height the moment the ellipsis\n // appears. With the floor at or above the trigger square at every\n // size, the row height no longer depends on whether it renders.\n // The `lg`/`xl` trigger saturation at `size-9` is the codified CLAUDE.md\n // note — deliberately left alone.\n size: {\n xs: {\n list: 'gap-1',\n item: 'min-h-6',\n link: 'text-xs',\n current: 'text-xs',\n disabled: 'text-xs',\n separator: 'min-h-6 text-xs',\n overflowTrigger: 'size-6 text-xs',\n },\n sm: {\n list: 'gap-1',\n item: 'min-h-8',\n link: 'text-sm',\n current: 'text-sm',\n disabled: 'text-sm',\n separator: 'min-h-8 text-sm',\n overflowTrigger: 'size-7 text-sm',\n },\n md: {\n list: 'gap-1.5',\n item: 'min-h-9',\n link: 'text-sm',\n current: 'text-sm',\n disabled: 'text-sm',\n separator: 'min-h-9 text-sm',\n overflowTrigger: 'size-8 text-sm',\n },\n lg: {\n list: 'gap-2',\n item: 'min-h-11',\n link: 'text-base',\n current: 'text-base',\n disabled: 'text-base',\n separator: 'min-h-11 text-base',\n overflowTrigger: 'size-9 text-base',\n },\n xl: {\n list: 'gap-2',\n item: 'min-h-12',\n link: 'text-base',\n current: 'text-base',\n disabled: 'text-base',\n separator: 'min-h-12 text-base',\n overflowTrigger: 'size-9 text-base',\n },\n },\n },\n defaultVariants: { size: 'md' },\n },\n { twMerge: true },\n);\n\n// Separator glyph size mirrors the menu-item icon scale: keep the chevron one\n// step below the text-size step so it reads as a quiet hint, not a glyph.\nconst ICON_SIZE_FOR_BREADCRUMB: Record<TwSize, TwSize> = {\n xs: 'xs',\n sm: 'sm',\n md: 'sm',\n lg: 'md',\n xl: 'md',\n};\n\n// ── Template slot directives ──\n\n/**\n * Structural directive applied to an `<ng-template>` projected into\n * `tw-breadcrumbs`. Replaces the default per-item rendering so consumers can\n * wire their own anchors (typically `routerLink`-bound). The template receives\n * `$implicit` (the item), `item`, `index`, and `isCurrent` in its context.\n */\n@Directive({\n selector: '[twBreadcrumbsItem]',\n})\nexport class BreadcrumbsItemTemplateDirective<T = unknown> {\n /** @internal */\n readonly templateRef = inject<TemplateRef<TwBreadcrumbsItemContext<T>>>(TemplateRef);\n\n /** Type-narrows the template context for `let-` destructuring in templates. */\n static ngTemplateContextGuard<T>(\n _dir: BreadcrumbsItemTemplateDirective<T>,\n _ctx: unknown,\n ): _ctx is TwBreadcrumbsItemContext<T> {\n return true;\n }\n}\n\n/**\n * Structural directive applied to an `<ng-template>` projected into\n * `tw-breadcrumbs`. Replaces the default chevron separator with custom\n * content (any element — icon, text, dot, etc.).\n */\n@Directive({\n selector: '[twBreadcrumbsSeparator]',\n})\nexport class BreadcrumbsSeparatorTemplateDirective {\n /** @internal */\n readonly templateRef = inject<TemplateRef<unknown>>(TemplateRef);\n}\n\n// ── Link styling directive ──\n\n/**\n * Applies the parent `tw-breadcrumbs` link / current / disabled styling to a\n * consumer-projected anchor (or span) inside `*twBreadcrumbsItem`. Without\n * this directive, projected content renders unstyled — only the layout `<li>`\n * is provided by the component.\n *\n * The directive sets `aria-current=\"page\"` when `current` is true. It does\n * NOT itself swap the element tag; the consumer should still pick `<a>` for\n * link items and `<span>` for the current item.\n */\n@Directive({\n selector: '[twBreadcrumbsLink]',\n host: {\n '[class]': 'classes()',\n '[attr.aria-current]': 'current() ? \"page\" : null',\n },\n})\nexport class BreadcrumbsLinkDirective {\n /** When true, this element is the current page: styled bold and gets `aria-current=\"page\"`. Defaults to `false`. */\n readonly current = input(false);\n\n /** When true, applies disabled styling (muted, not-allowed cursor). Defaults to `false`. */\n readonly disabled = input(false);\n\n private readonly parent = inject<BreadcrumbsComponent<unknown>>(\n forwardRef(() => BreadcrumbsComponent),\n );\n\n /** @internal Combined classes pulled from the parent component's slot tokens. */\n readonly classes = computed(() => {\n const slots = this.parent.slotResult();\n if (this.disabled()) return slots.disabled();\n if (this.current()) return slots.current();\n return slots.link();\n });\n}\n\n// ── Rendered entry types (internal) ──\n\ninterface RenderedItem<T> {\n readonly kind: 'item';\n readonly item: TwBreadcrumbsItem<T>;\n readonly index: number;\n readonly isCurrent: boolean;\n readonly key: string;\n}\n\ninterface RenderedSeparator {\n readonly kind: 'separator';\n readonly key: string;\n}\n\ninterface RenderedOverflow {\n readonly kind: 'overflow';\n readonly key: string;\n}\n\ntype RenderedEntry<T> = RenderedItem<T> | RenderedSeparator | RenderedOverflow;\n\n// ── BreadcrumbsComponent ──\n\n/**\n * Breadcrumb trail rendered as a `<nav>` landmark with an ordered list of\n * navigation hops. The last entry in `items` is the current page (gets\n * `aria-current=\"page\"`, never an anchor). Items beyond `maxItems` are\n * collapsed behind an ellipsis trigger that opens an overflow menu.\n *\n * Customize per-item rendering with `*twBreadcrumbsItem` (consumer projects\n * router-aware anchors), and the separator with `*twBreadcrumbsSeparator` or\n * the `separator` input (Lucide icon name).\n *\n * @example\n * ```html\n * <tw-breadcrumbs [items]=\"trail\" />\n *\n * <tw-breadcrumbs [items]=\"trail\" [maxItems]=\"3\" separator=\"slash\">\n * <ng-template twBreadcrumbsItem let-item let-isCurrent=\"isCurrent\">\n * @if (isCurrent) {\n * <span twBreadcrumbsLink [current]=\"true\">{{ item.label }}</span>\n * } @else {\n * <a twBreadcrumbsLink [routerLink]=\"item.data?.routerLink\">{{ item.label }}</a>\n * }\n * </ng-template>\n * </tw-breadcrumbs>\n * ```\n */\n@Component({\n selector: 'tw-breadcrumbs',\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [\n NgTemplateOutlet,\n IconComponent,\n MenuComponent,\n MenuItemDirective,\n MenuTriggerDirective,\n ],\n host: {\n // Custom elements default to inline; force a block layout so the trail\n // behaves predictably inside flex / grid containers.\n class: 'block min-w-0',\n },\n template: `\n <nav [attr.aria-label]=\"ariaLabel()\">\n <ol [class]=\"listClasses()\">\n @for (entry of renderedEntries(); track entry.key) {\n @if (entry.kind === 'item') {\n <li [class]=\"itemClasses()\">\n @if (itemTemplate(); as tpl) {\n <ng-container\n *ngTemplateOutlet=\"\n tpl.templateRef;\n context: {\n $implicit: entry.item,\n item: entry.item,\n index: entry.index,\n isCurrent: entry.isCurrent\n }\n \"\n />\n } @else {\n @if (entry.isCurrent) {\n <span [class]=\"currentClasses()\" aria-current=\"page\">{{ entry.item.label }}</span>\n } @else if (entry.item.disabled) {\n <span [class]=\"disabledClasses()\" aria-disabled=\"true\">{{ entry.item.label }}</span>\n } @else if (entry.item.href) {\n <a [class]=\"linkClasses()\" [attr.href]=\"entry.item.href\">{{ entry.item.label }}</a>\n } @else {\n <span [class]=\"linkClasses()\">{{ entry.item.label }}</span>\n }\n }\n </li>\n } @else if (entry.kind === 'separator') {\n <li [class]=\"separatorClasses()\" aria-hidden=\"true\">\n @if (separatorTemplate(); as sepTpl) {\n <ng-container *ngTemplateOutlet=\"sepTpl.templateRef\" />\n } @else {\n <tw-icon [name]=\"separator()\" [size]=\"iconSize()\" />\n }\n </li>\n } @else {\n <li [class]=\"itemClasses()\">\n <!--\n The overflow menu pulls in tw-menu + @angular/cdk/menu (overlay,\n focus-trap). @defer keeps that weight in a lazy chunk so a plain\n breadcrumb trail (the common case, maxItems=0) never bundles it;\n the chunk loads when the trigger scrolls into view and is\n prefetched on idle to keep the first click responsive. The\n @placeholder mirrors the trigger so layout and a11y are stable.\n -->\n @defer (on viewport; prefetch on idle) {\n <button\n type=\"button\"\n [class]=\"overflowTriggerClasses()\"\n [twMenuTrigger]=\"overflowMenuTpl\"\n aria-label=\"Show more breadcrumbs\"\n >\n <span aria-hidden=\"true\">…</span>\n </button>\n <ng-template #overflowMenuTpl>\n <tw-menu aria-label=\"Hidden breadcrumb items\">\n @for (hidden of collapsedItems(); track hidden) {\n @if (hidden.disabled || !hidden.href) {\n <span twMenuItem [attr.aria-disabled]=\"hidden.disabled ? 'true' : null\">\n {{ hidden.label }}\n </span>\n } @else {\n <a twMenuItem [attr.href]=\"hidden.href\">{{ hidden.label }}</a>\n }\n }\n </tw-menu>\n </ng-template>\n } @placeholder {\n <button\n type=\"button\"\n [class]=\"overflowTriggerClasses()\"\n aria-label=\"Show more breadcrumbs\"\n >\n <span aria-hidden=\"true\">…</span>\n </button>\n }\n </li>\n }\n }\n </ol>\n </nav>\n `,\n})\nexport class BreadcrumbsComponent<T = unknown> {\n /** The full breadcrumb trail. The last entry is treated as the current page. Defaults to `[]`. */\n readonly items = input<readonly TwBreadcrumbsItem<T>[]>([]);\n\n /** Density of the trail — drives font size, gap, icon size, and the overflow-button square. Defaults to `'md'`. */\n readonly size = input<TwSize>('md');\n\n /**\n * When greater than `0`, the trail collapses any middle items past this\n * threshold behind an overflow menu. The first item and the current (last)\n * item are always visible. Values `< 2` are clamped to `2`. Defaults to `0`\n * (no collapsing).\n */\n readonly maxItems = input<number>(0);\n\n /**\n * Lucide icon name used for the default separator between items. Ignored\n * when a `*twBreadcrumbsSeparator` template is projected. Defaults to\n * `'chevron-right'`. Requires the consumer to register the icon via\n * `provideTwLucideIcons({ ChevronRight, ... })`.\n */\n readonly separator = input<string>('chevron-right');\n\n /** Accessible label applied to the `<nav>` landmark. Defaults to `'Breadcrumb'`. Aliased as `aria-label`. */\n readonly ariaLabel = input<string>('Breadcrumb', { alias: 'aria-label' });\n\n /** Custom per-item template projected via `*twBreadcrumbsItem`. */\n readonly itemTemplate = contentChild(BreadcrumbsItemTemplateDirective<T>);\n\n /** Custom separator template projected via `*twBreadcrumbsSeparator`. */\n readonly separatorTemplate = contentChild(BreadcrumbsSeparatorTemplateDirective);\n\n /** @internal Materialised tv() slot result; exposed so `BreadcrumbsLinkDirective` can pull link/current/disabled classes. */\n readonly slotResult = computed(() => breadcrumbs({ size: this.size() }));\n\n /** @internal Icon size for the default chevron separator. */\n readonly iconSize = computed(() => ICON_SIZE_FOR_BREADCRUMB[this.size()]);\n\n /** @internal Class strings per slot, memoised. */\n readonly listClasses = computed(() => this.slotResult().list());\n readonly itemClasses = computed(() => this.slotResult().item());\n readonly linkClasses = computed(() => this.slotResult().link());\n readonly currentClasses = computed(() => this.slotResult().current());\n readonly disabledClasses = computed(() => this.slotResult().disabled());\n readonly separatorClasses = computed(() => this.slotResult().separator());\n readonly overflowTriggerClasses = computed(() => this.slotResult().overflowTrigger());\n\n /** @internal Items hidden behind the overflow menu. Empty when no collapsing is active. */\n readonly collapsedItems = computed<readonly TwBreadcrumbsItem<T>[]>(() => {\n const all = this.items();\n const max = this.maxItems();\n if (max <= 0 || all.length <= 2 || all.length <= max) return [];\n const effectiveMax = Math.max(max, 2);\n if (all.length <= effectiveMax) return [];\n const tailCount = effectiveMax - 1;\n const hiddenEnd = all.length - tailCount;\n return all.slice(1, hiddenEnd);\n });\n\n /** @internal Flat sequence of items + separators + overflow used by the template's @for. */\n readonly renderedEntries = computed<RenderedEntry<T>[]>(() => {\n const all = this.items();\n const max = this.maxItems();\n if (all.length === 0) return [];\n\n const entries: RenderedEntry<T>[] = [];\n const lastIndex = all.length - 1;\n const pushSeparator = (key: string) =>\n entries.push({ kind: 'separator', key: `sep-${key}` });\n\n const collapsing = max > 0 && all.length > 2 && all.length > max;\n const effectiveMax = Math.max(max, 2);\n const shouldCollapse = collapsing && all.length > effectiveMax;\n\n if (!shouldCollapse) {\n for (let i = 0; i < all.length; i++) {\n if (i > 0) pushSeparator(`${i}`);\n entries.push({\n kind: 'item',\n item: all[i],\n index: i,\n isCurrent: i === lastIndex,\n key: `item-${i}`,\n });\n }\n return entries;\n }\n\n const tailCount = effectiveMax - 1;\n const tailStart = all.length - tailCount;\n\n // First item.\n entries.push({\n kind: 'item',\n item: all[0],\n index: 0,\n isCurrent: lastIndex === 0,\n key: 'item-0',\n });\n pushSeparator('after-first');\n\n // Overflow trigger.\n entries.push({ kind: 'overflow', key: 'overflow' });\n pushSeparator('after-overflow');\n\n // Tail items.\n for (let i = tailStart; i < all.length; i++) {\n if (i > tailStart) pushSeparator(`${i}`);\n entries.push({\n kind: 'item',\n item: all[i],\n index: i,\n isCurrent: i === lastIndex,\n key: `item-${i}`,\n });\n }\n\n return entries;\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
@@ -23,12 +23,17 @@ const buttonVariants = tv({
|
|
|
23
23
|
warning: '',
|
|
24
24
|
error: '',
|
|
25
25
|
},
|
|
26
|
+
// Pinned control heights — see `docs/vertical-rhythm.md`. Vertical padding
|
|
27
|
+
// is deliberately absent: the height is declared, not derived. The
|
|
28
|
+
// `outline` variant's 1px border sits *inside* the pinned box under
|
|
29
|
+
// Preflight's global `box-sizing: border-box`, so outline and solid now
|
|
30
|
+
// measure identically at every size (they used to differ by 2px).
|
|
26
31
|
size: {
|
|
27
|
-
xs: 'px-2
|
|
28
|
-
sm: 'px-3
|
|
29
|
-
md: 'px-4
|
|
30
|
-
lg: 'px-5
|
|
31
|
-
xl: 'px-6
|
|
32
|
+
xs: 'px-2 text-xs h-6',
|
|
33
|
+
sm: 'px-3 text-sm h-8',
|
|
34
|
+
md: 'px-4 text-sm h-9',
|
|
35
|
+
lg: 'px-5 text-base h-11',
|
|
36
|
+
xl: 'px-6 text-base h-12',
|
|
32
37
|
},
|
|
33
38
|
disabled: {
|
|
34
39
|
true: 'opacity-50 pointer-events-none',
|
|
@@ -92,12 +97,18 @@ const buttonVariants = tv({
|
|
|
92
97
|
{ variant: 'ghost', color: 'error', class: 'text-error-700 hover:bg-error-50' },
|
|
93
98
|
{ variant: 'soft', color: 'error', class: 'bg-error-50 text-error-800 hover:bg-error-100' },
|
|
94
99
|
{ variant: 'link', color: 'error', class: 'text-error-700' },
|
|
95
|
-
// ===== Link variant: strip padding =====
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
100
|
+
// ===== Link variant: strip padding, and opt out of the pinned height =====
|
|
101
|
+
// `link` has no box — no border, no fill — so it is not part of the
|
|
102
|
+
// form-row cohort in `docs/vertical-rhythm.md` and must stay text-shaped:
|
|
103
|
+
// pinning it to `h-9` would blow out the line box of any paragraph it sits
|
|
104
|
+
// in. `h-auto` collapses the size variant's `h-*` via twMerge (same group,
|
|
105
|
+
// compoundVariants apply last), leaving the height equal to the line box —
|
|
106
|
+
// exactly what `py-0` produced before this migration.
|
|
107
|
+
{ variant: 'link', size: 'xs', class: 'px-0 h-auto' },
|
|
108
|
+
{ variant: 'link', size: 'sm', class: 'px-0 h-auto' },
|
|
109
|
+
{ variant: 'link', size: 'md', class: 'px-0 h-auto' },
|
|
110
|
+
{ variant: 'link', size: 'lg', class: 'px-0 h-auto' },
|
|
111
|
+
{ variant: 'link', size: 'xl', class: 'px-0 h-auto' },
|
|
101
112
|
],
|
|
102
113
|
defaultVariants: {
|
|
103
114
|
variant: 'solid',
|
|
@@ -172,6 +183,7 @@ class ButtonDirective {
|
|
|
172
183
|
this.focusMonitor.stopMonitoring(this.elementRef);
|
|
173
184
|
});
|
|
174
185
|
}
|
|
186
|
+
/** @internal Swallows clicks while the button is disabled or loading, so neither navigation nor consumer handlers run. */
|
|
175
187
|
handleClick(event) {
|
|
176
188
|
if (this.disabled() || this.loading()) {
|
|
177
189
|
event.preventDefault();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cdevhub-ngx-tw-button.mjs","sources":["../../../projects/ngx-tw/button/button.ts","../../../projects/ngx-tw/button/cdevhub-ngx-tw-button.ts"],"sourcesContent":["import {\n computed,\n DestroyRef,\n Directive,\n ElementRef,\n inject,\n input,\n type OnInit,\n} from '@angular/core';\nimport { FocusMonitor } from '@angular/cdk/a11y';\nimport { tv } from 'tailwind-variants';\nimport type { TwColor, TwSize } from '@cdevhub/ngx-tw/core';\n\n/** Visual style of the button. */\nexport type ButtonVariant = 'solid' | 'outline' | 'ghost' | 'soft' | 'link';\n\nconst buttonVariants = tv({\n base: 'inline-flex items-center justify-center gap-1.5 rounded-md font-medium transition-colors duration-normal motion-reduce:transition-none cursor-pointer focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-500',\n variants: {\n variant: {\n solid: '',\n outline: 'border',\n ghost: '',\n soft: '',\n link: 'underline-offset-4 hover:underline',\n },\n color: {\n primary: '',\n secondary: '',\n accent: '',\n neutral: '',\n info: '',\n success: '',\n warning: '',\n error: '',\n },\n size: {\n xs: 'px-2 py-1 text-xs',\n sm: 'px-3 py-1.5 text-sm',\n md: 'px-4 py-2 text-sm',\n lg: 'px-5 py-2.5 text-base',\n xl: 'px-6 py-3 text-base',\n },\n disabled: {\n true: 'opacity-50 pointer-events-none',\n false: '',\n },\n loading: {\n true: 'pointer-events-none',\n false: '',\n },\n },\n compoundVariants: [\n // Solid variants use the semantic `${color}-solid` token pair so the\n // theme layer owns the AA-contrast bg/fg pairing for each color. Raw\n // `${color}-600` direct usage was violating CLAUDE.md and producing\n // sub-AA contrast for info / success in light mode.\n\n // ===== Primary =====\n { variant: 'solid', color: 'primary', class: 'bg-primary-solid text-on-primary hover:bg-primary-solid-hover' },\n { variant: 'outline', color: 'primary', class: 'border-primary-300 text-primary-700 hover:bg-primary-50' },\n { variant: 'ghost', color: 'primary', class: 'text-primary-700 hover:bg-primary-50' },\n { variant: 'soft', color: 'primary', class: 'bg-primary-50 text-primary-800 hover:bg-primary-100' },\n { variant: 'link', color: 'primary', class: 'text-primary-700' },\n\n // ===== Secondary =====\n { variant: 'solid', color: 'secondary', class: 'bg-secondary-solid text-on-secondary hover:bg-secondary-solid-hover' },\n { variant: 'outline', color: 'secondary', class: 'border-secondary-300 text-secondary-700 hover:bg-secondary-50' },\n { variant: 'ghost', color: 'secondary', class: 'text-secondary-700 hover:bg-secondary-50' },\n { variant: 'soft', color: 'secondary', class: 'bg-secondary-50 text-secondary-800 hover:bg-secondary-100' },\n { variant: 'link', color: 'secondary', class: 'text-secondary-700' },\n\n // ===== Accent =====\n { variant: 'solid', color: 'accent', class: 'bg-accent-solid text-on-accent hover:bg-accent-solid-hover' },\n { variant: 'outline', color: 'accent', class: 'border-accent-300 text-accent-700 hover:bg-accent-50' },\n { variant: 'ghost', color: 'accent', class: 'text-accent-700 hover:bg-accent-50' },\n { variant: 'soft', color: 'accent', class: 'bg-accent-50 text-accent-800 hover:bg-accent-100' },\n { variant: 'link', color: 'accent', class: 'text-accent-700' },\n\n // ===== Neutral ===== (subdued solid pairs surface-muted with text-fg; on-neutral is white and would be unreadable here)\n { variant: 'solid', color: 'neutral', class: 'bg-surface-muted text-fg hover:bg-surface-sunken' },\n { variant: 'outline', color: 'neutral', class: 'border-border text-fg hover:bg-surface-muted' },\n { variant: 'ghost', color: 'neutral', class: 'text-fg-muted hover:bg-surface-muted' },\n { variant: 'soft', color: 'neutral', class: 'bg-surface-muted text-fg hover:bg-surface-sunken' },\n { variant: 'link', color: 'neutral', class: 'text-fg-muted' },\n\n // ===== Info =====\n { variant: 'solid', color: 'info', class: 'bg-info-solid text-on-info hover:bg-info-solid-hover' },\n { variant: 'outline', color: 'info', class: 'border-info-300 text-info-700 hover:bg-info-50' },\n { variant: 'ghost', color: 'info', class: 'text-info-700 hover:bg-info-50' },\n { variant: 'soft', color: 'info', class: 'bg-info-50 text-info-800 hover:bg-info-100' },\n { variant: 'link', color: 'info', class: 'text-info-700' },\n\n // ===== Success =====\n { variant: 'solid', color: 'success', class: 'bg-success-solid text-on-success hover:bg-success-solid-hover' },\n { variant: 'outline', color: 'success', class: 'border-success-300 text-success-700 hover:bg-success-50' },\n { variant: 'ghost', color: 'success', class: 'text-success-700 hover:bg-success-50' },\n { variant: 'soft', color: 'success', class: 'bg-success-50 text-success-800 hover:bg-success-100' },\n { variant: 'link', color: 'success', class: 'text-success-700' },\n\n // ===== Warning =====\n { variant: 'solid', color: 'warning', class: 'bg-warning-solid text-on-warning hover:bg-warning-solid-hover' },\n { variant: 'outline', color: 'warning', class: 'border-warning-300 text-warning-700 hover:bg-warning-50' },\n { variant: 'ghost', color: 'warning', class: 'text-warning-700 hover:bg-warning-50' },\n { variant: 'soft', color: 'warning', class: 'bg-warning-50 text-warning-800 hover:bg-warning-100' },\n { variant: 'link', color: 'warning', class: 'text-warning-700' },\n\n // ===== Error =====\n { variant: 'solid', color: 'error', class: 'bg-error-solid text-on-error hover:bg-error-solid-hover' },\n { variant: 'outline', color: 'error', class: 'border-error-300 text-error-700 hover:bg-error-50' },\n { variant: 'ghost', color: 'error', class: 'text-error-700 hover:bg-error-50' },\n { variant: 'soft', color: 'error', class: 'bg-error-50 text-error-800 hover:bg-error-100' },\n { variant: 'link', color: 'error', class: 'text-error-700' },\n\n // ===== Link variant: strip padding =====\n { variant: 'link', size: 'xs', class: 'px-0 py-0' },\n { variant: 'link', size: 'sm', class: 'px-0 py-0' },\n { variant: 'link', size: 'md', class: 'px-0 py-0' },\n { variant: 'link', size: 'lg', class: 'px-0 py-0' },\n { variant: 'link', size: 'xl', class: 'px-0 py-0' },\n ],\n defaultVariants: {\n variant: 'solid',\n color: 'primary',\n size: 'md',\n disabled: false,\n loading: false,\n },\n}, {\n twMerge: true,\n});\n\n/**\n * Enhances a native `<button>` or `<a>` element with library styling, semantic\n * color/size variants, and disabled/loading states. The directive does not\n * expose a `clicked` output — bind `(click)` directly on the host element. When\n * `disabled` or `loading` is true, the directive intercepts clicks with\n * `preventDefault()` + `stopImmediatePropagation()` so the host handler does\n * not run.\n *\n * The directive owns no template, so visual loading affordances (spinner,\n * status text) are composed by the consumer — see the `loading` input.\n */\n@Directive({\n selector: '[twButton]',\n exportAs: 'twButton',\n host: {\n '[class]': 'classes()',\n '[attr.aria-disabled]': 'disabled() || loading() || null',\n '[attr.aria-busy]': 'loading() || null',\n '[attr.disabled]': 'isNativeButton() && (disabled() || loading()) ? true : null',\n '[attr.tabindex]': '!isNativeButton() && (disabled() || loading()) ? -1 : null',\n '(click)': 'handleClick($event)',\n },\n})\nexport class ButtonDirective implements OnInit {\n /** Controls the visual style. Defaults to `'solid'`. */\n readonly variant = input<ButtonVariant>('solid');\n\n /** Sets the semantic color palette. Defaults to `'primary'`. */\n readonly color = input<TwColor>('primary');\n\n /** Controls the size (padding, font size, icon size). Defaults to `'md'`. */\n readonly size = input<TwSize>('md');\n\n /** When true, prevents interaction and applies muted styling. Defaults to `false`. */\n readonly disabled = input(false);\n\n /**\n * When true, blocks clicks and sets `aria-busy=\"true\"`. Defaults to `false`.\n * The directive does not render a spinner or status text — compose them in\n * the projected content:\n *\n * ```html\n * <button twButton [loading]=\"saving()\">\n * @if (saving()) {\n * <tw-spinner twButtonIcon size=\"sm\" />\n * <span class=\"sr-only\">Saving…</span>\n * }\n * Save\n * </button>\n * ```\n *\n * The `sr-only` text gives assistive tech an explicit status announcement\n * to pair with the `aria-busy` attribute.\n */\n readonly loading = input(false);\n\n private readonly elementRef = inject(ElementRef<HTMLElement>);\n private readonly focusMonitor = inject(FocusMonitor);\n private readonly destroyRef = inject(DestroyRef);\n\n readonly isNativeButton = computed(\n () => this.elementRef.nativeElement.tagName === 'BUTTON',\n );\n\n readonly classes = computed(() =>\n buttonVariants({\n variant: this.variant(),\n color: this.color(),\n size: this.size(),\n disabled: this.disabled() || this.loading(),\n loading: this.loading(),\n }),\n );\n\n ngOnInit(): void {\n this.focusMonitor.monitor(this.elementRef);\n this.destroyRef.onDestroy(() => {\n this.focusMonitor.stopMonitoring(this.elementRef);\n });\n }\n\n handleClick(event: MouseEvent): void {\n if (this.disabled() || this.loading()) {\n event.preventDefault();\n event.stopImmediatePropagation();\n }\n }\n}\n\n@Directive({\n selector: '[twButtonIcon]',\n host: {\n '[class]': 'classes()',\n },\n})\nexport class ButtonIconDirective {\n /**\n * Position of the icon relative to the button label. Defaults to `'leading'`.\n *\n * The empty-string member of the union is load-bearing: Angular binds `''` to\n * the input when the selector is used as a bare attribute (`<svg twButtonIcon>`),\n * which is the canonical \"leading\" usage. Templates that omit a value still\n * have the directive attached and resolve to leading placement at runtime via\n * the `=== 'trailing'` check below.\n */\n readonly twButtonIcon = input<'' | 'leading' | 'trailing'>('leading');\n\n private readonly button = inject(ButtonDirective);\n\n readonly classes = computed(() => {\n const size = this.button.size();\n const iconSize = size === 'xs' || size === 'sm' ? 'size-4' : 'size-5';\n // `order-last` only takes effect when the button host is a flex container —\n // `ButtonDirective`'s base class includes `inline-flex`, which is the\n // contract this directive relies on for trailing-icon placement.\n const order = this.twButtonIcon() === 'trailing' ? 'order-last' : '';\n return [iconSize, 'shrink-0', order].filter(Boolean).join(' ');\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cdevhub-ngx-tw-button.mjs","sources":["../../../projects/ngx-tw/button/button.ts","../../../projects/ngx-tw/button/cdevhub-ngx-tw-button.ts"],"sourcesContent":["import {\n computed,\n DestroyRef,\n Directive,\n ElementRef,\n inject,\n input,\n type OnInit,\n} from '@angular/core';\nimport { FocusMonitor } from '@angular/cdk/a11y';\nimport { tv } from 'tailwind-variants';\nimport type { TwColor, TwSize } from '@cdevhub/ngx-tw/core';\n\n/** Visual style of the button. */\nexport type ButtonVariant = 'solid' | 'outline' | 'ghost' | 'soft' | 'link';\n\nconst buttonVariants = tv({\n base: 'inline-flex items-center justify-center gap-1.5 rounded-md font-medium transition-colors duration-normal motion-reduce:transition-none cursor-pointer focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-500',\n variants: {\n variant: {\n solid: '',\n outline: 'border',\n ghost: '',\n soft: '',\n link: 'underline-offset-4 hover:underline',\n },\n color: {\n primary: '',\n secondary: '',\n accent: '',\n neutral: '',\n info: '',\n success: '',\n warning: '',\n error: '',\n },\n // Pinned control heights — see `docs/vertical-rhythm.md`. Vertical padding\n // is deliberately absent: the height is declared, not derived. The\n // `outline` variant's 1px border sits *inside* the pinned box under\n // Preflight's global `box-sizing: border-box`, so outline and solid now\n // measure identically at every size (they used to differ by 2px).\n size: {\n xs: 'px-2 text-xs h-6',\n sm: 'px-3 text-sm h-8',\n md: 'px-4 text-sm h-9',\n lg: 'px-5 text-base h-11',\n xl: 'px-6 text-base h-12',\n },\n disabled: {\n true: 'opacity-50 pointer-events-none',\n false: '',\n },\n loading: {\n true: 'pointer-events-none',\n false: '',\n },\n },\n compoundVariants: [\n // Solid variants use the semantic `${color}-solid` token pair so the\n // theme layer owns the AA-contrast bg/fg pairing for each color. Raw\n // `${color}-600` direct usage was violating CLAUDE.md and producing\n // sub-AA contrast for info / success in light mode.\n\n // ===== Primary =====\n { variant: 'solid', color: 'primary', class: 'bg-primary-solid text-on-primary hover:bg-primary-solid-hover' },\n { variant: 'outline', color: 'primary', class: 'border-primary-300 text-primary-700 hover:bg-primary-50' },\n { variant: 'ghost', color: 'primary', class: 'text-primary-700 hover:bg-primary-50' },\n { variant: 'soft', color: 'primary', class: 'bg-primary-50 text-primary-800 hover:bg-primary-100' },\n { variant: 'link', color: 'primary', class: 'text-primary-700' },\n\n // ===== Secondary =====\n { variant: 'solid', color: 'secondary', class: 'bg-secondary-solid text-on-secondary hover:bg-secondary-solid-hover' },\n { variant: 'outline', color: 'secondary', class: 'border-secondary-300 text-secondary-700 hover:bg-secondary-50' },\n { variant: 'ghost', color: 'secondary', class: 'text-secondary-700 hover:bg-secondary-50' },\n { variant: 'soft', color: 'secondary', class: 'bg-secondary-50 text-secondary-800 hover:bg-secondary-100' },\n { variant: 'link', color: 'secondary', class: 'text-secondary-700' },\n\n // ===== Accent =====\n { variant: 'solid', color: 'accent', class: 'bg-accent-solid text-on-accent hover:bg-accent-solid-hover' },\n { variant: 'outline', color: 'accent', class: 'border-accent-300 text-accent-700 hover:bg-accent-50' },\n { variant: 'ghost', color: 'accent', class: 'text-accent-700 hover:bg-accent-50' },\n { variant: 'soft', color: 'accent', class: 'bg-accent-50 text-accent-800 hover:bg-accent-100' },\n { variant: 'link', color: 'accent', class: 'text-accent-700' },\n\n // ===== Neutral ===== (subdued solid pairs surface-muted with text-fg; on-neutral is white and would be unreadable here)\n { variant: 'solid', color: 'neutral', class: 'bg-surface-muted text-fg hover:bg-surface-sunken' },\n { variant: 'outline', color: 'neutral', class: 'border-border text-fg hover:bg-surface-muted' },\n { variant: 'ghost', color: 'neutral', class: 'text-fg-muted hover:bg-surface-muted' },\n { variant: 'soft', color: 'neutral', class: 'bg-surface-muted text-fg hover:bg-surface-sunken' },\n { variant: 'link', color: 'neutral', class: 'text-fg-muted' },\n\n // ===== Info =====\n { variant: 'solid', color: 'info', class: 'bg-info-solid text-on-info hover:bg-info-solid-hover' },\n { variant: 'outline', color: 'info', class: 'border-info-300 text-info-700 hover:bg-info-50' },\n { variant: 'ghost', color: 'info', class: 'text-info-700 hover:bg-info-50' },\n { variant: 'soft', color: 'info', class: 'bg-info-50 text-info-800 hover:bg-info-100' },\n { variant: 'link', color: 'info', class: 'text-info-700' },\n\n // ===== Success =====\n { variant: 'solid', color: 'success', class: 'bg-success-solid text-on-success hover:bg-success-solid-hover' },\n { variant: 'outline', color: 'success', class: 'border-success-300 text-success-700 hover:bg-success-50' },\n { variant: 'ghost', color: 'success', class: 'text-success-700 hover:bg-success-50' },\n { variant: 'soft', color: 'success', class: 'bg-success-50 text-success-800 hover:bg-success-100' },\n { variant: 'link', color: 'success', class: 'text-success-700' },\n\n // ===== Warning =====\n { variant: 'solid', color: 'warning', class: 'bg-warning-solid text-on-warning hover:bg-warning-solid-hover' },\n { variant: 'outline', color: 'warning', class: 'border-warning-300 text-warning-700 hover:bg-warning-50' },\n { variant: 'ghost', color: 'warning', class: 'text-warning-700 hover:bg-warning-50' },\n { variant: 'soft', color: 'warning', class: 'bg-warning-50 text-warning-800 hover:bg-warning-100' },\n { variant: 'link', color: 'warning', class: 'text-warning-700' },\n\n // ===== Error =====\n { variant: 'solid', color: 'error', class: 'bg-error-solid text-on-error hover:bg-error-solid-hover' },\n { variant: 'outline', color: 'error', class: 'border-error-300 text-error-700 hover:bg-error-50' },\n { variant: 'ghost', color: 'error', class: 'text-error-700 hover:bg-error-50' },\n { variant: 'soft', color: 'error', class: 'bg-error-50 text-error-800 hover:bg-error-100' },\n { variant: 'link', color: 'error', class: 'text-error-700' },\n\n // ===== Link variant: strip padding, and opt out of the pinned height =====\n // `link` has no box — no border, no fill — so it is not part of the\n // form-row cohort in `docs/vertical-rhythm.md` and must stay text-shaped:\n // pinning it to `h-9` would blow out the line box of any paragraph it sits\n // in. `h-auto` collapses the size variant's `h-*` via twMerge (same group,\n // compoundVariants apply last), leaving the height equal to the line box —\n // exactly what `py-0` produced before this migration.\n { variant: 'link', size: 'xs', class: 'px-0 h-auto' },\n { variant: 'link', size: 'sm', class: 'px-0 h-auto' },\n { variant: 'link', size: 'md', class: 'px-0 h-auto' },\n { variant: 'link', size: 'lg', class: 'px-0 h-auto' },\n { variant: 'link', size: 'xl', class: 'px-0 h-auto' },\n ],\n defaultVariants: {\n variant: 'solid',\n color: 'primary',\n size: 'md',\n disabled: false,\n loading: false,\n },\n}, {\n twMerge: true,\n});\n\n/**\n * Enhances a native `<button>` or `<a>` element with library styling, semantic\n * color/size variants, and disabled/loading states. The directive does not\n * expose a `clicked` output — bind `(click)` directly on the host element. When\n * `disabled` or `loading` is true, the directive intercepts clicks with\n * `preventDefault()` + `stopImmediatePropagation()` so the host handler does\n * not run.\n *\n * The directive owns no template, so visual loading affordances (spinner,\n * status text) are composed by the consumer — see the `loading` input.\n */\n@Directive({\n selector: '[twButton]',\n exportAs: 'twButton',\n host: {\n '[class]': 'classes()',\n '[attr.aria-disabled]': 'disabled() || loading() || null',\n '[attr.aria-busy]': 'loading() || null',\n '[attr.disabled]': 'isNativeButton() && (disabled() || loading()) ? true : null',\n '[attr.tabindex]': '!isNativeButton() && (disabled() || loading()) ? -1 : null',\n '(click)': 'handleClick($event)',\n },\n})\nexport class ButtonDirective implements OnInit {\n /** Controls the visual style. Defaults to `'solid'`. */\n readonly variant = input<ButtonVariant>('solid');\n\n /** Sets the semantic color palette. Defaults to `'primary'`. */\n readonly color = input<TwColor>('primary');\n\n /** Controls the size (padding, font size, icon size). Defaults to `'md'`. */\n readonly size = input<TwSize>('md');\n\n /** When true, prevents interaction and applies muted styling. Defaults to `false`. */\n readonly disabled = input(false);\n\n /**\n * When true, blocks clicks and sets `aria-busy=\"true\"`. Defaults to `false`.\n * The directive does not render a spinner or status text — compose them in\n * the projected content:\n *\n * ```html\n * <button twButton [loading]=\"saving()\">\n * @if (saving()) {\n * <tw-spinner twButtonIcon size=\"sm\" />\n * <span class=\"sr-only\">Saving…</span>\n * }\n * Save\n * </button>\n * ```\n *\n * The `sr-only` text gives assistive tech an explicit status announcement\n * to pair with the `aria-busy` attribute.\n */\n readonly loading = input(false);\n\n private readonly elementRef = inject(ElementRef<HTMLElement>);\n private readonly focusMonitor = inject(FocusMonitor);\n private readonly destroyRef = inject(DestroyRef);\n\n readonly isNativeButton = computed(\n () => this.elementRef.nativeElement.tagName === 'BUTTON',\n );\n\n readonly classes = computed(() =>\n buttonVariants({\n variant: this.variant(),\n color: this.color(),\n size: this.size(),\n disabled: this.disabled() || this.loading(),\n loading: this.loading(),\n }),\n );\n\n ngOnInit(): void {\n this.focusMonitor.monitor(this.elementRef);\n this.destroyRef.onDestroy(() => {\n this.focusMonitor.stopMonitoring(this.elementRef);\n });\n }\n\n /** @internal Swallows clicks while the button is disabled or loading, so neither navigation nor consumer handlers run. */\n handleClick(event: MouseEvent): void {\n if (this.disabled() || this.loading()) {\n event.preventDefault();\n event.stopImmediatePropagation();\n }\n }\n}\n\n@Directive({\n selector: '[twButtonIcon]',\n host: {\n '[class]': 'classes()',\n },\n})\nexport class ButtonIconDirective {\n /**\n * Position of the icon relative to the button label. Defaults to `'leading'`.\n *\n * The empty-string member of the union is load-bearing: Angular binds `''` to\n * the input when the selector is used as a bare attribute (`<svg twButtonIcon>`),\n * which is the canonical \"leading\" usage. Templates that omit a value still\n * have the directive attached and resolve to leading placement at runtime via\n * the `=== 'trailing'` check below.\n */\n readonly twButtonIcon = input<'' | 'leading' | 'trailing'>('leading');\n\n private readonly button = inject(ButtonDirective);\n\n readonly classes = computed(() => {\n const size = this.button.size();\n const iconSize = size === 'xs' || size === 'sm' ? 'size-4' : 'size-5';\n // `order-last` only takes effect when the button host is a flex container —\n // `ButtonDirective`'s base class includes `inline-flex`, which is the\n // contract this directive relies on for trailing-icon placement.\n const order = this.twButtonIcon() === 'trailing' ? 'order-last' : '';\n return [iconSize, 'shrink-0', order].filter(Boolean).join(' ');\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
2
|
import { inject, Injectable, makeEnvironmentProviders } from '@angular/core';
|
|
3
3
|
import { Settings, DateTime, Info } from 'luxon';
|
|
4
|
-
import { DateAdapter,
|
|
4
|
+
import { DateAdapter, TW_TZ_OVERRIDE, TW_DATE_ADAPTER } from '@cdevhub/ngx-tw/calendar';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
* Luxon-backed `DateAdapter`. Honors the calendar's `
|
|
7
|
+
* Luxon-backed `DateAdapter`. Honors the calendar's `TW_TZ_OVERRIDE` injection
|
|
8
8
|
* token so `today()` resolves against the consumer-configured timezone, and
|
|
9
9
|
* implements the §20.2 TZ-aware virtuals (`getTimezone`, `withTimezone`,
|
|
10
10
|
* `isDST`, `resolveAmbiguous`).
|
|
@@ -20,7 +20,7 @@ class LuxonDateAdapter extends DateAdapter {
|
|
|
20
20
|
* the constructor stays SSR-safe — Luxon's default zone reads from the host
|
|
21
21
|
* environment which we want to defer until first use.
|
|
22
22
|
*/
|
|
23
|
-
_tzOverride = inject(
|
|
23
|
+
_tzOverride = inject(TW_TZ_OVERRIDE, { optional: true });
|
|
24
24
|
/**
|
|
25
25
|
* Locale is resolved lazily on first read so the constructor stays
|
|
26
26
|
* SSR-safe (no `navigator` access at class-construction time — §31.1).
|
|
@@ -37,7 +37,7 @@ class LuxonDateAdapter extends DateAdapter {
|
|
|
37
37
|
}
|
|
38
38
|
/**
|
|
39
39
|
* Returns the configured timezone for new dates. Precedence: per-instance
|
|
40
|
-
* `
|
|
40
|
+
* `TW_TZ_OVERRIDE` → Luxon's `Settings.defaultZone` → `'local'`.
|
|
41
41
|
*/
|
|
42
42
|
get zone() {
|
|
43
43
|
if (this._tzOverride)
|
|
@@ -405,7 +405,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
|
|
|
405
405
|
function provideLuxonDateAdapter() {
|
|
406
406
|
return makeEnvironmentProviders([
|
|
407
407
|
LuxonDateAdapter,
|
|
408
|
-
{ provide:
|
|
408
|
+
{ provide: TW_DATE_ADAPTER, useExisting: LuxonDateAdapter },
|
|
409
409
|
]);
|
|
410
410
|
}
|
|
411
411
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cdevhub-ngx-tw-calendar-luxon.mjs","sources":["../../../projects/ngx-tw/calendar/luxon/luxon-date-adapter.ts","../../../projects/ngx-tw/calendar/luxon/provide-luxon-date-adapter.ts","../../../projects/ngx-tw/calendar/luxon/cdevhub-ngx-tw-calendar-luxon.ts"],"sourcesContent":["import { Injectable, inject } from '@angular/core';\nimport { DateTime, Info, Settings } from 'luxon';\nimport { DateAdapter, TZ_OVERRIDE, type TwDateNameStyle } from '@cdevhub/ngx-tw/calendar';\n\n/**\n * Display-format descriptor used by `LuxonDateAdapter.format()` and\n * `LuxonDateAdapter.parse()`.\n *\n * Two shapes are supported:\n * - `{ format: 'yyyy-LL-dd' }` — a Luxon `toFormat`/`fromFormat` token string.\n * - `{ preset: DateTime.DATE_FULL }` — a Luxon preset (an `Intl.DateTimeFormatOptions` bag).\n *\n * If both are provided, `format` (the explicit token) wins.\n */\nexport interface TwLuxonDateFormat {\n /** Luxon token string consumed by `DateTime#toFormat` / `DateTime.fromFormat`. */\n readonly format?: string;\n /** Luxon preset (e.g. `DateTime.DATE_FULL`) consumed by `DateTime#toLocaleString`. */\n readonly preset?: Intl.DateTimeFormatOptions;\n}\n\n/**\n * Luxon-backed `DateAdapter`. Honors the calendar's `TZ_OVERRIDE` injection\n * token so `today()` resolves against the consumer-configured timezone, and\n * implements the §20.2 TZ-aware virtuals (`getTimezone`, `withTimezone`,\n * `isDST`, `resolveAmbiguous`).\n *\n * `create()` accepts **1-based** months (matching the abstract contract and\n * Luxon's `DateTime.fromObject({ month })` convention), while `getMonth()`\n * returns **0-based** months (also per the abstract contract — see\n * `NativeDateAdapter`).\n */\n@Injectable()\nexport class LuxonDateAdapter extends DateAdapter<DateTime> {\n /**\n * Optional per-instance timezone override (§4.3, §7.4). Resolved lazily so\n * the constructor stays SSR-safe — Luxon's default zone reads from the host\n * environment which we want to defer until first use.\n */\n private readonly _tzOverride = inject(TZ_OVERRIDE, { optional: true });\n\n /**\n * Locale is resolved lazily on first read so the constructor stays\n * SSR-safe (no `navigator` access at class-construction time — §31.1).\n */\n private _locale: string | null = null;\n\n private get locale(): string {\n if (this._locale !== null) return this._locale;\n this._locale =\n typeof navigator !== 'undefined' && typeof navigator.language === 'string'\n ? navigator.language\n : Settings.defaultLocale ?? 'en-US';\n return this._locale;\n }\n\n /**\n * Returns the configured timezone for new dates. Precedence: per-instance\n * `TZ_OVERRIDE` → Luxon's `Settings.defaultZone` → `'local'`.\n */\n private get zone(): string {\n if (this._tzOverride) return this._tzOverride;\n const def = Settings.defaultZone;\n // `Settings.defaultZone` is a `Zone` instance — fall back to its name.\n return def && typeof def !== 'string' ? def.name : 'local';\n }\n\n /** Builds a `DateTime` in the adapter's configured zone + locale. */\n private inZone(dt: DateTime): DateTime {\n return dt.setZone(this.zone).setLocale(this.locale);\n }\n\n today(): DateTime {\n return DateTime.now().setZone(this.zone).setLocale(this.locale).startOf('day');\n }\n\n /**\n * Constructs a `DateTime`. **`month` is 1-based** — `create(2026, 1, 1)`\n * yields January 1, 2026 at 00:00 in the adapter's configured zone.\n */\n create(year: number, month: number, day: number): DateTime {\n return DateTime.fromObject(\n { year, month, day },\n { zone: this.zone, locale: this.locale },\n );\n }\n\n clone(date: DateTime): DateTime {\n // `DateTime` is immutable; reconstructing preserves zone/locale and\n // matches the defensive-copy semantics of the abstract contract.\n return DateTime.fromMillis(date.toMillis(), {\n zone: date.zone,\n locale: date.locale ?? this.locale,\n });\n }\n\n setLocale(locale: string): void {\n this._locale = locale;\n }\n\n getLocale(): string {\n return this.locale;\n }\n\n getYear(date: DateTime): number {\n return date.year;\n }\n\n /**\n * Returns the **0-based** month (0 = January, 11 = December) to match the\n * abstract `DateAdapter` contract and the `NativeDateAdapter`. Luxon's\n * `DateTime.month` is 1-based, so we subtract one.\n */\n getMonth(date: DateTime): number {\n return date.month - 1;\n }\n\n getDate(date: DateTime): number {\n return date.day;\n }\n\n /**\n * Day of week mapped to the Sunday-based scheme used by the abstract\n * contract: `0 = Sunday`, `6 = Saturday`. Luxon's `weekday` is\n * `1 = Monday … 7 = Sunday`, so Sunday gets re-anchored to `0`.\n */\n getDayOfWeek(date: DateTime): number {\n return date.weekday % 7;\n }\n\n getNumDaysInMonth(date: DateTime): number {\n return date.daysInMonth ?? 0;\n }\n\n getHours(date: DateTime): number {\n return date.hour;\n }\n\n getMinutes(date: DateTime): number {\n return date.minute;\n }\n\n getSeconds(date: DateTime): number {\n return date.second;\n }\n\n withTime(date: DateTime, hours: number, minutes: number, seconds: number): DateTime {\n return date.set({ hour: hours, minute: minutes, second: seconds, millisecond: 0 });\n }\n\n /**\n * Locale-aware first day of week (§19.2). Mirrors `NativeDateAdapter` —\n * derives from `Intl.Locale.getWeekInfo()` when available, falling back to\n * Monday (`1`) when the locale is unknown to ICU. ICU/CLDR returns\n * `1=Monday … 7=Sunday`; we normalize to the project's `0=Sunday … 6=Saturday`\n * convention so both adapters report identical values for identical locales.\n */\n getFirstDayOfWeek(): number {\n if (typeof Intl !== 'undefined' && typeof Intl.Locale === 'function') {\n try {\n const l = new Intl.Locale(this.locale) as Intl.Locale & {\n getWeekInfo?: () => { firstDay: number };\n weekInfo?: { firstDay: number };\n };\n const info = (l.getWeekInfo?.() ?? l.weekInfo) as { firstDay: number } | undefined;\n if (info && typeof info.firstDay === 'number') {\n return info.firstDay === 7 ? 0 : info.firstDay;\n }\n } catch {\n return 1;\n }\n }\n return 1;\n }\n\n getMonthNames(style: TwDateNameStyle): string[] {\n return Info.months(style, { locale: this.locale });\n }\n\n /**\n * Day-of-week names starting at Sunday. Luxon's `Info.weekdays` starts at\n * Monday, so the array is rotated to put Sunday first to match the abstract\n * contract (and the native adapter).\n */\n getDayOfWeekNames(style: TwDateNameStyle): string[] {\n const monStart = Info.weekdays(style, { locale: this.locale });\n // `[Mon, Tue, Wed, Thu, Fri, Sat, Sun]` → `[Sun, Mon, Tue, Wed, Thu, Fri, Sat]`.\n return [monStart[6], ...monStart.slice(0, 6)];\n }\n\n override getDateNames(_style: TwDateNameStyle = 'short'): string[] {\n // Use a 31-day reference month to localize numerals where needed.\n const ref = DateTime.fromObject(\n { year: 2017, month: 1, day: 1 },\n { zone: 'utc', locale: this.locale },\n );\n return Array.from({ length: 31 }, (_, i) => ref.plus({ days: i }).toFormat('d'));\n }\n\n getYearName(date: DateTime): string {\n return date.toFormat('yyyy', { locale: this.locale });\n }\n\n /**\n * Formats a date for display.\n *\n * Accepts either a Luxon token string (e.g. `'yyyy-LL-dd'`), a\n * {@link TwLuxonDateFormat} descriptor, or a Luxon preset bag\n * (`Intl.DateTimeFormatOptions`). When no format is supplied the\n * adapter falls back to a locale-aware medium date.\n */\n format(date: DateTime, displayFormat: unknown): string {\n if (!this.isValid(date)) return '';\n const localized = date.setLocale(this.locale);\n if (typeof displayFormat === 'string') {\n return localized.toFormat(displayFormat);\n }\n if (displayFormat && typeof displayFormat === 'object') {\n const desc = displayFormat as TwLuxonDateFormat;\n if (typeof desc.format === 'string') {\n return localized.toFormat(desc.format);\n }\n if (desc.preset) {\n return localized.toLocaleString(desc.preset);\n }\n // Treat a bare option-bag as a preset.\n return localized.toLocaleString(displayFormat as Intl.DateTimeFormatOptions);\n }\n return localized.toLocaleString(DateTime.DATE_MED);\n }\n\n addYears(date: DateTime, years: number): DateTime {\n return date.plus({ years });\n }\n\n addMonths(date: DateTime, months: number): DateTime {\n return date.plus({ months });\n }\n\n addDays(date: DateTime, days: number): DateTime {\n return date.plus({ days });\n }\n\n override addHours(date: DateTime, hours: number): DateTime {\n return date.plus({ hours });\n }\n\n addMinutes(date: DateTime, minutes: number): DateTime {\n return date.plus({ minutes });\n }\n\n compare(first: DateTime, second: DateTime): number {\n // Compare by calendar fields (year/month/day) so two `DateTime` instances\n // representing the same wall-clock date in different zones still compare\n // equal — matches the native adapter's behaviour.\n return (\n first.year - second.year ||\n first.month - second.month ||\n first.day - second.day\n );\n }\n\n sameDate(\n first: DateTime | null | undefined,\n second: DateTime | null | undefined,\n ): boolean {\n if (first && second) {\n const f = this.isValid(first);\n const s = this.isValid(second);\n if (f && s) return this.compare(first, second) === 0;\n return f === s;\n }\n return first === second;\n }\n\n /**\n * Start-of-day normalization in the adapter's zone. Luxon's `startOf('day')`\n * is DST-aware — on spring-forward days it returns the wall-clock instant the\n * zone considers the first valid moment of the day.\n */\n startOfDay(date: DateTime): DateTime {\n return date.setZone(this.zone).startOf('day');\n }\n\n isValid(date: DateTime): boolean {\n return date instanceof DateTime && date.isValid;\n }\n\n /** Returns Luxon's invalid sentinel (an invalid `DateTime`). */\n invalid(): DateTime {\n return DateTime.invalid('invalid');\n }\n\n /**\n * Parses a value into a `DateTime`. Accepts:\n * - `DateTime` (cloned),\n * - JS `Date` (re-anchored to the adapter's zone + locale),\n * - number (epoch milliseconds),\n * - string — when `parseFormat` is a Luxon token, uses `fromFormat`;\n * otherwise parses strict ISO 8601 only.\n *\n * The free-form-string path is intentionally narrow: SQL and RFC 2822\n * fallbacks were dropped so the Luxon adapter's acceptance surface matches\n * `NativeDateAdapter.parse()` (which only accepts strings parseable by\n * `new Date(value)`). Callers that need bespoke string formats should pass\n * an explicit `parseFormat` token.\n *\n * Returns `null` on empty input or unrecognised value types, mirroring the\n * native adapter's contract.\n */\n parse(value: unknown, parseFormat?: unknown): DateTime | null {\n if (value == null) return null;\n if (value instanceof DateTime) {\n return value.isValid ? this.clone(value) : null;\n }\n if (value instanceof Date) {\n const dt = DateTime.fromJSDate(value, { zone: this.zone }).setLocale(this.locale);\n return dt.isValid ? dt : null;\n }\n if (typeof value === 'number') {\n const dt = DateTime.fromMillis(value, { zone: this.zone, locale: this.locale });\n return dt.isValid ? dt : null;\n }\n if (typeof value === 'string') {\n if (!value.trim()) return null;\n const opts = { zone: this.zone, locale: this.locale };\n if (typeof parseFormat === 'string') {\n const dt = DateTime.fromFormat(value, parseFormat, opts);\n return dt.isValid ? dt : null;\n }\n if (parseFormat && typeof parseFormat === 'object') {\n const desc = parseFormat as TwLuxonDateFormat;\n if (typeof desc.format === 'string') {\n const dt = DateTime.fromFormat(value, desc.format, opts);\n return dt.isValid ? dt : null;\n }\n }\n const iso = DateTime.fromISO(value, opts);\n return iso.isValid ? iso : null;\n }\n return null;\n }\n\n /**\n * Coerce an incoming form value. Returns `null` for empty input, an invalid\n * `DateTime` sentinel for malformed input (so validators can flag it), and a\n * cloned valid `DateTime` otherwise.\n */\n deserialize(value: unknown): DateTime | null {\n if (value == null || value === '') return null;\n if (value instanceof DateTime) {\n return value.isValid ? this.clone(value) : this.invalid();\n }\n const parsed = this.parse(value);\n if (parsed && parsed.isValid) return parsed;\n return this.invalid();\n }\n\n /**\n * ISO 8601 date serialization (`YYYY-MM-DD`). Calendar dates have no\n * time component — `toIso` returns just the date portion.\n */\n toIso(date: DateTime): string {\n return date.toFormat('yyyy-LL-dd');\n }\n\n /**\n * Parses an ISO-8601 string to a `DateTime`. Overrides the base\n * implementation to use Luxon's optimised `fromISO` parser.\n */\n override fromIso(iso: string): DateTime | null {\n if (typeof iso !== 'string' || !iso.trim()) return null;\n const dt = DateTime.fromISO(iso, { zone: this.zone, locale: this.locale });\n return dt.isValid ? dt : null;\n }\n\n // ---------------------------------------------------------------------------\n // TZ-aware virtuals (§20.2). Luxon's `DateTime` carries zone information, so\n // these are real implementations rather than the floating no-ops in the base\n // class.\n // ---------------------------------------------------------------------------\n\n /** Returns the adapter's configured IANA zone name (e.g. `'America/New_York'`). */\n override getTimezone(): string {\n return this.zone;\n }\n\n /** Returns a copy of `date` reinterpreted in `tz`. */\n override withTimezone(date: DateTime, tz: string): DateTime {\n return date.setZone(tz);\n }\n\n /** Whether `date` is currently observing daylight-saving time in its zone. */\n override isDST(date: DateTime): boolean {\n return date.isInDST;\n }\n\n /**\n * Resolves an ambiguous wall-clock time during DST fall-back. On fall-back\n * days a single wall-clock hour repeats — e.g. 2024-11-03 01:30 in\n * `America/New_York` happens once at offset -04:00 (EDT) and again at\n * offset -05:00 (EST). Luxon's `DateTime.fromObject({...}, { zone })` always\n * returns the **earlier** of the two candidates, so we reconstruct the\n * \"later\" occurrence explicitly: rebuild the candidate at the date's\n * wall-clock fields (anchoring it on the earlier occurrence) and add one\n * hour to land on the same wall-clock's second occurrence in the\n * post-fall-back offset.\n *\n * Non-ambiguous wall-clocks return unchanged for both `prefer` values:\n * after `plus({ hour: 1 })` the wall-clock hour increments (no DST\n * repetition), so the candidate is rejected and we return `date` as-is.\n */\n override resolveAmbiguous(date: DateTime, prefer: 'earlier' | 'later'): DateTime {\n if (!date.isValid) return date;\n // Re-anchor on the earlier of the two candidates by reconstructing the\n // wall-clock from `date`'s fields in its current zone. Luxon's\n // `fromObject` deterministically picks the earlier instant when the\n // input wall-clock is ambiguous, regardless of which occurrence `date`\n // happens to represent.\n const zone = date.zoneName ?? this.zone;\n const earlier = DateTime.fromObject(\n {\n year: date.year,\n month: date.month,\n day: date.day,\n hour: date.hour,\n minute: date.minute,\n second: date.second,\n millisecond: date.millisecond,\n },\n { zone, locale: date.locale ?? this.locale },\n );\n if (!earlier.isValid) return date;\n if (prefer === 'earlier') return earlier;\n // Advancing one hour from the earlier-occurrence instant lands on the\n // same wall-clock at the post-fall-back offset on ambiguous hours, and\n // increments the wall-clock by one on non-ambiguous hours. Compare\n // wall-clock fields to distinguish the two cases.\n const later = earlier.plus({ hours: 1 });\n if (\n later.hour === earlier.hour &&\n later.day === earlier.day &&\n later.month === earlier.month &&\n later.year === earlier.year\n ) {\n return later;\n }\n return earlier;\n }\n}\n","import { type EnvironmentProviders, makeEnvironmentProviders } from '@angular/core';\nimport { DATE_ADAPTER } from '@cdevhub/ngx-tw/calendar';\nimport { LuxonDateAdapter } from './luxon-date-adapter';\n\n/**\n * Provides {@link LuxonDateAdapter} as the calendar's `DateAdapter`.\n *\n * Wire at app or route level so the adapter stays constant for the\n * component's lifetime (§11.7 of the calendar spec):\n *\n * ```ts\n * import { provideLuxonDateAdapter } from '@cdevhub/ngx-tw/calendar/luxon';\n *\n * bootstrapApplication(AppComponent, {\n * providers: [provideLuxonDateAdapter()],\n * });\n * ```\n */\nexport function provideLuxonDateAdapter(): EnvironmentProviders {\n return makeEnvironmentProviders([\n LuxonDateAdapter,\n { provide: DATE_ADAPTER, useExisting: LuxonDateAdapter },\n ]);\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cdevhub-ngx-tw-calendar-luxon.mjs","sources":["../../../projects/ngx-tw/calendar/luxon/luxon-date-adapter.ts","../../../projects/ngx-tw/calendar/luxon/provide-luxon-date-adapter.ts","../../../projects/ngx-tw/calendar/luxon/cdevhub-ngx-tw-calendar-luxon.ts"],"sourcesContent":["import { Injectable, inject } from '@angular/core';\nimport { DateTime, Info, Settings } from 'luxon';\nimport { DateAdapter, TW_TZ_OVERRIDE, type TwDateNameStyle } from '@cdevhub/ngx-tw/calendar';\n\n/**\n * Display-format descriptor used by `LuxonDateAdapter.format()` and\n * `LuxonDateAdapter.parse()`.\n *\n * Two shapes are supported:\n * - `{ format: 'yyyy-LL-dd' }` — a Luxon `toFormat`/`fromFormat` token string.\n * - `{ preset: DateTime.DATE_FULL }` — a Luxon preset (an `Intl.DateTimeFormatOptions` bag).\n *\n * If both are provided, `format` (the explicit token) wins.\n */\nexport interface TwLuxonDateFormat {\n /** Luxon token string consumed by `DateTime#toFormat` / `DateTime.fromFormat`. */\n readonly format?: string;\n /** Luxon preset (e.g. `DateTime.DATE_FULL`) consumed by `DateTime#toLocaleString`. */\n readonly preset?: Intl.DateTimeFormatOptions;\n}\n\n/**\n * Luxon-backed `DateAdapter`. Honors the calendar's `TW_TZ_OVERRIDE` injection\n * token so `today()` resolves against the consumer-configured timezone, and\n * implements the §20.2 TZ-aware virtuals (`getTimezone`, `withTimezone`,\n * `isDST`, `resolveAmbiguous`).\n *\n * `create()` accepts **1-based** months (matching the abstract contract and\n * Luxon's `DateTime.fromObject({ month })` convention), while `getMonth()`\n * returns **0-based** months (also per the abstract contract — see\n * `NativeDateAdapter`).\n */\n@Injectable()\nexport class LuxonDateAdapter extends DateAdapter<DateTime> {\n /**\n * Optional per-instance timezone override (§4.3, §7.4). Resolved lazily so\n * the constructor stays SSR-safe — Luxon's default zone reads from the host\n * environment which we want to defer until first use.\n */\n private readonly _tzOverride = inject(TW_TZ_OVERRIDE, { optional: true });\n\n /**\n * Locale is resolved lazily on first read so the constructor stays\n * SSR-safe (no `navigator` access at class-construction time — §31.1).\n */\n private _locale: string | null = null;\n\n private get locale(): string {\n if (this._locale !== null) return this._locale;\n this._locale =\n typeof navigator !== 'undefined' && typeof navigator.language === 'string'\n ? navigator.language\n : Settings.defaultLocale ?? 'en-US';\n return this._locale;\n }\n\n /**\n * Returns the configured timezone for new dates. Precedence: per-instance\n * `TW_TZ_OVERRIDE` → Luxon's `Settings.defaultZone` → `'local'`.\n */\n private get zone(): string {\n if (this._tzOverride) return this._tzOverride;\n const def = Settings.defaultZone;\n // `Settings.defaultZone` is a `Zone` instance — fall back to its name.\n return def && typeof def !== 'string' ? def.name : 'local';\n }\n\n /** Builds a `DateTime` in the adapter's configured zone + locale. */\n private inZone(dt: DateTime): DateTime {\n return dt.setZone(this.zone).setLocale(this.locale);\n }\n\n today(): DateTime {\n return DateTime.now().setZone(this.zone).setLocale(this.locale).startOf('day');\n }\n\n /**\n * Constructs a `DateTime`. **`month` is 1-based** — `create(2026, 1, 1)`\n * yields January 1, 2026 at 00:00 in the adapter's configured zone.\n */\n create(year: number, month: number, day: number): DateTime {\n return DateTime.fromObject(\n { year, month, day },\n { zone: this.zone, locale: this.locale },\n );\n }\n\n clone(date: DateTime): DateTime {\n // `DateTime` is immutable; reconstructing preserves zone/locale and\n // matches the defensive-copy semantics of the abstract contract.\n return DateTime.fromMillis(date.toMillis(), {\n zone: date.zone,\n locale: date.locale ?? this.locale,\n });\n }\n\n setLocale(locale: string): void {\n this._locale = locale;\n }\n\n getLocale(): string {\n return this.locale;\n }\n\n getYear(date: DateTime): number {\n return date.year;\n }\n\n /**\n * Returns the **0-based** month (0 = January, 11 = December) to match the\n * abstract `DateAdapter` contract and the `NativeDateAdapter`. Luxon's\n * `DateTime.month` is 1-based, so we subtract one.\n */\n getMonth(date: DateTime): number {\n return date.month - 1;\n }\n\n getDate(date: DateTime): number {\n return date.day;\n }\n\n /**\n * Day of week mapped to the Sunday-based scheme used by the abstract\n * contract: `0 = Sunday`, `6 = Saturday`. Luxon's `weekday` is\n * `1 = Monday … 7 = Sunday`, so Sunday gets re-anchored to `0`.\n */\n getDayOfWeek(date: DateTime): number {\n return date.weekday % 7;\n }\n\n getNumDaysInMonth(date: DateTime): number {\n return date.daysInMonth ?? 0;\n }\n\n getHours(date: DateTime): number {\n return date.hour;\n }\n\n getMinutes(date: DateTime): number {\n return date.minute;\n }\n\n getSeconds(date: DateTime): number {\n return date.second;\n }\n\n withTime(date: DateTime, hours: number, minutes: number, seconds: number): DateTime {\n return date.set({ hour: hours, minute: minutes, second: seconds, millisecond: 0 });\n }\n\n /**\n * Locale-aware first day of week (§19.2). Mirrors `NativeDateAdapter` —\n * derives from `Intl.Locale.getWeekInfo()` when available, falling back to\n * Monday (`1`) when the locale is unknown to ICU. ICU/CLDR returns\n * `1=Monday … 7=Sunday`; we normalize to the project's `0=Sunday … 6=Saturday`\n * convention so both adapters report identical values for identical locales.\n */\n getFirstDayOfWeek(): number {\n if (typeof Intl !== 'undefined' && typeof Intl.Locale === 'function') {\n try {\n const l = new Intl.Locale(this.locale) as Intl.Locale & {\n getWeekInfo?: () => { firstDay: number };\n weekInfo?: { firstDay: number };\n };\n const info = (l.getWeekInfo?.() ?? l.weekInfo) as { firstDay: number } | undefined;\n if (info && typeof info.firstDay === 'number') {\n return info.firstDay === 7 ? 0 : info.firstDay;\n }\n } catch {\n return 1;\n }\n }\n return 1;\n }\n\n getMonthNames(style: TwDateNameStyle): string[] {\n return Info.months(style, { locale: this.locale });\n }\n\n /**\n * Day-of-week names starting at Sunday. Luxon's `Info.weekdays` starts at\n * Monday, so the array is rotated to put Sunday first to match the abstract\n * contract (and the native adapter).\n */\n getDayOfWeekNames(style: TwDateNameStyle): string[] {\n const monStart = Info.weekdays(style, { locale: this.locale });\n // `[Mon, Tue, Wed, Thu, Fri, Sat, Sun]` → `[Sun, Mon, Tue, Wed, Thu, Fri, Sat]`.\n return [monStart[6], ...monStart.slice(0, 6)];\n }\n\n override getDateNames(_style: TwDateNameStyle = 'short'): string[] {\n // Use a 31-day reference month to localize numerals where needed.\n const ref = DateTime.fromObject(\n { year: 2017, month: 1, day: 1 },\n { zone: 'utc', locale: this.locale },\n );\n return Array.from({ length: 31 }, (_, i) => ref.plus({ days: i }).toFormat('d'));\n }\n\n getYearName(date: DateTime): string {\n return date.toFormat('yyyy', { locale: this.locale });\n }\n\n /**\n * Formats a date for display.\n *\n * Accepts either a Luxon token string (e.g. `'yyyy-LL-dd'`), a\n * {@link TwLuxonDateFormat} descriptor, or a Luxon preset bag\n * (`Intl.DateTimeFormatOptions`). When no format is supplied the\n * adapter falls back to a locale-aware medium date.\n */\n format(date: DateTime, displayFormat: unknown): string {\n if (!this.isValid(date)) return '';\n const localized = date.setLocale(this.locale);\n if (typeof displayFormat === 'string') {\n return localized.toFormat(displayFormat);\n }\n if (displayFormat && typeof displayFormat === 'object') {\n const desc = displayFormat as TwLuxonDateFormat;\n if (typeof desc.format === 'string') {\n return localized.toFormat(desc.format);\n }\n if (desc.preset) {\n return localized.toLocaleString(desc.preset);\n }\n // Treat a bare option-bag as a preset.\n return localized.toLocaleString(displayFormat as Intl.DateTimeFormatOptions);\n }\n return localized.toLocaleString(DateTime.DATE_MED);\n }\n\n addYears(date: DateTime, years: number): DateTime {\n return date.plus({ years });\n }\n\n addMonths(date: DateTime, months: number): DateTime {\n return date.plus({ months });\n }\n\n addDays(date: DateTime, days: number): DateTime {\n return date.plus({ days });\n }\n\n override addHours(date: DateTime, hours: number): DateTime {\n return date.plus({ hours });\n }\n\n addMinutes(date: DateTime, minutes: number): DateTime {\n return date.plus({ minutes });\n }\n\n compare(first: DateTime, second: DateTime): number {\n // Compare by calendar fields (year/month/day) so two `DateTime` instances\n // representing the same wall-clock date in different zones still compare\n // equal — matches the native adapter's behaviour.\n return (\n first.year - second.year ||\n first.month - second.month ||\n first.day - second.day\n );\n }\n\n sameDate(\n first: DateTime | null | undefined,\n second: DateTime | null | undefined,\n ): boolean {\n if (first && second) {\n const f = this.isValid(first);\n const s = this.isValid(second);\n if (f && s) return this.compare(first, second) === 0;\n return f === s;\n }\n return first === second;\n }\n\n /**\n * Start-of-day normalization in the adapter's zone. Luxon's `startOf('day')`\n * is DST-aware — on spring-forward days it returns the wall-clock instant the\n * zone considers the first valid moment of the day.\n */\n startOfDay(date: DateTime): DateTime {\n return date.setZone(this.zone).startOf('day');\n }\n\n isValid(date: DateTime): boolean {\n return date instanceof DateTime && date.isValid;\n }\n\n /** Returns Luxon's invalid sentinel (an invalid `DateTime`). */\n invalid(): DateTime {\n return DateTime.invalid('invalid');\n }\n\n /**\n * Parses a value into a `DateTime`. Accepts:\n * - `DateTime` (cloned),\n * - JS `Date` (re-anchored to the adapter's zone + locale),\n * - number (epoch milliseconds),\n * - string — when `parseFormat` is a Luxon token, uses `fromFormat`;\n * otherwise parses strict ISO 8601 only.\n *\n * The free-form-string path is intentionally narrow: SQL and RFC 2822\n * fallbacks were dropped so the Luxon adapter's acceptance surface matches\n * `NativeDateAdapter.parse()` (which only accepts strings parseable by\n * `new Date(value)`). Callers that need bespoke string formats should pass\n * an explicit `parseFormat` token.\n *\n * Returns `null` on empty input or unrecognised value types, mirroring the\n * native adapter's contract.\n */\n parse(value: unknown, parseFormat?: unknown): DateTime | null {\n if (value == null) return null;\n if (value instanceof DateTime) {\n return value.isValid ? this.clone(value) : null;\n }\n if (value instanceof Date) {\n const dt = DateTime.fromJSDate(value, { zone: this.zone }).setLocale(this.locale);\n return dt.isValid ? dt : null;\n }\n if (typeof value === 'number') {\n const dt = DateTime.fromMillis(value, { zone: this.zone, locale: this.locale });\n return dt.isValid ? dt : null;\n }\n if (typeof value === 'string') {\n if (!value.trim()) return null;\n const opts = { zone: this.zone, locale: this.locale };\n if (typeof parseFormat === 'string') {\n const dt = DateTime.fromFormat(value, parseFormat, opts);\n return dt.isValid ? dt : null;\n }\n if (parseFormat && typeof parseFormat === 'object') {\n const desc = parseFormat as TwLuxonDateFormat;\n if (typeof desc.format === 'string') {\n const dt = DateTime.fromFormat(value, desc.format, opts);\n return dt.isValid ? dt : null;\n }\n }\n const iso = DateTime.fromISO(value, opts);\n return iso.isValid ? iso : null;\n }\n return null;\n }\n\n /**\n * Coerce an incoming form value. Returns `null` for empty input, an invalid\n * `DateTime` sentinel for malformed input (so validators can flag it), and a\n * cloned valid `DateTime` otherwise.\n */\n deserialize(value: unknown): DateTime | null {\n if (value == null || value === '') return null;\n if (value instanceof DateTime) {\n return value.isValid ? this.clone(value) : this.invalid();\n }\n const parsed = this.parse(value);\n if (parsed && parsed.isValid) return parsed;\n return this.invalid();\n }\n\n /**\n * ISO 8601 date serialization (`YYYY-MM-DD`). Calendar dates have no\n * time component — `toIso` returns just the date portion.\n */\n toIso(date: DateTime): string {\n return date.toFormat('yyyy-LL-dd');\n }\n\n /**\n * Parses an ISO-8601 string to a `DateTime`. Overrides the base\n * implementation to use Luxon's optimised `fromISO` parser.\n */\n override fromIso(iso: string): DateTime | null {\n if (typeof iso !== 'string' || !iso.trim()) return null;\n const dt = DateTime.fromISO(iso, { zone: this.zone, locale: this.locale });\n return dt.isValid ? dt : null;\n }\n\n // ---------------------------------------------------------------------------\n // TZ-aware virtuals (§20.2). Luxon's `DateTime` carries zone information, so\n // these are real implementations rather than the floating no-ops in the base\n // class.\n // ---------------------------------------------------------------------------\n\n /** Returns the adapter's configured IANA zone name (e.g. `'America/New_York'`). */\n override getTimezone(): string {\n return this.zone;\n }\n\n /** Returns a copy of `date` reinterpreted in `tz`. */\n override withTimezone(date: DateTime, tz: string): DateTime {\n return date.setZone(tz);\n }\n\n /** Whether `date` is currently observing daylight-saving time in its zone. */\n override isDST(date: DateTime): boolean {\n return date.isInDST;\n }\n\n /**\n * Resolves an ambiguous wall-clock time during DST fall-back. On fall-back\n * days a single wall-clock hour repeats — e.g. 2024-11-03 01:30 in\n * `America/New_York` happens once at offset -04:00 (EDT) and again at\n * offset -05:00 (EST). Luxon's `DateTime.fromObject({...}, { zone })` always\n * returns the **earlier** of the two candidates, so we reconstruct the\n * \"later\" occurrence explicitly: rebuild the candidate at the date's\n * wall-clock fields (anchoring it on the earlier occurrence) and add one\n * hour to land on the same wall-clock's second occurrence in the\n * post-fall-back offset.\n *\n * Non-ambiguous wall-clocks return unchanged for both `prefer` values:\n * after `plus({ hour: 1 })` the wall-clock hour increments (no DST\n * repetition), so the candidate is rejected and we return `date` as-is.\n */\n override resolveAmbiguous(date: DateTime, prefer: 'earlier' | 'later'): DateTime {\n if (!date.isValid) return date;\n // Re-anchor on the earlier of the two candidates by reconstructing the\n // wall-clock from `date`'s fields in its current zone. Luxon's\n // `fromObject` deterministically picks the earlier instant when the\n // input wall-clock is ambiguous, regardless of which occurrence `date`\n // happens to represent.\n const zone = date.zoneName ?? this.zone;\n const earlier = DateTime.fromObject(\n {\n year: date.year,\n month: date.month,\n day: date.day,\n hour: date.hour,\n minute: date.minute,\n second: date.second,\n millisecond: date.millisecond,\n },\n { zone, locale: date.locale ?? this.locale },\n );\n if (!earlier.isValid) return date;\n if (prefer === 'earlier') return earlier;\n // Advancing one hour from the earlier-occurrence instant lands on the\n // same wall-clock at the post-fall-back offset on ambiguous hours, and\n // increments the wall-clock by one on non-ambiguous hours. Compare\n // wall-clock fields to distinguish the two cases.\n const later = earlier.plus({ hours: 1 });\n if (\n later.hour === earlier.hour &&\n later.day === earlier.day &&\n later.month === earlier.month &&\n later.year === earlier.year\n ) {\n return later;\n }\n return earlier;\n }\n}\n","import { type EnvironmentProviders, makeEnvironmentProviders } from '@angular/core';\nimport { TW_DATE_ADAPTER } from '@cdevhub/ngx-tw/calendar';\nimport { LuxonDateAdapter } from './luxon-date-adapter';\n\n/**\n * Provides {@link LuxonDateAdapter} as the calendar's `DateAdapter`.\n *\n * Wire at app or route level so the adapter stays constant for the\n * component's lifetime (§11.7 of the calendar spec):\n *\n * ```ts\n * import { provideLuxonDateAdapter } from '@cdevhub/ngx-tw/calendar/luxon';\n *\n * bootstrapApplication(AppComponent, {\n * providers: [provideLuxonDateAdapter()],\n * });\n * ```\n */\nexport function provideLuxonDateAdapter(): EnvironmentProviders {\n return makeEnvironmentProviders([\n LuxonDateAdapter,\n { provide: TW_DATE_ADAPTER, useExisting: LuxonDateAdapter },\n ]);\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;AAqBA;;;;;;;;;;AAUG;AAEG,MAAO,gBAAiB,SAAQ,WAAqB,CAAA;AACzD;;;;AAIG;IACc,WAAW,GAAG,MAAM,CAAC,cAAc,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;AAEzE;;;AAGG;IACK,OAAO,GAAkB,IAAI;AAErC,IAAA,IAAY,MAAM,GAAA;AAChB,QAAA,IAAI,IAAI,CAAC,OAAO,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC,OAAO;AAC9C,QAAA,IAAI,CAAC,OAAO;YACV,OAAO,SAAS,KAAK,WAAW,IAAI,OAAO,SAAS,CAAC,QAAQ,KAAK;kBAC9D,SAAS,CAAC;AACZ,kBAAE,QAAQ,CAAC,aAAa,IAAI,OAAO;QACvC,OAAO,IAAI,CAAC,OAAO;IACrB;AAEA;;;AAGG;AACH,IAAA,IAAY,IAAI,GAAA;QACd,IAAI,IAAI,CAAC,WAAW;YAAE,OAAO,IAAI,CAAC,WAAW;AAC7C,QAAA,MAAM,GAAG,GAAG,QAAQ,CAAC,WAAW;;AAEhC,QAAA,OAAO,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,GAAG,GAAG,CAAC,IAAI,GAAG,OAAO;IAC5D;;AAGQ,IAAA,MAAM,CAAC,EAAY,EAAA;AACzB,QAAA,OAAO,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC;IACrD;IAEA,KAAK,GAAA;QACH,OAAO,QAAQ,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC;IAChF;AAEA;;;AAGG;AACH,IAAA,MAAM,CAAC,IAAY,EAAE,KAAa,EAAE,GAAW,EAAA;QAC7C,OAAO,QAAQ,CAAC,UAAU,CACxB,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,EACpB,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CACzC;IACH;AAEA,IAAA,KAAK,CAAC,IAAc,EAAA;;;QAGlB,OAAO,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE;YAC1C,IAAI,EAAE,IAAI,CAAC,IAAI;AACf,YAAA,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM;AACnC,SAAA,CAAC;IACJ;AAEA,IAAA,SAAS,CAAC,MAAc,EAAA;AACtB,QAAA,IAAI,CAAC,OAAO,GAAG,MAAM;IACvB;IAEA,SAAS,GAAA;QACP,OAAO,IAAI,CAAC,MAAM;IACpB;AAEA,IAAA,OAAO,CAAC,IAAc,EAAA;QACpB,OAAO,IAAI,CAAC,IAAI;IAClB;AAEA;;;;AAIG;AACH,IAAA,QAAQ,CAAC,IAAc,EAAA;AACrB,QAAA,OAAO,IAAI,CAAC,KAAK,GAAG,CAAC;IACvB;AAEA,IAAA,OAAO,CAAC,IAAc,EAAA;QACpB,OAAO,IAAI,CAAC,GAAG;IACjB;AAEA;;;;AAIG;AACH,IAAA,YAAY,CAAC,IAAc,EAAA;AACzB,QAAA,OAAO,IAAI,CAAC,OAAO,GAAG,CAAC;IACzB;AAEA,IAAA,iBAAiB,CAAC,IAAc,EAAA;AAC9B,QAAA,OAAO,IAAI,CAAC,WAAW,IAAI,CAAC;IAC9B;AAEA,IAAA,QAAQ,CAAC,IAAc,EAAA;QACrB,OAAO,IAAI,CAAC,IAAI;IAClB;AAEA,IAAA,UAAU,CAAC,IAAc,EAAA;QACvB,OAAO,IAAI,CAAC,MAAM;IACpB;AAEA,IAAA,UAAU,CAAC,IAAc,EAAA;QACvB,OAAO,IAAI,CAAC,MAAM;IACpB;AAEA,IAAA,QAAQ,CAAC,IAAc,EAAE,KAAa,EAAE,OAAe,EAAE,OAAe,EAAA;QACtE,OAAO,IAAI,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC,EAAE,CAAC;IACpF;AAEA;;;;;;AAMG;IACH,iBAAiB,GAAA;AACf,QAAA,IAAI,OAAO,IAAI,KAAK,WAAW,IAAI,OAAO,IAAI,CAAC,MAAM,KAAK,UAAU,EAAE;AACpE,YAAA,IAAI;gBACF,MAAM,CAAC,GAAG,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAGpC;AACD,gBAAA,MAAM,IAAI,IAAI,CAAC,CAAC,WAAW,IAAI,IAAI,CAAC,CAAC,QAAQ,CAAqC;gBAClF,IAAI,IAAI,IAAI,OAAO,IAAI,CAAC,QAAQ,KAAK,QAAQ,EAAE;AAC7C,oBAAA,OAAO,IAAI,CAAC,QAAQ,KAAK,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,QAAQ;gBAChD;YACF;AAAE,YAAA,MAAM;AACN,gBAAA,OAAO,CAAC;YACV;QACF;AACA,QAAA,OAAO,CAAC;IACV;AAEA,IAAA,aAAa,CAAC,KAAsB,EAAA;AAClC,QAAA,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;IACpD;AAEA;;;;AAIG;AACH,IAAA,iBAAiB,CAAC,KAAsB,EAAA;AACtC,QAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;;AAE9D,QAAA,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC/C;IAES,YAAY,CAAC,SAA0B,OAAO,EAAA;;AAErD,QAAA,MAAM,GAAG,GAAG,QAAQ,CAAC,UAAU,CAC7B,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,EAChC,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CACrC;AACD,QAAA,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IAClF;AAEA,IAAA,WAAW,CAAC,IAAc,EAAA;AACxB,QAAA,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;IACvD;AAEA;;;;;;;AAOG;IACH,MAAM,CAAC,IAAc,EAAE,aAAsB,EAAA;AAC3C,QAAA,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC;AAAE,YAAA,OAAO,EAAE;QAClC,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC;AAC7C,QAAA,IAAI,OAAO,aAAa,KAAK,QAAQ,EAAE;AACrC,YAAA,OAAO,SAAS,CAAC,QAAQ,CAAC,aAAa,CAAC;QAC1C;AACA,QAAA,IAAI,aAAa,IAAI,OAAO,aAAa,KAAK,QAAQ,EAAE;YACtD,MAAM,IAAI,GAAG,aAAkC;AAC/C,YAAA,IAAI,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ,EAAE;gBACnC,OAAO,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC;YACxC;AACA,YAAA,IAAI,IAAI,CAAC,MAAM,EAAE;gBACf,OAAO,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC;YAC9C;;AAEA,YAAA,OAAO,SAAS,CAAC,cAAc,CAAC,aAA2C,CAAC;QAC9E;QACA,OAAO,SAAS,CAAC,cAAc,CAAC,QAAQ,CAAC,QAAQ,CAAC;IACpD;IAEA,QAAQ,CAAC,IAAc,EAAE,KAAa,EAAA;QACpC,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC;IAC7B;IAEA,SAAS,CAAC,IAAc,EAAE,MAAc,EAAA;QACtC,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAC9B;IAEA,OAAO,CAAC,IAAc,EAAE,IAAY,EAAA;QAClC,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC;IAC5B;IAES,QAAQ,CAAC,IAAc,EAAE,KAAa,EAAA;QAC7C,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC;IAC7B;IAEA,UAAU,CAAC,IAAc,EAAE,OAAe,EAAA;QACxC,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC;IAC/B;IAEA,OAAO,CAAC,KAAe,EAAE,MAAgB,EAAA;;;;AAIvC,QAAA,QACE,KAAK,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI;AACxB,YAAA,KAAK,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK;AAC1B,YAAA,KAAK,CAAC,GAAG,GAAG,MAAM,CAAC,GAAG;IAE1B;IAEA,QAAQ,CACN,KAAkC,EAClC,MAAmC,EAAA;AAEnC,QAAA,IAAI,KAAK,IAAI,MAAM,EAAE;YACnB,MAAM,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC;YAC7B,MAAM,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;YAC9B,IAAI,CAAC,IAAI,CAAC;gBAAE,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC;YACpD,OAAO,CAAC,KAAK,CAAC;QAChB;QACA,OAAO,KAAK,KAAK,MAAM;IACzB;AAEA;;;;AAIG;AACH,IAAA,UAAU,CAAC,IAAc,EAAA;AACvB,QAAA,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC;IAC/C;AAEA,IAAA,OAAO,CAAC,IAAc,EAAA;AACpB,QAAA,OAAO,IAAI,YAAY,QAAQ,IAAI,IAAI,CAAC,OAAO;IACjD;;IAGA,OAAO,GAAA;AACL,QAAA,OAAO,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC;IACpC;AAEA;;;;;;;;;;;;;;;;AAgBG;IACH,KAAK,CAAC,KAAc,EAAE,WAAqB,EAAA;QACzC,IAAI,KAAK,IAAI,IAAI;AAAE,YAAA,OAAO,IAAI;AAC9B,QAAA,IAAI,KAAK,YAAY,QAAQ,EAAE;AAC7B,YAAA,OAAO,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,IAAI;QACjD;AACA,QAAA,IAAI,KAAK,YAAY,IAAI,EAAE;YACzB,MAAM,EAAE,GAAG,QAAQ,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC;YACjF,OAAO,EAAE,CAAC,OAAO,GAAG,EAAE,GAAG,IAAI;QAC/B;AACA,QAAA,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE;YAC7B,MAAM,EAAE,GAAG,QAAQ,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;YAC/E,OAAO,EAAE,CAAC,OAAO,GAAG,EAAE,GAAG,IAAI;QAC/B;AACA,QAAA,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE;AAC7B,YAAA,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE;AAAE,gBAAA,OAAO,IAAI;AAC9B,YAAA,MAAM,IAAI,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE;AACrD,YAAA,IAAI,OAAO,WAAW,KAAK,QAAQ,EAAE;AACnC,gBAAA,MAAM,EAAE,GAAG,QAAQ,CAAC,UAAU,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,CAAC;gBACxD,OAAO,EAAE,CAAC,OAAO,GAAG,EAAE,GAAG,IAAI;YAC/B;AACA,YAAA,IAAI,WAAW,IAAI,OAAO,WAAW,KAAK,QAAQ,EAAE;gBAClD,MAAM,IAAI,GAAG,WAAgC;AAC7C,gBAAA,IAAI,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ,EAAE;AACnC,oBAAA,MAAM,EAAE,GAAG,QAAQ,CAAC,UAAU,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC;oBACxD,OAAO,EAAE,CAAC,OAAO,GAAG,EAAE,GAAG,IAAI;gBAC/B;YACF;YACA,MAAM,GAAG,GAAG,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC;YACzC,OAAO,GAAG,CAAC,OAAO,GAAG,GAAG,GAAG,IAAI;QACjC;AACA,QAAA,OAAO,IAAI;IACb;AAEA;;;;AAIG;AACH,IAAA,WAAW,CAAC,KAAc,EAAA;AACxB,QAAA,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,KAAK,EAAE;AAAE,YAAA,OAAO,IAAI;AAC9C,QAAA,IAAI,KAAK,YAAY,QAAQ,EAAE;AAC7B,YAAA,OAAO,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,OAAO,EAAE;QAC3D;QACA,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC;AAChC,QAAA,IAAI,MAAM,IAAI,MAAM,CAAC,OAAO;AAAE,YAAA,OAAO,MAAM;AAC3C,QAAA,OAAO,IAAI,CAAC,OAAO,EAAE;IACvB;AAEA;;;AAGG;AACH,IAAA,KAAK,CAAC,IAAc,EAAA;AAClB,QAAA,OAAO,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC;IACpC;AAEA;;;AAGG;AACM,IAAA,OAAO,CAAC,GAAW,EAAA;QAC1B,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE;AAAE,YAAA,OAAO,IAAI;QACvD,MAAM,EAAE,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;QAC1E,OAAO,EAAE,CAAC,OAAO,GAAG,EAAE,GAAG,IAAI;IAC/B;;;;;;;IASS,WAAW,GAAA;QAClB,OAAO,IAAI,CAAC,IAAI;IAClB;;IAGS,YAAY,CAAC,IAAc,EAAE,EAAU,EAAA;AAC9C,QAAA,OAAO,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;IACzB;;AAGS,IAAA,KAAK,CAAC,IAAc,EAAA;QAC3B,OAAO,IAAI,CAAC,OAAO;IACrB;AAEA;;;;;;;;;;;;;;AAcG;IACM,gBAAgB,CAAC,IAAc,EAAE,MAA2B,EAAA;QACnE,IAAI,CAAC,IAAI,CAAC,OAAO;AAAE,YAAA,OAAO,IAAI;;;;;;QAM9B,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,IAAI;AACvC,QAAA,MAAM,OAAO,GAAG,QAAQ,CAAC,UAAU,CACjC;YACE,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,GAAG,EAAE,IAAI,CAAC,GAAG;YACb,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,WAAW,EAAE,IAAI,CAAC,WAAW;AAC9B,SAAA,EACD,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,EAAE,CAC7C;QACD,IAAI,CAAC,OAAO,CAAC,OAAO;AAAE,YAAA,OAAO,IAAI;QACjC,IAAI,MAAM,KAAK,SAAS;AAAE,YAAA,OAAO,OAAO;;;;;AAKxC,QAAA,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;AACxC,QAAA,IACE,KAAK,CAAC,IAAI,KAAK,OAAO,CAAC,IAAI;AAC3B,YAAA,KAAK,CAAC,GAAG,KAAK,OAAO,CAAC,GAAG;AACzB,YAAA,KAAK,CAAC,KAAK,KAAK,OAAO,CAAC,KAAK;AAC7B,YAAA,KAAK,CAAC,IAAI,KAAK,OAAO,CAAC,IAAI,EAC3B;AACA,YAAA,OAAO,KAAK;QACd;AACA,QAAA,OAAO,OAAO;IAChB;uGA/ZW,gBAAgB,EAAA,IAAA,EAAA,IAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA;2GAAhB,gBAAgB,EAAA,CAAA;;2FAAhB,gBAAgB,EAAA,UAAA,EAAA,CAAA;kBAD5B;;;AC5BD;;;;;;;;;;;;;AAaG;SACa,uBAAuB,GAAA;AACrC,IAAA,OAAO,wBAAwB,CAAC;QAC9B,gBAAgB;AAChB,QAAA,EAAE,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,gBAAgB,EAAE;AAC5D,KAAA,CAAC;AACJ;;ACvBA;;AAEG;;;;"}
|
|
@@ -21,10 +21,10 @@ class CalendarCellHarness extends ComponentHarness {
|
|
|
21
21
|
const btn = await this.button();
|
|
22
22
|
return btn.getAttribute('aria-label');
|
|
23
23
|
}
|
|
24
|
-
/** Whether the cell carries `aria-selected="true"`. */
|
|
24
|
+
/** Whether the cell carries `aria-selected="true"`. Read from the `role="gridcell"` host, which is the element that carries selection state — a `<button>`'s implicit role does not support `aria-selected`. */
|
|
25
25
|
async isSelected() {
|
|
26
|
-
const
|
|
27
|
-
return (await
|
|
26
|
+
const host = await this.host();
|
|
27
|
+
return (await host.getAttribute('aria-selected')) === 'true';
|
|
28
28
|
}
|
|
29
29
|
/** Whether the cell is disabled — true if the button has `disabled` or carries `aria-disabled="true"`. */
|
|
30
30
|
async isDisabled() {
|