@radix-ng/primitives 0.33.2 → 0.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/calendar/src/calendar-root.directive.d.ts +3 -3
- package/core/index.d.ts +1 -0
- package/core/src/accessor/provide-value-accessor.d.ts +1 -1
- package/core/src/date-time/index.d.ts +3 -0
- package/core/src/date-time/parser.d.ts +37 -0
- package/core/src/date-time/parts.d.ts +12 -0
- package/core/src/date-time/segment.d.ts +4 -0
- package/core/src/date-time/types.d.ts +18 -1
- package/core/src/date-time/useDateField.d.ts +141 -0
- package/core/src/date-time/utils.d.ts +3 -0
- package/core/src/provide-token.d.ts +22 -0
- package/date-field/README.md +1 -0
- package/date-field/index.d.ts +2 -0
- package/date-field/src/date-field-context.token.d.ts +18 -0
- package/date-field/src/date-field-input.directive.d.ts +53 -0
- package/date-field/src/date-field-root.directive.d.ts +126 -0
- package/dialog/src/dialog-ref.d.ts +3 -0
- package/dialog/src/dialog.config.d.ts +1 -0
- package/fesm2022/radix-ng-primitives-calendar.mjs +6 -15
- package/fesm2022/radix-ng-primitives-calendar.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-checkbox.mjs +3 -4
- package/fesm2022/radix-ng-primitives-checkbox.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-core.mjs +1014 -8
- package/fesm2022/radix-ng-primitives-core.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-date-field.mjs +320 -0
- package/fesm2022/radix-ng-primitives-date-field.mjs.map +1 -0
- package/fesm2022/radix-ng-primitives-dialog.mjs +54 -4
- package/fesm2022/radix-ng-primitives-dialog.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-navigation-menu.mjs +0 -2
- package/fesm2022/radix-ng-primitives-navigation-menu.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-progress.mjs +3 -3
- package/fesm2022/radix-ng-primitives-progress.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-radio.mjs +7 -7
- package/fesm2022/radix-ng-primitives-radio.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-switch.mjs +7 -15
- package/fesm2022/radix-ng-primitives-switch.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-tabs.mjs +3 -6
- package/fesm2022/radix-ng-primitives-tabs.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-toggle-group.mjs +8 -10
- package/fesm2022/radix-ng-primitives-toggle-group.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-toggle.mjs +5 -12
- package/fesm2022/radix-ng-primitives-toggle.mjs.map +1 -1
- package/hover-card/src/hover-card-root.directive.d.ts +4 -4
- package/package.json +5 -1
- package/popover/src/popover-root.directive.d.ts +4 -4
- package/switch/src/switch-root.directive.d.ts +0 -1
- package/toggle/src/toggle.directive.d.ts +0 -1
- 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
|
}
|
@@ -239,6 +239,33 @@ const j = 'j';
|
|
239
239
|
const k = 'k';
|
240
240
|
const SPACE_CODE = 'Space';
|
241
241
|
|
242
|
+
/**
|
243
|
+
* Creates an Angular provider that binds the given token to the existing instance
|
244
|
+
* of the specified class. This is especially useful when you want multiple
|
245
|
+
* tokens (or interfaces) to resolve to the same directive/component instance.
|
246
|
+
*
|
247
|
+
* @template T - The type associated with the injection token.
|
248
|
+
* @param token - The InjectionToken or abstract type you want to provide.
|
249
|
+
* @param type - The class type whose existing instance will be used for this token.
|
250
|
+
* @returns A Provider configuration object for Angular's DI system.
|
251
|
+
*
|
252
|
+
* @example
|
253
|
+
*
|
254
|
+
* @Directive({
|
255
|
+
* providers: [
|
256
|
+
* provideToken(RdxToggleGroupToken, RdxToggleGroupDirective),
|
257
|
+
* provideValueAccessor(RdxToggleGroupDirective)
|
258
|
+
* ]
|
259
|
+
* })
|
260
|
+
* export class RdxToggleGroupDirective {}
|
261
|
+
*/
|
262
|
+
function provideToken(token, type) {
|
263
|
+
return {
|
264
|
+
provide: token,
|
265
|
+
useExisting: forwardRef(() => type)
|
266
|
+
};
|
267
|
+
}
|
268
|
+
|
242
269
|
const WINDOW = new InjectionToken('An abstraction over global window object', {
|
243
270
|
factory: () => {
|
244
271
|
const { defaultView } = injectDocument();
|
@@ -606,6 +633,32 @@ function createFormatter(initialLocale) {
|
|
606
633
|
};
|
607
634
|
}
|
608
635
|
|
636
|
+
/*
|
637
|
+
* Implementation ported from https://github.com/melt-ui/melt-ui/blob/develop/src/lib/builders/date-field/_internal/helpers.ts
|
638
|
+
*/
|
639
|
+
const DATE_SEGMENT_PARTS = ['day', 'month', 'year'];
|
640
|
+
const TIME_SEGMENT_PARTS = ['hour', 'minute', 'second', 'dayPeriod'];
|
641
|
+
const NON_EDITABLE_SEGMENT_PARTS = ['literal', 'timeZoneName'];
|
642
|
+
const EDITABLE_SEGMENT_PARTS = [...DATE_SEGMENT_PARTS, ...TIME_SEGMENT_PARTS];
|
643
|
+
const EDITABLE_TIME_SEGMENT_PARTS = [...TIME_SEGMENT_PARTS];
|
644
|
+
const ALL_SEGMENT_PARTS = [
|
645
|
+
...EDITABLE_SEGMENT_PARTS,
|
646
|
+
...NON_EDITABLE_SEGMENT_PARTS
|
647
|
+
];
|
648
|
+
const ALL_EXCEPT_LITERAL_PARTS = ALL_SEGMENT_PARTS.filter((part) => part !== 'literal');
|
649
|
+
function isDateSegmentPart(part) {
|
650
|
+
return DATE_SEGMENT_PARTS.includes(part);
|
651
|
+
}
|
652
|
+
function isTimeSegmentPart(part) {
|
653
|
+
return TIME_SEGMENT_PARTS.includes(part);
|
654
|
+
}
|
655
|
+
function isSegmentPart(part) {
|
656
|
+
return EDITABLE_SEGMENT_PARTS.includes(part);
|
657
|
+
}
|
658
|
+
function isAnySegmentPart(part) {
|
659
|
+
return ALL_SEGMENT_PARTS.includes(part);
|
660
|
+
}
|
661
|
+
|
609
662
|
/*
|
610
663
|
* Implementation ported from from from https://github.com/melt-ui/melt-ui/blob/develop/src/lib/internal/helpers/date/placeholders.ts
|
611
664
|
*/
|
@@ -739,10 +792,37 @@ function getLocaleLanguage(locale) {
|
|
739
792
|
return locale.split('-')[0];
|
740
793
|
}
|
741
794
|
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
|
795
|
+
function getOptsByGranularity(granularity, hourCycle, isTimeValue = false) {
|
796
|
+
const opts = {
|
797
|
+
year: 'numeric',
|
798
|
+
month: '2-digit',
|
799
|
+
day: '2-digit',
|
800
|
+
hour: '2-digit',
|
801
|
+
minute: '2-digit',
|
802
|
+
second: '2-digit',
|
803
|
+
timeZoneName: 'short',
|
804
|
+
hourCycle: hourCycle === 24 ? 'h23' : undefined,
|
805
|
+
hour12: hourCycle === 24 ? false : undefined
|
806
|
+
};
|
807
|
+
if (isTimeValue) {
|
808
|
+
delete opts.year;
|
809
|
+
delete opts.month;
|
810
|
+
delete opts.day;
|
811
|
+
}
|
812
|
+
if (granularity === 'day') {
|
813
|
+
delete opts.second;
|
814
|
+
delete opts.hour;
|
815
|
+
delete opts.minute;
|
816
|
+
delete opts.timeZoneName;
|
817
|
+
}
|
818
|
+
if (granularity === 'hour') {
|
819
|
+
delete opts.minute;
|
820
|
+
delete opts.second;
|
821
|
+
}
|
822
|
+
if (granularity === 'minute')
|
823
|
+
delete opts.second;
|
824
|
+
return opts;
|
825
|
+
}
|
746
826
|
function handleCalendarInitialFocus(calendar) {
|
747
827
|
const selectedDay = calendar.querySelector('[data-selected]');
|
748
828
|
if (selectedDay)
|
@@ -755,6 +835,932 @@ function handleCalendarInitialFocus(calendar) {
|
|
755
835
|
return firstDay.focus();
|
756
836
|
}
|
757
837
|
|
838
|
+
const calendarDateTimeGranularities = ['hour', 'minute', 'second'];
|
839
|
+
function syncTimeSegmentValues(props) {
|
840
|
+
return Object.fromEntries(TIME_SEGMENT_PARTS.map((part) => {
|
841
|
+
if (part === 'dayPeriod')
|
842
|
+
return [part, props.formatter.dayPeriod(toDate(props.value))];
|
843
|
+
return [part, props.value[part]];
|
844
|
+
}));
|
845
|
+
}
|
846
|
+
function initializeSegmentValues(granularity) {
|
847
|
+
const initialParts = EDITABLE_SEGMENT_PARTS.map((part) => {
|
848
|
+
if (part === 'dayPeriod')
|
849
|
+
return [part, 'AM'];
|
850
|
+
return [part, null];
|
851
|
+
}).filter(([key]) => {
|
852
|
+
if (key === 'literal' || key === null)
|
853
|
+
return false;
|
854
|
+
if (granularity === 'minute' && key === 'second')
|
855
|
+
return false;
|
856
|
+
if (granularity === 'hour' && (key === 'second' || key === 'minute'))
|
857
|
+
return false;
|
858
|
+
if (granularity === 'day')
|
859
|
+
return !calendarDateTimeGranularities.includes(key) && key !== 'dayPeriod';
|
860
|
+
else
|
861
|
+
return true;
|
862
|
+
});
|
863
|
+
return Object.fromEntries(initialParts);
|
864
|
+
}
|
865
|
+
function syncSegmentValues(props) {
|
866
|
+
const { formatter } = props;
|
867
|
+
const dateValues = DATE_SEGMENT_PARTS.map((part) => {
|
868
|
+
return [part, props.value[part]];
|
869
|
+
});
|
870
|
+
if ('hour' in props.value) {
|
871
|
+
const timeValues = syncTimeSegmentValues({ value: props.value, formatter });
|
872
|
+
return { ...Object.fromEntries(dateValues), ...timeValues };
|
873
|
+
}
|
874
|
+
return Object.fromEntries(dateValues);
|
875
|
+
}
|
876
|
+
function createContentObj(props) {
|
877
|
+
const { segmentValues, formatter, locale } = props;
|
878
|
+
function getPartContent(part) {
|
879
|
+
if ('hour' in segmentValues) {
|
880
|
+
const value = segmentValues[part];
|
881
|
+
if (value !== null) {
|
882
|
+
/**
|
883
|
+
* 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
|
884
|
+
*/
|
885
|
+
if (part === 'day' && segmentValues.month !== null) {
|
886
|
+
return formatter.part(props.dateRef.set({ [part]: value, month: segmentValues.month }), part, {
|
887
|
+
hourCycle: props.hourCycle === 24 ? 'h23' : undefined
|
888
|
+
});
|
889
|
+
}
|
890
|
+
return formatter.part(props.dateRef.set({ [part]: value }), part, {
|
891
|
+
hourCycle: props.hourCycle === 24 ? 'h23' : undefined
|
892
|
+
});
|
893
|
+
}
|
894
|
+
else {
|
895
|
+
return getPlaceholder(part, '', locale());
|
896
|
+
}
|
897
|
+
}
|
898
|
+
else {
|
899
|
+
if (isDateSegmentPart(part)) {
|
900
|
+
const value = segmentValues[part];
|
901
|
+
if (value !== null) {
|
902
|
+
if (part === 'day' && segmentValues.month !== null)
|
903
|
+
/**
|
904
|
+
* As described above, same function
|
905
|
+
*/
|
906
|
+
return formatter.part(props.dateRef.set({ [part]: value, month: segmentValues.month }), part);
|
907
|
+
return formatter.part(props.dateRef.set({ [part]: value }), part);
|
908
|
+
}
|
909
|
+
else {
|
910
|
+
return getPlaceholder(part, '', locale());
|
911
|
+
}
|
912
|
+
}
|
913
|
+
return '';
|
914
|
+
}
|
915
|
+
}
|
916
|
+
const content = Object.keys(segmentValues).reduce((obj, part) => {
|
917
|
+
if (!isSegmentPart(part))
|
918
|
+
return obj;
|
919
|
+
if ('hour' in segmentValues && part === 'dayPeriod') {
|
920
|
+
const value = segmentValues[part];
|
921
|
+
if (value !== null)
|
922
|
+
obj[part] = value;
|
923
|
+
else
|
924
|
+
obj[part] = getPlaceholder(part, 'AM', locale());
|
925
|
+
}
|
926
|
+
else {
|
927
|
+
obj[part] = getPartContent(part);
|
928
|
+
}
|
929
|
+
return obj;
|
930
|
+
}, {});
|
931
|
+
return content;
|
932
|
+
}
|
933
|
+
function createContentArr(props) {
|
934
|
+
const { granularity, formatter, contentObj, hideTimeZone, hourCycle, isTimeValue } = props;
|
935
|
+
const parts = formatter.toParts(props.dateRef, getOptsByGranularity(granularity, hourCycle, isTimeValue));
|
936
|
+
const segmentContentArr = parts
|
937
|
+
.map((part) => {
|
938
|
+
const defaultParts = ['literal', 'timeZoneName', null];
|
939
|
+
if (defaultParts.includes(part.type) || !isSegmentPart(part.type)) {
|
940
|
+
return {
|
941
|
+
part: part.type,
|
942
|
+
value: part.value
|
943
|
+
};
|
944
|
+
}
|
945
|
+
return {
|
946
|
+
part: part.type,
|
947
|
+
value: contentObj[part.type]
|
948
|
+
};
|
949
|
+
})
|
950
|
+
.filter((segment) => {
|
951
|
+
if (segment.part === null || segment.value === null)
|
952
|
+
return false;
|
953
|
+
if (segment.part === 'timeZoneName' && (!isZonedDateTime(props.dateRef) || hideTimeZone))
|
954
|
+
return false;
|
955
|
+
return true;
|
956
|
+
});
|
957
|
+
return segmentContentArr;
|
958
|
+
}
|
959
|
+
function createContent(props) {
|
960
|
+
const contentObj = createContentObj(props);
|
961
|
+
const contentArr = createContentArr({
|
962
|
+
contentObj,
|
963
|
+
...props
|
964
|
+
});
|
965
|
+
return {
|
966
|
+
obj: contentObj,
|
967
|
+
arr: contentArr
|
968
|
+
};
|
969
|
+
}
|
970
|
+
|
971
|
+
function isSegmentNavigationKey(key) {
|
972
|
+
if (key === ARROW_RIGHT || key === ARROW_LEFT)
|
973
|
+
return true;
|
974
|
+
return false;
|
975
|
+
}
|
976
|
+
function isNumberString(value) {
|
977
|
+
if (Number.isNaN(Number.parseInt(value)))
|
978
|
+
return false;
|
979
|
+
return true;
|
980
|
+
}
|
981
|
+
function isAcceptableSegmentKey(key) {
|
982
|
+
const acceptableSegmentKeys = [
|
983
|
+
ENTER,
|
984
|
+
ARROW_UP,
|
985
|
+
ARROW_DOWN,
|
986
|
+
ARROW_LEFT,
|
987
|
+
ARROW_RIGHT,
|
988
|
+
BACKSPACE,
|
989
|
+
SPACE,
|
990
|
+
'a',
|
991
|
+
'A',
|
992
|
+
'p',
|
993
|
+
'P'
|
994
|
+
];
|
995
|
+
if (acceptableSegmentKeys.includes(key))
|
996
|
+
return true;
|
997
|
+
if (isNumberString(key))
|
998
|
+
return true;
|
999
|
+
return false;
|
1000
|
+
}
|
1001
|
+
function getSegmentElements(parentElement) {
|
1002
|
+
return Array.from(parentElement.querySelectorAll('[data-rdx-date-field-segment]')).filter((item) => item.getAttribute('data-rdx-date-field-segment') !== 'literal');
|
1003
|
+
}
|
1004
|
+
|
1005
|
+
/*
|
1006
|
+
* Implementation ported from https://github.com/melt-ui/melt-ui/blob/develop/src/lib/internal/helpers/date/types.ts
|
1007
|
+
*/
|
1008
|
+
|
1009
|
+
// https://github.com/unovue/reka-ui/blob/v2/packages/core/src/shared/date/useDateField.ts
|
1010
|
+
function commonSegmentAttrs(props) {
|
1011
|
+
return {
|
1012
|
+
role: 'spinbutton',
|
1013
|
+
contenteditable: true,
|
1014
|
+
tabindex: props.disabled ? undefined : 0,
|
1015
|
+
spellcheck: false,
|
1016
|
+
inputmode: 'numeric',
|
1017
|
+
autocorrect: 'off',
|
1018
|
+
enterkeyhint: 'next',
|
1019
|
+
style: 'caret-color: transparent;'
|
1020
|
+
};
|
1021
|
+
}
|
1022
|
+
function daySegmentAttrs(props) {
|
1023
|
+
const { segmentValues, placeholder } = props;
|
1024
|
+
const isEmpty = segmentValues.day === null;
|
1025
|
+
const date = segmentValues.day ? placeholder.set({ day: segmentValues.day }) : placeholder;
|
1026
|
+
const valueNow = date.day;
|
1027
|
+
const valueMin = 1;
|
1028
|
+
const valueMax = getDaysInMonth(date);
|
1029
|
+
const valueText = isEmpty ? 'Empty' : `${valueNow}`;
|
1030
|
+
return {
|
1031
|
+
...commonSegmentAttrs(props),
|
1032
|
+
'aria-label': 'day,',
|
1033
|
+
'aria-valuemin': valueMin,
|
1034
|
+
'aria-valuemax': valueMax,
|
1035
|
+
'aria-valuenow': valueNow,
|
1036
|
+
'aria-valuetext': valueText,
|
1037
|
+
'data-placeholder': isEmpty ? '' : undefined
|
1038
|
+
};
|
1039
|
+
}
|
1040
|
+
function monthSegmentAttrs(props) {
|
1041
|
+
const { segmentValues, placeholder, formatter } = props;
|
1042
|
+
const isEmpty = segmentValues.month === null;
|
1043
|
+
const date = segmentValues.month ? placeholder.set({ month: segmentValues.month }) : placeholder;
|
1044
|
+
const valueNow = date.month;
|
1045
|
+
const valueMin = 1;
|
1046
|
+
const valueMax = 12;
|
1047
|
+
const valueText = isEmpty ? 'Empty' : `${valueNow} - ${formatter.fullMonth(toDate(date))}`;
|
1048
|
+
return {
|
1049
|
+
...commonSegmentAttrs(props),
|
1050
|
+
'aria-label': 'month, ',
|
1051
|
+
contenteditable: true,
|
1052
|
+
'aria-valuemin': valueMin,
|
1053
|
+
'aria-valuemax': valueMax,
|
1054
|
+
'aria-valuenow': valueNow,
|
1055
|
+
'aria-valuetext': valueText,
|
1056
|
+
'data-placeholder': isEmpty ? '' : undefined
|
1057
|
+
};
|
1058
|
+
}
|
1059
|
+
function yearSegmentAttrs(props) {
|
1060
|
+
const { segmentValues, placeholder } = props;
|
1061
|
+
const isEmpty = segmentValues.year === null;
|
1062
|
+
const date = segmentValues.year ? placeholder.set({ year: segmentValues.year }) : placeholder;
|
1063
|
+
const valueMin = 1;
|
1064
|
+
const valueMax = 9999;
|
1065
|
+
const valueNow = date.year;
|
1066
|
+
const valueText = isEmpty ? 'Empty' : `${valueNow}`;
|
1067
|
+
return {
|
1068
|
+
...commonSegmentAttrs(props),
|
1069
|
+
'aria-label': 'year, ',
|
1070
|
+
'aria-valuemin': valueMin,
|
1071
|
+
'aria-valuemax': valueMax,
|
1072
|
+
'aria-valuenow': valueNow,
|
1073
|
+
'aria-valuetext': valueText,
|
1074
|
+
'data-placeholder': isEmpty ? '' : undefined
|
1075
|
+
};
|
1076
|
+
}
|
1077
|
+
function hourSegmentAttrs(props) {
|
1078
|
+
const { segmentValues, hourCycle, placeholder } = props;
|
1079
|
+
if (!('hour' in segmentValues) || !('hour' in placeholder))
|
1080
|
+
return {};
|
1081
|
+
const isEmpty = segmentValues.hour === null;
|
1082
|
+
const date = segmentValues.hour ? placeholder.set({ hour: segmentValues.hour }) : placeholder;
|
1083
|
+
const valueMin = hourCycle === 12 ? 1 : 0;
|
1084
|
+
const valueMax = hourCycle === 12 ? 12 : 23;
|
1085
|
+
const valueNow = date.hour;
|
1086
|
+
const valueText = isEmpty ? 'Empty' : `${valueNow} ${segmentValues.dayPeriod ?? ''}`;
|
1087
|
+
return {
|
1088
|
+
...commonSegmentAttrs(props),
|
1089
|
+
'aria-label': 'hour, ',
|
1090
|
+
'aria-valuemin': valueMin,
|
1091
|
+
'aria-valuemax': valueMax,
|
1092
|
+
'aria-valuenow': valueNow,
|
1093
|
+
'aria-valuetext': valueText,
|
1094
|
+
'data-placeholder': isEmpty ? '' : undefined
|
1095
|
+
};
|
1096
|
+
}
|
1097
|
+
function minuteSegmentAttrs(props) {
|
1098
|
+
const { segmentValues, placeholder } = props;
|
1099
|
+
if (!('minute' in segmentValues) || !('minute' in placeholder))
|
1100
|
+
return {};
|
1101
|
+
const isEmpty = segmentValues.minute === null;
|
1102
|
+
const date = segmentValues.minute ? placeholder.set({ minute: segmentValues.minute }) : placeholder;
|
1103
|
+
const valueNow = date.minute;
|
1104
|
+
const valueMin = 0;
|
1105
|
+
const valueMax = 59;
|
1106
|
+
const valueText = isEmpty ? 'Empty' : `${valueNow}`;
|
1107
|
+
return {
|
1108
|
+
...commonSegmentAttrs(props),
|
1109
|
+
'aria-label': 'minute, ',
|
1110
|
+
'aria-valuemin': valueMin,
|
1111
|
+
'aria-valuemax': valueMax,
|
1112
|
+
'aria-valuenow': valueNow,
|
1113
|
+
'aria-valuetext': valueText,
|
1114
|
+
'data-placeholder': isEmpty ? '' : undefined
|
1115
|
+
};
|
1116
|
+
}
|
1117
|
+
function secondSegmentAttrs(props) {
|
1118
|
+
const { segmentValues, placeholder } = props;
|
1119
|
+
if (!('second' in segmentValues) || !('second' in placeholder))
|
1120
|
+
return {};
|
1121
|
+
const isEmpty = segmentValues.second === null;
|
1122
|
+
const date = segmentValues.second ? placeholder.set({ second: segmentValues.second }) : placeholder;
|
1123
|
+
const valueNow = date.second;
|
1124
|
+
const valueMin = 0;
|
1125
|
+
const valueMax = 59;
|
1126
|
+
const valueText = isEmpty ? 'Empty' : `${valueNow}`;
|
1127
|
+
return {
|
1128
|
+
...commonSegmentAttrs(props),
|
1129
|
+
'aria-label': 'second, ',
|
1130
|
+
'aria-valuemin': valueMin,
|
1131
|
+
'aria-valuemax': valueMax,
|
1132
|
+
'aria-valuenow': valueNow,
|
1133
|
+
'aria-valuetext': valueText,
|
1134
|
+
'data-placeholder': isEmpty ? '' : undefined
|
1135
|
+
};
|
1136
|
+
}
|
1137
|
+
function dayPeriodSegmentAttrs(props) {
|
1138
|
+
const { segmentValues } = props;
|
1139
|
+
if (!('dayPeriod' in segmentValues))
|
1140
|
+
return {};
|
1141
|
+
const valueMin = 0;
|
1142
|
+
const valueMax = 12;
|
1143
|
+
const valueNow = segmentValues.hour ? (segmentValues.hour > 12 ? segmentValues.hour - 12 : segmentValues.hour) : 0;
|
1144
|
+
const valueText = segmentValues.dayPeriod ?? 'AM';
|
1145
|
+
return {
|
1146
|
+
...commonSegmentAttrs(props),
|
1147
|
+
inputmode: 'text',
|
1148
|
+
'aria-label': 'AM/PM',
|
1149
|
+
'aria-valuemin': valueMin,
|
1150
|
+
'aria-valuemax': valueMax,
|
1151
|
+
'aria-valuenow': valueNow,
|
1152
|
+
'aria-valuetext': valueText
|
1153
|
+
};
|
1154
|
+
}
|
1155
|
+
function literalSegmentAttrs(_props) {
|
1156
|
+
return {
|
1157
|
+
'aria-hidden': true,
|
1158
|
+
'data-segment': 'literal'
|
1159
|
+
};
|
1160
|
+
}
|
1161
|
+
function timeZoneSegmentAttrs(props) {
|
1162
|
+
return {
|
1163
|
+
role: 'textbox',
|
1164
|
+
'aria-label': 'timezone, ',
|
1165
|
+
'data-readonly': true,
|
1166
|
+
'data-segment': 'timeZoneName',
|
1167
|
+
tabindex: props.disabled ? undefined : 0,
|
1168
|
+
style: 'caret-color: transparent;'
|
1169
|
+
};
|
1170
|
+
}
|
1171
|
+
function eraSegmentAttrs(props) {
|
1172
|
+
const { segmentValues, placeholder } = props;
|
1173
|
+
const valueMin = 0;
|
1174
|
+
const valueMax = 0;
|
1175
|
+
const valueNow = 0;
|
1176
|
+
const valueText = 'era' in segmentValues ? segmentValues.era : placeholder.era;
|
1177
|
+
return {
|
1178
|
+
...commonSegmentAttrs(props),
|
1179
|
+
'aria-label': 'era',
|
1180
|
+
'aria-valuemin': valueMin,
|
1181
|
+
'aria-valuemax': valueMax,
|
1182
|
+
'aria-valuenow': valueNow,
|
1183
|
+
'aria-valuetext': valueText
|
1184
|
+
};
|
1185
|
+
}
|
1186
|
+
const segmentBuilders = {
|
1187
|
+
day: {
|
1188
|
+
attrs: daySegmentAttrs
|
1189
|
+
},
|
1190
|
+
month: {
|
1191
|
+
attrs: monthSegmentAttrs
|
1192
|
+
},
|
1193
|
+
year: {
|
1194
|
+
attrs: yearSegmentAttrs
|
1195
|
+
},
|
1196
|
+
hour: {
|
1197
|
+
attrs: hourSegmentAttrs
|
1198
|
+
},
|
1199
|
+
minute: {
|
1200
|
+
attrs: minuteSegmentAttrs
|
1201
|
+
},
|
1202
|
+
second: {
|
1203
|
+
attrs: secondSegmentAttrs
|
1204
|
+
},
|
1205
|
+
dayPeriod: {
|
1206
|
+
attrs: dayPeriodSegmentAttrs
|
1207
|
+
},
|
1208
|
+
literal: {
|
1209
|
+
attrs: literalSegmentAttrs
|
1210
|
+
},
|
1211
|
+
timeZoneName: {
|
1212
|
+
attrs: timeZoneSegmentAttrs
|
1213
|
+
},
|
1214
|
+
era: {
|
1215
|
+
attrs: eraSegmentAttrs
|
1216
|
+
}
|
1217
|
+
};
|
1218
|
+
function useDateField(props) {
|
1219
|
+
function handleSegmentClick(e) {
|
1220
|
+
const disabled = props.disabled();
|
1221
|
+
if (disabled)
|
1222
|
+
e.preventDefault();
|
1223
|
+
}
|
1224
|
+
function deleteValue(prevValue) {
|
1225
|
+
props.hasLeftFocus.set(false);
|
1226
|
+
if (prevValue === null)
|
1227
|
+
return prevValue;
|
1228
|
+
const str = prevValue.toString();
|
1229
|
+
if (str.length === 1) {
|
1230
|
+
props.modelValue.set(undefined);
|
1231
|
+
return null;
|
1232
|
+
}
|
1233
|
+
return Number.parseInt(str.slice(0, -1));
|
1234
|
+
}
|
1235
|
+
function dateTimeValueIncrementation({ e, part, dateRef, prevValue, hourCycle }) {
|
1236
|
+
const sign = e.key === ARROW_UP ? 1 : -1;
|
1237
|
+
if (prevValue === null)
|
1238
|
+
return dateRef[part];
|
1239
|
+
if (part === 'hour' && 'hour' in dateRef) {
|
1240
|
+
const cycleArgs = [
|
1241
|
+
part,
|
1242
|
+
sign,
|
1243
|
+
{ hourCycle }
|
1244
|
+
];
|
1245
|
+
return dateRef.set({ [part]: prevValue }).cycle(...cycleArgs)[part];
|
1246
|
+
}
|
1247
|
+
const cycleArgs = [part, sign];
|
1248
|
+
if (part === 'day' && props.segmentValues().month !== null)
|
1249
|
+
return dateRef
|
1250
|
+
.set({ [part]: prevValue, month: props.segmentValues().month })
|
1251
|
+
.cycle(...cycleArgs)[part];
|
1252
|
+
return dateRef.set({ [part]: prevValue }).cycle(...cycleArgs)[part];
|
1253
|
+
}
|
1254
|
+
function updateDayOrMonth(max, num, prev) {
|
1255
|
+
let moveToNext = false;
|
1256
|
+
const maxStart = Math.floor(max / 10);
|
1257
|
+
/**
|
1258
|
+
* If the user has left the segment, we want to reset the
|
1259
|
+
* `prev` value so that we can start the segment over again
|
1260
|
+
* when the user types a number.
|
1261
|
+
*/
|
1262
|
+
if (props.hasLeftFocus()) {
|
1263
|
+
props.hasLeftFocus.set(false);
|
1264
|
+
prev = null;
|
1265
|
+
}
|
1266
|
+
if (prev === null) {
|
1267
|
+
/**
|
1268
|
+
* If the user types a 0 as the first number, we want
|
1269
|
+
* to keep track of that so that when they type the next
|
1270
|
+
* number, we can move to the next segment.
|
1271
|
+
*/
|
1272
|
+
if (num === 0) {
|
1273
|
+
props.lastKeyZero.set(true);
|
1274
|
+
return { value: null, moveToNext };
|
1275
|
+
}
|
1276
|
+
/**
|
1277
|
+
* If the last key was a 0, or if the first number is
|
1278
|
+
* greater than the max start digit (0-3 in most cases), then
|
1279
|
+
* we want to move to the next segment, since it's not possible
|
1280
|
+
* to continue typing a valid number in this segment.
|
1281
|
+
*/
|
1282
|
+
if (props.lastKeyZero() || num > maxStart) {
|
1283
|
+
// move to next
|
1284
|
+
moveToNext = true;
|
1285
|
+
}
|
1286
|
+
props.lastKeyZero.set(false);
|
1287
|
+
/**
|
1288
|
+
* If none of the above conditions are met, then we can just
|
1289
|
+
* return the number as the segment value and continue typing
|
1290
|
+
* in this segment.
|
1291
|
+
*/
|
1292
|
+
return { value: num, moveToNext };
|
1293
|
+
}
|
1294
|
+
/**
|
1295
|
+
* If the number of digits is 2, or if the total with the existing digit
|
1296
|
+
* and the pressed digit is greater than the maximum value for this
|
1297
|
+
* month, then we will reset the segment as if the user had pressed the
|
1298
|
+
* backspace key and then typed the number.
|
1299
|
+
*/
|
1300
|
+
const digits = prev.toString().length;
|
1301
|
+
const total = Number.parseInt(prev.toString() + num.toString());
|
1302
|
+
/**
|
1303
|
+
* If the number of digits is 2, or if the total with the existing digit
|
1304
|
+
* and the pressed digit is greater than the maximum value for this
|
1305
|
+
* month, then we will reset the segment as if the user had pressed the
|
1306
|
+
* backspace key and then typed the number.
|
1307
|
+
*/
|
1308
|
+
if (digits === 2 || total > max) {
|
1309
|
+
/**
|
1310
|
+
* As we're doing elsewhere, we're checking if the number is greater
|
1311
|
+
* than the max start digit (0-3 in most months), and if so, we're
|
1312
|
+
* going to move to the next segment.
|
1313
|
+
*/
|
1314
|
+
if (num > maxStart || total > max) {
|
1315
|
+
// move to next
|
1316
|
+
moveToNext = true;
|
1317
|
+
}
|
1318
|
+
return { value: num, moveToNext };
|
1319
|
+
}
|
1320
|
+
// move to next
|
1321
|
+
moveToNext = true;
|
1322
|
+
return { value: total, moveToNext };
|
1323
|
+
}
|
1324
|
+
function updateYear(num, prev) {
|
1325
|
+
let moveToNext = false;
|
1326
|
+
/**
|
1327
|
+
* If the user has left the segment, we want to reset the
|
1328
|
+
* `prev` value so that we can start the segment over again
|
1329
|
+
* when the user types a number.
|
1330
|
+
*/
|
1331
|
+
// probably not implement, kind of weird
|
1332
|
+
if (props.hasLeftFocus()) {
|
1333
|
+
props.hasLeftFocus.set(false);
|
1334
|
+
prev = null;
|
1335
|
+
}
|
1336
|
+
if (prev === null)
|
1337
|
+
return { value: num === 0 ? 1 : num, moveToNext };
|
1338
|
+
const str = prev.toString() + num.toString();
|
1339
|
+
if (str.length > 4)
|
1340
|
+
return { value: num === 0 ? 1 : num, moveToNext };
|
1341
|
+
if (str.length === 4)
|
1342
|
+
moveToNext = true;
|
1343
|
+
const int = Number.parseInt(str);
|
1344
|
+
return { value: int, moveToNext };
|
1345
|
+
}
|
1346
|
+
function updateHour(num, prev) {
|
1347
|
+
const max = 24;
|
1348
|
+
let moveToNext = false;
|
1349
|
+
const maxStart = Math.floor(max / 10);
|
1350
|
+
/**
|
1351
|
+
* If the user has left the segment, we want to reset the
|
1352
|
+
* `prev` value so that we can start the segment over again
|
1353
|
+
* when the user types a number.
|
1354
|
+
*/
|
1355
|
+
// probably not implement, kind of weird
|
1356
|
+
if (props.hasLeftFocus()) {
|
1357
|
+
props.hasLeftFocus.set(false);
|
1358
|
+
prev = null;
|
1359
|
+
}
|
1360
|
+
if (prev === null) {
|
1361
|
+
/**
|
1362
|
+
* If the user types a 0 as the first number, we want
|
1363
|
+
* to keep track of that so that when they type the next
|
1364
|
+
* number, we can move to the next segment.
|
1365
|
+
*/
|
1366
|
+
if (num === 0) {
|
1367
|
+
props.lastKeyZero.set(true);
|
1368
|
+
return { value: 0, moveToNext };
|
1369
|
+
}
|
1370
|
+
/**
|
1371
|
+
* If the last key was a 0, or if the first number is
|
1372
|
+
* greater than the max start digit (0-3 in most cases), then
|
1373
|
+
* we want to move to the next segment, since it's not possible
|
1374
|
+
* to continue typing a valid number in this segment.
|
1375
|
+
*/
|
1376
|
+
if (props.lastKeyZero() || num > maxStart) {
|
1377
|
+
// move to next
|
1378
|
+
moveToNext = true;
|
1379
|
+
}
|
1380
|
+
props.lastKeyZero.set(false);
|
1381
|
+
/**
|
1382
|
+
* If none of the above conditions are met, then we can just
|
1383
|
+
* return the number as the segment value and continue typing
|
1384
|
+
* in this segment.
|
1385
|
+
*/
|
1386
|
+
return { value: num, moveToNext };
|
1387
|
+
}
|
1388
|
+
/**
|
1389
|
+
* If the number of digits is 2, or if the total with the existing digit
|
1390
|
+
* and the pressed digit is greater than the maximum value for this
|
1391
|
+
* month, then we will reset the segment as if the user had pressed the
|
1392
|
+
* backspace key and then typed the number.
|
1393
|
+
*/
|
1394
|
+
const digits = prev.toString().length;
|
1395
|
+
const total = Number.parseInt(prev.toString() + num.toString());
|
1396
|
+
/**
|
1397
|
+
* If the number of digits is 2, or if the total with the existing digit
|
1398
|
+
* and the pressed digit is greater than the maximum value for this
|
1399
|
+
* month, then we will reset the segment as if the user had pressed the
|
1400
|
+
* backspace key and then typed the number.
|
1401
|
+
*/
|
1402
|
+
if (digits === 2 || total > max) {
|
1403
|
+
/**
|
1404
|
+
* As we're doing elsewhere, we're checking if the number is greater
|
1405
|
+
* than the max start digit (0-3 in most months), and if so, we're
|
1406
|
+
* going to move to the next segment.
|
1407
|
+
*/
|
1408
|
+
if (num > maxStart) {
|
1409
|
+
// move to next
|
1410
|
+
moveToNext = true;
|
1411
|
+
}
|
1412
|
+
return { value: num, moveToNext };
|
1413
|
+
}
|
1414
|
+
// move to next
|
1415
|
+
moveToNext = true;
|
1416
|
+
return { value: total, moveToNext };
|
1417
|
+
}
|
1418
|
+
function updateMinuteOrSecond(num, prev) {
|
1419
|
+
const max = 59;
|
1420
|
+
let moveToNext = false;
|
1421
|
+
const maxStart = Math.floor(max / 10);
|
1422
|
+
/**
|
1423
|
+
* If the user has left the segment, we want to reset the
|
1424
|
+
* `prev` value so that we can start the segment over again
|
1425
|
+
* when the user types a number.
|
1426
|
+
*/
|
1427
|
+
if (props.hasLeftFocus()) {
|
1428
|
+
props.hasLeftFocus.set(false);
|
1429
|
+
prev = null;
|
1430
|
+
}
|
1431
|
+
if (prev === null) {
|
1432
|
+
/**
|
1433
|
+
* If the user types a 0 as the first number, we want
|
1434
|
+
* to keep track of that so that when they type the next
|
1435
|
+
* number, we can move to the next segment.
|
1436
|
+
*/
|
1437
|
+
if (num === 0) {
|
1438
|
+
props.lastKeyZero.set(true);
|
1439
|
+
return { value: 0, moveToNext };
|
1440
|
+
}
|
1441
|
+
/**
|
1442
|
+
* If the last key was a 0, or if the first number is
|
1443
|
+
* greater than the max start digit (0-3 in most cases), then
|
1444
|
+
* we want to move to the next segment, since it's not possible
|
1445
|
+
* to continue typing a valid number in this segment.
|
1446
|
+
*/
|
1447
|
+
if (props.lastKeyZero() || num > maxStart) {
|
1448
|
+
// move to next
|
1449
|
+
moveToNext = true;
|
1450
|
+
}
|
1451
|
+
props.lastKeyZero.set(false);
|
1452
|
+
/**
|
1453
|
+
* If none of the above conditions are met, then we can just
|
1454
|
+
* return the number as the segment value and continue typing
|
1455
|
+
* in this segment.
|
1456
|
+
*/
|
1457
|
+
return { value: num, moveToNext };
|
1458
|
+
}
|
1459
|
+
/**
|
1460
|
+
* If the number of digits is 2, or if the total with the existing digit
|
1461
|
+
* and the pressed digit is greater than the maximum value for this
|
1462
|
+
* month, then we will reset the segment as if the user had pressed the
|
1463
|
+
* backspace key and then typed the number.
|
1464
|
+
*/
|
1465
|
+
const digits = prev.toString().length;
|
1466
|
+
const total = Number.parseInt(prev.toString() + num.toString());
|
1467
|
+
/**
|
1468
|
+
* If the number of digits is 2, or if the total with the existing digit
|
1469
|
+
* and the pressed digit is greater than the maximum value for this
|
1470
|
+
* month, then we will reset the segment as if the user had pressed the
|
1471
|
+
* backspace key and then typed the number.
|
1472
|
+
*/
|
1473
|
+
if (digits === 2 || total > max) {
|
1474
|
+
/**
|
1475
|
+
* As we're doing elsewhere, we're checking if the number is greater
|
1476
|
+
* than the max start digit (0-3 in most months), and if so, we're
|
1477
|
+
* going to move to the next segment.
|
1478
|
+
*/
|
1479
|
+
if (num > maxStart) {
|
1480
|
+
// move to next
|
1481
|
+
moveToNext = true;
|
1482
|
+
}
|
1483
|
+
return { value: num, moveToNext };
|
1484
|
+
}
|
1485
|
+
// move to next
|
1486
|
+
moveToNext = true;
|
1487
|
+
return { value: total, moveToNext };
|
1488
|
+
}
|
1489
|
+
function minuteSecondIncrementation({ e, part, dateRef, prevValue }) {
|
1490
|
+
const sign = e.key === ARROW_UP ? 1 : -1;
|
1491
|
+
const min = 0;
|
1492
|
+
const max = 59;
|
1493
|
+
if (prevValue === null)
|
1494
|
+
return sign > 0 ? min : max;
|
1495
|
+
const cycleArgs = [part, sign];
|
1496
|
+
return dateRef.set({ [part]: prevValue }).cycle(...cycleArgs)[part];
|
1497
|
+
}
|
1498
|
+
const attributes = computed(() => segmentBuilders[props.part]?.attrs({
|
1499
|
+
disabled: props.disabled(),
|
1500
|
+
placeholder: props.placeholder(),
|
1501
|
+
hourCycle: props.hourCycle,
|
1502
|
+
segmentValues: props.segmentValues(),
|
1503
|
+
formatter: props.formatter
|
1504
|
+
}) ?? {});
|
1505
|
+
function handleMonthSegmentKeydown(e) {
|
1506
|
+
if (!isAcceptableSegmentKey(e.key) || isSegmentNavigationKey(e.key))
|
1507
|
+
return;
|
1508
|
+
const prevValue = props.segmentValues().month;
|
1509
|
+
if (e.key === ARROW_DOWN || e.key === ARROW_UP) {
|
1510
|
+
props.segmentValues.update((prev) => ({
|
1511
|
+
...prev,
|
1512
|
+
month: dateTimeValueIncrementation({
|
1513
|
+
e,
|
1514
|
+
part: 'month',
|
1515
|
+
dateRef: props.placeholder(),
|
1516
|
+
prevValue
|
1517
|
+
})
|
1518
|
+
}));
|
1519
|
+
return;
|
1520
|
+
}
|
1521
|
+
if (isNumberString(e.key)) {
|
1522
|
+
const num = Number.parseInt(e.key);
|
1523
|
+
const { value, moveToNext } = updateDayOrMonth(12, num, prevValue);
|
1524
|
+
props.segmentValues.update((prev) => ({ ...prev, month: value }));
|
1525
|
+
if (moveToNext)
|
1526
|
+
props.focusNext();
|
1527
|
+
}
|
1528
|
+
if (e.key === BACKSPACE) {
|
1529
|
+
props.hasLeftFocus.set(false);
|
1530
|
+
props.segmentValues.update((prev) => ({ ...prev, month: deleteValue(prevValue) }));
|
1531
|
+
}
|
1532
|
+
}
|
1533
|
+
function handleDaySegmentKeydown(e) {
|
1534
|
+
if (!isAcceptableSegmentKey(e.key) || isSegmentNavigationKey(e.key))
|
1535
|
+
return;
|
1536
|
+
const prevValue = props.segmentValues().day;
|
1537
|
+
if (e.key === ARROW_DOWN || e.key === ARROW_UP) {
|
1538
|
+
props.segmentValues.update((prev) => ({
|
1539
|
+
...prev,
|
1540
|
+
day: dateTimeValueIncrementation({
|
1541
|
+
e,
|
1542
|
+
part: 'day',
|
1543
|
+
dateRef: props.placeholder(),
|
1544
|
+
prevValue
|
1545
|
+
})
|
1546
|
+
}));
|
1547
|
+
return;
|
1548
|
+
}
|
1549
|
+
if (isNumberString(e.key)) {
|
1550
|
+
const num = Number.parseInt(e.key);
|
1551
|
+
const segmentMonthValue = props.segmentValues().month;
|
1552
|
+
const daysInMonth = segmentMonthValue
|
1553
|
+
? getDaysInMonth(props.placeholder().set({ month: segmentMonthValue }))
|
1554
|
+
: getDaysInMonth(props.placeholder());
|
1555
|
+
const { value, moveToNext } = updateDayOrMonth(daysInMonth, num, prevValue);
|
1556
|
+
props.segmentValues.update((prev) => ({ ...prev, day: value }));
|
1557
|
+
if (moveToNext)
|
1558
|
+
props.focusNext();
|
1559
|
+
}
|
1560
|
+
if (e.key === BACKSPACE) {
|
1561
|
+
props.hasLeftFocus.set(false);
|
1562
|
+
props.segmentValues.update((prev) => ({ ...prev, day: deleteValue(prevValue) }));
|
1563
|
+
}
|
1564
|
+
}
|
1565
|
+
function handleYearSegmentKeydown(e) {
|
1566
|
+
if (!isAcceptableSegmentKey(e.key) || isSegmentNavigationKey(e.key))
|
1567
|
+
return;
|
1568
|
+
const prevValue = props.segmentValues().year;
|
1569
|
+
if (e.key === ARROW_DOWN || e.key === ARROW_UP) {
|
1570
|
+
props.segmentValues.update((prev) => ({
|
1571
|
+
...prev,
|
1572
|
+
year: dateTimeValueIncrementation({
|
1573
|
+
e,
|
1574
|
+
part: 'year',
|
1575
|
+
dateRef: props.placeholder(),
|
1576
|
+
prevValue
|
1577
|
+
})
|
1578
|
+
}));
|
1579
|
+
return;
|
1580
|
+
}
|
1581
|
+
if (isNumberString(e.key)) {
|
1582
|
+
const num = Number.parseInt(e.key);
|
1583
|
+
const { value, moveToNext } = updateYear(num, prevValue);
|
1584
|
+
props.segmentValues.update((prev) => ({ ...prev, year: value }));
|
1585
|
+
if (moveToNext)
|
1586
|
+
props.focusNext();
|
1587
|
+
}
|
1588
|
+
if (e.key === BACKSPACE) {
|
1589
|
+
props.hasLeftFocus.set(false);
|
1590
|
+
props.segmentValues.update((prev) => ({ ...prev, year: deleteValue(prevValue) }));
|
1591
|
+
}
|
1592
|
+
}
|
1593
|
+
function handleHourSegmentKeydown(e) {
|
1594
|
+
const dateRef = props.placeholder();
|
1595
|
+
const values = props.segmentValues();
|
1596
|
+
if (!isAcceptableSegmentKey(e.key) ||
|
1597
|
+
isSegmentNavigationKey(e.key) ||
|
1598
|
+
!('hour' in dateRef) ||
|
1599
|
+
!('hour' in values))
|
1600
|
+
return;
|
1601
|
+
const prevValue = values.hour;
|
1602
|
+
const hourCycle = props.hourCycle;
|
1603
|
+
if (e.key === ARROW_UP || e.key === ARROW_DOWN) {
|
1604
|
+
props.segmentValues.update((prev) => ({
|
1605
|
+
...prev,
|
1606
|
+
hour: dateTimeValueIncrementation({
|
1607
|
+
e,
|
1608
|
+
part: 'hour',
|
1609
|
+
dateRef: props.placeholder(),
|
1610
|
+
prevValue,
|
1611
|
+
hourCycle
|
1612
|
+
})
|
1613
|
+
}));
|
1614
|
+
if ('dayPeriod' in props.segmentValues() && values.hour != null) {
|
1615
|
+
if (values.hour < 12)
|
1616
|
+
props.segmentValues.update((prev) => ({ ...prev, dayPeriod: 'AM' }));
|
1617
|
+
else if (values.hour)
|
1618
|
+
props.segmentValues.update((prev) => ({ ...prev, dayPeriod: 'PM' }));
|
1619
|
+
}
|
1620
|
+
return;
|
1621
|
+
}
|
1622
|
+
if (isNumberString(e.key)) {
|
1623
|
+
const num = Number.parseInt(e.key);
|
1624
|
+
const { value, moveToNext } = updateHour(num, prevValue);
|
1625
|
+
if ('dayPeriod' in props.segmentValues() && value && value > 12)
|
1626
|
+
props.segmentValues.update((prev) => ({ ...prev, dayPeriod: 'AM' }));
|
1627
|
+
else if ('dayPeriod' in props.segmentValues() && value)
|
1628
|
+
props.segmentValues.update((prev) => ({ ...prev, dayPeriod: 'PM' }));
|
1629
|
+
props.segmentValues.update((prev) => ({ ...prev, hour: value }));
|
1630
|
+
if (moveToNext)
|
1631
|
+
props.focusNext();
|
1632
|
+
}
|
1633
|
+
if (e.key === BACKSPACE) {
|
1634
|
+
props.hasLeftFocus.set(false);
|
1635
|
+
props.segmentValues.update((prev) => ({ ...prev, hour: deleteValue(prevValue) }));
|
1636
|
+
}
|
1637
|
+
}
|
1638
|
+
function handleMinuteSegmentKeydown(e) {
|
1639
|
+
const dateRef = props.placeholder();
|
1640
|
+
const values = props.segmentValues();
|
1641
|
+
if (!isAcceptableSegmentKey(e.key) ||
|
1642
|
+
isSegmentNavigationKey(e.key) ||
|
1643
|
+
!('minute' in dateRef) ||
|
1644
|
+
!('minute' in values))
|
1645
|
+
return;
|
1646
|
+
const prevValue = values.minute;
|
1647
|
+
props.segmentValues.update((prev) => ({
|
1648
|
+
...prev,
|
1649
|
+
minute: minuteSecondIncrementation({
|
1650
|
+
e,
|
1651
|
+
part: 'minute',
|
1652
|
+
dateRef: props.placeholder(),
|
1653
|
+
prevValue
|
1654
|
+
})
|
1655
|
+
}));
|
1656
|
+
if (isNumberString(e.key)) {
|
1657
|
+
const num = Number.parseInt(e.key);
|
1658
|
+
const { value, moveToNext } = updateMinuteOrSecond(num, prevValue);
|
1659
|
+
props.segmentValues.update((prev) => ({ ...prev, minute: value }));
|
1660
|
+
if (moveToNext)
|
1661
|
+
props.focusNext();
|
1662
|
+
}
|
1663
|
+
if (e.key === BACKSPACE) {
|
1664
|
+
props.hasLeftFocus.set(false);
|
1665
|
+
props.segmentValues.update((prev) => ({ ...prev, minute: deleteValue(prevValue) }));
|
1666
|
+
}
|
1667
|
+
}
|
1668
|
+
function handleSecondSegmentKeydown(e) {
|
1669
|
+
const dateRef = props.placeholder();
|
1670
|
+
const values = props.segmentValues();
|
1671
|
+
if (!isAcceptableSegmentKey(e.key) ||
|
1672
|
+
isSegmentNavigationKey(e.key) ||
|
1673
|
+
!('second' in dateRef) ||
|
1674
|
+
!('second' in values))
|
1675
|
+
return;
|
1676
|
+
const prevValue = values.second;
|
1677
|
+
props.segmentValues.update((prev) => ({
|
1678
|
+
...prev,
|
1679
|
+
second: minuteSecondIncrementation({
|
1680
|
+
e,
|
1681
|
+
part: 'second',
|
1682
|
+
dateRef: props.placeholder(),
|
1683
|
+
prevValue
|
1684
|
+
})
|
1685
|
+
}));
|
1686
|
+
if (isNumberString(e.key)) {
|
1687
|
+
const num = Number.parseInt(e.key);
|
1688
|
+
const { value, moveToNext } = updateMinuteOrSecond(num, prevValue);
|
1689
|
+
props.segmentValues.update((prev) => ({ ...prev, second: value }));
|
1690
|
+
if (moveToNext)
|
1691
|
+
props.focusNext();
|
1692
|
+
}
|
1693
|
+
if (e.key === BACKSPACE) {
|
1694
|
+
props.hasLeftFocus.set(false);
|
1695
|
+
props.segmentValues.update((prev) => ({ ...prev, second: deleteValue(prevValue) }));
|
1696
|
+
}
|
1697
|
+
}
|
1698
|
+
function handleDayPeriodSegmentKeydown(e) {
|
1699
|
+
if (((!isAcceptableSegmentKey(e.key) || isSegmentNavigationKey(e.key)) && e.key !== 'a' && e.key !== 'p') ||
|
1700
|
+
!('hour' in props.placeholder()) ||
|
1701
|
+
!('dayPeriod' in props.segmentValues()))
|
1702
|
+
return;
|
1703
|
+
const values = props.segmentValues();
|
1704
|
+
if (e.key === ARROW_UP || e.key === ARROW_DOWN) {
|
1705
|
+
if (values.dayPeriod === 'AM') {
|
1706
|
+
props.segmentValues.update((prev) => ({ ...prev, dayPeriod: 'PM' }));
|
1707
|
+
props.segmentValues.update((prev) => ({ ...prev, hour: values.hour + 12 }));
|
1708
|
+
return;
|
1709
|
+
}
|
1710
|
+
props.segmentValues.update((prev) => ({ ...prev, dayPeriod: 'AM' }));
|
1711
|
+
props.segmentValues.update((prev) => ({ ...prev, hour: values.hour - 12 }));
|
1712
|
+
return;
|
1713
|
+
}
|
1714
|
+
if (['a', 'A'].includes(e.key) && values.dayPeriod !== 'AM') {
|
1715
|
+
props.segmentValues.update((prev) => ({ ...prev, dayPeriod: 'AM' }));
|
1716
|
+
props.segmentValues.update((prev) => ({ ...prev, hour: values.hour - 12 }));
|
1717
|
+
return;
|
1718
|
+
}
|
1719
|
+
if (['p', 'P'].includes(e.key) && values.dayPeriod !== 'PM') {
|
1720
|
+
props.segmentValues.update((prev) => ({ ...prev, dayPeriod: 'PM' }));
|
1721
|
+
props.segmentValues.update((prev) => ({ ...prev, hour: values.hour + 12 }));
|
1722
|
+
}
|
1723
|
+
}
|
1724
|
+
function handleSegmentKeydown(e) {
|
1725
|
+
const disabled = props.disabled();
|
1726
|
+
const readonly = props.readonly();
|
1727
|
+
if (e.key !== TAB)
|
1728
|
+
e.preventDefault();
|
1729
|
+
if (disabled || readonly)
|
1730
|
+
return;
|
1731
|
+
const segmentKeydownHandlers = {
|
1732
|
+
month: handleMonthSegmentKeydown,
|
1733
|
+
day: handleDaySegmentKeydown,
|
1734
|
+
year: handleYearSegmentKeydown,
|
1735
|
+
hour: handleHourSegmentKeydown,
|
1736
|
+
minute: handleMinuteSegmentKeydown,
|
1737
|
+
second: handleSecondSegmentKeydown,
|
1738
|
+
dayPeriod: handleDayPeriodSegmentKeydown,
|
1739
|
+
timeZoneName: () => { }
|
1740
|
+
};
|
1741
|
+
segmentKeydownHandlers[props.part](e);
|
1742
|
+
if (![ARROW_LEFT, ARROW_RIGHT].includes(e.key) &&
|
1743
|
+
e.key !== TAB &&
|
1744
|
+
e.key !== SHIFT &&
|
1745
|
+
isAcceptableSegmentKey(e.key)) {
|
1746
|
+
if (Object.values(props.segmentValues()).every((item) => item !== null)) {
|
1747
|
+
const updateObject = { ...props.segmentValues() };
|
1748
|
+
let dateRef = props.placeholder().copy();
|
1749
|
+
Object.keys(updateObject).forEach((part) => {
|
1750
|
+
const value = updateObject[part];
|
1751
|
+
dateRef = dateRef.set({ [part]: value });
|
1752
|
+
});
|
1753
|
+
props.modelValue.set(dateRef.copy());
|
1754
|
+
}
|
1755
|
+
}
|
1756
|
+
}
|
1757
|
+
return {
|
1758
|
+
handleSegmentClick,
|
1759
|
+
handleSegmentKeydown,
|
1760
|
+
attributes
|
1761
|
+
};
|
1762
|
+
}
|
1763
|
+
|
758
1764
|
var RdxPositionSide;
|
759
1765
|
(function (RdxPositionSide) {
|
760
1766
|
RdxPositionSide["Top"] = "top";
|
@@ -1011,5 +2017,5 @@ function watch(deps, fn, options) {
|
|
1011
2017
|
* Generated bundle index. Do not edit.
|
1012
2018
|
*/
|
1013
2019
|
|
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 };
|
2020
|
+
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, createContent, createFormatter, createMonth, createMonths, 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, segmentBuilders, syncSegmentValues, syncTimeSegmentValues, toDate, useDateField, watch };
|
1015
2021
|
//# sourceMappingURL=radix-ng-primitives-core.mjs.map
|