@radix-ng/primitives 0.34.0 → 0.36.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 (61) hide show
  1. package/calendar/src/calendar-root.directive.d.ts +3 -3
  2. package/core/index.d.ts +3 -0
  3. package/core/src/accessor/provide-value-accessor.d.ts +1 -1
  4. package/core/src/clamp.d.ts +38 -0
  5. package/core/src/date-time/index.d.ts +3 -0
  6. package/core/src/date-time/parser.d.ts +37 -0
  7. package/core/src/date-time/parts.d.ts +12 -0
  8. package/core/src/date-time/segment.d.ts +4 -0
  9. package/core/src/date-time/types.d.ts +18 -1
  10. package/core/src/date-time/useDateField.d.ts +141 -0
  11. package/core/src/date-time/utils.d.ts +3 -0
  12. package/core/src/getActiveElement.d.ts +1 -0
  13. package/core/src/provide-token.d.ts +22 -0
  14. package/date-field/README.md +1 -0
  15. package/date-field/index.d.ts +11 -0
  16. package/date-field/src/date-field-context.token.d.ts +18 -0
  17. package/date-field/src/date-field-input.directive.d.ts +53 -0
  18. package/date-field/src/date-field-root.directive.d.ts +126 -0
  19. package/dialog/src/dialog-ref.d.ts +3 -0
  20. package/dialog/src/dialog.config.d.ts +1 -0
  21. package/fesm2022/radix-ng-primitives-calendar.mjs +6 -15
  22. package/fesm2022/radix-ng-primitives-calendar.mjs.map +1 -1
  23. package/fesm2022/radix-ng-primitives-checkbox.mjs +3 -4
  24. package/fesm2022/radix-ng-primitives-checkbox.mjs.map +1 -1
  25. package/fesm2022/radix-ng-primitives-core.mjs +1097 -8
  26. package/fesm2022/radix-ng-primitives-core.mjs.map +1 -1
  27. package/fesm2022/radix-ng-primitives-date-field.mjs +334 -0
  28. package/fesm2022/radix-ng-primitives-date-field.mjs.map +1 -0
  29. package/fesm2022/radix-ng-primitives-dialog.mjs +54 -4
  30. package/fesm2022/radix-ng-primitives-dialog.mjs.map +1 -1
  31. package/fesm2022/radix-ng-primitives-navigation-menu.mjs +0 -2
  32. package/fesm2022/radix-ng-primitives-navigation-menu.mjs.map +1 -1
  33. package/fesm2022/radix-ng-primitives-number-field.mjs +502 -0
  34. package/fesm2022/radix-ng-primitives-number-field.mjs.map +1 -0
  35. package/fesm2022/radix-ng-primitives-progress.mjs +3 -3
  36. package/fesm2022/radix-ng-primitives-progress.mjs.map +1 -1
  37. package/fesm2022/radix-ng-primitives-radio.mjs +7 -7
  38. package/fesm2022/radix-ng-primitives-radio.mjs.map +1 -1
  39. package/fesm2022/radix-ng-primitives-switch.mjs +7 -15
  40. package/fesm2022/radix-ng-primitives-switch.mjs.map +1 -1
  41. package/fesm2022/radix-ng-primitives-tabs.mjs +3 -6
  42. package/fesm2022/radix-ng-primitives-tabs.mjs.map +1 -1
  43. package/fesm2022/radix-ng-primitives-toggle-group.mjs +8 -10
  44. package/fesm2022/radix-ng-primitives-toggle-group.mjs.map +1 -1
  45. package/fesm2022/radix-ng-primitives-toggle.mjs +5 -12
  46. package/fesm2022/radix-ng-primitives-toggle.mjs.map +1 -1
  47. package/hover-card/src/hover-card-root.directive.d.ts +4 -4
  48. package/number-field/README.md +1 -0
  49. package/number-field/index.d.ts +17 -0
  50. package/number-field/src/number-field-context.token.d.ts +24 -0
  51. package/number-field/src/number-field-decrement.directive.d.ts +23 -0
  52. package/number-field/src/number-field-increment.directive.d.ts +23 -0
  53. package/number-field/src/number-field-input.directive.d.ts +22 -0
  54. package/number-field/src/number-field-root.directive.d.ts +86 -0
  55. package/number-field/src/types.d.ts +1 -0
  56. package/number-field/src/utils.d.ts +18 -0
  57. package/package.json +9 -1
  58. package/popover/src/popover-root.directive.d.ts +4 -4
  59. package/switch/src/switch-root.directive.d.ts +0 -1
  60. package/toggle/src/toggle.directive.d.ts +0 -1
  61. package/tooltip/src/tooltip-root.directive.d.ts +4 -4
@@ -1,6 +1,6 @@
1
- import { NG_VALUE_ACCESSOR, NgControl, FormControlDirective, FormControlName, NgModel } from '@angular/forms';
2
1
  import * as i0 from '@angular/core';
3
- import { inject, ElementRef, NgZone, booleanAttribute, Input, Directive, APP_ID, Injectable, InjectionToken, effect, untracked } from '@angular/core';
2
+ import { forwardRef, inject, ElementRef, NgZone, booleanAttribute, Input, Directive, APP_ID, Injectable, InjectionToken, computed, effect, untracked } from '@angular/core';
3
+ import { NG_VALUE_ACCESSOR, NgControl, FormControlDirective, FormControlName, NgModel } from '@angular/forms';
4
4
  import { DOCUMENT } from '@angular/common';
5
5
  import { Platform } from '@angular/cdk/platform';
6
6
  import { getLocalTimeZone, CalendarDateTime, ZonedDateTime, getDayOfWeek, DateFormatter, createCalendar, toCalendar, CalendarDate, startOfMonth, endOfMonth, today } from '@internationalized/date';
@@ -18,7 +18,7 @@ import { getLocalTimeZone, CalendarDateTime, ZonedDateTime, getDayOfWeek, DateFo
18
18
  function provideValueAccessor(type) {
19
19
  return {
20
20
  provide: NG_VALUE_ACCESSOR,
21
- useExisting: type,
21
+ useExisting: forwardRef(() => type),
22
22
  multi: true
23
23
  };
24
24
  }
@@ -101,6 +101,76 @@ function getRequestAnimationFrame() {
101
101
  // Get the requestAnimationFrame function or its polyfill.
102
102
  const reqAnimationFrame = getRequestAnimationFrame();
103
103
 
104
+ /**
105
+ * The `clamp` function restricts a number within a specified range by returning the value itself if it
106
+ * falls within the range, or the closest boundary value if it exceeds the range.
107
+ * @param {number} value - The `value` parameter represents the number that you want to clamp within
108
+ * the specified range defined by `min` and `max` values.
109
+ * @param {number} min - If the `value` parameter is less than the `min` value, the
110
+ * function will return the `min` value.
111
+ * @param {number} max - If the `value` parameter is greater than the `max` value,
112
+ * the function will return `max`.
113
+ * @returns The `clamp` function returns the value of `value` constrained within the range defined by
114
+ * `min` and `max`.
115
+ */
116
+ function clamp(value, min = Number.NEGATIVE_INFINITY, max = Number.POSITIVE_INFINITY) {
117
+ return Math.min(max, Math.max(min, value));
118
+ }
119
+ /**
120
+ * The function `roundToStepPrecision` rounds a number to a specified precision step.
121
+ * @param {number} value - The `value` parameter is the number that you want to round to a specific
122
+ * precision based on the `step` parameter.
123
+ * @param {number} step - The `step` parameter in the `roundToStepPrecision` function represents the
124
+ * interval at which you want to round the `value`. For example, if `step` is 0.5, the `value` will be
125
+ * rounded to the nearest half.
126
+ * @returns the `roundedValue` after rounding it to the precision specified by the `step`.
127
+ */
128
+ function roundToStepPrecision(value, step) {
129
+ let roundedValue = value;
130
+ const stepString = step.toString();
131
+ const pointIndex = stepString.indexOf('.');
132
+ const precision = pointIndex >= 0 ? stepString.length - pointIndex : 0;
133
+ if (precision > 0) {
134
+ const pow = 10 ** precision;
135
+ roundedValue = Math.round(roundedValue * pow) / pow;
136
+ }
137
+ return roundedValue;
138
+ }
139
+ /**
140
+ * The function `snapValueToStep` snaps a given value to the nearest step within a specified range.
141
+ * @param {number} value - The `value` parameter represents the number that you want to snap to the
142
+ * nearest step value.
143
+ * @param {number | undefined} min - The `min` parameter represents the minimum value that the `value`
144
+ * should be snapped to. If `value` is less than `min`, it will be snapped to `min`. If `min` is not
145
+ * provided (undefined), then the snapping will not consider a minimum value.
146
+ * @param {number | undefined} max - The `max` parameter represents the maximum value that the `value`
147
+ * should be snapped to. It ensures that the snapped value does not exceed this maximum value.
148
+ * @param {number} step - The `step` parameter in the `snapValueToStep` function represents the
149
+ * interval at which the `value` should be snapped to. It determines the granularity of the snapping
150
+ * operation. For example, if `step` is 5, the `value` will be snapped to the nearest multiple of
151
+ * @returns a number that has been snapped to the nearest step value within the specified range of minimum and maximum values.
152
+ */
153
+ function snapValueToStep(value, min, max, step) {
154
+ min = Number(min);
155
+ max = Number(max);
156
+ const remainder = (value - (Number.isNaN(min) ? 0 : min)) % step;
157
+ let snappedValue = roundToStepPrecision(Math.abs(remainder) * 2 >= step
158
+ ? value + Math.sign(remainder) * (step - Math.abs(remainder))
159
+ : value - remainder, step);
160
+ if (!Number.isNaN(min)) {
161
+ if (snappedValue < min)
162
+ snappedValue = min;
163
+ else if (!Number.isNaN(max) && snappedValue > max)
164
+ snappedValue = min + Math.floor(roundToStepPrecision((max - min) / step, step)) * step;
165
+ }
166
+ else if (!Number.isNaN(max) && snappedValue > max) {
167
+ snappedValue = Math.floor(roundToStepPrecision(max / step, step)) * step;
168
+ }
169
+ // correct floating point behavior by rounding to step precision
170
+ snappedValue = roundToStepPrecision(snappedValue, step);
171
+ return snappedValue;
172
+ }
173
+
104
174
  function injectDocument() {
105
175
  return inject(DOCUMENT);
106
176
  }
@@ -124,6 +194,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImpor
124
194
  }]
125
195
  }] });
126
196
 
197
+ function getActiveElement() {
198
+ let activeElement = document.activeElement;
199
+ if (activeElement == null) {
200
+ return null;
201
+ }
202
+ while (activeElement != null &&
203
+ activeElement.shadowRoot != null &&
204
+ activeElement.shadowRoot.activeElement != null) {
205
+ activeElement = activeElement.shadowRoot.activeElement;
206
+ }
207
+ return activeElement;
208
+ }
209
+
127
210
  /**
128
211
  * @license
129
212
  * Copyright Google LLC All Rights Reserved.
@@ -239,6 +322,33 @@ const j = 'j';
239
322
  const k = 'k';
240
323
  const SPACE_CODE = 'Space';
241
324
 
325
+ /**
326
+ * Creates an Angular provider that binds the given token to the existing instance
327
+ * of the specified class. This is especially useful when you want multiple
328
+ * tokens (or interfaces) to resolve to the same directive/component instance.
329
+ *
330
+ * @template T - The type associated with the injection token.
331
+ * @param token - The InjectionToken or abstract type you want to provide.
332
+ * @param type - The class type whose existing instance will be used for this token.
333
+ * @returns A Provider configuration object for Angular's DI system.
334
+ *
335
+ * @example
336
+ *
337
+ * @Directive({
338
+ * providers: [
339
+ * provideToken(RdxToggleGroupToken, RdxToggleGroupDirective),
340
+ * provideValueAccessor(RdxToggleGroupDirective)
341
+ * ]
342
+ * })
343
+ * export class RdxToggleGroupDirective {}
344
+ */
345
+ function provideToken(token, type) {
346
+ return {
347
+ provide: token,
348
+ useExisting: forwardRef(() => type)
349
+ };
350
+ }
351
+
242
352
  const WINDOW = new InjectionToken('An abstraction over global window object', {
243
353
  factory: () => {
244
354
  const { defaultView } = injectDocument();
@@ -606,6 +716,32 @@ function createFormatter(initialLocale) {
606
716
  };
607
717
  }
608
718
 
719
+ /*
720
+ * Implementation ported from https://github.com/melt-ui/melt-ui/blob/develop/src/lib/builders/date-field/_internal/helpers.ts
721
+ */
722
+ const DATE_SEGMENT_PARTS = ['day', 'month', 'year'];
723
+ const TIME_SEGMENT_PARTS = ['hour', 'minute', 'second', 'dayPeriod'];
724
+ const NON_EDITABLE_SEGMENT_PARTS = ['literal', 'timeZoneName'];
725
+ const EDITABLE_SEGMENT_PARTS = [...DATE_SEGMENT_PARTS, ...TIME_SEGMENT_PARTS];
726
+ const EDITABLE_TIME_SEGMENT_PARTS = [...TIME_SEGMENT_PARTS];
727
+ const ALL_SEGMENT_PARTS = [
728
+ ...EDITABLE_SEGMENT_PARTS,
729
+ ...NON_EDITABLE_SEGMENT_PARTS
730
+ ];
731
+ const ALL_EXCEPT_LITERAL_PARTS = ALL_SEGMENT_PARTS.filter((part) => part !== 'literal');
732
+ function isDateSegmentPart(part) {
733
+ return DATE_SEGMENT_PARTS.includes(part);
734
+ }
735
+ function isTimeSegmentPart(part) {
736
+ return TIME_SEGMENT_PARTS.includes(part);
737
+ }
738
+ function isSegmentPart(part) {
739
+ return EDITABLE_SEGMENT_PARTS.includes(part);
740
+ }
741
+ function isAnySegmentPart(part) {
742
+ return ALL_SEGMENT_PARTS.includes(part);
743
+ }
744
+
609
745
  /*
610
746
  * Implementation ported from from from https://github.com/melt-ui/melt-ui/blob/develop/src/lib/internal/helpers/date/placeholders.ts
611
747
  */
@@ -739,10 +875,37 @@ function getLocaleLanguage(locale) {
739
875
  return locale.split('-')[0];
740
876
  }
741
877
 
742
- /*
743
- * Implementation ported from https://github.com/melt-ui/melt-ui/blob/develop/src/lib/internal/helpers/date/types.ts
744
- */
745
-
878
+ function getOptsByGranularity(granularity, hourCycle, isTimeValue = false) {
879
+ const opts = {
880
+ year: 'numeric',
881
+ month: '2-digit',
882
+ day: '2-digit',
883
+ hour: '2-digit',
884
+ minute: '2-digit',
885
+ second: '2-digit',
886
+ timeZoneName: 'short',
887
+ hourCycle: hourCycle === 24 ? 'h23' : undefined,
888
+ hour12: hourCycle === 24 ? false : undefined
889
+ };
890
+ if (isTimeValue) {
891
+ delete opts.year;
892
+ delete opts.month;
893
+ delete opts.day;
894
+ }
895
+ if (granularity === 'day') {
896
+ delete opts.second;
897
+ delete opts.hour;
898
+ delete opts.minute;
899
+ delete opts.timeZoneName;
900
+ }
901
+ if (granularity === 'hour') {
902
+ delete opts.minute;
903
+ delete opts.second;
904
+ }
905
+ if (granularity === 'minute')
906
+ delete opts.second;
907
+ return opts;
908
+ }
746
909
  function handleCalendarInitialFocus(calendar) {
747
910
  const selectedDay = calendar.querySelector('[data-selected]');
748
911
  if (selectedDay)
@@ -755,6 +918,932 @@ function handleCalendarInitialFocus(calendar) {
755
918
  return firstDay.focus();
756
919
  }
757
920
 
921
+ const calendarDateTimeGranularities = ['hour', 'minute', 'second'];
922
+ function syncTimeSegmentValues(props) {
923
+ return Object.fromEntries(TIME_SEGMENT_PARTS.map((part) => {
924
+ if (part === 'dayPeriod')
925
+ return [part, props.formatter.dayPeriod(toDate(props.value))];
926
+ return [part, props.value[part]];
927
+ }));
928
+ }
929
+ function initializeSegmentValues(granularity) {
930
+ const initialParts = EDITABLE_SEGMENT_PARTS.map((part) => {
931
+ if (part === 'dayPeriod')
932
+ return [part, 'AM'];
933
+ return [part, null];
934
+ }).filter(([key]) => {
935
+ if (key === 'literal' || key === null)
936
+ return false;
937
+ if (granularity === 'minute' && key === 'second')
938
+ return false;
939
+ if (granularity === 'hour' && (key === 'second' || key === 'minute'))
940
+ return false;
941
+ if (granularity === 'day')
942
+ return !calendarDateTimeGranularities.includes(key) && key !== 'dayPeriod';
943
+ else
944
+ return true;
945
+ });
946
+ return Object.fromEntries(initialParts);
947
+ }
948
+ function syncSegmentValues(props) {
949
+ const { formatter } = props;
950
+ const dateValues = DATE_SEGMENT_PARTS.map((part) => {
951
+ return [part, props.value[part]];
952
+ });
953
+ if ('hour' in props.value) {
954
+ const timeValues = syncTimeSegmentValues({ value: props.value, formatter });
955
+ return { ...Object.fromEntries(dateValues), ...timeValues };
956
+ }
957
+ return Object.fromEntries(dateValues);
958
+ }
959
+ function createContentObj(props) {
960
+ const { segmentValues, formatter, locale } = props;
961
+ function getPartContent(part) {
962
+ if ('hour' in segmentValues) {
963
+ const value = segmentValues[part];
964
+ if (value !== null) {
965
+ /**
966
+ * Edge case for when the month field is filled and the day field snaps to the maximum value of the value of the placeholder month
967
+ */
968
+ if (part === 'day' && segmentValues.month !== null) {
969
+ return formatter.part(props.dateRef.set({ [part]: value, month: segmentValues.month }), part, {
970
+ hourCycle: props.hourCycle === 24 ? 'h23' : undefined
971
+ });
972
+ }
973
+ return formatter.part(props.dateRef.set({ [part]: value }), part, {
974
+ hourCycle: props.hourCycle === 24 ? 'h23' : undefined
975
+ });
976
+ }
977
+ else {
978
+ return getPlaceholder(part, '', locale());
979
+ }
980
+ }
981
+ else {
982
+ if (isDateSegmentPart(part)) {
983
+ const value = segmentValues[part];
984
+ if (value !== null) {
985
+ if (part === 'day' && segmentValues.month !== null)
986
+ /**
987
+ * As described above, same function
988
+ */
989
+ return formatter.part(props.dateRef.set({ [part]: value, month: segmentValues.month }), part);
990
+ return formatter.part(props.dateRef.set({ [part]: value }), part);
991
+ }
992
+ else {
993
+ return getPlaceholder(part, '', locale());
994
+ }
995
+ }
996
+ return '';
997
+ }
998
+ }
999
+ const content = Object.keys(segmentValues).reduce((obj, part) => {
1000
+ if (!isSegmentPart(part))
1001
+ return obj;
1002
+ if ('hour' in segmentValues && part === 'dayPeriod') {
1003
+ const value = segmentValues[part];
1004
+ if (value !== null)
1005
+ obj[part] = value;
1006
+ else
1007
+ obj[part] = getPlaceholder(part, 'AM', locale());
1008
+ }
1009
+ else {
1010
+ obj[part] = getPartContent(part);
1011
+ }
1012
+ return obj;
1013
+ }, {});
1014
+ return content;
1015
+ }
1016
+ function createContentArr(props) {
1017
+ const { granularity, formatter, contentObj, hideTimeZone, hourCycle, isTimeValue } = props;
1018
+ const parts = formatter.toParts(props.dateRef, getOptsByGranularity(granularity, hourCycle, isTimeValue));
1019
+ const segmentContentArr = parts
1020
+ .map((part) => {
1021
+ const defaultParts = ['literal', 'timeZoneName', null];
1022
+ if (defaultParts.includes(part.type) || !isSegmentPart(part.type)) {
1023
+ return {
1024
+ part: part.type,
1025
+ value: part.value
1026
+ };
1027
+ }
1028
+ return {
1029
+ part: part.type,
1030
+ value: contentObj[part.type]
1031
+ };
1032
+ })
1033
+ .filter((segment) => {
1034
+ if (segment.part === null || segment.value === null)
1035
+ return false;
1036
+ if (segment.part === 'timeZoneName' && (!isZonedDateTime(props.dateRef) || hideTimeZone))
1037
+ return false;
1038
+ return true;
1039
+ });
1040
+ return segmentContentArr;
1041
+ }
1042
+ function createContent(props) {
1043
+ const contentObj = createContentObj(props);
1044
+ const contentArr = createContentArr({
1045
+ contentObj,
1046
+ ...props
1047
+ });
1048
+ return {
1049
+ obj: contentObj,
1050
+ arr: contentArr
1051
+ };
1052
+ }
1053
+
1054
+ function isSegmentNavigationKey(key) {
1055
+ if (key === ARROW_RIGHT || key === ARROW_LEFT)
1056
+ return true;
1057
+ return false;
1058
+ }
1059
+ function isNumberString(value) {
1060
+ if (Number.isNaN(Number.parseInt(value)))
1061
+ return false;
1062
+ return true;
1063
+ }
1064
+ function isAcceptableSegmentKey(key) {
1065
+ const acceptableSegmentKeys = [
1066
+ ENTER,
1067
+ ARROW_UP,
1068
+ ARROW_DOWN,
1069
+ ARROW_LEFT,
1070
+ ARROW_RIGHT,
1071
+ BACKSPACE,
1072
+ SPACE,
1073
+ 'a',
1074
+ 'A',
1075
+ 'p',
1076
+ 'P'
1077
+ ];
1078
+ if (acceptableSegmentKeys.includes(key))
1079
+ return true;
1080
+ if (isNumberString(key))
1081
+ return true;
1082
+ return false;
1083
+ }
1084
+ function getSegmentElements(parentElement) {
1085
+ return Array.from(parentElement.querySelectorAll('[data-rdx-date-field-segment]')).filter((item) => item.getAttribute('data-rdx-date-field-segment') !== 'literal');
1086
+ }
1087
+
1088
+ /*
1089
+ * Implementation ported from https://github.com/melt-ui/melt-ui/blob/develop/src/lib/internal/helpers/date/types.ts
1090
+ */
1091
+
1092
+ // https://github.com/unovue/reka-ui/blob/v2/packages/core/src/shared/date/useDateField.ts
1093
+ function commonSegmentAttrs(props) {
1094
+ return {
1095
+ role: 'spinbutton',
1096
+ contenteditable: true,
1097
+ tabindex: props.disabled ? undefined : 0,
1098
+ spellcheck: false,
1099
+ inputmode: 'numeric',
1100
+ autocorrect: 'off',
1101
+ enterkeyhint: 'next',
1102
+ style: 'caret-color: transparent;'
1103
+ };
1104
+ }
1105
+ function daySegmentAttrs(props) {
1106
+ const { segmentValues, placeholder } = props;
1107
+ const isEmpty = segmentValues.day === null;
1108
+ const date = segmentValues.day ? placeholder.set({ day: segmentValues.day }) : placeholder;
1109
+ const valueNow = date.day;
1110
+ const valueMin = 1;
1111
+ const valueMax = getDaysInMonth(date);
1112
+ const valueText = isEmpty ? 'Empty' : `${valueNow}`;
1113
+ return {
1114
+ ...commonSegmentAttrs(props),
1115
+ 'aria-label': 'day,',
1116
+ 'aria-valuemin': valueMin,
1117
+ 'aria-valuemax': valueMax,
1118
+ 'aria-valuenow': valueNow,
1119
+ 'aria-valuetext': valueText,
1120
+ 'data-placeholder': isEmpty ? '' : undefined
1121
+ };
1122
+ }
1123
+ function monthSegmentAttrs(props) {
1124
+ const { segmentValues, placeholder, formatter } = props;
1125
+ const isEmpty = segmentValues.month === null;
1126
+ const date = segmentValues.month ? placeholder.set({ month: segmentValues.month }) : placeholder;
1127
+ const valueNow = date.month;
1128
+ const valueMin = 1;
1129
+ const valueMax = 12;
1130
+ const valueText = isEmpty ? 'Empty' : `${valueNow} - ${formatter.fullMonth(toDate(date))}`;
1131
+ return {
1132
+ ...commonSegmentAttrs(props),
1133
+ 'aria-label': 'month, ',
1134
+ contenteditable: true,
1135
+ 'aria-valuemin': valueMin,
1136
+ 'aria-valuemax': valueMax,
1137
+ 'aria-valuenow': valueNow,
1138
+ 'aria-valuetext': valueText,
1139
+ 'data-placeholder': isEmpty ? '' : undefined
1140
+ };
1141
+ }
1142
+ function yearSegmentAttrs(props) {
1143
+ const { segmentValues, placeholder } = props;
1144
+ const isEmpty = segmentValues.year === null;
1145
+ const date = segmentValues.year ? placeholder.set({ year: segmentValues.year }) : placeholder;
1146
+ const valueMin = 1;
1147
+ const valueMax = 9999;
1148
+ const valueNow = date.year;
1149
+ const valueText = isEmpty ? 'Empty' : `${valueNow}`;
1150
+ return {
1151
+ ...commonSegmentAttrs(props),
1152
+ 'aria-label': 'year, ',
1153
+ 'aria-valuemin': valueMin,
1154
+ 'aria-valuemax': valueMax,
1155
+ 'aria-valuenow': valueNow,
1156
+ 'aria-valuetext': valueText,
1157
+ 'data-placeholder': isEmpty ? '' : undefined
1158
+ };
1159
+ }
1160
+ function hourSegmentAttrs(props) {
1161
+ const { segmentValues, hourCycle, placeholder } = props;
1162
+ if (!('hour' in segmentValues) || !('hour' in placeholder))
1163
+ return {};
1164
+ const isEmpty = segmentValues.hour === null;
1165
+ const date = segmentValues.hour ? placeholder.set({ hour: segmentValues.hour }) : placeholder;
1166
+ const valueMin = hourCycle === 12 ? 1 : 0;
1167
+ const valueMax = hourCycle === 12 ? 12 : 23;
1168
+ const valueNow = date.hour;
1169
+ const valueText = isEmpty ? 'Empty' : `${valueNow} ${segmentValues.dayPeriod ?? ''}`;
1170
+ return {
1171
+ ...commonSegmentAttrs(props),
1172
+ 'aria-label': 'hour, ',
1173
+ 'aria-valuemin': valueMin,
1174
+ 'aria-valuemax': valueMax,
1175
+ 'aria-valuenow': valueNow,
1176
+ 'aria-valuetext': valueText,
1177
+ 'data-placeholder': isEmpty ? '' : undefined
1178
+ };
1179
+ }
1180
+ function minuteSegmentAttrs(props) {
1181
+ const { segmentValues, placeholder } = props;
1182
+ if (!('minute' in segmentValues) || !('minute' in placeholder))
1183
+ return {};
1184
+ const isEmpty = segmentValues.minute === null;
1185
+ const date = segmentValues.minute ? placeholder.set({ minute: segmentValues.minute }) : placeholder;
1186
+ const valueNow = date.minute;
1187
+ const valueMin = 0;
1188
+ const valueMax = 59;
1189
+ const valueText = isEmpty ? 'Empty' : `${valueNow}`;
1190
+ return {
1191
+ ...commonSegmentAttrs(props),
1192
+ 'aria-label': 'minute, ',
1193
+ 'aria-valuemin': valueMin,
1194
+ 'aria-valuemax': valueMax,
1195
+ 'aria-valuenow': valueNow,
1196
+ 'aria-valuetext': valueText,
1197
+ 'data-placeholder': isEmpty ? '' : undefined
1198
+ };
1199
+ }
1200
+ function secondSegmentAttrs(props) {
1201
+ const { segmentValues, placeholder } = props;
1202
+ if (!('second' in segmentValues) || !('second' in placeholder))
1203
+ return {};
1204
+ const isEmpty = segmentValues.second === null;
1205
+ const date = segmentValues.second ? placeholder.set({ second: segmentValues.second }) : placeholder;
1206
+ const valueNow = date.second;
1207
+ const valueMin = 0;
1208
+ const valueMax = 59;
1209
+ const valueText = isEmpty ? 'Empty' : `${valueNow}`;
1210
+ return {
1211
+ ...commonSegmentAttrs(props),
1212
+ 'aria-label': 'second, ',
1213
+ 'aria-valuemin': valueMin,
1214
+ 'aria-valuemax': valueMax,
1215
+ 'aria-valuenow': valueNow,
1216
+ 'aria-valuetext': valueText,
1217
+ 'data-placeholder': isEmpty ? '' : undefined
1218
+ };
1219
+ }
1220
+ function dayPeriodSegmentAttrs(props) {
1221
+ const { segmentValues } = props;
1222
+ if (!('dayPeriod' in segmentValues))
1223
+ return {};
1224
+ const valueMin = 0;
1225
+ const valueMax = 12;
1226
+ const valueNow = segmentValues.hour ? (segmentValues.hour > 12 ? segmentValues.hour - 12 : segmentValues.hour) : 0;
1227
+ const valueText = segmentValues.dayPeriod ?? 'AM';
1228
+ return {
1229
+ ...commonSegmentAttrs(props),
1230
+ inputmode: 'text',
1231
+ 'aria-label': 'AM/PM',
1232
+ 'aria-valuemin': valueMin,
1233
+ 'aria-valuemax': valueMax,
1234
+ 'aria-valuenow': valueNow,
1235
+ 'aria-valuetext': valueText
1236
+ };
1237
+ }
1238
+ function literalSegmentAttrs(_props) {
1239
+ return {
1240
+ 'aria-hidden': true,
1241
+ 'data-segment': 'literal'
1242
+ };
1243
+ }
1244
+ function timeZoneSegmentAttrs(props) {
1245
+ return {
1246
+ role: 'textbox',
1247
+ 'aria-label': 'timezone, ',
1248
+ 'data-readonly': true,
1249
+ 'data-segment': 'timeZoneName',
1250
+ tabindex: props.disabled ? undefined : 0,
1251
+ style: 'caret-color: transparent;'
1252
+ };
1253
+ }
1254
+ function eraSegmentAttrs(props) {
1255
+ const { segmentValues, placeholder } = props;
1256
+ const valueMin = 0;
1257
+ const valueMax = 0;
1258
+ const valueNow = 0;
1259
+ const valueText = 'era' in segmentValues ? segmentValues.era : placeholder.era;
1260
+ return {
1261
+ ...commonSegmentAttrs(props),
1262
+ 'aria-label': 'era',
1263
+ 'aria-valuemin': valueMin,
1264
+ 'aria-valuemax': valueMax,
1265
+ 'aria-valuenow': valueNow,
1266
+ 'aria-valuetext': valueText
1267
+ };
1268
+ }
1269
+ const segmentBuilders = {
1270
+ day: {
1271
+ attrs: daySegmentAttrs
1272
+ },
1273
+ month: {
1274
+ attrs: monthSegmentAttrs
1275
+ },
1276
+ year: {
1277
+ attrs: yearSegmentAttrs
1278
+ },
1279
+ hour: {
1280
+ attrs: hourSegmentAttrs
1281
+ },
1282
+ minute: {
1283
+ attrs: minuteSegmentAttrs
1284
+ },
1285
+ second: {
1286
+ attrs: secondSegmentAttrs
1287
+ },
1288
+ dayPeriod: {
1289
+ attrs: dayPeriodSegmentAttrs
1290
+ },
1291
+ literal: {
1292
+ attrs: literalSegmentAttrs
1293
+ },
1294
+ timeZoneName: {
1295
+ attrs: timeZoneSegmentAttrs
1296
+ },
1297
+ era: {
1298
+ attrs: eraSegmentAttrs
1299
+ }
1300
+ };
1301
+ function useDateField(props) {
1302
+ function handleSegmentClick(e) {
1303
+ const disabled = props.disabled();
1304
+ if (disabled)
1305
+ e.preventDefault();
1306
+ }
1307
+ function deleteValue(prevValue) {
1308
+ props.hasLeftFocus.set(false);
1309
+ if (prevValue === null)
1310
+ return prevValue;
1311
+ const str = prevValue.toString();
1312
+ if (str.length === 1) {
1313
+ props.modelValue.set(undefined);
1314
+ return null;
1315
+ }
1316
+ return Number.parseInt(str.slice(0, -1));
1317
+ }
1318
+ function dateTimeValueIncrementation({ e, part, dateRef, prevValue, hourCycle }) {
1319
+ const sign = e.key === ARROW_UP ? 1 : -1;
1320
+ if (prevValue === null)
1321
+ return dateRef[part];
1322
+ if (part === 'hour' && 'hour' in dateRef) {
1323
+ const cycleArgs = [
1324
+ part,
1325
+ sign,
1326
+ { hourCycle }
1327
+ ];
1328
+ return dateRef.set({ [part]: prevValue }).cycle(...cycleArgs)[part];
1329
+ }
1330
+ const cycleArgs = [part, sign];
1331
+ if (part === 'day' && props.segmentValues().month !== null)
1332
+ return dateRef
1333
+ .set({ [part]: prevValue, month: props.segmentValues().month })
1334
+ .cycle(...cycleArgs)[part];
1335
+ return dateRef.set({ [part]: prevValue }).cycle(...cycleArgs)[part];
1336
+ }
1337
+ function updateDayOrMonth(max, num, prev) {
1338
+ let moveToNext = false;
1339
+ const maxStart = Math.floor(max / 10);
1340
+ /**
1341
+ * If the user has left the segment, we want to reset the
1342
+ * `prev` value so that we can start the segment over again
1343
+ * when the user types a number.
1344
+ */
1345
+ if (props.hasLeftFocus()) {
1346
+ props.hasLeftFocus.set(false);
1347
+ prev = null;
1348
+ }
1349
+ if (prev === null) {
1350
+ /**
1351
+ * If the user types a 0 as the first number, we want
1352
+ * to keep track of that so that when they type the next
1353
+ * number, we can move to the next segment.
1354
+ */
1355
+ if (num === 0) {
1356
+ props.lastKeyZero.set(true);
1357
+ return { value: null, moveToNext };
1358
+ }
1359
+ /**
1360
+ * If the last key was a 0, or if the first number is
1361
+ * greater than the max start digit (0-3 in most cases), then
1362
+ * we want to move to the next segment, since it's not possible
1363
+ * to continue typing a valid number in this segment.
1364
+ */
1365
+ if (props.lastKeyZero() || num > maxStart) {
1366
+ // move to next
1367
+ moveToNext = true;
1368
+ }
1369
+ props.lastKeyZero.set(false);
1370
+ /**
1371
+ * If none of the above conditions are met, then we can just
1372
+ * return the number as the segment value and continue typing
1373
+ * in this segment.
1374
+ */
1375
+ return { value: num, moveToNext };
1376
+ }
1377
+ /**
1378
+ * If the number of digits is 2, or if the total with the existing digit
1379
+ * and the pressed digit is greater than the maximum value for this
1380
+ * month, then we will reset the segment as if the user had pressed the
1381
+ * backspace key and then typed the number.
1382
+ */
1383
+ const digits = prev.toString().length;
1384
+ const total = Number.parseInt(prev.toString() + num.toString());
1385
+ /**
1386
+ * If the number of digits is 2, or if the total with the existing digit
1387
+ * and the pressed digit is greater than the maximum value for this
1388
+ * month, then we will reset the segment as if the user had pressed the
1389
+ * backspace key and then typed the number.
1390
+ */
1391
+ if (digits === 2 || total > max) {
1392
+ /**
1393
+ * As we're doing elsewhere, we're checking if the number is greater
1394
+ * than the max start digit (0-3 in most months), and if so, we're
1395
+ * going to move to the next segment.
1396
+ */
1397
+ if (num > maxStart || total > max) {
1398
+ // move to next
1399
+ moveToNext = true;
1400
+ }
1401
+ return { value: num, moveToNext };
1402
+ }
1403
+ // move to next
1404
+ moveToNext = true;
1405
+ return { value: total, moveToNext };
1406
+ }
1407
+ function updateYear(num, prev) {
1408
+ let moveToNext = false;
1409
+ /**
1410
+ * If the user has left the segment, we want to reset the
1411
+ * `prev` value so that we can start the segment over again
1412
+ * when the user types a number.
1413
+ */
1414
+ // probably not implement, kind of weird
1415
+ if (props.hasLeftFocus()) {
1416
+ props.hasLeftFocus.set(false);
1417
+ prev = null;
1418
+ }
1419
+ if (prev === null)
1420
+ return { value: num === 0 ? 1 : num, moveToNext };
1421
+ const str = prev.toString() + num.toString();
1422
+ if (str.length > 4)
1423
+ return { value: num === 0 ? 1 : num, moveToNext };
1424
+ if (str.length === 4)
1425
+ moveToNext = true;
1426
+ const int = Number.parseInt(str);
1427
+ return { value: int, moveToNext };
1428
+ }
1429
+ function updateHour(num, prev) {
1430
+ const max = 24;
1431
+ let moveToNext = false;
1432
+ const maxStart = Math.floor(max / 10);
1433
+ /**
1434
+ * If the user has left the segment, we want to reset the
1435
+ * `prev` value so that we can start the segment over again
1436
+ * when the user types a number.
1437
+ */
1438
+ // probably not implement, kind of weird
1439
+ if (props.hasLeftFocus()) {
1440
+ props.hasLeftFocus.set(false);
1441
+ prev = null;
1442
+ }
1443
+ if (prev === null) {
1444
+ /**
1445
+ * If the user types a 0 as the first number, we want
1446
+ * to keep track of that so that when they type the next
1447
+ * number, we can move to the next segment.
1448
+ */
1449
+ if (num === 0) {
1450
+ props.lastKeyZero.set(true);
1451
+ return { value: 0, moveToNext };
1452
+ }
1453
+ /**
1454
+ * If the last key was a 0, or if the first number is
1455
+ * greater than the max start digit (0-3 in most cases), then
1456
+ * we want to move to the next segment, since it's not possible
1457
+ * to continue typing a valid number in this segment.
1458
+ */
1459
+ if (props.lastKeyZero() || num > maxStart) {
1460
+ // move to next
1461
+ moveToNext = true;
1462
+ }
1463
+ props.lastKeyZero.set(false);
1464
+ /**
1465
+ * If none of the above conditions are met, then we can just
1466
+ * return the number as the segment value and continue typing
1467
+ * in this segment.
1468
+ */
1469
+ return { value: num, moveToNext };
1470
+ }
1471
+ /**
1472
+ * If the number of digits is 2, or if the total with the existing digit
1473
+ * and the pressed digit is greater than the maximum value for this
1474
+ * month, then we will reset the segment as if the user had pressed the
1475
+ * backspace key and then typed the number.
1476
+ */
1477
+ const digits = prev.toString().length;
1478
+ const total = Number.parseInt(prev.toString() + num.toString());
1479
+ /**
1480
+ * If the number of digits is 2, or if the total with the existing digit
1481
+ * and the pressed digit is greater than the maximum value for this
1482
+ * month, then we will reset the segment as if the user had pressed the
1483
+ * backspace key and then typed the number.
1484
+ */
1485
+ if (digits === 2 || total > max) {
1486
+ /**
1487
+ * As we're doing elsewhere, we're checking if the number is greater
1488
+ * than the max start digit (0-3 in most months), and if so, we're
1489
+ * going to move to the next segment.
1490
+ */
1491
+ if (num > maxStart) {
1492
+ // move to next
1493
+ moveToNext = true;
1494
+ }
1495
+ return { value: num, moveToNext };
1496
+ }
1497
+ // move to next
1498
+ moveToNext = true;
1499
+ return { value: total, moveToNext };
1500
+ }
1501
+ function updateMinuteOrSecond(num, prev) {
1502
+ const max = 59;
1503
+ let moveToNext = false;
1504
+ const maxStart = Math.floor(max / 10);
1505
+ /**
1506
+ * If the user has left the segment, we want to reset the
1507
+ * `prev` value so that we can start the segment over again
1508
+ * when the user types a number.
1509
+ */
1510
+ if (props.hasLeftFocus()) {
1511
+ props.hasLeftFocus.set(false);
1512
+ prev = null;
1513
+ }
1514
+ if (prev === null) {
1515
+ /**
1516
+ * If the user types a 0 as the first number, we want
1517
+ * to keep track of that so that when they type the next
1518
+ * number, we can move to the next segment.
1519
+ */
1520
+ if (num === 0) {
1521
+ props.lastKeyZero.set(true);
1522
+ return { value: 0, moveToNext };
1523
+ }
1524
+ /**
1525
+ * If the last key was a 0, or if the first number is
1526
+ * greater than the max start digit (0-3 in most cases), then
1527
+ * we want to move to the next segment, since it's not possible
1528
+ * to continue typing a valid number in this segment.
1529
+ */
1530
+ if (props.lastKeyZero() || num > maxStart) {
1531
+ // move to next
1532
+ moveToNext = true;
1533
+ }
1534
+ props.lastKeyZero.set(false);
1535
+ /**
1536
+ * If none of the above conditions are met, then we can just
1537
+ * return the number as the segment value and continue typing
1538
+ * in this segment.
1539
+ */
1540
+ return { value: num, moveToNext };
1541
+ }
1542
+ /**
1543
+ * If the number of digits is 2, or if the total with the existing digit
1544
+ * and the pressed digit is greater than the maximum value for this
1545
+ * month, then we will reset the segment as if the user had pressed the
1546
+ * backspace key and then typed the number.
1547
+ */
1548
+ const digits = prev.toString().length;
1549
+ const total = Number.parseInt(prev.toString() + num.toString());
1550
+ /**
1551
+ * If the number of digits is 2, or if the total with the existing digit
1552
+ * and the pressed digit is greater than the maximum value for this
1553
+ * month, then we will reset the segment as if the user had pressed the
1554
+ * backspace key and then typed the number.
1555
+ */
1556
+ if (digits === 2 || total > max) {
1557
+ /**
1558
+ * As we're doing elsewhere, we're checking if the number is greater
1559
+ * than the max start digit (0-3 in most months), and if so, we're
1560
+ * going to move to the next segment.
1561
+ */
1562
+ if (num > maxStart) {
1563
+ // move to next
1564
+ moveToNext = true;
1565
+ }
1566
+ return { value: num, moveToNext };
1567
+ }
1568
+ // move to next
1569
+ moveToNext = true;
1570
+ return { value: total, moveToNext };
1571
+ }
1572
+ function minuteSecondIncrementation({ e, part, dateRef, prevValue }) {
1573
+ const sign = e.key === ARROW_UP ? 1 : -1;
1574
+ const min = 0;
1575
+ const max = 59;
1576
+ if (prevValue === null)
1577
+ return sign > 0 ? min : max;
1578
+ const cycleArgs = [part, sign];
1579
+ return dateRef.set({ [part]: prevValue }).cycle(...cycleArgs)[part];
1580
+ }
1581
+ const attributes = computed(() => segmentBuilders[props.part]?.attrs({
1582
+ disabled: props.disabled(),
1583
+ placeholder: props.placeholder(),
1584
+ hourCycle: props.hourCycle,
1585
+ segmentValues: props.segmentValues(),
1586
+ formatter: props.formatter
1587
+ }) ?? {});
1588
+ function handleMonthSegmentKeydown(e) {
1589
+ if (!isAcceptableSegmentKey(e.key) || isSegmentNavigationKey(e.key))
1590
+ return;
1591
+ const prevValue = props.segmentValues().month;
1592
+ if (e.key === ARROW_DOWN || e.key === ARROW_UP) {
1593
+ props.segmentValues.update((prev) => ({
1594
+ ...prev,
1595
+ month: dateTimeValueIncrementation({
1596
+ e,
1597
+ part: 'month',
1598
+ dateRef: props.placeholder(),
1599
+ prevValue
1600
+ })
1601
+ }));
1602
+ return;
1603
+ }
1604
+ if (isNumberString(e.key)) {
1605
+ const num = Number.parseInt(e.key);
1606
+ const { value, moveToNext } = updateDayOrMonth(12, num, prevValue);
1607
+ props.segmentValues.update((prev) => ({ ...prev, month: value }));
1608
+ if (moveToNext)
1609
+ props.focusNext();
1610
+ }
1611
+ if (e.key === BACKSPACE) {
1612
+ props.hasLeftFocus.set(false);
1613
+ props.segmentValues.update((prev) => ({ ...prev, month: deleteValue(prevValue) }));
1614
+ }
1615
+ }
1616
+ function handleDaySegmentKeydown(e) {
1617
+ if (!isAcceptableSegmentKey(e.key) || isSegmentNavigationKey(e.key))
1618
+ return;
1619
+ const prevValue = props.segmentValues().day;
1620
+ if (e.key === ARROW_DOWN || e.key === ARROW_UP) {
1621
+ props.segmentValues.update((prev) => ({
1622
+ ...prev,
1623
+ day: dateTimeValueIncrementation({
1624
+ e,
1625
+ part: 'day',
1626
+ dateRef: props.placeholder(),
1627
+ prevValue
1628
+ })
1629
+ }));
1630
+ return;
1631
+ }
1632
+ if (isNumberString(e.key)) {
1633
+ const num = Number.parseInt(e.key);
1634
+ const segmentMonthValue = props.segmentValues().month;
1635
+ const daysInMonth = segmentMonthValue
1636
+ ? getDaysInMonth(props.placeholder().set({ month: segmentMonthValue }))
1637
+ : getDaysInMonth(props.placeholder());
1638
+ const { value, moveToNext } = updateDayOrMonth(daysInMonth, num, prevValue);
1639
+ props.segmentValues.update((prev) => ({ ...prev, day: value }));
1640
+ if (moveToNext)
1641
+ props.focusNext();
1642
+ }
1643
+ if (e.key === BACKSPACE) {
1644
+ props.hasLeftFocus.set(false);
1645
+ props.segmentValues.update((prev) => ({ ...prev, day: deleteValue(prevValue) }));
1646
+ }
1647
+ }
1648
+ function handleYearSegmentKeydown(e) {
1649
+ if (!isAcceptableSegmentKey(e.key) || isSegmentNavigationKey(e.key))
1650
+ return;
1651
+ const prevValue = props.segmentValues().year;
1652
+ if (e.key === ARROW_DOWN || e.key === ARROW_UP) {
1653
+ props.segmentValues.update((prev) => ({
1654
+ ...prev,
1655
+ year: dateTimeValueIncrementation({
1656
+ e,
1657
+ part: 'year',
1658
+ dateRef: props.placeholder(),
1659
+ prevValue
1660
+ })
1661
+ }));
1662
+ return;
1663
+ }
1664
+ if (isNumberString(e.key)) {
1665
+ const num = Number.parseInt(e.key);
1666
+ const { value, moveToNext } = updateYear(num, prevValue);
1667
+ props.segmentValues.update((prev) => ({ ...prev, year: value }));
1668
+ if (moveToNext)
1669
+ props.focusNext();
1670
+ }
1671
+ if (e.key === BACKSPACE) {
1672
+ props.hasLeftFocus.set(false);
1673
+ props.segmentValues.update((prev) => ({ ...prev, year: deleteValue(prevValue) }));
1674
+ }
1675
+ }
1676
+ function handleHourSegmentKeydown(e) {
1677
+ const dateRef = props.placeholder();
1678
+ const values = props.segmentValues();
1679
+ if (!isAcceptableSegmentKey(e.key) ||
1680
+ isSegmentNavigationKey(e.key) ||
1681
+ !('hour' in dateRef) ||
1682
+ !('hour' in values))
1683
+ return;
1684
+ const prevValue = values.hour;
1685
+ const hourCycle = props.hourCycle;
1686
+ if (e.key === ARROW_UP || e.key === ARROW_DOWN) {
1687
+ props.segmentValues.update((prev) => ({
1688
+ ...prev,
1689
+ hour: dateTimeValueIncrementation({
1690
+ e,
1691
+ part: 'hour',
1692
+ dateRef: props.placeholder(),
1693
+ prevValue,
1694
+ hourCycle
1695
+ })
1696
+ }));
1697
+ if ('dayPeriod' in props.segmentValues() && values.hour != null) {
1698
+ if (values.hour < 12)
1699
+ props.segmentValues.update((prev) => ({ ...prev, dayPeriod: 'AM' }));
1700
+ else if (values.hour)
1701
+ props.segmentValues.update((prev) => ({ ...prev, dayPeriod: 'PM' }));
1702
+ }
1703
+ return;
1704
+ }
1705
+ if (isNumberString(e.key)) {
1706
+ const num = Number.parseInt(e.key);
1707
+ const { value, moveToNext } = updateHour(num, prevValue);
1708
+ if ('dayPeriod' in props.segmentValues() && value && value > 12)
1709
+ props.segmentValues.update((prev) => ({ ...prev, dayPeriod: 'AM' }));
1710
+ else if ('dayPeriod' in props.segmentValues() && value)
1711
+ props.segmentValues.update((prev) => ({ ...prev, dayPeriod: 'PM' }));
1712
+ props.segmentValues.update((prev) => ({ ...prev, hour: value }));
1713
+ if (moveToNext)
1714
+ props.focusNext();
1715
+ }
1716
+ if (e.key === BACKSPACE) {
1717
+ props.hasLeftFocus.set(false);
1718
+ props.segmentValues.update((prev) => ({ ...prev, hour: deleteValue(prevValue) }));
1719
+ }
1720
+ }
1721
+ function handleMinuteSegmentKeydown(e) {
1722
+ const dateRef = props.placeholder();
1723
+ const values = props.segmentValues();
1724
+ if (!isAcceptableSegmentKey(e.key) ||
1725
+ isSegmentNavigationKey(e.key) ||
1726
+ !('minute' in dateRef) ||
1727
+ !('minute' in values))
1728
+ return;
1729
+ const prevValue = values.minute;
1730
+ props.segmentValues.update((prev) => ({
1731
+ ...prev,
1732
+ minute: minuteSecondIncrementation({
1733
+ e,
1734
+ part: 'minute',
1735
+ dateRef: props.placeholder(),
1736
+ prevValue
1737
+ })
1738
+ }));
1739
+ if (isNumberString(e.key)) {
1740
+ const num = Number.parseInt(e.key);
1741
+ const { value, moveToNext } = updateMinuteOrSecond(num, prevValue);
1742
+ props.segmentValues.update((prev) => ({ ...prev, minute: value }));
1743
+ if (moveToNext)
1744
+ props.focusNext();
1745
+ }
1746
+ if (e.key === BACKSPACE) {
1747
+ props.hasLeftFocus.set(false);
1748
+ props.segmentValues.update((prev) => ({ ...prev, minute: deleteValue(prevValue) }));
1749
+ }
1750
+ }
1751
+ function handleSecondSegmentKeydown(e) {
1752
+ const dateRef = props.placeholder();
1753
+ const values = props.segmentValues();
1754
+ if (!isAcceptableSegmentKey(e.key) ||
1755
+ isSegmentNavigationKey(e.key) ||
1756
+ !('second' in dateRef) ||
1757
+ !('second' in values))
1758
+ return;
1759
+ const prevValue = values.second;
1760
+ props.segmentValues.update((prev) => ({
1761
+ ...prev,
1762
+ second: minuteSecondIncrementation({
1763
+ e,
1764
+ part: 'second',
1765
+ dateRef: props.placeholder(),
1766
+ prevValue
1767
+ })
1768
+ }));
1769
+ if (isNumberString(e.key)) {
1770
+ const num = Number.parseInt(e.key);
1771
+ const { value, moveToNext } = updateMinuteOrSecond(num, prevValue);
1772
+ props.segmentValues.update((prev) => ({ ...prev, second: value }));
1773
+ if (moveToNext)
1774
+ props.focusNext();
1775
+ }
1776
+ if (e.key === BACKSPACE) {
1777
+ props.hasLeftFocus.set(false);
1778
+ props.segmentValues.update((prev) => ({ ...prev, second: deleteValue(prevValue) }));
1779
+ }
1780
+ }
1781
+ function handleDayPeriodSegmentKeydown(e) {
1782
+ if (((!isAcceptableSegmentKey(e.key) || isSegmentNavigationKey(e.key)) && e.key !== 'a' && e.key !== 'p') ||
1783
+ !('hour' in props.placeholder()) ||
1784
+ !('dayPeriod' in props.segmentValues()))
1785
+ return;
1786
+ const values = props.segmentValues();
1787
+ if (e.key === ARROW_UP || e.key === ARROW_DOWN) {
1788
+ if (values.dayPeriod === 'AM') {
1789
+ props.segmentValues.update((prev) => ({ ...prev, dayPeriod: 'PM' }));
1790
+ props.segmentValues.update((prev) => ({ ...prev, hour: values.hour + 12 }));
1791
+ return;
1792
+ }
1793
+ props.segmentValues.update((prev) => ({ ...prev, dayPeriod: 'AM' }));
1794
+ props.segmentValues.update((prev) => ({ ...prev, hour: values.hour - 12 }));
1795
+ return;
1796
+ }
1797
+ if (['a', 'A'].includes(e.key) && values.dayPeriod !== 'AM') {
1798
+ props.segmentValues.update((prev) => ({ ...prev, dayPeriod: 'AM' }));
1799
+ props.segmentValues.update((prev) => ({ ...prev, hour: values.hour - 12 }));
1800
+ return;
1801
+ }
1802
+ if (['p', 'P'].includes(e.key) && values.dayPeriod !== 'PM') {
1803
+ props.segmentValues.update((prev) => ({ ...prev, dayPeriod: 'PM' }));
1804
+ props.segmentValues.update((prev) => ({ ...prev, hour: values.hour + 12 }));
1805
+ }
1806
+ }
1807
+ function handleSegmentKeydown(e) {
1808
+ const disabled = props.disabled();
1809
+ const readonly = props.readonly();
1810
+ if (e.key !== TAB)
1811
+ e.preventDefault();
1812
+ if (disabled || readonly)
1813
+ return;
1814
+ const segmentKeydownHandlers = {
1815
+ month: handleMonthSegmentKeydown,
1816
+ day: handleDaySegmentKeydown,
1817
+ year: handleYearSegmentKeydown,
1818
+ hour: handleHourSegmentKeydown,
1819
+ minute: handleMinuteSegmentKeydown,
1820
+ second: handleSecondSegmentKeydown,
1821
+ dayPeriod: handleDayPeriodSegmentKeydown,
1822
+ timeZoneName: () => { }
1823
+ };
1824
+ segmentKeydownHandlers[props.part](e);
1825
+ if (![ARROW_LEFT, ARROW_RIGHT].includes(e.key) &&
1826
+ e.key !== TAB &&
1827
+ e.key !== SHIFT &&
1828
+ isAcceptableSegmentKey(e.key)) {
1829
+ if (Object.values(props.segmentValues()).every((item) => item !== null)) {
1830
+ const updateObject = { ...props.segmentValues() };
1831
+ let dateRef = props.placeholder().copy();
1832
+ Object.keys(updateObject).forEach((part) => {
1833
+ const value = updateObject[part];
1834
+ dateRef = dateRef.set({ [part]: value });
1835
+ });
1836
+ props.modelValue.set(dateRef.copy());
1837
+ }
1838
+ }
1839
+ }
1840
+ return {
1841
+ handleSegmentClick,
1842
+ handleSegmentKeydown,
1843
+ attributes
1844
+ };
1845
+ }
1846
+
758
1847
  var RdxPositionSide;
759
1848
  (function (RdxPositionSide) {
760
1849
  RdxPositionSide["Top"] = "top";
@@ -1011,5 +2100,5 @@ function watch(deps, fn, options) {
1011
2100
  * Generated bundle index. Do not edit.
1012
2101
  */
1013
2102
 
1014
- export { A, ALT, ARROW_DOWN, ARROW_LEFT, ARROW_RIGHT, ARROW_UP, ASTERISK, BACKSPACE, CAPS_LOCK, CONTROL, CTRL, DELETE, END, ENTER, ESCAPE, F1, F10, F11, F12, F2, F3, F4, F5, F6, F7, F8, F9, HOME, META, P, PAGE_DOWN, PAGE_UP, RDX_POSITIONING_DEFAULTS, RDX_POSITIONS, RdxAutoFocusDirective, RdxFocusInitialDirective, RdxPositionAlign, RdxPositionSide, SHIFT, SPACE, SPACE_CODE, TAB, WINDOW, _IdGenerator, a, areAllDaysBetweenValid, createFormatter, createMonth, createMonths, getAllPossibleConnectedPositions, getArrowPositionParams, getContentPosition, getDaysBetween, getDaysInMonth, getDefaultDate, getLastFirstDayOfWeek, getNextLastDayOfWeek, getPlaceholder, getSideAndAlignFromAllPossibleConnectedPositions, handleCalendarInitialFocus, hasTime, injectDocument, injectIsClient, injectNgControl, injectWindow, isAfter, isAfterOrSame, isBefore, isBeforeOrSame, isBetween, isBetweenInclusive, isCalendarDateTime, isInsideForm, isNullish, isNumber, isZonedDateTime, j, k, n, p, provideValueAccessor, toDate, watch };
2103
+ export { A, ALT, ARROW_DOWN, ARROW_LEFT, ARROW_RIGHT, ARROW_UP, ASTERISK, BACKSPACE, CAPS_LOCK, CONTROL, CTRL, DELETE, END, ENTER, ESCAPE, F1, F10, F11, F12, F2, F3, F4, F5, F6, F7, F8, F9, HOME, META, P, PAGE_DOWN, PAGE_UP, RDX_POSITIONING_DEFAULTS, RDX_POSITIONS, RdxAutoFocusDirective, RdxFocusInitialDirective, RdxPositionAlign, RdxPositionSide, SHIFT, SPACE, SPACE_CODE, TAB, WINDOW, _IdGenerator, a, areAllDaysBetweenValid, clamp, createContent, createFormatter, createMonth, createMonths, getActiveElement, getAllPossibleConnectedPositions, getArrowPositionParams, getContentPosition, getDaysBetween, getDaysInMonth, getDefaultDate, getLastFirstDayOfWeek, getNextLastDayOfWeek, getOptsByGranularity, getPlaceholder, getSegmentElements, getSideAndAlignFromAllPossibleConnectedPositions, handleCalendarInitialFocus, hasTime, initializeSegmentValues, injectDocument, injectIsClient, injectNgControl, injectWindow, isAcceptableSegmentKey, isAfter, isAfterOrSame, isBefore, isBeforeOrSame, isBetween, isBetweenInclusive, isCalendarDateTime, isInsideForm, isNullish, isNumber, isNumberString, isSegmentNavigationKey, isZonedDateTime, j, k, n, p, provideToken, provideValueAccessor, roundToStepPrecision, segmentBuilders, snapValueToStep, syncSegmentValues, syncTimeSegmentValues, toDate, useDateField, watch };
1015
2104
  //# sourceMappingURL=radix-ng-primitives-core.mjs.map