@cdevhub/ngx-tw 0.5.0 → 0.6.0
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 +18 -3
- 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 +110 -36
- package/theme/_high-contrast-dark.css +367 -0
- package/theme/_high-contrast.css +13 -2
- package/theme/_light.css +276 -0
- package/theme/_semantic.css +47 -12
- 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
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
2
|
import { inject, ElementRef, signal, Directive, viewChild, viewChildren, computed, afterRenderEffect, ChangeDetectionStrategy, Component, input, booleanAttribute, model, output, ViewContainerRef, Injector, DestroyRef, linkedSignal, effect, untracked, afterNextRender, isDevMode, forwardRef } from '@angular/core';
|
|
3
|
-
import {
|
|
3
|
+
import { NgControl, NG_VALIDATORS, NG_VALUE_ACCESSOR } from '@angular/forms';
|
|
4
4
|
import { Overlay } from '@angular/cdk/overlay';
|
|
5
5
|
import { FocusMonitor, LiveAnnouncer } from '@angular/cdk/a11y';
|
|
6
|
-
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
7
|
-
import { merge } from 'rxjs';
|
|
8
6
|
import { tv } from 'tailwind-variants';
|
|
9
|
-
import { PickerOverlayCoordinator,
|
|
7
|
+
import { PickerOverlayCoordinator, wireErrorState, resolveSelectScrollStrategy, buildSelectLikePositions } from '@cdevhub/ngx-tw/core';
|
|
10
8
|
import { FormFieldControl, TW_FORM_FIELD, TW_FORM_FIELD_CONTROL } from '@cdevhub/ngx-tw/form-field';
|
|
11
9
|
import { TwDateRange, CalendarComponent, provideRangeSelectionStrategy, DATE_ADAPTER, calendarValidator } from '@cdevhub/ngx-tw/calendar';
|
|
12
10
|
import { ButtonDirective } from '@cdevhub/ngx-tw/button';
|
|
@@ -74,6 +72,9 @@ class DateRangePickerOverlayComponent {
|
|
|
74
72
|
/** @internal */
|
|
75
73
|
startView = signal('day', /* @ts-ignore */
|
|
76
74
|
...(ngDevMode ? [{ debugName: "startView" }] : /* istanbul ignore next */ []));
|
|
75
|
+
/** @internal Mirrors the host's `startAt`. Anchors the calendar when no range is pending. */
|
|
76
|
+
startAt = signal(null, /* @ts-ignore */
|
|
77
|
+
...(ngDevMode ? [{ debugName: "startAt" }] : /* istanbul ignore next */ []));
|
|
77
78
|
/** @internal */
|
|
78
79
|
numberOfMonths = signal(2, /* @ts-ignore */
|
|
79
80
|
...(ngDevMode ? [{ debugName: "numberOfMonths" }] : /* istanbul ignore next */ []));
|
|
@@ -213,10 +214,18 @@ class DateRangePickerOverlayComponent {
|
|
|
213
214
|
return v ? { start: v.start, end: v.end } : { start: null, end: null };
|
|
214
215
|
}, /* @ts-ignore */
|
|
215
216
|
...(ngDevMode ? [{ debugName: "pendingRangeAsRange" }] : /* istanbul ignore next */ []));
|
|
216
|
-
/**
|
|
217
|
+
/**
|
|
218
|
+
* @internal Seeds the calendar's active month on first attach so it anchors to
|
|
219
|
+
* the current value rather than today.
|
|
220
|
+
*
|
|
221
|
+
* A pending range wins: once the user has a selection, the calendar should
|
|
222
|
+
* follow it. `startAt` is the fallback for an empty picker, which is the case
|
|
223
|
+
* the host input exists for — it was declared but never forwarded here, so a
|
|
224
|
+
* consumer setting it got silence and the calendar always opened on today.
|
|
225
|
+
*/
|
|
217
226
|
initialStartAt = computed(() => {
|
|
218
227
|
const v = this.pendingRange();
|
|
219
|
-
return v?.start ??
|
|
228
|
+
return v?.start ?? this.startAt();
|
|
220
229
|
}, /* @ts-ignore */
|
|
221
230
|
...(ngDevMode ? [{ debugName: "initialStartAt" }] : /* istanbul ignore next */ []));
|
|
222
231
|
// ── Template handlers ──
|
|
@@ -461,7 +470,7 @@ class DateRangePickerOverlayComponent {
|
|
|
461
470
|
</div>
|
|
462
471
|
}
|
|
463
472
|
</div>
|
|
464
|
-
`, isInline: true, dependencies: [{ kind: "component", type: CalendarComponent, selector: "tw-calendar", inputs: ["mode", "value", "startAt", "minDate", "maxDate", "dateFilter", "disabledDates", "disabledDaysOfWeek", "constraints", "minRangeLength", "maxRangeLength", "maxSelections", "maxSelectionBehavior", "errorAriaDescribedBy", "rangeClickBehavior", "rangeBehavior", "dateClass", "bordered", "startView", "firstDayOfWeek", "monthColumns", "disabled", "readonly", "resetBehavior", "cellTemplate", "locale", "intl"], outputs: ["valueChange", "selectionStart", "rangePreview", "selectionComplete", "selectionRestart", "selectionCleared", "selectionLimitReached", "presetChange", "viewChange", "activeDateChange", "monthChange", "yearChange", "cellClick", "cellHover", "modeChange"] }, { kind: "component", type: TimePickerComponent, selector: "tw-time-picker", inputs: ["id", "disabled", "required", "readonly", "size", "color", "variant", "format", "showSeconds", "hourStep", "minuteStep", "secondStep", "minTime", "maxTime", "referenceDate", "placeholder", "showSteppers", "showClear", "clearLabel", "errorStateMatcher", "aria-label", "aria-labelledby", "aria-describedby", "value"], outputs: ["valueChange", "timeInput", "timeChange"] }, { kind: "directive", type: ButtonDirective, selector: "[twButton]", inputs: ["variant", "color", "size", "disabled", "loading"], exportAs: ["twButton"] }, { kind: "directive", type: DateRangePresetOptionDirective, selector: "button[twDateRangePresetOption]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
473
|
+
`, isInline: true, dependencies: [{ kind: "component", type: CalendarComponent, selector: "tw-calendar", inputs: ["mode", "value", "startAt", "minDate", "maxDate", "dateFilter", "disabledDates", "disabledDaysOfWeek", "constraints", "minRangeLength", "maxRangeLength", "maxSelections", "maxSelectionBehavior", "errorAriaDescribedBy", "aria-describedby", "rangeClickBehavior", "rangeBehavior", "dateClass", "bordered", "startView", "firstDayOfWeek", "monthColumns", "disabled", "readonly", "resetBehavior", "cellTemplate", "locale", "intl"], outputs: ["valueChange", "selectionStart", "rangePreview", "selectionComplete", "selectionRestart", "selectionCleared", "selectionLimitReached", "presetChange", "viewChange", "activeDateChange", "monthChange", "yearChange", "cellClick", "cellHover", "modeChange"] }, { kind: "component", type: TimePickerComponent, selector: "tw-time-picker", inputs: ["id", "disabled", "required", "readonly", "size", "color", "variant", "format", "showSeconds", "hourStep", "minuteStep", "secondStep", "minTime", "maxTime", "referenceDate", "placeholder", "showSteppers", "showClear", "clearLabel", "errorStateMatcher", "aria-label", "aria-labelledby", "aria-describedby", "value"], outputs: ["valueChange", "timeInput", "timeChange"] }, { kind: "directive", type: ButtonDirective, selector: "[twButton]", inputs: ["variant", "color", "size", "disabled", "loading"], exportAs: ["twButton"] }, { kind: "directive", type: DateRangePresetOptionDirective, selector: "button[twDateRangePresetOption]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
465
474
|
}
|
|
466
475
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: DateRangePickerOverlayComponent, decorators: [{
|
|
467
476
|
type: Component,
|
|
@@ -626,7 +635,11 @@ function rangesEqual(a, b, adapter) {
|
|
|
626
635
|
const dateRangePickerVariants = tv({
|
|
627
636
|
slots: {
|
|
628
637
|
root: 'relative inline-flex items-center w-full text-fg transition-[color,border-color,box-shadow] duration-normal motion-reduce:transition-none',
|
|
629
|
-
|
|
638
|
+
// `self-stretch` so the trigger — the component's only interactive surface —
|
|
639
|
+
// fills the pinned control height. Without it the button is exactly its line
|
|
640
|
+
// box tall and everything above/below it inside the shell is dead click area
|
|
641
|
+
// (up to 22px at xl).
|
|
642
|
+
trigger: 'flex-1 inline-flex items-center gap-2 min-w-0 self-stretch bg-transparent text-left text-fg outline-none border-0 p-0 m-0 cursor-pointer focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-500 disabled:cursor-not-allowed',
|
|
630
643
|
startText: 'truncate',
|
|
631
644
|
separator: 'shrink-0 text-fg-subtle',
|
|
632
645
|
endText: 'truncate',
|
|
@@ -634,6 +647,11 @@ const dateRangePickerVariants = tv({
|
|
|
634
647
|
triggerIconWrapper: 'inline-flex items-center justify-center shrink-0 text-fg-muted',
|
|
635
648
|
triggerIcon: 'shrink-0 text-fg-muted transition-colors duration-normal motion-reduce:transition-none',
|
|
636
649
|
// size-6 (24×24 CSS px) is the WCAG AA minimum interactive target — bumping past size-5 keeps the affordance hittable without growing the inline row.
|
|
650
|
+
// Held flat across every size. It fits the 30/34/42/46px content boxes at sm..xl. At xs the
|
|
651
|
+
// pinned root is h-6 (24px border-box) so its content box is only 22px: the button is kept at
|
|
652
|
+
// 24×24 anyway — docs/vertical-rhythm.md calls 24px a hard floor — and therefore coincides with
|
|
653
|
+
// the root's *border*-box, overlapping the 1px border row top and bottom. The root's height is
|
|
654
|
+
// unaffected (h-6 is a definite height); only the hover fill meets the border at that density.
|
|
637
655
|
clearButton: 'inline-flex items-center justify-center shrink-0 rounded-md text-fg-muted hover:text-fg hover:bg-surface-muted transition-colors duration-normal motion-reduce:transition-none size-6 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-500',
|
|
638
656
|
},
|
|
639
657
|
variants: {
|
|
@@ -663,7 +681,10 @@ const dateRangePickerVariants = tv({
|
|
|
663
681
|
},
|
|
664
682
|
variant: {
|
|
665
683
|
default: {
|
|
666
|
-
|
|
684
|
+
// No `py-*`: the height is pinned per size in compoundVariants below.
|
|
685
|
+
// See docs/vertical-rhythm.md §3 — padding and a pinned height fight,
|
|
686
|
+
// and under border-box the taller one silently wins.
|
|
687
|
+
root: 'rounded-md border border-border bg-surface px-3 hover:border-border-strong',
|
|
667
688
|
},
|
|
668
689
|
naked: {
|
|
669
690
|
root: 'bg-transparent border-0 rounded-none p-0 focus-within:outline-none',
|
|
@@ -688,6 +709,16 @@ const dateRangePickerVariants = tv({
|
|
|
688
709
|
},
|
|
689
710
|
},
|
|
690
711
|
compoundVariants: [
|
|
712
|
+
// ── Pinned control height (docs/vertical-rhythm.md §1) ──
|
|
713
|
+
// Only the `default` variant carries a height. `naked` means somebody else
|
|
714
|
+
// owns the box — `<tw-form-field>` auto-nakeds this picker and supplies its
|
|
715
|
+
// own bordered, padded controlWrapper, so pinning naked would stack 36px on
|
|
716
|
+
// top of the form-field's `py-2` and inflate a wrapped picker at every size.
|
|
717
|
+
{ variant: 'default', size: 'xs', class: { root: 'h-6' } },
|
|
718
|
+
{ variant: 'default', size: 'sm', class: { root: 'h-8' } },
|
|
719
|
+
{ variant: 'default', size: 'md', class: { root: 'h-9' } },
|
|
720
|
+
{ variant: 'default', size: 'lg', class: { root: 'h-11' } },
|
|
721
|
+
{ variant: 'default', size: 'xl', class: { root: 'h-12' } },
|
|
691
722
|
{ variant: 'default', focused: true, color: 'primary', class: { root: 'border-primary-500' } },
|
|
692
723
|
{ variant: 'default', focused: true, color: 'secondary', class: { root: 'border-secondary-500' } },
|
|
693
724
|
{ variant: 'default', focused: true, color: 'accent', class: { root: 'border-accent-500' } },
|
|
@@ -732,13 +763,13 @@ class DateRangePickerComponent extends FormFieldControl {
|
|
|
732
763
|
/** Latest selectable date for either endpoint. Presets and calendar cells later than this are rejected or disabled. Defaults to `null`. */
|
|
733
764
|
maxDate = input(null, /* @ts-ignore */
|
|
734
765
|
...(ngDevMode ? [{ debugName: "maxDate" }] : /* istanbul ignore next */ []));
|
|
735
|
-
/** Per-date predicate — return `false` to disable. Applied in both calendars. Presets that fall on a filtered date are skipped. */
|
|
766
|
+
/** Per-date predicate — return `false` to disable. Applied in both calendars. Presets that fall on a filtered date are skipped. Defaults to `null`. */
|
|
736
767
|
dateFilter = input(null, /* @ts-ignore */
|
|
737
768
|
...(ngDevMode ? [{ debugName: "dateFilter" }] : /* istanbul ignore next */ []));
|
|
738
769
|
/** Which calendar view opens first — `'day'`, `'month'`, or `'year'`. Defaults to `'day'`. */
|
|
739
770
|
startView = input('day', /* @ts-ignore */
|
|
740
771
|
...(ngDevMode ? [{ debugName: "startView" }] : /* istanbul ignore next */ []));
|
|
741
|
-
/** Date the left calendar focuses on when opened with no value. Falls back to today. Ignored when a value is already set. */
|
|
772
|
+
/** Date the left calendar focuses on when opened with no value. Falls back to today. Ignored when a value is already set. Defaults to `null`. */
|
|
742
773
|
startAt = input(null, /* @ts-ignore */
|
|
743
774
|
...(ngDevMode ? [{ debugName: "startAt" }] : /* istanbul ignore next */ []));
|
|
744
775
|
/** Display format for each endpoint, passed to `DateAdapter.format()`. When `showTime` is true and this is left at the default, hour/minute (and optional seconds) are folded in automatically. */
|
|
@@ -747,13 +778,13 @@ class DateRangePickerComponent extends FormFieldControl {
|
|
|
747
778
|
/** Separator rendered between the two formatted endpoints in the trigger. Defaults to `" – "`. */
|
|
748
779
|
rangeSeparator = input(' – ', /* @ts-ignore */
|
|
749
780
|
...(ngDevMode ? [{ debugName: "rangeSeparator" }] : /* istanbul ignore next */ []));
|
|
750
|
-
/** Placeholder text shown in the trigger for an empty `start` endpoint. */
|
|
781
|
+
/** Placeholder text shown in the trigger for an empty `start` endpoint. Defaults to `'Start date'`. */
|
|
751
782
|
emptyStartLabel = input('Start date', /* @ts-ignore */
|
|
752
783
|
...(ngDevMode ? [{ debugName: "emptyStartLabel" }] : /* istanbul ignore next */ []));
|
|
753
|
-
/** Placeholder text shown in the trigger for an empty `end` endpoint. */
|
|
784
|
+
/** Placeholder text shown in the trigger for an empty `end` endpoint. Defaults to `'End date'`. */
|
|
754
785
|
emptyEndLabel = input('End date', /* @ts-ignore */
|
|
755
786
|
...(ngDevMode ? [{ debugName: "emptyEndLabel" }] : /* istanbul ignore next */ []));
|
|
756
|
-
/** When set, overrides the composed `${emptyStartLabel}${rangeSeparator}${emptyEndLabel}` placeholder with a single string. */
|
|
787
|
+
/** When set, overrides the composed `${emptyStartLabel}${rangeSeparator}${emptyEndLabel}` placeholder with a single string. Defaults to `undefined`. */
|
|
757
788
|
placeholder = input(undefined, /* @ts-ignore */
|
|
758
789
|
...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
|
|
759
790
|
/** When true, the trigger cannot open the overlay and `aria-disabled="true"` is set. Defaults to `false`. */
|
|
@@ -774,11 +805,10 @@ class DateRangePickerComponent extends FormFieldControl {
|
|
|
774
805
|
/** How many months the overlay shows side-by-side. `2` is the standard range-picker layout; use `1` for compact contexts. Defaults to `2`. */
|
|
775
806
|
numberOfMonths = input(2, /* @ts-ignore */
|
|
776
807
|
...(ngDevMode ? [{ debugName: "numberOfMonths" }] : /* istanbul ignore next */ []));
|
|
777
|
-
/** Optional quick-select presets rendered as a vertical list before the calendars. Each preset provides a label and a factory returning a `TwDateRange<D>`. An empty array hides the preset panel. */
|
|
808
|
+
/** Optional quick-select presets rendered as a vertical list before the calendars. Each preset provides a label and a factory returning a `TwDateRange<D>`. An empty array hides the preset panel. Defaults to an empty array. */
|
|
778
809
|
presets = input([], /* @ts-ignore */
|
|
779
810
|
...(ngDevMode ? [{ debugName: "presets" }] : /* istanbul ignore next */ []));
|
|
780
|
-
/** Whether to show a clear-button affordance inside the trigger when a value is set. Defaults to `true
|
|
781
|
-
// TRUE-default: a picker without a clear affordance forces consumers to wire one — most form pickers expect the inline clear, matching `<tw-input>`'s clear button.
|
|
811
|
+
/** Whether to show a clear-button affordance inside the trigger when a value is set. Defaults to `true` — most form pickers expect the inline clear, matching `<tw-input>`'s clear button; without it consumers must wire one themselves. */
|
|
782
812
|
showClear = input(true, /* @ts-ignore */
|
|
783
813
|
...(ngDevMode ? [{ debugName: "showClear" }] : /* istanbul ignore next */ []));
|
|
784
814
|
/** Minimum range length in days, inclusive. Commits shorter than this are rejected and surface `calendarRangeTooShort` on the bound `NgControl`. `null` = no minimum. Defaults to `null`. */
|
|
@@ -800,13 +830,13 @@ class DateRangePickerComponent extends FormFieldControl {
|
|
|
800
830
|
/** Override first day of week (0=Sun, 1=Mon) on the embedded calendar. Falls back to the adapter's default. */
|
|
801
831
|
firstDayOfWeek = input(null, /* @ts-ignore */
|
|
802
832
|
...(ngDevMode ? [{ debugName: "firstDayOfWeek" }] : /* istanbul ignore next */ []));
|
|
803
|
-
/** Per-instance locale override. Forwarded to the embedded calendar and the underlying `DateAdapter` so the trigger display tracks the picker's locale. Falls back to Angular `LOCALE_ID` when `null`. */
|
|
833
|
+
/** Per-instance locale override. Forwarded to the embedded calendar and the underlying `DateAdapter` so the trigger display tracks the picker's locale. Falls back to Angular `LOCALE_ID` when `null`. Defaults to `null`. */
|
|
804
834
|
locale = input(null, /* @ts-ignore */
|
|
805
835
|
...(ngDevMode ? [{ debugName: "locale" }] : /* istanbul ignore next */ []));
|
|
806
|
-
/** Function producing per-cell CSS classes on the embedded calendar. */
|
|
836
|
+
/** Function producing per-cell CSS classes on the embedded calendar. Defaults to `null`. */
|
|
807
837
|
dateClass = input(null, /* @ts-ignore */
|
|
808
838
|
...(ngDevMode ? [{ debugName: "dateClass" }] : /* istanbul ignore next */ []));
|
|
809
|
-
/** Optional cell-content template, forwarded to the embedded calendar. Use to customize cell visuals beyond `dateClass`. */
|
|
839
|
+
/** Optional cell-content template, forwarded to the embedded calendar. Use to customize cell visuals beyond `dateClass`. Defaults to `null`. */
|
|
810
840
|
cellTemplate = input(null, /* @ts-ignore */
|
|
811
841
|
...(ngDevMode ? [{ debugName: "cellTemplate" }] : /* istanbul ignore next */ []));
|
|
812
842
|
/** When true, renders a `Today / Clear / Cancel / Apply` action bar at the bottom of the overlay. The calendar commits on the second click by default — turn this on for touch-heavy contexts. Defaults to `false`. */
|
|
@@ -830,19 +860,19 @@ class DateRangePickerComponent extends FormFieldControl {
|
|
|
830
860
|
/** Step for the embedded time-pickers' second fields. Defaults to `1`. */
|
|
831
861
|
secondStep = input(1, /* @ts-ignore */
|
|
832
862
|
...(ngDevMode ? [{ debugName: "secondStep" }] : /* istanbul ignore next */ []));
|
|
833
|
-
/** Label for the `Today` action in the overlay's action bar. */
|
|
863
|
+
/** Label for the `Today` action in the overlay's action bar. Defaults to `'Today'`. */
|
|
834
864
|
todayLabel = input('Today', /* @ts-ignore */
|
|
835
865
|
...(ngDevMode ? [{ debugName: "todayLabel" }] : /* istanbul ignore next */ []));
|
|
836
|
-
/** Label for the `Clear` action in the overlay's action bar. */
|
|
866
|
+
/** Label for the `Clear` action in the overlay's action bar. Defaults to `'Clear'`. */
|
|
837
867
|
clearLabel = input('Clear', /* @ts-ignore */
|
|
838
868
|
...(ngDevMode ? [{ debugName: "clearLabel" }] : /* istanbul ignore next */ []));
|
|
839
|
-
/** Label for the `Cancel` action in the overlay's action bar. */
|
|
869
|
+
/** Label for the `Cancel` action in the overlay's action bar. Defaults to `'Cancel'`. */
|
|
840
870
|
cancelLabel = input('Cancel', /* @ts-ignore */
|
|
841
871
|
...(ngDevMode ? [{ debugName: "cancelLabel" }] : /* istanbul ignore next */ []));
|
|
842
|
-
/** Label for the `Apply` action in the overlay's action bar. */
|
|
872
|
+
/** Label for the `Apply` action in the overlay's action bar. Defaults to `'Apply'`. */
|
|
843
873
|
applyLabel = input('Apply', /* @ts-ignore */
|
|
844
874
|
...(ngDevMode ? [{ debugName: "applyLabel" }] : /* istanbul ignore next */ []));
|
|
845
|
-
/** Extra class(es) applied to the overlay panel element. `twMerge` resolves conflicts with internal classes. */
|
|
875
|
+
/** Extra class(es) applied to the overlay panel element. `twMerge` resolves conflicts with internal classes. Defaults to an empty string. */
|
|
846
876
|
panelClass = input('', /* @ts-ignore */
|
|
847
877
|
...(ngDevMode ? [{ debugName: "panelClass" }] : /* istanbul ignore next */ []));
|
|
848
878
|
/** CDK scroll strategy for the overlay. Defaults to `'reposition'`. */
|
|
@@ -854,20 +884,20 @@ class DateRangePickerComponent extends FormFieldControl {
|
|
|
854
884
|
/** Accessible label for the clear button. Defaults to `'Clear date range'`. */
|
|
855
885
|
clearAriaLabel = input('Clear date range', /* @ts-ignore */
|
|
856
886
|
...(ngDevMode ? [{ debugName: "clearAriaLabel" }] : /* istanbul ignore next */ []));
|
|
857
|
-
/** Per-instance override of the `ErrorStateMatcher`. When omitted, uses the injected `TW_ERROR_STATE_MATCHER`. */
|
|
887
|
+
/** Per-instance override of the `ErrorStateMatcher`. When omitted, uses the injected `TW_ERROR_STATE_MATCHER`. Defaults to `undefined`. */
|
|
858
888
|
errorStateMatcher = input(undefined, /* @ts-ignore */
|
|
859
889
|
...(ngDevMode ? [{ debugName: "errorStateMatcher" }] : /* istanbul ignore next */ []));
|
|
860
|
-
/** Accessible name for the trigger. Required when no visible label is supplied via `tw-form-field` or an external `aria-labelledby`. Alias: `aria-label`. */
|
|
890
|
+
/** Accessible name for the trigger. Required when no visible label is supplied via `tw-form-field` or an external `aria-labelledby`. Alias: `aria-label`. Defaults to `undefined`. */
|
|
861
891
|
ariaLabel = input(undefined, { ...(ngDevMode ? { debugName: "ariaLabel" } : /* istanbul ignore next */ {}), alias: 'aria-label' });
|
|
862
|
-
/** ID of an external element that labels the trigger. Alias: `aria-labelledby`. */
|
|
892
|
+
/** ID of an external element that labels the trigger. Alias: `aria-labelledby`. Defaults to `undefined`. */
|
|
863
893
|
ariaLabelledby = input(undefined, { ...(ngDevMode ? { debugName: "ariaLabelledby" } : /* istanbul ignore next */ {}), alias: 'aria-labelledby' });
|
|
864
|
-
/** Consumer-supplied `aria-describedby` ids. The form-field preserves these when merging hint/error ids. Alias: `aria-describedby`. */
|
|
894
|
+
/** Consumer-supplied `aria-describedby` ids. The form-field preserves these when merging hint/error ids. Alias: `aria-describedby`. Defaults to `undefined`. */
|
|
865
895
|
userAriaDescribedByInput = input(undefined, { ...(ngDevMode ? { debugName: "userAriaDescribedByInput" } : /* istanbul ignore next */ {}), alias: 'aria-describedby' });
|
|
866
896
|
// ── Models (two-way) ──
|
|
867
|
-
/** Two-way bound selected range. `null` when no selection. Setting programmatically updates the trigger display and the calendar selection; it does NOT trigger `onChange`. */
|
|
897
|
+
/** Two-way bound selected range. `null` when no selection. Setting programmatically updates the trigger display and the calendar selection; it does NOT trigger `onChange`. Defaults to `null`. */
|
|
868
898
|
value = model(null, /* @ts-ignore */
|
|
869
899
|
...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
870
|
-
/** Two-way bound open state of the overlay. Setting to `true` opens; setting to `false` closes. */
|
|
900
|
+
/** Two-way bound open state of the overlay. Setting to `true` opens; setting to `false` closes. Defaults to `false`. */
|
|
871
901
|
open = model(false, /* @ts-ignore */
|
|
872
902
|
...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
|
|
873
903
|
// ── Outputs ──
|
|
@@ -895,9 +925,6 @@ class DateRangePickerComponent extends FormFieldControl {
|
|
|
895
925
|
// constructor pulls the validator set — eager `inject(NgControl, {self})`
|
|
896
926
|
// would resolve to this component while it is still being created).
|
|
897
927
|
ngControl = null;
|
|
898
|
-
parentForm = inject(NgForm, { optional: true });
|
|
899
|
-
parentFormGroup = inject(FormGroupDirective, { optional: true });
|
|
900
|
-
defaultMatcher = inject(TW_ERROR_STATE_MATCHER);
|
|
901
928
|
// ── View refs ──
|
|
902
929
|
triggerRef = viewChild.required('trigger', /* @ts-ignore */
|
|
903
930
|
...(ngDevMode ? [{ debugName: "triggerRef" }] : /* istanbul ignore next */ []));
|
|
@@ -924,29 +951,41 @@ class DateRangePickerComponent extends FormFieldControl {
|
|
|
924
951
|
...(ngDevMode ? [{ debugName: "cvaDisabled" }] : /* istanbul ignore next */ []));
|
|
925
952
|
describedByIdsSignal = signal([], /* @ts-ignore */
|
|
926
953
|
...(ngDevMode ? [{ debugName: "describedByIdsSignal" }] : /* istanbul ignore next */ []));
|
|
927
|
-
closingSignal = signal(false, /* @ts-ignore */
|
|
928
|
-
...(ngDevMode ? [{ debugName: "closingSignal" }] : /* istanbul ignore next */ []));
|
|
929
954
|
lastValueBeforeOpen = signal(null, /* @ts-ignore */
|
|
930
955
|
...(ngDevMode ? [{ debugName: "lastValueBeforeOpen" }] : /* istanbul ignore next */ []));
|
|
931
956
|
pendingRange = signal(null, /* @ts-ignore */
|
|
932
957
|
...(ngDevMode ? [{ debugName: "pendingRange" }] : /* istanbul ignore next */ []));
|
|
933
958
|
currentView = signal('day', /* @ts-ignore */
|
|
934
959
|
...(ngDevMode ? [{ debugName: "currentView" }] : /* istanbul ignore next */ []));
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
960
|
+
/** @internal Shared `errorState` / `required` / `errors` derivation — see `wireErrorState`. */
|
|
961
|
+
errorWiring = wireErrorState({
|
|
962
|
+
ngControl: () => this.ngControl,
|
|
963
|
+
matcher: () => this.errorStateMatcher(),
|
|
964
|
+
required: () => this.requiredInput(),
|
|
965
|
+
track: [() => this.focusedSignal()],
|
|
966
|
+
// An unparseable entry or an out-of-range date is an error regardless of validator status.
|
|
967
|
+
errorStateOverride: () => (this.parseError() || this.rangeError() ? true : undefined),
|
|
968
|
+
});
|
|
939
969
|
onChange = () => { };
|
|
940
970
|
onTouched = () => { };
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
971
|
+
// Overlay lifecycle bookkeeping. Both are PLAIN FIELDS, not signals, and that
|
|
972
|
+
// is load-bearing: the lifecycle effect in the constructor both reads and
|
|
973
|
+
// writes them, so as signals they would form a read → write → re-trigger
|
|
974
|
+
// cycle of exactly the shape CLAUDE.md forbids. Demoting them is the fix
|
|
975
|
+
// `popover.ts` and `command-palette.ts` already use — see the comment on that
|
|
976
|
+
// effect. Do not promote either back to `signal()`.
|
|
977
|
+
overlayInstance = null;
|
|
978
|
+
closing = false;
|
|
979
|
+
// The one piece of overlay state that must stay a signal: it is the *only*
|
|
980
|
+
// dependency that re-runs the overlay state-push effect when the panel
|
|
981
|
+
// attaches. Written in openOverlay / closeOverlay, never read by the
|
|
982
|
+
// lifecycle effect, so it cannot cycle. Mirrors `command-palette.ts`.
|
|
983
|
+
isAttached = signal(false, /* @ts-ignore */
|
|
984
|
+
...(ngDevMode ? [{ debugName: "isAttached" }] : /* istanbul ignore next */ []));
|
|
946
985
|
// ── Derived state ──
|
|
947
986
|
/** @internal */
|
|
948
987
|
isDisabled = computed(() => {
|
|
949
|
-
this.
|
|
988
|
+
this.errorWiring.rev();
|
|
950
989
|
return this.disabledInput() || this.cvaDisabled() || !!this.ngControl?.disabled;
|
|
951
990
|
}, /* @ts-ignore */
|
|
952
991
|
...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
|
|
@@ -1106,26 +1145,19 @@ class DateRangePickerComponent extends FormFieldControl {
|
|
|
1106
1145
|
/** @internal */
|
|
1107
1146
|
disabled = this.isDisabled;
|
|
1108
1147
|
/** @internal */
|
|
1109
|
-
required =
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
return true;
|
|
1123
|
-
const matcher = this.errorStateMatcher() ?? this.defaultMatcher;
|
|
1124
|
-
const form = this.parentFormGroup ??
|
|
1125
|
-
this.parentForm;
|
|
1126
|
-
return matcher.isErrorState(this.ngControl?.control ?? null, form);
|
|
1127
|
-
}, /* @ts-ignore */
|
|
1128
|
-
...(ngDevMode ? [{ debugName: "errorState" }] : /* istanbul ignore next */ []));
|
|
1148
|
+
required = this.errorWiring.required;
|
|
1149
|
+
/** @internal */
|
|
1150
|
+
errorState = this.errorWiring.errorState;
|
|
1151
|
+
/**
|
|
1152
|
+
* @internal Active validation errors map from the bound `NgControl` (or `null` when the
|
|
1153
|
+
* control reports none / is unbound). This is what `[twError match="…"]` filters on inside a
|
|
1154
|
+
* `<tw-form-field>`; without it every `match`-targeted message — including the
|
|
1155
|
+
* `calendarMinDate` / `calendarMaxDate` / `calendarInvalidRange` / `calendarRangeTooShort` /
|
|
1156
|
+
* `calendarRangeTooLong` codes this component's `NG_VALIDATORS` apparatus exists to produce —
|
|
1157
|
+
* stays permanently hidden. (Authoritative key list: `CalendarValidationErrors` in
|
|
1158
|
+
* `calendar.types.ts`.)
|
|
1159
|
+
*/
|
|
1160
|
+
errors = this.errorWiring.errors;
|
|
1129
1161
|
/** @internal */
|
|
1130
1162
|
controlType = 'date-range-picker';
|
|
1131
1163
|
/** @internal */
|
|
@@ -1135,24 +1167,54 @@ class DateRangePickerComponent extends FormFieldControl {
|
|
|
1135
1167
|
constructor() {
|
|
1136
1168
|
super();
|
|
1137
1169
|
// Mirror the `open` model into the overlay lifecycle.
|
|
1170
|
+
//
|
|
1171
|
+
// The only tracked reads here are `open()` and `isDisabled()`. Everything
|
|
1172
|
+
// this effect *writes* — `overlayInstance`, `closing` — is a plain field,
|
|
1173
|
+
// never a signal, so opening or closing cannot re-trigger the effect that
|
|
1174
|
+
// ordered it. That is the whole reason those two are declared as fields
|
|
1175
|
+
// (see their declaration); promoting either back to `signal()` recreates
|
|
1176
|
+
// the read → write → re-trigger cycle CLAUDE.md forbids.
|
|
1177
|
+
//
|
|
1178
|
+
// `openOverlay()` / `closeOverlay()` do write `isAttached`, but this effect
|
|
1179
|
+
// never reads it, so that write is a one-way hand-off to the state-push
|
|
1180
|
+
// effect below and terminates there.
|
|
1181
|
+
//
|
|
1182
|
+
// The body is wrapped in `untracked()` because demoting the two fields is
|
|
1183
|
+
// NOT sufficient on its own. `openOverlay()` reads roughly twenty-five
|
|
1184
|
+
// inputs while pushing them into the freshly attached panel, and it writes
|
|
1185
|
+
// two signals it then reads straight back. Called from the tracked phase,
|
|
1186
|
+
// every one of those reads becomes a dependency of THIS effect, which both
|
|
1187
|
+
// recreates the forbidden write-then-read-back shape and re-runs the whole
|
|
1188
|
+
// lifecycle on any of those twenty-five inputs changing. Tracked reads are
|
|
1189
|
+
// now exactly `open()` and `isDisabled()`, which is what the paragraph
|
|
1190
|
+
// above claims — and only now true.
|
|
1138
1191
|
effect(() => {
|
|
1139
1192
|
const shouldOpen = this.open();
|
|
1140
1193
|
const disabled = this.isDisabled();
|
|
1141
|
-
|
|
1142
|
-
this.
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
this.
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
this.
|
|
1150
|
-
|
|
1194
|
+
untracked(() => {
|
|
1195
|
+
if (disabled && this.overlayInstance) {
|
|
1196
|
+
this.closeOverlay('programmatic');
|
|
1197
|
+
return;
|
|
1198
|
+
}
|
|
1199
|
+
if (shouldOpen && !this.overlayInstance && !disabled && !this.closing) {
|
|
1200
|
+
this.openOverlay();
|
|
1201
|
+
}
|
|
1202
|
+
else if (!shouldOpen && this.overlayInstance && !this.closing) {
|
|
1203
|
+
this.closeOverlay('programmatic');
|
|
1204
|
+
}
|
|
1205
|
+
});
|
|
1151
1206
|
});
|
|
1152
1207
|
// Push config into the overlay whenever anything relevant changes.
|
|
1153
1208
|
// Reads happen in the tracked phase; writes to the overlay instance are
|
|
1154
1209
|
// wrapped in `untracked()` so they never feed back into this effect.
|
|
1210
|
+
//
|
|
1211
|
+
// `isAttached()` MUST be read first and unconditionally: it is this
|
|
1212
|
+
// effect's only dependency on the overlay's existence, so it is what makes
|
|
1213
|
+
// the initial push happen when the panel attaches (`overlayInstance` is a
|
|
1214
|
+
// plain field and is invisible to the signal graph). Do not reorder it
|
|
1215
|
+
// behind the null check — the effect would then never re-run on open.
|
|
1155
1216
|
effect(() => {
|
|
1217
|
+
this.isAttached();
|
|
1156
1218
|
const instance = this.overlayInstance;
|
|
1157
1219
|
if (!instance)
|
|
1158
1220
|
return;
|
|
@@ -1162,6 +1224,7 @@ class DateRangePickerComponent extends FormFieldControl {
|
|
|
1162
1224
|
const maxDate = this.maxDate();
|
|
1163
1225
|
const dateFilter = this.dateFilter();
|
|
1164
1226
|
const startView = this.startView();
|
|
1227
|
+
const startAt = this.startAt();
|
|
1165
1228
|
const numberOfMonths = this.numberOfMonths();
|
|
1166
1229
|
const pendingRange = this.pendingRange();
|
|
1167
1230
|
const currentView = this.currentView();
|
|
@@ -1196,6 +1259,7 @@ class DateRangePickerComponent extends FormFieldControl {
|
|
|
1196
1259
|
instance.maxDate.set(maxDate);
|
|
1197
1260
|
instance.dateFilter.set(dateFilter);
|
|
1198
1261
|
instance.startView.set(startView);
|
|
1262
|
+
instance.startAt.set(startAt);
|
|
1199
1263
|
instance.numberOfMonths.set(numberOfMonths);
|
|
1200
1264
|
instance.pendingRange.set(pendingRange);
|
|
1201
1265
|
instance.currentView.set(currentView);
|
|
@@ -1263,15 +1327,16 @@ class DateRangePickerComponent extends FormFieldControl {
|
|
|
1263
1327
|
this.focusedSignal.set(!!origin);
|
|
1264
1328
|
if (wasFocused && !origin) {
|
|
1265
1329
|
this.onTouched();
|
|
1266
|
-
this.
|
|
1330
|
+
this.errorWiring.bump();
|
|
1267
1331
|
}
|
|
1268
1332
|
});
|
|
1269
1333
|
this.destroyRef.onDestroy(() => {
|
|
1270
1334
|
monitorSub.unsubscribe();
|
|
1271
1335
|
this.focusMonitor.stopMonitoring(this.elementRef);
|
|
1272
1336
|
// The coordinator's own DestroyRef.onDestroy disposes the overlay and
|
|
1273
|
-
// clears its timers; we only need to drop the local instance
|
|
1274
|
-
this.
|
|
1337
|
+
// clears its timers; we only need to drop the local instance reference.
|
|
1338
|
+
this.overlayInstance = null;
|
|
1339
|
+
this.isAttached.set(false);
|
|
1275
1340
|
});
|
|
1276
1341
|
}
|
|
1277
1342
|
ngOnInit() {
|
|
@@ -1281,27 +1346,33 @@ class DateRangePickerComponent extends FormFieldControl {
|
|
|
1281
1346
|
self: true,
|
|
1282
1347
|
optional: true,
|
|
1283
1348
|
});
|
|
1284
|
-
//
|
|
1285
|
-
//
|
|
1286
|
-
//
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1349
|
+
// There is deliberately NO `ngControl.valueAccessor = this` here, and none is needed:
|
|
1350
|
+
// registration happens through the static NG_VALUE_ACCESSOR provider on this component,
|
|
1351
|
+
// which `FormControlName` / `NgModel` resolve while constructing on the same element —
|
|
1352
|
+
// long before ngOnInit. An assignment at this point would be a pure no-op on both the
|
|
1353
|
+
// classic and the signal-forms path.
|
|
1354
|
+
//
|
|
1355
|
+
// Do NOT read the absence of a runtime assignment as evidence the static provider is
|
|
1356
|
+
// optional. It is the only registration this component has: delete it and `validate()`
|
|
1357
|
+
// is never invoked, so every calendar error code silently disappears with no test failure
|
|
1358
|
+
// outside the guard spec. See the provider's own comment above.
|
|
1359
|
+
//
|
|
1360
|
+
// Bump the revision so any control-derived signal that was already read while
|
|
1361
|
+
// `ngControl` was still `null` recomputes. `date-picker` and `time-picker` have
|
|
1362
|
+
// always done this; this component's omission was an asymmetry preserved
|
|
1363
|
+
// verbatim through the `wireErrorState` extraction.
|
|
1364
|
+
//
|
|
1365
|
+
// Honest scope, measured rather than assumed: **no test distinguishes this
|
|
1366
|
+
// line.** Removing it leaves the whole suite green, including the
|
|
1367
|
+
// `aria-required from the bound control` guard below. `required` and its
|
|
1368
|
+
// siblings are `computed()`, and a `computed()` is lazy — nothing reads them
|
|
1369
|
+
// before `ngOnInit` runs, so in every path exercised today there is no stale
|
|
1370
|
+
// cache to invalidate and the bump is a no-op. It is kept for symmetry with
|
|
1371
|
+
// the two sibling pickers, and as a guard against a future eager read (a
|
|
1372
|
+
// parent querying `required` during its own first pass would cache the
|
|
1373
|
+
// `null`-era answer permanently). Do not cite it as a bug fix.
|
|
1374
|
+
this.errorWiring.bump();
|
|
1375
|
+
this.errorWiring.connect();
|
|
1305
1376
|
}
|
|
1306
1377
|
// ── Public imperative API ──
|
|
1307
1378
|
/** Opens the overlay. No-op when disabled or already open. */
|
|
@@ -1524,7 +1595,10 @@ class DateRangePickerComponent extends FormFieldControl {
|
|
|
1524
1595
|
instance.locale.set(this.locale());
|
|
1525
1596
|
instance.dateClass.set(this.dateClass());
|
|
1526
1597
|
instance.cellTemplate.set(this.cellTemplate());
|
|
1527
|
-
this.
|
|
1598
|
+
this.overlayInstance = instance;
|
|
1599
|
+
// Must follow the assignment above: this is what wakes the state-push
|
|
1600
|
+
// effect, and it reads `overlayInstance` as a plain field.
|
|
1601
|
+
this.isAttached.set(true);
|
|
1528
1602
|
// Flush the initial config into the overlay's first render so view children
|
|
1529
1603
|
// (calendar, time pickers, action bar) observe the picker's current inputs.
|
|
1530
1604
|
result.componentRef.changeDetectorRef.detectChanges();
|
|
@@ -1553,9 +1627,9 @@ class DateRangePickerComponent extends FormFieldControl {
|
|
|
1553
1627
|
});
|
|
1554
1628
|
}
|
|
1555
1629
|
closeOverlay(reason, restore = false) {
|
|
1556
|
-
if (this.
|
|
1630
|
+
if (this.closing || !this.overlayInstance)
|
|
1557
1631
|
return;
|
|
1558
|
-
this.
|
|
1632
|
+
this.closing = true;
|
|
1559
1633
|
this.overlayInstance.leaving.set(true);
|
|
1560
1634
|
if (restore) {
|
|
1561
1635
|
const previous = this.lastValueBeforeOpen();
|
|
@@ -1569,10 +1643,11 @@ class DateRangePickerComponent extends FormFieldControl {
|
|
|
1569
1643
|
}
|
|
1570
1644
|
this.triggerRef().nativeElement.focus();
|
|
1571
1645
|
this.coordinator.close(() => {
|
|
1572
|
-
this.
|
|
1646
|
+
this.overlayInstance = null;
|
|
1647
|
+
this.isAttached.set(false);
|
|
1573
1648
|
this.pendingRange.set(null);
|
|
1574
1649
|
untracked(() => this.open.set(false));
|
|
1575
|
-
this.
|
|
1650
|
+
this.closing = false;
|
|
1576
1651
|
this.closed.emit(reason);
|
|
1577
1652
|
});
|
|
1578
1653
|
}
|
|
@@ -1818,7 +1893,7 @@ class DateRangePickerComponent extends FormFieldControl {
|
|
|
1818
1893
|
[attr.aria-label]="triggerAccessibleName() || null"
|
|
1819
1894
|
[attr.aria-labelledby]="ariaLabelledby() || null"
|
|
1820
1895
|
[attr.aria-describedby]="describedBy() || null"
|
|
1821
|
-
[attr.aria-required]="
|
|
1896
|
+
[attr.aria-required]="required() || null"
|
|
1822
1897
|
[attr.aria-invalid]="errorState() || null"
|
|
1823
1898
|
[attr.aria-disabled]="isDisabled() || null"
|
|
1824
1899
|
[attr.data-variant]="resolvedVariant()"
|
|
@@ -1917,7 +1992,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
|
|
|
1917
1992
|
[attr.aria-label]="triggerAccessibleName() || null"
|
|
1918
1993
|
[attr.aria-labelledby]="ariaLabelledby() || null"
|
|
1919
1994
|
[attr.aria-describedby]="describedBy() || null"
|
|
1920
|
-
[attr.aria-required]="
|
|
1995
|
+
[attr.aria-required]="required() || null"
|
|
1921
1996
|
[attr.aria-invalid]="errorState() || null"
|
|
1922
1997
|
[attr.aria-disabled]="isDisabled() || null"
|
|
1923
1998
|
[attr.data-variant]="resolvedVariant()"
|