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