ngx-com 0.0.22 → 0.1.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 +137 -33
- package/fesm2022/ngx-com-components-alert.mjs +21 -11
- package/fesm2022/ngx-com-components-alert.mjs.map +1 -1
- package/fesm2022/ngx-com-components-avatar.mjs +9 -7
- package/fesm2022/ngx-com-components-avatar.mjs.map +1 -1
- package/fesm2022/ngx-com-components-button.mjs +1 -1
- package/fesm2022/ngx-com-components-button.mjs.map +1 -1
- package/fesm2022/ngx-com-components-calendar.mjs +27 -3410
- package/fesm2022/ngx-com-components-calendar.mjs.map +1 -1
- package/fesm2022/ngx-com-components-card.mjs +8 -8
- package/fesm2022/ngx-com-components-card.mjs.map +1 -1
- package/fesm2022/ngx-com-components-carousel.mjs +16 -4
- package/fesm2022/ngx-com-components-carousel.mjs.map +1 -1
- package/fesm2022/ngx-com-components-checkbox.mjs +1 -1
- package/fesm2022/ngx-com-components-checkbox.mjs.map +1 -1
- package/fesm2022/ngx-com-components-code-block.mjs +9 -9
- package/fesm2022/ngx-com-components-code-block.mjs.map +1 -1
- package/fesm2022/ngx-com-components-collapsible.mjs +15 -13
- package/fesm2022/ngx-com-components-collapsible.mjs.map +1 -1
- package/fesm2022/ngx-com-components-confirm.mjs +4 -4
- package/fesm2022/ngx-com-components-confirm.mjs.map +1 -1
- package/fesm2022/ngx-com-components-datepicker.mjs +2334 -0
- package/fesm2022/ngx-com-components-datepicker.mjs.map +1 -0
- package/fesm2022/ngx-com-components-dialog.mjs +47 -45
- package/fesm2022/ngx-com-components-dialog.mjs.map +1 -1
- package/fesm2022/ngx-com-components-dropdown.mjs +446 -340
- package/fesm2022/ngx-com-components-dropdown.mjs.map +1 -1
- package/fesm2022/ngx-com-components-empty-state.mjs +5 -3
- package/fesm2022/ngx-com-components-empty-state.mjs.map +1 -1
- package/fesm2022/ngx-com-components-form-field.mjs +9 -4
- package/fesm2022/ngx-com-components-form-field.mjs.map +1 -1
- package/fesm2022/ngx-com-components-icon-lucide.mjs +41 -0
- package/fesm2022/ngx-com-components-icon-lucide.mjs.map +1 -0
- package/fesm2022/ngx-com-components-icon.mjs +89 -61
- package/fesm2022/ngx-com-components-icon.mjs.map +1 -1
- package/fesm2022/ngx-com-components-item.mjs +14 -4
- package/fesm2022/ngx-com-components-item.mjs.map +1 -1
- package/fesm2022/ngx-com-components-menu.mjs +61 -69
- package/fesm2022/ngx-com-components-menu.mjs.map +1 -1
- package/fesm2022/ngx-com-components-paginator.mjs +11 -3
- package/fesm2022/ngx-com-components-paginator.mjs.map +1 -1
- package/fesm2022/ngx-com-components-popover.mjs +58 -33
- package/fesm2022/ngx-com-components-popover.mjs.map +1 -1
- package/fesm2022/ngx-com-components-radio.mjs +4 -4
- package/fesm2022/ngx-com-components-radio.mjs.map +1 -1
- package/fesm2022/ngx-com-components-segmented-control.mjs +6 -4
- package/fesm2022/ngx-com-components-segmented-control.mjs.map +1 -1
- package/fesm2022/ngx-com-components-sort.mjs +63 -57
- package/fesm2022/ngx-com-components-sort.mjs.map +1 -1
- package/fesm2022/ngx-com-components-spinner.mjs +6 -6
- package/fesm2022/ngx-com-components-spinner.mjs.map +1 -1
- package/fesm2022/ngx-com-components-switch.mjs +2 -2
- package/fesm2022/ngx-com-components-switch.mjs.map +1 -1
- package/fesm2022/ngx-com-components-table.mjs +23 -9
- package/fesm2022/ngx-com-components-table.mjs.map +1 -1
- package/fesm2022/ngx-com-components-tabs.mjs +81 -58
- package/fesm2022/ngx-com-components-tabs.mjs.map +1 -1
- package/fesm2022/ngx-com-components-timepicker.mjs +1048 -0
- package/fesm2022/ngx-com-components-timepicker.mjs.map +1 -0
- package/fesm2022/ngx-com-components-toast.mjs +18 -14
- package/fesm2022/ngx-com-components-toast.mjs.map +1 -1
- package/fesm2022/ngx-com-components-tooltip.mjs +5 -5
- package/fesm2022/ngx-com-components-tooltip.mjs.map +1 -1
- package/fesm2022/ngx-com-components.mjs +0 -13
- package/fesm2022/ngx-com-components.mjs.map +1 -1
- package/fesm2022/ngx-com-tokens.mjs +0 -8
- package/fesm2022/ngx-com-tokens.mjs.map +1 -1
- package/fesm2022/ngx-com-utils.mjs +13 -1
- package/fesm2022/ngx-com-utils.mjs.map +1 -1
- package/fesm2022/ngx-com.mjs +1 -1
- package/fesm2022/ngx-com.mjs.map +1 -1
- package/package.json +47 -8
- package/styles/animations.css +38 -0
- package/styles/candy.css +121 -0
- package/styles/dark.css +159 -0
- package/styles/forest.css +117 -0
- package/styles/ocean.css +117 -0
- package/styles/themes.css +7 -0
- package/styles/tokens.css +277 -0
- package/styles/utilities.css +16 -0
- package/types/ngx-com-components-alert.d.ts +14 -4
- package/types/ngx-com-components-avatar.d.ts +2 -0
- package/types/ngx-com-components-calendar.d.ts +3 -883
- package/types/ngx-com-components-card.d.ts +2 -2
- package/types/ngx-com-components-carousel.d.ts +11 -1
- package/types/ngx-com-components-code-block.d.ts +4 -4
- package/types/ngx-com-components-collapsible.d.ts +10 -2
- package/types/ngx-com-components-confirm.d.ts +2 -2
- package/types/ngx-com-components-datepicker.d.ts +623 -0
- package/types/ngx-com-components-dialog.d.ts +5 -2
- package/types/ngx-com-components-dropdown.d.ts +22 -4
- package/types/ngx-com-components-empty-state.d.ts +2 -0
- package/types/ngx-com-components-form-field.d.ts +4 -1
- package/types/ngx-com-components-icon-lucide.d.ts +32 -0
- package/types/ngx-com-components-icon.d.ts +49 -35
- package/types/ngx-com-components-item.d.ts +12 -2
- package/types/ngx-com-components-menu.d.ts +38 -38
- package/types/ngx-com-components-paginator.d.ts +2 -0
- package/types/ngx-com-components-popover.d.ts +19 -12
- package/types/ngx-com-components-segmented-control.d.ts +3 -1
- package/types/ngx-com-components-sort.d.ts +13 -10
- package/types/ngx-com-components-table.d.ts +16 -2
- package/types/ngx-com-components-tabs.d.ts +46 -34
- package/types/ngx-com-components-timepicker.d.ts +273 -0
- package/types/ngx-com-components-toast.d.ts +4 -2
- package/types/ngx-com-components-tooltip.d.ts +1 -1
- package/types/ngx-com-components.d.ts +6 -7
- package/types/ngx-com-tokens.d.ts +5 -3
- package/types/ngx-com-utils.d.ts +11 -1
- package/types/ngx-com.d.ts +1 -1
|
@@ -1,16 +1,7 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { InjectionToken, inject, LOCALE_ID, Injectable, viewChild, input, output, computed, ChangeDetectionStrategy, Component, signal, afterEveryRender, Directive, viewChildren, linkedSignal
|
|
2
|
+
import { InjectionToken, inject, LOCALE_ID, Injectable, viewChild, input, output, computed, ChangeDetectionStrategy, Component, signal, afterEveryRender, Directive, viewChildren, linkedSignal } from '@angular/core';
|
|
3
3
|
import { cva } from 'class-variance-authority';
|
|
4
4
|
import { NgTemplateOutlet } from '@angular/common';
|
|
5
|
-
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
6
|
-
import { NgControl, NgForm, FormGroupDirective } from '@angular/forms';
|
|
7
|
-
import { Overlay, OverlayModule } from '@angular/cdk/overlay';
|
|
8
|
-
import { TemplatePortal } from '@angular/cdk/portal';
|
|
9
|
-
import * as i1 from '@angular/cdk/a11y';
|
|
10
|
-
import { A11yModule } from '@angular/cdk/a11y';
|
|
11
|
-
import { ComIcon } from 'ngx-com/components/icon';
|
|
12
|
-
import { mergeClasses } from 'ngx-com/utils';
|
|
13
|
-
import { ErrorStateMatcher, FormFieldControl } from 'ngx-com/components/form-field';
|
|
14
5
|
|
|
15
6
|
/**
|
|
16
7
|
* Calendar shared types and interfaces
|
|
@@ -341,14 +332,14 @@ const calendarCellVariants = cva(
|
|
|
341
332
|
'cursor-pointer',
|
|
342
333
|
'select-none',
|
|
343
334
|
'transition-colors',
|
|
344
|
-
'duration-
|
|
335
|
+
'duration-normal',
|
|
345
336
|
'focus-visible:outline-[1px]',
|
|
346
337
|
'focus-visible:outline-offset-2',
|
|
347
338
|
'focus-visible:outline-ring',
|
|
348
339
|
], {
|
|
349
340
|
variants: {
|
|
350
341
|
view: {
|
|
351
|
-
month: ['h-9', 'w-9', 'text-sm', 'rounded-
|
|
342
|
+
month: ['h-9', 'w-9', 'text-sm', 'rounded-pill'],
|
|
352
343
|
year: ['h-10', 'w-16', 'text-sm', 'rounded-calendar-cell'],
|
|
353
344
|
'multi-year': ['h-10', 'w-14', 'text-sm', 'rounded-calendar-cell'],
|
|
354
345
|
},
|
|
@@ -371,7 +362,7 @@ const calendarCellVariants = cva(
|
|
|
371
362
|
'bg-primary',
|
|
372
363
|
'text-primary-foreground',
|
|
373
364
|
'font-semibold',
|
|
374
|
-
'rounded-l-
|
|
365
|
+
'rounded-l-pill',
|
|
375
366
|
'rounded-r-none',
|
|
376
367
|
'hover:bg-primary-hover',
|
|
377
368
|
],
|
|
@@ -385,14 +376,17 @@ const calendarCellVariants = cva(
|
|
|
385
376
|
'bg-primary',
|
|
386
377
|
'text-primary-foreground',
|
|
387
378
|
'font-semibold',
|
|
388
|
-
'rounded-r-
|
|
379
|
+
'rounded-r-pill',
|
|
389
380
|
'rounded-l-none',
|
|
390
381
|
'hover:bg-primary-hover',
|
|
391
382
|
],
|
|
383
|
+
// Preview states use opacity-70 intentionally: the reduced opacity distinguishes
|
|
384
|
+
// the hover preview range from the committed selection, providing clear UX feedback.
|
|
385
|
+
// No semantic token exists for this transient visual state.
|
|
392
386
|
'preview-start': [
|
|
393
387
|
'bg-primary-subtle',
|
|
394
388
|
'text-primary-subtle-foreground',
|
|
395
|
-
'rounded-l-
|
|
389
|
+
'rounded-l-pill',
|
|
396
390
|
'rounded-r-none',
|
|
397
391
|
'ring-1',
|
|
398
392
|
'ring-ring',
|
|
@@ -407,7 +401,7 @@ const calendarCellVariants = cva(
|
|
|
407
401
|
'preview-end': [
|
|
408
402
|
'bg-primary-subtle',
|
|
409
403
|
'text-primary-subtle-foreground',
|
|
410
|
-
'rounded-r-
|
|
404
|
+
'rounded-r-pill',
|
|
411
405
|
'rounded-l-none',
|
|
412
406
|
'ring-1',
|
|
413
407
|
'ring-ring',
|
|
@@ -446,9 +440,9 @@ const calendarCellWrapperVariants = cva(['relative', 'flex', 'items-center', 'ju
|
|
|
446
440
|
variants: {
|
|
447
441
|
range: {
|
|
448
442
|
none: [],
|
|
449
|
-
start: ['bg-primary-subtle', 'rounded-l-
|
|
443
|
+
start: ['bg-primary-subtle', 'rounded-l-pill'],
|
|
450
444
|
middle: ['bg-primary-subtle'],
|
|
451
|
-
end: ['bg-primary-subtle', 'rounded-r-
|
|
445
|
+
end: ['bg-primary-subtle', 'rounded-r-pill'],
|
|
452
446
|
single: [],
|
|
453
447
|
},
|
|
454
448
|
},
|
|
@@ -483,7 +477,7 @@ const calendarHeaderButtonVariants = cva([
|
|
|
483
477
|
'justify-center',
|
|
484
478
|
'select-none',
|
|
485
479
|
'transition-colors',
|
|
486
|
-
'duration-
|
|
480
|
+
'duration-normal',
|
|
487
481
|
'text-foreground',
|
|
488
482
|
'focus-visible:outline-[1px]',
|
|
489
483
|
'focus-visible:outline-offset-2',
|
|
@@ -496,7 +490,7 @@ const calendarHeaderButtonVariants = cva([
|
|
|
496
490
|
navigation: [
|
|
497
491
|
'h-9',
|
|
498
492
|
'w-9',
|
|
499
|
-
'rounded-
|
|
493
|
+
'rounded-pill',
|
|
500
494
|
'hover:bg-muted',
|
|
501
495
|
],
|
|
502
496
|
period: [
|
|
@@ -698,17 +692,6 @@ function isYearDisabled(year, minDate, maxDate, adapter) {
|
|
|
698
692
|
function getMultiYearStartingYear(activeYear, yearsPerPage = 24) {
|
|
699
693
|
return Math.floor(activeYear / yearsPerPage) * yearsPerPage;
|
|
700
694
|
}
|
|
701
|
-
/**
|
|
702
|
-
* Joins CSS class strings, filtering out null/undefined values.
|
|
703
|
-
* Unlike `mergeClasses` from utils, this does NOT resolve Tailwind conflicts -
|
|
704
|
-
* it simply concatenates class strings. Use when classes are guaranteed not to conflict.
|
|
705
|
-
*
|
|
706
|
-
* @param classes - Class strings (may include null/undefined)
|
|
707
|
-
* @returns Space-separated class string
|
|
708
|
-
*/
|
|
709
|
-
function joinClasses(...classes) {
|
|
710
|
-
return classes.filter(Boolean).join(' ');
|
|
711
|
-
}
|
|
712
695
|
|
|
713
696
|
/**
|
|
714
697
|
* A single cell in the calendar grid.
|
|
@@ -1294,7 +1277,7 @@ const TOTAL_CELLS = DAYS_PER_WEEK * WEEKS_PER_MONTH;
|
|
|
1294
1277
|
class ComCalendarMonthView extends CalendarViewBase {
|
|
1295
1278
|
view = 'month';
|
|
1296
1279
|
/** Cell components for focus management */
|
|
1297
|
-
cellComponents = viewChildren(
|
|
1280
|
+
cellComponents = viewChildren(ComCalendarCell, ...(ngDevMode ? [{ debugName: "cellComponents" }] : []));
|
|
1298
1281
|
/** Override first day of week (0=Sun, 1=Mon, ..., 6=Sat) */
|
|
1299
1282
|
firstDayOfWeek = input(0, ...(ngDevMode ? [{ debugName: "firstDayOfWeek" }] : []));
|
|
1300
1283
|
/** Grid index for dual-month display (0=left, 1=right) */
|
|
@@ -1314,8 +1297,8 @@ class ComCalendarMonthView extends CalendarViewBase {
|
|
|
1314
1297
|
/** Grid of day cells (7 columns x 6 rows = 42 cells) */
|
|
1315
1298
|
cells = computed(() => {
|
|
1316
1299
|
const activeDate = this.activeDate();
|
|
1317
|
-
const
|
|
1318
|
-
const
|
|
1300
|
+
const _year = this.dateAdapter.getYear(activeDate);
|
|
1301
|
+
const _month = this.dateAdapter.getMonth(activeDate);
|
|
1319
1302
|
const firstOfMonth = this.dateAdapter.getFirstDayOfMonth(activeDate);
|
|
1320
1303
|
const firstDayOfWeek = this.firstDayOfWeek();
|
|
1321
1304
|
// Calculate the offset to start the grid
|
|
@@ -1443,7 +1426,7 @@ class ComCalendarMonthView extends CalendarViewBase {
|
|
|
1443
1426
|
}
|
|
1444
1427
|
}
|
|
1445
1428
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: ComCalendarMonthView, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
1446
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: ComCalendarMonthView, isStandalone: true, selector: "com-calendar-month-view", inputs: { firstDayOfWeek: { classPropertyName: "firstDayOfWeek", publicName: "firstDayOfWeek", isSignal: true, isRequired: false, transformFunction: null }, gridIndex: { classPropertyName: "gridIndex", publicName: "gridIndex", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "com-calendar-month-view-host" }, viewQueries: [{ propertyName: "cellComponents", predicate:
|
|
1429
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: ComCalendarMonthView, isStandalone: true, selector: "com-calendar-month-view", inputs: { firstDayOfWeek: { classPropertyName: "firstDayOfWeek", publicName: "firstDayOfWeek", isSignal: true, isRequired: false, transformFunction: null }, gridIndex: { classPropertyName: "gridIndex", publicName: "gridIndex", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "com-calendar-month-view-host" }, viewQueries: [{ propertyName: "cellComponents", predicate: ComCalendarCell, descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: `
|
|
1447
1430
|
<div
|
|
1448
1431
|
role="grid"
|
|
1449
1432
|
class="com-calendar-month-view"
|
|
@@ -1556,7 +1539,7 @@ const MONTHS_PER_YEAR = 12;
|
|
|
1556
1539
|
class ComCalendarYearView extends CalendarViewBase {
|
|
1557
1540
|
view = 'year';
|
|
1558
1541
|
/** Cell components for focus management */
|
|
1559
|
-
cellComponents = viewChildren(
|
|
1542
|
+
cellComponents = viewChildren(ComCalendarCell, ...(ngDevMode ? [{ debugName: "cellComponents" }] : []));
|
|
1560
1543
|
/** The year being displayed */
|
|
1561
1544
|
displayYear = computed(() => {
|
|
1562
1545
|
return this.dateAdapter.getYear(this.activeDate());
|
|
@@ -1568,7 +1551,7 @@ class ComCalendarYearView extends CalendarViewBase {
|
|
|
1568
1551
|
const today = this.today();
|
|
1569
1552
|
const todayYear = this.dateAdapter.getYear(today);
|
|
1570
1553
|
const todayMonth = this.dateAdapter.getMonth(today);
|
|
1571
|
-
const
|
|
1554
|
+
const _activeMonth = this.dateAdapter.getMonth(this.activeDate());
|
|
1572
1555
|
const minDate = this.minDate();
|
|
1573
1556
|
const maxDate = this.maxDate();
|
|
1574
1557
|
const dateClass = this.dateClass();
|
|
@@ -1732,7 +1715,7 @@ class ComCalendarYearView extends CalendarViewBase {
|
|
|
1732
1715
|
this.dateAdapter.compareDate(monthDate, endMonth) < 0);
|
|
1733
1716
|
}
|
|
1734
1717
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: ComCalendarYearView, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
1735
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: ComCalendarYearView, isStandalone: true, selector: "com-calendar-year-view", host: { classAttribute: "com-calendar-year-view-host" }, viewQueries: [{ propertyName: "cellComponents", predicate:
|
|
1718
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: ComCalendarYearView, isStandalone: true, selector: "com-calendar-year-view", host: { classAttribute: "com-calendar-year-view-host" }, viewQueries: [{ propertyName: "cellComponents", predicate: ComCalendarCell, descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: `
|
|
1736
1719
|
<div
|
|
1737
1720
|
role="grid"
|
|
1738
1721
|
class="com-calendar-year-view"
|
|
@@ -1814,7 +1797,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImpor
|
|
|
1814
1797
|
class ComCalendarMultiYearView extends CalendarViewBase {
|
|
1815
1798
|
view = 'multi-year';
|
|
1816
1799
|
/** Cell components for focus management */
|
|
1817
|
-
cellComponents = viewChildren(
|
|
1800
|
+
cellComponents = viewChildren(ComCalendarCell, ...(ngDevMode ? [{ debugName: "cellComponents" }] : []));
|
|
1818
1801
|
/** The first year in the displayed range */
|
|
1819
1802
|
startYear = computed(() => {
|
|
1820
1803
|
const activeYear = this.dateAdapter.getYear(this.activeDate());
|
|
@@ -1829,7 +1812,7 @@ class ComCalendarMultiYearView extends CalendarViewBase {
|
|
|
1829
1812
|
const start = this.startYear();
|
|
1830
1813
|
const today = this.today();
|
|
1831
1814
|
const todayYear = this.dateAdapter.getYear(today);
|
|
1832
|
-
const
|
|
1815
|
+
const _activeYear = this.dateAdapter.getYear(this.activeDate());
|
|
1833
1816
|
const minDate = this.minDate();
|
|
1834
1817
|
const maxDate = this.maxDate();
|
|
1835
1818
|
const dateClass = this.dateClass();
|
|
@@ -1986,7 +1969,7 @@ class ComCalendarMultiYearView extends CalendarViewBase {
|
|
|
1986
1969
|
return year > startYear && year < endYear;
|
|
1987
1970
|
}
|
|
1988
1971
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: ComCalendarMultiYearView, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
1989
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: ComCalendarMultiYearView, isStandalone: true, selector: "com-calendar-multi-year-view", host: { classAttribute: "com-calendar-multi-year-view-host" }, viewQueries: [{ propertyName: "cellComponents", predicate:
|
|
1972
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: ComCalendarMultiYearView, isStandalone: true, selector: "com-calendar-multi-year-view", host: { classAttribute: "com-calendar-multi-year-view-host" }, viewQueries: [{ propertyName: "cellComponents", predicate: ComCalendarCell, descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: `
|
|
1990
1973
|
<div
|
|
1991
1974
|
role="grid"
|
|
1992
1975
|
class="com-calendar-multi-year-view"
|
|
@@ -2411,7 +2394,7 @@ class ComCalendar {
|
|
|
2411
2394
|
optional: true,
|
|
2412
2395
|
});
|
|
2413
2396
|
/** Query for month views for cross-grid focus management */
|
|
2414
|
-
monthViews = viewChildren(
|
|
2397
|
+
monthViews = viewChildren(ComCalendarMonthView, ...(ngDevMode ? [{ debugName: "monthViews" }] : []));
|
|
2415
2398
|
/** The date to display and navigate from */
|
|
2416
2399
|
activeDate = input(...(ngDevMode ? [undefined, { debugName: "activeDate" }] : []));
|
|
2417
2400
|
/** The currently selected date or date range */
|
|
@@ -2779,7 +2762,7 @@ class ComCalendar {
|
|
|
2779
2762
|
this.liveAnnouncement.set(`Navigated to ${direction} ${this.periodLabel()}`);
|
|
2780
2763
|
}
|
|
2781
2764
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: ComCalendar, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2782
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: ComCalendar, isStandalone: true, selector: "com-calendar", inputs: { activeDate: { classPropertyName: "activeDate", publicName: "activeDate", isSignal: true, isRequired: false, transformFunction: null }, selected: { classPropertyName: "selected", publicName: "selected", isSignal: true, isRequired: false, transformFunction: null }, minDate: { classPropertyName: "minDate", publicName: "minDate", isSignal: true, isRequired: false, transformFunction: null }, maxDate: { classPropertyName: "maxDate", publicName: "maxDate", isSignal: true, isRequired: false, transformFunction: null }, dateFilter: { classPropertyName: "dateFilter", publicName: "dateFilter", isSignal: true, isRequired: false, transformFunction: null }, dateClass: { classPropertyName: "dateClass", publicName: "dateClass", isSignal: true, isRequired: false, transformFunction: null }, bordered: { classPropertyName: "bordered", publicName: "bordered", isSignal: true, isRequired: false, transformFunction: null }, startView: { classPropertyName: "startView", publicName: "startView", isSignal: true, isRequired: false, transformFunction: null }, firstDayOfWeek: { classPropertyName: "firstDayOfWeek", publicName: "firstDayOfWeek", isSignal: true, isRequired: false, transformFunction: null }, monthColumns: { classPropertyName: "monthColumns", publicName: "monthColumns", isSignal: true, isRequired: false, transformFunction: null }, cellTemplate: { classPropertyName: "cellTemplate", publicName: "cellTemplate", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectedChange: "selectedChange", viewChanged: "viewChanged", activeDateChange: "activeDateChange" }, host: { classAttribute: "com-calendar-host block" }, viewQueries: [{ propertyName: "monthViews", predicate:
|
|
2765
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: ComCalendar, isStandalone: true, selector: "com-calendar", inputs: { activeDate: { classPropertyName: "activeDate", publicName: "activeDate", isSignal: true, isRequired: false, transformFunction: null }, selected: { classPropertyName: "selected", publicName: "selected", isSignal: true, isRequired: false, transformFunction: null }, minDate: { classPropertyName: "minDate", publicName: "minDate", isSignal: true, isRequired: false, transformFunction: null }, maxDate: { classPropertyName: "maxDate", publicName: "maxDate", isSignal: true, isRequired: false, transformFunction: null }, dateFilter: { classPropertyName: "dateFilter", publicName: "dateFilter", isSignal: true, isRequired: false, transformFunction: null }, dateClass: { classPropertyName: "dateClass", publicName: "dateClass", isSignal: true, isRequired: false, transformFunction: null }, bordered: { classPropertyName: "bordered", publicName: "bordered", isSignal: true, isRequired: false, transformFunction: null }, startView: { classPropertyName: "startView", publicName: "startView", isSignal: true, isRequired: false, transformFunction: null }, firstDayOfWeek: { classPropertyName: "firstDayOfWeek", publicName: "firstDayOfWeek", isSignal: true, isRequired: false, transformFunction: null }, monthColumns: { classPropertyName: "monthColumns", publicName: "monthColumns", isSignal: true, isRequired: false, transformFunction: null }, cellTemplate: { classPropertyName: "cellTemplate", publicName: "cellTemplate", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectedChange: "selectedChange", viewChanged: "viewChanged", activeDateChange: "activeDateChange" }, host: { classAttribute: "com-calendar-host block" }, viewQueries: [{ propertyName: "monthViews", predicate: ComCalendarMonthView, descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
2783
2766
|
<div
|
|
2784
2767
|
[class]="calendarClasses()"
|
|
2785
2768
|
role="application"
|
|
@@ -3087,3372 +3070,6 @@ function provideWeekSelectionStrategy() {
|
|
|
3087
3070
|
return { provide: CALENDAR_SELECTION_STRATEGY, useClass: WeekSelectionStrategy };
|
|
3088
3071
|
}
|
|
3089
3072
|
|
|
3090
|
-
/**
|
|
3091
|
-
* Types and interfaces for DatePicker and DateRangePicker components.
|
|
3092
|
-
*/
|
|
3093
|
-
/** Creates a DateRangeValue */
|
|
3094
|
-
function createDateRangeValue(start = null, end = null) {
|
|
3095
|
-
return { start, end };
|
|
3096
|
-
}
|
|
3097
|
-
/** Generates a unique ID for datepicker instances */
|
|
3098
|
-
let datepickerIdCounter = 0;
|
|
3099
|
-
function generateDatepickerId() {
|
|
3100
|
-
return `com-datepicker-${datepickerIdCounter++}`;
|
|
3101
|
-
}
|
|
3102
|
-
|
|
3103
|
-
/**
|
|
3104
|
-
* CVA variants for the datepicker trigger input.
|
|
3105
|
-
* Uses semantic theme tokens for consistent cross-theme styling.
|
|
3106
|
-
*
|
|
3107
|
-
* @tokens `--color-input-background`, `--color-input-foreground`, `--color-input-border`,
|
|
3108
|
-
* `--color-input-placeholder`, `--color-ring`, `--color-muted`, `--color-muted-hover`,
|
|
3109
|
-
* `--color-warn`, `--color-success`, `--color-primary`, `--color-border`,
|
|
3110
|
-
* `--color-disabled`, `--color-disabled-foreground`, `--radius-input`
|
|
3111
|
-
*/
|
|
3112
|
-
const datepickerTriggerVariants = cva([
|
|
3113
|
-
'inline-flex',
|
|
3114
|
-
'items-center',
|
|
3115
|
-
'justify-between',
|
|
3116
|
-
'w-full',
|
|
3117
|
-
'rounded-input',
|
|
3118
|
-
'border',
|
|
3119
|
-
'bg-input-background',
|
|
3120
|
-
'text-input-foreground',
|
|
3121
|
-
'transition-colors',
|
|
3122
|
-
'duration-150',
|
|
3123
|
-
'placeholder:text-input-placeholder',
|
|
3124
|
-
'focus-within:outline-[1px]',
|
|
3125
|
-
'focus-within:outline-offset-2',
|
|
3126
|
-
'focus-within:outline-ring',
|
|
3127
|
-
'cursor-pointer',
|
|
3128
|
-
], {
|
|
3129
|
-
variants: {
|
|
3130
|
-
variant: {
|
|
3131
|
-
default: [
|
|
3132
|
-
'border-input-border',
|
|
3133
|
-
'hover:border-border',
|
|
3134
|
-
],
|
|
3135
|
-
outline: [
|
|
3136
|
-
'border-2',
|
|
3137
|
-
'border-input-border',
|
|
3138
|
-
'hover:border-foreground',
|
|
3139
|
-
],
|
|
3140
|
-
ghost: [
|
|
3141
|
-
'border-transparent',
|
|
3142
|
-
'bg-transparent',
|
|
3143
|
-
'hover:bg-muted',
|
|
3144
|
-
],
|
|
3145
|
-
filled: [
|
|
3146
|
-
'border-transparent',
|
|
3147
|
-
'bg-muted',
|
|
3148
|
-
'hover:bg-muted-hover',
|
|
3149
|
-
],
|
|
3150
|
-
naked: [
|
|
3151
|
-
'border-transparent',
|
|
3152
|
-
'bg-transparent',
|
|
3153
|
-
'shadow-none',
|
|
3154
|
-
'focus-within:outline-none',
|
|
3155
|
-
'rounded-none',
|
|
3156
|
-
],
|
|
3157
|
-
},
|
|
3158
|
-
size: {
|
|
3159
|
-
sm: ['h-8', 'px-2', 'text-xs', 'gap-1'],
|
|
3160
|
-
default: ['h-10', 'px-3', 'text-sm', 'gap-2'],
|
|
3161
|
-
lg: ['h-12', 'px-4', 'text-base', 'gap-3'],
|
|
3162
|
-
},
|
|
3163
|
-
state: {
|
|
3164
|
-
default: [],
|
|
3165
|
-
error: [
|
|
3166
|
-
'border-warn',
|
|
3167
|
-
'focus-within:outline-warn',
|
|
3168
|
-
],
|
|
3169
|
-
success: [
|
|
3170
|
-
'border-success',
|
|
3171
|
-
'focus-within:outline-success',
|
|
3172
|
-
],
|
|
3173
|
-
},
|
|
3174
|
-
open: {
|
|
3175
|
-
true: ['outline-[1px]', 'outline-ring', 'border-primary'],
|
|
3176
|
-
false: [],
|
|
3177
|
-
},
|
|
3178
|
-
},
|
|
3179
|
-
compoundVariants: [
|
|
3180
|
-
{
|
|
3181
|
-
open: true,
|
|
3182
|
-
variant: 'default',
|
|
3183
|
-
class: ['border-primary'],
|
|
3184
|
-
},
|
|
3185
|
-
{
|
|
3186
|
-
open: true,
|
|
3187
|
-
variant: 'outline',
|
|
3188
|
-
class: ['border-primary'],
|
|
3189
|
-
},
|
|
3190
|
-
// Naked variant should not show ring when open (form-field provides focus styling)
|
|
3191
|
-
{
|
|
3192
|
-
open: true,
|
|
3193
|
-
variant: 'naked',
|
|
3194
|
-
class: ['outline-none', 'border-transparent'],
|
|
3195
|
-
},
|
|
3196
|
-
// Naked variant should not show error border (form-field provides error styling)
|
|
3197
|
-
{
|
|
3198
|
-
state: 'error',
|
|
3199
|
-
variant: 'naked',
|
|
3200
|
-
class: ['border-transparent', 'focus-within:outline-none'],
|
|
3201
|
-
},
|
|
3202
|
-
// Naked variant should not show success border (form-field provides styling)
|
|
3203
|
-
{
|
|
3204
|
-
state: 'success',
|
|
3205
|
-
variant: 'naked',
|
|
3206
|
-
class: ['border-transparent', 'focus-within:outline-none'],
|
|
3207
|
-
},
|
|
3208
|
-
],
|
|
3209
|
-
defaultVariants: {
|
|
3210
|
-
variant: 'default',
|
|
3211
|
-
size: 'default',
|
|
3212
|
-
state: 'default',
|
|
3213
|
-
open: false,
|
|
3214
|
-
},
|
|
3215
|
-
});
|
|
3216
|
-
/**
|
|
3217
|
-
* CVA variants for the disabled state of datepicker trigger.
|
|
3218
|
-
*
|
|
3219
|
-
* @tokens `--color-disabled`, `--color-disabled-foreground`
|
|
3220
|
-
*/
|
|
3221
|
-
const datepickerDisabledVariants = cva([
|
|
3222
|
-
'cursor-not-allowed',
|
|
3223
|
-
'bg-disabled',
|
|
3224
|
-
'text-disabled-foreground',
|
|
3225
|
-
'hover:border-input-border',
|
|
3226
|
-
'pointer-events-none',
|
|
3227
|
-
]);
|
|
3228
|
-
/**
|
|
3229
|
-
* CVA variants for the datepicker input field.
|
|
3230
|
-
*
|
|
3231
|
-
* @tokens `--color-input-foreground`, `--color-input-placeholder`
|
|
3232
|
-
*/
|
|
3233
|
-
const datepickerInputVariants = cva([
|
|
3234
|
-
'flex-1',
|
|
3235
|
-
'bg-transparent',
|
|
3236
|
-
'outline-none',
|
|
3237
|
-
'placeholder:text-input-placeholder',
|
|
3238
|
-
'disabled:cursor-not-allowed',
|
|
3239
|
-
'min-w-0',
|
|
3240
|
-
'truncate',
|
|
3241
|
-
], {
|
|
3242
|
-
variants: {
|
|
3243
|
-
size: {
|
|
3244
|
-
sm: ['text-xs'],
|
|
3245
|
-
default: ['text-sm'],
|
|
3246
|
-
lg: ['text-base'],
|
|
3247
|
-
},
|
|
3248
|
-
},
|
|
3249
|
-
defaultVariants: {
|
|
3250
|
-
size: 'default',
|
|
3251
|
-
},
|
|
3252
|
-
});
|
|
3253
|
-
/**
|
|
3254
|
-
* CVA variants for the calendar icon button.
|
|
3255
|
-
*
|
|
3256
|
-
* @tokens `--color-ring`, `--color-muted-foreground`, `--radius-interactive-sm`
|
|
3257
|
-
*/
|
|
3258
|
-
const datepickerIconVariants = cva([
|
|
3259
|
-
'inline-flex',
|
|
3260
|
-
'items-center',
|
|
3261
|
-
'justify-center',
|
|
3262
|
-
'shrink-0',
|
|
3263
|
-
'text-muted-foreground',
|
|
3264
|
-
'transition-colors',
|
|
3265
|
-
'hover:text-foreground',
|
|
3266
|
-
'focus-visible:outline-[1px]',
|
|
3267
|
-
'focus-visible:outline-offset-2',
|
|
3268
|
-
'focus-visible:outline-ring',
|
|
3269
|
-
'rounded-interactive-sm',
|
|
3270
|
-
], {
|
|
3271
|
-
variants: {
|
|
3272
|
-
size: {
|
|
3273
|
-
sm: ['h-4', 'w-4'],
|
|
3274
|
-
default: ['h-5', 'w-5'],
|
|
3275
|
-
lg: ['h-6', 'w-6'],
|
|
3276
|
-
},
|
|
3277
|
-
},
|
|
3278
|
-
defaultVariants: {
|
|
3279
|
-
size: 'default',
|
|
3280
|
-
},
|
|
3281
|
-
});
|
|
3282
|
-
/**
|
|
3283
|
-
* CVA variants for the clear button.
|
|
3284
|
-
*
|
|
3285
|
-
* @tokens `--color-ring`, `--color-muted-foreground`, `--color-foreground`, `--radius-interactive-sm`
|
|
3286
|
-
*/
|
|
3287
|
-
const datepickerClearVariants = cva([
|
|
3288
|
-
'inline-flex',
|
|
3289
|
-
'items-center',
|
|
3290
|
-
'justify-center',
|
|
3291
|
-
'rounded-interactive-sm',
|
|
3292
|
-
'text-muted-foreground',
|
|
3293
|
-
'transition-colors',
|
|
3294
|
-
'hover:text-foreground',
|
|
3295
|
-
'focus-visible:outline-[1px]',
|
|
3296
|
-
'focus-visible:outline-offset-2',
|
|
3297
|
-
'focus-visible:outline-ring',
|
|
3298
|
-
'shrink-0',
|
|
3299
|
-
], {
|
|
3300
|
-
variants: {
|
|
3301
|
-
size: {
|
|
3302
|
-
sm: ['h-4', 'w-4'],
|
|
3303
|
-
default: ['h-5', 'w-5'],
|
|
3304
|
-
lg: ['h-6', 'w-6'],
|
|
3305
|
-
},
|
|
3306
|
-
},
|
|
3307
|
-
defaultVariants: {
|
|
3308
|
-
size: 'default',
|
|
3309
|
-
},
|
|
3310
|
-
});
|
|
3311
|
-
/**
|
|
3312
|
-
* CVA variants for the datepicker panel (overlay).
|
|
3313
|
-
*
|
|
3314
|
-
* @tokens `--color-popover`, `--color-popover-foreground`, `--color-border-subtle`, `--radius-overlay`
|
|
3315
|
-
*/
|
|
3316
|
-
const datepickerPanelVariants = cva([
|
|
3317
|
-
'z-50',
|
|
3318
|
-
'overflow-hidden',
|
|
3319
|
-
'rounded-overlay',
|
|
3320
|
-
'border',
|
|
3321
|
-
'border-border-subtle',
|
|
3322
|
-
'bg-popover',
|
|
3323
|
-
'text-popover-foreground',
|
|
3324
|
-
'shadow-lg',
|
|
3325
|
-
'outline-none',
|
|
3326
|
-
], {
|
|
3327
|
-
variants: {
|
|
3328
|
-
size: {
|
|
3329
|
-
sm: ['p-2', 'text-xs'],
|
|
3330
|
-
default: ['p-3', 'text-sm'],
|
|
3331
|
-
lg: ['p-4', 'text-base'],
|
|
3332
|
-
},
|
|
3333
|
-
},
|
|
3334
|
-
defaultVariants: {
|
|
3335
|
-
size: 'default',
|
|
3336
|
-
},
|
|
3337
|
-
});
|
|
3338
|
-
/**
|
|
3339
|
-
* CVA variants for the footer section of the datepicker panel.
|
|
3340
|
-
*
|
|
3341
|
-
* @tokens `--color-border-subtle`
|
|
3342
|
-
*/
|
|
3343
|
-
const datepickerFooterVariants = cva([
|
|
3344
|
-
'flex',
|
|
3345
|
-
'items-center',
|
|
3346
|
-
'justify-between',
|
|
3347
|
-
'border-t',
|
|
3348
|
-
'border-border-subtle',
|
|
3349
|
-
'mt-3',
|
|
3350
|
-
'pt-3',
|
|
3351
|
-
], {
|
|
3352
|
-
variants: {
|
|
3353
|
-
size: {
|
|
3354
|
-
sm: ['mt-2', 'pt-2', 'gap-1'],
|
|
3355
|
-
default: ['mt-3', 'pt-3', 'gap-2'],
|
|
3356
|
-
lg: ['mt-4', 'pt-4', 'gap-3'],
|
|
3357
|
-
},
|
|
3358
|
-
},
|
|
3359
|
-
defaultVariants: {
|
|
3360
|
-
size: 'default',
|
|
3361
|
-
},
|
|
3362
|
-
});
|
|
3363
|
-
/**
|
|
3364
|
-
* CVA variants for the footer buttons.
|
|
3365
|
-
*
|
|
3366
|
-
* @tokens `--color-primary`, `--color-primary-foreground`, `--color-primary-hover`,
|
|
3367
|
-
* `--color-muted`, `--color-muted-foreground`, `--color-muted-hover`, `--radius-control-sm`
|
|
3368
|
-
*/
|
|
3369
|
-
const datepickerFooterButtonVariants = cva([
|
|
3370
|
-
'inline-flex',
|
|
3371
|
-
'items-center',
|
|
3372
|
-
'justify-center',
|
|
3373
|
-
'rounded-control-sm',
|
|
3374
|
-
'font-medium',
|
|
3375
|
-
'transition-colors',
|
|
3376
|
-
'focus-visible:outline-[1px]',
|
|
3377
|
-
'focus-visible:outline-offset-2',
|
|
3378
|
-
'focus-visible:outline-ring',
|
|
3379
|
-
], {
|
|
3380
|
-
variants: {
|
|
3381
|
-
size: {
|
|
3382
|
-
sm: ['h-7', 'px-2', 'text-xs'],
|
|
3383
|
-
default: ['h-8', 'px-3', 'text-sm'],
|
|
3384
|
-
lg: ['h-9', 'px-4', 'text-base'],
|
|
3385
|
-
},
|
|
3386
|
-
variant: {
|
|
3387
|
-
primary: [
|
|
3388
|
-
'bg-primary',
|
|
3389
|
-
'text-primary-foreground',
|
|
3390
|
-
'hover:bg-primary-hover',
|
|
3391
|
-
],
|
|
3392
|
-
secondary: [
|
|
3393
|
-
'bg-muted',
|
|
3394
|
-
'text-muted-foreground',
|
|
3395
|
-
'hover:bg-muted-hover',
|
|
3396
|
-
],
|
|
3397
|
-
},
|
|
3398
|
-
},
|
|
3399
|
-
defaultVariants: {
|
|
3400
|
-
size: 'default',
|
|
3401
|
-
variant: 'secondary',
|
|
3402
|
-
},
|
|
3403
|
-
});
|
|
3404
|
-
/**
|
|
3405
|
-
* CVA variants for the range separator.
|
|
3406
|
-
*
|
|
3407
|
-
* @tokens `--color-muted-foreground`
|
|
3408
|
-
*/
|
|
3409
|
-
const datepickerRangeSeparatorVariants = cva([
|
|
3410
|
-
'inline-flex',
|
|
3411
|
-
'items-center',
|
|
3412
|
-
'justify-center',
|
|
3413
|
-
'text-muted-foreground',
|
|
3414
|
-
'shrink-0',
|
|
3415
|
-
], {
|
|
3416
|
-
variants: {
|
|
3417
|
-
size: {
|
|
3418
|
-
sm: ['px-1', 'text-xs'],
|
|
3419
|
-
default: ['px-2', 'text-sm'],
|
|
3420
|
-
lg: ['px-3', 'text-base'],
|
|
3421
|
-
},
|
|
3422
|
-
},
|
|
3423
|
-
defaultVariants: {
|
|
3424
|
-
size: 'default',
|
|
3425
|
-
},
|
|
3426
|
-
});
|
|
3427
|
-
|
|
3428
|
-
/**
|
|
3429
|
-
* CVA variants for the time picker container.
|
|
3430
|
-
*
|
|
3431
|
-
* @tokens `--color-input-background`, `--color-input-foreground`, `--color-input-border`,
|
|
3432
|
-
* `--color-ring`, `--color-warn`, `--color-success`, `--radius-input`
|
|
3433
|
-
*/
|
|
3434
|
-
const timepickerContainerVariants = cva([
|
|
3435
|
-
'inline-flex',
|
|
3436
|
-
'items-center',
|
|
3437
|
-
'gap-1',
|
|
3438
|
-
'rounded-input',
|
|
3439
|
-
'transition-colors',
|
|
3440
|
-
'duration-150',
|
|
3441
|
-
], {
|
|
3442
|
-
variants: {
|
|
3443
|
-
variant: {
|
|
3444
|
-
standalone: [
|
|
3445
|
-
'border',
|
|
3446
|
-
'border-input-border',
|
|
3447
|
-
'bg-input-background',
|
|
3448
|
-
'text-input-foreground',
|
|
3449
|
-
'focus-within:outline-1',
|
|
3450
|
-
'focus-within:outline-offset-2',
|
|
3451
|
-
'focus-within:outline-ring',
|
|
3452
|
-
],
|
|
3453
|
-
embedded: [
|
|
3454
|
-
'border-transparent',
|
|
3455
|
-
'bg-transparent',
|
|
3456
|
-
'text-foreground',
|
|
3457
|
-
],
|
|
3458
|
-
naked: [
|
|
3459
|
-
'border-transparent',
|
|
3460
|
-
'bg-transparent',
|
|
3461
|
-
'shadow-none',
|
|
3462
|
-
'focus-within:outline-none',
|
|
3463
|
-
'rounded-none',
|
|
3464
|
-
],
|
|
3465
|
-
},
|
|
3466
|
-
size: {
|
|
3467
|
-
sm: ['h-8', 'px-2', 'text-xs', 'gap-0.5'],
|
|
3468
|
-
default: ['h-10', 'px-3', 'text-sm', 'gap-1'],
|
|
3469
|
-
lg: ['h-12', 'px-4', 'text-base', 'gap-1.5'],
|
|
3470
|
-
},
|
|
3471
|
-
state: {
|
|
3472
|
-
default: [],
|
|
3473
|
-
error: [
|
|
3474
|
-
'border-warn',
|
|
3475
|
-
'focus-within:outline-warn',
|
|
3476
|
-
],
|
|
3477
|
-
success: [
|
|
3478
|
-
'border-success',
|
|
3479
|
-
'focus-within:outline-success',
|
|
3480
|
-
],
|
|
3481
|
-
},
|
|
3482
|
-
},
|
|
3483
|
-
compoundVariants: [
|
|
3484
|
-
{
|
|
3485
|
-
variant: 'embedded',
|
|
3486
|
-
state: 'error',
|
|
3487
|
-
class: [],
|
|
3488
|
-
},
|
|
3489
|
-
{
|
|
3490
|
-
variant: 'embedded',
|
|
3491
|
-
state: 'success',
|
|
3492
|
-
class: [],
|
|
3493
|
-
},
|
|
3494
|
-
// Naked variant should not show ring (form-field provides focus styling)
|
|
3495
|
-
{
|
|
3496
|
-
variant: 'naked',
|
|
3497
|
-
state: 'error',
|
|
3498
|
-
class: ['border-transparent', 'focus-within:outline-none'],
|
|
3499
|
-
},
|
|
3500
|
-
{
|
|
3501
|
-
variant: 'naked',
|
|
3502
|
-
state: 'success',
|
|
3503
|
-
class: ['border-transparent', 'focus-within:outline-none'],
|
|
3504
|
-
},
|
|
3505
|
-
],
|
|
3506
|
-
defaultVariants: {
|
|
3507
|
-
variant: 'standalone',
|
|
3508
|
-
size: 'default',
|
|
3509
|
-
state: 'default',
|
|
3510
|
-
},
|
|
3511
|
-
});
|
|
3512
|
-
/**
|
|
3513
|
-
* CVA variants for the disabled state of time picker.
|
|
3514
|
-
*
|
|
3515
|
-
* @tokens `--color-disabled`, `--color-disabled-foreground`
|
|
3516
|
-
*/
|
|
3517
|
-
const timepickerDisabledVariants = cva([
|
|
3518
|
-
'cursor-not-allowed',
|
|
3519
|
-
'bg-disabled',
|
|
3520
|
-
'text-disabled-foreground',
|
|
3521
|
-
'pointer-events-none',
|
|
3522
|
-
]);
|
|
3523
|
-
/**
|
|
3524
|
-
* CVA variants for each time segment input.
|
|
3525
|
-
*
|
|
3526
|
-
* @tokens `--color-primary-subtle`, `--color-primary-subtle-foreground`, `--radius-interactive-sm`
|
|
3527
|
-
*/
|
|
3528
|
-
const timepickerSegmentVariants = cva([
|
|
3529
|
-
'bg-transparent',
|
|
3530
|
-
'outline-none',
|
|
3531
|
-
'text-center',
|
|
3532
|
-
'font-mono',
|
|
3533
|
-
'tabular-nums',
|
|
3534
|
-
'select-all',
|
|
3535
|
-
'focus:bg-primary-subtle',
|
|
3536
|
-
'focus:text-primary-subtle-foreground',
|
|
3537
|
-
'focus:rounded-interactive-sm',
|
|
3538
|
-
], {
|
|
3539
|
-
variants: {
|
|
3540
|
-
size: {
|
|
3541
|
-
sm: ['w-5', 'text-xs'],
|
|
3542
|
-
default: ['w-7', 'text-sm'],
|
|
3543
|
-
lg: ['w-9', 'text-base'],
|
|
3544
|
-
},
|
|
3545
|
-
},
|
|
3546
|
-
defaultVariants: {
|
|
3547
|
-
size: 'default',
|
|
3548
|
-
},
|
|
3549
|
-
});
|
|
3550
|
-
/**
|
|
3551
|
-
* CVA variants for the colon separator.
|
|
3552
|
-
*
|
|
3553
|
-
* @tokens `--color-muted-foreground`
|
|
3554
|
-
*/
|
|
3555
|
-
const timepickerSeparatorVariants = cva([
|
|
3556
|
-
'text-muted-foreground',
|
|
3557
|
-
'select-none',
|
|
3558
|
-
'font-mono',
|
|
3559
|
-
], {
|
|
3560
|
-
variants: {
|
|
3561
|
-
size: {
|
|
3562
|
-
sm: ['text-xs'],
|
|
3563
|
-
default: ['text-sm'],
|
|
3564
|
-
lg: ['text-base'],
|
|
3565
|
-
},
|
|
3566
|
-
},
|
|
3567
|
-
defaultVariants: {
|
|
3568
|
-
size: 'default',
|
|
3569
|
-
},
|
|
3570
|
-
});
|
|
3571
|
-
/**
|
|
3572
|
-
* CVA variants for the AM/PM toggle button.
|
|
3573
|
-
*
|
|
3574
|
-
* @tokens `--color-muted`, `--color-muted-foreground`, `--color-muted-hover`, `--color-ring`,
|
|
3575
|
-
* `--radius-control-sm`
|
|
3576
|
-
*/
|
|
3577
|
-
const timepickerPeriodVariants = cva([
|
|
3578
|
-
'inline-flex',
|
|
3579
|
-
'items-center',
|
|
3580
|
-
'justify-center',
|
|
3581
|
-
'rounded-control-sm',
|
|
3582
|
-
'font-medium',
|
|
3583
|
-
'transition-colors',
|
|
3584
|
-
'select-none',
|
|
3585
|
-
'outline-none',
|
|
3586
|
-
'bg-muted',
|
|
3587
|
-
'text-muted-foreground',
|
|
3588
|
-
'hover:bg-muted-hover',
|
|
3589
|
-
'focus-visible:outline-1',
|
|
3590
|
-
'focus-visible:outline-offset-2',
|
|
3591
|
-
'focus-visible:outline-ring',
|
|
3592
|
-
], {
|
|
3593
|
-
variants: {
|
|
3594
|
-
size: {
|
|
3595
|
-
sm: ['h-5', 'px-1', 'text-xs', 'ml-0.5'],
|
|
3596
|
-
default: ['h-6', 'px-1.5', 'text-xs', 'ml-1'],
|
|
3597
|
-
lg: ['h-7', 'px-2', 'text-sm', 'ml-1.5'],
|
|
3598
|
-
},
|
|
3599
|
-
},
|
|
3600
|
-
defaultVariants: {
|
|
3601
|
-
size: 'default',
|
|
3602
|
-
},
|
|
3603
|
-
});
|
|
3604
|
-
/**
|
|
3605
|
-
* CVA variants for the time section divider in datepicker panel.
|
|
3606
|
-
*
|
|
3607
|
-
* @tokens `--color-border-subtle`
|
|
3608
|
-
*/
|
|
3609
|
-
const timepickerSectionVariants = cva([
|
|
3610
|
-
'flex',
|
|
3611
|
-
'items-center',
|
|
3612
|
-
'justify-center',
|
|
3613
|
-
'border-t',
|
|
3614
|
-
'border-border-subtle',
|
|
3615
|
-
], {
|
|
3616
|
-
variants: {
|
|
3617
|
-
size: {
|
|
3618
|
-
sm: ['pt-2', 'mt-2'],
|
|
3619
|
-
default: ['pt-3', 'mt-3'],
|
|
3620
|
-
lg: ['pt-4', 'mt-4'],
|
|
3621
|
-
},
|
|
3622
|
-
},
|
|
3623
|
-
defaultVariants: {
|
|
3624
|
-
size: 'default',
|
|
3625
|
-
},
|
|
3626
|
-
});
|
|
3627
|
-
/**
|
|
3628
|
-
* CVA variants for time labels in date range picker.
|
|
3629
|
-
*
|
|
3630
|
-
* @tokens `--color-muted-foreground`
|
|
3631
|
-
*/
|
|
3632
|
-
const timepickerLabelVariants = cva([
|
|
3633
|
-
'text-muted-foreground',
|
|
3634
|
-
'font-medium',
|
|
3635
|
-
], {
|
|
3636
|
-
variants: {
|
|
3637
|
-
size: {
|
|
3638
|
-
sm: ['text-xs'],
|
|
3639
|
-
default: ['text-xs'],
|
|
3640
|
-
lg: ['text-sm'],
|
|
3641
|
-
},
|
|
3642
|
-
},
|
|
3643
|
-
defaultVariants: {
|
|
3644
|
-
size: 'default',
|
|
3645
|
-
},
|
|
3646
|
-
});
|
|
3647
|
-
|
|
3648
|
-
/**
|
|
3649
|
-
* Types and interfaces for the TimePicker component.
|
|
3650
|
-
*/
|
|
3651
|
-
/** Creates a ComTimeValue */
|
|
3652
|
-
function createTimeValue(hours = 0, minutes = 0, seconds = 0) {
|
|
3653
|
-
return { hours, minutes, seconds };
|
|
3654
|
-
}
|
|
3655
|
-
/**
|
|
3656
|
-
* Compares two ComTimeValue objects.
|
|
3657
|
-
* @returns negative if a < b, 0 if equal, positive if a > b
|
|
3658
|
-
*/
|
|
3659
|
-
function compareTime(a, b) {
|
|
3660
|
-
const diff = a.hours * 3600 + a.minutes * 60 + a.seconds - (b.hours * 3600 + b.minutes * 60 + b.seconds);
|
|
3661
|
-
return diff;
|
|
3662
|
-
}
|
|
3663
|
-
/** Generates a unique ID for time picker instances */
|
|
3664
|
-
let timePickerIdCounter = 0;
|
|
3665
|
-
function generateTimePickerId() {
|
|
3666
|
-
return `com-time-picker-${timePickerIdCounter++}`;
|
|
3667
|
-
}
|
|
3668
|
-
|
|
3669
|
-
/**
|
|
3670
|
-
* Time picker component with segmented numeric input fields.
|
|
3671
|
-
* Supports standalone usage with ControlValueAccessor and embedded usage
|
|
3672
|
-
* within datepicker/date-range-picker panels.
|
|
3673
|
-
*
|
|
3674
|
-
* Visual layout: `[HH] : [MM] : [SS] [AM|PM]`
|
|
3675
|
-
*
|
|
3676
|
-
* @tokens `--color-input-background`, `--color-input-foreground`, `--color-input-border`,
|
|
3677
|
-
* `--color-ring`, `--color-primary-subtle`, `--color-primary-subtle-foreground`,
|
|
3678
|
-
* `--color-muted`, `--color-muted-foreground`, `--color-muted-hover`,
|
|
3679
|
-
* `--color-disabled`, `--color-disabled-foreground`,
|
|
3680
|
-
* `--color-warn`, `--color-success`, `--color-border`
|
|
3681
|
-
*
|
|
3682
|
-
* @example
|
|
3683
|
-
* ```html
|
|
3684
|
-
* <!-- Standalone with reactive forms -->
|
|
3685
|
-
* <com-time-picker formControlName="startTime" />
|
|
3686
|
-
*
|
|
3687
|
-
* <!-- 12-hour format with seconds -->
|
|
3688
|
-
* <com-time-picker formControlName="alarm" [use12HourFormat]="true" [showSeconds]="true" />
|
|
3689
|
-
*
|
|
3690
|
-
* <!-- 15-minute steps -->
|
|
3691
|
-
* <com-time-picker formControlName="meeting" [minuteStep]="15" />
|
|
3692
|
-
*
|
|
3693
|
-
* <!-- Embedded inside datepicker panel -->
|
|
3694
|
-
* <com-time-picker variant="embedded" [value]="time" (timeChange)="onTime($event)" />
|
|
3695
|
-
* ```
|
|
3696
|
-
*/
|
|
3697
|
-
class ComTimePicker {
|
|
3698
|
-
localeId = inject(LOCALE_ID);
|
|
3699
|
-
ngControl = inject(NgControl, { optional: true, self: true });
|
|
3700
|
-
defaultErrorStateMatcher = inject(ErrorStateMatcher);
|
|
3701
|
-
parentForm = inject(NgForm, { optional: true });
|
|
3702
|
-
parentFormGroup = inject(FormGroupDirective, { optional: true });
|
|
3703
|
-
timepickerId = generateTimePickerId();
|
|
3704
|
-
hoursInputRef = viewChild('hoursInput', ...(ngDevMode ? [{ debugName: "hoursInputRef" }] : []));
|
|
3705
|
-
minutesInputRef = viewChild('minutesInput', ...(ngDevMode ? [{ debugName: "minutesInputRef" }] : []));
|
|
3706
|
-
secondsInputRef = viewChild('secondsInput', ...(ngDevMode ? [{ debugName: "secondsInputRef" }] : []));
|
|
3707
|
-
periodButtonRef = viewChild('periodButton', ...(ngDevMode ? [{ debugName: "periodButtonRef" }] : []));
|
|
3708
|
-
// ============ INPUTS ============
|
|
3709
|
-
/** Current time value. */
|
|
3710
|
-
value = model(null, ...(ngDevMode ? [{ debugName: "value" }] : []));
|
|
3711
|
-
/** Whether the time picker is disabled. */
|
|
3712
|
-
disabled = model(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
|
|
3713
|
-
/** Whether the time picker is required. */
|
|
3714
|
-
required = input(false, ...(ngDevMode ? [{ debugName: "required" }] : []));
|
|
3715
|
-
/** Whether to show the seconds segment. */
|
|
3716
|
-
showSeconds = input(false, ...(ngDevMode ? [{ debugName: "showSeconds" }] : []));
|
|
3717
|
-
/** 12h vs 24h format. `null` = auto-detect from locale. */
|
|
3718
|
-
use12HourFormat = input(null, ...(ngDevMode ? [{ debugName: "use12HourFormat" }] : []));
|
|
3719
|
-
/** Step interval for minutes. */
|
|
3720
|
-
minuteStep = input(1, ...(ngDevMode ? [{ debugName: "minuteStep" }] : []));
|
|
3721
|
-
/** Step interval for seconds. */
|
|
3722
|
-
secondStep = input(1, ...(ngDevMode ? [{ debugName: "secondStep" }] : []));
|
|
3723
|
-
/** Minimum selectable time. */
|
|
3724
|
-
minTime = input(null, ...(ngDevMode ? [{ debugName: "minTime" }] : []));
|
|
3725
|
-
/** Maximum selectable time. */
|
|
3726
|
-
maxTime = input(null, ...(ngDevMode ? [{ debugName: "maxTime" }] : []));
|
|
3727
|
-
/** Visual variant. */
|
|
3728
|
-
variant = input('standalone', ...(ngDevMode ? [{ debugName: "variant" }] : []));
|
|
3729
|
-
/** Size variant. */
|
|
3730
|
-
size = input('default', ...(ngDevMode ? [{ debugName: "size" }] : []));
|
|
3731
|
-
/** Validation state. */
|
|
3732
|
-
state = input('default', ...(ngDevMode ? [{ debugName: "state" }] : []));
|
|
3733
|
-
/** Accessible label for the group. */
|
|
3734
|
-
ariaLabel = input(null, ...(ngDevMode ? [{ debugName: "ariaLabel" }] : []));
|
|
3735
|
-
/** Additional CSS classes. */
|
|
3736
|
-
userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : {}), alias: 'class' });
|
|
3737
|
-
/** Placeholder text for empty segments. */
|
|
3738
|
-
placeholder = input('--', ...(ngDevMode ? [{ debugName: "placeholder" }] : []));
|
|
3739
|
-
/** Custom error state matcher for determining when to show errors. */
|
|
3740
|
-
errorStateMatcher = input(...(ngDevMode ? [undefined, { debugName: "errorStateMatcher" }] : []));
|
|
3741
|
-
// Signal Forms inputs — set automatically by [formField] via setInputOnDirectives
|
|
3742
|
-
touched = model(false, ...(ngDevMode ? [{ debugName: "touched" }] : []));
|
|
3743
|
-
invalid = input(false, ...(ngDevMode ? [{ debugName: "invalid" }] : []));
|
|
3744
|
-
sfErrors = input([], { ...(ngDevMode ? { debugName: "sfErrors" } : {}), alias: 'errors' });
|
|
3745
|
-
// ============ OUTPUTS ============
|
|
3746
|
-
/** Emitted when time value changes. */
|
|
3747
|
-
timeChange = output();
|
|
3748
|
-
// ============ INTERNAL STATE ============
|
|
3749
|
-
/** Internal value state. */
|
|
3750
|
-
internalValue = linkedSignal(() => this.value() ?? null, ...(ngDevMode ? [{ debugName: "internalValue" }] : []));
|
|
3751
|
-
/** Which segment is currently focused. */
|
|
3752
|
-
activeSegment = signal(null, ...(ngDevMode ? [{ debugName: "activeSegment" }] : []));
|
|
3753
|
-
/** Pending typed digits for auto-advance. */
|
|
3754
|
-
pendingDigits = signal('', ...(ngDevMode ? [{ debugName: "pendingDigits" }] : []));
|
|
3755
|
-
/** Live announcements for screen readers. */
|
|
3756
|
-
liveAnnouncement = signal('', ...(ngDevMode ? [{ debugName: "liveAnnouncement" }] : []));
|
|
3757
|
-
/** IDs for aria-describedby (set by form-field). */
|
|
3758
|
-
_describedByIds = signal('', ...(ngDevMode ? [{ debugName: "_describedByIds" }] : []));
|
|
3759
|
-
/** Form field appearance (set by form-field). */
|
|
3760
|
-
_appearance = signal('outline', ...(ngDevMode ? [{ debugName: "_appearance" }] : []));
|
|
3761
|
-
// ============ FormFieldControl SIGNALS ============
|
|
3762
|
-
/** Whether the time picker is focused. Implements FormFieldControl. */
|
|
3763
|
-
focused = computed(() => this.activeSegment() !== null, ...(ngDevMode ? [{ debugName: "focused" }] : []));
|
|
3764
|
-
/** Whether the label should float. */
|
|
3765
|
-
shouldLabelFloat = computed(() => this.focused() || this.internalValue() !== null, ...(ngDevMode ? [{ debugName: "shouldLabelFloat" }] : []));
|
|
3766
|
-
/** Whether the control is in an error state. Implements FormFieldControl. */
|
|
3767
|
-
errorState = computed(() => {
|
|
3768
|
-
if (!this.ngControl) {
|
|
3769
|
-
return this.invalid() && this.touched();
|
|
3770
|
-
}
|
|
3771
|
-
const matcher = this.errorStateMatcher() ?? this.defaultErrorStateMatcher;
|
|
3772
|
-
const form = this.parentFormGroup ?? this.parentForm;
|
|
3773
|
-
return matcher.isErrorState(this.ngControl.control ?? null, form);
|
|
3774
|
-
}, ...(ngDevMode ? [{ debugName: "errorState" }] : []));
|
|
3775
|
-
/** Structured validation errors from Signal Forms. */
|
|
3776
|
-
errors = computed(() => !this.ngControl ? this.sfErrors() : null, ...(ngDevMode ? [{ debugName: "errors" }] : []));
|
|
3777
|
-
/** Unique ID for the control (maps to hours input). Implements FormFieldControl. */
|
|
3778
|
-
id = computed(() => `${this.timepickerId}-hours`, ...(ngDevMode ? [{ debugName: "id" }] : []));
|
|
3779
|
-
/**
|
|
3780
|
-
* Effective state combining manual state with automatic error detection.
|
|
3781
|
-
*/
|
|
3782
|
-
effectiveState = computed(() => {
|
|
3783
|
-
const manualState = this.state();
|
|
3784
|
-
if (manualState !== 'default')
|
|
3785
|
-
return manualState;
|
|
3786
|
-
return this.errorState() ? 'error' : 'default';
|
|
3787
|
-
}, ...(ngDevMode ? [{ debugName: "effectiveState" }] : []));
|
|
3788
|
-
/** Combined aria-describedby from form-field. */
|
|
3789
|
-
effectiveAriaDescribedBy = computed(() => this._describedByIds() || null, ...(ngDevMode ? [{ debugName: "effectiveAriaDescribedBy" }] : []));
|
|
3790
|
-
// ============ COMPUTED STATE ============
|
|
3791
|
-
/** Whether to use 12-hour format. */
|
|
3792
|
-
is12Hour = computed(() => {
|
|
3793
|
-
const explicit = this.use12HourFormat();
|
|
3794
|
-
if (explicit !== null)
|
|
3795
|
-
return explicit;
|
|
3796
|
-
try {
|
|
3797
|
-
const options = new Intl.DateTimeFormat(this.localeId, { hour: 'numeric' }).resolvedOptions();
|
|
3798
|
-
return options.hour12 === true;
|
|
3799
|
-
}
|
|
3800
|
-
catch {
|
|
3801
|
-
return false;
|
|
3802
|
-
}
|
|
3803
|
-
}, ...(ngDevMode ? [{ debugName: "is12Hour" }] : []));
|
|
3804
|
-
/** Current period (AM/PM). */
|
|
3805
|
-
period = computed(() => {
|
|
3806
|
-
const value = this.internalValue();
|
|
3807
|
-
if (!value)
|
|
3808
|
-
return 'AM';
|
|
3809
|
-
return value.hours >= 12 ? 'PM' : 'AM';
|
|
3810
|
-
}, ...(ngDevMode ? [{ debugName: "period" }] : []));
|
|
3811
|
-
/** Display hours (converted from 24h to 12h when needed). */
|
|
3812
|
-
displayHours = computed(() => {
|
|
3813
|
-
const value = this.internalValue();
|
|
3814
|
-
if (!value)
|
|
3815
|
-
return null;
|
|
3816
|
-
if (!this.is12Hour())
|
|
3817
|
-
return value.hours;
|
|
3818
|
-
const h = value.hours % 12;
|
|
3819
|
-
return h === 0 ? 12 : h;
|
|
3820
|
-
}, ...(ngDevMode ? [{ debugName: "displayHours" }] : []));
|
|
3821
|
-
/** Formatted hours string. */
|
|
3822
|
-
formattedHours = computed(() => {
|
|
3823
|
-
const h = this.displayHours();
|
|
3824
|
-
if (h === null)
|
|
3825
|
-
return '';
|
|
3826
|
-
return h.toString().padStart(2, '0');
|
|
3827
|
-
}, ...(ngDevMode ? [{ debugName: "formattedHours" }] : []));
|
|
3828
|
-
/** Formatted minutes string. */
|
|
3829
|
-
formattedMinutes = computed(() => {
|
|
3830
|
-
const value = this.internalValue();
|
|
3831
|
-
if (!value)
|
|
3832
|
-
return '';
|
|
3833
|
-
return value.minutes.toString().padStart(2, '0');
|
|
3834
|
-
}, ...(ngDevMode ? [{ debugName: "formattedMinutes" }] : []));
|
|
3835
|
-
/** Formatted seconds string. */
|
|
3836
|
-
formattedSeconds = computed(() => {
|
|
3837
|
-
const value = this.internalValue();
|
|
3838
|
-
if (!value)
|
|
3839
|
-
return '';
|
|
3840
|
-
return value.seconds.toString().padStart(2, '0');
|
|
3841
|
-
}, ...(ngDevMode ? [{ debugName: "formattedSeconds" }] : []));
|
|
3842
|
-
/** Container classes. */
|
|
3843
|
-
containerClasses = computed(() => {
|
|
3844
|
-
const base = timepickerContainerVariants({
|
|
3845
|
-
variant: this.variant(),
|
|
3846
|
-
size: this.size(),
|
|
3847
|
-
state: this.effectiveState(),
|
|
3848
|
-
});
|
|
3849
|
-
const disabled = this.disabled() ? timepickerDisabledVariants() : '';
|
|
3850
|
-
// For naked variant, add padding based on form-field appearance
|
|
3851
|
-
let paddingClasses = '';
|
|
3852
|
-
if (this.variant() === 'naked') {
|
|
3853
|
-
paddingClasses = this._appearance() === 'fill' ? 'pt-5 pb-1.5 px-3' : 'py-2.5 px-3';
|
|
3854
|
-
}
|
|
3855
|
-
return mergeClasses(base, disabled, paddingClasses, this.userClass());
|
|
3856
|
-
}, ...(ngDevMode ? [{ debugName: "containerClasses" }] : []));
|
|
3857
|
-
/** Segment input classes. */
|
|
3858
|
-
segmentClasses = computed(() => {
|
|
3859
|
-
return timepickerSegmentVariants({ size: this.size() });
|
|
3860
|
-
}, ...(ngDevMode ? [{ debugName: "segmentClasses" }] : []));
|
|
3861
|
-
/** Separator classes. */
|
|
3862
|
-
separatorClasses = computed(() => {
|
|
3863
|
-
return timepickerSeparatorVariants({ size: this.size() });
|
|
3864
|
-
}, ...(ngDevMode ? [{ debugName: "separatorClasses" }] : []));
|
|
3865
|
-
/** Period button classes. */
|
|
3866
|
-
periodClasses = computed(() => {
|
|
3867
|
-
return timepickerPeriodVariants({ size: this.size() });
|
|
3868
|
-
}, ...(ngDevMode ? [{ debugName: "periodClasses" }] : []));
|
|
3869
|
-
// ============ CVA CALLBACKS ============
|
|
3870
|
-
onChange = () => { };
|
|
3871
|
-
onTouched = () => { };
|
|
3872
|
-
onValidatorChange = () => { };
|
|
3873
|
-
constructor() {
|
|
3874
|
-
if (this.ngControl) {
|
|
3875
|
-
this.ngControl.valueAccessor = this;
|
|
3876
|
-
}
|
|
3877
|
-
}
|
|
3878
|
-
// ============ CVA IMPLEMENTATION ============
|
|
3879
|
-
writeValue(value) {
|
|
3880
|
-
this.internalValue.set(value);
|
|
3881
|
-
}
|
|
3882
|
-
registerOnChange(fn) {
|
|
3883
|
-
this.onChange = fn;
|
|
3884
|
-
}
|
|
3885
|
-
registerOnTouched(fn) {
|
|
3886
|
-
this.onTouched = fn;
|
|
3887
|
-
}
|
|
3888
|
-
setDisabledState(isDisabled) {
|
|
3889
|
-
this.disabled.set(isDisabled);
|
|
3890
|
-
}
|
|
3891
|
-
// ============ VALIDATOR IMPLEMENTATION ============
|
|
3892
|
-
validate() {
|
|
3893
|
-
const value = this.internalValue();
|
|
3894
|
-
if (this.required() && !value) {
|
|
3895
|
-
return { required: true };
|
|
3896
|
-
}
|
|
3897
|
-
if (value) {
|
|
3898
|
-
const minTime = this.minTime();
|
|
3899
|
-
if (minTime && compareTime(value, minTime) < 0) {
|
|
3900
|
-
return { minTime: { min: minTime, actual: value } };
|
|
3901
|
-
}
|
|
3902
|
-
const maxTime = this.maxTime();
|
|
3903
|
-
if (maxTime && compareTime(value, maxTime) > 0) {
|
|
3904
|
-
return { maxTime: { max: maxTime, actual: value } };
|
|
3905
|
-
}
|
|
3906
|
-
}
|
|
3907
|
-
return null;
|
|
3908
|
-
}
|
|
3909
|
-
registerOnValidatorChange(fn) {
|
|
3910
|
-
this.onValidatorChange = fn;
|
|
3911
|
-
}
|
|
3912
|
-
// ============ EVENT HANDLERS ============
|
|
3913
|
-
onSegmentFocus(segment) {
|
|
3914
|
-
this.activeSegment.set(segment);
|
|
3915
|
-
this.pendingDigits.set('');
|
|
3916
|
-
}
|
|
3917
|
-
onSegmentBlur(segment) {
|
|
3918
|
-
this.activeSegment.set(null);
|
|
3919
|
-
this.pendingDigits.set('');
|
|
3920
|
-
this.snapToStep(segment);
|
|
3921
|
-
this.onTouched();
|
|
3922
|
-
this.touched.set(true);
|
|
3923
|
-
}
|
|
3924
|
-
onSegmentInput(event, segment) {
|
|
3925
|
-
// Prevent default browser input handling — we manage value via keydown
|
|
3926
|
-
const input = event.target;
|
|
3927
|
-
// Restore the formatted value to prevent browser from changing display
|
|
3928
|
-
if (segment === 'hours') {
|
|
3929
|
-
input.value = this.formattedHours();
|
|
3930
|
-
}
|
|
3931
|
-
else if (segment === 'minutes') {
|
|
3932
|
-
input.value = this.formattedMinutes();
|
|
3933
|
-
}
|
|
3934
|
-
else {
|
|
3935
|
-
input.value = this.formattedSeconds();
|
|
3936
|
-
}
|
|
3937
|
-
}
|
|
3938
|
-
onSegmentKeydown(event, segment) {
|
|
3939
|
-
switch (event.key) {
|
|
3940
|
-
case 'ArrowUp':
|
|
3941
|
-
event.preventDefault();
|
|
3942
|
-
this.incrementSegment(segment, 1);
|
|
3943
|
-
break;
|
|
3944
|
-
case 'ArrowDown':
|
|
3945
|
-
event.preventDefault();
|
|
3946
|
-
this.incrementSegment(segment, -1);
|
|
3947
|
-
break;
|
|
3948
|
-
case 'ArrowRight':
|
|
3949
|
-
event.preventDefault();
|
|
3950
|
-
this.focusNextSegment(segment);
|
|
3951
|
-
break;
|
|
3952
|
-
case 'ArrowLeft':
|
|
3953
|
-
event.preventDefault();
|
|
3954
|
-
this.focusPrevSegment(segment);
|
|
3955
|
-
break;
|
|
3956
|
-
case 'Home':
|
|
3957
|
-
event.preventDefault();
|
|
3958
|
-
this.setSegmentToMin(segment);
|
|
3959
|
-
break;
|
|
3960
|
-
case 'End':
|
|
3961
|
-
event.preventDefault();
|
|
3962
|
-
this.setSegmentToMax(segment);
|
|
3963
|
-
break;
|
|
3964
|
-
case 'Backspace':
|
|
3965
|
-
case 'Delete':
|
|
3966
|
-
event.preventDefault();
|
|
3967
|
-
this.pendingDigits.set('');
|
|
3968
|
-
break;
|
|
3969
|
-
default:
|
|
3970
|
-
if (/^[0-9]$/.test(event.key)) {
|
|
3971
|
-
event.preventDefault();
|
|
3972
|
-
this.handleDigitInput(event.key, segment);
|
|
3973
|
-
}
|
|
3974
|
-
break;
|
|
3975
|
-
}
|
|
3976
|
-
}
|
|
3977
|
-
onPeriodKeydown(event) {
|
|
3978
|
-
switch (event.key) {
|
|
3979
|
-
case 'ArrowUp':
|
|
3980
|
-
case 'ArrowDown':
|
|
3981
|
-
event.preventDefault();
|
|
3982
|
-
this.togglePeriod();
|
|
3983
|
-
break;
|
|
3984
|
-
case 'ArrowLeft':
|
|
3985
|
-
event.preventDefault();
|
|
3986
|
-
this.focusPrevSegment('period');
|
|
3987
|
-
break;
|
|
3988
|
-
case 'a':
|
|
3989
|
-
case 'A':
|
|
3990
|
-
event.preventDefault();
|
|
3991
|
-
this.setPeriod('AM');
|
|
3992
|
-
break;
|
|
3993
|
-
case 'p':
|
|
3994
|
-
case 'P':
|
|
3995
|
-
event.preventDefault();
|
|
3996
|
-
this.setPeriod('PM');
|
|
3997
|
-
break;
|
|
3998
|
-
}
|
|
3999
|
-
}
|
|
4000
|
-
togglePeriod() {
|
|
4001
|
-
const value = this.internalValue();
|
|
4002
|
-
if (!value) {
|
|
4003
|
-
this.updateValue({ hours: 12, minutes: 0, seconds: 0 });
|
|
4004
|
-
return;
|
|
4005
|
-
}
|
|
4006
|
-
const newHours = value.hours >= 12 ? value.hours - 12 : value.hours + 12;
|
|
4007
|
-
this.updateValue({ ...value, hours: newHours });
|
|
4008
|
-
}
|
|
4009
|
-
// ============ FormFieldControl IMPLEMENTATION ============
|
|
4010
|
-
/**
|
|
4011
|
-
* Called when the form field container is clicked.
|
|
4012
|
-
* Implements FormFieldControl.
|
|
4013
|
-
*/
|
|
4014
|
-
onContainerClick(_event) {
|
|
4015
|
-
if (!this.disabled()) {
|
|
4016
|
-
this.hoursInputRef()?.nativeElement.focus();
|
|
4017
|
-
}
|
|
4018
|
-
}
|
|
4019
|
-
/**
|
|
4020
|
-
* Sets the describedBy IDs from the form field.
|
|
4021
|
-
* Called by the parent form field component.
|
|
4022
|
-
*/
|
|
4023
|
-
setDescribedByIds(ids) {
|
|
4024
|
-
this._describedByIds.set(ids);
|
|
4025
|
-
}
|
|
4026
|
-
/**
|
|
4027
|
-
* Sets the appearance for styling.
|
|
4028
|
-
* Called by the parent form field component.
|
|
4029
|
-
*/
|
|
4030
|
-
setAppearance(appearance) {
|
|
4031
|
-
this._appearance.set(appearance);
|
|
4032
|
-
}
|
|
4033
|
-
// ============ PRIVATE METHODS ============
|
|
4034
|
-
setPeriod(period) {
|
|
4035
|
-
const value = this.internalValue();
|
|
4036
|
-
if (!value) {
|
|
4037
|
-
const hours = period === 'AM' ? 0 : 12;
|
|
4038
|
-
this.updateValue({ hours, minutes: 0, seconds: 0 });
|
|
4039
|
-
return;
|
|
4040
|
-
}
|
|
4041
|
-
const currentPeriod = this.period();
|
|
4042
|
-
if (currentPeriod === period)
|
|
4043
|
-
return;
|
|
4044
|
-
const newHours = period === 'AM' ? value.hours - 12 : value.hours + 12;
|
|
4045
|
-
this.updateValue({ ...value, hours: newHours });
|
|
4046
|
-
}
|
|
4047
|
-
incrementSegment(segment, direction) {
|
|
4048
|
-
const value = this.internalValue() ?? { hours: 0, minutes: 0, seconds: 0 };
|
|
4049
|
-
const step = segment === 'minutes' ? this.minuteStep() : segment === 'seconds' ? this.secondStep() : 1;
|
|
4050
|
-
const delta = step * direction;
|
|
4051
|
-
let newValue;
|
|
4052
|
-
switch (segment) {
|
|
4053
|
-
case 'hours': {
|
|
4054
|
-
const max = this.is12Hour() ? 12 : 23;
|
|
4055
|
-
const min = this.is12Hour() ? 1 : 0;
|
|
4056
|
-
let h = this.is12Hour() ? (this.displayHours() ?? min) : value.hours;
|
|
4057
|
-
h += delta;
|
|
4058
|
-
// Wrap
|
|
4059
|
-
if (this.is12Hour()) {
|
|
4060
|
-
if (h > max)
|
|
4061
|
-
h = min;
|
|
4062
|
-
if (h < min)
|
|
4063
|
-
h = max;
|
|
4064
|
-
// Convert back to 24h
|
|
4065
|
-
const isPM = this.period() === 'PM';
|
|
4066
|
-
let h24 = h === 12 ? 0 : h;
|
|
4067
|
-
if (isPM)
|
|
4068
|
-
h24 += 12;
|
|
4069
|
-
newValue = { ...value, hours: h24 };
|
|
4070
|
-
}
|
|
4071
|
-
else {
|
|
4072
|
-
if (h > 23)
|
|
4073
|
-
h = 0;
|
|
4074
|
-
if (h < 0)
|
|
4075
|
-
h = 23;
|
|
4076
|
-
newValue = { ...value, hours: h };
|
|
4077
|
-
}
|
|
4078
|
-
break;
|
|
4079
|
-
}
|
|
4080
|
-
case 'minutes': {
|
|
4081
|
-
let m = value.minutes + delta;
|
|
4082
|
-
if (m > 59)
|
|
4083
|
-
m = 0;
|
|
4084
|
-
if (m < 0)
|
|
4085
|
-
m = 59;
|
|
4086
|
-
newValue = { ...value, minutes: m };
|
|
4087
|
-
break;
|
|
4088
|
-
}
|
|
4089
|
-
case 'seconds': {
|
|
4090
|
-
let s = value.seconds + delta;
|
|
4091
|
-
if (s > 59)
|
|
4092
|
-
s = 0;
|
|
4093
|
-
if (s < 0)
|
|
4094
|
-
s = 59;
|
|
4095
|
-
newValue = { ...value, seconds: s };
|
|
4096
|
-
break;
|
|
4097
|
-
}
|
|
4098
|
-
}
|
|
4099
|
-
this.updateValue(newValue);
|
|
4100
|
-
}
|
|
4101
|
-
handleDigitInput(digit, segment) {
|
|
4102
|
-
const pending = this.pendingDigits() + digit;
|
|
4103
|
-
const value = this.internalValue() ?? { hours: 0, minutes: 0, seconds: 0 };
|
|
4104
|
-
let parsed = parseInt(pending, 10);
|
|
4105
|
-
switch (segment) {
|
|
4106
|
-
case 'hours': {
|
|
4107
|
-
const max = this.is12Hour() ? 12 : 23;
|
|
4108
|
-
const min = this.is12Hour() ? 1 : 0;
|
|
4109
|
-
if (parsed > max)
|
|
4110
|
-
parsed = parseInt(digit, 10);
|
|
4111
|
-
if (parsed < min && pending.length >= 2)
|
|
4112
|
-
parsed = min;
|
|
4113
|
-
// Convert to 24h if 12h mode
|
|
4114
|
-
let h24 = parsed;
|
|
4115
|
-
if (this.is12Hour()) {
|
|
4116
|
-
const isPM = this.period() === 'PM';
|
|
4117
|
-
h24 = parsed === 12 ? 0 : parsed;
|
|
4118
|
-
if (isPM)
|
|
4119
|
-
h24 += 12;
|
|
4120
|
-
}
|
|
4121
|
-
this.updateValue({ ...value, hours: h24 });
|
|
4122
|
-
break;
|
|
4123
|
-
}
|
|
4124
|
-
case 'minutes': {
|
|
4125
|
-
if (parsed > 59)
|
|
4126
|
-
parsed = parseInt(digit, 10);
|
|
4127
|
-
this.updateValue({ ...value, minutes: parsed });
|
|
4128
|
-
break;
|
|
4129
|
-
}
|
|
4130
|
-
case 'seconds': {
|
|
4131
|
-
if (parsed > 59)
|
|
4132
|
-
parsed = parseInt(digit, 10);
|
|
4133
|
-
this.updateValue({ ...value, seconds: parsed });
|
|
4134
|
-
break;
|
|
4135
|
-
}
|
|
4136
|
-
}
|
|
4137
|
-
// Auto-advance after 2 digits
|
|
4138
|
-
if (pending.length >= 2) {
|
|
4139
|
-
this.pendingDigits.set('');
|
|
4140
|
-
this.focusNextSegment(segment);
|
|
4141
|
-
}
|
|
4142
|
-
else {
|
|
4143
|
-
this.pendingDigits.set(pending);
|
|
4144
|
-
}
|
|
4145
|
-
}
|
|
4146
|
-
snapToStep(segment) {
|
|
4147
|
-
const value = this.internalValue();
|
|
4148
|
-
if (!value)
|
|
4149
|
-
return;
|
|
4150
|
-
if (segment === 'minutes' && this.minuteStep() > 1) {
|
|
4151
|
-
const step = this.minuteStep();
|
|
4152
|
-
const snapped = Math.round(value.minutes / step) * step;
|
|
4153
|
-
const clamped = Math.min(snapped, 59);
|
|
4154
|
-
if (clamped !== value.minutes) {
|
|
4155
|
-
this.updateValue({ ...value, minutes: clamped });
|
|
4156
|
-
}
|
|
4157
|
-
}
|
|
4158
|
-
if (segment === 'seconds' && this.secondStep() > 1) {
|
|
4159
|
-
const step = this.secondStep();
|
|
4160
|
-
const snapped = Math.round(value.seconds / step) * step;
|
|
4161
|
-
const clamped = Math.min(snapped, 59);
|
|
4162
|
-
if (clamped !== value.seconds) {
|
|
4163
|
-
this.updateValue({ ...value, seconds: clamped });
|
|
4164
|
-
}
|
|
4165
|
-
}
|
|
4166
|
-
}
|
|
4167
|
-
setSegmentToMin(segment) {
|
|
4168
|
-
const value = this.internalValue() ?? { hours: 0, minutes: 0, seconds: 0 };
|
|
4169
|
-
switch (segment) {
|
|
4170
|
-
case 'hours': {
|
|
4171
|
-
if (this.is12Hour()) {
|
|
4172
|
-
// Min display is 1; convert to 24h
|
|
4173
|
-
const isPM = this.period() === 'PM';
|
|
4174
|
-
const h24 = isPM ? 13 : 1;
|
|
4175
|
-
this.updateValue({ ...value, hours: h24 });
|
|
4176
|
-
}
|
|
4177
|
-
else {
|
|
4178
|
-
this.updateValue({ ...value, hours: 0 });
|
|
4179
|
-
}
|
|
4180
|
-
break;
|
|
4181
|
-
}
|
|
4182
|
-
case 'minutes':
|
|
4183
|
-
this.updateValue({ ...value, minutes: 0 });
|
|
4184
|
-
break;
|
|
4185
|
-
case 'seconds':
|
|
4186
|
-
this.updateValue({ ...value, seconds: 0 });
|
|
4187
|
-
break;
|
|
4188
|
-
}
|
|
4189
|
-
}
|
|
4190
|
-
setSegmentToMax(segment) {
|
|
4191
|
-
const value = this.internalValue() ?? { hours: 0, minutes: 0, seconds: 0 };
|
|
4192
|
-
switch (segment) {
|
|
4193
|
-
case 'hours': {
|
|
4194
|
-
if (this.is12Hour()) {
|
|
4195
|
-
// Max display is 12; 12 in 12h = 0 in 24h (for AM) or 12 (for PM)
|
|
4196
|
-
const isPM = this.period() === 'PM';
|
|
4197
|
-
const h24 = isPM ? 12 : 0;
|
|
4198
|
-
this.updateValue({ ...value, hours: h24 });
|
|
4199
|
-
}
|
|
4200
|
-
else {
|
|
4201
|
-
this.updateValue({ ...value, hours: 23 });
|
|
4202
|
-
}
|
|
4203
|
-
break;
|
|
4204
|
-
}
|
|
4205
|
-
case 'minutes':
|
|
4206
|
-
this.updateValue({ ...value, minutes: 59 });
|
|
4207
|
-
break;
|
|
4208
|
-
case 'seconds':
|
|
4209
|
-
this.updateValue({ ...value, seconds: 59 });
|
|
4210
|
-
break;
|
|
4211
|
-
}
|
|
4212
|
-
}
|
|
4213
|
-
focusNextSegment(current) {
|
|
4214
|
-
switch (current) {
|
|
4215
|
-
case 'hours':
|
|
4216
|
-
this.minutesInputRef()?.nativeElement.focus();
|
|
4217
|
-
break;
|
|
4218
|
-
case 'minutes':
|
|
4219
|
-
if (this.showSeconds()) {
|
|
4220
|
-
this.secondsInputRef()?.nativeElement.focus();
|
|
4221
|
-
}
|
|
4222
|
-
else if (this.is12Hour()) {
|
|
4223
|
-
this.periodButtonRef()?.nativeElement.focus();
|
|
4224
|
-
}
|
|
4225
|
-
break;
|
|
4226
|
-
case 'seconds':
|
|
4227
|
-
if (this.is12Hour()) {
|
|
4228
|
-
this.periodButtonRef()?.nativeElement.focus();
|
|
4229
|
-
}
|
|
4230
|
-
break;
|
|
4231
|
-
}
|
|
4232
|
-
}
|
|
4233
|
-
focusPrevSegment(current) {
|
|
4234
|
-
switch (current) {
|
|
4235
|
-
case 'minutes':
|
|
4236
|
-
this.hoursInputRef()?.nativeElement.focus();
|
|
4237
|
-
break;
|
|
4238
|
-
case 'seconds':
|
|
4239
|
-
this.minutesInputRef()?.nativeElement.focus();
|
|
4240
|
-
break;
|
|
4241
|
-
case 'period':
|
|
4242
|
-
if (this.showSeconds()) {
|
|
4243
|
-
this.secondsInputRef()?.nativeElement.focus();
|
|
4244
|
-
}
|
|
4245
|
-
else {
|
|
4246
|
-
this.minutesInputRef()?.nativeElement.focus();
|
|
4247
|
-
}
|
|
4248
|
-
break;
|
|
4249
|
-
}
|
|
4250
|
-
}
|
|
4251
|
-
updateValue(value) {
|
|
4252
|
-
this.value.set(value);
|
|
4253
|
-
this.onChange(value);
|
|
4254
|
-
this.timeChange.emit(value);
|
|
4255
|
-
this.onValidatorChange();
|
|
4256
|
-
if (value) {
|
|
4257
|
-
const h = this.is12Hour()
|
|
4258
|
-
? `${(value.hours % 12 || 12)}:${value.minutes.toString().padStart(2, '0')}${this.showSeconds() ? ':' + value.seconds.toString().padStart(2, '0') : ''} ${value.hours >= 12 ? 'PM' : 'AM'}`
|
|
4259
|
-
: `${value.hours.toString().padStart(2, '0')}:${value.minutes.toString().padStart(2, '0')}${this.showSeconds() ? ':' + value.seconds.toString().padStart(2, '0') : ''}`;
|
|
4260
|
-
this.liveAnnouncement.set(`Time set to ${h}`);
|
|
4261
|
-
}
|
|
4262
|
-
}
|
|
4263
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: ComTimePicker, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4264
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: ComTimePicker, isStandalone: true, selector: "com-time-picker", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, showSeconds: { classPropertyName: "showSeconds", publicName: "showSeconds", isSignal: true, isRequired: false, transformFunction: null }, use12HourFormat: { classPropertyName: "use12HourFormat", publicName: "use12HourFormat", isSignal: true, isRequired: false, transformFunction: null }, minuteStep: { classPropertyName: "minuteStep", publicName: "minuteStep", isSignal: true, isRequired: false, transformFunction: null }, secondStep: { classPropertyName: "secondStep", publicName: "secondStep", isSignal: true, isRequired: false, transformFunction: null }, minTime: { classPropertyName: "minTime", publicName: "minTime", isSignal: true, isRequired: false, transformFunction: null }, maxTime: { classPropertyName: "maxTime", publicName: "maxTime", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, state: { classPropertyName: "state", publicName: "state", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, errorStateMatcher: { classPropertyName: "errorStateMatcher", publicName: "errorStateMatcher", isSignal: true, isRequired: false, transformFunction: null }, touched: { classPropertyName: "touched", publicName: "touched", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, sfErrors: { classPropertyName: "sfErrors", publicName: "errors", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", disabled: "disabledChange", touched: "touchedChange", timeChange: "timeChange" }, host: { properties: { "class.com-time-picker-disabled": "disabled()", "attr.role": "\"group\"", "attr.aria-label": "ariaLabel() || \"Time picker\"", "attr.aria-disabled": "disabled() || null" }, classAttribute: "com-time-picker-host inline-block" }, providers: [{ provide: FormFieldControl, useExisting: forwardRef(() => ComTimePicker) }], viewQueries: [{ propertyName: "hoursInputRef", first: true, predicate: ["hoursInput"], descendants: true, isSignal: true }, { propertyName: "minutesInputRef", first: true, predicate: ["minutesInput"], descendants: true, isSignal: true }, { propertyName: "secondsInputRef", first: true, predicate: ["secondsInput"], descendants: true, isSignal: true }, { propertyName: "periodButtonRef", first: true, predicate: ["periodButton"], descendants: true, isSignal: true }], exportAs: ["comTimePicker"], ngImport: i0, template: `
|
|
4265
|
-
<div [class]="containerClasses()">
|
|
4266
|
-
<!-- Hours -->
|
|
4267
|
-
<input
|
|
4268
|
-
#hoursInput
|
|
4269
|
-
type="text"
|
|
4270
|
-
inputmode="numeric"
|
|
4271
|
-
role="spinbutton"
|
|
4272
|
-
[class]="segmentClasses()"
|
|
4273
|
-
[attr.id]="id()"
|
|
4274
|
-
[attr.aria-label]="'Hours'"
|
|
4275
|
-
[attr.aria-describedby]="effectiveAriaDescribedBy() || null"
|
|
4276
|
-
[attr.aria-valuenow]="displayHours()"
|
|
4277
|
-
[attr.aria-valuemin]="is12Hour() ? 1 : 0"
|
|
4278
|
-
[attr.aria-valuemax]="is12Hour() ? 12 : 23"
|
|
4279
|
-
[value]="formattedHours()"
|
|
4280
|
-
[placeholder]="placeholder()"
|
|
4281
|
-
[disabled]="disabled()"
|
|
4282
|
-
maxlength="2"
|
|
4283
|
-
(keydown)="onSegmentKeydown($event, 'hours')"
|
|
4284
|
-
(input)="onSegmentInput($event, 'hours')"
|
|
4285
|
-
(focus)="onSegmentFocus('hours')"
|
|
4286
|
-
(blur)="onSegmentBlur('hours')"
|
|
4287
|
-
/>
|
|
4288
|
-
|
|
4289
|
-
<span [class]="separatorClasses()" aria-hidden="true">:</span>
|
|
4290
|
-
|
|
4291
|
-
<!-- Minutes -->
|
|
4292
|
-
<input
|
|
4293
|
-
#minutesInput
|
|
4294
|
-
type="text"
|
|
4295
|
-
inputmode="numeric"
|
|
4296
|
-
role="spinbutton"
|
|
4297
|
-
[class]="segmentClasses()"
|
|
4298
|
-
[attr.aria-label]="'Minutes'"
|
|
4299
|
-
[attr.aria-valuenow]="internalValue()?.minutes ?? null"
|
|
4300
|
-
[attr.aria-valuemin]="0"
|
|
4301
|
-
[attr.aria-valuemax]="59"
|
|
4302
|
-
[value]="formattedMinutes()"
|
|
4303
|
-
[placeholder]="placeholder()"
|
|
4304
|
-
[disabled]="disabled()"
|
|
4305
|
-
maxlength="2"
|
|
4306
|
-
(keydown)="onSegmentKeydown($event, 'minutes')"
|
|
4307
|
-
(input)="onSegmentInput($event, 'minutes')"
|
|
4308
|
-
(focus)="onSegmentFocus('minutes')"
|
|
4309
|
-
(blur)="onSegmentBlur('minutes')"
|
|
4310
|
-
/>
|
|
4311
|
-
|
|
4312
|
-
@if (showSeconds()) {
|
|
4313
|
-
<span [class]="separatorClasses()" aria-hidden="true">:</span>
|
|
4314
|
-
|
|
4315
|
-
<!-- Seconds -->
|
|
4316
|
-
<input
|
|
4317
|
-
#secondsInput
|
|
4318
|
-
type="text"
|
|
4319
|
-
inputmode="numeric"
|
|
4320
|
-
role="spinbutton"
|
|
4321
|
-
[class]="segmentClasses()"
|
|
4322
|
-
[attr.aria-label]="'Seconds'"
|
|
4323
|
-
[attr.aria-valuenow]="internalValue()?.seconds ?? null"
|
|
4324
|
-
[attr.aria-valuemin]="0"
|
|
4325
|
-
[attr.aria-valuemax]="59"
|
|
4326
|
-
[value]="formattedSeconds()"
|
|
4327
|
-
[placeholder]="placeholder()"
|
|
4328
|
-
[disabled]="disabled()"
|
|
4329
|
-
maxlength="2"
|
|
4330
|
-
(keydown)="onSegmentKeydown($event, 'seconds')"
|
|
4331
|
-
(input)="onSegmentInput($event, 'seconds')"
|
|
4332
|
-
(focus)="onSegmentFocus('seconds')"
|
|
4333
|
-
(blur)="onSegmentBlur('seconds')"
|
|
4334
|
-
/>
|
|
4335
|
-
}
|
|
4336
|
-
|
|
4337
|
-
@if (is12Hour()) {
|
|
4338
|
-
<button
|
|
4339
|
-
#periodButton
|
|
4340
|
-
type="button"
|
|
4341
|
-
[class]="periodClasses()"
|
|
4342
|
-
[attr.aria-label]="'Toggle AM/PM, currently ' + period()"
|
|
4343
|
-
[disabled]="disabled()"
|
|
4344
|
-
(click)="togglePeriod()"
|
|
4345
|
-
(keydown)="onPeriodKeydown($event)"
|
|
4346
|
-
>
|
|
4347
|
-
{{ period() }}
|
|
4348
|
-
</button>
|
|
4349
|
-
}
|
|
4350
|
-
</div>
|
|
4351
|
-
|
|
4352
|
-
<div class="sr-only" aria-live="polite" aria-atomic="true">
|
|
4353
|
-
{{ liveAnnouncement() }}
|
|
4354
|
-
</div>
|
|
4355
|
-
`, isInline: true, styles: [".sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
4356
|
-
}
|
|
4357
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: ComTimePicker, decorators: [{
|
|
4358
|
-
type: Component,
|
|
4359
|
-
args: [{ selector: 'com-time-picker', exportAs: 'comTimePicker', template: `
|
|
4360
|
-
<div [class]="containerClasses()">
|
|
4361
|
-
<!-- Hours -->
|
|
4362
|
-
<input
|
|
4363
|
-
#hoursInput
|
|
4364
|
-
type="text"
|
|
4365
|
-
inputmode="numeric"
|
|
4366
|
-
role="spinbutton"
|
|
4367
|
-
[class]="segmentClasses()"
|
|
4368
|
-
[attr.id]="id()"
|
|
4369
|
-
[attr.aria-label]="'Hours'"
|
|
4370
|
-
[attr.aria-describedby]="effectiveAriaDescribedBy() || null"
|
|
4371
|
-
[attr.aria-valuenow]="displayHours()"
|
|
4372
|
-
[attr.aria-valuemin]="is12Hour() ? 1 : 0"
|
|
4373
|
-
[attr.aria-valuemax]="is12Hour() ? 12 : 23"
|
|
4374
|
-
[value]="formattedHours()"
|
|
4375
|
-
[placeholder]="placeholder()"
|
|
4376
|
-
[disabled]="disabled()"
|
|
4377
|
-
maxlength="2"
|
|
4378
|
-
(keydown)="onSegmentKeydown($event, 'hours')"
|
|
4379
|
-
(input)="onSegmentInput($event, 'hours')"
|
|
4380
|
-
(focus)="onSegmentFocus('hours')"
|
|
4381
|
-
(blur)="onSegmentBlur('hours')"
|
|
4382
|
-
/>
|
|
4383
|
-
|
|
4384
|
-
<span [class]="separatorClasses()" aria-hidden="true">:</span>
|
|
4385
|
-
|
|
4386
|
-
<!-- Minutes -->
|
|
4387
|
-
<input
|
|
4388
|
-
#minutesInput
|
|
4389
|
-
type="text"
|
|
4390
|
-
inputmode="numeric"
|
|
4391
|
-
role="spinbutton"
|
|
4392
|
-
[class]="segmentClasses()"
|
|
4393
|
-
[attr.aria-label]="'Minutes'"
|
|
4394
|
-
[attr.aria-valuenow]="internalValue()?.minutes ?? null"
|
|
4395
|
-
[attr.aria-valuemin]="0"
|
|
4396
|
-
[attr.aria-valuemax]="59"
|
|
4397
|
-
[value]="formattedMinutes()"
|
|
4398
|
-
[placeholder]="placeholder()"
|
|
4399
|
-
[disabled]="disabled()"
|
|
4400
|
-
maxlength="2"
|
|
4401
|
-
(keydown)="onSegmentKeydown($event, 'minutes')"
|
|
4402
|
-
(input)="onSegmentInput($event, 'minutes')"
|
|
4403
|
-
(focus)="onSegmentFocus('minutes')"
|
|
4404
|
-
(blur)="onSegmentBlur('minutes')"
|
|
4405
|
-
/>
|
|
4406
|
-
|
|
4407
|
-
@if (showSeconds()) {
|
|
4408
|
-
<span [class]="separatorClasses()" aria-hidden="true">:</span>
|
|
4409
|
-
|
|
4410
|
-
<!-- Seconds -->
|
|
4411
|
-
<input
|
|
4412
|
-
#secondsInput
|
|
4413
|
-
type="text"
|
|
4414
|
-
inputmode="numeric"
|
|
4415
|
-
role="spinbutton"
|
|
4416
|
-
[class]="segmentClasses()"
|
|
4417
|
-
[attr.aria-label]="'Seconds'"
|
|
4418
|
-
[attr.aria-valuenow]="internalValue()?.seconds ?? null"
|
|
4419
|
-
[attr.aria-valuemin]="0"
|
|
4420
|
-
[attr.aria-valuemax]="59"
|
|
4421
|
-
[value]="formattedSeconds()"
|
|
4422
|
-
[placeholder]="placeholder()"
|
|
4423
|
-
[disabled]="disabled()"
|
|
4424
|
-
maxlength="2"
|
|
4425
|
-
(keydown)="onSegmentKeydown($event, 'seconds')"
|
|
4426
|
-
(input)="onSegmentInput($event, 'seconds')"
|
|
4427
|
-
(focus)="onSegmentFocus('seconds')"
|
|
4428
|
-
(blur)="onSegmentBlur('seconds')"
|
|
4429
|
-
/>
|
|
4430
|
-
}
|
|
4431
|
-
|
|
4432
|
-
@if (is12Hour()) {
|
|
4433
|
-
<button
|
|
4434
|
-
#periodButton
|
|
4435
|
-
type="button"
|
|
4436
|
-
[class]="periodClasses()"
|
|
4437
|
-
[attr.aria-label]="'Toggle AM/PM, currently ' + period()"
|
|
4438
|
-
[disabled]="disabled()"
|
|
4439
|
-
(click)="togglePeriod()"
|
|
4440
|
-
(keydown)="onPeriodKeydown($event)"
|
|
4441
|
-
>
|
|
4442
|
-
{{ period() }}
|
|
4443
|
-
</button>
|
|
4444
|
-
}
|
|
4445
|
-
</div>
|
|
4446
|
-
|
|
4447
|
-
<div class="sr-only" aria-live="polite" aria-atomic="true">
|
|
4448
|
-
{{ liveAnnouncement() }}
|
|
4449
|
-
</div>
|
|
4450
|
-
`, providers: [{ provide: FormFieldControl, useExisting: forwardRef(() => ComTimePicker) }], changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
4451
|
-
class: 'com-time-picker-host inline-block',
|
|
4452
|
-
'[class.com-time-picker-disabled]': 'disabled()',
|
|
4453
|
-
'[attr.role]': '"group"',
|
|
4454
|
-
'[attr.aria-label]': 'ariaLabel() || "Time picker"',
|
|
4455
|
-
'[attr.aria-disabled]': 'disabled() || null',
|
|
4456
|
-
}, styles: [".sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n"] }]
|
|
4457
|
-
}], ctorParameters: () => [], propDecorators: { hoursInputRef: [{ type: i0.ViewChild, args: ['hoursInput', { isSignal: true }] }], minutesInputRef: [{ type: i0.ViewChild, args: ['minutesInput', { isSignal: true }] }], secondsInputRef: [{ type: i0.ViewChild, args: ['secondsInput', { isSignal: true }] }], periodButtonRef: [{ type: i0.ViewChild, args: ['periodButton', { isSignal: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }, { type: i0.Output, args: ["disabledChange"] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], showSeconds: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSeconds", required: false }] }], use12HourFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "use12HourFormat", required: false }] }], minuteStep: [{ type: i0.Input, args: [{ isSignal: true, alias: "minuteStep", required: false }] }], secondStep: [{ type: i0.Input, args: [{ isSignal: true, alias: "secondStep", required: false }] }], minTime: [{ type: i0.Input, args: [{ isSignal: true, alias: "minTime", required: false }] }], maxTime: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxTime", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], state: [{ type: i0.Input, args: [{ isSignal: true, alias: "state", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], errorStateMatcher: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorStateMatcher", required: false }] }], touched: [{ type: i0.Input, args: [{ isSignal: true, alias: "touched", required: false }] }, { type: i0.Output, args: ["touchedChange"] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], sfErrors: [{ type: i0.Input, args: [{ isSignal: true, alias: "errors", required: false }] }], timeChange: [{ type: i0.Output, args: ["timeChange"] }] } });
|
|
4458
|
-
|
|
4459
|
-
/** Default position for the datepicker panel. */
|
|
4460
|
-
const DEFAULT_POSITIONS$1 = [
|
|
4461
|
-
// Below trigger, aligned start
|
|
4462
|
-
{ originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4 },
|
|
4463
|
-
// Above trigger, aligned start
|
|
4464
|
-
{ originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'bottom', offsetY: -4 },
|
|
4465
|
-
// Below trigger, aligned end
|
|
4466
|
-
{ originX: 'end', originY: 'bottom', overlayX: 'end', overlayY: 'top', offsetY: 4 },
|
|
4467
|
-
// Above trigger, aligned end
|
|
4468
|
-
{ originX: 'end', originY: 'top', overlayX: 'end', overlayY: 'bottom', offsetY: -4 },
|
|
4469
|
-
];
|
|
4470
|
-
/**
|
|
4471
|
-
* Single date picker component with calendar popup.
|
|
4472
|
-
* Implements ControlValueAccessor for Reactive Forms and Template-driven Forms.
|
|
4473
|
-
*
|
|
4474
|
-
* @tokens `--color-input-background`, `--color-input-foreground`, `--color-input-border`,
|
|
4475
|
-
* `--color-input-placeholder`, `--color-ring`, `--color-muted`, `--color-muted-foreground`,
|
|
4476
|
-
* `--color-popover`, `--color-popover-foreground`, `--color-border-subtle`,
|
|
4477
|
-
* `--color-primary`, `--color-primary-foreground`, `--color-primary-hover`,
|
|
4478
|
-
* `--color-warn`, `--color-success`, `--color-disabled`, `--color-disabled-foreground`
|
|
4479
|
-
*
|
|
4480
|
-
* @example
|
|
4481
|
-
* ```html
|
|
4482
|
-
* <com-datepicker
|
|
4483
|
-
* formControlName="birthDate"
|
|
4484
|
-
* placeholder="Select date..."
|
|
4485
|
-
* [min]="minDate"
|
|
4486
|
-
* [max]="maxDate"
|
|
4487
|
-
* [showTodayButton]="true"
|
|
4488
|
-
* [showClearButton]="true"
|
|
4489
|
-
* />
|
|
4490
|
-
* ```
|
|
4491
|
-
*/
|
|
4492
|
-
class ComDatepicker {
|
|
4493
|
-
elementRef = inject(ElementRef);
|
|
4494
|
-
destroyRef = inject(DestroyRef);
|
|
4495
|
-
overlay = inject(Overlay);
|
|
4496
|
-
viewContainerRef = inject(ViewContainerRef);
|
|
4497
|
-
document = inject(DOCUMENT);
|
|
4498
|
-
dateAdapter = inject(DATE_ADAPTER);
|
|
4499
|
-
/** NgControl bound to this datepicker (if using reactive forms). */
|
|
4500
|
-
ngControl = inject(NgControl, { optional: true, self: true });
|
|
4501
|
-
defaultErrorStateMatcher = inject(ErrorStateMatcher);
|
|
4502
|
-
parentForm = inject(NgForm, { optional: true });
|
|
4503
|
-
parentFormGroup = inject(FormGroupDirective, { optional: true });
|
|
4504
|
-
/** Reference to the trigger element. */
|
|
4505
|
-
triggerRef = viewChild.required('triggerElement');
|
|
4506
|
-
/** Reference to the input element. */
|
|
4507
|
-
inputRef = viewChild.required('inputElement');
|
|
4508
|
-
/** Reference to the panel template. */
|
|
4509
|
-
panelTemplateRef = viewChild.required('panelTemplate');
|
|
4510
|
-
/** Overlay reference. */
|
|
4511
|
-
overlayRef = null;
|
|
4512
|
-
/** Unique ID for the datepicker. */
|
|
4513
|
-
datepickerId = generateDatepickerId();
|
|
4514
|
-
// ============ INPUTS ============
|
|
4515
|
-
/** Current value. */
|
|
4516
|
-
value = model(null, ...(ngDevMode ? [{ debugName: "value" }] : []));
|
|
4517
|
-
/** Minimum selectable date. */
|
|
4518
|
-
min = input(null, ...(ngDevMode ? [{ debugName: "min" }] : []));
|
|
4519
|
-
/** Maximum selectable date. */
|
|
4520
|
-
max = input(null, ...(ngDevMode ? [{ debugName: "max" }] : []));
|
|
4521
|
-
/** Custom filter function to disable specific dates. */
|
|
4522
|
-
dateFilter = input(null, ...(ngDevMode ? [{ debugName: "dateFilter" }] : []));
|
|
4523
|
-
/** Date the calendar opens to (defaults to selected or today). */
|
|
4524
|
-
startAt = input(null, ...(ngDevMode ? [{ debugName: "startAt" }] : []));
|
|
4525
|
-
/** Initial calendar view. */
|
|
4526
|
-
startView = input('month', ...(ngDevMode ? [{ debugName: "startView" }] : []));
|
|
4527
|
-
/** First day of week override (0=Sun, 1=Mon, ..., 6=Sat). */
|
|
4528
|
-
firstDayOfWeek = input(null, ...(ngDevMode ? [{ debugName: "firstDayOfWeek" }] : []));
|
|
4529
|
-
/** Placeholder text. */
|
|
4530
|
-
placeholder = input('Select date...', ...(ngDevMode ? [{ debugName: "placeholder" }] : []));
|
|
4531
|
-
/** Whether the datepicker is disabled. */
|
|
4532
|
-
disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
|
|
4533
|
-
/** Whether the datepicker is required. */
|
|
4534
|
-
required = input(false, ...(ngDevMode ? [{ debugName: "required" }] : []));
|
|
4535
|
-
/** Display format for the date. */
|
|
4536
|
-
dateFormat = input('medium', ...(ngDevMode ? [{ debugName: "dateFormat" }] : []));
|
|
4537
|
-
/** Show a clear button in the trigger. */
|
|
4538
|
-
showClearButton = input(false, ...(ngDevMode ? [{ debugName: "showClearButton" }] : []));
|
|
4539
|
-
/** Show a today button in the footer. */
|
|
4540
|
-
showTodayButton = input(false, ...(ngDevMode ? [{ debugName: "showTodayButton" }] : []));
|
|
4541
|
-
/** Show a clear button in the footer. */
|
|
4542
|
-
showFooterClearButton = input(false, ...(ngDevMode ? [{ debugName: "showFooterClearButton" }] : []));
|
|
4543
|
-
/** Don't auto-close on selection. */
|
|
4544
|
-
keepOpen = input(false, ...(ngDevMode ? [{ debugName: "keepOpen" }] : []));
|
|
4545
|
-
/** Allow manual text input. */
|
|
4546
|
-
allowManualInput = input(true, ...(ngDevMode ? [{ debugName: "allowManualInput" }] : []));
|
|
4547
|
-
/** Additional CSS classes for the panel. */
|
|
4548
|
-
panelClass = input('', ...(ngDevMode ? [{ debugName: "panelClass" }] : []));
|
|
4549
|
-
/** Panel width strategy. */
|
|
4550
|
-
panelWidth = input('auto', ...(ngDevMode ? [{ debugName: "panelWidth" }] : []));
|
|
4551
|
-
/** CVA variant for trigger styling. */
|
|
4552
|
-
variant = input('default', ...(ngDevMode ? [{ debugName: "variant" }] : []));
|
|
4553
|
-
/** Size variant. */
|
|
4554
|
-
size = input('default', ...(ngDevMode ? [{ debugName: "size" }] : []));
|
|
4555
|
-
/** Validation state. */
|
|
4556
|
-
state = input('default', ...(ngDevMode ? [{ debugName: "state" }] : []));
|
|
4557
|
-
/** Additional CSS classes for the trigger. */
|
|
4558
|
-
userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : {}), alias: 'class' });
|
|
4559
|
-
/** Accessible label for the input. */
|
|
4560
|
-
ariaLabel = input(null, ...(ngDevMode ? [{ debugName: "ariaLabel" }] : []));
|
|
4561
|
-
/** ID of element describing the input. */
|
|
4562
|
-
ariaDescribedBy = input(null, ...(ngDevMode ? [{ debugName: "ariaDescribedBy" }] : []));
|
|
4563
|
-
/** Custom error state matcher for determining when to show errors. */
|
|
4564
|
-
errorStateMatcher = input(...(ngDevMode ? [undefined, { debugName: "errorStateMatcher" }] : []));
|
|
4565
|
-
// Signal Forms inputs — set automatically by [formField] via setInputOnDirectives
|
|
4566
|
-
touched = model(false, ...(ngDevMode ? [{ debugName: "touched" }] : []));
|
|
4567
|
-
invalid = input(false, ...(ngDevMode ? [{ debugName: "invalid" }] : []));
|
|
4568
|
-
sfErrors = input([], { ...(ngDevMode ? { debugName: "sfErrors" } : {}), alias: 'errors' });
|
|
4569
|
-
/** Whether to show the time picker below the calendar. */
|
|
4570
|
-
showTimePicker = input(false, ...(ngDevMode ? [{ debugName: "showTimePicker" }] : []));
|
|
4571
|
-
/** 12h vs 24h format for the time picker. `null` = auto-detect. */
|
|
4572
|
-
use12HourFormat = input(null, ...(ngDevMode ? [{ debugName: "use12HourFormat" }] : []));
|
|
4573
|
-
/** Whether the time picker shows seconds. */
|
|
4574
|
-
showSeconds = input(false, ...(ngDevMode ? [{ debugName: "showSeconds" }] : []));
|
|
4575
|
-
/** Step interval for minutes in the time picker. */
|
|
4576
|
-
minuteStep = input(1, ...(ngDevMode ? [{ debugName: "minuteStep" }] : []));
|
|
4577
|
-
// ============ OUTPUTS ============
|
|
4578
|
-
/** Emitted when a date is selected. */
|
|
4579
|
-
dateChange = output();
|
|
4580
|
-
/** Emitted when the panel opens. */
|
|
4581
|
-
opened = output();
|
|
4582
|
-
/** Emitted when the panel closes. */
|
|
4583
|
-
closed = output();
|
|
4584
|
-
// ============ INTERNAL STATE ============
|
|
4585
|
-
/** Whether the panel is open. */
|
|
4586
|
-
isOpen = signal(false, ...(ngDevMode ? [{ debugName: "isOpen" }] : []));
|
|
4587
|
-
/** Internal value state (managed by CVA or input). */
|
|
4588
|
-
internalValue = linkedSignal(() => this.value() ?? null, ...(ngDevMode ? [{ debugName: "internalValue" }] : []));
|
|
4589
|
-
/** Calendar active date for navigation. */
|
|
4590
|
-
calendarActiveDate = signal(this.dateAdapter.today(), ...(ngDevMode ? [{ debugName: "calendarActiveDate" }] : []));
|
|
4591
|
-
/** Live announcements for screen readers. */
|
|
4592
|
-
liveAnnouncement = signal('', ...(ngDevMode ? [{ debugName: "liveAnnouncement" }] : []));
|
|
4593
|
-
/** Whether the input is focused (not panel). */
|
|
4594
|
-
_inputFocused = signal(false, ...(ngDevMode ? [{ debugName: "_inputFocused" }] : []));
|
|
4595
|
-
/** IDs for aria-describedby (set by form-field). */
|
|
4596
|
-
_describedByIds = signal('', ...(ngDevMode ? [{ debugName: "_describedByIds" }] : []));
|
|
4597
|
-
/** Form field appearance (set by form-field). */
|
|
4598
|
-
_appearance = signal('outline', ...(ngDevMode ? [{ debugName: "_appearance" }] : []));
|
|
4599
|
-
// ============ FormFieldControl SIGNALS ============
|
|
4600
|
-
/** Whether the datepicker is focused (input focused or panel open). Implements FormFieldControl. */
|
|
4601
|
-
focused = computed(() => this._inputFocused() || this.isOpen(), ...(ngDevMode ? [{ debugName: "focused" }] : []));
|
|
4602
|
-
/** Whether the label should float. Label floats when focused or has a value. */
|
|
4603
|
-
shouldLabelFloat = computed(() => this.focused() || this.hasValue(), ...(ngDevMode ? [{ debugName: "shouldLabelFloat" }] : []));
|
|
4604
|
-
/** Whether the control is in an error state. Implements FormFieldControl. */
|
|
4605
|
-
errorState = computed(() => {
|
|
4606
|
-
if (!this.ngControl) {
|
|
4607
|
-
// Signal Forms: gate on invalid AND touched
|
|
4608
|
-
return this.invalid() && this.touched();
|
|
4609
|
-
}
|
|
4610
|
-
// Reactive Forms: use ErrorStateMatcher
|
|
4611
|
-
this.isOpen();
|
|
4612
|
-
this.hasValue();
|
|
4613
|
-
const matcher = this.errorStateMatcher() ?? this.defaultErrorStateMatcher;
|
|
4614
|
-
const form = this.parentFormGroup ?? this.parentForm;
|
|
4615
|
-
return matcher.isErrorState(this.ngControl.control ?? null, form);
|
|
4616
|
-
}, ...(ngDevMode ? [{ debugName: "errorState" }] : []));
|
|
4617
|
-
/** Structured validation errors from Signal Forms, exposed for the parent form field. */
|
|
4618
|
-
errors = computed(() => !this.ngControl ? this.sfErrors() : null, ...(ngDevMode ? [{ debugName: "errors" }] : []));
|
|
4619
|
-
/** Unique ID for the control. Implements FormFieldControl. */
|
|
4620
|
-
id = computed(() => `${this.datepickerId}-input`, ...(ngDevMode ? [{ debugName: "id" }] : []));
|
|
4621
|
-
/**
|
|
4622
|
-
* Effective state combining manual state with automatic error detection.
|
|
4623
|
-
* Manual state takes precedence over auto-detected error state.
|
|
4624
|
-
*/
|
|
4625
|
-
effectiveState = computed(() => {
|
|
4626
|
-
const manualState = this.state();
|
|
4627
|
-
if (manualState !== 'default')
|
|
4628
|
-
return manualState;
|
|
4629
|
-
return this.errorState() ? 'error' : 'default';
|
|
4630
|
-
}, ...(ngDevMode ? [{ debugName: "effectiveState" }] : []));
|
|
4631
|
-
/** Combined aria-describedby from form-field and manual input. */
|
|
4632
|
-
effectiveAriaDescribedBy = computed(() => this._describedByIds() || this.ariaDescribedBy() || null, ...(ngDevMode ? [{ debugName: "effectiveAriaDescribedBy" }] : []));
|
|
4633
|
-
// ============ COMPUTED STATE ============
|
|
4634
|
-
/** Input element ID (alias for FormFieldControl id). */
|
|
4635
|
-
inputId = this.id;
|
|
4636
|
-
/** Panel element ID. */
|
|
4637
|
-
panelId = computed(() => `${this.datepickerId}-panel`, ...(ngDevMode ? [{ debugName: "panelId" }] : []));
|
|
4638
|
-
/** Whether the datepicker has a value. */
|
|
4639
|
-
hasValue = computed(() => this.internalValue() !== null, ...(ngDevMode ? [{ debugName: "hasValue" }] : []));
|
|
4640
|
-
/** Icon size based on datepicker size. */
|
|
4641
|
-
iconSize = computed(() => {
|
|
4642
|
-
const sizeMap = {
|
|
4643
|
-
sm: 'sm',
|
|
4644
|
-
default: 'md',
|
|
4645
|
-
lg: 'lg',
|
|
4646
|
-
};
|
|
4647
|
-
return sizeMap[this.size()];
|
|
4648
|
-
}, ...(ngDevMode ? [{ debugName: "iconSize" }] : []));
|
|
4649
|
-
/** Formatted display value. */
|
|
4650
|
-
displayValue = computed(() => {
|
|
4651
|
-
const value = this.internalValue();
|
|
4652
|
-
if (!value)
|
|
4653
|
-
return '';
|
|
4654
|
-
return this.dateAdapter.format(value, this.effectiveDateFormat());
|
|
4655
|
-
}, ...(ngDevMode ? [{ debugName: "displayValue" }] : []));
|
|
4656
|
-
/** Computed trigger classes. */
|
|
4657
|
-
triggerClasses = computed(() => {
|
|
4658
|
-
const baseClasses = datepickerTriggerVariants({
|
|
4659
|
-
variant: this.variant(),
|
|
4660
|
-
size: this.size(),
|
|
4661
|
-
state: this.effectiveState(),
|
|
4662
|
-
open: this.isOpen(),
|
|
4663
|
-
});
|
|
4664
|
-
const disabledClasses = this.disabled() ? datepickerDisabledVariants() : '';
|
|
4665
|
-
// For naked variant, add padding based on form-field appearance
|
|
4666
|
-
let paddingClasses = '';
|
|
4667
|
-
if (this.variant() === 'naked') {
|
|
4668
|
-
paddingClasses = this._appearance() === 'fill' ? 'pt-5 pb-1.5 px-3' : 'py-2.5 px-3';
|
|
4669
|
-
}
|
|
4670
|
-
return mergeClasses(baseClasses, disabledClasses, paddingClasses, this.userClass());
|
|
4671
|
-
}, ...(ngDevMode ? [{ debugName: "triggerClasses" }] : []));
|
|
4672
|
-
/** Computed input classes. */
|
|
4673
|
-
inputClasses = computed(() => {
|
|
4674
|
-
return datepickerInputVariants({ size: this.size() });
|
|
4675
|
-
}, ...(ngDevMode ? [{ debugName: "inputClasses" }] : []));
|
|
4676
|
-
/** Computed icon classes. */
|
|
4677
|
-
iconClasses = computed(() => {
|
|
4678
|
-
return datepickerIconVariants({ size: this.size() });
|
|
4679
|
-
}, ...(ngDevMode ? [{ debugName: "iconClasses" }] : []));
|
|
4680
|
-
/** Computed clear button classes. */
|
|
4681
|
-
clearClasses = computed(() => {
|
|
4682
|
-
return datepickerClearVariants({ size: this.size() });
|
|
4683
|
-
}, ...(ngDevMode ? [{ debugName: "clearClasses" }] : []));
|
|
4684
|
-
/** Computed panel classes. */
|
|
4685
|
-
panelClasses = computed(() => {
|
|
4686
|
-
const baseClasses = datepickerPanelVariants({ size: this.size() });
|
|
4687
|
-
return joinClasses(baseClasses, this.panelClass());
|
|
4688
|
-
}, ...(ngDevMode ? [{ debugName: "panelClasses" }] : []));
|
|
4689
|
-
/** Computed footer classes. */
|
|
4690
|
-
footerClasses = computed(() => {
|
|
4691
|
-
return datepickerFooterVariants({ size: this.size() });
|
|
4692
|
-
}, ...(ngDevMode ? [{ debugName: "footerClasses" }] : []));
|
|
4693
|
-
/** Computed today button classes. */
|
|
4694
|
-
todayButtonClasses = computed(() => {
|
|
4695
|
-
return datepickerFooterButtonVariants({ size: this.size(), variant: 'primary' });
|
|
4696
|
-
}, ...(ngDevMode ? [{ debugName: "todayButtonClasses" }] : []));
|
|
4697
|
-
/** Computed clear button classes (footer). */
|
|
4698
|
-
clearButtonClasses = computed(() => {
|
|
4699
|
-
return datepickerFooterButtonVariants({ size: this.size(), variant: 'secondary' });
|
|
4700
|
-
}, ...(ngDevMode ? [{ debugName: "clearButtonClasses" }] : []));
|
|
4701
|
-
/** Time section divider classes. */
|
|
4702
|
-
timeSectionClasses = computed(() => {
|
|
4703
|
-
return timepickerSectionVariants({ size: this.size() });
|
|
4704
|
-
}, ...(ngDevMode ? [{ debugName: "timeSectionClasses" }] : []));
|
|
4705
|
-
/** Time value derived from the current date value. */
|
|
4706
|
-
timeValue = computed(() => {
|
|
4707
|
-
const date = this.internalValue();
|
|
4708
|
-
if (!date)
|
|
4709
|
-
return null;
|
|
4710
|
-
return createTimeValue(this.dateAdapter.getHours(date), this.dateAdapter.getMinutes(date), this.dateAdapter.getSeconds(date));
|
|
4711
|
-
}, ...(ngDevMode ? [{ debugName: "timeValue" }] : []));
|
|
4712
|
-
/** Effective display format — switches to dateTime when time picker is shown. */
|
|
4713
|
-
effectiveDateFormat = computed(() => {
|
|
4714
|
-
if (this.showTimePicker()) {
|
|
4715
|
-
return this.showSeconds() ? 'dateTimeLong' : 'dateTimeMedium';
|
|
4716
|
-
}
|
|
4717
|
-
return this.dateFormat();
|
|
4718
|
-
}, ...(ngDevMode ? [{ debugName: "effectiveDateFormat" }] : []));
|
|
4719
|
-
/** Whether the panel should stay open (keepOpen or time picker shown). */
|
|
4720
|
-
effectiveKeepOpen = computed(() => {
|
|
4721
|
-
return this.keepOpen() || this.showTimePicker();
|
|
4722
|
-
}, ...(ngDevMode ? [{ debugName: "effectiveKeepOpen" }] : []));
|
|
4723
|
-
// ============ CVA CALLBACKS ============
|
|
4724
|
-
onChange = () => { };
|
|
4725
|
-
onTouched = () => { };
|
|
4726
|
-
onValidatorChange = () => { };
|
|
4727
|
-
constructor() {
|
|
4728
|
-
// Wire up NgControl if present
|
|
4729
|
-
if (this.ngControl) {
|
|
4730
|
-
this.ngControl.valueAccessor = this;
|
|
4731
|
-
}
|
|
4732
|
-
// Sync calendar active date with internal value or startAt
|
|
4733
|
-
effect(() => {
|
|
4734
|
-
const value = this.internalValue();
|
|
4735
|
-
const startAt = this.startAt();
|
|
4736
|
-
if (value) {
|
|
4737
|
-
this.calendarActiveDate.set(value);
|
|
4738
|
-
}
|
|
4739
|
-
else if (startAt) {
|
|
4740
|
-
this.calendarActiveDate.set(startAt);
|
|
4741
|
-
}
|
|
4742
|
-
else {
|
|
4743
|
-
this.calendarActiveDate.set(this.dateAdapter.today());
|
|
4744
|
-
}
|
|
4745
|
-
}, {});
|
|
4746
|
-
}
|
|
4747
|
-
ngOnDestroy() {
|
|
4748
|
-
this.destroyOverlay();
|
|
4749
|
-
}
|
|
4750
|
-
// ============ CVA IMPLEMENTATION ============
|
|
4751
|
-
writeValue(value) {
|
|
4752
|
-
this.internalValue.set(value);
|
|
4753
|
-
}
|
|
4754
|
-
registerOnChange(fn) {
|
|
4755
|
-
this.onChange = fn;
|
|
4756
|
-
}
|
|
4757
|
-
registerOnTouched(fn) {
|
|
4758
|
-
this.onTouched = fn;
|
|
4759
|
-
}
|
|
4760
|
-
setDisabledState(_isDisabled) {
|
|
4761
|
-
// Disabled state is handled via the disabled input
|
|
4762
|
-
}
|
|
4763
|
-
// ============ VALIDATOR IMPLEMENTATION ============
|
|
4764
|
-
validate() {
|
|
4765
|
-
const value = this.internalValue();
|
|
4766
|
-
// Required validation
|
|
4767
|
-
if (this.required() && !value) {
|
|
4768
|
-
return { required: true };
|
|
4769
|
-
}
|
|
4770
|
-
if (value) {
|
|
4771
|
-
// Min validation
|
|
4772
|
-
const min = this.min();
|
|
4773
|
-
if (min && this.dateAdapter.compareDate(value, min) < 0) {
|
|
4774
|
-
return { minDate: { min, actual: value } };
|
|
4775
|
-
}
|
|
4776
|
-
// Max validation
|
|
4777
|
-
const max = this.max();
|
|
4778
|
-
if (max && this.dateAdapter.compareDate(value, max) > 0) {
|
|
4779
|
-
return { maxDate: { max, actual: value } };
|
|
4780
|
-
}
|
|
4781
|
-
// Date filter validation
|
|
4782
|
-
const dateFilter = this.dateFilter();
|
|
4783
|
-
if (dateFilter && !dateFilter(value)) {
|
|
4784
|
-
return { dateFilter: true };
|
|
4785
|
-
}
|
|
4786
|
-
}
|
|
4787
|
-
return null;
|
|
4788
|
-
}
|
|
4789
|
-
registerOnValidatorChange(fn) {
|
|
4790
|
-
this.onValidatorChange = fn;
|
|
4791
|
-
}
|
|
4792
|
-
// ============ PUBLIC METHODS ============
|
|
4793
|
-
/** Opens the datepicker panel. */
|
|
4794
|
-
open() {
|
|
4795
|
-
if (this.disabled() || this.isOpen()) {
|
|
4796
|
-
return;
|
|
4797
|
-
}
|
|
4798
|
-
this.createOverlay();
|
|
4799
|
-
this.isOpen.set(true);
|
|
4800
|
-
this.opened.emit();
|
|
4801
|
-
this.announce('Calendar opened');
|
|
4802
|
-
}
|
|
4803
|
-
/** Closes the datepicker panel. */
|
|
4804
|
-
close() {
|
|
4805
|
-
if (!this.isOpen()) {
|
|
4806
|
-
return;
|
|
4807
|
-
}
|
|
4808
|
-
this.destroyOverlay();
|
|
4809
|
-
this.isOpen.set(false);
|
|
4810
|
-
this.closed.emit();
|
|
4811
|
-
this.onTouched();
|
|
4812
|
-
this.touched.set(true);
|
|
4813
|
-
// Return focus to trigger
|
|
4814
|
-
this.inputRef().nativeElement.focus();
|
|
4815
|
-
}
|
|
4816
|
-
/** Toggles the datepicker panel. */
|
|
4817
|
-
toggle() {
|
|
4818
|
-
if (this.isOpen()) {
|
|
4819
|
-
this.close();
|
|
4820
|
-
}
|
|
4821
|
-
else {
|
|
4822
|
-
this.open();
|
|
4823
|
-
}
|
|
4824
|
-
}
|
|
4825
|
-
/** Clears the selected date. */
|
|
4826
|
-
clear(event) {
|
|
4827
|
-
event?.preventDefault();
|
|
4828
|
-
event?.stopPropagation();
|
|
4829
|
-
this.updateValue(null);
|
|
4830
|
-
this.announce('Date cleared');
|
|
4831
|
-
}
|
|
4832
|
-
/** Selects today's date. */
|
|
4833
|
-
selectToday() {
|
|
4834
|
-
const today = this.dateAdapter.today();
|
|
4835
|
-
this.updateValue(today);
|
|
4836
|
-
if (!this.effectiveKeepOpen()) {
|
|
4837
|
-
this.close();
|
|
4838
|
-
}
|
|
4839
|
-
}
|
|
4840
|
-
// ============ EVENT HANDLERS ============
|
|
4841
|
-
onInputFocus() {
|
|
4842
|
-
this._inputFocused.set(true);
|
|
4843
|
-
}
|
|
4844
|
-
onTriggerClick() {
|
|
4845
|
-
if (!this.disabled()) {
|
|
4846
|
-
this.toggle();
|
|
4847
|
-
}
|
|
4848
|
-
}
|
|
4849
|
-
onTriggerKeydown(event) {
|
|
4850
|
-
switch (event.key) {
|
|
4851
|
-
case 'ArrowDown':
|
|
4852
|
-
case 'ArrowUp':
|
|
4853
|
-
event.preventDefault();
|
|
4854
|
-
this.open();
|
|
4855
|
-
break;
|
|
4856
|
-
case 'Escape':
|
|
4857
|
-
if (this.isOpen()) {
|
|
4858
|
-
event.preventDefault();
|
|
4859
|
-
this.close();
|
|
4860
|
-
}
|
|
4861
|
-
break;
|
|
4862
|
-
}
|
|
4863
|
-
}
|
|
4864
|
-
onInputKeydown(event) {
|
|
4865
|
-
switch (event.key) {
|
|
4866
|
-
case 'Enter':
|
|
4867
|
-
event.preventDefault();
|
|
4868
|
-
if (this.isOpen()) {
|
|
4869
|
-
// Commit manual input
|
|
4870
|
-
this.parseAndSetValue(this.inputRef().nativeElement.value);
|
|
4871
|
-
}
|
|
4872
|
-
else {
|
|
4873
|
-
this.open();
|
|
4874
|
-
}
|
|
4875
|
-
break;
|
|
4876
|
-
case 'Escape':
|
|
4877
|
-
if (this.isOpen()) {
|
|
4878
|
-
event.preventDefault();
|
|
4879
|
-
this.close();
|
|
4880
|
-
}
|
|
4881
|
-
break;
|
|
4882
|
-
case 'ArrowDown':
|
|
4883
|
-
if (!this.isOpen()) {
|
|
4884
|
-
event.preventDefault();
|
|
4885
|
-
this.open();
|
|
4886
|
-
}
|
|
4887
|
-
break;
|
|
4888
|
-
}
|
|
4889
|
-
}
|
|
4890
|
-
onInputChange(event) {
|
|
4891
|
-
if (!this.allowManualInput()) {
|
|
4892
|
-
return;
|
|
4893
|
-
}
|
|
4894
|
-
const input = event.target;
|
|
4895
|
-
// Debounce or wait for blur/enter to actually parse
|
|
4896
|
-
// For now, we just allow typing without immediate parsing
|
|
4897
|
-
}
|
|
4898
|
-
onInputBlur() {
|
|
4899
|
-
this._inputFocused.set(false);
|
|
4900
|
-
if (this.allowManualInput()) {
|
|
4901
|
-
this.parseAndSetValue(this.inputRef().nativeElement.value);
|
|
4902
|
-
}
|
|
4903
|
-
this.onTouched();
|
|
4904
|
-
this.touched.set(true);
|
|
4905
|
-
}
|
|
4906
|
-
onPanelKeydown(event) {
|
|
4907
|
-
switch (event.key) {
|
|
4908
|
-
case 'Escape':
|
|
4909
|
-
event.preventDefault();
|
|
4910
|
-
this.close();
|
|
4911
|
-
break;
|
|
4912
|
-
}
|
|
4913
|
-
}
|
|
4914
|
-
onDateSelected(date) {
|
|
4915
|
-
// Preserve time when selecting a new date if time picker is shown
|
|
4916
|
-
if (this.showTimePicker()) {
|
|
4917
|
-
const currentValue = this.internalValue();
|
|
4918
|
-
if (currentValue) {
|
|
4919
|
-
const withTime = this.dateAdapter.setTime(date, this.dateAdapter.getHours(currentValue), this.dateAdapter.getMinutes(currentValue), this.dateAdapter.getSeconds(currentValue));
|
|
4920
|
-
this.updateValue(withTime);
|
|
4921
|
-
}
|
|
4922
|
-
else {
|
|
4923
|
-
this.updateValue(date);
|
|
4924
|
-
}
|
|
4925
|
-
}
|
|
4926
|
-
else {
|
|
4927
|
-
this.updateValue(date);
|
|
4928
|
-
}
|
|
4929
|
-
if (!this.effectiveKeepOpen()) {
|
|
4930
|
-
this.close();
|
|
4931
|
-
}
|
|
4932
|
-
this.announce(`Selected ${this.dateAdapter.format(date, 'long')}`);
|
|
4933
|
-
}
|
|
4934
|
-
onTimeChange(time) {
|
|
4935
|
-
if (!time)
|
|
4936
|
-
return;
|
|
4937
|
-
const current = this.internalValue() ?? this.dateAdapter.today();
|
|
4938
|
-
const updated = this.dateAdapter.setTime(current, time.hours, time.minutes, time.seconds);
|
|
4939
|
-
this.updateValue(updated);
|
|
4940
|
-
}
|
|
4941
|
-
onActiveDateChange(date) {
|
|
4942
|
-
this.calendarActiveDate.set(date);
|
|
4943
|
-
}
|
|
4944
|
-
// ============ FormFieldControl IMPLEMENTATION ============
|
|
4945
|
-
/**
|
|
4946
|
-
* Called when the form field container is clicked.
|
|
4947
|
-
* Implements FormFieldControl.
|
|
4948
|
-
*/
|
|
4949
|
-
onContainerClick(event) {
|
|
4950
|
-
const target = event.target;
|
|
4951
|
-
if (!this.disabled() && !this.triggerRef().nativeElement.contains(target)) {
|
|
4952
|
-
this.toggle();
|
|
4953
|
-
}
|
|
4954
|
-
}
|
|
4955
|
-
/**
|
|
4956
|
-
* Sets the describedBy IDs from the form field.
|
|
4957
|
-
* Called by the parent form field component.
|
|
4958
|
-
*/
|
|
4959
|
-
setDescribedByIds(ids) {
|
|
4960
|
-
this._describedByIds.set(ids);
|
|
4961
|
-
}
|
|
4962
|
-
/**
|
|
4963
|
-
* Sets the appearance for styling.
|
|
4964
|
-
* Called by the parent form field component.
|
|
4965
|
-
*/
|
|
4966
|
-
setAppearance(appearance) {
|
|
4967
|
-
this._appearance.set(appearance);
|
|
4968
|
-
}
|
|
4969
|
-
// ============ PRIVATE METHODS ============
|
|
4970
|
-
createOverlay() {
|
|
4971
|
-
if (this.overlayRef) {
|
|
4972
|
-
return;
|
|
4973
|
-
}
|
|
4974
|
-
const hostEl = this.elementRef.nativeElement;
|
|
4975
|
-
const positionStrategy = this.overlay
|
|
4976
|
-
.position()
|
|
4977
|
-
.flexibleConnectedTo(hostEl)
|
|
4978
|
-
.withPositions(DEFAULT_POSITIONS$1)
|
|
4979
|
-
.withFlexibleDimensions(false)
|
|
4980
|
-
.withPush(true);
|
|
4981
|
-
this.overlayRef = this.overlay.create({
|
|
4982
|
-
positionStrategy,
|
|
4983
|
-
scrollStrategy: this.overlay.scrollStrategies.reposition(),
|
|
4984
|
-
hasBackdrop: true,
|
|
4985
|
-
backdropClass: 'cdk-overlay-transparent-backdrop',
|
|
4986
|
-
});
|
|
4987
|
-
// Attach panel template
|
|
4988
|
-
const portal = new TemplatePortal(this.panelTemplateRef(), this.viewContainerRef);
|
|
4989
|
-
this.overlayRef.attach(portal);
|
|
4990
|
-
// Close on backdrop click
|
|
4991
|
-
this.overlayRef
|
|
4992
|
-
.backdropClick()
|
|
4993
|
-
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
4994
|
-
.subscribe(() => this.close());
|
|
4995
|
-
// Close on outside click
|
|
4996
|
-
this.overlayRef
|
|
4997
|
-
.outsidePointerEvents()
|
|
4998
|
-
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
4999
|
-
.subscribe(() => this.close());
|
|
5000
|
-
}
|
|
5001
|
-
destroyOverlay() {
|
|
5002
|
-
if (this.overlayRef) {
|
|
5003
|
-
this.overlayRef.dispose();
|
|
5004
|
-
this.overlayRef = null;
|
|
5005
|
-
}
|
|
5006
|
-
}
|
|
5007
|
-
updateValue(value) {
|
|
5008
|
-
this.value.set(value);
|
|
5009
|
-
this.onChange(value);
|
|
5010
|
-
this.dateChange.emit(value);
|
|
5011
|
-
this.onValidatorChange();
|
|
5012
|
-
}
|
|
5013
|
-
parseAndSetValue(inputValue) {
|
|
5014
|
-
if (!inputValue.trim()) {
|
|
5015
|
-
// Empty input - clear if allowed
|
|
5016
|
-
if (this.hasValue()) {
|
|
5017
|
-
this.updateValue(null);
|
|
5018
|
-
}
|
|
5019
|
-
return;
|
|
5020
|
-
}
|
|
5021
|
-
const parsed = this.dateAdapter.parse(inputValue, this.effectiveDateFormat());
|
|
5022
|
-
if (parsed && this.dateAdapter.isValid(parsed)) {
|
|
5023
|
-
// Validate against min/max/filter
|
|
5024
|
-
if (this.isDateValid(parsed)) {
|
|
5025
|
-
this.updateValue(parsed);
|
|
5026
|
-
}
|
|
5027
|
-
else {
|
|
5028
|
-
// Invalid date - revert to current value
|
|
5029
|
-
this.inputRef().nativeElement.value = this.displayValue();
|
|
5030
|
-
}
|
|
5031
|
-
}
|
|
5032
|
-
else {
|
|
5033
|
-
// Parse failed - revert to current value
|
|
5034
|
-
this.inputRef().nativeElement.value = this.displayValue();
|
|
5035
|
-
}
|
|
5036
|
-
}
|
|
5037
|
-
isDateValid(date) {
|
|
5038
|
-
const min = this.min();
|
|
5039
|
-
const max = this.max();
|
|
5040
|
-
const filter = this.dateFilter();
|
|
5041
|
-
if (min && this.dateAdapter.compareDate(date, min) < 0) {
|
|
5042
|
-
return false;
|
|
5043
|
-
}
|
|
5044
|
-
if (max && this.dateAdapter.compareDate(date, max) > 0) {
|
|
5045
|
-
return false;
|
|
5046
|
-
}
|
|
5047
|
-
if (filter && !filter(date)) {
|
|
5048
|
-
return false;
|
|
5049
|
-
}
|
|
5050
|
-
return true;
|
|
5051
|
-
}
|
|
5052
|
-
announce(message) {
|
|
5053
|
-
this.liveAnnouncement.set(message);
|
|
5054
|
-
}
|
|
5055
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: ComDatepicker, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5056
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: ComDatepicker, isStandalone: true, selector: "com-datepicker", inputs: { value: { classPropertyName: "value", publicName: "value", 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 }, dateFilter: { classPropertyName: "dateFilter", publicName: "dateFilter", isSignal: true, isRequired: false, transformFunction: null }, startAt: { classPropertyName: "startAt", publicName: "startAt", isSignal: true, isRequired: false, transformFunction: null }, startView: { classPropertyName: "startView", publicName: "startView", isSignal: true, isRequired: false, transformFunction: null }, firstDayOfWeek: { classPropertyName: "firstDayOfWeek", publicName: "firstDayOfWeek", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, dateFormat: { classPropertyName: "dateFormat", publicName: "dateFormat", isSignal: true, isRequired: false, transformFunction: null }, showClearButton: { classPropertyName: "showClearButton", publicName: "showClearButton", isSignal: true, isRequired: false, transformFunction: null }, showTodayButton: { classPropertyName: "showTodayButton", publicName: "showTodayButton", isSignal: true, isRequired: false, transformFunction: null }, showFooterClearButton: { classPropertyName: "showFooterClearButton", publicName: "showFooterClearButton", isSignal: true, isRequired: false, transformFunction: null }, keepOpen: { classPropertyName: "keepOpen", publicName: "keepOpen", isSignal: true, isRequired: false, transformFunction: null }, allowManualInput: { classPropertyName: "allowManualInput", publicName: "allowManualInput", isSignal: true, isRequired: false, transformFunction: null }, panelClass: { classPropertyName: "panelClass", publicName: "panelClass", isSignal: true, isRequired: false, transformFunction: null }, panelWidth: { classPropertyName: "panelWidth", publicName: "panelWidth", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, state: { classPropertyName: "state", publicName: "state", isSignal: true, isRequired: false, transformFunction: null }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, ariaDescribedBy: { classPropertyName: "ariaDescribedBy", publicName: "ariaDescribedBy", isSignal: true, isRequired: false, transformFunction: null }, errorStateMatcher: { classPropertyName: "errorStateMatcher", publicName: "errorStateMatcher", isSignal: true, isRequired: false, transformFunction: null }, touched: { classPropertyName: "touched", publicName: "touched", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, sfErrors: { classPropertyName: "sfErrors", publicName: "errors", isSignal: true, isRequired: false, transformFunction: null }, showTimePicker: { classPropertyName: "showTimePicker", publicName: "showTimePicker", isSignal: true, isRequired: false, transformFunction: null }, use12HourFormat: { classPropertyName: "use12HourFormat", publicName: "use12HourFormat", 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 } }, outputs: { value: "valueChange", touched: "touchedChange", dateChange: "dateChange", opened: "opened", closed: "closed" }, host: { properties: { "class.com-datepicker-disabled": "disabled()", "class.com-datepicker-open": "isOpen()" }, classAttribute: "com-datepicker-host inline-block" }, providers: [
|
|
5057
|
-
SingleSelectionStrategy,
|
|
5058
|
-
{ provide: CALENDAR_SELECTION_STRATEGY, useExisting: SingleSelectionStrategy },
|
|
5059
|
-
{ provide: FormFieldControl, useExisting: forwardRef(() => ComDatepicker) },
|
|
5060
|
-
], viewQueries: [{ propertyName: "triggerRef", first: true, predicate: ["triggerElement"], descendants: true, isSignal: true }, { propertyName: "inputRef", first: true, predicate: ["inputElement"], descendants: true, isSignal: true }, { propertyName: "panelTemplateRef", first: true, predicate: ["panelTemplate"], descendants: true, isSignal: true }], exportAs: ["comDatepicker"], ngImport: i0, template: `
|
|
5061
|
-
<!-- Trigger container -->
|
|
5062
|
-
<div
|
|
5063
|
-
#triggerElement
|
|
5064
|
-
[class]="triggerClasses()"
|
|
5065
|
-
[attr.aria-expanded]="isOpen()"
|
|
5066
|
-
[attr.aria-haspopup]="'dialog'"
|
|
5067
|
-
[attr.aria-owns]="panelId()"
|
|
5068
|
-
[attr.aria-disabled]="disabled() || null"
|
|
5069
|
-
(click)="onTriggerClick()"
|
|
5070
|
-
(keydown)="onTriggerKeydown($event)"
|
|
5071
|
-
>
|
|
5072
|
-
<!-- Date input display -->
|
|
5073
|
-
<input
|
|
5074
|
-
#inputElement
|
|
5075
|
-
type="text"
|
|
5076
|
-
[class]="inputClasses()"
|
|
5077
|
-
[value]="displayValue()"
|
|
5078
|
-
[placeholder]="placeholder()"
|
|
5079
|
-
[disabled]="disabled()"
|
|
5080
|
-
[readonly]="!allowManualInput()"
|
|
5081
|
-
[attr.id]="inputId()"
|
|
5082
|
-
[attr.aria-label]="ariaLabel() || placeholder()"
|
|
5083
|
-
[attr.aria-describedby]="effectiveAriaDescribedBy() || null"
|
|
5084
|
-
[attr.aria-invalid]="effectiveState() === 'error' || null"
|
|
5085
|
-
[attr.aria-required]="required() || null"
|
|
5086
|
-
(focus)="onInputFocus()"
|
|
5087
|
-
(input)="onInputChange($event)"
|
|
5088
|
-
(blur)="onInputBlur()"
|
|
5089
|
-
(keydown)="onInputKeydown($event)"
|
|
5090
|
-
/>
|
|
5091
|
-
|
|
5092
|
-
<!-- Clear button -->
|
|
5093
|
-
@if (showClearButton() && hasValue() && !disabled()) {
|
|
5094
|
-
<button
|
|
5095
|
-
type="button"
|
|
5096
|
-
[class]="clearClasses()"
|
|
5097
|
-
[attr.aria-label]="'Clear date'"
|
|
5098
|
-
(click)="clear($event)"
|
|
5099
|
-
>
|
|
5100
|
-
<com-icon name="x" [size]="iconSize()" />
|
|
5101
|
-
</button>
|
|
5102
|
-
}
|
|
5103
|
-
|
|
5104
|
-
<!-- Calendar icon -->
|
|
5105
|
-
<button
|
|
5106
|
-
type="button"
|
|
5107
|
-
[class]="iconClasses()"
|
|
5108
|
-
[attr.aria-label]="isOpen() ? 'Close calendar' : 'Open calendar'"
|
|
5109
|
-
[disabled]="disabled()"
|
|
5110
|
-
tabindex="-1"
|
|
5111
|
-
>
|
|
5112
|
-
<com-icon name="calendar" [size]="iconSize()" />
|
|
5113
|
-
</button>
|
|
5114
|
-
</div>
|
|
5115
|
-
|
|
5116
|
-
<!-- Panel template (rendered in overlay) -->
|
|
5117
|
-
<ng-template #panelTemplate>
|
|
5118
|
-
<div
|
|
5119
|
-
[class]="panelClasses()"
|
|
5120
|
-
[attr.id]="panelId()"
|
|
5121
|
-
role="dialog"
|
|
5122
|
-
aria-modal="true"
|
|
5123
|
-
[attr.aria-label]="'Choose date'"
|
|
5124
|
-
(keydown)="onPanelKeydown($event)"
|
|
5125
|
-
cdkTrapFocus
|
|
5126
|
-
[cdkTrapFocusAutoCapture]="true"
|
|
5127
|
-
>
|
|
5128
|
-
<com-calendar
|
|
5129
|
-
[activeDate]="calendarActiveDate()"
|
|
5130
|
-
[selected]="internalValue()"
|
|
5131
|
-
[minDate]="min()"
|
|
5132
|
-
[maxDate]="max()"
|
|
5133
|
-
[dateFilter]="dateFilter()"
|
|
5134
|
-
[startView]="startView()"
|
|
5135
|
-
[firstDayOfWeek]="firstDayOfWeek()"
|
|
5136
|
-
[bordered]="false"
|
|
5137
|
-
(selectedChange)="onDateSelected($event)"
|
|
5138
|
-
(activeDateChange)="onActiveDateChange($event)"
|
|
5139
|
-
/>
|
|
5140
|
-
|
|
5141
|
-
@if (showTimePicker()) {
|
|
5142
|
-
<div [class]="timeSectionClasses()">
|
|
5143
|
-
<com-time-picker
|
|
5144
|
-
variant="embedded"
|
|
5145
|
-
[size]="size()"
|
|
5146
|
-
[value]="timeValue()"
|
|
5147
|
-
[use12HourFormat]="use12HourFormat()"
|
|
5148
|
-
[showSeconds]="showSeconds()"
|
|
5149
|
-
[minuteStep]="minuteStep()"
|
|
5150
|
-
[disabled]="disabled()"
|
|
5151
|
-
(timeChange)="onTimeChange($event)"
|
|
5152
|
-
/>
|
|
5153
|
-
</div>
|
|
5154
|
-
}
|
|
5155
|
-
|
|
5156
|
-
@if (showTodayButton() || showFooterClearButton() || showTimePicker()) {
|
|
5157
|
-
<div [class]="footerClasses()">
|
|
5158
|
-
@if (showTodayButton()) {
|
|
5159
|
-
<button
|
|
5160
|
-
type="button"
|
|
5161
|
-
[class]="todayButtonClasses()"
|
|
5162
|
-
(click)="selectToday()"
|
|
5163
|
-
>
|
|
5164
|
-
Today
|
|
5165
|
-
</button>
|
|
5166
|
-
}
|
|
5167
|
-
@if (showFooterClearButton()) {
|
|
5168
|
-
<button
|
|
5169
|
-
type="button"
|
|
5170
|
-
[class]="clearButtonClasses()"
|
|
5171
|
-
(click)="clear($event)"
|
|
5172
|
-
>
|
|
5173
|
-
Clear
|
|
5174
|
-
</button>
|
|
5175
|
-
}
|
|
5176
|
-
@if (showTimePicker()) {
|
|
5177
|
-
<button
|
|
5178
|
-
type="button"
|
|
5179
|
-
[class]="todayButtonClasses()"
|
|
5180
|
-
(click)="close()"
|
|
5181
|
-
>
|
|
5182
|
-
Done
|
|
5183
|
-
</button>
|
|
5184
|
-
}
|
|
5185
|
-
</div>
|
|
5186
|
-
}
|
|
5187
|
-
</div>
|
|
5188
|
-
</ng-template>
|
|
5189
|
-
|
|
5190
|
-
<!-- Live announcer region -->
|
|
5191
|
-
<div class="sr-only" aria-live="polite" aria-atomic="true">
|
|
5192
|
-
{{ liveAnnouncement() }}
|
|
5193
|
-
</div>
|
|
5194
|
-
`, isInline: true, styles: [".sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n"], dependencies: [{ kind: "ngmodule", type: OverlayModule }, { kind: "ngmodule", type: A11yModule }, { kind: "directive", type: i1.CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "component", type: ComCalendar, selector: "com-calendar", inputs: ["activeDate", "selected", "minDate", "maxDate", "dateFilter", "dateClass", "bordered", "startView", "firstDayOfWeek", "monthColumns", "cellTemplate"], outputs: ["selectedChange", "viewChanged", "activeDateChange"] }, { kind: "component", type: ComIcon, selector: "com-icon", inputs: ["name", "img", "color", "size", "strokeWidth", "absoluteStrokeWidth", "ariaLabel"] }, { kind: "component", type: ComTimePicker, selector: "com-time-picker", inputs: ["value", "disabled", "required", "showSeconds", "use12HourFormat", "minuteStep", "secondStep", "minTime", "maxTime", "variant", "size", "state", "ariaLabel", "class", "placeholder", "errorStateMatcher", "touched", "invalid", "errors"], outputs: ["valueChange", "disabledChange", "touchedChange", "timeChange"], exportAs: ["comTimePicker"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
5195
|
-
}
|
|
5196
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: ComDatepicker, decorators: [{
|
|
5197
|
-
type: Component,
|
|
5198
|
-
args: [{ selector: 'com-datepicker', exportAs: 'comDatepicker', template: `
|
|
5199
|
-
<!-- Trigger container -->
|
|
5200
|
-
<div
|
|
5201
|
-
#triggerElement
|
|
5202
|
-
[class]="triggerClasses()"
|
|
5203
|
-
[attr.aria-expanded]="isOpen()"
|
|
5204
|
-
[attr.aria-haspopup]="'dialog'"
|
|
5205
|
-
[attr.aria-owns]="panelId()"
|
|
5206
|
-
[attr.aria-disabled]="disabled() || null"
|
|
5207
|
-
(click)="onTriggerClick()"
|
|
5208
|
-
(keydown)="onTriggerKeydown($event)"
|
|
5209
|
-
>
|
|
5210
|
-
<!-- Date input display -->
|
|
5211
|
-
<input
|
|
5212
|
-
#inputElement
|
|
5213
|
-
type="text"
|
|
5214
|
-
[class]="inputClasses()"
|
|
5215
|
-
[value]="displayValue()"
|
|
5216
|
-
[placeholder]="placeholder()"
|
|
5217
|
-
[disabled]="disabled()"
|
|
5218
|
-
[readonly]="!allowManualInput()"
|
|
5219
|
-
[attr.id]="inputId()"
|
|
5220
|
-
[attr.aria-label]="ariaLabel() || placeholder()"
|
|
5221
|
-
[attr.aria-describedby]="effectiveAriaDescribedBy() || null"
|
|
5222
|
-
[attr.aria-invalid]="effectiveState() === 'error' || null"
|
|
5223
|
-
[attr.aria-required]="required() || null"
|
|
5224
|
-
(focus)="onInputFocus()"
|
|
5225
|
-
(input)="onInputChange($event)"
|
|
5226
|
-
(blur)="onInputBlur()"
|
|
5227
|
-
(keydown)="onInputKeydown($event)"
|
|
5228
|
-
/>
|
|
5229
|
-
|
|
5230
|
-
<!-- Clear button -->
|
|
5231
|
-
@if (showClearButton() && hasValue() && !disabled()) {
|
|
5232
|
-
<button
|
|
5233
|
-
type="button"
|
|
5234
|
-
[class]="clearClasses()"
|
|
5235
|
-
[attr.aria-label]="'Clear date'"
|
|
5236
|
-
(click)="clear($event)"
|
|
5237
|
-
>
|
|
5238
|
-
<com-icon name="x" [size]="iconSize()" />
|
|
5239
|
-
</button>
|
|
5240
|
-
}
|
|
5241
|
-
|
|
5242
|
-
<!-- Calendar icon -->
|
|
5243
|
-
<button
|
|
5244
|
-
type="button"
|
|
5245
|
-
[class]="iconClasses()"
|
|
5246
|
-
[attr.aria-label]="isOpen() ? 'Close calendar' : 'Open calendar'"
|
|
5247
|
-
[disabled]="disabled()"
|
|
5248
|
-
tabindex="-1"
|
|
5249
|
-
>
|
|
5250
|
-
<com-icon name="calendar" [size]="iconSize()" />
|
|
5251
|
-
</button>
|
|
5252
|
-
</div>
|
|
5253
|
-
|
|
5254
|
-
<!-- Panel template (rendered in overlay) -->
|
|
5255
|
-
<ng-template #panelTemplate>
|
|
5256
|
-
<div
|
|
5257
|
-
[class]="panelClasses()"
|
|
5258
|
-
[attr.id]="panelId()"
|
|
5259
|
-
role="dialog"
|
|
5260
|
-
aria-modal="true"
|
|
5261
|
-
[attr.aria-label]="'Choose date'"
|
|
5262
|
-
(keydown)="onPanelKeydown($event)"
|
|
5263
|
-
cdkTrapFocus
|
|
5264
|
-
[cdkTrapFocusAutoCapture]="true"
|
|
5265
|
-
>
|
|
5266
|
-
<com-calendar
|
|
5267
|
-
[activeDate]="calendarActiveDate()"
|
|
5268
|
-
[selected]="internalValue()"
|
|
5269
|
-
[minDate]="min()"
|
|
5270
|
-
[maxDate]="max()"
|
|
5271
|
-
[dateFilter]="dateFilter()"
|
|
5272
|
-
[startView]="startView()"
|
|
5273
|
-
[firstDayOfWeek]="firstDayOfWeek()"
|
|
5274
|
-
[bordered]="false"
|
|
5275
|
-
(selectedChange)="onDateSelected($event)"
|
|
5276
|
-
(activeDateChange)="onActiveDateChange($event)"
|
|
5277
|
-
/>
|
|
5278
|
-
|
|
5279
|
-
@if (showTimePicker()) {
|
|
5280
|
-
<div [class]="timeSectionClasses()">
|
|
5281
|
-
<com-time-picker
|
|
5282
|
-
variant="embedded"
|
|
5283
|
-
[size]="size()"
|
|
5284
|
-
[value]="timeValue()"
|
|
5285
|
-
[use12HourFormat]="use12HourFormat()"
|
|
5286
|
-
[showSeconds]="showSeconds()"
|
|
5287
|
-
[minuteStep]="minuteStep()"
|
|
5288
|
-
[disabled]="disabled()"
|
|
5289
|
-
(timeChange)="onTimeChange($event)"
|
|
5290
|
-
/>
|
|
5291
|
-
</div>
|
|
5292
|
-
}
|
|
5293
|
-
|
|
5294
|
-
@if (showTodayButton() || showFooterClearButton() || showTimePicker()) {
|
|
5295
|
-
<div [class]="footerClasses()">
|
|
5296
|
-
@if (showTodayButton()) {
|
|
5297
|
-
<button
|
|
5298
|
-
type="button"
|
|
5299
|
-
[class]="todayButtonClasses()"
|
|
5300
|
-
(click)="selectToday()"
|
|
5301
|
-
>
|
|
5302
|
-
Today
|
|
5303
|
-
</button>
|
|
5304
|
-
}
|
|
5305
|
-
@if (showFooterClearButton()) {
|
|
5306
|
-
<button
|
|
5307
|
-
type="button"
|
|
5308
|
-
[class]="clearButtonClasses()"
|
|
5309
|
-
(click)="clear($event)"
|
|
5310
|
-
>
|
|
5311
|
-
Clear
|
|
5312
|
-
</button>
|
|
5313
|
-
}
|
|
5314
|
-
@if (showTimePicker()) {
|
|
5315
|
-
<button
|
|
5316
|
-
type="button"
|
|
5317
|
-
[class]="todayButtonClasses()"
|
|
5318
|
-
(click)="close()"
|
|
5319
|
-
>
|
|
5320
|
-
Done
|
|
5321
|
-
</button>
|
|
5322
|
-
}
|
|
5323
|
-
</div>
|
|
5324
|
-
}
|
|
5325
|
-
</div>
|
|
5326
|
-
</ng-template>
|
|
5327
|
-
|
|
5328
|
-
<!-- Live announcer region -->
|
|
5329
|
-
<div class="sr-only" aria-live="polite" aria-atomic="true">
|
|
5330
|
-
{{ liveAnnouncement() }}
|
|
5331
|
-
</div>
|
|
5332
|
-
`, imports: [
|
|
5333
|
-
OverlayModule,
|
|
5334
|
-
A11yModule,
|
|
5335
|
-
ComCalendar,
|
|
5336
|
-
ComIcon,
|
|
5337
|
-
ComTimePicker,
|
|
5338
|
-
], providers: [
|
|
5339
|
-
SingleSelectionStrategy,
|
|
5340
|
-
{ provide: CALENDAR_SELECTION_STRATEGY, useExisting: SingleSelectionStrategy },
|
|
5341
|
-
{ provide: FormFieldControl, useExisting: forwardRef(() => ComDatepicker) },
|
|
5342
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
5343
|
-
class: 'com-datepicker-host inline-block',
|
|
5344
|
-
'[class.com-datepicker-disabled]': 'disabled()',
|
|
5345
|
-
'[class.com-datepicker-open]': 'isOpen()',
|
|
5346
|
-
}, styles: [".sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n"] }]
|
|
5347
|
-
}], ctorParameters: () => [], propDecorators: { triggerRef: [{ type: i0.ViewChild, args: ['triggerElement', { isSignal: true }] }], inputRef: [{ type: i0.ViewChild, args: ['inputElement', { isSignal: true }] }], panelTemplateRef: [{ type: i0.ViewChild, args: ['panelTemplate', { isSignal: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], dateFilter: [{ type: i0.Input, args: [{ isSignal: true, alias: "dateFilter", required: false }] }], startAt: [{ type: i0.Input, args: [{ isSignal: true, alias: "startAt", required: false }] }], startView: [{ type: i0.Input, args: [{ isSignal: true, alias: "startView", required: false }] }], firstDayOfWeek: [{ type: i0.Input, args: [{ isSignal: true, alias: "firstDayOfWeek", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], dateFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "dateFormat", required: false }] }], showClearButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "showClearButton", required: false }] }], showTodayButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "showTodayButton", required: false }] }], showFooterClearButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "showFooterClearButton", required: false }] }], keepOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "keepOpen", required: false }] }], allowManualInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowManualInput", required: false }] }], panelClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "panelClass", required: false }] }], panelWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "panelWidth", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], state: [{ type: i0.Input, args: [{ isSignal: true, alias: "state", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], ariaDescribedBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaDescribedBy", required: false }] }], errorStateMatcher: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorStateMatcher", required: false }] }], touched: [{ type: i0.Input, args: [{ isSignal: true, alias: "touched", required: false }] }, { type: i0.Output, args: ["touchedChange"] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], sfErrors: [{ type: i0.Input, args: [{ isSignal: true, alias: "errors", required: false }] }], showTimePicker: [{ type: i0.Input, args: [{ isSignal: true, alias: "showTimePicker", required: false }] }], use12HourFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "use12HourFormat", required: false }] }], showSeconds: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSeconds", required: false }] }], minuteStep: [{ type: i0.Input, args: [{ isSignal: true, alias: "minuteStep", required: false }] }], dateChange: [{ type: i0.Output, args: ["dateChange"] }], opened: [{ type: i0.Output, args: ["opened"] }], closed: [{ type: i0.Output, args: ["closed"] }] } });
|
|
5348
|
-
|
|
5349
|
-
/** Default position for the datepicker panel. */
|
|
5350
|
-
const DEFAULT_POSITIONS = [
|
|
5351
|
-
// Below trigger, aligned start
|
|
5352
|
-
{ originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4 },
|
|
5353
|
-
// Above trigger, aligned start
|
|
5354
|
-
{ originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'bottom', offsetY: -4 },
|
|
5355
|
-
// Below trigger, aligned end
|
|
5356
|
-
{ originX: 'end', originY: 'bottom', overlayX: 'end', overlayY: 'top', offsetY: 4 },
|
|
5357
|
-
// Above trigger, aligned end
|
|
5358
|
-
{ originX: 'end', originY: 'top', overlayX: 'end', overlayY: 'bottom', offsetY: -4 },
|
|
5359
|
-
];
|
|
5360
|
-
/**
|
|
5361
|
-
* Date range picker component with calendar popup.
|
|
5362
|
-
* Allows selecting a start and end date via a two-click interaction.
|
|
5363
|
-
* Implements ControlValueAccessor for Reactive Forms and Template-driven Forms.
|
|
5364
|
-
*
|
|
5365
|
-
* @tokens `--color-input-background`, `--color-input-foreground`, `--color-input-border`,
|
|
5366
|
-
* `--color-input-placeholder`, `--color-ring`, `--color-muted`, `--color-muted-foreground`,
|
|
5367
|
-
* `--color-popover`, `--color-popover-foreground`, `--color-border-subtle`,
|
|
5368
|
-
* `--color-primary`, `--color-primary-foreground`, `--color-primary-hover`,
|
|
5369
|
-
* `--color-warn`, `--color-success`, `--color-disabled`, `--color-disabled-foreground`
|
|
5370
|
-
*
|
|
5371
|
-
* @example
|
|
5372
|
-
* ```html
|
|
5373
|
-
* <com-date-range-picker
|
|
5374
|
-
* formControlName="dateRange"
|
|
5375
|
-
* startPlaceholder="Start date"
|
|
5376
|
-
* endPlaceholder="End date"
|
|
5377
|
-
* [min]="minDate"
|
|
5378
|
-
* [max]="maxDate"
|
|
5379
|
-
* [showTodayButton]="true"
|
|
5380
|
-
* />
|
|
5381
|
-
* ```
|
|
5382
|
-
*/
|
|
5383
|
-
class ComDateRangePicker {
|
|
5384
|
-
elementRef = inject(ElementRef);
|
|
5385
|
-
destroyRef = inject(DestroyRef);
|
|
5386
|
-
overlay = inject(Overlay);
|
|
5387
|
-
viewContainerRef = inject(ViewContainerRef);
|
|
5388
|
-
document = inject(DOCUMENT);
|
|
5389
|
-
dateAdapter = inject(DATE_ADAPTER);
|
|
5390
|
-
/** NgControl bound to this date range picker (if using reactive forms). */
|
|
5391
|
-
ngControl = inject(NgControl, { optional: true, self: true });
|
|
5392
|
-
defaultErrorStateMatcher = inject(ErrorStateMatcher);
|
|
5393
|
-
parentForm = inject(NgForm, { optional: true });
|
|
5394
|
-
parentFormGroup = inject(FormGroupDirective, { optional: true });
|
|
5395
|
-
/** Reference to the trigger element. */
|
|
5396
|
-
triggerRef = viewChild.required('triggerElement');
|
|
5397
|
-
/** Reference to the start input element. */
|
|
5398
|
-
startInputRef = viewChild.required('startInputElement');
|
|
5399
|
-
/** Reference to the end input element. */
|
|
5400
|
-
endInputRef = viewChild.required('endInputElement');
|
|
5401
|
-
/** Reference to the panel template. */
|
|
5402
|
-
panelTemplateRef = viewChild.required('panelTemplate');
|
|
5403
|
-
/** Overlay reference. */
|
|
5404
|
-
overlayRef = null;
|
|
5405
|
-
/** Unique ID for the datepicker. */
|
|
5406
|
-
datepickerId = generateDatepickerId();
|
|
5407
|
-
// ============ INPUTS ============
|
|
5408
|
-
/** Current value. */
|
|
5409
|
-
value = model(null, ...(ngDevMode ? [{ debugName: "value" }] : []));
|
|
5410
|
-
/** Minimum selectable date. */
|
|
5411
|
-
min = input(null, ...(ngDevMode ? [{ debugName: "min" }] : []));
|
|
5412
|
-
/** Maximum selectable date. */
|
|
5413
|
-
max = input(null, ...(ngDevMode ? [{ debugName: "max" }] : []));
|
|
5414
|
-
/** Custom filter function to disable specific dates. */
|
|
5415
|
-
dateFilter = input(null, ...(ngDevMode ? [{ debugName: "dateFilter" }] : []));
|
|
5416
|
-
/** Date the calendar opens to (defaults to start date or today). */
|
|
5417
|
-
startAt = input(null, ...(ngDevMode ? [{ debugName: "startAt" }] : []));
|
|
5418
|
-
/** Initial calendar view. */
|
|
5419
|
-
startView = input('month', ...(ngDevMode ? [{ debugName: "startView" }] : []));
|
|
5420
|
-
/** First day of week override (0=Sun, 1=Mon, ..., 6=Sat). */
|
|
5421
|
-
firstDayOfWeek = input(null, ...(ngDevMode ? [{ debugName: "firstDayOfWeek" }] : []));
|
|
5422
|
-
/** Placeholder text for start date. */
|
|
5423
|
-
startPlaceholder = input('Start date', ...(ngDevMode ? [{ debugName: "startPlaceholder" }] : []));
|
|
5424
|
-
/** Placeholder text for end date. */
|
|
5425
|
-
endPlaceholder = input('End date', ...(ngDevMode ? [{ debugName: "endPlaceholder" }] : []));
|
|
5426
|
-
/** Whether the datepicker is disabled. */
|
|
5427
|
-
disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
|
|
5428
|
-
/** Whether the datepicker is required. */
|
|
5429
|
-
required = input(false, ...(ngDevMode ? [{ debugName: "required" }] : []));
|
|
5430
|
-
/** Display format for the date. */
|
|
5431
|
-
dateFormat = input('medium', ...(ngDevMode ? [{ debugName: "dateFormat" }] : []));
|
|
5432
|
-
/** Show a clear button in the trigger. */
|
|
5433
|
-
showClearButton = input(false, ...(ngDevMode ? [{ debugName: "showClearButton" }] : []));
|
|
5434
|
-
/** Show a today button in the footer. */
|
|
5435
|
-
showTodayButton = input(false, ...(ngDevMode ? [{ debugName: "showTodayButton" }] : []));
|
|
5436
|
-
/** Show a clear button in the footer. */
|
|
5437
|
-
showFooterClearButton = input(false, ...(ngDevMode ? [{ debugName: "showFooterClearButton" }] : []));
|
|
5438
|
-
/** Don't auto-close on complete range selection. */
|
|
5439
|
-
keepOpen = input(false, ...(ngDevMode ? [{ debugName: "keepOpen" }] : []));
|
|
5440
|
-
/** Allow manual text input. */
|
|
5441
|
-
allowManualInput = input(true, ...(ngDevMode ? [{ debugName: "allowManualInput" }] : []));
|
|
5442
|
-
/** Additional CSS classes for the panel. */
|
|
5443
|
-
panelClass = input('', ...(ngDevMode ? [{ debugName: "panelClass" }] : []));
|
|
5444
|
-
/** Panel width strategy. */
|
|
5445
|
-
panelWidth = input('auto', ...(ngDevMode ? [{ debugName: "panelWidth" }] : []));
|
|
5446
|
-
/** CVA variant for trigger styling. */
|
|
5447
|
-
variant = input('default', ...(ngDevMode ? [{ debugName: "variant" }] : []));
|
|
5448
|
-
/** Size variant. */
|
|
5449
|
-
size = input('default', ...(ngDevMode ? [{ debugName: "size" }] : []));
|
|
5450
|
-
/** Validation state. */
|
|
5451
|
-
state = input('default', ...(ngDevMode ? [{ debugName: "state" }] : []));
|
|
5452
|
-
/** Additional CSS classes for the trigger. */
|
|
5453
|
-
userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : {}), alias: 'class' });
|
|
5454
|
-
/** Accessible label for the start input. */
|
|
5455
|
-
startAriaLabel = input(null, ...(ngDevMode ? [{ debugName: "startAriaLabel" }] : []));
|
|
5456
|
-
/** Accessible label for the end input. */
|
|
5457
|
-
endAriaLabel = input(null, ...(ngDevMode ? [{ debugName: "endAriaLabel" }] : []));
|
|
5458
|
-
/** Custom error state matcher for determining when to show errors. */
|
|
5459
|
-
errorStateMatcher = input(...(ngDevMode ? [undefined, { debugName: "errorStateMatcher" }] : []));
|
|
5460
|
-
// Signal Forms inputs — set automatically by [formField] via setInputOnDirectives
|
|
5461
|
-
touched = model(false, ...(ngDevMode ? [{ debugName: "touched" }] : []));
|
|
5462
|
-
invalid = input(false, ...(ngDevMode ? [{ debugName: "invalid" }] : []));
|
|
5463
|
-
sfErrors = input([], { ...(ngDevMode ? { debugName: "sfErrors" } : {}), alias: 'errors' });
|
|
5464
|
-
/** Whether to show time pickers below the calendar. */
|
|
5465
|
-
showTimePicker = input(false, ...(ngDevMode ? [{ debugName: "showTimePicker" }] : []));
|
|
5466
|
-
/** 12h vs 24h format for the time pickers. `null` = auto-detect. */
|
|
5467
|
-
use12HourFormat = input(null, ...(ngDevMode ? [{ debugName: "use12HourFormat" }] : []));
|
|
5468
|
-
/** Whether the time pickers show seconds. */
|
|
5469
|
-
showSeconds = input(false, ...(ngDevMode ? [{ debugName: "showSeconds" }] : []));
|
|
5470
|
-
/** Step interval for minutes in the time pickers. */
|
|
5471
|
-
minuteStep = input(1, ...(ngDevMode ? [{ debugName: "minuteStep" }] : []));
|
|
5472
|
-
// ============ OUTPUTS ============
|
|
5473
|
-
/** Emitted when a complete range is selected. */
|
|
5474
|
-
rangeChange = output();
|
|
5475
|
-
/** Emitted when the panel opens. */
|
|
5476
|
-
opened = output();
|
|
5477
|
-
/** Emitted when the panel closes. */
|
|
5478
|
-
closed = output();
|
|
5479
|
-
// ============ INTERNAL STATE ============
|
|
5480
|
-
/** Whether the panel is open. */
|
|
5481
|
-
isOpen = signal(false, ...(ngDevMode ? [{ debugName: "isOpen" }] : []));
|
|
5482
|
-
/** Which input is currently focused. */
|
|
5483
|
-
activeInput = signal(null, ...(ngDevMode ? [{ debugName: "activeInput" }] : []));
|
|
5484
|
-
/** Internal value state (managed by CVA or input). */
|
|
5485
|
-
internalValue = linkedSignal(() => this.value() ?? null, ...(ngDevMode ? [{ debugName: "internalValue" }] : []));
|
|
5486
|
-
/** Calendar active date for navigation. */
|
|
5487
|
-
calendarActiveDate = signal(this.dateAdapter.today(), ...(ngDevMode ? [{ debugName: "calendarActiveDate" }] : []));
|
|
5488
|
-
/** Live announcements for screen readers. */
|
|
5489
|
-
liveAnnouncement = signal('', ...(ngDevMode ? [{ debugName: "liveAnnouncement" }] : []));
|
|
5490
|
-
/** Whether any input is focused. */
|
|
5491
|
-
_inputFocused = signal(false, ...(ngDevMode ? [{ debugName: "_inputFocused" }] : []));
|
|
5492
|
-
/** IDs for aria-describedby (set by form-field). */
|
|
5493
|
-
_describedByIds = signal('', ...(ngDevMode ? [{ debugName: "_describedByIds" }] : []));
|
|
5494
|
-
/** Form field appearance (set by form-field). */
|
|
5495
|
-
_appearance = signal('outline', ...(ngDevMode ? [{ debugName: "_appearance" }] : []));
|
|
5496
|
-
// ============ FormFieldControl SIGNALS ============
|
|
5497
|
-
/** Whether the date range picker is focused. Implements FormFieldControl. */
|
|
5498
|
-
focused = computed(() => this._inputFocused() || this.isOpen(), ...(ngDevMode ? [{ debugName: "focused" }] : []));
|
|
5499
|
-
/** Whether the label should float. */
|
|
5500
|
-
shouldLabelFloat = computed(() => this.focused() || this.hasValue(), ...(ngDevMode ? [{ debugName: "shouldLabelFloat" }] : []));
|
|
5501
|
-
/** Whether the control is in an error state. Implements FormFieldControl. */
|
|
5502
|
-
errorState = computed(() => {
|
|
5503
|
-
if (!this.ngControl) {
|
|
5504
|
-
return this.invalid() && this.touched();
|
|
5505
|
-
}
|
|
5506
|
-
this.isOpen();
|
|
5507
|
-
this.hasValue();
|
|
5508
|
-
const matcher = this.errorStateMatcher() ?? this.defaultErrorStateMatcher;
|
|
5509
|
-
const form = this.parentFormGroup ?? this.parentForm;
|
|
5510
|
-
return matcher.isErrorState(this.ngControl.control ?? null, form);
|
|
5511
|
-
}, ...(ngDevMode ? [{ debugName: "errorState" }] : []));
|
|
5512
|
-
/** Structured validation errors from Signal Forms. */
|
|
5513
|
-
errors = computed(() => !this.ngControl ? this.sfErrors() : null, ...(ngDevMode ? [{ debugName: "errors" }] : []));
|
|
5514
|
-
/** Unique ID for the control (maps to start input). Implements FormFieldControl. */
|
|
5515
|
-
id = computed(() => `${this.datepickerId}-start`, ...(ngDevMode ? [{ debugName: "id" }] : []));
|
|
5516
|
-
/**
|
|
5517
|
-
* Effective state combining manual state with automatic error detection.
|
|
5518
|
-
*/
|
|
5519
|
-
effectiveState = computed(() => {
|
|
5520
|
-
const manualState = this.state();
|
|
5521
|
-
if (manualState !== 'default')
|
|
5522
|
-
return manualState;
|
|
5523
|
-
return this.errorState() ? 'error' : 'default';
|
|
5524
|
-
}, ...(ngDevMode ? [{ debugName: "effectiveState" }] : []));
|
|
5525
|
-
/** Combined aria-describedby from form-field. */
|
|
5526
|
-
effectiveAriaDescribedBy = computed(() => this._describedByIds() || null, ...(ngDevMode ? [{ debugName: "effectiveAriaDescribedBy" }] : []));
|
|
5527
|
-
// ============ COMPUTED STATE ============
|
|
5528
|
-
/** Start input element ID (alias for FormFieldControl id). */
|
|
5529
|
-
startInputId = this.id;
|
|
5530
|
-
/** End input element ID. */
|
|
5531
|
-
endInputId = computed(() => `${this.datepickerId}-end`, ...(ngDevMode ? [{ debugName: "endInputId" }] : []));
|
|
5532
|
-
/** Panel element ID. */
|
|
5533
|
-
panelId = computed(() => `${this.datepickerId}-panel`, ...(ngDevMode ? [{ debugName: "panelId" }] : []));
|
|
5534
|
-
/** Whether the datepicker has a value. */
|
|
5535
|
-
hasValue = computed(() => {
|
|
5536
|
-
const value = this.internalValue();
|
|
5537
|
-
return value !== null && (value.start !== null || value.end !== null);
|
|
5538
|
-
}, ...(ngDevMode ? [{ debugName: "hasValue" }] : []));
|
|
5539
|
-
/** Icon size based on datepicker size. */
|
|
5540
|
-
iconSize = computed(() => {
|
|
5541
|
-
const sizeMap = {
|
|
5542
|
-
sm: 'sm',
|
|
5543
|
-
default: 'md',
|
|
5544
|
-
lg: 'lg',
|
|
5545
|
-
};
|
|
5546
|
-
return sizeMap[this.size()];
|
|
5547
|
-
}, ...(ngDevMode ? [{ debugName: "iconSize" }] : []));
|
|
5548
|
-
/** Calendar selection (converts DateRangeValue to DateRange for calendar). */
|
|
5549
|
-
calendarSelection = computed(() => {
|
|
5550
|
-
const value = this.internalValue();
|
|
5551
|
-
if (!value)
|
|
5552
|
-
return null;
|
|
5553
|
-
return { start: value.start, end: value.end };
|
|
5554
|
-
}, ...(ngDevMode ? [{ debugName: "calendarSelection" }] : []));
|
|
5555
|
-
/** Formatted start display value. */
|
|
5556
|
-
startDisplayValue = computed(() => {
|
|
5557
|
-
const value = this.internalValue();
|
|
5558
|
-
if (!value?.start)
|
|
5559
|
-
return '';
|
|
5560
|
-
return this.dateAdapter.format(value.start, this.effectiveDateFormat());
|
|
5561
|
-
}, ...(ngDevMode ? [{ debugName: "startDisplayValue" }] : []));
|
|
5562
|
-
/** Formatted end display value. */
|
|
5563
|
-
endDisplayValue = computed(() => {
|
|
5564
|
-
const value = this.internalValue();
|
|
5565
|
-
if (!value?.end)
|
|
5566
|
-
return '';
|
|
5567
|
-
return this.dateAdapter.format(value.end, this.effectiveDateFormat());
|
|
5568
|
-
}, ...(ngDevMode ? [{ debugName: "endDisplayValue" }] : []));
|
|
5569
|
-
/** Computed trigger classes. */
|
|
5570
|
-
triggerClasses = computed(() => {
|
|
5571
|
-
const baseClasses = datepickerTriggerVariants({
|
|
5572
|
-
variant: this.variant(),
|
|
5573
|
-
size: this.size(),
|
|
5574
|
-
state: this.effectiveState(),
|
|
5575
|
-
open: this.isOpen(),
|
|
5576
|
-
});
|
|
5577
|
-
const disabledClasses = this.disabled() ? datepickerDisabledVariants() : '';
|
|
5578
|
-
// For naked variant, add padding based on form-field appearance
|
|
5579
|
-
let paddingClasses = '';
|
|
5580
|
-
if (this.variant() === 'naked') {
|
|
5581
|
-
paddingClasses = this._appearance() === 'fill' ? 'pt-5 pb-1.5 px-3' : 'py-2.5 px-3';
|
|
5582
|
-
}
|
|
5583
|
-
return mergeClasses(baseClasses, disabledClasses, paddingClasses, this.userClass());
|
|
5584
|
-
}, ...(ngDevMode ? [{ debugName: "triggerClasses" }] : []));
|
|
5585
|
-
/** Computed input classes. */
|
|
5586
|
-
inputClasses = computed(() => {
|
|
5587
|
-
return datepickerInputVariants({ size: this.size() });
|
|
5588
|
-
}, ...(ngDevMode ? [{ debugName: "inputClasses" }] : []));
|
|
5589
|
-
/** Computed separator classes. */
|
|
5590
|
-
separatorClasses = computed(() => {
|
|
5591
|
-
return datepickerRangeSeparatorVariants({ size: this.size() });
|
|
5592
|
-
}, ...(ngDevMode ? [{ debugName: "separatorClasses" }] : []));
|
|
5593
|
-
/** Computed icon classes. */
|
|
5594
|
-
iconClasses = computed(() => {
|
|
5595
|
-
return datepickerIconVariants({ size: this.size() });
|
|
5596
|
-
}, ...(ngDevMode ? [{ debugName: "iconClasses" }] : []));
|
|
5597
|
-
/** Computed clear button classes. */
|
|
5598
|
-
clearClasses = computed(() => {
|
|
5599
|
-
return datepickerClearVariants({ size: this.size() });
|
|
5600
|
-
}, ...(ngDevMode ? [{ debugName: "clearClasses" }] : []));
|
|
5601
|
-
/** Computed panel classes. */
|
|
5602
|
-
panelClasses = computed(() => {
|
|
5603
|
-
const baseClasses = datepickerPanelVariants({ size: this.size() });
|
|
5604
|
-
return joinClasses(baseClasses, this.panelClass());
|
|
5605
|
-
}, ...(ngDevMode ? [{ debugName: "panelClasses" }] : []));
|
|
5606
|
-
/** Computed footer classes. */
|
|
5607
|
-
footerClasses = computed(() => {
|
|
5608
|
-
return datepickerFooterVariants({ size: this.size() });
|
|
5609
|
-
}, ...(ngDevMode ? [{ debugName: "footerClasses" }] : []));
|
|
5610
|
-
/** Computed today button classes. */
|
|
5611
|
-
todayButtonClasses = computed(() => {
|
|
5612
|
-
return datepickerFooterButtonVariants({ size: this.size(), variant: 'primary' });
|
|
5613
|
-
}, ...(ngDevMode ? [{ debugName: "todayButtonClasses" }] : []));
|
|
5614
|
-
/** Computed clear button classes (footer). */
|
|
5615
|
-
clearButtonClasses = computed(() => {
|
|
5616
|
-
return datepickerFooterButtonVariants({ size: this.size(), variant: 'secondary' });
|
|
5617
|
-
}, ...(ngDevMode ? [{ debugName: "clearButtonClasses" }] : []));
|
|
5618
|
-
/** Time section divider classes. */
|
|
5619
|
-
timeSectionClasses = computed(() => {
|
|
5620
|
-
return timepickerSectionVariants({ size: this.size() });
|
|
5621
|
-
}, ...(ngDevMode ? [{ debugName: "timeSectionClasses" }] : []));
|
|
5622
|
-
/** Time label classes. */
|
|
5623
|
-
timeLabelClasses = computed(() => {
|
|
5624
|
-
return timepickerLabelVariants({ size: this.size() });
|
|
5625
|
-
}, ...(ngDevMode ? [{ debugName: "timeLabelClasses" }] : []));
|
|
5626
|
-
/** Start time value derived from the start date. */
|
|
5627
|
-
startTimeValue = computed(() => {
|
|
5628
|
-
const value = this.internalValue();
|
|
5629
|
-
if (!value?.start)
|
|
5630
|
-
return null;
|
|
5631
|
-
return createTimeValue(this.dateAdapter.getHours(value.start), this.dateAdapter.getMinutes(value.start), this.dateAdapter.getSeconds(value.start));
|
|
5632
|
-
}, ...(ngDevMode ? [{ debugName: "startTimeValue" }] : []));
|
|
5633
|
-
/** End time value derived from the end date. */
|
|
5634
|
-
endTimeValue = computed(() => {
|
|
5635
|
-
const value = this.internalValue();
|
|
5636
|
-
if (!value?.end)
|
|
5637
|
-
return null;
|
|
5638
|
-
return createTimeValue(this.dateAdapter.getHours(value.end), this.dateAdapter.getMinutes(value.end), this.dateAdapter.getSeconds(value.end));
|
|
5639
|
-
}, ...(ngDevMode ? [{ debugName: "endTimeValue" }] : []));
|
|
5640
|
-
/** Effective display format — switches to dateTime when time picker is shown. */
|
|
5641
|
-
effectiveDateFormat = computed(() => {
|
|
5642
|
-
if (this.showTimePicker()) {
|
|
5643
|
-
return this.showSeconds() ? 'dateTimeLong' : 'dateTimeMedium';
|
|
5644
|
-
}
|
|
5645
|
-
return this.dateFormat();
|
|
5646
|
-
}, ...(ngDevMode ? [{ debugName: "effectiveDateFormat" }] : []));
|
|
5647
|
-
/** Whether the panel should stay open (keepOpen or time picker shown). */
|
|
5648
|
-
effectiveKeepOpen = computed(() => {
|
|
5649
|
-
return this.keepOpen() || this.showTimePicker();
|
|
5650
|
-
}, ...(ngDevMode ? [{ debugName: "effectiveKeepOpen" }] : []));
|
|
5651
|
-
// ============ CVA CALLBACKS ============
|
|
5652
|
-
onChange = () => { };
|
|
5653
|
-
onTouched = () => { };
|
|
5654
|
-
onValidatorChange = () => { };
|
|
5655
|
-
constructor() {
|
|
5656
|
-
// Wire up NgControl if present
|
|
5657
|
-
if (this.ngControl) {
|
|
5658
|
-
this.ngControl.valueAccessor = this;
|
|
5659
|
-
}
|
|
5660
|
-
// Sync calendar active date with internal value or startAt
|
|
5661
|
-
effect(() => {
|
|
5662
|
-
const value = this.internalValue();
|
|
5663
|
-
const startAt = this.startAt();
|
|
5664
|
-
if (value?.start) {
|
|
5665
|
-
this.calendarActiveDate.set(value.start);
|
|
5666
|
-
}
|
|
5667
|
-
else if (startAt) {
|
|
5668
|
-
this.calendarActiveDate.set(startAt);
|
|
5669
|
-
}
|
|
5670
|
-
else {
|
|
5671
|
-
this.calendarActiveDate.set(this.dateAdapter.today());
|
|
5672
|
-
}
|
|
5673
|
-
}, {});
|
|
5674
|
-
}
|
|
5675
|
-
ngOnDestroy() {
|
|
5676
|
-
this.destroyOverlay();
|
|
5677
|
-
}
|
|
5678
|
-
// ============ CVA IMPLEMENTATION ============
|
|
5679
|
-
writeValue(value) {
|
|
5680
|
-
this.internalValue.set(value);
|
|
5681
|
-
}
|
|
5682
|
-
registerOnChange(fn) {
|
|
5683
|
-
this.onChange = fn;
|
|
5684
|
-
}
|
|
5685
|
-
registerOnTouched(fn) {
|
|
5686
|
-
this.onTouched = fn;
|
|
5687
|
-
}
|
|
5688
|
-
setDisabledState(_isDisabled) {
|
|
5689
|
-
// Disabled state is handled via the disabled input
|
|
5690
|
-
}
|
|
5691
|
-
// ============ VALIDATOR IMPLEMENTATION ============
|
|
5692
|
-
validate() {
|
|
5693
|
-
const value = this.internalValue();
|
|
5694
|
-
// Required validation
|
|
5695
|
-
if (this.required() && (!value || (!value.start && !value.end))) {
|
|
5696
|
-
return { required: true };
|
|
5697
|
-
}
|
|
5698
|
-
if (value) {
|
|
5699
|
-
const { start, end } = value;
|
|
5700
|
-
const min = this.min();
|
|
5701
|
-
const max = this.max();
|
|
5702
|
-
const dateFilter = this.dateFilter();
|
|
5703
|
-
// Min validation for start
|
|
5704
|
-
if (start && min && this.dateAdapter.compareDate(start, min) < 0) {
|
|
5705
|
-
return { minDate: { min, actual: start } };
|
|
5706
|
-
}
|
|
5707
|
-
// Max validation for end
|
|
5708
|
-
if (end && max && this.dateAdapter.compareDate(end, max) > 0) {
|
|
5709
|
-
return { maxDate: { max, actual: end } };
|
|
5710
|
-
}
|
|
5711
|
-
// Date filter validation
|
|
5712
|
-
if (start && dateFilter && !dateFilter(start)) {
|
|
5713
|
-
return { dateFilter: { date: start } };
|
|
5714
|
-
}
|
|
5715
|
-
if (end && dateFilter && !dateFilter(end)) {
|
|
5716
|
-
return { dateFilter: { date: end } };
|
|
5717
|
-
}
|
|
5718
|
-
// Range validation (start must be before or equal to end)
|
|
5719
|
-
if (start && end && this.dateAdapter.compareDate(start, end) > 0) {
|
|
5720
|
-
return { rangeInvalid: { start, end } };
|
|
5721
|
-
}
|
|
5722
|
-
}
|
|
5723
|
-
return null;
|
|
5724
|
-
}
|
|
5725
|
-
registerOnValidatorChange(fn) {
|
|
5726
|
-
this.onValidatorChange = fn;
|
|
5727
|
-
}
|
|
5728
|
-
// ============ PUBLIC METHODS ============
|
|
5729
|
-
/** Opens the datepicker panel. */
|
|
5730
|
-
open() {
|
|
5731
|
-
if (this.disabled() || this.isOpen()) {
|
|
5732
|
-
return;
|
|
5733
|
-
}
|
|
5734
|
-
this.createOverlay();
|
|
5735
|
-
this.isOpen.set(true);
|
|
5736
|
-
this.opened.emit();
|
|
5737
|
-
this.announce('Calendar opened. Select a start date.');
|
|
5738
|
-
}
|
|
5739
|
-
/** Closes the datepicker panel. */
|
|
5740
|
-
close() {
|
|
5741
|
-
if (!this.isOpen()) {
|
|
5742
|
-
return;
|
|
5743
|
-
}
|
|
5744
|
-
this.destroyOverlay();
|
|
5745
|
-
this.isOpen.set(false);
|
|
5746
|
-
this.closed.emit();
|
|
5747
|
-
this.onTouched();
|
|
5748
|
-
this.touched.set(true);
|
|
5749
|
-
// Return focus to appropriate input
|
|
5750
|
-
const activeInput = this.activeInput();
|
|
5751
|
-
if (activeInput === 'end') {
|
|
5752
|
-
this.endInputRef().nativeElement.focus();
|
|
5753
|
-
}
|
|
5754
|
-
else {
|
|
5755
|
-
this.startInputRef().nativeElement.focus();
|
|
5756
|
-
}
|
|
5757
|
-
}
|
|
5758
|
-
/** Toggles the datepicker panel. */
|
|
5759
|
-
toggle() {
|
|
5760
|
-
if (this.isOpen()) {
|
|
5761
|
-
this.close();
|
|
5762
|
-
}
|
|
5763
|
-
else {
|
|
5764
|
-
this.open();
|
|
5765
|
-
}
|
|
5766
|
-
}
|
|
5767
|
-
/** Clears the selected date range. */
|
|
5768
|
-
clear(event) {
|
|
5769
|
-
event?.preventDefault();
|
|
5770
|
-
event?.stopPropagation();
|
|
5771
|
-
this.updateValue(null);
|
|
5772
|
-
this.announce('Date range cleared');
|
|
5773
|
-
}
|
|
5774
|
-
/** Selects today's date as the start date. */
|
|
5775
|
-
selectToday() {
|
|
5776
|
-
const today = this.dateAdapter.today();
|
|
5777
|
-
const currentValue = this.internalValue();
|
|
5778
|
-
// Set today as start if no start, or as end if we have a start
|
|
5779
|
-
if (!currentValue?.start) {
|
|
5780
|
-
this.updateValue(createDateRangeValue(today, null));
|
|
5781
|
-
this.announce('Start date set to today');
|
|
5782
|
-
}
|
|
5783
|
-
else if (!currentValue.end) {
|
|
5784
|
-
// Ensure proper ordering
|
|
5785
|
-
const newRange = this.dateAdapter.compareDate(today, currentValue.start) < 0
|
|
5786
|
-
? createDateRangeValue(today, currentValue.start)
|
|
5787
|
-
: createDateRangeValue(currentValue.start, today);
|
|
5788
|
-
this.updateValue(newRange);
|
|
5789
|
-
if (!this.effectiveKeepOpen()) {
|
|
5790
|
-
this.close();
|
|
5791
|
-
}
|
|
5792
|
-
}
|
|
5793
|
-
}
|
|
5794
|
-
// ============ EVENT HANDLERS ============
|
|
5795
|
-
onTriggerClick() {
|
|
5796
|
-
if (!this.disabled() && !this.isOpen()) {
|
|
5797
|
-
this.open();
|
|
5798
|
-
}
|
|
5799
|
-
}
|
|
5800
|
-
onTriggerKeydown(event) {
|
|
5801
|
-
switch (event.key) {
|
|
5802
|
-
case 'Escape':
|
|
5803
|
-
if (this.isOpen()) {
|
|
5804
|
-
event.preventDefault();
|
|
5805
|
-
this.close();
|
|
5806
|
-
}
|
|
5807
|
-
break;
|
|
5808
|
-
}
|
|
5809
|
-
}
|
|
5810
|
-
onStartInputFocus() {
|
|
5811
|
-
this.activeInput.set('start');
|
|
5812
|
-
this._inputFocused.set(true);
|
|
5813
|
-
}
|
|
5814
|
-
onEndInputFocus() {
|
|
5815
|
-
this.activeInput.set('end');
|
|
5816
|
-
this._inputFocused.set(true);
|
|
5817
|
-
}
|
|
5818
|
-
onInputKeydown(event, inputType) {
|
|
5819
|
-
switch (event.key) {
|
|
5820
|
-
case 'Enter':
|
|
5821
|
-
event.preventDefault();
|
|
5822
|
-
if (this.isOpen()) {
|
|
5823
|
-
// Commit manual input
|
|
5824
|
-
if (inputType === 'start') {
|
|
5825
|
-
this.parseAndSetStart(this.startInputRef().nativeElement.value);
|
|
5826
|
-
}
|
|
5827
|
-
else {
|
|
5828
|
-
this.parseAndSetEnd(this.endInputRef().nativeElement.value);
|
|
5829
|
-
}
|
|
5830
|
-
}
|
|
5831
|
-
else {
|
|
5832
|
-
this.open();
|
|
5833
|
-
}
|
|
5834
|
-
break;
|
|
5835
|
-
case 'Escape':
|
|
5836
|
-
if (this.isOpen()) {
|
|
5837
|
-
event.preventDefault();
|
|
5838
|
-
this.close();
|
|
5839
|
-
}
|
|
5840
|
-
break;
|
|
5841
|
-
case 'ArrowDown':
|
|
5842
|
-
if (!this.isOpen()) {
|
|
5843
|
-
event.preventDefault();
|
|
5844
|
-
this.open();
|
|
5845
|
-
}
|
|
5846
|
-
break;
|
|
5847
|
-
case 'Tab':
|
|
5848
|
-
// Allow natural tab navigation between inputs
|
|
5849
|
-
break;
|
|
5850
|
-
}
|
|
5851
|
-
}
|
|
5852
|
-
onStartInputChange(_event) {
|
|
5853
|
-
// Debounced in blur handler
|
|
5854
|
-
}
|
|
5855
|
-
onEndInputChange(_event) {
|
|
5856
|
-
// Debounced in blur handler
|
|
5857
|
-
}
|
|
5858
|
-
onStartInputBlur() {
|
|
5859
|
-
this._inputFocused.set(false);
|
|
5860
|
-
if (this.allowManualInput()) {
|
|
5861
|
-
this.parseAndSetStart(this.startInputRef().nativeElement.value);
|
|
5862
|
-
}
|
|
5863
|
-
this.onTouched();
|
|
5864
|
-
this.touched.set(true);
|
|
5865
|
-
}
|
|
5866
|
-
onEndInputBlur() {
|
|
5867
|
-
this._inputFocused.set(false);
|
|
5868
|
-
if (this.allowManualInput()) {
|
|
5869
|
-
this.parseAndSetEnd(this.endInputRef().nativeElement.value);
|
|
5870
|
-
}
|
|
5871
|
-
this.onTouched();
|
|
5872
|
-
this.touched.set(true);
|
|
5873
|
-
}
|
|
5874
|
-
onPanelKeydown(event) {
|
|
5875
|
-
switch (event.key) {
|
|
5876
|
-
case 'Escape':
|
|
5877
|
-
event.preventDefault();
|
|
5878
|
-
this.close();
|
|
5879
|
-
break;
|
|
5880
|
-
}
|
|
5881
|
-
}
|
|
5882
|
-
onCalendarSelectionChange(selection) {
|
|
5883
|
-
// The calendar emits the selection from the strategy
|
|
5884
|
-
const range = selection;
|
|
5885
|
-
if (range) {
|
|
5886
|
-
const newValue = createDateRangeValue(range.start, range.end);
|
|
5887
|
-
this.updateValue(newValue);
|
|
5888
|
-
if (range.start && range.end) {
|
|
5889
|
-
// Complete range selected
|
|
5890
|
-
this.announce(`Range selected: ${this.formatDate(range.start)} to ${this.formatDate(range.end)}`);
|
|
5891
|
-
if (!this.effectiveKeepOpen()) {
|
|
5892
|
-
this.close();
|
|
5893
|
-
}
|
|
5894
|
-
}
|
|
5895
|
-
else if (range.start) {
|
|
5896
|
-
this.announce(`Start date selected: ${this.formatDate(range.start)}. Now select end date.`);
|
|
5897
|
-
}
|
|
5898
|
-
}
|
|
5899
|
-
}
|
|
5900
|
-
onActiveDateChange(date) {
|
|
5901
|
-
this.calendarActiveDate.set(date);
|
|
5902
|
-
}
|
|
5903
|
-
onStartTimeChange(time) {
|
|
5904
|
-
if (!time)
|
|
5905
|
-
return;
|
|
5906
|
-
const value = this.internalValue();
|
|
5907
|
-
const startDate = value?.start ?? this.dateAdapter.today();
|
|
5908
|
-
const updated = this.dateAdapter.setTime(startDate, time.hours, time.minutes, time.seconds);
|
|
5909
|
-
this.updateValue(createDateRangeValue(updated, value?.end ?? null));
|
|
5910
|
-
}
|
|
5911
|
-
onEndTimeChange(time) {
|
|
5912
|
-
if (!time)
|
|
5913
|
-
return;
|
|
5914
|
-
const value = this.internalValue();
|
|
5915
|
-
const endDate = value?.end ?? this.dateAdapter.today();
|
|
5916
|
-
const updated = this.dateAdapter.setTime(endDate, time.hours, time.minutes, time.seconds);
|
|
5917
|
-
this.updateValue(createDateRangeValue(value?.start ?? null, updated));
|
|
5918
|
-
}
|
|
5919
|
-
// ============ FormFieldControl IMPLEMENTATION ============
|
|
5920
|
-
/**
|
|
5921
|
-
* Called when the form field container is clicked.
|
|
5922
|
-
* Implements FormFieldControl.
|
|
5923
|
-
*/
|
|
5924
|
-
onContainerClick(event) {
|
|
5925
|
-
const target = event.target;
|
|
5926
|
-
if (!this.disabled() && !this.triggerRef().nativeElement.contains(target)) {
|
|
5927
|
-
this.open();
|
|
5928
|
-
this.startInputRef().nativeElement.focus();
|
|
5929
|
-
}
|
|
5930
|
-
}
|
|
5931
|
-
/**
|
|
5932
|
-
* Sets the describedBy IDs from the form field.
|
|
5933
|
-
* Called by the parent form field component.
|
|
5934
|
-
*/
|
|
5935
|
-
setDescribedByIds(ids) {
|
|
5936
|
-
this._describedByIds.set(ids);
|
|
5937
|
-
}
|
|
5938
|
-
/**
|
|
5939
|
-
* Sets the appearance for styling.
|
|
5940
|
-
* Called by the parent form field component.
|
|
5941
|
-
*/
|
|
5942
|
-
setAppearance(appearance) {
|
|
5943
|
-
this._appearance.set(appearance);
|
|
5944
|
-
}
|
|
5945
|
-
// ============ PRIVATE METHODS ============
|
|
5946
|
-
createOverlay() {
|
|
5947
|
-
if (this.overlayRef) {
|
|
5948
|
-
return;
|
|
5949
|
-
}
|
|
5950
|
-
const hostEl = this.elementRef.nativeElement;
|
|
5951
|
-
const positionStrategy = this.overlay
|
|
5952
|
-
.position()
|
|
5953
|
-
.flexibleConnectedTo(hostEl)
|
|
5954
|
-
.withPositions(DEFAULT_POSITIONS)
|
|
5955
|
-
.withFlexibleDimensions(false)
|
|
5956
|
-
.withPush(true);
|
|
5957
|
-
this.overlayRef = this.overlay.create({
|
|
5958
|
-
positionStrategy,
|
|
5959
|
-
scrollStrategy: this.overlay.scrollStrategies.reposition(),
|
|
5960
|
-
hasBackdrop: true,
|
|
5961
|
-
backdropClass: 'cdk-overlay-transparent-backdrop',
|
|
5962
|
-
});
|
|
5963
|
-
// Attach panel template
|
|
5964
|
-
const portal = new TemplatePortal(this.panelTemplateRef(), this.viewContainerRef);
|
|
5965
|
-
this.overlayRef.attach(portal);
|
|
5966
|
-
// Close on backdrop click
|
|
5967
|
-
this.overlayRef
|
|
5968
|
-
.backdropClick()
|
|
5969
|
-
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
5970
|
-
.subscribe(() => this.close());
|
|
5971
|
-
// Close on outside click
|
|
5972
|
-
this.overlayRef
|
|
5973
|
-
.outsidePointerEvents()
|
|
5974
|
-
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
5975
|
-
.subscribe(() => this.close());
|
|
5976
|
-
}
|
|
5977
|
-
destroyOverlay() {
|
|
5978
|
-
if (this.overlayRef) {
|
|
5979
|
-
this.overlayRef.dispose();
|
|
5980
|
-
this.overlayRef = null;
|
|
5981
|
-
}
|
|
5982
|
-
}
|
|
5983
|
-
updateValue(value) {
|
|
5984
|
-
this.value.set(value);
|
|
5985
|
-
this.onChange(value);
|
|
5986
|
-
this.rangeChange.emit(value);
|
|
5987
|
-
this.onValidatorChange();
|
|
5988
|
-
}
|
|
5989
|
-
parseAndSetStart(inputValue) {
|
|
5990
|
-
const currentValue = this.internalValue();
|
|
5991
|
-
if (!inputValue.trim()) {
|
|
5992
|
-
// Clear start date
|
|
5993
|
-
if (currentValue?.start) {
|
|
5994
|
-
this.updateValue(createDateRangeValue(null, currentValue.end));
|
|
5995
|
-
}
|
|
5996
|
-
return;
|
|
5997
|
-
}
|
|
5998
|
-
const parsed = this.dateAdapter.parse(inputValue, this.effectiveDateFormat());
|
|
5999
|
-
if (parsed && this.dateAdapter.isValid(parsed) && this.isDateValid(parsed)) {
|
|
6000
|
-
// Ensure start <= end
|
|
6001
|
-
if (currentValue?.end && this.dateAdapter.compareDate(parsed, currentValue.end) > 0) {
|
|
6002
|
-
// Swap dates
|
|
6003
|
-
this.updateValue(createDateRangeValue(currentValue.end, parsed));
|
|
6004
|
-
}
|
|
6005
|
-
else {
|
|
6006
|
-
this.updateValue(createDateRangeValue(parsed, currentValue?.end ?? null));
|
|
6007
|
-
}
|
|
6008
|
-
}
|
|
6009
|
-
else {
|
|
6010
|
-
// Revert to current value
|
|
6011
|
-
this.startInputRef().nativeElement.value = this.startDisplayValue();
|
|
6012
|
-
}
|
|
6013
|
-
}
|
|
6014
|
-
parseAndSetEnd(inputValue) {
|
|
6015
|
-
const currentValue = this.internalValue();
|
|
6016
|
-
if (!inputValue.trim()) {
|
|
6017
|
-
// Clear end date
|
|
6018
|
-
if (currentValue?.end) {
|
|
6019
|
-
this.updateValue(createDateRangeValue(currentValue.start, null));
|
|
6020
|
-
}
|
|
6021
|
-
return;
|
|
6022
|
-
}
|
|
6023
|
-
const parsed = this.dateAdapter.parse(inputValue, this.effectiveDateFormat());
|
|
6024
|
-
if (parsed && this.dateAdapter.isValid(parsed) && this.isDateValid(parsed)) {
|
|
6025
|
-
// Ensure start <= end
|
|
6026
|
-
if (currentValue?.start && this.dateAdapter.compareDate(parsed, currentValue.start) < 0) {
|
|
6027
|
-
// Swap dates
|
|
6028
|
-
this.updateValue(createDateRangeValue(parsed, currentValue.start));
|
|
6029
|
-
}
|
|
6030
|
-
else {
|
|
6031
|
-
this.updateValue(createDateRangeValue(currentValue?.start ?? null, parsed));
|
|
6032
|
-
}
|
|
6033
|
-
}
|
|
6034
|
-
else {
|
|
6035
|
-
// Revert to current value
|
|
6036
|
-
this.endInputRef().nativeElement.value = this.endDisplayValue();
|
|
6037
|
-
}
|
|
6038
|
-
}
|
|
6039
|
-
isDateValid(date) {
|
|
6040
|
-
const min = this.min();
|
|
6041
|
-
const max = this.max();
|
|
6042
|
-
const filter = this.dateFilter();
|
|
6043
|
-
if (min && this.dateAdapter.compareDate(date, min) < 0) {
|
|
6044
|
-
return false;
|
|
6045
|
-
}
|
|
6046
|
-
if (max && this.dateAdapter.compareDate(date, max) > 0) {
|
|
6047
|
-
return false;
|
|
6048
|
-
}
|
|
6049
|
-
if (filter && !filter(date)) {
|
|
6050
|
-
return false;
|
|
6051
|
-
}
|
|
6052
|
-
return true;
|
|
6053
|
-
}
|
|
6054
|
-
formatDate(date) {
|
|
6055
|
-
return this.dateAdapter.format(date, 'long');
|
|
6056
|
-
}
|
|
6057
|
-
announce(message) {
|
|
6058
|
-
this.liveAnnouncement.set(message);
|
|
6059
|
-
}
|
|
6060
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: ComDateRangePicker, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
6061
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: ComDateRangePicker, isStandalone: true, selector: "com-date-range-picker", inputs: { value: { classPropertyName: "value", publicName: "value", 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 }, dateFilter: { classPropertyName: "dateFilter", publicName: "dateFilter", isSignal: true, isRequired: false, transformFunction: null }, startAt: { classPropertyName: "startAt", publicName: "startAt", isSignal: true, isRequired: false, transformFunction: null }, startView: { classPropertyName: "startView", publicName: "startView", isSignal: true, isRequired: false, transformFunction: null }, firstDayOfWeek: { classPropertyName: "firstDayOfWeek", publicName: "firstDayOfWeek", isSignal: true, isRequired: false, transformFunction: null }, startPlaceholder: { classPropertyName: "startPlaceholder", publicName: "startPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, endPlaceholder: { classPropertyName: "endPlaceholder", publicName: "endPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, dateFormat: { classPropertyName: "dateFormat", publicName: "dateFormat", isSignal: true, isRequired: false, transformFunction: null }, showClearButton: { classPropertyName: "showClearButton", publicName: "showClearButton", isSignal: true, isRequired: false, transformFunction: null }, showTodayButton: { classPropertyName: "showTodayButton", publicName: "showTodayButton", isSignal: true, isRequired: false, transformFunction: null }, showFooterClearButton: { classPropertyName: "showFooterClearButton", publicName: "showFooterClearButton", isSignal: true, isRequired: false, transformFunction: null }, keepOpen: { classPropertyName: "keepOpen", publicName: "keepOpen", isSignal: true, isRequired: false, transformFunction: null }, allowManualInput: { classPropertyName: "allowManualInput", publicName: "allowManualInput", isSignal: true, isRequired: false, transformFunction: null }, panelClass: { classPropertyName: "panelClass", publicName: "panelClass", isSignal: true, isRequired: false, transformFunction: null }, panelWidth: { classPropertyName: "panelWidth", publicName: "panelWidth", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, state: { classPropertyName: "state", publicName: "state", isSignal: true, isRequired: false, transformFunction: null }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, startAriaLabel: { classPropertyName: "startAriaLabel", publicName: "startAriaLabel", isSignal: true, isRequired: false, transformFunction: null }, endAriaLabel: { classPropertyName: "endAriaLabel", publicName: "endAriaLabel", isSignal: true, isRequired: false, transformFunction: null }, errorStateMatcher: { classPropertyName: "errorStateMatcher", publicName: "errorStateMatcher", isSignal: true, isRequired: false, transformFunction: null }, touched: { classPropertyName: "touched", publicName: "touched", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, sfErrors: { classPropertyName: "sfErrors", publicName: "errors", isSignal: true, isRequired: false, transformFunction: null }, showTimePicker: { classPropertyName: "showTimePicker", publicName: "showTimePicker", isSignal: true, isRequired: false, transformFunction: null }, use12HourFormat: { classPropertyName: "use12HourFormat", publicName: "use12HourFormat", 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 } }, outputs: { value: "valueChange", touched: "touchedChange", rangeChange: "rangeChange", opened: "opened", closed: "closed" }, host: { properties: { "class.com-date-range-picker-disabled": "disabled()", "class.com-date-range-picker-open": "isOpen()" }, classAttribute: "com-date-range-picker-host inline-block" }, providers: [
|
|
6062
|
-
RangeSelectionStrategy,
|
|
6063
|
-
{ provide: CALENDAR_SELECTION_STRATEGY, useExisting: RangeSelectionStrategy },
|
|
6064
|
-
{ provide: FormFieldControl, useExisting: forwardRef(() => ComDateRangePicker) },
|
|
6065
|
-
], viewQueries: [{ propertyName: "triggerRef", first: true, predicate: ["triggerElement"], descendants: true, isSignal: true }, { propertyName: "startInputRef", first: true, predicate: ["startInputElement"], descendants: true, isSignal: true }, { propertyName: "endInputRef", first: true, predicate: ["endInputElement"], descendants: true, isSignal: true }, { propertyName: "panelTemplateRef", first: true, predicate: ["panelTemplate"], descendants: true, isSignal: true }], exportAs: ["comDateRangePicker"], ngImport: i0, template: `
|
|
6066
|
-
<!-- Trigger container -->
|
|
6067
|
-
<div
|
|
6068
|
-
#triggerElement
|
|
6069
|
-
[class]="triggerClasses()"
|
|
6070
|
-
[attr.aria-expanded]="isOpen()"
|
|
6071
|
-
[attr.aria-haspopup]="'dialog'"
|
|
6072
|
-
[attr.aria-owns]="panelId()"
|
|
6073
|
-
[attr.aria-disabled]="disabled() || null"
|
|
6074
|
-
(click)="onTriggerClick()"
|
|
6075
|
-
(keydown)="onTriggerKeydown($event)"
|
|
6076
|
-
>
|
|
6077
|
-
<!-- Start date input -->
|
|
6078
|
-
<input
|
|
6079
|
-
#startInputElement
|
|
6080
|
-
type="text"
|
|
6081
|
-
[class]="inputClasses()"
|
|
6082
|
-
[value]="startDisplayValue()"
|
|
6083
|
-
[placeholder]="startPlaceholder()"
|
|
6084
|
-
[disabled]="disabled()"
|
|
6085
|
-
[readonly]="!allowManualInput()"
|
|
6086
|
-
[attr.id]="startInputId()"
|
|
6087
|
-
[attr.aria-label]="startAriaLabel() || startPlaceholder()"
|
|
6088
|
-
[attr.aria-describedby]="effectiveAriaDescribedBy() || null"
|
|
6089
|
-
[attr.aria-invalid]="effectiveState() === 'error' || null"
|
|
6090
|
-
[attr.aria-required]="required() || null"
|
|
6091
|
-
(focus)="onStartInputFocus()"
|
|
6092
|
-
(input)="onStartInputChange($event)"
|
|
6093
|
-
(blur)="onStartInputBlur()"
|
|
6094
|
-
(keydown)="onInputKeydown($event, 'start')"
|
|
6095
|
-
/>
|
|
6096
|
-
|
|
6097
|
-
<!-- Range separator -->
|
|
6098
|
-
<span [class]="separatorClasses()">
|
|
6099
|
-
<com-icon name="arrow-right" [size]="iconSize()" />
|
|
6100
|
-
</span>
|
|
6101
|
-
|
|
6102
|
-
<!-- End date input -->
|
|
6103
|
-
<input
|
|
6104
|
-
#endInputElement
|
|
6105
|
-
type="text"
|
|
6106
|
-
[class]="inputClasses()"
|
|
6107
|
-
[value]="endDisplayValue()"
|
|
6108
|
-
[placeholder]="endPlaceholder()"
|
|
6109
|
-
[disabled]="disabled()"
|
|
6110
|
-
[readonly]="!allowManualInput()"
|
|
6111
|
-
[attr.id]="endInputId()"
|
|
6112
|
-
[attr.aria-label]="endAriaLabel() || endPlaceholder()"
|
|
6113
|
-
[attr.aria-invalid]="effectiveState() === 'error' || null"
|
|
6114
|
-
(focus)="onEndInputFocus()"
|
|
6115
|
-
(input)="onEndInputChange($event)"
|
|
6116
|
-
(blur)="onEndInputBlur()"
|
|
6117
|
-
(keydown)="onInputKeydown($event, 'end')"
|
|
6118
|
-
/>
|
|
6119
|
-
|
|
6120
|
-
<!-- Clear button -->
|
|
6121
|
-
@if (showClearButton() && hasValue() && !disabled()) {
|
|
6122
|
-
<button
|
|
6123
|
-
type="button"
|
|
6124
|
-
[class]="clearClasses()"
|
|
6125
|
-
[attr.aria-label]="'Clear date range'"
|
|
6126
|
-
(click)="clear($event)"
|
|
6127
|
-
>
|
|
6128
|
-
<com-icon name="x" [size]="iconSize()" />
|
|
6129
|
-
</button>
|
|
6130
|
-
}
|
|
6131
|
-
|
|
6132
|
-
<!-- Calendar icon -->
|
|
6133
|
-
<button
|
|
6134
|
-
type="button"
|
|
6135
|
-
[class]="iconClasses()"
|
|
6136
|
-
[attr.aria-label]="isOpen() ? 'Close calendar' : 'Open calendar'"
|
|
6137
|
-
[disabled]="disabled()"
|
|
6138
|
-
tabindex="-1"
|
|
6139
|
-
>
|
|
6140
|
-
<com-icon name="calendar" [size]="iconSize()" />
|
|
6141
|
-
</button>
|
|
6142
|
-
</div>
|
|
6143
|
-
|
|
6144
|
-
<!-- Panel template (rendered in overlay) -->
|
|
6145
|
-
<ng-template #panelTemplate>
|
|
6146
|
-
<div
|
|
6147
|
-
[class]="panelClasses()"
|
|
6148
|
-
[attr.id]="panelId()"
|
|
6149
|
-
role="dialog"
|
|
6150
|
-
aria-modal="true"
|
|
6151
|
-
[attr.aria-label]="'Choose date range'"
|
|
6152
|
-
(keydown)="onPanelKeydown($event)"
|
|
6153
|
-
cdkTrapFocus
|
|
6154
|
-
[cdkTrapFocusAutoCapture]="true"
|
|
6155
|
-
>
|
|
6156
|
-
<com-calendar
|
|
6157
|
-
[activeDate]="calendarActiveDate()"
|
|
6158
|
-
[selected]="calendarSelection()"
|
|
6159
|
-
[minDate]="min()"
|
|
6160
|
-
[maxDate]="max()"
|
|
6161
|
-
[dateFilter]="dateFilter()"
|
|
6162
|
-
[startView]="startView()"
|
|
6163
|
-
[firstDayOfWeek]="firstDayOfWeek()"
|
|
6164
|
-
[monthColumns]="2"
|
|
6165
|
-
[bordered]="false"
|
|
6166
|
-
(selectedChange)="onCalendarSelectionChange($event)"
|
|
6167
|
-
(activeDateChange)="onActiveDateChange($event)"
|
|
6168
|
-
/>
|
|
6169
|
-
|
|
6170
|
-
@if (showTimePicker()) {
|
|
6171
|
-
<div [class]="timeSectionClasses()">
|
|
6172
|
-
<div class="flex items-center gap-3">
|
|
6173
|
-
<div class="flex flex-col gap-1">
|
|
6174
|
-
<span [class]="timeLabelClasses()">Start time</span>
|
|
6175
|
-
<com-time-picker
|
|
6176
|
-
variant="embedded"
|
|
6177
|
-
[size]="size()"
|
|
6178
|
-
[value]="startTimeValue()"
|
|
6179
|
-
[use12HourFormat]="use12HourFormat()"
|
|
6180
|
-
[showSeconds]="showSeconds()"
|
|
6181
|
-
[minuteStep]="minuteStep()"
|
|
6182
|
-
[disabled]="disabled()"
|
|
6183
|
-
ariaLabel="Start time"
|
|
6184
|
-
(timeChange)="onStartTimeChange($event)"
|
|
6185
|
-
/>
|
|
6186
|
-
</div>
|
|
6187
|
-
<span class="text-muted-foreground mt-5">
|
|
6188
|
-
<com-icon name="arrow-right" [size]="iconSize()" />
|
|
6189
|
-
</span>
|
|
6190
|
-
<div class="flex flex-col gap-1">
|
|
6191
|
-
<span [class]="timeLabelClasses()">End time</span>
|
|
6192
|
-
<com-time-picker
|
|
6193
|
-
variant="embedded"
|
|
6194
|
-
[size]="size()"
|
|
6195
|
-
[value]="endTimeValue()"
|
|
6196
|
-
[use12HourFormat]="use12HourFormat()"
|
|
6197
|
-
[showSeconds]="showSeconds()"
|
|
6198
|
-
[minuteStep]="minuteStep()"
|
|
6199
|
-
[disabled]="disabled()"
|
|
6200
|
-
ariaLabel="End time"
|
|
6201
|
-
(timeChange)="onEndTimeChange($event)"
|
|
6202
|
-
/>
|
|
6203
|
-
</div>
|
|
6204
|
-
</div>
|
|
6205
|
-
</div>
|
|
6206
|
-
}
|
|
6207
|
-
|
|
6208
|
-
@if (showTodayButton() || showFooterClearButton() || showTimePicker()) {
|
|
6209
|
-
<div [class]="footerClasses()">
|
|
6210
|
-
@if (showTodayButton()) {
|
|
6211
|
-
<button
|
|
6212
|
-
type="button"
|
|
6213
|
-
[class]="todayButtonClasses()"
|
|
6214
|
-
(click)="selectToday()"
|
|
6215
|
-
>
|
|
6216
|
-
Today
|
|
6217
|
-
</button>
|
|
6218
|
-
}
|
|
6219
|
-
@if (showFooterClearButton()) {
|
|
6220
|
-
<button
|
|
6221
|
-
type="button"
|
|
6222
|
-
[class]="clearButtonClasses()"
|
|
6223
|
-
(click)="clear($event)"
|
|
6224
|
-
>
|
|
6225
|
-
Clear
|
|
6226
|
-
</button>
|
|
6227
|
-
}
|
|
6228
|
-
@if (showTimePicker()) {
|
|
6229
|
-
<button
|
|
6230
|
-
type="button"
|
|
6231
|
-
[class]="todayButtonClasses()"
|
|
6232
|
-
(click)="close()"
|
|
6233
|
-
>
|
|
6234
|
-
Done
|
|
6235
|
-
</button>
|
|
6236
|
-
}
|
|
6237
|
-
</div>
|
|
6238
|
-
}
|
|
6239
|
-
</div>
|
|
6240
|
-
</ng-template>
|
|
6241
|
-
|
|
6242
|
-
<!-- Live announcer region -->
|
|
6243
|
-
<div class="sr-only" aria-live="polite" aria-atomic="true">
|
|
6244
|
-
{{ liveAnnouncement() }}
|
|
6245
|
-
</div>
|
|
6246
|
-
`, isInline: true, styles: [".sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n"], dependencies: [{ kind: "ngmodule", type: OverlayModule }, { kind: "ngmodule", type: A11yModule }, { kind: "directive", type: i1.CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "component", type: ComCalendar, selector: "com-calendar", inputs: ["activeDate", "selected", "minDate", "maxDate", "dateFilter", "dateClass", "bordered", "startView", "firstDayOfWeek", "monthColumns", "cellTemplate"], outputs: ["selectedChange", "viewChanged", "activeDateChange"] }, { kind: "component", type: ComIcon, selector: "com-icon", inputs: ["name", "img", "color", "size", "strokeWidth", "absoluteStrokeWidth", "ariaLabel"] }, { kind: "component", type: ComTimePicker, selector: "com-time-picker", inputs: ["value", "disabled", "required", "showSeconds", "use12HourFormat", "minuteStep", "secondStep", "minTime", "maxTime", "variant", "size", "state", "ariaLabel", "class", "placeholder", "errorStateMatcher", "touched", "invalid", "errors"], outputs: ["valueChange", "disabledChange", "touchedChange", "timeChange"], exportAs: ["comTimePicker"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
6247
|
-
}
|
|
6248
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: ComDateRangePicker, decorators: [{
|
|
6249
|
-
type: Component,
|
|
6250
|
-
args: [{ selector: 'com-date-range-picker', exportAs: 'comDateRangePicker', template: `
|
|
6251
|
-
<!-- Trigger container -->
|
|
6252
|
-
<div
|
|
6253
|
-
#triggerElement
|
|
6254
|
-
[class]="triggerClasses()"
|
|
6255
|
-
[attr.aria-expanded]="isOpen()"
|
|
6256
|
-
[attr.aria-haspopup]="'dialog'"
|
|
6257
|
-
[attr.aria-owns]="panelId()"
|
|
6258
|
-
[attr.aria-disabled]="disabled() || null"
|
|
6259
|
-
(click)="onTriggerClick()"
|
|
6260
|
-
(keydown)="onTriggerKeydown($event)"
|
|
6261
|
-
>
|
|
6262
|
-
<!-- Start date input -->
|
|
6263
|
-
<input
|
|
6264
|
-
#startInputElement
|
|
6265
|
-
type="text"
|
|
6266
|
-
[class]="inputClasses()"
|
|
6267
|
-
[value]="startDisplayValue()"
|
|
6268
|
-
[placeholder]="startPlaceholder()"
|
|
6269
|
-
[disabled]="disabled()"
|
|
6270
|
-
[readonly]="!allowManualInput()"
|
|
6271
|
-
[attr.id]="startInputId()"
|
|
6272
|
-
[attr.aria-label]="startAriaLabel() || startPlaceholder()"
|
|
6273
|
-
[attr.aria-describedby]="effectiveAriaDescribedBy() || null"
|
|
6274
|
-
[attr.aria-invalid]="effectiveState() === 'error' || null"
|
|
6275
|
-
[attr.aria-required]="required() || null"
|
|
6276
|
-
(focus)="onStartInputFocus()"
|
|
6277
|
-
(input)="onStartInputChange($event)"
|
|
6278
|
-
(blur)="onStartInputBlur()"
|
|
6279
|
-
(keydown)="onInputKeydown($event, 'start')"
|
|
6280
|
-
/>
|
|
6281
|
-
|
|
6282
|
-
<!-- Range separator -->
|
|
6283
|
-
<span [class]="separatorClasses()">
|
|
6284
|
-
<com-icon name="arrow-right" [size]="iconSize()" />
|
|
6285
|
-
</span>
|
|
6286
|
-
|
|
6287
|
-
<!-- End date input -->
|
|
6288
|
-
<input
|
|
6289
|
-
#endInputElement
|
|
6290
|
-
type="text"
|
|
6291
|
-
[class]="inputClasses()"
|
|
6292
|
-
[value]="endDisplayValue()"
|
|
6293
|
-
[placeholder]="endPlaceholder()"
|
|
6294
|
-
[disabled]="disabled()"
|
|
6295
|
-
[readonly]="!allowManualInput()"
|
|
6296
|
-
[attr.id]="endInputId()"
|
|
6297
|
-
[attr.aria-label]="endAriaLabel() || endPlaceholder()"
|
|
6298
|
-
[attr.aria-invalid]="effectiveState() === 'error' || null"
|
|
6299
|
-
(focus)="onEndInputFocus()"
|
|
6300
|
-
(input)="onEndInputChange($event)"
|
|
6301
|
-
(blur)="onEndInputBlur()"
|
|
6302
|
-
(keydown)="onInputKeydown($event, 'end')"
|
|
6303
|
-
/>
|
|
6304
|
-
|
|
6305
|
-
<!-- Clear button -->
|
|
6306
|
-
@if (showClearButton() && hasValue() && !disabled()) {
|
|
6307
|
-
<button
|
|
6308
|
-
type="button"
|
|
6309
|
-
[class]="clearClasses()"
|
|
6310
|
-
[attr.aria-label]="'Clear date range'"
|
|
6311
|
-
(click)="clear($event)"
|
|
6312
|
-
>
|
|
6313
|
-
<com-icon name="x" [size]="iconSize()" />
|
|
6314
|
-
</button>
|
|
6315
|
-
}
|
|
6316
|
-
|
|
6317
|
-
<!-- Calendar icon -->
|
|
6318
|
-
<button
|
|
6319
|
-
type="button"
|
|
6320
|
-
[class]="iconClasses()"
|
|
6321
|
-
[attr.aria-label]="isOpen() ? 'Close calendar' : 'Open calendar'"
|
|
6322
|
-
[disabled]="disabled()"
|
|
6323
|
-
tabindex="-1"
|
|
6324
|
-
>
|
|
6325
|
-
<com-icon name="calendar" [size]="iconSize()" />
|
|
6326
|
-
</button>
|
|
6327
|
-
</div>
|
|
6328
|
-
|
|
6329
|
-
<!-- Panel template (rendered in overlay) -->
|
|
6330
|
-
<ng-template #panelTemplate>
|
|
6331
|
-
<div
|
|
6332
|
-
[class]="panelClasses()"
|
|
6333
|
-
[attr.id]="panelId()"
|
|
6334
|
-
role="dialog"
|
|
6335
|
-
aria-modal="true"
|
|
6336
|
-
[attr.aria-label]="'Choose date range'"
|
|
6337
|
-
(keydown)="onPanelKeydown($event)"
|
|
6338
|
-
cdkTrapFocus
|
|
6339
|
-
[cdkTrapFocusAutoCapture]="true"
|
|
6340
|
-
>
|
|
6341
|
-
<com-calendar
|
|
6342
|
-
[activeDate]="calendarActiveDate()"
|
|
6343
|
-
[selected]="calendarSelection()"
|
|
6344
|
-
[minDate]="min()"
|
|
6345
|
-
[maxDate]="max()"
|
|
6346
|
-
[dateFilter]="dateFilter()"
|
|
6347
|
-
[startView]="startView()"
|
|
6348
|
-
[firstDayOfWeek]="firstDayOfWeek()"
|
|
6349
|
-
[monthColumns]="2"
|
|
6350
|
-
[bordered]="false"
|
|
6351
|
-
(selectedChange)="onCalendarSelectionChange($event)"
|
|
6352
|
-
(activeDateChange)="onActiveDateChange($event)"
|
|
6353
|
-
/>
|
|
6354
|
-
|
|
6355
|
-
@if (showTimePicker()) {
|
|
6356
|
-
<div [class]="timeSectionClasses()">
|
|
6357
|
-
<div class="flex items-center gap-3">
|
|
6358
|
-
<div class="flex flex-col gap-1">
|
|
6359
|
-
<span [class]="timeLabelClasses()">Start time</span>
|
|
6360
|
-
<com-time-picker
|
|
6361
|
-
variant="embedded"
|
|
6362
|
-
[size]="size()"
|
|
6363
|
-
[value]="startTimeValue()"
|
|
6364
|
-
[use12HourFormat]="use12HourFormat()"
|
|
6365
|
-
[showSeconds]="showSeconds()"
|
|
6366
|
-
[minuteStep]="minuteStep()"
|
|
6367
|
-
[disabled]="disabled()"
|
|
6368
|
-
ariaLabel="Start time"
|
|
6369
|
-
(timeChange)="onStartTimeChange($event)"
|
|
6370
|
-
/>
|
|
6371
|
-
</div>
|
|
6372
|
-
<span class="text-muted-foreground mt-5">
|
|
6373
|
-
<com-icon name="arrow-right" [size]="iconSize()" />
|
|
6374
|
-
</span>
|
|
6375
|
-
<div class="flex flex-col gap-1">
|
|
6376
|
-
<span [class]="timeLabelClasses()">End time</span>
|
|
6377
|
-
<com-time-picker
|
|
6378
|
-
variant="embedded"
|
|
6379
|
-
[size]="size()"
|
|
6380
|
-
[value]="endTimeValue()"
|
|
6381
|
-
[use12HourFormat]="use12HourFormat()"
|
|
6382
|
-
[showSeconds]="showSeconds()"
|
|
6383
|
-
[minuteStep]="minuteStep()"
|
|
6384
|
-
[disabled]="disabled()"
|
|
6385
|
-
ariaLabel="End time"
|
|
6386
|
-
(timeChange)="onEndTimeChange($event)"
|
|
6387
|
-
/>
|
|
6388
|
-
</div>
|
|
6389
|
-
</div>
|
|
6390
|
-
</div>
|
|
6391
|
-
}
|
|
6392
|
-
|
|
6393
|
-
@if (showTodayButton() || showFooterClearButton() || showTimePicker()) {
|
|
6394
|
-
<div [class]="footerClasses()">
|
|
6395
|
-
@if (showTodayButton()) {
|
|
6396
|
-
<button
|
|
6397
|
-
type="button"
|
|
6398
|
-
[class]="todayButtonClasses()"
|
|
6399
|
-
(click)="selectToday()"
|
|
6400
|
-
>
|
|
6401
|
-
Today
|
|
6402
|
-
</button>
|
|
6403
|
-
}
|
|
6404
|
-
@if (showFooterClearButton()) {
|
|
6405
|
-
<button
|
|
6406
|
-
type="button"
|
|
6407
|
-
[class]="clearButtonClasses()"
|
|
6408
|
-
(click)="clear($event)"
|
|
6409
|
-
>
|
|
6410
|
-
Clear
|
|
6411
|
-
</button>
|
|
6412
|
-
}
|
|
6413
|
-
@if (showTimePicker()) {
|
|
6414
|
-
<button
|
|
6415
|
-
type="button"
|
|
6416
|
-
[class]="todayButtonClasses()"
|
|
6417
|
-
(click)="close()"
|
|
6418
|
-
>
|
|
6419
|
-
Done
|
|
6420
|
-
</button>
|
|
6421
|
-
}
|
|
6422
|
-
</div>
|
|
6423
|
-
}
|
|
6424
|
-
</div>
|
|
6425
|
-
</ng-template>
|
|
6426
|
-
|
|
6427
|
-
<!-- Live announcer region -->
|
|
6428
|
-
<div class="sr-only" aria-live="polite" aria-atomic="true">
|
|
6429
|
-
{{ liveAnnouncement() }}
|
|
6430
|
-
</div>
|
|
6431
|
-
`, imports: [
|
|
6432
|
-
OverlayModule,
|
|
6433
|
-
A11yModule,
|
|
6434
|
-
ComCalendar,
|
|
6435
|
-
ComIcon,
|
|
6436
|
-
ComTimePicker,
|
|
6437
|
-
], providers: [
|
|
6438
|
-
RangeSelectionStrategy,
|
|
6439
|
-
{ provide: CALENDAR_SELECTION_STRATEGY, useExisting: RangeSelectionStrategy },
|
|
6440
|
-
{ provide: FormFieldControl, useExisting: forwardRef(() => ComDateRangePicker) },
|
|
6441
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
6442
|
-
class: 'com-date-range-picker-host inline-block',
|
|
6443
|
-
'[class.com-date-range-picker-disabled]': 'disabled()',
|
|
6444
|
-
'[class.com-date-range-picker-open]': 'isOpen()',
|
|
6445
|
-
}, styles: [".sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n"] }]
|
|
6446
|
-
}], ctorParameters: () => [], propDecorators: { triggerRef: [{ type: i0.ViewChild, args: ['triggerElement', { isSignal: true }] }], startInputRef: [{ type: i0.ViewChild, args: ['startInputElement', { isSignal: true }] }], endInputRef: [{ type: i0.ViewChild, args: ['endInputElement', { isSignal: true }] }], panelTemplateRef: [{ type: i0.ViewChild, args: ['panelTemplate', { isSignal: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], dateFilter: [{ type: i0.Input, args: [{ isSignal: true, alias: "dateFilter", required: false }] }], startAt: [{ type: i0.Input, args: [{ isSignal: true, alias: "startAt", required: false }] }], startView: [{ type: i0.Input, args: [{ isSignal: true, alias: "startView", required: false }] }], firstDayOfWeek: [{ type: i0.Input, args: [{ isSignal: true, alias: "firstDayOfWeek", required: false }] }], startPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "startPlaceholder", required: false }] }], endPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "endPlaceholder", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], dateFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "dateFormat", required: false }] }], showClearButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "showClearButton", required: false }] }], showTodayButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "showTodayButton", required: false }] }], showFooterClearButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "showFooterClearButton", required: false }] }], keepOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "keepOpen", required: false }] }], allowManualInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowManualInput", required: false }] }], panelClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "panelClass", required: false }] }], panelWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "panelWidth", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], state: [{ type: i0.Input, args: [{ isSignal: true, alias: "state", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], startAriaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "startAriaLabel", required: false }] }], endAriaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "endAriaLabel", required: false }] }], errorStateMatcher: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorStateMatcher", required: false }] }], touched: [{ type: i0.Input, args: [{ isSignal: true, alias: "touched", required: false }] }, { type: i0.Output, args: ["touchedChange"] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], sfErrors: [{ type: i0.Input, args: [{ isSignal: true, alias: "errors", required: false }] }], showTimePicker: [{ type: i0.Input, args: [{ isSignal: true, alias: "showTimePicker", required: false }] }], use12HourFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "use12HourFormat", required: false }] }], showSeconds: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSeconds", required: false }] }], minuteStep: [{ type: i0.Input, args: [{ isSignal: true, alias: "minuteStep", required: false }] }], rangeChange: [{ type: i0.Output, args: ["rangeChange"] }], opened: [{ type: i0.Output, args: ["opened"] }], closed: [{ type: i0.Output, args: ["closed"] }] } });
|
|
6447
|
-
|
|
6448
|
-
/**
|
|
6449
|
-
* Datepicker components public API.
|
|
6450
|
-
*/
|
|
6451
|
-
|
|
6452
|
-
/**
|
|
6453
|
-
* TimePicker component public API.
|
|
6454
|
-
*/
|
|
6455
|
-
|
|
6456
3073
|
/**
|
|
6457
3074
|
* ngx-com/components/calendar
|
|
6458
3075
|
* Calendar component public API
|
|
@@ -6462,5 +3079,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImpor
|
|
|
6462
3079
|
* Generated bundle index. Do not edit.
|
|
6463
3080
|
*/
|
|
6464
3081
|
|
|
6465
|
-
export { CALENDAR_SELECTION_STRATEGY, CalendarSelectionStrategy, CalendarViewBase, ComCalendar, ComCalendarCell, ComCalendarHeader, ComCalendarMonthView, ComCalendarMultiYearView, ComCalendarYearView,
|
|
3082
|
+
export { CALENDAR_SELECTION_STRATEGY, CalendarSelectionStrategy, CalendarViewBase, ComCalendar, ComCalendarCell, ComCalendarHeader, ComCalendarMonthView, ComCalendarMultiYearView, ComCalendarYearView, DATE_ADAPTER, DAYS_PER_WEEK, DateAdapter, MONTHS_PER_ROW, MultiSelectionStrategy, NativeDateAdapter, RangeSelectionStrategy, SingleSelectionStrategy, WEEKS_PER_MONTH, WeekSelectionStrategy, YEARS_PER_PAGE, YEARS_PER_ROW, calendarCellVariants, calendarCellWrapperVariants, calendarHeaderButtonVariants, calendarHeaderVariants, calendarVariants, createCalendarCell, createDateRange, createGrid, getMultiYearStartingYear, getWeekdayHeaders, isDateDisabled, isMonthDisabled, isYearDisabled, navigateGrid, provideMultiSelectionStrategy, provideNativeDateAdapter, provideRangeSelectionStrategy, provideSingleSelectionStrategy, provideWeekSelectionStrategy };
|
|
6466
3083
|
//# sourceMappingURL=ngx-com-components-calendar.mjs.map
|