@raintonic/formaui 0.4.0 → 0.9.2
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/CHANGELOG.md +80 -35
- package/README.md +22 -26
- package/fesm2022/raintonic-formaui-cdk-drag-drop.mjs +39 -41
- package/fesm2022/raintonic-formaui-cdk-drag-drop.mjs.map +1 -1
- package/fesm2022/raintonic-formaui-cdk-form-field.mjs +207 -3
- package/fesm2022/raintonic-formaui-cdk-form-field.mjs.map +1 -1
- package/fesm2022/raintonic-formaui-cdk-overlay.mjs +19 -1
- package/fesm2022/raintonic-formaui-cdk-overlay.mjs.map +1 -1
- package/fesm2022/raintonic-formaui-cdk-virtual-scroll.mjs +5 -12
- package/fesm2022/raintonic-formaui-cdk-virtual-scroll.mjs.map +1 -1
- package/fesm2022/raintonic-formaui-components-accordion.mjs +8 -5
- package/fesm2022/raintonic-formaui-components-accordion.mjs.map +1 -1
- package/fesm2022/raintonic-formaui-components-alert.mjs +16 -2
- package/fesm2022/raintonic-formaui-components-alert.mjs.map +1 -1
- package/fesm2022/raintonic-formaui-components-autocomplete.mjs +255 -462
- package/fesm2022/raintonic-formaui-components-autocomplete.mjs.map +1 -1
- package/fesm2022/raintonic-formaui-components-avatar.mjs +34 -59
- package/fesm2022/raintonic-formaui-components-avatar.mjs.map +1 -1
- package/fesm2022/raintonic-formaui-components-badge.mjs +2 -2
- package/fesm2022/raintonic-formaui-components-badge.mjs.map +1 -1
- package/fesm2022/raintonic-formaui-components-breadcrumb.mjs +4 -4
- package/fesm2022/raintonic-formaui-components-breadcrumb.mjs.map +1 -1
- package/fesm2022/raintonic-formaui-components-button-group.mjs +2 -2
- package/fesm2022/raintonic-formaui-components-button-group.mjs.map +1 -1
- package/fesm2022/raintonic-formaui-components-button.mjs +15 -20
- package/fesm2022/raintonic-formaui-components-button.mjs.map +1 -1
- package/fesm2022/raintonic-formaui-components-card.mjs +2 -2
- package/fesm2022/raintonic-formaui-components-card.mjs.map +1 -1
- package/fesm2022/raintonic-formaui-components-checkbox.mjs +2 -2
- package/fesm2022/raintonic-formaui-components-checkbox.mjs.map +1 -1
- package/fesm2022/raintonic-formaui-components-chip.mjs +97 -0
- package/fesm2022/raintonic-formaui-components-chip.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-components-data-table.mjs +69 -29
- package/fesm2022/raintonic-formaui-components-data-table.mjs.map +1 -1
- package/fesm2022/raintonic-formaui-components-date-picker.mjs +223 -144
- package/fesm2022/raintonic-formaui-components-date-picker.mjs.map +1 -1
- package/fesm2022/raintonic-formaui-components-divider.mjs +2 -2
- package/fesm2022/raintonic-formaui-components-divider.mjs.map +1 -1
- package/fesm2022/raintonic-formaui-components-drawer.mjs +2 -2
- package/fesm2022/raintonic-formaui-components-drawer.mjs.map +1 -1
- package/fesm2022/raintonic-formaui-components-dropdown-menu.mjs +888 -0
- package/fesm2022/raintonic-formaui-components-dropdown-menu.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-components-dual-tier-navigation.mjs +774 -0
- package/fesm2022/raintonic-formaui-components-dual-tier-navigation.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-components-empty-state.mjs +2 -2
- package/fesm2022/raintonic-formaui-components-empty-state.mjs.map +1 -1
- package/fesm2022/raintonic-formaui-components-file-upload.mjs +2 -2
- package/fesm2022/raintonic-formaui-components-file-upload.mjs.map +1 -1
- package/fesm2022/raintonic-formaui-components-form-field.mjs +81 -50
- package/fesm2022/raintonic-formaui-components-form-field.mjs.map +1 -1
- package/fesm2022/raintonic-formaui-components-icon.mjs +2 -2
- package/fesm2022/raintonic-formaui-components-icon.mjs.map +1 -1
- package/fesm2022/raintonic-formaui-components-input.mjs +47 -12
- package/fesm2022/raintonic-formaui-components-input.mjs.map +1 -1
- package/fesm2022/raintonic-formaui-components-list.mjs +4 -4
- package/fesm2022/raintonic-formaui-components-list.mjs.map +1 -1
- package/fesm2022/raintonic-formaui-components-number-input.mjs +20 -12
- package/fesm2022/raintonic-formaui-components-number-input.mjs.map +1 -1
- package/fesm2022/raintonic-formaui-components-paginator.mjs +2 -2
- package/fesm2022/raintonic-formaui-components-paginator.mjs.map +1 -1
- package/fesm2022/raintonic-formaui-components-password-input.mjs +35 -110
- package/fesm2022/raintonic-formaui-components-password-input.mjs.map +1 -1
- package/fesm2022/raintonic-formaui-components-popover.mjs +3 -2
- package/fesm2022/raintonic-formaui-components-popover.mjs.map +1 -1
- package/fesm2022/raintonic-formaui-components-progressbar.mjs +3 -2
- package/fesm2022/raintonic-formaui-components-progressbar.mjs.map +1 -1
- package/fesm2022/raintonic-formaui-components-radio.mjs +5 -6
- package/fesm2022/raintonic-formaui-components-radio.mjs.map +1 -1
- package/fesm2022/raintonic-formaui-components-select.mjs +257 -412
- package/fesm2022/raintonic-formaui-components-select.mjs.map +1 -1
- package/fesm2022/raintonic-formaui-components-side-panel.mjs +2 -2
- package/fesm2022/raintonic-formaui-components-side-panel.mjs.map +1 -1
- package/fesm2022/raintonic-formaui-components-sidebar-nav-menu.mjs +525 -0
- package/fesm2022/raintonic-formaui-components-sidebar-nav-menu.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-components-skeleton.mjs +2 -2
- package/fesm2022/raintonic-formaui-components-skeleton.mjs.map +1 -1
- package/fesm2022/raintonic-formaui-components-slider.mjs +2 -2
- package/fesm2022/raintonic-formaui-components-slider.mjs.map +1 -1
- package/fesm2022/raintonic-formaui-components-spinner.mjs +2 -2
- package/fesm2022/raintonic-formaui-components-spinner.mjs.map +1 -1
- package/fesm2022/raintonic-formaui-components-stepper.mjs +50 -45
- package/fesm2022/raintonic-formaui-components-stepper.mjs.map +1 -1
- package/fesm2022/raintonic-formaui-components-strength-meter.mjs +149 -0
- package/fesm2022/raintonic-formaui-components-strength-meter.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-components-tab.mjs +2 -2
- package/fesm2022/raintonic-formaui-components-tab.mjs.map +1 -1
- package/fesm2022/raintonic-formaui-components-time-picker.mjs +194 -154
- package/fesm2022/raintonic-formaui-components-time-picker.mjs.map +1 -1
- package/fesm2022/raintonic-formaui-components-toggle-group.mjs +302 -0
- package/fesm2022/raintonic-formaui-components-toggle-group.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-components-toggle.mjs +2 -2
- package/fesm2022/raintonic-formaui-components-toggle.mjs.map +1 -1
- package/fesm2022/raintonic-formaui-components-toolbar.mjs +2 -2
- package/fesm2022/raintonic-formaui-components-toolbar.mjs.map +1 -1
- package/fesm2022/raintonic-formaui-components-tooltip.mjs +10 -4
- package/fesm2022/raintonic-formaui-components-tooltip.mjs.map +1 -1
- package/fesm2022/raintonic-formaui-components-topbar.mjs +60 -0
- package/fesm2022/raintonic-formaui-components-topbar.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-components-tree-select.mjs +59 -69
- package/fesm2022/raintonic-formaui-components-tree-select.mjs.map +1 -1
- package/fesm2022/raintonic-formaui-components-tree-table.mjs +2 -2
- package/fesm2022/raintonic-formaui-components-tree-table.mjs.map +1 -1
- package/fesm2022/raintonic-formaui-components-tree.mjs +31 -5
- package/fesm2022/raintonic-formaui-components-tree.mjs.map +1 -1
- package/fesm2022/raintonic-formaui-core.mjs +279 -1
- package/fesm2022/raintonic-formaui-core.mjs.map +1 -1
- package/fesm2022/raintonic-formaui-services-breakpoint.mjs +93 -0
- package/fesm2022/raintonic-formaui-services-breakpoint.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-services-dialog.mjs +314 -16
- package/fesm2022/raintonic-formaui-services-dialog.mjs.map +1 -1
- package/fesm2022/raintonic-formaui-services-notification.mjs +93 -29
- package/fesm2022/raintonic-formaui-services-notification.mjs.map +1 -1
- package/fesm2022/raintonic-formaui-services-theme.mjs +46 -196
- package/fesm2022/raintonic-formaui-services-theme.mjs.map +1 -1
- package/fesm2022/raintonic-formaui.mjs +1 -1
- package/fesm2022/raintonic-formaui.mjs.map +1 -1
- package/llms-full.txt +2329 -450
- package/llms.txt +36 -33
- package/package.json +42 -19
- package/styles/fonts/Geist-Bold.woff2 +0 -0
- package/styles/fonts/Geist-Italic.woff2 +0 -0
- package/styles/fonts/Geist-Light.woff2 +0 -0
- package/styles/fonts/Geist-Medium.woff2 +0 -0
- package/styles/fonts/Geist-Regular.woff2 +0 -0
- package/styles/fonts/Geist-SemiBold.woff2 +0 -0
- package/styles/fonts/GeistMono-Regular.woff2 +0 -0
- package/styles/generated/_tokens.scss +906 -0
- package/styles/index.scss +11 -10
- package/styles/partials/_brand.scss +46 -0
- package/styles/partials/_constants.scss +22 -20
- package/styles/partials/_fonts.scss +54 -10
- package/styles/partials/_grid.scss +29 -18
- package/styles/partials/_mixins.scss +69 -27
- package/styles/partials/_motion.scss +28 -33
- package/styles/partials/_theme.scss +28 -255
- package/styles/partials/_type.scss +117 -0
- package/styles/partials/_typography.scss +45 -45
- package/styles/partials/_utilities.scss +198 -98
- package/styles/partials/components/_button.scss +144 -75
- package/styles/partials/components/_dialog.scss +181 -180
- package/styles/partials/components/_overlay.scss +87 -87
- package/styles/partials/themes/_dark.scss +3 -268
- package/styles/partials/themes/_light.scss +4 -268
- package/styles/styles.css +7744 -0
- package/styles/styles.entry.scss +3 -0
- package/styles/utilities.css +4802 -0
- package/styles/utilities.entry.scss +3 -0
- package/types/raintonic-formaui-cdk-drag-drop.d.ts +0 -1
- package/types/raintonic-formaui-cdk-drag-drop.d.ts.map +1 -1
- package/types/raintonic-formaui-cdk-form-field.d.ts +118 -2
- package/types/raintonic-formaui-cdk-form-field.d.ts.map +1 -1
- package/types/raintonic-formaui-cdk-overlay.d.ts +2 -0
- package/types/raintonic-formaui-cdk-overlay.d.ts.map +1 -1
- package/types/raintonic-formaui-cdk-virtual-scroll.d.ts +0 -1
- package/types/raintonic-formaui-cdk-virtual-scroll.d.ts.map +1 -1
- package/types/raintonic-formaui-components-accordion.d.ts +1 -1
- package/types/raintonic-formaui-components-accordion.d.ts.map +1 -1
- package/types/raintonic-formaui-components-alert.d.ts +6 -1
- package/types/raintonic-formaui-components-alert.d.ts.map +1 -1
- package/types/raintonic-formaui-components-autocomplete.d.ts +73 -116
- package/types/raintonic-formaui-components-autocomplete.d.ts.map +1 -1
- package/types/raintonic-formaui-components-avatar.d.ts +13 -31
- package/types/raintonic-formaui-components-avatar.d.ts.map +1 -1
- package/types/raintonic-formaui-components-button.d.ts +4 -10
- package/types/raintonic-formaui-components-button.d.ts.map +1 -1
- package/types/raintonic-formaui-components-chip.d.ts +43 -0
- package/types/raintonic-formaui-components-chip.d.ts.map +1 -0
- package/types/raintonic-formaui-components-data-table.d.ts +48 -11
- package/types/raintonic-formaui-components-data-table.d.ts.map +1 -1
- package/types/raintonic-formaui-components-date-picker.d.ts +59 -23
- package/types/raintonic-formaui-components-date-picker.d.ts.map +1 -1
- package/types/raintonic-formaui-components-dropdown-menu.d.ts +394 -0
- package/types/raintonic-formaui-components-dropdown-menu.d.ts.map +1 -0
- package/types/raintonic-formaui-components-dual-tier-navigation.d.ts +87 -0
- package/types/raintonic-formaui-components-dual-tier-navigation.d.ts.map +1 -0
- package/types/raintonic-formaui-components-form-field.d.ts +51 -21
- package/types/raintonic-formaui-components-form-field.d.ts.map +1 -1
- package/types/raintonic-formaui-components-input.d.ts +20 -11
- package/types/raintonic-formaui-components-input.d.ts.map +1 -1
- package/types/raintonic-formaui-components-number-input.d.ts +5 -3
- package/types/raintonic-formaui-components-number-input.d.ts.map +1 -1
- package/types/raintonic-formaui-components-password-input.d.ts +18 -32
- package/types/raintonic-formaui-components-password-input.d.ts.map +1 -1
- package/types/raintonic-formaui-components-popover.d.ts.map +1 -1
- package/types/raintonic-formaui-components-progressbar.d.ts +1 -1
- package/types/raintonic-formaui-components-progressbar.d.ts.map +1 -1
- package/types/raintonic-formaui-components-radio.d.ts +1 -2
- package/types/raintonic-formaui-components-radio.d.ts.map +1 -1
- package/types/raintonic-formaui-components-select.d.ts +107 -76
- package/types/raintonic-formaui-components-select.d.ts.map +1 -1
- package/types/raintonic-formaui-components-sidebar-nav-menu.d.ts +223 -0
- package/types/raintonic-formaui-components-sidebar-nav-menu.d.ts.map +1 -0
- package/types/raintonic-formaui-components-stepper.d.ts +4 -2
- package/types/raintonic-formaui-components-stepper.d.ts.map +1 -1
- package/types/raintonic-formaui-components-strength-meter.d.ts +78 -0
- package/types/raintonic-formaui-components-strength-meter.d.ts.map +1 -0
- package/types/raintonic-formaui-components-time-picker.d.ts +44 -24
- package/types/raintonic-formaui-components-time-picker.d.ts.map +1 -1
- package/types/raintonic-formaui-components-toggle-group.d.ts +100 -0
- package/types/raintonic-formaui-components-toggle-group.d.ts.map +1 -0
- package/types/raintonic-formaui-components-tooltip.d.ts +2 -1
- package/types/raintonic-formaui-components-tooltip.d.ts.map +1 -1
- package/types/raintonic-formaui-components-topbar.d.ts +48 -0
- package/types/raintonic-formaui-components-topbar.d.ts.map +1 -0
- package/types/raintonic-formaui-components-tree-select.d.ts +25 -9
- package/types/raintonic-formaui-components-tree-select.d.ts.map +1 -1
- package/types/raintonic-formaui-components-tree.d.ts +12 -1
- package/types/raintonic-formaui-components-tree.d.ts.map +1 -1
- package/types/raintonic-formaui-core.d.ts +243 -5
- package/types/raintonic-formaui-core.d.ts.map +1 -1
- package/types/raintonic-formaui-services-breakpoint.d.ts +44 -0
- package/types/raintonic-formaui-services-breakpoint.d.ts.map +1 -0
- package/types/raintonic-formaui-services-dialog.d.ts +141 -2
- package/types/raintonic-formaui-services-dialog.d.ts.map +1 -1
- package/types/raintonic-formaui-services-notification.d.ts +24 -2
- package/types/raintonic-formaui-services-notification.d.ts.map +1 -1
- package/types/raintonic-formaui-services-theme.d.ts +13 -103
- package/types/raintonic-formaui-services-theme.d.ts.map +1 -1
- package/types/raintonic-formaui.d.ts +1 -1
- package/fesm2022/raintonic-formaui-components-big-menu.mjs +0 -86
- package/fesm2022/raintonic-formaui-components-big-menu.mjs.map +0 -1
- package/fesm2022/raintonic-formaui-components-menu.mjs +0 -896
- package/fesm2022/raintonic-formaui-components-menu.mjs.map +0 -1
- package/fesm2022/raintonic-formaui-components-sidebar.mjs +0 -275
- package/fesm2022/raintonic-formaui-components-sidebar.mjs.map +0 -1
- package/fesm2022/raintonic-formaui-components-tag.mjs +0 -95
- package/fesm2022/raintonic-formaui-components-tag.mjs.map +0 -1
- package/styles/_fonts-entry.scss +0 -3
- package/styles/fonts/inter-tight-latin-italic.woff2 +0 -0
- package/styles/fonts/inter-tight-latin.woff2 +0 -0
- package/types/raintonic-formaui-components-big-menu.d.ts +0 -73
- package/types/raintonic-formaui-components-big-menu.d.ts.map +0 -1
- package/types/raintonic-formaui-components-menu.d.ts +0 -403
- package/types/raintonic-formaui-components-menu.d.ts.map +0 -1
- package/types/raintonic-formaui-components-sidebar.d.ts +0 -185
- package/types/raintonic-formaui-components-sidebar.d.ts.map +0 -1
- package/types/raintonic-formaui-components-tag.d.ts +0 -43
- package/types/raintonic-formaui-components-tag.d.ts.map +0 -1
|
@@ -1,33 +1,39 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { Injectable, input, output, signal, inject, ChangeDetectorRef, effect, ViewChild, ViewEncapsulation, ChangeDetectionStrategy, Component,
|
|
2
|
+
import { Injectable, input, output, signal, computed, inject, ChangeDetectorRef, effect, ViewChild, ViewEncapsulation, ChangeDetectionStrategy, Component, ElementRef, booleanAttribute } from '@angular/core';
|
|
3
3
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
4
|
-
import { NgForm, FormGroupDirective, NG_VALUE_ACCESSOR } from '@angular/forms';
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
8
|
-
import { injectNgControl, updateErrorState, syncRequiredState, syncNgControlDisabled } from '@raintonic/formaui/cdk/form-field';
|
|
4
|
+
import { NgForm, FormGroupDirective, NG_VALUE_ACCESSOR, NG_VALIDATORS } from '@angular/forms';
|
|
5
|
+
import { Subject } from 'rxjs';
|
|
6
|
+
import * as i1 from '@raintonic/formaui/cdk/form-field';
|
|
7
|
+
import { FuiPopupOverlayDirective, FuiFormControlSyncDirective, injectNgControl } from '@raintonic/formaui/cdk/form-field';
|
|
9
8
|
import { FuiIntlBase, DefaultErrorStateMatcher, FUI_FORM_FIELD_CONTROL } from '@raintonic/formaui/core';
|
|
10
|
-
import {
|
|
9
|
+
import { FuiInputDirective } from '@raintonic/formaui/components/input';
|
|
11
10
|
import { FuiIconComponent } from '@raintonic/formaui/components/icon';
|
|
12
11
|
|
|
12
|
+
/** Standard time arithmetic constants — avoid magic numbers in time parsing/generation. */
|
|
13
|
+
const HOURS_PER_DAY = 24;
|
|
14
|
+
const MINUTES_PER_HOUR = 60;
|
|
15
|
+
const SECONDS_PER_MINUTE = 60;
|
|
16
|
+
const NOON_HOUR = 12;
|
|
17
|
+
const MIDNIGHT_HOUR = 0;
|
|
13
18
|
/**
|
|
14
|
-
*
|
|
19
|
+
* @deprecated Use `timeToSeconds()` for integer-precise comparison. Will be removed in v0.6.0.
|
|
15
20
|
*/
|
|
16
21
|
function timeToMinutes(time) {
|
|
17
|
-
return time.hours *
|
|
22
|
+
return time.hours * MINUTES_PER_HOUR + time.minutes + (time.seconds ? time.seconds / MINUTES_PER_HOUR : 0);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Converts a `TimeValue` to total seconds since 00:00:00.
|
|
26
|
+
* Integer-precise; safe for comparison without floating-point error.
|
|
27
|
+
*/
|
|
28
|
+
function timeToSeconds(t) {
|
|
29
|
+
return t.hours * (MINUTES_PER_HOUR * SECONDS_PER_MINUTE) + t.minutes * SECONDS_PER_MINUTE + (t.seconds ?? 0);
|
|
18
30
|
}
|
|
19
31
|
/**
|
|
20
32
|
* Compare two TimeValue objects.
|
|
21
|
-
* Returns
|
|
33
|
+
* Returns a negative number if a < b, 0 if equal, positive if a > b.
|
|
22
34
|
*/
|
|
23
35
|
function compareTime(a, b) {
|
|
24
|
-
|
|
25
|
-
const bMin = timeToMinutes(b);
|
|
26
|
-
if (aMin < bMin)
|
|
27
|
-
return -1;
|
|
28
|
-
if (aMin > bMin)
|
|
29
|
-
return 1;
|
|
30
|
-
return 0;
|
|
36
|
+
return timeToSeconds(a) - timeToSeconds(b);
|
|
31
37
|
}
|
|
32
38
|
/**
|
|
33
39
|
* Check if a time is within an optional min/max range (inclusive).
|
|
@@ -56,12 +62,12 @@ function formatTime(value, format, showSeconds) {
|
|
|
56
62
|
return showSeconds ? `${base}:${pad(s)}` : base;
|
|
57
63
|
}
|
|
58
64
|
// 12h format
|
|
59
|
-
const period = h >=
|
|
65
|
+
const period = h >= NOON_HOUR ? 'PM' : 'AM';
|
|
60
66
|
let displayHour = h;
|
|
61
|
-
if (h ===
|
|
62
|
-
displayHour =
|
|
63
|
-
else if (h >
|
|
64
|
-
displayHour = h -
|
|
67
|
+
if (h === MIDNIGHT_HOUR)
|
|
68
|
+
displayHour = NOON_HOUR;
|
|
69
|
+
else if (h > NOON_HOUR)
|
|
70
|
+
displayHour = h - NOON_HOUR;
|
|
65
71
|
const base = `${displayHour}:${pad(m)}`;
|
|
66
72
|
return showSeconds ? `${base}:${pad(s)} ${period}` : `${base} ${period}`;
|
|
67
73
|
}
|
|
@@ -83,14 +89,14 @@ function parseTimeString(str, format) {
|
|
|
83
89
|
const minutes = parseInt(match12[2], 10);
|
|
84
90
|
const seconds = match12[3] ? parseInt(match12[3], 10) : undefined;
|
|
85
91
|
const period = match12[4].toUpperCase();
|
|
86
|
-
if (hours < 1 || hours >
|
|
92
|
+
if (hours < 1 || hours > NOON_HOUR || minutes < 0 || minutes > 59)
|
|
87
93
|
return null;
|
|
88
94
|
if (seconds !== undefined && (seconds < 0 || seconds > 59))
|
|
89
95
|
return null;
|
|
90
|
-
if (period === 'AM' && hours ===
|
|
91
|
-
hours =
|
|
92
|
-
else if (period === 'PM' && hours !==
|
|
93
|
-
hours +=
|
|
96
|
+
if (period === 'AM' && hours === NOON_HOUR)
|
|
97
|
+
hours = MIDNIGHT_HOUR;
|
|
98
|
+
else if (period === 'PM' && hours !== NOON_HOUR)
|
|
99
|
+
hours += NOON_HOUR;
|
|
94
100
|
return { hours, minutes, ...(seconds !== undefined ? { seconds } : {}) };
|
|
95
101
|
}
|
|
96
102
|
// 24h format: "HH:mm" or "HH:mm:ss"
|
|
@@ -100,7 +106,7 @@ function parseTimeString(str, format) {
|
|
|
100
106
|
const hours = parseInt(match24[1], 10);
|
|
101
107
|
const minutes = parseInt(match24[2], 10);
|
|
102
108
|
const seconds = match24[3] ? parseInt(match24[3], 10) : undefined;
|
|
103
|
-
if (hours < 0 || hours >
|
|
109
|
+
if (hours < 0 || hours > HOURS_PER_DAY - 1 || minutes < 0 || minutes > 59)
|
|
104
110
|
return null;
|
|
105
111
|
if (seconds !== undefined && (seconds < 0 || seconds > 59))
|
|
106
112
|
return null;
|
|
@@ -111,9 +117,9 @@ function parseTimeString(str, format) {
|
|
|
111
117
|
*/
|
|
112
118
|
function generateTimeOptions(format, minuteStep, showSeconds, min, max) {
|
|
113
119
|
const options = [];
|
|
114
|
-
const step = Math.max(1, Math.min(
|
|
115
|
-
for (let h = 0; h <
|
|
116
|
-
for (let m = 0; m <
|
|
120
|
+
const step = Math.max(1, Math.min(MINUTES_PER_HOUR, minuteStep));
|
|
121
|
+
for (let h = 0; h < HOURS_PER_DAY; h++) {
|
|
122
|
+
for (let m = 0; m < MINUTES_PER_HOUR; m += step) {
|
|
117
123
|
const value = { hours: h, minutes: m, ...(showSeconds ? { seconds: 0 } : {}) };
|
|
118
124
|
const label = formatTime(value, format, showSeconds);
|
|
119
125
|
const disabled = !isTimeInRange(value, min, max);
|
|
@@ -165,6 +171,23 @@ class FuiTimeListComponent {
|
|
|
165
171
|
optionSelected = output();
|
|
166
172
|
/** Index of the currently focused option for keyboard navigation. */
|
|
167
173
|
_focusedIndex = signal(-1, ...(ngDevMode ? [{ debugName: "_focusedIndex" }] : /* istanbul ignore next */ []));
|
|
174
|
+
/**
|
|
175
|
+
* Label of the currently active option for the aria-live announcement region.
|
|
176
|
+
* Resolves to the focused option during keyboard navigation, falling back to
|
|
177
|
+
* the selected option's label when no keyboard focus is active.
|
|
178
|
+
*/
|
|
179
|
+
activeOptionLabel = computed(() => {
|
|
180
|
+
const opts = this.options();
|
|
181
|
+
const focusedIdx = this._focusedIndex();
|
|
182
|
+
if (focusedIdx >= 0 && focusedIdx < opts.length) {
|
|
183
|
+
return opts[focusedIdx].label;
|
|
184
|
+
}
|
|
185
|
+
const selected = this.selectedValue();
|
|
186
|
+
if (!selected)
|
|
187
|
+
return '';
|
|
188
|
+
const match = opts.find((o) => compareTime(o.value, selected) === 0);
|
|
189
|
+
return match?.label ?? '';
|
|
190
|
+
}, ...(ngDevMode ? [{ debugName: "activeOptionLabel" }] : /* istanbul ignore next */ []));
|
|
168
191
|
listContainer;
|
|
169
192
|
intl = inject(FuiTimePickerIntl);
|
|
170
193
|
_cdr = inject(ChangeDetectorRef);
|
|
@@ -289,13 +312,13 @@ class FuiTimeListComponent {
|
|
|
289
312
|
}
|
|
290
313
|
}
|
|
291
314
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: FuiTimeListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
292
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.6", type: FuiTimeListComponent, isStandalone: true, selector: "fui-time-list", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: true, transformFunction: null }, selectedValue: { classPropertyName: "selectedValue", publicName: "selectedValue", isSignal: true, isRequired: false, transformFunction: null }, format: { classPropertyName: "format", publicName: "format", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { optionSelected: "optionSelected" }, host: { classAttribute: "fui-time-list" }, viewQueries: [{ propertyName: "listContainer", first: true, predicate: ["listContainer"], descendants: true }], ngImport: i0, template: "<div\r\n #listContainer\r\n class=\"fui-time-list__container\"\r\n role=\"listbox\"\r\n tabindex=\"0\"\r\n [attr.aria-label]=\"intl.availableTimesAriaLabel\"\r\n (keydown)=\"onKeydown($event)\"\r\n>\r\n @for (option of options(); track option.label) {\r\n <div\r\n class=\"fui-time-list__option\"\r\n [class.fui-time-list__option--selected]=\"_isSelected(option)\"\r\n [class.fui-time-list__option--disabled]=\"option.disabled\"\r\n [class.fui-time-list__option--focused]=\"_focusedIndex() === $index\"\r\n [attr.id]=\"'fui-time-option-' + $index\"\r\n [attr.aria-selected]=\"_isSelected(option)\"\r\n [attr.aria-disabled]=\"option.disabled\"\r\n role=\"option\"\r\n (click)=\"_selectOption(option)\"\r\n >\r\n {{ option.label }}\r\n </div>\r\n }\r\n</div>\r\n", styles: ["@keyframes fui-skeleton-pulse{0%{opacity:1}50%{opacity:.4}to{opacity:1}}@keyframes fui-spin{to{transform:rotate(360deg)}}@keyframes fui-shake{0%,to{transform:translate(0)}10%,30%,50%,70%,90%{transform:translate(-2px)}20%,40%,60%,80%{transform:translate(2px)}}.fui-motion-fade-in{transition:opacity
|
|
315
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.6", type: FuiTimeListComponent, isStandalone: true, selector: "fui-time-list", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: true, transformFunction: null }, selectedValue: { classPropertyName: "selectedValue", publicName: "selectedValue", isSignal: true, isRequired: false, transformFunction: null }, format: { classPropertyName: "format", publicName: "format", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { optionSelected: "optionSelected" }, host: { classAttribute: "fui-time-list" }, viewQueries: [{ propertyName: "listContainer", first: true, predicate: ["listContainer"], descendants: true }], ngImport: i0, template: "<span class=\"fui-sr-only\" aria-live=\"polite\" aria-atomic=\"true\">{{ activeOptionLabel() }}</span>\r\n<div\r\n #listContainer\r\n class=\"fui-time-list__container\"\r\n role=\"listbox\"\r\n tabindex=\"0\"\r\n [attr.aria-label]=\"intl.availableTimesAriaLabel\"\r\n (keydown)=\"onKeydown($event)\"\r\n>\r\n @for (option of options(); track option.label) {\r\n <div\r\n class=\"fui-time-list__option\"\r\n [class.fui-time-list__option--selected]=\"_isSelected(option)\"\r\n [class.fui-time-list__option--disabled]=\"option.disabled\"\r\n [class.fui-time-list__option--focused]=\"_focusedIndex() === $index\"\r\n [attr.id]=\"'fui-time-option-' + $index\"\r\n [attr.aria-selected]=\"_isSelected(option)\"\r\n [attr.aria-disabled]=\"option.disabled\"\r\n role=\"option\"\r\n (click)=\"_selectOption(option)\"\r\n >\r\n {{ option.label }}\r\n </div>\r\n }\r\n</div>\r\n", styles: ["@keyframes fui-skeleton-pulse{0%{opacity:1}50%{opacity:.4}to{opacity:1}}@keyframes fui-spin{to{transform:rotate(360deg)}}@keyframes fui-shake{0%,to{transform:translate(0)}10%,30%,50%,70%,90%{transform:translate(-2px)}20%,40%,60%,80%{transform:translate(2px)}}.fui-motion-fade-in{transition-property:opacity;transition-duration:var(--fui-duration-fast);transition-timing-function:var(--fui-ease-out);transition-delay:0ms}.fui-motion-fade-out{transition-property:opacity;transition-duration:var(--fui-duration-fast);transition-timing-function:var(--fui-ease-in);transition-delay:0ms}.fui-motion-slide-in-bottom{transition-property:transform;transition-duration:var(--fui-duration-base);transition-timing-function:var(--fui-ease-out);transition-delay:0ms;transform:translateY(0)}.fui-motion-slide-in-bottom.fui-motion-entering{transform:translateY(1rem)}.fui-motion-slide-in-top{transition-property:transform;transition-duration:var(--fui-duration-base);transition-timing-function:var(--fui-ease-out);transition-delay:0ms;transform:translateY(0)}.fui-motion-slide-in-top.fui-motion-entering{transform:translateY(-1rem)}.fui-motion-scale-in{transition-property:transform,opacity;transition-duration:var(--fui-duration-base);transition-timing-function:var(--fui-ease-out);transition-delay:0ms;transform:scale(1);opacity:1}.fui-motion-scale-in.fui-motion-entering{transform:scale(.95);opacity:0}.fui-no-motion{transition:none!important;animation:none!important}@media(prefers-reduced-motion:reduce){*,*:before,*:after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}}@keyframes fui-pulse{0%{transform:scale(1);opacity:1}50%{transform:scale(1.05)}to{transform:scale(1);opacity:1}}@keyframes fui-slide-in{0%{transform:translate(120%)}to{transform:translate(0)}}.fui-slide-in{animation:fui-slide-in var(--fui-duration-base) var(--fui-ease-out)}@keyframes fui-popover-enter{0%{opacity:0;transform:translateY(-14px)}60%{opacity:1}to{opacity:1;transform:translateY(0)}}.fui-time-list{display:block}.fui-time-list__container{max-height:200px;overflow-y:auto;overflow-x:hidden;padding:var(--fui-spacing-2) 0}.fui-time-list__container::-webkit-scrollbar{width:8px}.fui-time-list__container::-webkit-scrollbar-track{background:var(--fui-bg-default);border-radius:var(--fui-radius-sm)}.fui-time-list__container::-webkit-scrollbar-thumb{background:var(--fui-border-default);border-radius:var(--fui-radius-sm)}.fui-time-list__container::-webkit-scrollbar-thumb:hover{background:var(--fui-text-secondary)}.fui-time-list__option{display:flex;align-items:center;padding:var(--fui-spacing-2) var(--fui-spacing-6);font-family:var(--fui-font-sans);font-size:var(--fui-text-base);line-height:var(--fui-leading-normal);color:var(--fui-text-primary);cursor:pointer;-webkit-user-select:none;user-select:none;transition-property:background-color;transition-duration:var(--fui-duration-fast);transition-timing-function:var(--fui-ease-in-out);transition-delay:0ms}.fui-time-list__option:hover:not(.fui-time-list__option--disabled){background-color:var(--fui-bg-subtle)}.fui-time-list__option--selected{background-color:var(--fui-primary-20);color:var(--fui-primary-80);font-weight:var(--fui-weight-medium)}.fui-time-list__option--selected:hover:not(.fui-time-list__option--disabled){background-color:var(--fui-primary-30)}.fui-time-list__option--focused{outline:2px solid var(--fui-primary-60);outline-offset:-2px;border-radius:var(--fui-radius-sm)}.fui-time-list__option--disabled{color:var(--fui-text-disabled);cursor:not-allowed;opacity:.5}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
293
316
|
}
|
|
294
317
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: FuiTimeListComponent, decorators: [{
|
|
295
318
|
type: Component,
|
|
296
319
|
args: [{ selector: 'fui-time-list', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: {
|
|
297
320
|
class: 'fui-time-list',
|
|
298
|
-
}, template: "<div\r\n #listContainer\r\n class=\"fui-time-list__container\"\r\n role=\"listbox\"\r\n tabindex=\"0\"\r\n [attr.aria-label]=\"intl.availableTimesAriaLabel\"\r\n (keydown)=\"onKeydown($event)\"\r\n>\r\n @for (option of options(); track option.label) {\r\n <div\r\n class=\"fui-time-list__option\"\r\n [class.fui-time-list__option--selected]=\"_isSelected(option)\"\r\n [class.fui-time-list__option--disabled]=\"option.disabled\"\r\n [class.fui-time-list__option--focused]=\"_focusedIndex() === $index\"\r\n [attr.id]=\"'fui-time-option-' + $index\"\r\n [attr.aria-selected]=\"_isSelected(option)\"\r\n [attr.aria-disabled]=\"option.disabled\"\r\n role=\"option\"\r\n (click)=\"_selectOption(option)\"\r\n >\r\n {{ option.label }}\r\n </div>\r\n }\r\n</div>\r\n", styles: ["@keyframes fui-skeleton-pulse{0%{opacity:1}50%{opacity:.4}to{opacity:1}}@keyframes fui-spin{to{transform:rotate(360deg)}}@keyframes fui-shake{0%,to{transform:translate(0)}10%,30%,50%,70%,90%{transform:translate(-2px)}20%,40%,60%,80%{transform:translate(2px)}}.fui-motion-fade-in{transition:opacity
|
|
321
|
+
}, template: "<span class=\"fui-sr-only\" aria-live=\"polite\" aria-atomic=\"true\">{{ activeOptionLabel() }}</span>\r\n<div\r\n #listContainer\r\n class=\"fui-time-list__container\"\r\n role=\"listbox\"\r\n tabindex=\"0\"\r\n [attr.aria-label]=\"intl.availableTimesAriaLabel\"\r\n (keydown)=\"onKeydown($event)\"\r\n>\r\n @for (option of options(); track option.label) {\r\n <div\r\n class=\"fui-time-list__option\"\r\n [class.fui-time-list__option--selected]=\"_isSelected(option)\"\r\n [class.fui-time-list__option--disabled]=\"option.disabled\"\r\n [class.fui-time-list__option--focused]=\"_focusedIndex() === $index\"\r\n [attr.id]=\"'fui-time-option-' + $index\"\r\n [attr.aria-selected]=\"_isSelected(option)\"\r\n [attr.aria-disabled]=\"option.disabled\"\r\n role=\"option\"\r\n (click)=\"_selectOption(option)\"\r\n >\r\n {{ option.label }}\r\n </div>\r\n }\r\n</div>\r\n", styles: ["@keyframes fui-skeleton-pulse{0%{opacity:1}50%{opacity:.4}to{opacity:1}}@keyframes fui-spin{to{transform:rotate(360deg)}}@keyframes fui-shake{0%,to{transform:translate(0)}10%,30%,50%,70%,90%{transform:translate(-2px)}20%,40%,60%,80%{transform:translate(2px)}}.fui-motion-fade-in{transition-property:opacity;transition-duration:var(--fui-duration-fast);transition-timing-function:var(--fui-ease-out);transition-delay:0ms}.fui-motion-fade-out{transition-property:opacity;transition-duration:var(--fui-duration-fast);transition-timing-function:var(--fui-ease-in);transition-delay:0ms}.fui-motion-slide-in-bottom{transition-property:transform;transition-duration:var(--fui-duration-base);transition-timing-function:var(--fui-ease-out);transition-delay:0ms;transform:translateY(0)}.fui-motion-slide-in-bottom.fui-motion-entering{transform:translateY(1rem)}.fui-motion-slide-in-top{transition-property:transform;transition-duration:var(--fui-duration-base);transition-timing-function:var(--fui-ease-out);transition-delay:0ms;transform:translateY(0)}.fui-motion-slide-in-top.fui-motion-entering{transform:translateY(-1rem)}.fui-motion-scale-in{transition-property:transform,opacity;transition-duration:var(--fui-duration-base);transition-timing-function:var(--fui-ease-out);transition-delay:0ms;transform:scale(1);opacity:1}.fui-motion-scale-in.fui-motion-entering{transform:scale(.95);opacity:0}.fui-no-motion{transition:none!important;animation:none!important}@media(prefers-reduced-motion:reduce){*,*:before,*:after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}}@keyframes fui-pulse{0%{transform:scale(1);opacity:1}50%{transform:scale(1.05)}to{transform:scale(1);opacity:1}}@keyframes fui-slide-in{0%{transform:translate(120%)}to{transform:translate(0)}}.fui-slide-in{animation:fui-slide-in var(--fui-duration-base) var(--fui-ease-out)}@keyframes fui-popover-enter{0%{opacity:0;transform:translateY(-14px)}60%{opacity:1}to{opacity:1;transform:translateY(0)}}.fui-time-list{display:block}.fui-time-list__container{max-height:200px;overflow-y:auto;overflow-x:hidden;padding:var(--fui-spacing-2) 0}.fui-time-list__container::-webkit-scrollbar{width:8px}.fui-time-list__container::-webkit-scrollbar-track{background:var(--fui-bg-default);border-radius:var(--fui-radius-sm)}.fui-time-list__container::-webkit-scrollbar-thumb{background:var(--fui-border-default);border-radius:var(--fui-radius-sm)}.fui-time-list__container::-webkit-scrollbar-thumb:hover{background:var(--fui-text-secondary)}.fui-time-list__option{display:flex;align-items:center;padding:var(--fui-spacing-2) var(--fui-spacing-6);font-family:var(--fui-font-sans);font-size:var(--fui-text-base);line-height:var(--fui-leading-normal);color:var(--fui-text-primary);cursor:pointer;-webkit-user-select:none;user-select:none;transition-property:background-color;transition-duration:var(--fui-duration-fast);transition-timing-function:var(--fui-ease-in-out);transition-delay:0ms}.fui-time-list__option:hover:not(.fui-time-list__option--disabled){background-color:var(--fui-bg-subtle)}.fui-time-list__option--selected{background-color:var(--fui-primary-20);color:var(--fui-primary-80);font-weight:var(--fui-weight-medium)}.fui-time-list__option--selected:hover:not(.fui-time-list__option--disabled){background-color:var(--fui-primary-30)}.fui-time-list__option--focused{outline:2px solid var(--fui-primary-60);outline-offset:-2px;border-radius:var(--fui-radius-sm)}.fui-time-list__option--disabled{color:var(--fui-text-disabled);cursor:not-allowed;opacity:.5}\n"] }]
|
|
299
322
|
}], ctorParameters: () => [], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: true }] }], selectedValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedValue", required: false }] }], format: [{ type: i0.Input, args: [{ isSignal: true, alias: "format", required: false }] }], optionSelected: [{ type: i0.Output, args: ["optionSelected"] }], listContainer: [{
|
|
300
323
|
type: ViewChild,
|
|
301
324
|
args: ['listContainer', { static: false }]
|
|
@@ -304,6 +327,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.6", ngImpor
|
|
|
304
327
|
class FuiTimePickerComponent {
|
|
305
328
|
static nextId = 0;
|
|
306
329
|
controlType = 'fui-time-picker';
|
|
330
|
+
// --- Injected dependencies ---
|
|
331
|
+
intl = inject(FuiTimePickerIntl);
|
|
332
|
+
_cdr = inject(ChangeDetectorRef);
|
|
333
|
+
_popup = inject(FuiPopupOverlayDirective);
|
|
334
|
+
_formSync = inject(FuiFormControlSyncDirective);
|
|
335
|
+
_elementRef = inject(ElementRef);
|
|
307
336
|
// --- Inputs ---
|
|
308
337
|
formatInput = input('24h', { ...(ngDevMode ? { debugName: "formatInput" } : /* istanbul ignore next */ {}), alias: 'format' });
|
|
309
338
|
showSeconds = input(false, { ...(ngDevMode ? { debugName: "showSeconds" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
@@ -313,7 +342,8 @@ class FuiTimePickerComponent {
|
|
|
313
342
|
placeholderInput = input('', { ...(ngDevMode ? { debugName: "placeholderInput" } : /* istanbul ignore next */ {}), alias: 'placeholder' });
|
|
314
343
|
disabledInput = input(false, { ...(ngDevMode ? { debugName: "disabledInput" } : /* istanbul ignore next */ {}), alias: 'disabled',
|
|
315
344
|
transform: booleanAttribute });
|
|
316
|
-
|
|
345
|
+
readonlyInput = input(false, { ...(ngDevMode ? { debugName: "readonlyInput" } : /* istanbul ignore next */ {}), alias: 'readonly',
|
|
346
|
+
transform: booleanAttribute });
|
|
317
347
|
errorStateMatcher = input(null, ...(ngDevMode ? [{ debugName: "errorStateMatcher" }] : /* istanbul ignore next */ []));
|
|
318
348
|
// --- Outputs ---
|
|
319
349
|
timeChange = output();
|
|
@@ -324,8 +354,8 @@ class FuiTimePickerComponent {
|
|
|
324
354
|
_ngControlDisabled = signal(false, ...(ngDevMode ? [{ debugName: "_ngControlDisabled" }] : /* istanbul ignore next */ []));
|
|
325
355
|
_required = signal(false, ...(ngDevMode ? [{ debugName: "_required" }] : /* istanbul ignore next */ []));
|
|
326
356
|
_errorState = signal(false, ...(ngDevMode ? [{ debugName: "_errorState" }] : /* istanbul ignore next */ []));
|
|
327
|
-
|
|
328
|
-
|
|
357
|
+
/** Whether the panel is open — projected from FuiPopupOverlayDirective. */
|
|
358
|
+
panelOpen = computed(() => this._popup.panelOpen(), ...(ngDevMode ? [{ debugName: "panelOpen" }] : /* istanbul ignore next */ []));
|
|
329
359
|
// --- FuiFormFieldControl ---
|
|
330
360
|
stateChanges = new Subject();
|
|
331
361
|
_uid = `fui-time-picker-${FuiTimePickerComponent.nextId++}`;
|
|
@@ -347,6 +377,7 @@ class FuiTimePickerComponent {
|
|
|
347
377
|
}, ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
348
378
|
focused = this._focused;
|
|
349
379
|
disabled = computed(() => this._disabled() || this.disabledInput() || this._ngControlDisabled(), ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
|
|
380
|
+
readonly = computed(() => this.readonlyInput(), ...(ngDevMode ? [{ debugName: "readonly" }] : /* istanbul ignore next */ []));
|
|
350
381
|
errorState = this._errorState;
|
|
351
382
|
id = this._uid;
|
|
352
383
|
empty = computed(() => {
|
|
@@ -364,19 +395,11 @@ class FuiTimePickerComponent {
|
|
|
364
395
|
const maxVal = this.max() ? parseISOTimeString(this.max()) : undefined;
|
|
365
396
|
return generateTimeOptions(this.formatInput(), this.minuteStep(), this.showSeconds(), minVal ?? undefined, maxVal ?? undefined);
|
|
366
397
|
}, ...(ngDevMode ? [{ debugName: "_timeOptions" }] : /* istanbul ignore next */ []));
|
|
367
|
-
// ViewChild
|
|
398
|
+
// ViewChild — static: true because the panel is always rendered via [hidden]
|
|
368
399
|
inputElement;
|
|
369
400
|
timeListPanel;
|
|
370
|
-
//
|
|
371
|
-
|
|
372
|
-
_overlaySubscriptions = new Subscription();
|
|
373
|
-
_overlayService = inject(FuiOverlayService);
|
|
374
|
-
_elementRef = inject(ElementRef);
|
|
375
|
-
_document = inject(DOCUMENT);
|
|
376
|
-
_ngZone = inject(NgZone);
|
|
377
|
-
intl = inject(FuiTimePickerIntl);
|
|
378
|
-
_cdr = inject(ChangeDetectorRef);
|
|
379
|
-
_outsideClickSub;
|
|
401
|
+
// ViewChild for fuiInput directive — delegates readonly/disabled/aria/state to it
|
|
402
|
+
_fuiInput;
|
|
380
403
|
// CVA callbacks
|
|
381
404
|
_onChange = () => {
|
|
382
405
|
/* noop */
|
|
@@ -384,8 +407,12 @@ class FuiTimePickerComponent {
|
|
|
384
407
|
_onTouched = () => {
|
|
385
408
|
/* noop */
|
|
386
409
|
};
|
|
410
|
+
// Validator callback — null until Angular calls registerOnValidatorChange
|
|
411
|
+
_validatorChange = null;
|
|
387
412
|
constructor() {
|
|
388
|
-
this.intl.changes.pipe(takeUntilDestroyed()).subscribe(() => {
|
|
413
|
+
this.intl.changes.pipe(takeUntilDestroyed()).subscribe(() => {
|
|
414
|
+
this._cdr.markForCheck();
|
|
415
|
+
});
|
|
389
416
|
void Promise.resolve().then(() => {
|
|
390
417
|
if (this._ngControlRef.ngControl) {
|
|
391
418
|
this._ngControlRef.ngControl.valueAccessor = this;
|
|
@@ -410,20 +437,52 @@ class FuiTimePickerComponent {
|
|
|
410
437
|
this._errorState();
|
|
411
438
|
this.stateChanges.next();
|
|
412
439
|
});
|
|
440
|
+
// Shift focus into the panel on open (a11y: moves ESC handling out of
|
|
441
|
+
// any ancestor focus-trap such as fui-dialog) and restore it on close.
|
|
442
|
+
// queueMicrotask defers until Angular has flushed the [hidden] binding,
|
|
443
|
+
// so the panel div is focusable when .focus() fires.
|
|
444
|
+
// _panelOpenInitialized skips the initial synchronous run so the input
|
|
445
|
+
// does not steal focus on first render.
|
|
446
|
+
let _panelOpenInitialized = false;
|
|
447
|
+
effect(() => {
|
|
448
|
+
const isOpen = this._popup.panelOpen();
|
|
449
|
+
if (!_panelOpenInitialized) {
|
|
450
|
+
_panelOpenInitialized = true;
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
if (isOpen) {
|
|
454
|
+
queueMicrotask(() => this.timeListPanel?.nativeElement.focus());
|
|
455
|
+
}
|
|
456
|
+
else {
|
|
457
|
+
queueMicrotask(() => this.inputElement?.nativeElement.focus());
|
|
458
|
+
}
|
|
459
|
+
});
|
|
460
|
+
// Re-trigger validation when min/max signals change so Angular re-runs validate().
|
|
461
|
+
effect(() => {
|
|
462
|
+
this.min();
|
|
463
|
+
this.max();
|
|
464
|
+
this._validatorChange?.();
|
|
465
|
+
});
|
|
413
466
|
}
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
467
|
+
ngAfterViewInit() {
|
|
468
|
+
// Wire popup-overlay directive — panel is always rendered via [hidden]
|
|
469
|
+
// so timeListPanel is available at this point.
|
|
470
|
+
this._popup.setTrigger(this._elementRef);
|
|
471
|
+
this._popup.setPanel(this.timeListPanel ?? null);
|
|
472
|
+
this._popup.panelClass.set(['fui-time-picker-overlay-panel']);
|
|
473
|
+
this._popup.backdropClass.set('fui-time-picker-backdrop');
|
|
474
|
+
// Wire form-control-sync directive signals
|
|
475
|
+
this._formSync.errorState.set(this._errorState);
|
|
476
|
+
this._formSync.errorStateMatcher.set(this.errorStateMatcher());
|
|
477
|
+
this._formSync.required.set(this._required);
|
|
478
|
+
this._formSync.ngControlDisabled.set(this._ngControlDisabled);
|
|
479
|
+
this._formSync.stateChanges.set(this.stateChanges);
|
|
420
480
|
}
|
|
421
481
|
ngOnDestroy() {
|
|
422
482
|
this.stateChanges.complete();
|
|
423
|
-
this._outsideClickSub?.unsubscribe();
|
|
424
|
-
this._disposeOverlay();
|
|
425
483
|
}
|
|
426
484
|
// --- CVA ---
|
|
485
|
+
/** Sets the time value from the form model; accepts an ISO time string or null. */
|
|
427
486
|
writeValue(value) {
|
|
428
487
|
if (value) {
|
|
429
488
|
const parsed = parseISOTimeString(value);
|
|
@@ -434,60 +493,84 @@ class FuiTimePickerComponent {
|
|
|
434
493
|
}
|
|
435
494
|
this.stateChanges.next();
|
|
436
495
|
}
|
|
496
|
+
/** Registers the callback Angular calls when the value should propagate to the model. */
|
|
437
497
|
registerOnChange(fn) {
|
|
438
498
|
this._onChange = fn;
|
|
439
499
|
}
|
|
500
|
+
/** Registers the callback Angular calls when the control should be marked as touched. */
|
|
440
501
|
registerOnTouched(fn) {
|
|
441
502
|
this._onTouched = fn;
|
|
442
503
|
}
|
|
504
|
+
/** Enables or disables the control programmatically; mirrors the `disabled` form-control state. */
|
|
443
505
|
setDisabledState(isDisabled) {
|
|
444
506
|
this._disabled.set(isDisabled);
|
|
445
507
|
this.stateChanges.next();
|
|
446
508
|
}
|
|
509
|
+
// --- Validator ---
|
|
510
|
+
/** Stores the callback Angular calls when validator inputs (min/max) change so the form re-validates. */
|
|
511
|
+
registerOnValidatorChange(fn) {
|
|
512
|
+
this._validatorChange = fn;
|
|
513
|
+
}
|
|
514
|
+
/** Validates the current time value against min and max constraints. Returns null when valid. */
|
|
515
|
+
validate(control) {
|
|
516
|
+
const value = control.value;
|
|
517
|
+
if (value == null || value === '')
|
|
518
|
+
return null;
|
|
519
|
+
const parsed = parseISOTimeString(value);
|
|
520
|
+
if (!parsed)
|
|
521
|
+
return { timeInvalid: { value } };
|
|
522
|
+
const minStr = this.min();
|
|
523
|
+
if (minStr) {
|
|
524
|
+
const min = parseISOTimeString(minStr);
|
|
525
|
+
if (min && compareTime(parsed, min) < 0) {
|
|
526
|
+
return { minTime: { min: minStr, actual: value } };
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
const maxStr = this.max();
|
|
530
|
+
if (maxStr) {
|
|
531
|
+
const max = parseISOTimeString(maxStr);
|
|
532
|
+
if (max && compareTime(parsed, max) > 0) {
|
|
533
|
+
return { maxTime: { max: maxStr, actual: value } };
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
return null;
|
|
537
|
+
}
|
|
447
538
|
// --- FuiFormFieldControl ---
|
|
539
|
+
/** Focuses the time input and opens the time panel when the form-field container is clicked, unless disabled. */
|
|
448
540
|
onContainerClick(_event) {
|
|
449
541
|
if (!this.disabled()) {
|
|
450
542
|
this.inputElement?.nativeElement.focus();
|
|
451
|
-
if (!this.
|
|
543
|
+
if (!this.panelOpen()) {
|
|
452
544
|
this.open();
|
|
453
545
|
}
|
|
454
546
|
}
|
|
455
547
|
}
|
|
548
|
+
/** Stores the space-separated list of IDs for the aria-describedby attribute. */
|
|
456
549
|
setDescribedByIds(ids) {
|
|
457
550
|
this._ariaDescribedby = ids.length ? ids.join(' ') : null;
|
|
458
|
-
|
|
459
|
-
setReadOnly(readOnly) {
|
|
460
|
-
this._readOnly.set(readOnly);
|
|
551
|
+
this._fuiInput?.setDescribedByIds(ids);
|
|
461
552
|
}
|
|
462
553
|
// --- Panel open/close ---
|
|
554
|
+
/** Opens the time list panel. No-op if disabled, readonly, or already open. */
|
|
463
555
|
open() {
|
|
464
|
-
if (this.disabled() || this.readonly() || this.
|
|
556
|
+
if (this.disabled() || this.readonly() || this.panelOpen())
|
|
465
557
|
return;
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
this._focused.set(true);
|
|
469
|
-
setTimeout(() => {
|
|
470
|
-
this._createOverlay();
|
|
471
|
-
this._listenForOutsideClicks();
|
|
472
|
-
});
|
|
473
|
-
});
|
|
558
|
+
this._focused.set(true);
|
|
559
|
+
this._popup.open();
|
|
474
560
|
}
|
|
561
|
+
/** Closes the time list panel. Focus is restored to the input by the constructor effect. No-op if already closed. */
|
|
475
562
|
close() {
|
|
476
|
-
if (!this.
|
|
563
|
+
if (!this.panelOpen())
|
|
477
564
|
return;
|
|
478
|
-
this._outsideClickSub?.unsubscribe();
|
|
479
|
-
this._panelOpen.set(false);
|
|
480
565
|
this._focused.set(false);
|
|
481
|
-
this._disposeOverlay();
|
|
482
566
|
this._onTouched();
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
}, 0);
|
|
567
|
+
this._popup.close();
|
|
568
|
+
// Focus restore is handled by the effect in the constructor.
|
|
486
569
|
}
|
|
487
570
|
_togglePanel() {
|
|
488
571
|
if (this.disabled() || this.readonly())
|
|
489
572
|
return;
|
|
490
|
-
if (this.
|
|
573
|
+
if (this.panelOpen()) {
|
|
491
574
|
this.close();
|
|
492
575
|
}
|
|
493
576
|
else {
|
|
@@ -499,15 +582,16 @@ class FuiTimePickerComponent {
|
|
|
499
582
|
// We handle parsing on blur, not on every keystroke
|
|
500
583
|
}
|
|
501
584
|
_onKeydown(event) {
|
|
502
|
-
if (event.key === 'Escape' && this.
|
|
585
|
+
if (event.key === 'Escape' && this.panelOpen()) {
|
|
503
586
|
event.preventDefault();
|
|
587
|
+
event.stopPropagation();
|
|
504
588
|
this.close();
|
|
505
589
|
}
|
|
506
|
-
else if (event.key === 'ArrowDown' && !this.
|
|
590
|
+
else if (event.key === 'ArrowDown' && !this.panelOpen()) {
|
|
507
591
|
event.preventDefault();
|
|
508
592
|
this.open();
|
|
509
593
|
}
|
|
510
|
-
else if (event.key === 'Enter' && !this.
|
|
594
|
+
else if (event.key === 'Enter' && !this.panelOpen()) {
|
|
511
595
|
event.preventDefault();
|
|
512
596
|
this.open();
|
|
513
597
|
}
|
|
@@ -532,7 +616,7 @@ class FuiTimePickerComponent {
|
|
|
532
616
|
// Invalid input — revert to previous formatted value
|
|
533
617
|
input.value = this._displayValue();
|
|
534
618
|
}
|
|
535
|
-
if (!this.
|
|
619
|
+
if (!this.panelOpen()) {
|
|
536
620
|
this._onTouched();
|
|
537
621
|
}
|
|
538
622
|
}
|
|
@@ -541,26 +625,6 @@ class FuiTimePickerComponent {
|
|
|
541
625
|
this._setValue(value);
|
|
542
626
|
this.close();
|
|
543
627
|
}
|
|
544
|
-
// Start listening for outside clicks when panel opens
|
|
545
|
-
_listenForOutsideClicks() {
|
|
546
|
-
this._outsideClickSub?.unsubscribe();
|
|
547
|
-
this._ngZone.runOutsideAngular(() => {
|
|
548
|
-
setTimeout(() => {
|
|
549
|
-
this._outsideClickSub = fromEvent(this._document, 'click')
|
|
550
|
-
.pipe(filter(() => this._panelOpen()), filter((event) => {
|
|
551
|
-
const target = event.target;
|
|
552
|
-
const hostElement = this._elementRef.nativeElement;
|
|
553
|
-
const overlayElement = this._overlayRef?.overlayElement;
|
|
554
|
-
return !hostElement.contains(target) && !overlayElement?.contains(target);
|
|
555
|
-
}))
|
|
556
|
-
.subscribe(() => {
|
|
557
|
-
this._ngZone.run(() => {
|
|
558
|
-
this.close();
|
|
559
|
-
});
|
|
560
|
-
});
|
|
561
|
-
});
|
|
562
|
-
});
|
|
563
|
-
}
|
|
564
628
|
// --- Private helpers ---
|
|
565
629
|
_setValue(value) {
|
|
566
630
|
this._value.set(value);
|
|
@@ -569,95 +633,71 @@ class FuiTimePickerComponent {
|
|
|
569
633
|
this.timeChange.emit(stringVal ?? '');
|
|
570
634
|
this.stateChanges.next();
|
|
571
635
|
}
|
|
572
|
-
_createOverlay() {
|
|
573
|
-
if (this._overlayRef || !this.timeListPanel)
|
|
574
|
-
return;
|
|
575
|
-
const triggerElement = this._elementRef.nativeElement;
|
|
576
|
-
const triggerWidth = triggerElement.getBoundingClientRect().width;
|
|
577
|
-
const positions = [
|
|
578
|
-
{ originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4 },
|
|
579
|
-
{ originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'bottom', offsetY: -4 },
|
|
580
|
-
];
|
|
581
|
-
const positionStrategy = this._overlayService
|
|
582
|
-
.position()
|
|
583
|
-
.connectedTo(triggerElement, positions)
|
|
584
|
-
.withPush(true)
|
|
585
|
-
.withViewportMargin(8);
|
|
586
|
-
this._overlayRef = this._overlayService.create({
|
|
587
|
-
positionStrategy,
|
|
588
|
-
scrollStrategy: this._overlayService.scrollStrategies.reposition(),
|
|
589
|
-
hasBackdrop: true,
|
|
590
|
-
backdropClass: 'fui-time-picker-backdrop',
|
|
591
|
-
backdropClickBehavior: 'close',
|
|
592
|
-
panelClass: ['fui-time-picker-overlay-panel'],
|
|
593
|
-
minWidth: Math.max(triggerWidth, 160),
|
|
594
|
-
});
|
|
595
|
-
// Track overlay subscriptions for proper cleanup
|
|
596
|
-
this._overlaySubscriptions.unsubscribe();
|
|
597
|
-
this._overlaySubscriptions = new Subscription();
|
|
598
|
-
this._overlaySubscriptions.add(this._overlayRef.backdropClick.subscribe(() => {
|
|
599
|
-
this.close();
|
|
600
|
-
}));
|
|
601
|
-
this._overlaySubscriptions.add(this._overlayRef.keydownEvents.subscribe((event) => {
|
|
602
|
-
if (event.key === 'Escape') {
|
|
603
|
-
this.close();
|
|
604
|
-
}
|
|
605
|
-
}));
|
|
606
|
-
const panelElement = this.timeListPanel.nativeElement;
|
|
607
|
-
this._overlayRef.attach(panelElement);
|
|
608
|
-
}
|
|
609
|
-
_disposeOverlay() {
|
|
610
|
-
this._overlaySubscriptions.unsubscribe();
|
|
611
|
-
if (this._overlayRef) {
|
|
612
|
-
this._overlayRef.dispose();
|
|
613
|
-
this._overlayRef = null;
|
|
614
|
-
}
|
|
615
|
-
}
|
|
616
636
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: FuiTimePickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
617
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.6", type: FuiTimePickerComponent, isStandalone: true, selector: "fui-time-picker", inputs: { formatInput: { classPropertyName: "formatInput", publicName: "format", isSignal: true, isRequired: false, transformFunction: null }, showSeconds: { classPropertyName: "showSeconds", publicName: "showSeconds", isSignal: true, isRequired: false, transformFunction: null }, minuteStep: { classPropertyName: "minuteStep", publicName: "minuteStep", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, placeholderInput: { classPropertyName: "placeholderInput", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, disabledInput: { classPropertyName: "disabledInput", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null },
|
|
637
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.6", type: FuiTimePickerComponent, isStandalone: true, selector: "fui-time-picker", inputs: { formatInput: { classPropertyName: "formatInput", publicName: "format", isSignal: true, isRequired: false, transformFunction: null }, showSeconds: { classPropertyName: "showSeconds", publicName: "showSeconds", isSignal: true, isRequired: false, transformFunction: null }, minuteStep: { classPropertyName: "minuteStep", publicName: "minuteStep", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, placeholderInput: { classPropertyName: "placeholderInput", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, disabledInput: { classPropertyName: "disabledInput", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, readonlyInput: { classPropertyName: "readonlyInput", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, errorStateMatcher: { classPropertyName: "errorStateMatcher", publicName: "errorStateMatcher", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { timeChange: "timeChange" }, host: { properties: { "attr.id": "id", "class.fui-time-picker--open": "panelOpen()", "class.fui-time-picker--disabled": "disabled()", "class.fui-time-picker--focused": "focused()", "class.fui-time-picker--error": "errorState()", "class.fui-time-picker--readonly": "readonly()" }, classAttribute: "fui-time-picker" }, providers: [
|
|
618
638
|
{
|
|
619
639
|
provide: NG_VALUE_ACCESSOR,
|
|
620
640
|
useExisting: FuiTimePickerComponent,
|
|
621
641
|
multi: true,
|
|
622
642
|
},
|
|
643
|
+
{
|
|
644
|
+
provide: NG_VALIDATORS,
|
|
645
|
+
useExisting: FuiTimePickerComponent,
|
|
646
|
+
multi: true,
|
|
647
|
+
},
|
|
623
648
|
{
|
|
624
649
|
provide: FUI_FORM_FIELD_CONTROL,
|
|
625
650
|
useExisting: FuiTimePickerComponent,
|
|
626
651
|
},
|
|
627
|
-
], viewQueries: [{ propertyName: "inputElement", first: true, predicate: ["inputElement"], descendants: true }, { propertyName: "timeListPanel", first: true, predicate: ["timeListPanel"], descendants: true }], ngImport: i0, template: "<div class=\"fui-time-picker__wrapper\">\r\n <input\r\n #inputElement\r\n class=\"fui-time-picker__input\"\r\n type=\"text\"\r\n [
|
|
652
|
+
], viewQueries: [{ propertyName: "inputElement", first: true, predicate: ["inputElement"], descendants: true }, { propertyName: "timeListPanel", first: true, predicate: ["timeListPanel"], descendants: true, static: true }, { propertyName: "_fuiInput", first: true, predicate: FuiInputDirective, descendants: true }], hostDirectives: [{ directive: i1.FuiPopupOverlayDirective, inputs: ["positions", "positions", "panelClass", "panelClass", "backdropClass", "backdropClass", "scrollStrategy", "scrollStrategy", "minWidthFromTrigger", "minWidthFromTrigger"], outputs: ["openedChange", "openedChange", "escapeKey", "escapeKey"] }, { directive: i1.FuiFormControlSyncDirective }], ngImport: i0, template: "<div class=\"fui-time-picker__wrapper\">\r\n <input\r\n #inputElement\r\n fuiInput\r\n class=\"fui-time-picker__input\"\r\n type=\"text\"\r\n [placeholder]=\"placeholder()\"\r\n [disabled]=\"disabled()\"\r\n [readonly]=\"readonly()\"\r\n [value]=\"_displayValue()\"\r\n [attr.aria-expanded]=\"panelOpen()\"\r\n [attr.aria-haspopup]=\"'listbox'\"\r\n role=\"combobox\"\r\n autocomplete=\"off\"\r\n (input)=\"_onManualInput($event)\"\r\n (keydown)=\"_onKeydown($event)\"\r\n (focus)=\"_onFocus()\"\r\n (blur)=\"_onBlur()\"\r\n />\r\n @if (!disabled() && !readonly()) {\r\n <button\r\n type=\"button\"\r\n class=\"fui-time-picker__toggle\"\r\n (click)=\"_togglePanel()\"\r\n tabindex=\"-1\"\r\n [attr.aria-label]=\"intl.toggleAriaLabel\"\r\n >\r\n <fui-icon name=\"clock\" size=\"sm\"></fui-icon>\r\n </button>\r\n }\r\n</div>\r\n\r\n<div\r\n #timeListPanel\r\n class=\"fui-time-picker__panel\"\r\n role=\"dialog\"\r\n tabindex=\"-1\"\r\n [attr.aria-label]=\"intl.dialogAriaLabel\"\r\n [hidden]=\"!panelOpen()\"\r\n>\r\n <fui-time-list\r\n [options]=\"_timeOptions()\"\r\n [selectedValue]=\"_value()\"\r\n [format]=\"formatInput()\"\r\n (optionSelected)=\"_onOptionSelected($event)\"\r\n />\r\n</div>\r\n", styles: ["@keyframes fui-skeleton-pulse{0%{opacity:1}50%{opacity:.4}to{opacity:1}}@keyframes fui-spin{to{transform:rotate(360deg)}}@keyframes fui-shake{0%,to{transform:translate(0)}10%,30%,50%,70%,90%{transform:translate(-2px)}20%,40%,60%,80%{transform:translate(2px)}}.fui-motion-fade-in{transition-property:opacity;transition-duration:var(--fui-duration-fast);transition-timing-function:var(--fui-ease-out);transition-delay:0ms}.fui-motion-fade-out{transition-property:opacity;transition-duration:var(--fui-duration-fast);transition-timing-function:var(--fui-ease-in);transition-delay:0ms}.fui-motion-slide-in-bottom{transition-property:transform;transition-duration:var(--fui-duration-base);transition-timing-function:var(--fui-ease-out);transition-delay:0ms;transform:translateY(0)}.fui-motion-slide-in-bottom.fui-motion-entering{transform:translateY(1rem)}.fui-motion-slide-in-top{transition-property:transform;transition-duration:var(--fui-duration-base);transition-timing-function:var(--fui-ease-out);transition-delay:0ms;transform:translateY(0)}.fui-motion-slide-in-top.fui-motion-entering{transform:translateY(-1rem)}.fui-motion-scale-in{transition-property:transform,opacity;transition-duration:var(--fui-duration-base);transition-timing-function:var(--fui-ease-out);transition-delay:0ms;transform:scale(1);opacity:1}.fui-motion-scale-in.fui-motion-entering{transform:scale(.95);opacity:0}.fui-no-motion{transition:none!important;animation:none!important}@media(prefers-reduced-motion:reduce){*,*:before,*:after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}}@keyframes fui-pulse{0%{transform:scale(1);opacity:1}50%{transform:scale(1.05)}to{transform:scale(1);opacity:1}}@keyframes fui-slide-in{0%{transform:translate(120%)}to{transform:translate(0)}}.fui-slide-in{animation:fui-slide-in var(--fui-duration-base) var(--fui-ease-out)}@keyframes fui-popover-enter{0%{opacity:0;transform:translateY(-14px)}60%{opacity:1}to{opacity:1;transform:translateY(0)}}.fui-time-picker{--fui-time-picker-font-size: var(--fui-text-base);--fui-time-picker-toggle-size: 1.5rem;display:inline-block;width:100%;position:relative}.fui-time-picker__wrapper{width:100%;min-height:100%;display:flex;align-items:center;gap:var(--fui-spacing-1);cursor:pointer;background:transparent;border:none}.fui-time-picker__input{flex:1;min-width:0;border:none;outline:none;background:transparent;font-family:var(--fui-font-sans);font-size:var(--fui-text-base);font-weight:var(--fui-weight-regular);line-height:var(--fui-leading-normal);letter-spacing:var(--fui-tracking-normal);color:var(--fui-text-primary);padding:0;cursor:text}.fui-time-picker__input::placeholder{color:var(--fui-text-disabled)}.fui-time-picker__input:disabled{cursor:not-allowed;opacity:.5}.fui-time-picker__toggle{background:none;border:none;padding:0;margin:0;font:inherit;color:inherit;cursor:pointer;outline:none}.fui-time-picker__toggle:focus-visible{outline:2px solid var(--fui-primary-10)}.fui-time-picker__toggle{display:flex;align-items:center;justify-content:center;flex-shrink:0;width:var(--fui-time-picker-toggle-size);height:var(--fui-time-picker-toggle-size);color:var(--fui-text-secondary);border-radius:var(--fui-radius-sm);transition-property:color;transition-duration:var(--fui-duration-fast);transition-timing-function:var(--fui-ease-in-out);transition-delay:0ms}.fui-time-picker__toggle:hover:not(:disabled){color:var(--fui-text-primary)}.fui-time-picker__toggle:disabled{color:var(--fui-text-disabled);cursor:not-allowed}.fui-time-picker--disabled{pointer-events:none}.fui-time-picker--disabled .fui-time-picker__wrapper{cursor:not-allowed;opacity:.5}.fui-time-picker--readonly{pointer-events:none}.fui-time-picker--readonly .fui-time-picker__wrapper{cursor:default;opacity:1}.fui-time-picker--readonly .fui-time-picker__input{color:var(--fui-text-primary)!important;-webkit-text-fill-color:var(--fui-text-primary)!important;cursor:default;opacity:1}.fui-time-picker--readonly .fui-time-picker__input:disabled{opacity:1;cursor:default}.fui-time-picker__panel{--fui-time-picker-panel-border-radius: var(--fui-radius-md);--fui-time-picker-panel-shadow: var(--fui-shadow-md);--fui-time-picker-panel-bg: var(--fui-bg-default);--fui-time-picker-panel-border-color: var(--fui-border-default);background:var(--fui-time-picker-panel-bg);border:var(--fui-border-width-sm) solid var(--fui-time-picker-panel-border-color);border-radius:var(--fui-time-picker-panel-border-radius);box-shadow:var(--fui-time-picker-panel-shadow);transition-property:opacity,transform;transition-duration:var(--fui-duration-fast);transition-timing-function:var(--fui-ease-in-out);transition-delay:0ms}.fui-time-picker-backdrop{background:transparent}@media(prefers-contrast:high){.fui-time-picker__panel{border-width:2px}}@media(prefers-reduced-motion:reduce){.fui-time-picker__panel{transition:none}}\n"], dependencies: [{ kind: "directive", type: FuiInputDirective, selector: "input[fuiInput], textarea[fuiInput], select[fuiInput]", inputs: ["type", "placeholder", "readonly", "maxlength", "minlength", "pattern", "errorStateMatcher", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: FuiIconComponent, selector: "fui-icon", inputs: ["name", "size", "weight", "color", "ariaLabel", "spin", "pulse"] }, { kind: "component", type: FuiTimeListComponent, selector: "fui-time-list", inputs: ["options", "selectedValue", "format"], outputs: ["optionSelected"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
628
653
|
}
|
|
629
654
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: FuiTimePickerComponent, decorators: [{
|
|
630
655
|
type: Component,
|
|
631
|
-
args: [{ selector: 'fui-time-picker', standalone: true, imports: [FuiIconComponent, FuiTimeListComponent], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None,
|
|
656
|
+
args: [{ selector: 'fui-time-picker', standalone: true, imports: [FuiInputDirective, FuiIconComponent, FuiTimeListComponent], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, hostDirectives: [
|
|
657
|
+
{
|
|
658
|
+
directive: FuiPopupOverlayDirective,
|
|
659
|
+
inputs: ['positions', 'panelClass', 'backdropClass', 'scrollStrategy', 'minWidthFromTrigger'],
|
|
660
|
+
outputs: ['openedChange', 'escapeKey'],
|
|
661
|
+
},
|
|
662
|
+
FuiFormControlSyncDirective,
|
|
663
|
+
], host: {
|
|
632
664
|
class: 'fui-time-picker',
|
|
633
665
|
'[attr.id]': 'id',
|
|
634
|
-
'[class.fui-time-picker--open]': '
|
|
666
|
+
'[class.fui-time-picker--open]': 'panelOpen()',
|
|
635
667
|
'[class.fui-time-picker--disabled]': 'disabled()',
|
|
636
668
|
'[class.fui-time-picker--focused]': 'focused()',
|
|
637
669
|
'[class.fui-time-picker--error]': 'errorState()',
|
|
638
|
-
'[class.fui-time-picker--readonly]': '
|
|
670
|
+
'[class.fui-time-picker--readonly]': 'readonly()',
|
|
639
671
|
}, providers: [
|
|
640
672
|
{
|
|
641
673
|
provide: NG_VALUE_ACCESSOR,
|
|
642
674
|
useExisting: FuiTimePickerComponent,
|
|
643
675
|
multi: true,
|
|
644
676
|
},
|
|
677
|
+
{
|
|
678
|
+
provide: NG_VALIDATORS,
|
|
679
|
+
useExisting: FuiTimePickerComponent,
|
|
680
|
+
multi: true,
|
|
681
|
+
},
|
|
645
682
|
{
|
|
646
683
|
provide: FUI_FORM_FIELD_CONTROL,
|
|
647
684
|
useExisting: FuiTimePickerComponent,
|
|
648
685
|
},
|
|
649
|
-
], template: "<div class=\"fui-time-picker__wrapper\">\r\n <input\r\n #inputElement\r\n class=\"fui-time-picker__input\"\r\n type=\"text\"\r\n [
|
|
650
|
-
}], ctorParameters: () => [], propDecorators: { formatInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "format", required: false }] }], showSeconds: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSeconds", required: false }] }], minuteStep: [{ type: i0.Input, args: [{ isSignal: true, alias: "minuteStep", required: false }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], placeholderInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], disabledInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }],
|
|
686
|
+
], template: "<div class=\"fui-time-picker__wrapper\">\r\n <input\r\n #inputElement\r\n fuiInput\r\n class=\"fui-time-picker__input\"\r\n type=\"text\"\r\n [placeholder]=\"placeholder()\"\r\n [disabled]=\"disabled()\"\r\n [readonly]=\"readonly()\"\r\n [value]=\"_displayValue()\"\r\n [attr.aria-expanded]=\"panelOpen()\"\r\n [attr.aria-haspopup]=\"'listbox'\"\r\n role=\"combobox\"\r\n autocomplete=\"off\"\r\n (input)=\"_onManualInput($event)\"\r\n (keydown)=\"_onKeydown($event)\"\r\n (focus)=\"_onFocus()\"\r\n (blur)=\"_onBlur()\"\r\n />\r\n @if (!disabled() && !readonly()) {\r\n <button\r\n type=\"button\"\r\n class=\"fui-time-picker__toggle\"\r\n (click)=\"_togglePanel()\"\r\n tabindex=\"-1\"\r\n [attr.aria-label]=\"intl.toggleAriaLabel\"\r\n >\r\n <fui-icon name=\"clock\" size=\"sm\"></fui-icon>\r\n </button>\r\n }\r\n</div>\r\n\r\n<div\r\n #timeListPanel\r\n class=\"fui-time-picker__panel\"\r\n role=\"dialog\"\r\n tabindex=\"-1\"\r\n [attr.aria-label]=\"intl.dialogAriaLabel\"\r\n [hidden]=\"!panelOpen()\"\r\n>\r\n <fui-time-list\r\n [options]=\"_timeOptions()\"\r\n [selectedValue]=\"_value()\"\r\n [format]=\"formatInput()\"\r\n (optionSelected)=\"_onOptionSelected($event)\"\r\n />\r\n</div>\r\n", styles: ["@keyframes fui-skeleton-pulse{0%{opacity:1}50%{opacity:.4}to{opacity:1}}@keyframes fui-spin{to{transform:rotate(360deg)}}@keyframes fui-shake{0%,to{transform:translate(0)}10%,30%,50%,70%,90%{transform:translate(-2px)}20%,40%,60%,80%{transform:translate(2px)}}.fui-motion-fade-in{transition-property:opacity;transition-duration:var(--fui-duration-fast);transition-timing-function:var(--fui-ease-out);transition-delay:0ms}.fui-motion-fade-out{transition-property:opacity;transition-duration:var(--fui-duration-fast);transition-timing-function:var(--fui-ease-in);transition-delay:0ms}.fui-motion-slide-in-bottom{transition-property:transform;transition-duration:var(--fui-duration-base);transition-timing-function:var(--fui-ease-out);transition-delay:0ms;transform:translateY(0)}.fui-motion-slide-in-bottom.fui-motion-entering{transform:translateY(1rem)}.fui-motion-slide-in-top{transition-property:transform;transition-duration:var(--fui-duration-base);transition-timing-function:var(--fui-ease-out);transition-delay:0ms;transform:translateY(0)}.fui-motion-slide-in-top.fui-motion-entering{transform:translateY(-1rem)}.fui-motion-scale-in{transition-property:transform,opacity;transition-duration:var(--fui-duration-base);transition-timing-function:var(--fui-ease-out);transition-delay:0ms;transform:scale(1);opacity:1}.fui-motion-scale-in.fui-motion-entering{transform:scale(.95);opacity:0}.fui-no-motion{transition:none!important;animation:none!important}@media(prefers-reduced-motion:reduce){*,*:before,*:after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}}@keyframes fui-pulse{0%{transform:scale(1);opacity:1}50%{transform:scale(1.05)}to{transform:scale(1);opacity:1}}@keyframes fui-slide-in{0%{transform:translate(120%)}to{transform:translate(0)}}.fui-slide-in{animation:fui-slide-in var(--fui-duration-base) var(--fui-ease-out)}@keyframes fui-popover-enter{0%{opacity:0;transform:translateY(-14px)}60%{opacity:1}to{opacity:1;transform:translateY(0)}}.fui-time-picker{--fui-time-picker-font-size: var(--fui-text-base);--fui-time-picker-toggle-size: 1.5rem;display:inline-block;width:100%;position:relative}.fui-time-picker__wrapper{width:100%;min-height:100%;display:flex;align-items:center;gap:var(--fui-spacing-1);cursor:pointer;background:transparent;border:none}.fui-time-picker__input{flex:1;min-width:0;border:none;outline:none;background:transparent;font-family:var(--fui-font-sans);font-size:var(--fui-text-base);font-weight:var(--fui-weight-regular);line-height:var(--fui-leading-normal);letter-spacing:var(--fui-tracking-normal);color:var(--fui-text-primary);padding:0;cursor:text}.fui-time-picker__input::placeholder{color:var(--fui-text-disabled)}.fui-time-picker__input:disabled{cursor:not-allowed;opacity:.5}.fui-time-picker__toggle{background:none;border:none;padding:0;margin:0;font:inherit;color:inherit;cursor:pointer;outline:none}.fui-time-picker__toggle:focus-visible{outline:2px solid var(--fui-primary-10)}.fui-time-picker__toggle{display:flex;align-items:center;justify-content:center;flex-shrink:0;width:var(--fui-time-picker-toggle-size);height:var(--fui-time-picker-toggle-size);color:var(--fui-text-secondary);border-radius:var(--fui-radius-sm);transition-property:color;transition-duration:var(--fui-duration-fast);transition-timing-function:var(--fui-ease-in-out);transition-delay:0ms}.fui-time-picker__toggle:hover:not(:disabled){color:var(--fui-text-primary)}.fui-time-picker__toggle:disabled{color:var(--fui-text-disabled);cursor:not-allowed}.fui-time-picker--disabled{pointer-events:none}.fui-time-picker--disabled .fui-time-picker__wrapper{cursor:not-allowed;opacity:.5}.fui-time-picker--readonly{pointer-events:none}.fui-time-picker--readonly .fui-time-picker__wrapper{cursor:default;opacity:1}.fui-time-picker--readonly .fui-time-picker__input{color:var(--fui-text-primary)!important;-webkit-text-fill-color:var(--fui-text-primary)!important;cursor:default;opacity:1}.fui-time-picker--readonly .fui-time-picker__input:disabled{opacity:1;cursor:default}.fui-time-picker__panel{--fui-time-picker-panel-border-radius: var(--fui-radius-md);--fui-time-picker-panel-shadow: var(--fui-shadow-md);--fui-time-picker-panel-bg: var(--fui-bg-default);--fui-time-picker-panel-border-color: var(--fui-border-default);background:var(--fui-time-picker-panel-bg);border:var(--fui-border-width-sm) solid var(--fui-time-picker-panel-border-color);border-radius:var(--fui-time-picker-panel-border-radius);box-shadow:var(--fui-time-picker-panel-shadow);transition-property:opacity,transform;transition-duration:var(--fui-duration-fast);transition-timing-function:var(--fui-ease-in-out);transition-delay:0ms}.fui-time-picker-backdrop{background:transparent}@media(prefers-contrast:high){.fui-time-picker__panel{border-width:2px}}@media(prefers-reduced-motion:reduce){.fui-time-picker__panel{transition:none}}\n"] }]
|
|
687
|
+
}], ctorParameters: () => [], propDecorators: { formatInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "format", required: false }] }], showSeconds: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSeconds", required: false }] }], minuteStep: [{ type: i0.Input, args: [{ isSignal: true, alias: "minuteStep", required: false }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], placeholderInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], disabledInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], readonlyInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], errorStateMatcher: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorStateMatcher", required: false }] }], timeChange: [{ type: i0.Output, args: ["timeChange"] }], inputElement: [{
|
|
651
688
|
type: ViewChild,
|
|
652
689
|
args: ['inputElement', { static: false }]
|
|
653
690
|
}], timeListPanel: [{
|
|
654
691
|
type: ViewChild,
|
|
655
|
-
args: ['timeListPanel', { static:
|
|
692
|
+
args: ['timeListPanel', { static: true }]
|
|
693
|
+
}], _fuiInput: [{
|
|
694
|
+
type: ViewChild,
|
|
695
|
+
args: [FuiInputDirective, { static: false }]
|
|
656
696
|
}] } });
|
|
657
697
|
|
|
658
698
|
/**
|
|
659
699
|
* Generated bundle index. Do not edit.
|
|
660
700
|
*/
|
|
661
701
|
|
|
662
|
-
export { FuiTimeListComponent, FuiTimePickerComponent, FuiTimePickerIntl, compareTime, formatTime, generateTimeOptions, isTimeInRange, parseISOTimeString, parseTimeString, timeToMinutes, timeValueToString };
|
|
702
|
+
export { FuiTimeListComponent, FuiTimePickerComponent, FuiTimePickerIntl, compareTime, formatTime, generateTimeOptions, isTimeInRange, parseISOTimeString, parseTimeString, timeToMinutes, timeToSeconds, timeValueToString };
|
|
663
703
|
//# sourceMappingURL=raintonic-formaui-components-time-picker.mjs.map
|