@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
|
@@ -121,7 +121,7 @@ const iconVariants = tv({
|
|
|
121
121
|
* ```html
|
|
122
122
|
* <tw-icon name="star" />
|
|
123
123
|
* <tw-icon name="check" color="success" size="lg" />
|
|
124
|
-
* <tw-icon name="alert-triangle" color="warning"
|
|
124
|
+
* <tw-icon name="alert-triangle" color="warning" aria-label="Warning" />
|
|
125
125
|
* <tw-icon name="star" [svg]="{ strokeWidth: 1.5, absoluteStrokeWidth: true }" />
|
|
126
126
|
* ```
|
|
127
127
|
*/
|
|
@@ -148,9 +148,8 @@ class IconComponent {
|
|
|
148
148
|
/** Icon size. Defaults to `'md'` (20px). */
|
|
149
149
|
size = input('md', /* @ts-ignore */
|
|
150
150
|
...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
151
|
-
/** Accessible label
|
|
152
|
-
ariaLabel = input(/*
|
|
153
|
-
...(ngDevMode ? [undefined, { debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
|
|
151
|
+
/** Accessible label, supplied as the plain `aria-label` attribute. When set, the generated SVG takes `role="img"` plus the label instead of `aria-hidden="true"`. Defaults to `undefined` (icon is decorative, `aria-hidden="true"`). */
|
|
152
|
+
ariaLabel = input(undefined, { ...(ngDevMode ? { debugName: "ariaLabel" } : /* istanbul ignore next */ {}), alias: 'aria-label' });
|
|
154
153
|
/**
|
|
155
154
|
* SVG-author configuration: `strokeWidth`, `absoluteStrokeWidth`, `viewBox`.
|
|
156
155
|
* Unset fields fall back to `{ strokeWidth: 2, absoluteStrokeWidth: false, viewBox: '0 0 24 24' }`.
|
|
@@ -266,7 +265,7 @@ class IconComponent {
|
|
|
266
265
|
this.prevAriaLabel = label;
|
|
267
266
|
}
|
|
268
267
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: IconComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
269
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.7", type: IconComponent, isStandalone: true, selector: "tw-icon", inputs: { name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, img: { classPropertyName: "img", publicName: "img", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "
|
|
268
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.7", type: IconComponent, isStandalone: true, selector: "tw-icon", inputs: { name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, img: { classPropertyName: "img", publicName: "img", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "aria-label", isSignal: true, isRequired: false, transformFunction: null }, svg: { classPropertyName: "svg", publicName: "svg", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "classes()", "attr.aria-label": "null" } }, ngImport: i0, template: '', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
270
269
|
}
|
|
271
270
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: IconComponent, decorators: [{
|
|
272
271
|
type: Component,
|
|
@@ -277,9 +276,20 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
|
|
|
277
276
|
encapsulation: ViewEncapsulation.None,
|
|
278
277
|
host: {
|
|
279
278
|
'[class]': 'classes()',
|
|
279
|
+
// The accessible name belongs on the generated `<svg>` (which takes
|
|
280
|
+
// `role="img"`), never on the role-less `<tw-icon>` host: ARIA 1.2
|
|
281
|
+
// prohibits `aria-label` on an element with no role, and axe flags it as a
|
|
282
|
+
// serious `aria-prohibited-attr` violation.
|
|
283
|
+
//
|
|
284
|
+
// This binding is load-bearing, not defensive. Angular DOES leave the
|
|
285
|
+
// literal `aria-label="…"` on the host after handing it to the aliased
|
|
286
|
+
// input, so without this the host keeps a prohibited attribute. Verified
|
|
287
|
+
// by deleting the line: the "plain aria-label attribute reachable" spec
|
|
288
|
+
// fails. Do not remove it as dead code.
|
|
289
|
+
'[attr.aria-label]': 'null',
|
|
280
290
|
},
|
|
281
291
|
}]
|
|
282
|
-
}], ctorParameters: () => [], propDecorators: { name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], img: [{ type: i0.Input, args: [{ isSignal: true, alias: "img", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "
|
|
292
|
+
}], ctorParameters: () => [], propDecorators: { name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], img: [{ type: i0.Input, args: [{ isSignal: true, alias: "img", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-label", required: false }] }], svg: [{ type: i0.Input, args: [{ isSignal: true, alias: "svg", required: false }] }] } });
|
|
283
293
|
|
|
284
294
|
/**
|
|
285
295
|
* Generated bundle index. Do not edit.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cdevhub-ngx-tw-icon.mjs","sources":["../../../projects/ngx-tw/icon/icon.registry.ts","../../../projects/ngx-tw/icon/icon.providers.ts","../../../projects/ngx-tw/icon/icon.ts","../../../projects/ngx-tw/icon/cdevhub-ngx-tw-icon.ts"],"sourcesContent":["import { Injectable } from '@angular/core';\nimport type { TwIconData, TwIconMap } from './icon.types';\n\n/**\n * Stores icon data (SVG element tuples) for use by `tw-icon`.\n *\n * Not provided in root — use `provideTwIcons()` or `provideTwLucideIcons()`\n * to register icons and supply this service.\n */\n@Injectable()\nexport class IconRegistry {\n private readonly icons: TwIconMap = {};\n\n /** Merges the given icons into the registry. */\n register(icons: TwIconMap): void {\n Object.assign(this.icons, icons);\n }\n\n /** Returns the icon data for the given PascalCase name, or `null` if not registered. */\n get(name: string): TwIconData | null {\n return this.icons[name] ?? null;\n }\n}\n","import { inject, InjectionToken } from '@angular/core';\nimport type { Provider } from '@angular/core';\nimport type { TwIconMap } from './icon.types';\nimport { IconRegistry } from './icon.registry';\n\n/**\n * Multi-provider token whose factories register icons into the `IconRegistry`.\n * Injected by `IconComponent` to trigger factory execution. The token value is never read.\n */\nexport const TW_ICON_REGISTRAR = new InjectionToken<void[]>('TwIconRegistrar');\n\n/**\n * Registers icons for use with `tw-icon`.\n *\n * Returns an array of providers that includes the `IconRegistry` service\n * and a multi-provider factory that merges the given icons into it.\n * Angular deduplicates the class provider, so multiple calls are safe.\n *\n * Works at all injector levels: app root, lazy route, and component.\n *\n * @example\n * ```ts\n * import { provideTwIcons } from '@cdevhub/ngx-tw/icon';\n *\n * const myIcons = {\n * Star: [['polygon', { points: '12 2 15.09 8.26 ...' }]],\n * };\n *\n * export const appConfig = {\n * providers: [provideTwIcons(myIcons)]\n * };\n * ```\n */\nexport function provideTwIcons(icons: TwIconMap): Provider[] {\n return [\n IconRegistry,\n {\n provide: TW_ICON_REGISTRAR,\n multi: true,\n useFactory: () => inject(IconRegistry).register(icons),\n },\n ];\n}\n","import {\n ChangeDetectionStrategy,\n Component,\n computed,\n DOCUMENT,\n effect,\n ElementRef,\n inject,\n input,\n isDevMode,\n Renderer2,\n ViewEncapsulation,\n} from '@angular/core';\nimport { tv } from 'tailwind-variants';\nimport type { TwSize } from '@cdevhub/ngx-tw/core';\nimport { TW_ICON_REGISTRAR } from './icon.providers';\nimport { IconRegistry } from './icon.registry';\nimport type { TwIconColor, TwIconData, TwIconSvgConfig } from './icon.types';\n\nconst SVG_NS = 'http://www.w3.org/2000/svg';\n\nconst DEFAULT_STROKE_WIDTH = 2;\nconst DEFAULT_ABSOLUTE_STROKE_WIDTH = false;\nconst DEFAULT_VIEW_BOX = '0 0 24 24';\n\n/** Converts a kebab-case icon name to PascalCase for registry lookup. */\nfunction toPascalCase(str: string): string {\n return str.replace(/(^|[-_\\s])([a-z0-9])/g, (_match, _sep, char: string) =>\n char.toUpperCase(),\n );\n}\n\n/** Pixel sizes for each TwSize value, used as SVG width/height attributes. */\nconst ICON_SIZE_PX: Record<TwSize, number> = {\n xs: 12,\n sm: 16,\n md: 20,\n lg: 24,\n xl: 32,\n};\n\nconst iconVariants = tv({\n base: 'inline-flex items-center justify-center shrink-0 align-middle',\n variants: {\n color: {\n current: '',\n primary: 'text-primary-500',\n secondary: 'text-secondary-500',\n accent: 'text-accent-500',\n neutral: 'text-fg-muted',\n info: 'text-info-500',\n success: 'text-success-500',\n warning: 'text-warning-500',\n error: 'text-error-500',\n },\n size: {\n xs: 'size-3',\n sm: 'size-4',\n md: 'size-5',\n lg: 'size-6',\n xl: 'size-8',\n },\n },\n defaultVariants: {\n color: 'current',\n size: 'md',\n },\n}, {\n twMerge: true,\n});\n\n/**\n * Renders SVG icons from a registry or from direct icon data.\n *\n * Icons inherit `currentColor` by default. Use the `color` input for semantic\n * color variants that respond to theme changes.\n *\n * Register icons tree-shakably via `provideTwIcons()` (raw SVG data) or\n * `provideTwLucideIcons()` from `ngx-tw/icon/lucide` (Lucide adapter).\n *\n * @example\n * ```html\n * <tw-icon name=\"star\" />\n * <tw-icon name=\"check\" color=\"success\" size=\"lg\" />\n * <tw-icon name=\"alert-triangle\" color=\"warning\" ariaLabel=\"Warning\" />\n * <tw-icon name=\"star\" [svg]=\"{ strokeWidth: 1.5, absoluteStrokeWidth: true }\" />\n * ```\n */\n// IMPORTANT: `ViewEncapsulation.None` is required because the SVG is built\n// imperatively via Renderer2 and appended outside Angular's view boundary —\n// emulated-encapsulation attribute selectors would not match it. The host class\n// list (color/size utilities) must also cascade `currentColor` into the SVG\n// children for `stroke=\"currentColor\"` to resolve correctly.\n@Component({\n selector: 'tw-icon',\n template: '',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n host: {\n '[class]': 'classes()',\n },\n})\nexport class IconComponent {\n private readonly doc = inject(DOCUMENT);\n private readonly renderer = inject(Renderer2);\n private readonly elRef = inject(ElementRef<HTMLElement>);\n private readonly registry = inject(IconRegistry);\n private readonly _registrar = inject(TW_ICON_REGISTRAR, { optional: true });\n\n /** Icon name in kebab-case (e.g. `'chevron-right'`). Resolved via the registry. Defaults to `undefined`. */\n readonly name = input<string>();\n\n /** Direct icon data (SVG element tuples). Takes precedence over `name`. Defaults to `undefined`. */\n readonly img = input<TwIconData>();\n\n /** Semantic color. `'current'` inherits from parent text color. Defaults to `'current'`. */\n readonly color = input<TwIconColor>('current');\n\n /** Icon size. Defaults to `'md'` (20px). */\n readonly size = input<TwSize>('md');\n\n /** Accessible label. When set, removes `aria-hidden` and applies `aria-label` to the SVG. Defaults to `undefined` (icon is decorative, `aria-hidden=\"true\"`). */\n readonly ariaLabel = input<string>();\n\n /**\n * SVG-author configuration: `strokeWidth`, `absoluteStrokeWidth`, `viewBox`.\n * Unset fields fall back to `{ strokeWidth: 2, absoluteStrokeWidth: false, viewBox: '0 0 24 24' }`.\n */\n readonly svg = input<TwIconSvgConfig>();\n\n /** Resolves icon data from `img` (priority) or `name` (registry lookup). */\n protected readonly resolvedIcon = computed<TwIconData | undefined>(() => {\n const imgData = this.img();\n if (imgData) return imgData;\n\n const iconName = this.name();\n if (iconName) return this.registry.get(toPascalCase(iconName)) ?? undefined;\n\n return undefined;\n });\n\n /** Pixel size for the current `size` input. */\n protected readonly sizeInPx = computed(() => ICON_SIZE_PX[this.size()]);\n\n /** Effective stroke width, accounting for `absoluteStrokeWidth`. */\n protected readonly effectiveStrokeWidth = computed(() => {\n const cfg = this.svg();\n const sw = cfg?.strokeWidth ?? DEFAULT_STROKE_WIDTH;\n const absolute = cfg?.absoluteStrokeWidth ?? DEFAULT_ABSOLUTE_STROKE_WIDTH;\n if (!absolute) return sw;\n return (sw * 24) / this.sizeInPx();\n });\n\n /** Effective viewBox. */\n protected readonly effectiveViewBox = computed(() =>\n this.svg()?.viewBox ?? DEFAULT_VIEW_BOX,\n );\n\n /** Host class string from tv(). */\n readonly classes = computed(() =>\n iconVariants({ color: this.color(), size: this.size() }),\n );\n\n /** Tracks the previous render state to avoid unnecessary DOM rebuilds. */\n private prevIconData: TwIconData | undefined;\n private prevStrokeWidth: number | undefined;\n private prevViewBox: string | undefined;\n private prevAriaLabel: string | undefined;\n private currentSvg: SVGElement | null = null;\n\n constructor() {\n effect(() => {\n this.renderSvg();\n });\n }\n\n private renderSvg(): void {\n const el = this.elRef.nativeElement;\n const data = this.resolvedIcon();\n const sw = this.effectiveStrokeWidth();\n const vb = this.effectiveViewBox();\n const label = this.ariaLabel();\n const iconName = this.name();\n\n // Dev-mode warning when icon name is provided but not found\n if (isDevMode() && iconName && !data) {\n console.warn(\n `Icon \"${iconName}\" not found in registry. Did you forget to register it with provideTwIcons()?`,\n );\n }\n\n // No icon data — clear and return\n if (!data) {\n if (this.currentSvg) {\n this.renderer.removeChild(el, this.currentSvg);\n this.currentSvg = null;\n this.prevIconData = undefined;\n }\n return;\n }\n\n // SVG caching: skip DOM rebuild if only color/size changed (handled by host classes)\n if (\n this.currentSvg &&\n data === this.prevIconData &&\n sw === this.prevStrokeWidth &&\n vb === this.prevViewBox &&\n label === this.prevAriaLabel\n ) {\n return;\n }\n\n // Remove previous SVG\n if (this.currentSvg) {\n this.renderer.removeChild(el, this.currentSvg);\n }\n\n const size = this.sizeInPx();\n\n // Build SVG element\n const svg = this.doc.createElementNS(SVG_NS, 'svg');\n this.renderer.setAttribute(svg, 'xmlns', SVG_NS);\n this.renderer.setAttribute(svg, 'width', String(size));\n this.renderer.setAttribute(svg, 'height', String(size));\n this.renderer.setAttribute(svg, 'viewBox', vb);\n this.renderer.setAttribute(svg, 'fill', 'none');\n this.renderer.setAttribute(svg, 'stroke', 'currentColor');\n this.renderer.setAttribute(svg, 'stroke-width', String(sw));\n this.renderer.setAttribute(svg, 'stroke-linecap', 'round');\n this.renderer.setAttribute(svg, 'stroke-linejoin', 'round');\n\n if (label) {\n this.renderer.setAttribute(svg, 'role', 'img');\n this.renderer.setAttribute(svg, 'aria-label', label);\n } else {\n this.renderer.setAttribute(svg, 'aria-hidden', 'true');\n }\n\n // Render SVG children from icon data\n for (const [tag, attrs] of data) {\n const child = this.doc.createElementNS(SVG_NS, tag);\n for (const [key, value] of Object.entries(attrs)) {\n this.renderer.setAttribute(child, key, String(value));\n }\n this.renderer.appendChild(svg, child);\n }\n\n this.renderer.appendChild(el, svg);\n\n // Update cache\n this.currentSvg = svg;\n this.prevIconData = data;\n this.prevStrokeWidth = sw;\n this.prevViewBox = vb;\n this.prevAriaLabel = label;\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-icon.mjs","sources":["../../../projects/ngx-tw/icon/icon.registry.ts","../../../projects/ngx-tw/icon/icon.providers.ts","../../../projects/ngx-tw/icon/icon.ts","../../../projects/ngx-tw/icon/cdevhub-ngx-tw-icon.ts"],"sourcesContent":["import { Injectable } from '@angular/core';\nimport type { TwIconData, TwIconMap } from './icon.types';\n\n/**\n * Stores icon data (SVG element tuples) for use by `tw-icon`.\n *\n * Not provided in root — use `provideTwIcons()` or `provideTwLucideIcons()`\n * to register icons and supply this service.\n */\n@Injectable()\nexport class IconRegistry {\n private readonly icons: TwIconMap = {};\n\n /** Merges the given icons into the registry. */\n register(icons: TwIconMap): void {\n Object.assign(this.icons, icons);\n }\n\n /** Returns the icon data for the given PascalCase name, or `null` if not registered. */\n get(name: string): TwIconData | null {\n return this.icons[name] ?? null;\n }\n}\n","import { inject, InjectionToken } from '@angular/core';\nimport type { Provider } from '@angular/core';\nimport type { TwIconMap } from './icon.types';\nimport { IconRegistry } from './icon.registry';\n\n/**\n * Multi-provider token whose factories register icons into the `IconRegistry`.\n * Injected by `IconComponent` to trigger factory execution. The token value is never read.\n */\nexport const TW_ICON_REGISTRAR = new InjectionToken<void[]>('TwIconRegistrar');\n\n/**\n * Registers icons for use with `tw-icon`.\n *\n * Returns an array of providers that includes the `IconRegistry` service\n * and a multi-provider factory that merges the given icons into it.\n * Angular deduplicates the class provider, so multiple calls are safe.\n *\n * Works at all injector levels: app root, lazy route, and component.\n *\n * @example\n * ```ts\n * import { provideTwIcons } from '@cdevhub/ngx-tw/icon';\n *\n * const myIcons = {\n * Star: [['polygon', { points: '12 2 15.09 8.26 ...' }]],\n * };\n *\n * export const appConfig = {\n * providers: [provideTwIcons(myIcons)]\n * };\n * ```\n */\nexport function provideTwIcons(icons: TwIconMap): Provider[] {\n return [\n IconRegistry,\n {\n provide: TW_ICON_REGISTRAR,\n multi: true,\n useFactory: () => inject(IconRegistry).register(icons),\n },\n ];\n}\n","import {\n ChangeDetectionStrategy,\n Component,\n computed,\n DOCUMENT,\n effect,\n ElementRef,\n inject,\n input,\n isDevMode,\n Renderer2,\n ViewEncapsulation,\n} from '@angular/core';\nimport { tv } from 'tailwind-variants';\nimport type { TwSize } from '@cdevhub/ngx-tw/core';\nimport { TW_ICON_REGISTRAR } from './icon.providers';\nimport { IconRegistry } from './icon.registry';\nimport type { TwIconColor, TwIconData, TwIconSvgConfig } from './icon.types';\n\nconst SVG_NS = 'http://www.w3.org/2000/svg';\n\nconst DEFAULT_STROKE_WIDTH = 2;\nconst DEFAULT_ABSOLUTE_STROKE_WIDTH = false;\nconst DEFAULT_VIEW_BOX = '0 0 24 24';\n\n/** Converts a kebab-case icon name to PascalCase for registry lookup. */\nfunction toPascalCase(str: string): string {\n return str.replace(/(^|[-_\\s])([a-z0-9])/g, (_match, _sep, char: string) =>\n char.toUpperCase(),\n );\n}\n\n/** Pixel sizes for each TwSize value, used as SVG width/height attributes. */\nconst ICON_SIZE_PX: Record<TwSize, number> = {\n xs: 12,\n sm: 16,\n md: 20,\n lg: 24,\n xl: 32,\n};\n\nconst iconVariants = tv({\n base: 'inline-flex items-center justify-center shrink-0 align-middle',\n variants: {\n color: {\n current: '',\n primary: 'text-primary-500',\n secondary: 'text-secondary-500',\n accent: 'text-accent-500',\n neutral: 'text-fg-muted',\n info: 'text-info-500',\n success: 'text-success-500',\n warning: 'text-warning-500',\n error: 'text-error-500',\n },\n size: {\n xs: 'size-3',\n sm: 'size-4',\n md: 'size-5',\n lg: 'size-6',\n xl: 'size-8',\n },\n },\n defaultVariants: {\n color: 'current',\n size: 'md',\n },\n}, {\n twMerge: true,\n});\n\n/**\n * Renders SVG icons from a registry or from direct icon data.\n *\n * Icons inherit `currentColor` by default. Use the `color` input for semantic\n * color variants that respond to theme changes.\n *\n * Register icons tree-shakably via `provideTwIcons()` (raw SVG data) or\n * `provideTwLucideIcons()` from `ngx-tw/icon/lucide` (Lucide adapter).\n *\n * @example\n * ```html\n * <tw-icon name=\"star\" />\n * <tw-icon name=\"check\" color=\"success\" size=\"lg\" />\n * <tw-icon name=\"alert-triangle\" color=\"warning\" aria-label=\"Warning\" />\n * <tw-icon name=\"star\" [svg]=\"{ strokeWidth: 1.5, absoluteStrokeWidth: true }\" />\n * ```\n */\n// IMPORTANT: `ViewEncapsulation.None` is required because the SVG is built\n// imperatively via Renderer2 and appended outside Angular's view boundary —\n// emulated-encapsulation attribute selectors would not match it. The host class\n// list (color/size utilities) must also cascade `currentColor` into the SVG\n// children for `stroke=\"currentColor\"` to resolve correctly.\n@Component({\n selector: 'tw-icon',\n template: '',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n host: {\n '[class]': 'classes()',\n // The accessible name belongs on the generated `<svg>` (which takes\n // `role=\"img\"`), never on the role-less `<tw-icon>` host: ARIA 1.2\n // prohibits `aria-label` on an element with no role, and axe flags it as a\n // serious `aria-prohibited-attr` violation.\n //\n // This binding is load-bearing, not defensive. Angular DOES leave the\n // literal `aria-label=\"…\"` on the host after handing it to the aliased\n // input, so without this the host keeps a prohibited attribute. Verified\n // by deleting the line: the \"plain aria-label attribute reachable\" spec\n // fails. Do not remove it as dead code.\n '[attr.aria-label]': 'null',\n },\n})\nexport class IconComponent {\n private readonly doc = inject(DOCUMENT);\n private readonly renderer = inject(Renderer2);\n private readonly elRef = inject(ElementRef<HTMLElement>);\n private readonly registry = inject(IconRegistry);\n private readonly _registrar = inject(TW_ICON_REGISTRAR, { optional: true });\n\n /** Icon name in kebab-case (e.g. `'chevron-right'`). Resolved via the registry. Defaults to `undefined`. */\n readonly name = input<string>();\n\n /** Direct icon data (SVG element tuples). Takes precedence over `name`. Defaults to `undefined`. */\n readonly img = input<TwIconData>();\n\n /** Semantic color. `'current'` inherits from parent text color. Defaults to `'current'`. */\n readonly color = input<TwIconColor>('current');\n\n /** Icon size. Defaults to `'md'` (20px). */\n readonly size = input<TwSize>('md');\n\n /** Accessible label, supplied as the plain `aria-label` attribute. When set, the generated SVG takes `role=\"img\"` plus the label instead of `aria-hidden=\"true\"`. Defaults to `undefined` (icon is decorative, `aria-hidden=\"true\"`). */\n readonly ariaLabel = input<string | undefined>(undefined, { alias: 'aria-label' });\n\n /**\n * SVG-author configuration: `strokeWidth`, `absoluteStrokeWidth`, `viewBox`.\n * Unset fields fall back to `{ strokeWidth: 2, absoluteStrokeWidth: false, viewBox: '0 0 24 24' }`.\n */\n readonly svg = input<TwIconSvgConfig>();\n\n /** Resolves icon data from `img` (priority) or `name` (registry lookup). */\n protected readonly resolvedIcon = computed<TwIconData | undefined>(() => {\n const imgData = this.img();\n if (imgData) return imgData;\n\n const iconName = this.name();\n if (iconName) return this.registry.get(toPascalCase(iconName)) ?? undefined;\n\n return undefined;\n });\n\n /** Pixel size for the current `size` input. */\n protected readonly sizeInPx = computed(() => ICON_SIZE_PX[this.size()]);\n\n /** Effective stroke width, accounting for `absoluteStrokeWidth`. */\n protected readonly effectiveStrokeWidth = computed(() => {\n const cfg = this.svg();\n const sw = cfg?.strokeWidth ?? DEFAULT_STROKE_WIDTH;\n const absolute = cfg?.absoluteStrokeWidth ?? DEFAULT_ABSOLUTE_STROKE_WIDTH;\n if (!absolute) return sw;\n return (sw * 24) / this.sizeInPx();\n });\n\n /** Effective viewBox. */\n protected readonly effectiveViewBox = computed(() =>\n this.svg()?.viewBox ?? DEFAULT_VIEW_BOX,\n );\n\n /** Host class string from tv(). */\n readonly classes = computed(() =>\n iconVariants({ color: this.color(), size: this.size() }),\n );\n\n /** Tracks the previous render state to avoid unnecessary DOM rebuilds. */\n private prevIconData: TwIconData | undefined;\n private prevStrokeWidth: number | undefined;\n private prevViewBox: string | undefined;\n private prevAriaLabel: string | undefined;\n private currentSvg: SVGElement | null = null;\n\n constructor() {\n effect(() => {\n this.renderSvg();\n });\n }\n\n private renderSvg(): void {\n const el = this.elRef.nativeElement;\n const data = this.resolvedIcon();\n const sw = this.effectiveStrokeWidth();\n const vb = this.effectiveViewBox();\n const label = this.ariaLabel();\n const iconName = this.name();\n\n // Dev-mode warning when icon name is provided but not found\n if (isDevMode() && iconName && !data) {\n console.warn(\n `Icon \"${iconName}\" not found in registry. Did you forget to register it with provideTwIcons()?`,\n );\n }\n\n // No icon data — clear and return\n if (!data) {\n if (this.currentSvg) {\n this.renderer.removeChild(el, this.currentSvg);\n this.currentSvg = null;\n this.prevIconData = undefined;\n }\n return;\n }\n\n // SVG caching: skip DOM rebuild if only color/size changed (handled by host classes)\n if (\n this.currentSvg &&\n data === this.prevIconData &&\n sw === this.prevStrokeWidth &&\n vb === this.prevViewBox &&\n label === this.prevAriaLabel\n ) {\n return;\n }\n\n // Remove previous SVG\n if (this.currentSvg) {\n this.renderer.removeChild(el, this.currentSvg);\n }\n\n const size = this.sizeInPx();\n\n // Build SVG element\n const svg = this.doc.createElementNS(SVG_NS, 'svg');\n this.renderer.setAttribute(svg, 'xmlns', SVG_NS);\n this.renderer.setAttribute(svg, 'width', String(size));\n this.renderer.setAttribute(svg, 'height', String(size));\n this.renderer.setAttribute(svg, 'viewBox', vb);\n this.renderer.setAttribute(svg, 'fill', 'none');\n this.renderer.setAttribute(svg, 'stroke', 'currentColor');\n this.renderer.setAttribute(svg, 'stroke-width', String(sw));\n this.renderer.setAttribute(svg, 'stroke-linecap', 'round');\n this.renderer.setAttribute(svg, 'stroke-linejoin', 'round');\n\n if (label) {\n this.renderer.setAttribute(svg, 'role', 'img');\n this.renderer.setAttribute(svg, 'aria-label', label);\n } else {\n this.renderer.setAttribute(svg, 'aria-hidden', 'true');\n }\n\n // Render SVG children from icon data\n for (const [tag, attrs] of data) {\n const child = this.doc.createElementNS(SVG_NS, tag);\n for (const [key, value] of Object.entries(attrs)) {\n this.renderer.setAttribute(child, key, String(value));\n }\n this.renderer.appendChild(svg, child);\n }\n\n this.renderer.appendChild(el, svg);\n\n // Update cache\n this.currentSvg = svg;\n this.prevIconData = data;\n this.prevStrokeWidth = sw;\n this.prevViewBox = vb;\n this.prevAriaLabel = label;\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
2
|
import { InjectionToken, inject, ElementRef, DestroyRef, input, booleanAttribute, signal, computed, isSignal, effect, untracked, isDevMode, Directive } from '@angular/core';
|
|
3
3
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
4
|
-
import { NgControl
|
|
4
|
+
import { NgControl } from '@angular/forms';
|
|
5
5
|
import { FocusMonitor } from '@angular/cdk/a11y';
|
|
6
6
|
import { AutofillMonitor } from '@angular/cdk/text-field';
|
|
7
7
|
import { Platform } from '@angular/cdk/platform';
|
|
8
|
-
import { merge } from 'rxjs';
|
|
9
8
|
import { tv } from 'tailwind-variants';
|
|
10
|
-
import {
|
|
9
|
+
import { wireErrorState } from '@cdevhub/ngx-tw/core';
|
|
11
10
|
import { FormFieldControl, TW_FORM_FIELD, TW_FORM_FIELD_CONTROL } from '@cdevhub/ngx-tw/form-field';
|
|
12
11
|
|
|
13
12
|
/**
|
|
@@ -43,6 +42,23 @@ const inputVariants = tv({
|
|
|
43
42
|
true: 'border-0 p-0 shadow-none focus:outline-none focus-visible:outline-none',
|
|
44
43
|
false: 'rounded-md border border-border transition-[color,border-color,box-shadow] duration-normal motion-reduce:transition-none hover:border-border-strong focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-500',
|
|
45
44
|
},
|
|
45
|
+
// Height strategy for the standalone (`inFormField: false`) size scale —
|
|
46
|
+
// see `docs/vertical-rhythm.md`. Driven by the host tag, not an input.
|
|
47
|
+
//
|
|
48
|
+
// `false` (a single-line `<input>`) pins the border-box height
|
|
49
|
+
// (`h-6` … `h-12`) and deletes the vertical padding, so padding and
|
|
50
|
+
// height cannot fight. Browsers centre an `<input>`'s text natively, so
|
|
51
|
+
// no flex box is needed.
|
|
52
|
+
//
|
|
53
|
+
// `true` (a `<textarea>`) is multi-line by definition: the vertical
|
|
54
|
+
// padding is retained and no height utility is emitted here. The
|
|
55
|
+
// control-scale `min-h-*` floor is applied by `TextareaDirective`
|
|
56
|
+
// instead, because it has to be droppable while CDK autosize owns the
|
|
57
|
+
// height — see the `heightFloor` variant in `textarea.ts`.
|
|
58
|
+
multiline: {
|
|
59
|
+
true: '',
|
|
60
|
+
false: '',
|
|
61
|
+
},
|
|
46
62
|
size: {
|
|
47
63
|
xs: '',
|
|
48
64
|
sm: '',
|
|
@@ -60,12 +76,20 @@ const inputVariants = tv({
|
|
|
60
76
|
},
|
|
61
77
|
},
|
|
62
78
|
compoundVariants: [
|
|
63
|
-
// Standalone density × size —
|
|
64
|
-
|
|
65
|
-
{ inFormField: false, size: '
|
|
66
|
-
{ inFormField: false, size: '
|
|
67
|
-
{ inFormField: false, size: '
|
|
68
|
-
{ inFormField: false, size: '
|
|
79
|
+
// Standalone density × size — single-line: horizontal padding, font
|
|
80
|
+
// scale, and the pinned control height (no vertical padding).
|
|
81
|
+
{ inFormField: false, multiline: false, size: 'xs', class: 'px-2 text-xs h-6' },
|
|
82
|
+
{ inFormField: false, multiline: false, size: 'sm', class: 'px-3 text-sm h-8' },
|
|
83
|
+
{ inFormField: false, multiline: false, size: 'md', class: 'px-4 text-sm h-9' },
|
|
84
|
+
{ inFormField: false, multiline: false, size: 'lg', class: 'px-5 text-base h-11' },
|
|
85
|
+
{ inFormField: false, multiline: false, size: 'xl', class: 'px-6 text-base h-12' },
|
|
86
|
+
// Standalone density × size — multi-line: vertical padding is kept so the
|
|
87
|
+
// box can grow; the height floor is applied by `TextareaDirective`.
|
|
88
|
+
{ inFormField: false, multiline: true, size: 'xs', class: 'px-2 py-1 text-xs' },
|
|
89
|
+
{ inFormField: false, multiline: true, size: 'sm', class: 'px-3 py-1.5 text-sm' },
|
|
90
|
+
{ inFormField: false, multiline: true, size: 'md', class: 'px-4 py-2 text-sm' },
|
|
91
|
+
{ inFormField: false, multiline: true, size: 'lg', class: 'px-5 py-2.5 text-base' },
|
|
92
|
+
{ inFormField: false, multiline: true, size: 'xl', class: 'px-6 py-3 text-base' },
|
|
69
93
|
// Standalone error state — colored border and focus ring.
|
|
70
94
|
{
|
|
71
95
|
inFormField: false,
|
|
@@ -75,6 +99,7 @@ const inputVariants = tv({
|
|
|
75
99
|
],
|
|
76
100
|
defaultVariants: {
|
|
77
101
|
inFormField: false,
|
|
102
|
+
multiline: false,
|
|
78
103
|
size: 'md',
|
|
79
104
|
errorState: false,
|
|
80
105
|
disabled: false,
|
|
@@ -92,7 +117,7 @@ let nextInputId = 0;
|
|
|
92
117
|
* and provide under `TW_FORM_FIELD_CONTROL`.
|
|
93
118
|
* - Swap value storage for an existing `<input twInput>` (masked input,
|
|
94
119
|
* date parser, etc.) → provide {@link TW_INPUT_VALUE_ACCESSOR}.
|
|
95
|
-
* - Change when errors show → override
|
|
120
|
+
* - Change when errors show → override `TW_ERROR_STATE_MATCHER` at any
|
|
96
121
|
* injector level, or pass `errorStateMatcher` per instance.
|
|
97
122
|
*
|
|
98
123
|
* Forms integration is inherited: Angular's native value accessors attach to
|
|
@@ -110,11 +135,6 @@ class InputDirective extends FormFieldControl {
|
|
|
110
135
|
optional: true,
|
|
111
136
|
self: true,
|
|
112
137
|
});
|
|
113
|
-
parentForm = inject(NgForm, { optional: true });
|
|
114
|
-
parentFormGroup = inject(FormGroupDirective, {
|
|
115
|
-
optional: true,
|
|
116
|
-
});
|
|
117
|
-
defaultMatcher = inject(TW_ERROR_STATE_MATCHER);
|
|
118
138
|
valueAccessor = inject(TW_INPUT_VALUE_ACCESSOR, {
|
|
119
139
|
optional: true,
|
|
120
140
|
self: true,
|
|
@@ -129,7 +149,7 @@ class InputDirective extends FormFieldControl {
|
|
|
129
149
|
/** Native HTML input `type`. Defaults to `'text'`. Dev-mode throws on unsupported values (`checkbox`, `radio`, `submit`, etc.) — use the dedicated component instead. Ignored on `<textarea>`. */
|
|
130
150
|
type = input('text', /* @ts-ignore */
|
|
131
151
|
...(ngDevMode ? [{ debugName: "type" }] : /* istanbul ignore next */ []));
|
|
132
|
-
/** Density of a standalone field. Maps to the
|
|
152
|
+
/** Density of a standalone field. Maps to the pinned control-height scale (24/32/36/44/48px, `h-6` xs … `h-12` xl), the horizontal-padding scale (`px-2` xs … `px-6` xl), and the font scale (`text-xs` xs, `text-sm` sm/md, `text-base` lg/xl). On a `<textarea>` the height becomes a `min-h-*` floor and the vertical padding is retained. Ignored inside a `<tw-form-field>` — the wrapper's `size` carries density. Defaults to `'md'`. */
|
|
133
153
|
size = input('md', /* @ts-ignore */
|
|
134
154
|
...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
135
155
|
/** Disables the control. Also reflects `ngControl.disabled` when the element is bound to a reactive form. Defaults to `false`. */
|
|
@@ -155,10 +175,13 @@ class InputDirective extends FormFieldControl {
|
|
|
155
175
|
...(ngDevMode ? [{ debugName: "_focused" }] : /* istanbul ignore next */ []));
|
|
156
176
|
_autofilled = signal(false, /* @ts-ignore */
|
|
157
177
|
...(ngDevMode ? [{ debugName: "_autofilled" }] : /* istanbul ignore next */ []));
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
178
|
+
/** @internal Shared `errorState` / `required` / `errors` derivation — see `wireErrorState`. */
|
|
179
|
+
errorWiring = wireErrorState({
|
|
180
|
+
ngControl: () => this.ngControl,
|
|
181
|
+
matcher: () => this.errorStateMatcher(),
|
|
182
|
+
required: () => this.requiredInput(),
|
|
183
|
+
track: [() => this._focused()],
|
|
184
|
+
});
|
|
162
185
|
// ── FormFieldControl signals ──
|
|
163
186
|
/** @internal */
|
|
164
187
|
id = computed(() => this.idInput() ?? this.uid, /* @ts-ignore */
|
|
@@ -178,38 +201,20 @@ class InputDirective extends FormFieldControl {
|
|
|
178
201
|
...(ngDevMode ? [{ debugName: "empty" }] : /* istanbul ignore next */ []));
|
|
179
202
|
/** @internal */
|
|
180
203
|
disabled = computed(() => {
|
|
181
|
-
this.
|
|
204
|
+
this.errorWiring.rev();
|
|
182
205
|
return this.disabledInput() || !!this.ngControl?.disabled;
|
|
183
206
|
}, /* @ts-ignore */
|
|
184
207
|
...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
|
|
185
208
|
/** @internal */
|
|
186
|
-
required =
|
|
187
|
-
this._ngControlRev();
|
|
188
|
-
if (this.requiredInput())
|
|
189
|
-
return true;
|
|
190
|
-
return !!this.ngControl?.control?.hasValidator(Validators.required);
|
|
191
|
-
}, /* @ts-ignore */
|
|
192
|
-
...(ngDevMode ? [{ debugName: "required" }] : /* istanbul ignore next */ []));
|
|
209
|
+
required = this.errorWiring.required;
|
|
193
210
|
/** @internal */
|
|
194
|
-
errorState =
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
this._focused();
|
|
198
|
-
const matcher = this.errorStateMatcher() ?? this.defaultMatcher;
|
|
199
|
-
const form = this.parentFormGroup ??
|
|
200
|
-
this.parentForm;
|
|
201
|
-
return matcher.isErrorState(this.ngControl?.control ?? null, form);
|
|
202
|
-
}, /* @ts-ignore */
|
|
203
|
-
...(ngDevMode ? [{ debugName: "errorState" }] : /* istanbul ignore next */ []));
|
|
204
|
-
/** @internal Active validation errors map from the bound `NgControl` (or `null` when the control reports none / is unbound). Recomputes on every `_ngControlRev` tick so `[twError match="…"]` reacts to validator transitions, including rules that fire/clear without flipping `VALID`/`INVALID`. */
|
|
205
|
-
errors = computed(() => {
|
|
206
|
-
this._ngControlRev();
|
|
207
|
-
return this.ngControl?.control?.errors ?? null;
|
|
208
|
-
}, /* @ts-ignore */
|
|
209
|
-
...(ngDevMode ? [{ debugName: "errors" }] : /* istanbul ignore next */ []));
|
|
211
|
+
errorState = this.errorWiring.errorState;
|
|
212
|
+
/** @internal Active validation errors map from the bound `NgControl`, for `[twError match="…"]` filtering. */
|
|
213
|
+
errors = this.errorWiring.errors;
|
|
210
214
|
/** @internal */
|
|
211
215
|
classes = computed(() => inputVariants({
|
|
212
216
|
inFormField: !!this.formField,
|
|
217
|
+
multiline: this._isTextarea,
|
|
213
218
|
size: this.size(),
|
|
214
219
|
errorState: this.errorState(),
|
|
215
220
|
disabled: this.disabled(),
|
|
@@ -226,7 +231,7 @@ class InputDirective extends FormFieldControl {
|
|
|
226
231
|
if (wasFocused && !origin) {
|
|
227
232
|
// Blur often flips `touched` on the bound `NgControl`; bump the
|
|
228
233
|
// revision signal so `errorState` recomputes.
|
|
229
|
-
this.
|
|
234
|
+
this.errorWiring.bump();
|
|
230
235
|
}
|
|
231
236
|
});
|
|
232
237
|
this.destroyRef.onDestroy(() => this.focusMonitor.stopMonitoring(this.elementRef));
|
|
@@ -266,23 +271,9 @@ class InputDirective extends FormFieldControl {
|
|
|
266
271
|
this.destroyRef.onDestroy(() => this.autofillMonitor.stopMonitoring(this.elementRef.nativeElement));
|
|
267
272
|
}
|
|
268
273
|
// NgControl's `control` is set by the parent `FormControl*` directive's
|
|
269
|
-
// own `ngOnChanges`, which runs before children's `ngOnInit`.
|
|
274
|
+
// own `ngOnChanges`, which runs before children's `ngOnInit`. Connect
|
|
270
275
|
// here so `statusChanges`/`valueChanges` are actually available.
|
|
271
|
-
|
|
272
|
-
if (ctrl) {
|
|
273
|
-
const streams = [ctrl.statusChanges, ctrl.valueChanges].filter((s) => !!s);
|
|
274
|
-
if (streams.length) {
|
|
275
|
-
merge(...streams)
|
|
276
|
-
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
277
|
-
.subscribe(() => this._ngControlRev.update((n) => n + 1));
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
const submit = this.parentFormGroup?.ngSubmit ?? this.parentForm?.ngSubmit;
|
|
281
|
-
if (submit) {
|
|
282
|
-
submit
|
|
283
|
-
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
284
|
-
.subscribe(() => this._formSubmitRev.update((n) => n + 1));
|
|
285
|
-
}
|
|
276
|
+
this.errorWiring.connect();
|
|
286
277
|
}
|
|
287
278
|
/** Moves focus to the underlying element. */
|
|
288
279
|
focus(options) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cdevhub-ngx-tw-input.mjs","sources":["../../../projects/ngx-tw/input/input.ts","../../../projects/ngx-tw/input/cdevhub-ngx-tw-input.ts"],"sourcesContent":["import {\n booleanAttribute,\n computed,\n DestroyRef,\n Directive,\n effect,\n ElementRef,\n inject,\n InjectionToken,\n input,\n isDevMode,\n isSignal,\n type OnInit,\n signal,\n type Signal,\n untracked,\n type WritableSignal,\n} from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport {\n FormGroupDirective,\n NgControl,\n NgForm,\n Validators,\n} from '@angular/forms';\nimport { FocusMonitor } from '@angular/cdk/a11y';\nimport { AutofillMonitor } from '@angular/cdk/text-field';\nimport { Platform } from '@angular/cdk/platform';\nimport { merge } from 'rxjs';\nimport { tv } from 'tailwind-variants';\nimport {\n type ErrorStateMatcher,\n TW_ERROR_STATE_MATCHER,\n type TwFormSubmitted,\n type TwSize,\n} from '@cdevhub/ngx-tw/core';\nimport {\n FormFieldControl,\n TW_FORM_FIELD,\n TW_FORM_FIELD_CONTROL,\n} from '@cdevhub/ngx-tw/form-field';\n\n/**\n * Extension point for directives that wrap an `<input twInput>` and need to\n * own how its value is read and written. The directive's `value` property can\n * be a plain object slot (`{ value: T }`) or a `WritableSignal<T>`. Mirrors\n * Angular Material's `MAT_INPUT_VALUE_ACCESSOR`.\n */\nexport const TW_INPUT_VALUE_ACCESSOR = new InjectionToken<{\n value: unknown | WritableSignal<unknown>;\n}>('TW_INPUT_VALUE_ACCESSOR');\n\nconst INVALID_INPUT_TYPES = new Set([\n 'button',\n 'checkbox',\n 'file',\n 'hidden',\n 'image',\n 'radio',\n 'range',\n 'reset',\n 'submit',\n]);\n\nconst NEVER_EMPTY_TYPES = new Set([\n 'date',\n 'datetime-local',\n 'month',\n 'time',\n 'week',\n 'color',\n]);\n\nconst inputVariants = tv(\n {\n base: 'block w-full bg-transparent text-fg placeholder:text-fg-subtle outline-none',\n variants: {\n inFormField: {\n true: 'border-0 p-0 shadow-none focus:outline-none focus-visible:outline-none',\n false:\n 'rounded-md border border-border transition-[color,border-color,box-shadow] duration-normal motion-reduce:transition-none hover:border-border-strong focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-500',\n },\n size: {\n xs: '',\n sm: '',\n md: '',\n lg: '',\n xl: '',\n },\n errorState: {\n true: '',\n false: '',\n },\n disabled: {\n true: 'opacity-50 cursor-not-allowed',\n false: '',\n },\n },\n compoundVariants: [\n // Standalone density × size — inline padding + font scale.\n { inFormField: false, size: 'xs', class: 'px-2 py-1 text-xs' },\n { inFormField: false, size: 'sm', class: 'px-3 py-1.5 text-sm' },\n { inFormField: false, size: 'md', class: 'px-4 py-2 text-sm' },\n { inFormField: false, size: 'lg', class: 'px-5 py-2.5 text-base' },\n { inFormField: false, size: 'xl', class: 'px-6 py-3 text-base' },\n // Standalone error state — colored border and focus ring.\n {\n inFormField: false,\n errorState: true,\n class: 'border-error-500 focus-visible:outline-error-500',\n },\n ],\n defaultVariants: {\n inFormField: false,\n size: 'md',\n errorState: false,\n disabled: false,\n },\n },\n { twMerge: true },\n);\n\nlet nextInputId = 0;\n\n/**\n * Adapts a native `<input>` or `<textarea>` into an ngx-tw form-field-compatible\n * control. Works standalone (applies its own border and focus styling) or\n * inside a `<tw-form-field>` (strips its chrome and provides signals the\n * form-field uses to float the label and wire `aria-describedby`).\n *\n * Extension points:\n * - Implement a completely custom control → extend {@link FormFieldControl}\n * and provide under `TW_FORM_FIELD_CONTROL`.\n * - Swap value storage for an existing `<input twInput>` (masked input,\n * date parser, etc.) → provide {@link TW_INPUT_VALUE_ACCESSOR}.\n * - Change when errors show → override {@link TW_ERROR_STATE_MATCHER} at any\n * injector level, or pass `errorStateMatcher` per instance.\n *\n * Forms integration is inherited: Angular's native value accessors attach to\n * the underlying element, so the directive works with template-driven,\n * reactive, and signal-based forms without additional glue.\n */\n@Directive({\n selector: 'input[twInput], textarea[twInput]',\n exportAs: 'twInput',\n providers: [\n { provide: TW_FORM_FIELD_CONTROL, useExisting: InputDirective },\n ],\n host: {\n '[class]': 'classes()',\n '[attr.id]': 'id()',\n '[attr.type]': '_isTextarea ? null : type()',\n '[disabled]': 'disabled()',\n '[attr.aria-invalid]': 'errorState() || null',\n '[attr.aria-required]': 'required() || null',\n '(input)': '_onInput()',\n },\n})\nexport class InputDirective\n extends FormFieldControl<string>\n implements OnInit\n{\n private readonly elementRef = inject<\n ElementRef<HTMLInputElement | HTMLTextAreaElement>\n >(ElementRef);\n private readonly focusMonitor = inject(FocusMonitor);\n private readonly autofillMonitor = inject(AutofillMonitor);\n private readonly platform = inject(Platform);\n private readonly destroyRef = inject(DestroyRef);\n private readonly formField = inject(TW_FORM_FIELD, { optional: true });\n private readonly ngControl = inject(NgControl, {\n optional: true,\n self: true,\n });\n private readonly parentForm = inject(NgForm, { optional: true });\n private readonly parentFormGroup = inject(FormGroupDirective, {\n optional: true,\n });\n private readonly defaultMatcher = inject(TW_ERROR_STATE_MATCHER);\n private readonly valueAccessor = inject(TW_INPUT_VALUE_ACCESSOR, {\n optional: true,\n self: true,\n });\n\n /** @internal */\n readonly _isTextarea =\n this.elementRef.nativeElement.tagName.toLowerCase() === 'textarea';\n\n /** Contract value — `'input'` for `<input>` and `'textarea'` for `<textarea>`. Form-field appends it as `tw-form-field-type-*` for styling hooks. */\n readonly controlType: string = this._isTextarea ? 'textarea' : 'input';\n\n private readonly uid = `tw-input-${nextInputId++}`;\n\n /** Id on the underlying element. Auto-generated as `tw-input-N` when not provided. Used by the form-field's `<label for>` attribute. */\n readonly idInput = input<string | undefined>(undefined, { alias: 'id' });\n\n /** Native HTML input `type`. Defaults to `'text'`. Dev-mode throws on unsupported values (`checkbox`, `radio`, `submit`, etc.) — use the dedicated component instead. Ignored on `<textarea>`. */\n readonly type = input<string>('text');\n\n /** Density of a standalone field. Maps to the inline-padding scale (`px-2 py-1` xs … `px-6 py-3` xl) and font scale (`text-xs` xs, `text-sm` sm/md, `text-base` lg/xl). Ignored inside a `<tw-form-field>` — the wrapper's `size` carries density. Defaults to `'md'`. */\n readonly size = input<TwSize>('md');\n\n /** Disables the control. Also reflects `ngControl.disabled` when the element is bound to a reactive form. Defaults to `false`. */\n readonly disabledInput = input<boolean, unknown>(false, {\n alias: 'disabled',\n transform: booleanAttribute,\n });\n\n /** Marks the control as required. Also inferred from `Validators.required` on a bound `NgControl`. Defaults to `false`. */\n readonly requiredInput = input<boolean, unknown>(false, {\n alias: 'required',\n transform: booleanAttribute,\n });\n\n /** Makes the control read-only (native `readonly` attribute). Defaults to `false`. */\n readonly readonlyInput = input<boolean, unknown>(false, {\n alias: 'readonly',\n transform: booleanAttribute,\n });\n\n /** Per-instance override of the {@link ErrorStateMatcher}. When omitted, the directive uses the `TW_ERROR_STATE_MATCHER` token's value. */\n readonly errorStateMatcher = input<ErrorStateMatcher | undefined>(undefined);\n\n /** Consumer-supplied `aria-describedby` ids. The form-field preserves these when merging hint and error ids. Alias: `aria-describedby`. */\n override readonly userAriaDescribedBy = input<string | undefined>(undefined, {\n alias: 'aria-describedby',\n });\n\n /** Consumer-supplied `aria-labelledby` ids. The form-field preserves these when merging in the projected label id. Alias: `aria-labelledby`. */\n override readonly userAriaLabelledby = input<string | undefined>(undefined, {\n alias: 'aria-labelledby',\n });\n\n // ── Internal state signals ──\n\n private readonly _value = signal<string>(this._readInitialValue());\n private readonly _focused = signal(false);\n private readonly _autofilled = signal(false);\n private readonly _ngControlRev = signal(0);\n private readonly _formSubmitRev = signal(0);\n\n // ── FormFieldControl signals ──\n\n /** @internal */\n readonly id = computed(() => this.idInput() ?? this.uid);\n\n /** @internal */\n readonly value = computed<string | null>(() => this._value() || null);\n\n /** @internal */\n readonly focused = this._focused.asReadonly();\n\n /** @internal */\n readonly empty = computed(() => {\n if (!this._isTextarea && NEVER_EMPTY_TYPES.has(this.type())) {\n return false;\n }\n return !this._value() && !this._autofilled();\n });\n\n /** @internal */\n readonly disabled = computed(() => {\n this._ngControlRev();\n return this.disabledInput() || !!this.ngControl?.disabled;\n });\n\n /** @internal */\n readonly required = computed(() => {\n this._ngControlRev();\n if (this.requiredInput()) return true;\n return !!this.ngControl?.control?.hasValidator(Validators.required);\n });\n\n /** @internal */\n readonly errorState = computed(() => {\n this._ngControlRev();\n this._formSubmitRev();\n this._focused();\n const matcher = this.errorStateMatcher() ?? this.defaultMatcher;\n const form: TwFormSubmitted | null =\n (this.parentFormGroup as TwFormSubmitted | null) ??\n (this.parentForm as TwFormSubmitted | null);\n return matcher.isErrorState(this.ngControl?.control ?? null, form);\n });\n\n /** @internal Active validation errors map from the bound `NgControl` (or `null` when the control reports none / is unbound). Recomputes on every `_ngControlRev` tick so `[twError match=\"…\"]` reacts to validator transitions, including rules that fire/clear without flipping `VALID`/`INVALID`. */\n override readonly errors = computed<Record<string, unknown> | null>(() => {\n this._ngControlRev();\n return (this.ngControl?.control?.errors as Record<string, unknown> | null) ?? null;\n });\n\n /** @internal */\n readonly classes = computed(() =>\n inputVariants({\n inFormField: !!this.formField,\n size: this.size(),\n errorState: this.errorState(),\n disabled: this.disabled(),\n }),\n );\n\n constructor() {\n super();\n\n this.focusMonitor\n .monitor(this.elementRef)\n .pipe(takeUntilDestroyed())\n .subscribe((origin) => {\n const wasFocused = this._focused();\n this._focused.set(!!origin);\n if (wasFocused && !origin) {\n // Blur often flips `touched` on the bound `NgControl`; bump the\n // revision signal so `errorState` recomputes.\n this._ngControlRev.update((n) => n + 1);\n }\n });\n this.destroyRef.onDestroy(() =>\n this.focusMonitor.stopMonitoring(this.elementRef),\n );\n\n // Keep `_value` in sync when the accessor's value is itself a signal.\n if (this.valueAccessor && isSignal(this.valueAccessor.value)) {\n effect(() => {\n const v = (this.valueAccessor!.value as Signal<unknown>)();\n untracked(() => this._value.set(this._stringify(v)));\n });\n }\n\n // Sync the `readonly` input to the native attribute.\n effect(() => {\n const el = this.elementRef.nativeElement;\n if (this.readonlyInput()) {\n el.setAttribute('readonly', 'true');\n } else {\n el.removeAttribute('readonly');\n }\n });\n\n // Validate the input type in dev mode.\n effect(() => {\n const t = this.type();\n if (\n isDevMode() &&\n !this._isTextarea &&\n INVALID_INPUT_TYPES.has(t)\n ) {\n throw new Error(\n `tw-input: unsupported input type \"${t}\". Use a dedicated ngx-tw component for that control.`,\n );\n }\n });\n }\n\n ngOnInit(): void {\n if (this.platform.isBrowser) {\n this.autofillMonitor\n .monitor(this.elementRef.nativeElement)\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe((event) => this._autofilled.set(event.isAutofilled));\n this.destroyRef.onDestroy(() =>\n this.autofillMonitor.stopMonitoring(this.elementRef.nativeElement),\n );\n }\n\n // NgControl's `control` is set by the parent `FormControl*` directive's\n // own `ngOnChanges`, which runs before children's `ngOnInit`. Subscribe\n // here so `statusChanges`/`valueChanges` are actually available.\n const ctrl = this.ngControl?.control;\n if (ctrl) {\n const streams = [ctrl.statusChanges, ctrl.valueChanges].filter(\n (s): s is NonNullable<typeof s> => !!s,\n );\n if (streams.length) {\n merge(...streams)\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe(() => this._ngControlRev.update((n) => n + 1));\n }\n }\n\n const submit = this.parentFormGroup?.ngSubmit ?? this.parentForm?.ngSubmit;\n if (submit) {\n submit\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe(() => this._formSubmitRev.update((n) => n + 1));\n }\n }\n\n /** Moves focus to the underlying element. */\n focus(options?: FocusOptions): void {\n this.elementRef.nativeElement.focus(options);\n }\n\n /** @internal Called when `<tw-form-field>` has computed the merged describedby ids. */\n setDescribedByIds(ids: string[]): void {\n const el = this.elementRef.nativeElement;\n if (ids.length) {\n el.setAttribute('aria-describedby', ids.join(' '));\n } else {\n el.removeAttribute('aria-describedby');\n }\n }\n\n /** @internal Called when `<tw-form-field>` has computed the merged labelledby ids. The native `<label for>` association covers most cases; this attribute is mainly useful when a consumer also supplies `aria-labelledby` for an external label. */\n override setLabelledByIds(ids: string[]): void {\n const el = this.elementRef.nativeElement;\n // Skip applying the projected label id by itself — native `<label for=>` already wires it\n // and a redundant `aria-labelledby` can cause double-announcements in some screen readers.\n // Only apply when the consumer supplied additional ids via `aria-labelledby`.\n const userIds = this.userAriaLabelledby();\n if (userIds || (ids.length > 1)) {\n el.setAttribute('aria-labelledby', ids.join(' '));\n } else {\n el.removeAttribute('aria-labelledby');\n }\n }\n\n /** @internal Called when the form-field container is clicked. */\n onContainerClick(_event: MouseEvent): void {\n if (!this._focused()) {\n this.focus();\n }\n }\n\n /** @internal */\n _onInput(): void {\n this._value.set(this._currentValue());\n }\n\n private _currentValue(): string {\n if (this.valueAccessor) {\n const v = this.valueAccessor.value;\n if (isSignal(v)) return this._stringify((v as Signal<unknown>)());\n return this._stringify(v);\n }\n return this.elementRef.nativeElement.value;\n }\n\n private _readInitialValue(): string {\n const el = this.elementRef.nativeElement;\n return el.value ?? '';\n }\n\n private _stringify(v: unknown): string {\n return v === null || v === undefined ? '' : String(v);\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-input.mjs","sources":["../../../projects/ngx-tw/input/input.ts","../../../projects/ngx-tw/input/cdevhub-ngx-tw-input.ts"],"sourcesContent":["import {\n booleanAttribute,\n computed,\n DestroyRef,\n Directive,\n effect,\n ElementRef,\n inject,\n InjectionToken,\n input,\n isDevMode,\n isSignal,\n type OnInit,\n signal,\n type Signal,\n untracked,\n type WritableSignal,\n} from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { NgControl } from '@angular/forms';\nimport { FocusMonitor } from '@angular/cdk/a11y';\nimport { AutofillMonitor } from '@angular/cdk/text-field';\nimport { Platform } from '@angular/cdk/platform';\nimport { tv } from 'tailwind-variants';\nimport {\n type ErrorStateMatcher,\n type TwSize,\n wireErrorState,\n} from '@cdevhub/ngx-tw/core';\nimport {\n FormFieldControl,\n TW_FORM_FIELD,\n TW_FORM_FIELD_CONTROL,\n} from '@cdevhub/ngx-tw/form-field';\n\n/**\n * Extension point for directives that wrap an `<input twInput>` and need to\n * own how its value is read and written. The directive's `value` property can\n * be a plain object slot (`{ value: T }`) or a `WritableSignal<T>`. Mirrors\n * Angular Material's `MAT_INPUT_VALUE_ACCESSOR`.\n */\nexport const TW_INPUT_VALUE_ACCESSOR = new InjectionToken<{\n value: unknown | WritableSignal<unknown>;\n}>('TW_INPUT_VALUE_ACCESSOR');\n\nconst INVALID_INPUT_TYPES = new Set([\n 'button',\n 'checkbox',\n 'file',\n 'hidden',\n 'image',\n 'radio',\n 'range',\n 'reset',\n 'submit',\n]);\n\nconst NEVER_EMPTY_TYPES = new Set([\n 'date',\n 'datetime-local',\n 'month',\n 'time',\n 'week',\n 'color',\n]);\n\nconst inputVariants = tv(\n {\n base: 'block w-full bg-transparent text-fg placeholder:text-fg-subtle outline-none',\n variants: {\n inFormField: {\n true: 'border-0 p-0 shadow-none focus:outline-none focus-visible:outline-none',\n false:\n 'rounded-md border border-border transition-[color,border-color,box-shadow] duration-normal motion-reduce:transition-none hover:border-border-strong focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-500',\n },\n // Height strategy for the standalone (`inFormField: false`) size scale —\n // see `docs/vertical-rhythm.md`. Driven by the host tag, not an input.\n //\n // `false` (a single-line `<input>`) pins the border-box height\n // (`h-6` … `h-12`) and deletes the vertical padding, so padding and\n // height cannot fight. Browsers centre an `<input>`'s text natively, so\n // no flex box is needed.\n //\n // `true` (a `<textarea>`) is multi-line by definition: the vertical\n // padding is retained and no height utility is emitted here. The\n // control-scale `min-h-*` floor is applied by `TextareaDirective`\n // instead, because it has to be droppable while CDK autosize owns the\n // height — see the `heightFloor` variant in `textarea.ts`.\n multiline: {\n true: '',\n false: '',\n },\n size: {\n xs: '',\n sm: '',\n md: '',\n lg: '',\n xl: '',\n },\n errorState: {\n true: '',\n false: '',\n },\n disabled: {\n true: 'opacity-50 cursor-not-allowed',\n false: '',\n },\n },\n compoundVariants: [\n // Standalone density × size — single-line: horizontal padding, font\n // scale, and the pinned control height (no vertical padding).\n { inFormField: false, multiline: false, size: 'xs', class: 'px-2 text-xs h-6' },\n { inFormField: false, multiline: false, size: 'sm', class: 'px-3 text-sm h-8' },\n { inFormField: false, multiline: false, size: 'md', class: 'px-4 text-sm h-9' },\n { inFormField: false, multiline: false, size: 'lg', class: 'px-5 text-base h-11' },\n { inFormField: false, multiline: false, size: 'xl', class: 'px-6 text-base h-12' },\n // Standalone density × size — multi-line: vertical padding is kept so the\n // box can grow; the height floor is applied by `TextareaDirective`.\n { inFormField: false, multiline: true, size: 'xs', class: 'px-2 py-1 text-xs' },\n { inFormField: false, multiline: true, size: 'sm', class: 'px-3 py-1.5 text-sm' },\n { inFormField: false, multiline: true, size: 'md', class: 'px-4 py-2 text-sm' },\n { inFormField: false, multiline: true, size: 'lg', class: 'px-5 py-2.5 text-base' },\n { inFormField: false, multiline: true, size: 'xl', class: 'px-6 py-3 text-base' },\n // Standalone error state — colored border and focus ring.\n {\n inFormField: false,\n errorState: true,\n class: 'border-error-500 focus-visible:outline-error-500',\n },\n ],\n defaultVariants: {\n inFormField: false,\n multiline: false,\n size: 'md',\n errorState: false,\n disabled: false,\n },\n },\n { twMerge: true },\n);\n\nlet nextInputId = 0;\n\n/**\n * Adapts a native `<input>` or `<textarea>` into an ngx-tw form-field-compatible\n * control. Works standalone (applies its own border and focus styling) or\n * inside a `<tw-form-field>` (strips its chrome and provides signals the\n * form-field uses to float the label and wire `aria-describedby`).\n *\n * Extension points:\n * - Implement a completely custom control → extend {@link FormFieldControl}\n * and provide under `TW_FORM_FIELD_CONTROL`.\n * - Swap value storage for an existing `<input twInput>` (masked input,\n * date parser, etc.) → provide {@link TW_INPUT_VALUE_ACCESSOR}.\n * - Change when errors show → override `TW_ERROR_STATE_MATCHER` at any\n * injector level, or pass `errorStateMatcher` per instance.\n *\n * Forms integration is inherited: Angular's native value accessors attach to\n * the underlying element, so the directive works with template-driven,\n * reactive, and signal-based forms without additional glue.\n */\n@Directive({\n selector: 'input[twInput], textarea[twInput]',\n exportAs: 'twInput',\n providers: [\n { provide: TW_FORM_FIELD_CONTROL, useExisting: InputDirective },\n ],\n host: {\n '[class]': 'classes()',\n '[attr.id]': 'id()',\n '[attr.type]': '_isTextarea ? null : type()',\n '[disabled]': 'disabled()',\n '[attr.aria-invalid]': 'errorState() || null',\n '[attr.aria-required]': 'required() || null',\n '(input)': '_onInput()',\n },\n})\nexport class InputDirective\n extends FormFieldControl<string>\n implements OnInit\n{\n private readonly elementRef = inject<\n ElementRef<HTMLInputElement | HTMLTextAreaElement>\n >(ElementRef);\n private readonly focusMonitor = inject(FocusMonitor);\n private readonly autofillMonitor = inject(AutofillMonitor);\n private readonly platform = inject(Platform);\n private readonly destroyRef = inject(DestroyRef);\n private readonly formField = inject(TW_FORM_FIELD, { optional: true });\n private readonly ngControl = inject(NgControl, {\n optional: true,\n self: true,\n });\n private readonly valueAccessor = inject(TW_INPUT_VALUE_ACCESSOR, {\n optional: true,\n self: true,\n });\n\n /** @internal */\n readonly _isTextarea =\n this.elementRef.nativeElement.tagName.toLowerCase() === 'textarea';\n\n /** Contract value — `'input'` for `<input>` and `'textarea'` for `<textarea>`. Form-field appends it as `tw-form-field-type-*` for styling hooks. */\n readonly controlType: string = this._isTextarea ? 'textarea' : 'input';\n\n private readonly uid = `tw-input-${nextInputId++}`;\n\n /** Id on the underlying element. Auto-generated as `tw-input-N` when not provided. Used by the form-field's `<label for>` attribute. */\n readonly idInput = input<string | undefined>(undefined, { alias: 'id' });\n\n /** Native HTML input `type`. Defaults to `'text'`. Dev-mode throws on unsupported values (`checkbox`, `radio`, `submit`, etc.) — use the dedicated component instead. Ignored on `<textarea>`. */\n readonly type = input<string>('text');\n\n /** Density of a standalone field. Maps to the pinned control-height scale (24/32/36/44/48px, `h-6` xs … `h-12` xl), the horizontal-padding scale (`px-2` xs … `px-6` xl), and the font scale (`text-xs` xs, `text-sm` sm/md, `text-base` lg/xl). On a `<textarea>` the height becomes a `min-h-*` floor and the vertical padding is retained. Ignored inside a `<tw-form-field>` — the wrapper's `size` carries density. Defaults to `'md'`. */\n readonly size = input<TwSize>('md');\n\n /** Disables the control. Also reflects `ngControl.disabled` when the element is bound to a reactive form. Defaults to `false`. */\n readonly disabledInput = input<boolean, unknown>(false, {\n alias: 'disabled',\n transform: booleanAttribute,\n });\n\n /** Marks the control as required. Also inferred from `Validators.required` on a bound `NgControl`. Defaults to `false`. */\n readonly requiredInput = input<boolean, unknown>(false, {\n alias: 'required',\n transform: booleanAttribute,\n });\n\n /** Makes the control read-only (native `readonly` attribute). Defaults to `false`. */\n readonly readonlyInput = input<boolean, unknown>(false, {\n alias: 'readonly',\n transform: booleanAttribute,\n });\n\n /** Per-instance override of the {@link ErrorStateMatcher}. When omitted, the directive uses the `TW_ERROR_STATE_MATCHER` token's value. */\n readonly errorStateMatcher = input<ErrorStateMatcher | undefined>(undefined);\n\n /** Consumer-supplied `aria-describedby` ids. The form-field preserves these when merging hint and error ids. Alias: `aria-describedby`. */\n override readonly userAriaDescribedBy = input<string | undefined>(undefined, {\n alias: 'aria-describedby',\n });\n\n /** Consumer-supplied `aria-labelledby` ids. The form-field preserves these when merging in the projected label id. Alias: `aria-labelledby`. */\n override readonly userAriaLabelledby = input<string | undefined>(undefined, {\n alias: 'aria-labelledby',\n });\n\n // ── Internal state signals ──\n\n private readonly _value = signal<string>(this._readInitialValue());\n private readonly _focused = signal(false);\n private readonly _autofilled = signal(false);\n\n /** @internal Shared `errorState` / `required` / `errors` derivation — see `wireErrorState`. */\n private readonly errorWiring = wireErrorState({\n ngControl: () => this.ngControl,\n matcher: () => this.errorStateMatcher(),\n required: () => this.requiredInput(),\n track: [() => this._focused()],\n });\n\n // ── FormFieldControl signals ──\n\n /** @internal */\n readonly id = computed(() => this.idInput() ?? this.uid);\n\n /** @internal */\n readonly value = computed<string | null>(() => this._value() || null);\n\n /** @internal */\n readonly focused = this._focused.asReadonly();\n\n /** @internal */\n readonly empty = computed(() => {\n if (!this._isTextarea && NEVER_EMPTY_TYPES.has(this.type())) {\n return false;\n }\n return !this._value() && !this._autofilled();\n });\n\n /** @internal */\n readonly disabled = computed(() => {\n this.errorWiring.rev();\n return this.disabledInput() || !!this.ngControl?.disabled;\n });\n\n /** @internal */\n readonly required = this.errorWiring.required;\n\n /** @internal */\n readonly errorState = this.errorWiring.errorState;\n\n /** @internal Active validation errors map from the bound `NgControl`, for `[twError match=\"…\"]` filtering. */\n override readonly errors = this.errorWiring.errors;\n\n /** @internal */\n readonly classes = computed(() =>\n inputVariants({\n inFormField: !!this.formField,\n multiline: this._isTextarea,\n size: this.size(),\n errorState: this.errorState(),\n disabled: this.disabled(),\n }),\n );\n\n constructor() {\n super();\n\n this.focusMonitor\n .monitor(this.elementRef)\n .pipe(takeUntilDestroyed())\n .subscribe((origin) => {\n const wasFocused = this._focused();\n this._focused.set(!!origin);\n if (wasFocused && !origin) {\n // Blur often flips `touched` on the bound `NgControl`; bump the\n // revision signal so `errorState` recomputes.\n this.errorWiring.bump();\n }\n });\n this.destroyRef.onDestroy(() =>\n this.focusMonitor.stopMonitoring(this.elementRef),\n );\n\n // Keep `_value` in sync when the accessor's value is itself a signal.\n if (this.valueAccessor && isSignal(this.valueAccessor.value)) {\n effect(() => {\n const v = (this.valueAccessor!.value as Signal<unknown>)();\n untracked(() => this._value.set(this._stringify(v)));\n });\n }\n\n // Sync the `readonly` input to the native attribute.\n effect(() => {\n const el = this.elementRef.nativeElement;\n if (this.readonlyInput()) {\n el.setAttribute('readonly', 'true');\n } else {\n el.removeAttribute('readonly');\n }\n });\n\n // Validate the input type in dev mode.\n effect(() => {\n const t = this.type();\n if (\n isDevMode() &&\n !this._isTextarea &&\n INVALID_INPUT_TYPES.has(t)\n ) {\n throw new Error(\n `tw-input: unsupported input type \"${t}\". Use a dedicated ngx-tw component for that control.`,\n );\n }\n });\n }\n\n ngOnInit(): void {\n if (this.platform.isBrowser) {\n this.autofillMonitor\n .monitor(this.elementRef.nativeElement)\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe((event) => this._autofilled.set(event.isAutofilled));\n this.destroyRef.onDestroy(() =>\n this.autofillMonitor.stopMonitoring(this.elementRef.nativeElement),\n );\n }\n\n // NgControl's `control` is set by the parent `FormControl*` directive's\n // own `ngOnChanges`, which runs before children's `ngOnInit`. Connect\n // here so `statusChanges`/`valueChanges` are actually available.\n this.errorWiring.connect();\n }\n\n /** Moves focus to the underlying element. */\n focus(options?: FocusOptions): void {\n this.elementRef.nativeElement.focus(options);\n }\n\n /** @internal Called when `<tw-form-field>` has computed the merged describedby ids. */\n setDescribedByIds(ids: string[]): void {\n const el = this.elementRef.nativeElement;\n if (ids.length) {\n el.setAttribute('aria-describedby', ids.join(' '));\n } else {\n el.removeAttribute('aria-describedby');\n }\n }\n\n /** @internal Called when `<tw-form-field>` has computed the merged labelledby ids. The native `<label for>` association covers most cases; this attribute is mainly useful when a consumer also supplies `aria-labelledby` for an external label. */\n override setLabelledByIds(ids: string[]): void {\n const el = this.elementRef.nativeElement;\n // Skip applying the projected label id by itself — native `<label for=>` already wires it\n // and a redundant `aria-labelledby` can cause double-announcements in some screen readers.\n // Only apply when the consumer supplied additional ids via `aria-labelledby`.\n const userIds = this.userAriaLabelledby();\n if (userIds || (ids.length > 1)) {\n el.setAttribute('aria-labelledby', ids.join(' '));\n } else {\n el.removeAttribute('aria-labelledby');\n }\n }\n\n /** @internal Called when the form-field container is clicked. */\n onContainerClick(_event: MouseEvent): void {\n if (!this._focused()) {\n this.focus();\n }\n }\n\n /** @internal */\n _onInput(): void {\n this._value.set(this._currentValue());\n }\n\n private _currentValue(): string {\n if (this.valueAccessor) {\n const v = this.valueAccessor.value;\n if (isSignal(v)) return this._stringify((v as Signal<unknown>)());\n return this._stringify(v);\n }\n return this.elementRef.nativeElement.value;\n }\n\n private _readInitialValue(): string {\n const el = this.elementRef.nativeElement;\n return el.value ?? '';\n }\n\n private _stringify(v: unknown): string {\n return v === null || v === undefined ? '' : String(v);\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
@@ -16,16 +16,29 @@ const itemVariants = tv({
|
|
|
16
16
|
trailing: 'flex shrink-0',
|
|
17
17
|
},
|
|
18
18
|
variants: {
|
|
19
|
+
// `tw-item` is on the `min-h-*` list (docs/vertical-rhythm.md §2), not the
|
|
20
|
+
// pinned one: a title + description is a two-line stack, so the box must
|
|
21
|
+
// stay free to grow. `py-*` is therefore kept exactly as it is.
|
|
22
|
+
//
|
|
23
|
+
// The three-step `ItemSize` axis maps onto the control scale by its own
|
|
24
|
+
// single-line natural height, so each floor binds at the size it belongs
|
|
25
|
+
// to rather than being decorative:
|
|
26
|
+
// sm → 32 (`min-h-8`) — py-1.5 (12) + text-sm line box (20)
|
|
27
|
+
// md → 36 (`min-h-9`) — py-2 (16) + text-sm line box (20)
|
|
28
|
+
// lg → 48 (`min-h-12`) — py-3 (24) + text-base line box (24)
|
|
29
|
+
// The scale's 24px (`xs`) step sits below `tw-item`'s densest row and its
|
|
30
|
+
// 44px (`lg`) step sits below the section-header line box, so both are
|
|
31
|
+
// skipped: `lg` takes the 48px step it already measures.
|
|
19
32
|
size: {
|
|
20
33
|
sm: {
|
|
21
|
-
root: 'gap-2 py-1.5',
|
|
34
|
+
root: 'gap-2 py-1.5 min-h-8',
|
|
22
35
|
content: 'gap-0',
|
|
23
36
|
title: 'text-sm truncate',
|
|
24
37
|
description: 'text-xs truncate',
|
|
25
38
|
trailing: 'gap-1.5',
|
|
26
39
|
},
|
|
27
40
|
md: {
|
|
28
|
-
root: 'gap-3 py-2',
|
|
41
|
+
root: 'gap-3 py-2 min-h-9',
|
|
29
42
|
leading: 'mt-0.5',
|
|
30
43
|
content: 'gap-1',
|
|
31
44
|
title: 'text-sm',
|
|
@@ -33,7 +46,7 @@ const itemVariants = tv({
|
|
|
33
46
|
trailing: 'gap-2',
|
|
34
47
|
},
|
|
35
48
|
lg: {
|
|
36
|
-
root: 'gap-3 py-3',
|
|
49
|
+
root: 'gap-3 py-3 min-h-12',
|
|
37
50
|
leading: 'mt-0.5',
|
|
38
51
|
content: 'gap-1',
|
|
39
52
|
title: 'text-base font-semibold',
|
|
@@ -66,7 +79,16 @@ const itemVariants = tv({
|
|
|
66
79
|
},
|
|
67
80
|
current: {
|
|
68
81
|
true: {
|
|
69
|
-
|
|
82
|
+
// `-border-strong`, not `-border`. This ring IS the selection
|
|
83
|
+
// indicator, so WCAG 2.2 SC 1.4.11 applies to it against the
|
|
84
|
+
// `bg-primary-soft` fill it sits on — and the subtle tier does not
|
|
85
|
+
// clear 3:1 there in every scheme (measured 2026-09-04: 7 of 8 roles
|
|
86
|
+
// failed in `light`, 4 of 8 in `dark`). The theme layer cannot fix
|
|
87
|
+
// that: raising `-border` far enough collapses it onto
|
|
88
|
+
// `-border-strong`, which is the tier already sized for rings.
|
|
89
|
+
// `theme-contrast.spec.ts` asserts the `-border-strong`-on-`-soft`
|
|
90
|
+
// pairing for every role in every scheme.
|
|
91
|
+
root: 'bg-primary-soft ring-2 ring-inset ring-primary-border-strong',
|
|
70
92
|
},
|
|
71
93
|
false: {},
|
|
72
94
|
},
|
|
@@ -134,11 +156,13 @@ class ItemComponent {
|
|
|
134
156
|
this.focusMonitor.stopMonitoring(this.elementRef);
|
|
135
157
|
});
|
|
136
158
|
}
|
|
159
|
+
/** @internal Emits `selected` on click when the item is interactive and enabled. */
|
|
137
160
|
onActivate(event) {
|
|
138
161
|
if (!this.interactive() || this.disabled())
|
|
139
162
|
return;
|
|
140
163
|
this.selected.emit(event);
|
|
141
164
|
}
|
|
165
|
+
/** @internal Emits `selected` on Enter/Space when the item is interactive and enabled. */
|
|
142
166
|
onKeydown(event) {
|
|
143
167
|
if (!this.interactive() || this.disabled())
|
|
144
168
|
return;
|