@skbkontur/react-ui 6.1.1 → 6.1.2-bea94.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/components/Input/Input.d.ts +1 -1
- package/components/Input/Input.js.map +1 -1
- package/components/TimePicker/TimeClockIcon.d.ts +3 -0
- package/components/TimePicker/TimeClockIcon.js +11 -0
- package/components/TimePicker/TimeClockIcon.js.map +1 -0
- package/components/TimePicker/TimeFragmentsView.d.ts +15 -0
- package/components/TimePicker/TimeFragmentsView.js +72 -0
- package/components/TimePicker/TimeFragmentsView.js.map +1 -0
- package/components/TimePicker/TimeFragmentsView.styles.d.ts +12 -0
- package/components/TimePicker/TimeFragmentsView.styles.js +43 -0
- package/components/TimePicker/TimeFragmentsView.styles.js.map +1 -0
- package/components/TimePicker/TimeInput.d.ts +22 -0
- package/components/TimePicker/TimeInput.js +103 -0
- package/components/TimePicker/TimeInput.js.map +1 -0
- package/components/TimePicker/TimePicker.d.ts +70 -0
- package/components/TimePicker/TimePicker.js +506 -0
- package/components/TimePicker/TimePicker.js.map +1 -0
- package/components/TimePicker/TimePicker.styles.d.ts +14 -0
- package/components/TimePicker/TimePicker.styles.js +45 -0
- package/components/TimePicker/TimePicker.styles.js.map +1 -0
- package/components/TimePicker/TimePickerMobilePopup.d.ts +38 -0
- package/components/TimePicker/TimePickerMobilePopup.js +21 -0
- package/components/TimePicker/TimePickerMobilePopup.js.map +1 -0
- package/components/TimePicker/TimePickerPopup.d.ts +20 -0
- package/components/TimePicker/TimePickerPopup.js +18 -0
- package/components/TimePicker/TimePickerPopup.js.map +1 -0
- package/components/TimePicker/TimePickerSlots.d.ts +17 -0
- package/components/TimePicker/TimePickerSlots.js +80 -0
- package/components/TimePicker/TimePickerSlots.js.map +1 -0
- package/components/TimePicker/helpers/TimePicker.constants.d.ts +22 -0
- package/components/TimePicker/helpers/TimePicker.constants.js +31 -0
- package/components/TimePicker/helpers/TimePicker.constants.js.map +1 -0
- package/components/TimePicker/helpers/TimePicker.editing.d.ts +23 -0
- package/components/TimePicker/helpers/TimePicker.editing.js +101 -0
- package/components/TimePicker/helpers/TimePicker.editing.js.map +1 -0
- package/components/TimePicker/helpers/TimePicker.layout.d.ts +6 -0
- package/components/TimePicker/helpers/TimePicker.layout.js +49 -0
- package/components/TimePicker/helpers/TimePicker.layout.js.map +1 -0
- package/components/TimePicker/helpers/TimePicker.selection.d.ts +5 -0
- package/components/TimePicker/helpers/TimePicker.selection.js +23 -0
- package/components/TimePicker/helpers/TimePicker.selection.js.map +1 -0
- package/components/TimePicker/helpers/TimePicker.shared.d.ts +47 -0
- package/components/TimePicker/helpers/TimePicker.shared.js +70 -0
- package/components/TimePicker/helpers/TimePicker.shared.js.map +1 -0
- package/components/TimePicker/helpers/TimePicker.value.d.ts +23 -0
- package/components/TimePicker/helpers/TimePicker.value.js +71 -0
- package/components/TimePicker/helpers/TimePicker.value.js.map +1 -0
- package/components/TimePicker/helpers/scrollSelectedSlotIntoView.d.ts +4 -0
- package/components/TimePicker/helpers/scrollSelectedSlotIntoView.js +24 -0
- package/components/TimePicker/helpers/scrollSelectedSlotIntoView.js.map +1 -0
- package/components/TimePicker/hooks/useTimePickerDropdown.d.ts +17 -0
- package/components/TimePicker/hooks/useTimePickerDropdown.js +62 -0
- package/components/TimePicker/hooks/useTimePickerDropdown.js.map +1 -0
- package/components/TimePicker/hooks/useTimePickerSelection.d.ts +18 -0
- package/components/TimePicker/hooks/useTimePickerSelection.js +66 -0
- package/components/TimePicker/hooks/useTimePickerSelection.js.map +1 -0
- package/components/TimePicker/hooks/useTimePickerValue.d.ts +18 -0
- package/components/TimePicker/hooks/useTimePickerValue.js +58 -0
- package/components/TimePicker/hooks/useTimePickerValue.js.map +1 -0
- package/components/TimePicker/index.d.ts +2 -0
- package/components/TimePicker/index.js +2 -0
- package/components/TimePicker/index.js.map +1 -0
- package/index.d.ts +1 -0
- package/index.js +1 -0
- package/index.js.map +1 -1
- package/internal/NativeTimeInput/NativeTimeInput.d.ts +16 -0
- package/internal/NativeTimeInput/NativeTimeInput.js +25 -0
- package/internal/NativeTimeInput/NativeTimeInput.js.map +1 -0
- package/internal/NativeTimeInput/NativeTimeInput.styles.d.ts +3 -0
- package/internal/NativeTimeInput/NativeTimeInput.styles.js +15 -0
- package/internal/NativeTimeInput/NativeTimeInput.styles.js.map +1 -0
- package/internal/NativeTimeInput/NativeTimeInput.utils.d.ts +7 -0
- package/internal/NativeTimeInput/NativeTimeInput.utils.js +28 -0
- package/internal/NativeTimeInput/NativeTimeInput.utils.js.map +1 -0
- package/internal/NativeTimeInput/index.d.ts +1 -0
- package/internal/NativeTimeInput/index.js +2 -0
- package/internal/NativeTimeInput/index.js.map +1 -0
- package/internal/icons2022/TimeClockIcon/TimeClockIcon16Light.d.ts +2 -0
- package/internal/icons2022/TimeClockIcon/TimeClockIcon16Light.js +20 -0
- package/internal/icons2022/TimeClockIcon/TimeClockIcon16Light.js.map +1 -0
- package/internal/icons2022/TimeClockIcon/TimeClockIcon20Light.d.ts +2 -0
- package/internal/icons2022/TimeClockIcon/TimeClockIcon20Light.js +20 -0
- package/internal/icons2022/TimeClockIcon/TimeClockIcon20Light.js.map +1 -0
- package/internal/icons2022/TimeClockIcon/TimeClockIcon24Regular.d.ts +2 -0
- package/internal/icons2022/TimeClockIcon/TimeClockIcon24Regular.js +20 -0
- package/internal/icons2022/TimeClockIcon/TimeClockIcon24Regular.js.map +1 -0
- package/internal/themes/BasicTheme.d.ts +34 -0
- package/internal/themes/BasicTheme.js +96 -0
- package/internal/themes/BasicTheme.js.map +1 -1
- package/package.json +5 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TimePicker.selection.js","sourceRoot":"","sources":["TimePicker.selection.ts"],"names":[],"mappings":"AAEA,sGAAsG;AACtG,MAAM,CAAC,IAAM,kBAAkB,GAAG,UAAC,OAAoB,EAAE,MAAkB;IACzE,QAAQ,OAAO,EAAE,CAAC;QAChB,KAAK,OAAO;YACV,OAAO,SAAS,CAAC;QAEnB,KAAK,SAAS;YACZ,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC;QAEhD,KAAK,SAAS;YACZ,OAAO,IAAI,CAAC;IAChB,CAAC;AACH,CAAC,CAAC;AAEF,oGAAoG;AACpG,MAAM,CAAC,IAAM,sBAAsB,GAAG,UAAC,OAAoB;IACzD,QAAQ,OAAO,EAAE,CAAC;QAChB,KAAK,OAAO;YACV,OAAO,IAAI,CAAC;QAEd,KAAK,SAAS;YACZ,OAAO,OAAO,CAAC;QAEjB,KAAK,SAAS;YACZ,OAAO,SAAS,CAAC;IACrB,CAAC;AACH,CAAC,CAAC","sourcesContent":["import type { TimeFormat, TimeSegment } from './TimePicker.shared.js';\n\n/** Возвращает следующий сегмент для навигации по вводу или `null`, если текущий сегмент последний. */\nexport const getNextTimeSegment = (segment: TimeSegment, format: TimeFormat): TimeSegment | null => {\n switch (segment) {\n case 'hours':\n return 'minutes';\n\n case 'minutes':\n return format === 'second' ? 'seconds' : null;\n\n case 'seconds':\n return null;\n }\n};\n\n/** Возвращает предыдущий сегмент для навигации по вводу или `null`, если текущий сегмент первый. */\nexport const getPreviousTimeSegment = (segment: TimeSegment): TimeSegment | null => {\n switch (segment) {\n case 'hours':\n return null;\n\n case 'minutes':\n return 'hours';\n\n case 'seconds':\n return 'minutes';\n }\n};\n"]}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
export type TimeFormat = 'minute' | 'second';
|
|
3
|
+
export type TimeSegment = 'hours' | 'minutes' | 'seconds';
|
|
4
|
+
export interface TimeSlot {
|
|
5
|
+
disabled?: boolean;
|
|
6
|
+
value: string;
|
|
7
|
+
label?: ReactNode;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Результат ввода одной цифры в активный сегмент.
|
|
11
|
+
* Содержит следующее display-значение и инструкции для UI по выбору сегмента и необходимости blink.
|
|
12
|
+
*/
|
|
13
|
+
export interface TimeDigitInputResult {
|
|
14
|
+
isFinalPart: boolean;
|
|
15
|
+
isCompletedPart: boolean;
|
|
16
|
+
nextValue: string;
|
|
17
|
+
selectedSegment: TimeSegment;
|
|
18
|
+
shouldBlink: boolean;
|
|
19
|
+
}
|
|
20
|
+
/** Возвращает список сегментов, используемых в заданном формате времени. */
|
|
21
|
+
export declare const getTimeSegments: (format: TimeFormat) => TimeSegment[];
|
|
22
|
+
/** Возвращает верхнюю границу допустимого значения для конкретного сегмента. */
|
|
23
|
+
export declare const getTimeSegmentMax: (segment: TimeSegment) => number;
|
|
24
|
+
/** Извлекает из сегмента только цифры и ограничивает результат длиной сегмента. */
|
|
25
|
+
export declare const getDigits: (segment: string) => string;
|
|
26
|
+
/**
|
|
27
|
+
* Приводит произвольное строковое значение сегмента к display-форме:
|
|
28
|
+
* оставляет только цифры и плейсхолдеры, ограничивает длину и дополняет незаполненные позиции плейсхолдером.
|
|
29
|
+
*/
|
|
30
|
+
export declare const sanitizeSegment: (segment: string) => string;
|
|
31
|
+
/**
|
|
32
|
+
* Нормализует сегмент для committed-значения.
|
|
33
|
+
* Пустой сегмент преобразуется в `00`, а значения выше максимума — к максимуму сегмента.
|
|
34
|
+
*/
|
|
35
|
+
export declare const normalizeTimeSegment: (segmentValue: string, segment: TimeSegment) => string;
|
|
36
|
+
/**
|
|
37
|
+
* Нормализует сегмент для режима редактирования.
|
|
38
|
+
* В отличие от committed-нормализации, полностью пустой сегмент остается пустым.
|
|
39
|
+
*/
|
|
40
|
+
export declare const normalizeEditableSegment: (segmentValue: string, segment: TimeSegment) => string;
|
|
41
|
+
/** Проверяет, что сегмент содержит одну введенную цифру и ожидает вторую. */
|
|
42
|
+
export declare const hasPendingSingleDigit: (segmentValue: string) => boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Разбивает входное значение на display-сегменты текущего формата.
|
|
45
|
+
* Поддерживает committed-значение (`hh:mm[:ss]`) и display-значение с плейсхолдерами
|
|
46
|
+
*/
|
|
47
|
+
export declare const getDisplaySegments: (value: string, format: TimeFormat) => string[];
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { DIGIT_REGEXP, EMPTY_SEGMENT, EMPTY_VALUE, HOURS_MAX_VALUE, MINUTES_AND_SECONDS_MAX_VALUE, NON_DIGIT_REGEXP, TIME_PLACEHOLDER_CHAR, TIME_SEGMENT_LENGTH, TIME_SEGMENTS_BY_FORMAT, TIME_SEPARATOR, ZERO_PAD_CHAR, } from './TimePicker.constants.js';
|
|
2
|
+
/** Возвращает список сегментов, используемых в заданном формате времени. */
|
|
3
|
+
export var getTimeSegments = function (format) { return TIME_SEGMENTS_BY_FORMAT[format]; };
|
|
4
|
+
/** Возвращает верхнюю границу допустимого значения для конкретного сегмента. */
|
|
5
|
+
export var getTimeSegmentMax = function (segment) {
|
|
6
|
+
return segment === 'hours' ? HOURS_MAX_VALUE : MINUTES_AND_SECONDS_MAX_VALUE;
|
|
7
|
+
};
|
|
8
|
+
/** Извлекает из сегмента только цифры и ограничивает результат длиной сегмента. */
|
|
9
|
+
export var getDigits = function (segment) {
|
|
10
|
+
return segment.replace(NON_DIGIT_REGEXP, EMPTY_VALUE).slice(0, TIME_SEGMENT_LENGTH);
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Приводит произвольное строковое значение сегмента к display-форме:
|
|
14
|
+
* оставляет только цифры и плейсхолдеры, ограничивает длину и дополняет незаполненные позиции плейсхолдером.
|
|
15
|
+
*/
|
|
16
|
+
export var sanitizeSegment = function (segment) {
|
|
17
|
+
return Array.from(segment)
|
|
18
|
+
.filter(function (char) { return DIGIT_REGEXP.test(char) || char === TIME_PLACEHOLDER_CHAR; })
|
|
19
|
+
.slice(0, TIME_SEGMENT_LENGTH)
|
|
20
|
+
.join('')
|
|
21
|
+
.padEnd(TIME_SEGMENT_LENGTH, TIME_PLACEHOLDER_CHAR);
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Нормализует сегмент для committed-значения.
|
|
25
|
+
* Пустой сегмент преобразуется в `00`, а значения выше максимума — к максимуму сегмента.
|
|
26
|
+
*/
|
|
27
|
+
export var normalizeTimeSegment = function (segmentValue, segment) {
|
|
28
|
+
var digits = getDigits(segmentValue);
|
|
29
|
+
if (!digits) {
|
|
30
|
+
return '00';
|
|
31
|
+
}
|
|
32
|
+
return String(Math.min(Number(digits), getTimeSegmentMax(segment))).padStart(TIME_SEGMENT_LENGTH, ZERO_PAD_CHAR);
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Нормализует сегмент для режима редактирования.
|
|
36
|
+
* В отличие от committed-нормализации, полностью пустой сегмент остается пустым.
|
|
37
|
+
*/
|
|
38
|
+
export var normalizeEditableSegment = function (segmentValue, segment) {
|
|
39
|
+
var digits = getDigits(segmentValue);
|
|
40
|
+
if (!digits) {
|
|
41
|
+
return EMPTY_SEGMENT;
|
|
42
|
+
}
|
|
43
|
+
return String(Math.min(Number(digits), getTimeSegmentMax(segment))).padStart(TIME_SEGMENT_LENGTH, ZERO_PAD_CHAR);
|
|
44
|
+
};
|
|
45
|
+
/** Проверяет, что сегмент содержит одну введенную цифру и ожидает вторую. */
|
|
46
|
+
export var hasPendingSingleDigit = function (segmentValue) {
|
|
47
|
+
return new RegExp("^\\d".concat(TIME_PLACEHOLDER_CHAR, "$")).test(segmentValue);
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Разбивает входное значение на display-сегменты текущего формата.
|
|
51
|
+
* Поддерживает committed-значение (`hh:mm[:ss]`) и display-значение с плейсхолдерами
|
|
52
|
+
*/
|
|
53
|
+
export var getDisplaySegments = function (value, format) {
|
|
54
|
+
var segments = getTimeSegments(format);
|
|
55
|
+
var fallback = segments.map(function () { return EMPTY_SEGMENT; });
|
|
56
|
+
if (value === EMPTY_VALUE) {
|
|
57
|
+
return fallback;
|
|
58
|
+
}
|
|
59
|
+
if (value.includes(TIME_SEPARATOR) || value.includes(TIME_PLACEHOLDER_CHAR)) {
|
|
60
|
+
var sourceParts_1 = value.split(TIME_SEPARATOR);
|
|
61
|
+
return segments.map(function (_, index) { var _a; return sanitizeSegment((_a = sourceParts_1[index]) !== null && _a !== void 0 ? _a : EMPTY_VALUE); });
|
|
62
|
+
}
|
|
63
|
+
var digits = value.replace(NON_DIGIT_REGEXP, EMPTY_VALUE);
|
|
64
|
+
return segments.map(function (_, index) {
|
|
65
|
+
var segmentStart = index * TIME_SEGMENT_LENGTH;
|
|
66
|
+
var segmentEnd = segmentStart + TIME_SEGMENT_LENGTH;
|
|
67
|
+
return sanitizeSegment(digits.slice(segmentStart, segmentEnd));
|
|
68
|
+
});
|
|
69
|
+
};
|
|
70
|
+
//# sourceMappingURL=TimePicker.shared.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TimePicker.shared.js","sourceRoot":"","sources":["TimePicker.shared.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ReactNode } from 'react';\n\nimport {\n DIGIT_REGEXP,\n EMPTY_SEGMENT,\n EMPTY_VALUE,\n HOURS_MAX_VALUE,\n MINUTES_AND_SECONDS_MAX_VALUE,\n NON_DIGIT_REGEXP,\n TIME_PLACEHOLDER_CHAR,\n TIME_SEGMENT_LENGTH,\n TIME_SEGMENTS_BY_FORMAT,\n TIME_SEPARATOR,\n ZERO_PAD_CHAR,\n} from './TimePicker.constants.js';\n\nexport type TimeFormat = 'minute' | 'second';\nexport type TimeSegment = 'hours' | 'minutes' | 'seconds';\n\nexport interface TimeSlot {\n disabled?: boolean;\n value: string;\n label?: ReactNode;\n}\n\n/**\n * Результат ввода одной цифры в активный сегмент.\n * Содержит следующее display-значение и инструкции для UI по выбору сегмента и необходимости blink.\n */\nexport interface TimeDigitInputResult {\n isFinalPart: boolean;\n isCompletedPart: boolean;\n nextValue: string;\n selectedSegment: TimeSegment;\n shouldBlink: boolean;\n}\n\n/** Возвращает список сегментов, используемых в заданном формате времени. */\nexport const getTimeSegments = (format: TimeFormat): TimeSegment[] => TIME_SEGMENTS_BY_FORMAT[format];\n\n/** Возвращает верхнюю границу допустимого значения для конкретного сегмента. */\nexport const getTimeSegmentMax = (segment: TimeSegment): number =>\n segment === 'hours' ? HOURS_MAX_VALUE : MINUTES_AND_SECONDS_MAX_VALUE;\n\n/** Извлекает из сегмента только цифры и ограничивает результат длиной сегмента. */\nexport const getDigits = (segment: string): string =>\n segment.replace(NON_DIGIT_REGEXP, EMPTY_VALUE).slice(0, TIME_SEGMENT_LENGTH);\n\n/**\n * Приводит произвольное строковое значение сегмента к display-форме:\n * оставляет только цифры и плейсхолдеры, ограничивает длину и дополняет незаполненные позиции плейсхолдером.\n */\nexport const sanitizeSegment = (segment: string): string =>\n Array.from(segment)\n .filter((char) => DIGIT_REGEXP.test(char) || char === TIME_PLACEHOLDER_CHAR)\n .slice(0, TIME_SEGMENT_LENGTH)\n .join('')\n .padEnd(TIME_SEGMENT_LENGTH, TIME_PLACEHOLDER_CHAR);\n\n/**\n * Нормализует сегмент для committed-значения.\n * Пустой сегмент преобразуется в `00`, а значения выше максимума — к максимуму сегмента.\n */\nexport const normalizeTimeSegment = (segmentValue: string, segment: TimeSegment): string => {\n const digits = getDigits(segmentValue);\n\n if (!digits) {\n return '00';\n }\n\n return String(Math.min(Number(digits), getTimeSegmentMax(segment))).padStart(TIME_SEGMENT_LENGTH, ZERO_PAD_CHAR);\n};\n\n/**\n * Нормализует сегмент для режима редактирования.\n * В отличие от committed-нормализации, полностью пустой сегмент остается пустым.\n */\nexport const normalizeEditableSegment = (segmentValue: string, segment: TimeSegment): string => {\n const digits = getDigits(segmentValue);\n\n if (!digits) {\n return EMPTY_SEGMENT;\n }\n\n return String(Math.min(Number(digits), getTimeSegmentMax(segment))).padStart(TIME_SEGMENT_LENGTH, ZERO_PAD_CHAR);\n};\n\n/** Проверяет, что сегмент содержит одну введенную цифру и ожидает вторую. */\nexport const hasPendingSingleDigit = (segmentValue: string): boolean =>\n new RegExp(`^\\\\d${TIME_PLACEHOLDER_CHAR}$`).test(segmentValue);\n\n/**\n * Разбивает входное значение на display-сегменты текущего формата.\n * Поддерживает committed-значение (`hh:mm[:ss]`) и display-значение с плейсхолдерами\n */\nexport const getDisplaySegments = (value: string, format: TimeFormat): string[] => {\n const segments = getTimeSegments(format);\n const fallback = segments.map(() => EMPTY_SEGMENT);\n\n if (value === EMPTY_VALUE) {\n return fallback;\n }\n\n if (value.includes(TIME_SEPARATOR) || value.includes(TIME_PLACEHOLDER_CHAR)) {\n const sourceParts = value.split(TIME_SEPARATOR);\n return segments.map((_, index) => sanitizeSegment(sourceParts[index] ?? EMPTY_VALUE));\n }\n\n const digits = value.replace(NON_DIGIT_REGEXP, EMPTY_VALUE);\n\n return segments.map((_, index) => {\n const segmentStart = index * TIME_SEGMENT_LENGTH;\n const segmentEnd = segmentStart + TIME_SEGMENT_LENGTH;\n\n return sanitizeSegment(digits.slice(segmentStart, segmentEnd));\n });\n};\n"]}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type TimeFormat, type TimeSegment } from './TimePicker.shared.js';
|
|
2
|
+
/** Проверяет, содержит ли display-значение хотя бы одну введенную цифру. */
|
|
3
|
+
export declare const isTimeDisplayEmpty: (value: string) => boolean;
|
|
4
|
+
/**
|
|
5
|
+
* Проверяет, выходит ли значение за диапазон `minTime`/`maxTime` после нормализации.
|
|
6
|
+
* Пустое значение не считается выходящим за диапазон. Если `minTime > maxTime`, диапазон трактуется как переходящий через полночь.
|
|
7
|
+
*/
|
|
8
|
+
export declare const isTimeValueOutOfRange: (value: string, format: TimeFormat, minTime?: string, maxTime?: string) => boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Нормализует display-значение в committed-форму `hh:mm[:ss]`.
|
|
11
|
+
* Частично заполненные сегменты дополняются и ограничиваются максимумами сегментов.
|
|
12
|
+
*/
|
|
13
|
+
export declare const normalizeTimeValue: (value: string, format: TimeFormat) => string;
|
|
14
|
+
/** Заменяет значение выбранного сегмента, сохраняя остальные сегменты display-значения без изменений. */
|
|
15
|
+
export declare const replaceTimeSegment: (value: string, segment: TimeSegment, nextSegmentValue: string, format: TimeFormat) => string;
|
|
16
|
+
/**
|
|
17
|
+
* Парсит вставленное пользователем значение и сразу преобразует его в committed-форму.
|
|
18
|
+
*/
|
|
19
|
+
export declare const parsePastedTimeValue: (value: string, format: TimeFormat) => string;
|
|
20
|
+
/** Возвращает текущее display-значение сегмента или пустой сегмент, если индекс отсутствует. */
|
|
21
|
+
export declare const getTimeSegmentValue: (value: string, segment: TimeSegment, format: TimeFormat) => string;
|
|
22
|
+
/** Возвращает полностью пустое display-значение для выбранной точности времени. */
|
|
23
|
+
export declare const getEmptyDisplayValue: (precision: TimeFormat) => string;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { DIGIT_REGEXP, EMPTY_SEGMENT, EMPTY_VALUE, TIME_SEPARATOR } from './TimePicker.constants.js';
|
|
2
|
+
import { getDisplaySegments, getTimeSegments, normalizeTimeSegment, sanitizeSegment, } from './TimePicker.shared.js';
|
|
3
|
+
/** Проверяет, содержит ли display-значение хотя бы одну введенную цифру. */
|
|
4
|
+
export var isTimeDisplayEmpty = function (value) { return !DIGIT_REGEXP.test(value); };
|
|
5
|
+
/**
|
|
6
|
+
* Проверяет, выходит ли значение за диапазон `minTime`/`maxTime` после нормализации.
|
|
7
|
+
* Пустое значение не считается выходящим за диапазон. Если `minTime > maxTime`, диапазон трактуется как переходящий через полночь.
|
|
8
|
+
*/
|
|
9
|
+
export var isTimeValueOutOfRange = function (value, format, minTime, maxTime) {
|
|
10
|
+
var normalizedValue = normalizeTimeValue(value, format);
|
|
11
|
+
if (normalizedValue === EMPTY_VALUE) {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
var normalizedMinTime = normalizeTimeRangeValue(minTime, format);
|
|
15
|
+
var normalizedMaxTime = normalizeTimeRangeValue(maxTime, format);
|
|
16
|
+
if (normalizedMinTime && normalizedMaxTime && normalizedMinTime > normalizedMaxTime) {
|
|
17
|
+
return normalizedValue < normalizedMinTime && normalizedValue > normalizedMaxTime;
|
|
18
|
+
}
|
|
19
|
+
return ((normalizedMinTime !== undefined && normalizedValue < normalizedMinTime) ||
|
|
20
|
+
(normalizedMaxTime !== undefined && normalizedValue > normalizedMaxTime));
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Нормализует display-значение в committed-форму `hh:mm[:ss]`.
|
|
24
|
+
* Частично заполненные сегменты дополняются и ограничиваются максимумами сегментов.
|
|
25
|
+
*/
|
|
26
|
+
export var normalizeTimeValue = function (value, format) {
|
|
27
|
+
if (isTimeDisplayEmpty(value)) {
|
|
28
|
+
return EMPTY_VALUE;
|
|
29
|
+
}
|
|
30
|
+
return getTimeSegments(format)
|
|
31
|
+
.map(function (segment, index) { return normalizeTimeSegment(getDisplaySegments(value, format)[index], segment); })
|
|
32
|
+
.join(TIME_SEPARATOR);
|
|
33
|
+
};
|
|
34
|
+
var normalizeTimeRangeValue = function (value, format) {
|
|
35
|
+
if (!value) {
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
var normalizedValue = normalizeTimeValue(value, format);
|
|
39
|
+
return normalizedValue === EMPTY_VALUE ? undefined : normalizedValue;
|
|
40
|
+
};
|
|
41
|
+
/** Заменяет значение выбранного сегмента, сохраняя остальные сегменты display-значения без изменений. */
|
|
42
|
+
export var replaceTimeSegment = function (value, segment, nextSegmentValue, format) {
|
|
43
|
+
var segments = getDisplaySegments(value, format);
|
|
44
|
+
var index = getTimeSegments(format).indexOf(segment);
|
|
45
|
+
segments[index] = sanitizeSegment(nextSegmentValue);
|
|
46
|
+
return segments.join(TIME_SEPARATOR);
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Парсит вставленное пользователем значение и сразу преобразует его в committed-форму.
|
|
50
|
+
*/
|
|
51
|
+
export var parsePastedTimeValue = function (value, format) {
|
|
52
|
+
if (!DIGIT_REGEXP.test(value)) {
|
|
53
|
+
return EMPTY_VALUE;
|
|
54
|
+
}
|
|
55
|
+
var segments = getDisplaySegments(value, format);
|
|
56
|
+
return normalizeTimeValue(segments.join(TIME_SEPARATOR), format);
|
|
57
|
+
};
|
|
58
|
+
/** Возвращает текущее display-значение сегмента или пустой сегмент, если индекс отсутствует. */
|
|
59
|
+
export var getTimeSegmentValue = function (value, segment, format) {
|
|
60
|
+
var _a;
|
|
61
|
+
var segments = getDisplaySegments(value, format);
|
|
62
|
+
var index = getTimeSegments(format).indexOf(segment);
|
|
63
|
+
return (_a = segments[index]) !== null && _a !== void 0 ? _a : EMPTY_SEGMENT;
|
|
64
|
+
};
|
|
65
|
+
/** Возвращает полностью пустое display-значение для выбранной точности времени. */
|
|
66
|
+
export var getEmptyDisplayValue = function (precision) {
|
|
67
|
+
return getTimeSegments(precision)
|
|
68
|
+
.map(function () { return EMPTY_SEGMENT; })
|
|
69
|
+
.join(TIME_SEPARATOR);
|
|
70
|
+
};
|
|
71
|
+
//# sourceMappingURL=TimePicker.value.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TimePicker.value.js","sourceRoot":"","sources":["TimePicker.value.ts"],"names":[],"mappings":"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","sourcesContent":["import { DIGIT_REGEXP, EMPTY_SEGMENT, EMPTY_VALUE, TIME_SEPARATOR } from './TimePicker.constants.js';\nimport {\n getDisplaySegments,\n getTimeSegments,\n normalizeTimeSegment,\n sanitizeSegment,\n type TimeFormat,\n type TimeSegment,\n} from './TimePicker.shared.js';\n\n/** Проверяет, содержит ли display-значение хотя бы одну введенную цифру. */\nexport const isTimeDisplayEmpty = (value: string): boolean => !DIGIT_REGEXP.test(value);\n\n/**\n * Проверяет, выходит ли значение за диапазон `minTime`/`maxTime` после нормализации.\n * Пустое значение не считается выходящим за диапазон. Если `minTime > maxTime`, диапазон трактуется как переходящий через полночь.\n */\nexport const isTimeValueOutOfRange = (\n value: string,\n format: TimeFormat,\n minTime?: string,\n maxTime?: string,\n): boolean => {\n const normalizedValue = normalizeTimeValue(value, format);\n\n if (normalizedValue === EMPTY_VALUE) {\n return false;\n }\n\n const normalizedMinTime = normalizeTimeRangeValue(minTime, format);\n const normalizedMaxTime = normalizeTimeRangeValue(maxTime, format);\n\n if (normalizedMinTime && normalizedMaxTime && normalizedMinTime > normalizedMaxTime) {\n return normalizedValue < normalizedMinTime && normalizedValue > normalizedMaxTime;\n }\n\n return (\n (normalizedMinTime !== undefined && normalizedValue < normalizedMinTime) ||\n (normalizedMaxTime !== undefined && normalizedValue > normalizedMaxTime)\n );\n};\n\n/**\n * Нормализует display-значение в committed-форму `hh:mm[:ss]`.\n * Частично заполненные сегменты дополняются и ограничиваются максимумами сегментов.\n */\nexport const normalizeTimeValue = (value: string, format: TimeFormat): string => {\n if (isTimeDisplayEmpty(value)) {\n return EMPTY_VALUE;\n }\n\n return getTimeSegments(format)\n .map((segment, index) => normalizeTimeSegment(getDisplaySegments(value, format)[index], segment))\n .join(TIME_SEPARATOR);\n};\n\nconst normalizeTimeRangeValue = (value: string | undefined, format: TimeFormat): string | undefined => {\n if (!value) {\n return undefined;\n }\n\n const normalizedValue = normalizeTimeValue(value, format);\n\n return normalizedValue === EMPTY_VALUE ? undefined : normalizedValue;\n};\n\n/** Заменяет значение выбранного сегмента, сохраняя остальные сегменты display-значения без изменений. */\nexport const replaceTimeSegment = (\n value: string,\n segment: TimeSegment,\n nextSegmentValue: string,\n format: TimeFormat,\n): string => {\n const segments = getDisplaySegments(value, format);\n const index = getTimeSegments(format).indexOf(segment);\n\n segments[index] = sanitizeSegment(nextSegmentValue);\n\n return segments.join(TIME_SEPARATOR);\n};\n\n/**\n * Парсит вставленное пользователем значение и сразу преобразует его в committed-форму.\n */\nexport const parsePastedTimeValue = (value: string, format: TimeFormat): string => {\n if (!DIGIT_REGEXP.test(value)) {\n return EMPTY_VALUE;\n }\n\n const segments = getDisplaySegments(value, format);\n\n return normalizeTimeValue(segments.join(TIME_SEPARATOR), format);\n};\n\n/** Возвращает текущее display-значение сегмента или пустой сегмент, если индекс отсутствует. */\nexport const getTimeSegmentValue = (value: string, segment: TimeSegment, format: TimeFormat): string => {\n const segments = getDisplaySegments(value, format);\n const index = getTimeSegments(format).indexOf(segment);\n\n return segments[index] ?? EMPTY_SEGMENT;\n};\n\n/** Возвращает полностью пустое display-значение для выбранной точности времени. */\nexport const getEmptyDisplayValue = (precision: TimeFormat): string =>\n getTimeSegments(precision)\n .map(() => EMPTY_SEGMENT)\n .join(TIME_SEPARATOR);\n"]}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { findNearestScrollableParent } from '../../../lib/dom/scrollYCenterIntoNearestScrollable.js';
|
|
2
|
+
import { getOwnerGlobalObject, isBrowser } from '../../../lib/globalObject.js';
|
|
3
|
+
/**
|
|
4
|
+
* Прокручивает ближайший scrollable-контейнер так, чтобы выбранный слот оказался ближе к центру viewport контейнера.
|
|
5
|
+
*/
|
|
6
|
+
export var scrollSelectedSlotIntoView = function (element) {
|
|
7
|
+
var globalObject = getOwnerGlobalObject(element);
|
|
8
|
+
if (!isBrowser(globalObject)) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
var parent = findNearestScrollableParent(element);
|
|
12
|
+
if (!parent) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
var elementRect = element.getBoundingClientRect();
|
|
16
|
+
var parentRect = parent.getBoundingClientRect();
|
|
17
|
+
var scrollTopPos = elementRect.top - parentRect.top;
|
|
18
|
+
var scrollBottomPos = elementRect.bottom - parentRect.bottom;
|
|
19
|
+
var scrollCenterPos = (scrollTopPos + scrollBottomPos) / 2;
|
|
20
|
+
if (scrollCenterPos !== 0) {
|
|
21
|
+
parent.scrollTo({ top: parent.scrollTop + scrollCenterPos });
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=scrollSelectedSlotIntoView.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scrollSelectedSlotIntoView.js","sourceRoot":"","sources":["scrollSelectedSlotIntoView.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,MAAM,wDAAwD,CAAC;AACrG,OAAO,EAAE,oBAAoB,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAE/E;;GAEG;AACH,MAAM,CAAC,IAAM,0BAA0B,GAAG,UAAC,OAAoB;IAC7D,IAAM,YAAY,GAAG,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAEnD,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,EAAE,CAAC;QAC7B,OAAO;IACT,CAAC;IAED,IAAM,MAAM,GAAG,2BAA2B,CAAC,OAAO,CAAC,CAAC;IAEpD,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO;IACT,CAAC;IAED,IAAM,WAAW,GAAG,OAAO,CAAC,qBAAqB,EAAE,CAAC;IACpD,IAAM,UAAU,GAAG,MAAM,CAAC,qBAAqB,EAAE,CAAC;IAElD,IAAM,YAAY,GAAG,WAAW,CAAC,GAAG,GAAG,UAAU,CAAC,GAAG,CAAC;IACtD,IAAM,eAAe,GAAG,WAAW,CAAC,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC;IAC/D,IAAM,eAAe,GAAG,CAAC,YAAY,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;IAE7D,IAAI,eAAe,KAAK,CAAC,EAAE,CAAC;QAC1B,MAAM,CAAC,QAAQ,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,SAAS,GAAG,eAAe,EAAE,CAAC,CAAC;IAC/D,CAAC;AACH,CAAC,CAAC","sourcesContent":["import { findNearestScrollableParent } from '../../../lib/dom/scrollYCenterIntoNearestScrollable.js';\nimport { getOwnerGlobalObject, isBrowser } from '../../../lib/globalObject.js';\n\n/**\n * Прокручивает ближайший scrollable-контейнер так, чтобы выбранный слот оказался ближе к центру viewport контейнера.\n */\nexport const scrollSelectedSlotIntoView = (element: HTMLElement): void => {\n const globalObject = getOwnerGlobalObject(element);\n\n if (!isBrowser(globalObject)) {\n return;\n }\n\n const parent = findNearestScrollableParent(element);\n\n if (!parent) {\n return;\n }\n\n const elementRect = element.getBoundingClientRect();\n const parentRect = parent.getBoundingClientRect();\n\n const scrollTopPos = elementRect.top - parentRect.top;\n const scrollBottomPos = elementRect.bottom - parentRect.bottom;\n const scrollCenterPos = (scrollTopPos + scrollBottomPos) / 2;\n\n if (scrollCenterPos !== 0) {\n parent.scrollTo({ top: parent.scrollTop + scrollCenterPos });\n }\n};\n"]}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { TimeSlot } from '../helpers/TimePicker.shared.js';
|
|
2
|
+
interface UseTimePickerDropdownOptions {
|
|
3
|
+
hasDropdown: boolean;
|
|
4
|
+
disabled?: boolean;
|
|
5
|
+
slots: TimeSlot[];
|
|
6
|
+
selectedSlotIndex: number | null;
|
|
7
|
+
}
|
|
8
|
+
interface UseTimePickerDropdownResult {
|
|
9
|
+
isDropdownOpened: boolean;
|
|
10
|
+
highlightedSlotIndex: number | null;
|
|
11
|
+
openDropdown(): void;
|
|
12
|
+
closeDropdown(): void;
|
|
13
|
+
resetHighlightedSlot(): void;
|
|
14
|
+
tryNavigateSlots(step: 1 | -1): boolean;
|
|
15
|
+
}
|
|
16
|
+
export declare const useTimePickerDropdown: (options: UseTimePickerDropdownOptions) => UseTimePickerDropdownResult;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { useCallback, useState } from 'react';
|
|
2
|
+
export var useTimePickerDropdown = function (options) {
|
|
3
|
+
var hasDropdown = options.hasDropdown, disabled = options.disabled, slots = options.slots, selectedSlotIndex = options.selectedSlotIndex;
|
|
4
|
+
var _a = useState(false), isDropdownOpened = _a[0], setIsDropdownOpened = _a[1];
|
|
5
|
+
var _b = useState(null), highlightedSlotIndex = _b[0], setHighlightedSlotIndex = _b[1];
|
|
6
|
+
var openDropdown = useCallback(function () {
|
|
7
|
+
if (!hasDropdown || disabled) {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
setIsDropdownOpened(true);
|
|
11
|
+
}, [disabled, hasDropdown]);
|
|
12
|
+
var closeDropdown = useCallback(function () {
|
|
13
|
+
setIsDropdownOpened(false);
|
|
14
|
+
setHighlightedSlotIndex(null);
|
|
15
|
+
}, []);
|
|
16
|
+
var resetHighlightedSlot = useCallback(function () {
|
|
17
|
+
setHighlightedSlotIndex(null);
|
|
18
|
+
}, []);
|
|
19
|
+
var tryNavigateSlots = useCallback(function (step) {
|
|
20
|
+
if (!hasDropdown || !isDropdownOpened) {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
var enabledIndexes = slots
|
|
24
|
+
.map(function (slot, index) { return ({ slot: slot, index: index }); })
|
|
25
|
+
.filter(function (_a) {
|
|
26
|
+
var slot = _a.slot;
|
|
27
|
+
return !slot.disabled;
|
|
28
|
+
})
|
|
29
|
+
.map(function (_a) {
|
|
30
|
+
var index = _a.index;
|
|
31
|
+
return index;
|
|
32
|
+
});
|
|
33
|
+
if (enabledIndexes.length === 0) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
if (highlightedSlotIndex === null) {
|
|
37
|
+
if (selectedSlotIndex !== null) {
|
|
38
|
+
var selectedEnabledIndex = enabledIndexes.indexOf(selectedSlotIndex);
|
|
39
|
+
if (selectedEnabledIndex >= 0) {
|
|
40
|
+
var nextEnabledIndex_1 = enabledIndexes[selectedEnabledIndex + step];
|
|
41
|
+
setHighlightedSlotIndex(nextEnabledIndex_1 !== null && nextEnabledIndex_1 !== void 0 ? nextEnabledIndex_1 : (step > 0 ? enabledIndexes[0] : enabledIndexes[enabledIndexes.length - 1]));
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
setHighlightedSlotIndex(step > 0 ? enabledIndexes[0] : enabledIndexes[enabledIndexes.length - 1]);
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
var currentIndex = enabledIndexes.indexOf(highlightedSlotIndex);
|
|
49
|
+
var nextEnabledIndex = enabledIndexes[currentIndex + step];
|
|
50
|
+
setHighlightedSlotIndex(nextEnabledIndex !== null && nextEnabledIndex !== void 0 ? nextEnabledIndex : (step > 0 ? enabledIndexes[0] : enabledIndexes[enabledIndexes.length - 1]));
|
|
51
|
+
return true;
|
|
52
|
+
}, [hasDropdown, highlightedSlotIndex, isDropdownOpened, selectedSlotIndex, slots]);
|
|
53
|
+
return {
|
|
54
|
+
isDropdownOpened: isDropdownOpened,
|
|
55
|
+
highlightedSlotIndex: highlightedSlotIndex,
|
|
56
|
+
openDropdown: openDropdown,
|
|
57
|
+
closeDropdown: closeDropdown,
|
|
58
|
+
resetHighlightedSlot: resetHighlightedSlot,
|
|
59
|
+
tryNavigateSlots: tryNavigateSlots,
|
|
60
|
+
};
|
|
61
|
+
};
|
|
62
|
+
//# sourceMappingURL=useTimePickerDropdown.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useTimePickerDropdown.js","sourceRoot":"","sources":["useTimePickerDropdown.ts"],"names":[],"mappings":"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","sourcesContent":["import { useCallback, useState } from 'react';\n\nimport type { TimeSlot } from '../helpers/TimePicker.shared.js';\n\ninterface UseTimePickerDropdownOptions {\n hasDropdown: boolean;\n disabled?: boolean;\n slots: TimeSlot[];\n selectedSlotIndex: number | null;\n}\n\ninterface UseTimePickerDropdownResult {\n isDropdownOpened: boolean;\n highlightedSlotIndex: number | null;\n\n openDropdown(): void;\n\n closeDropdown(): void;\n\n resetHighlightedSlot(): void;\n\n tryNavigateSlots(step: 1 | -1): boolean;\n}\n\nexport const useTimePickerDropdown = (options: UseTimePickerDropdownOptions): UseTimePickerDropdownResult => {\n const { hasDropdown, disabled, slots, selectedSlotIndex } = options;\n\n const [isDropdownOpened, setIsDropdownOpened] = useState(false);\n const [highlightedSlotIndex, setHighlightedSlotIndex] = useState<number | null>(null);\n\n const openDropdown = useCallback(() => {\n if (!hasDropdown || disabled) {\n return;\n }\n\n setIsDropdownOpened(true);\n }, [disabled, hasDropdown]);\n\n const closeDropdown = useCallback(() => {\n setIsDropdownOpened(false);\n setHighlightedSlotIndex(null);\n }, []);\n\n const resetHighlightedSlot = useCallback(() => {\n setHighlightedSlotIndex(null);\n }, []);\n\n const tryNavigateSlots = useCallback(\n (step: 1 | -1) => {\n if (!hasDropdown || !isDropdownOpened) {\n return false;\n }\n\n const enabledIndexes = slots\n .map((slot, index) => ({ slot, index }))\n .filter(({ slot }) => !slot.disabled)\n .map(({ index }) => index);\n\n if (enabledIndexes.length === 0) {\n return false;\n }\n\n if (highlightedSlotIndex === null) {\n if (selectedSlotIndex !== null) {\n const selectedEnabledIndex = enabledIndexes.indexOf(selectedSlotIndex);\n\n if (selectedEnabledIndex >= 0) {\n const nextEnabledIndex = enabledIndexes[selectedEnabledIndex + step];\n\n setHighlightedSlotIndex(\n nextEnabledIndex ?? (step > 0 ? enabledIndexes[0] : enabledIndexes[enabledIndexes.length - 1]),\n );\n return true;\n }\n }\n\n setHighlightedSlotIndex(step > 0 ? enabledIndexes[0] : enabledIndexes[enabledIndexes.length - 1]);\n return true;\n }\n\n const currentIndex = enabledIndexes.indexOf(highlightedSlotIndex);\n const nextEnabledIndex = enabledIndexes[currentIndex + step];\n\n setHighlightedSlotIndex(\n nextEnabledIndex ?? (step > 0 ? enabledIndexes[0] : enabledIndexes[enabledIndexes.length - 1]),\n );\n\n return true;\n },\n [hasDropdown, highlightedSlotIndex, isDropdownOpened, selectedSlotIndex, slots],\n );\n\n return {\n isDropdownOpened,\n highlightedSlotIndex,\n openDropdown,\n closeDropdown,\n resetHighlightedSlot,\n tryNavigateSlots,\n };\n};\n"]}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { TimeFormat, TimeSegment } from '../helpers/TimePicker.shared.js';
|
|
2
|
+
import type { TimeInputRef } from '../TimeInput.js';
|
|
3
|
+
type TimePickerSelection = TimeSegment | 'all';
|
|
4
|
+
interface UseTimePickerSelectionOptions {
|
|
5
|
+
isInputFocused: boolean;
|
|
6
|
+
format: TimeFormat;
|
|
7
|
+
getInput: () => TimeInputRef | null;
|
|
8
|
+
displayValue: string;
|
|
9
|
+
}
|
|
10
|
+
interface UseTimePickerSelectionResult {
|
|
11
|
+
selection: TimePickerSelection;
|
|
12
|
+
selectedSegment: TimeSegment;
|
|
13
|
+
selectSegment: (segment: TimeSegment) => void;
|
|
14
|
+
selectAll: () => void;
|
|
15
|
+
syncSelectionWithDOM: () => boolean;
|
|
16
|
+
}
|
|
17
|
+
export declare const useTimePickerSelection: (options: UseTimePickerSelectionOptions) => UseTimePickerSelectionResult;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { useCallback, useLayoutEffect, useState } from 'react';
|
|
2
|
+
export var useTimePickerSelection = function (options) {
|
|
3
|
+
var isInputFocused = options.isInputFocused, format = options.format, displayValue = options.displayValue, getInput = options.getInput;
|
|
4
|
+
var _a = useState('hours'), selection = _a[0], setSelectionState = _a[1];
|
|
5
|
+
var selectedSegment = selection === 'all' ? 'hours' : selection;
|
|
6
|
+
var applySelectionToInput = useCallback(function (nextSelection) {
|
|
7
|
+
var currentInput = getInput();
|
|
8
|
+
if (!currentInput) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
var node = currentInput.getNode();
|
|
12
|
+
var isInputActive = (node === null || node === void 0 ? void 0 : node.ownerDocument.activeElement) === node;
|
|
13
|
+
var applySelection = function (input) {
|
|
14
|
+
if (nextSelection === 'all') {
|
|
15
|
+
input.selectAll();
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
input.selectSegment(nextSelection);
|
|
19
|
+
};
|
|
20
|
+
if (isInputActive) {
|
|
21
|
+
applySelection(currentInput);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
setTimeout(function () {
|
|
25
|
+
var nextInput = getInput();
|
|
26
|
+
if (!nextInput) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
nextInput.focus();
|
|
30
|
+
applySelection(nextInput);
|
|
31
|
+
}, 0);
|
|
32
|
+
}, [getInput]);
|
|
33
|
+
var selectSegment = useCallback(function (segment) {
|
|
34
|
+
if (segment === 'seconds' && format === 'minute') {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
setSelectionState(segment);
|
|
38
|
+
applySelectionToInput(segment);
|
|
39
|
+
}, [applySelectionToInput, format]);
|
|
40
|
+
var selectAll = useCallback(function () {
|
|
41
|
+
setSelectionState('all');
|
|
42
|
+
applySelectionToInput('all');
|
|
43
|
+
}, [applySelectionToInput]);
|
|
44
|
+
var syncSelectionWithDOM = useCallback(function () {
|
|
45
|
+
var _a;
|
|
46
|
+
if (!((_a = getInput()) === null || _a === void 0 ? void 0 : _a.isAllSelected())) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
setSelectionState('all');
|
|
50
|
+
return true;
|
|
51
|
+
}, [getInput]);
|
|
52
|
+
useLayoutEffect(function () {
|
|
53
|
+
if (!isInputFocused || !getInput()) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
applySelectionToInput(selection);
|
|
57
|
+
}, [applySelectionToInput, displayValue, getInput, isInputFocused, selection]);
|
|
58
|
+
return {
|
|
59
|
+
selection: selection,
|
|
60
|
+
selectedSegment: selectedSegment,
|
|
61
|
+
selectSegment: selectSegment,
|
|
62
|
+
selectAll: selectAll,
|
|
63
|
+
syncSelectionWithDOM: syncSelectionWithDOM,
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
//# sourceMappingURL=useTimePickerSelection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useTimePickerSelection.js","sourceRoot":"","sources":["useTimePickerSelection.ts"],"names":[],"mappings":"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","sourcesContent":["import { useCallback, useLayoutEffect, useState } from 'react';\n\nimport type { TimeFormat, TimeSegment } from '../helpers/TimePicker.shared.js';\nimport type { TimeInputRef } from '../TimeInput.js';\n\ntype TimePickerSelection = TimeSegment | 'all';\n\ninterface UseTimePickerSelectionOptions {\n isInputFocused: boolean;\n format: TimeFormat;\n getInput: () => TimeInputRef | null;\n displayValue: string;\n}\ninterface UseTimePickerSelectionResult {\n selection: TimePickerSelection;\n selectedSegment: TimeSegment;\n selectSegment: (segment: TimeSegment) => void;\n selectAll: () => void;\n syncSelectionWithDOM: () => boolean;\n}\nexport const useTimePickerSelection = (options: UseTimePickerSelectionOptions): UseTimePickerSelectionResult => {\n const { isInputFocused, format, displayValue, getInput } = options;\n\n const [selection, setSelectionState] = useState<TimePickerSelection>('hours');\n\n const selectedSegment = selection === 'all' ? 'hours' : selection;\n\n const applySelectionToInput = useCallback(\n (nextSelection: TimePickerSelection) => {\n const currentInput = getInput();\n\n if (!currentInput) {\n return;\n }\n\n const node = currentInput.getNode();\n const isInputActive = node?.ownerDocument.activeElement === node;\n\n const applySelection = (input: TimeInputRef) => {\n if (nextSelection === 'all') {\n input.selectAll();\n return;\n }\n\n input.selectSegment(nextSelection);\n };\n\n if (isInputActive) {\n applySelection(currentInput);\n return;\n }\n\n setTimeout(() => {\n const nextInput = getInput();\n\n if (!nextInput) {\n return;\n }\n\n nextInput.focus();\n applySelection(nextInput);\n }, 0);\n },\n [getInput],\n );\n\n const selectSegment = useCallback(\n (segment: TimeSegment) => {\n if (segment === 'seconds' && format === 'minute') {\n return;\n }\n\n setSelectionState(segment);\n applySelectionToInput(segment);\n },\n [applySelectionToInput, format],\n );\n\n const selectAll = useCallback(() => {\n setSelectionState('all');\n applySelectionToInput('all');\n }, [applySelectionToInput]);\n\n const syncSelectionWithDOM = useCallback(() => {\n if (!getInput()?.isAllSelected()) {\n return false;\n }\n\n setSelectionState('all');\n return true;\n }, [getInput]);\n\n useLayoutEffect(() => {\n if (!isInputFocused || !getInput()) {\n return;\n }\n\n applySelectionToInput(selection);\n }, [applySelectionToInput, displayValue, getInput, isInputFocused, selection]);\n\n return {\n selection,\n selectedSegment,\n selectSegment,\n selectAll,\n syncSelectionWithDOM,\n };\n};\n"]}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { RefObject } from 'react';
|
|
2
|
+
import type { TimeFormat } from '../helpers/TimePicker.shared.js';
|
|
3
|
+
interface UseTimePickerValueOptions {
|
|
4
|
+
isInputFocused: boolean;
|
|
5
|
+
value?: string;
|
|
6
|
+
format: TimeFormat;
|
|
7
|
+
onValueChange?(value: string): void;
|
|
8
|
+
}
|
|
9
|
+
interface UseTimePickerValueResult {
|
|
10
|
+
editingValue: string;
|
|
11
|
+
editingValueRef: RefObject<string>;
|
|
12
|
+
setEditingValue(value: string): void;
|
|
13
|
+
updateEditingValue(value: string): void;
|
|
14
|
+
commitEditingValue(): string;
|
|
15
|
+
clearEditingValue(): void;
|
|
16
|
+
}
|
|
17
|
+
export declare const useTimePickerValue: (options: UseTimePickerValueOptions) => UseTimePickerValueResult;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { EMPTY_VALUE } from '../helpers/TimePicker.constants.js';
|
|
3
|
+
import { isTimeDisplayEmpty, normalizeTimeValue } from '../helpers/TimePicker.value.js';
|
|
4
|
+
export var useTimePickerValue = function (options) {
|
|
5
|
+
var value = options.value, format = options.format, isInputFocused = options.isInputFocused, onValueChange = options.onValueChange;
|
|
6
|
+
var committedValue = normalizeTimeValue(value !== null && value !== void 0 ? value : EMPTY_VALUE, format);
|
|
7
|
+
var _a = useState(committedValue), editingValue = _a[0], setEditingValueState = _a[1];
|
|
8
|
+
var editingValueRef = useRef(editingValue);
|
|
9
|
+
var emittedValueRef = useRef(committedValue);
|
|
10
|
+
var setEditingValue = useCallback(function (nextEditingValue) {
|
|
11
|
+
editingValueRef.current = nextEditingValue;
|
|
12
|
+
setEditingValueState(nextEditingValue);
|
|
13
|
+
}, []);
|
|
14
|
+
var emitValue = useCallback(function (nextValue) {
|
|
15
|
+
if (emittedValueRef.current !== nextValue) {
|
|
16
|
+
emittedValueRef.current = nextValue;
|
|
17
|
+
onValueChange === null || onValueChange === void 0 ? void 0 : onValueChange(nextValue);
|
|
18
|
+
}
|
|
19
|
+
}, [onValueChange]);
|
|
20
|
+
var updateEditingValue = useCallback(function (nextEditingValue) {
|
|
21
|
+
setEditingValue(nextEditingValue);
|
|
22
|
+
emitValue(normalizeTimeValue(nextEditingValue, format));
|
|
23
|
+
}, [emitValue, format, setEditingValue]);
|
|
24
|
+
var clearEditingValue = useCallback(function () {
|
|
25
|
+
setEditingValue(EMPTY_VALUE);
|
|
26
|
+
}, [setEditingValue]);
|
|
27
|
+
var commitEditingValue = useCallback(function () {
|
|
28
|
+
var currentEditingValue = editingValueRef.current;
|
|
29
|
+
if (isTimeDisplayEmpty(currentEditingValue)) {
|
|
30
|
+
clearEditingValue();
|
|
31
|
+
return EMPTY_VALUE;
|
|
32
|
+
}
|
|
33
|
+
var nextValue = normalizeTimeValue(currentEditingValue, format);
|
|
34
|
+
setEditingValue(nextValue);
|
|
35
|
+
emitValue(nextValue);
|
|
36
|
+
return nextValue;
|
|
37
|
+
}, [clearEditingValue, editingValueRef, emitValue, format, setEditingValue]);
|
|
38
|
+
useEffect(function () {
|
|
39
|
+
if (!isInputFocused) {
|
|
40
|
+
setEditingValue(committedValue);
|
|
41
|
+
}
|
|
42
|
+
else if (committedValue !== emittedValueRef.current) {
|
|
43
|
+
setEditingValue(committedValue);
|
|
44
|
+
}
|
|
45
|
+
}, [committedValue, isInputFocused, setEditingValue]);
|
|
46
|
+
useEffect(function () {
|
|
47
|
+
emittedValueRef.current = committedValue;
|
|
48
|
+
}, [committedValue]);
|
|
49
|
+
return {
|
|
50
|
+
editingValue: editingValue,
|
|
51
|
+
editingValueRef: editingValueRef,
|
|
52
|
+
setEditingValue: setEditingValue,
|
|
53
|
+
updateEditingValue: updateEditingValue,
|
|
54
|
+
commitEditingValue: commitEditingValue,
|
|
55
|
+
clearEditingValue: clearEditingValue,
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
//# sourceMappingURL=useTimePickerValue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useTimePickerValue.js","sourceRoot":"","sources":["useTimePickerValue.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAEjE,OAAO,EAAE,WAAW,EAAE,MAAM,oCAAoC,CAAC;AAEjE,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAkBxF,MAAM,CAAC,IAAM,kBAAkB,GAAG,UAAC,OAAkC;IAC3D,IAAA,KAAK,GAA4C,OAAO,MAAnD,EAAE,MAAM,GAAoC,OAAO,OAA3C,EAAE,cAAc,GAAoB,OAAO,eAA3B,EAAE,aAAa,GAAK,OAAO,cAAZ,CAAa;IAEjE,IAAM,cAAc,GAAG,kBAAkB,CAAC,KAAK,aAAL,KAAK,cAAL,KAAK,GAAI,WAAW,EAAE,MAAM,CAAC,CAAC;IAElE,IAAA,KAAuC,QAAQ,CAAS,cAAc,CAAC,EAAtE,YAAY,QAAA,EAAE,oBAAoB,QAAoC,CAAC;IAE9E,IAAM,eAAe,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC;IAC7C,IAAM,eAAe,GAAG,MAAM,CAAC,cAAc,CAAC,CAAC;IAE/C,IAAM,eAAe,GAAG,WAAW,CAAC,UAAC,gBAAwB;QAC3D,eAAe,CAAC,OAAO,GAAG,gBAAgB,CAAC;QAC3C,oBAAoB,CAAC,gBAAgB,CAAC,CAAC;IACzC,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,IAAM,SAAS,GAAG,WAAW,CAC3B,UAAC,SAAiB;QAChB,IAAI,eAAe,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;YAC1C,eAAe,CAAC,OAAO,GAAG,SAAS,CAAC;YACpC,aAAa,aAAb,aAAa,uBAAb,aAAa,CAAG,SAAS,CAAC,CAAC;QAC7B,CAAC;IACH,CAAC,EACD,CAAC,aAAa,CAAC,CAChB,CAAC;IAEF,IAAM,kBAAkB,GAAG,WAAW,CACpC,UAAC,gBAAwB;QACvB,eAAe,CAAC,gBAAgB,CAAC,CAAC;QAClC,SAAS,CAAC,kBAAkB,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAAC,CAAC;IAC1D,CAAC,EACD,CAAC,SAAS,EAAE,MAAM,EAAE,eAAe,CAAC,CACrC,CAAC;IAEF,IAAM,iBAAiB,GAAG,WAAW,CAAC;QACpC,eAAe,CAAC,WAAW,CAAC,CAAC;IAC/B,CAAC,EAAE,CAAC,eAAe,CAAC,CAAC,CAAC;IAEtB,IAAM,kBAAkB,GAAG,WAAW,CAAC;QACrC,IAAM,mBAAmB,GAAG,eAAe,CAAC,OAAO,CAAC;QAEpD,IAAI,kBAAkB,CAAC,mBAAmB,CAAC,EAAE,CAAC;YAC5C,iBAAiB,EAAE,CAAC;YACpB,OAAO,WAAW,CAAC;QACrB,CAAC;QAED,IAAM,SAAS,GAAG,kBAAkB,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAAC;QAElE,eAAe,CAAC,SAAS,CAAC,CAAC;QAC3B,SAAS,CAAC,SAAS,CAAC,CAAC;QAErB,OAAO,SAAS,CAAC;IACnB,CAAC,EAAE,CAAC,iBAAiB,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,CAAC,CAAC,CAAC;IAE7E,SAAS,CAAC;QACR,IAAI,CAAC,cAAc,EAAE,CAAC;YACpB,eAAe,CAAC,cAAc,CAAC,CAAC;QAClC,CAAC;aAAM,IAAI,cAAc,KAAK,eAAe,CAAC,OAAO,EAAE,CAAC;YACtD,eAAe,CAAC,cAAc,CAAC,CAAC;QAClC,CAAC;IACH,CAAC,EAAE,CAAC,cAAc,EAAE,cAAc,EAAE,eAAe,CAAC,CAAC,CAAC;IAEtD,SAAS,CAAC;QACR,eAAe,CAAC,OAAO,GAAG,cAAc,CAAC;IAC3C,CAAC,EAAE,CAAC,cAAc,CAAC,CAAC,CAAC;IAErB,OAAO;QACL,YAAY,cAAA;QACZ,eAAe,iBAAA;QACf,eAAe,iBAAA;QACf,kBAAkB,oBAAA;QAClB,kBAAkB,oBAAA;QAClB,iBAAiB,mBAAA;KAClB,CAAC;AACJ,CAAC,CAAC","sourcesContent":["import type { RefObject } from 'react';\nimport { useCallback, useEffect, useRef, useState } from 'react';\n\nimport { EMPTY_VALUE } from '../helpers/TimePicker.constants.js';\nimport type { TimeFormat } from '../helpers/TimePicker.shared.js';\nimport { isTimeDisplayEmpty, normalizeTimeValue } from '../helpers/TimePicker.value.js';\n\ninterface UseTimePickerValueOptions {\n isInputFocused: boolean;\n value?: string;\n format: TimeFormat;\n onValueChange?(value: string): void;\n}\n\ninterface UseTimePickerValueResult {\n editingValue: string;\n editingValueRef: RefObject<string>;\n setEditingValue(value: string): void;\n updateEditingValue(value: string): void;\n commitEditingValue(): string;\n clearEditingValue(): void;\n}\n\nexport const useTimePickerValue = (options: UseTimePickerValueOptions): UseTimePickerValueResult => {\n const { value, format, isInputFocused, onValueChange } = options;\n\n const committedValue = normalizeTimeValue(value ?? EMPTY_VALUE, format);\n\n const [editingValue, setEditingValueState] = useState<string>(committedValue);\n\n const editingValueRef = useRef(editingValue);\n const emittedValueRef = useRef(committedValue);\n\n const setEditingValue = useCallback((nextEditingValue: string) => {\n editingValueRef.current = nextEditingValue;\n setEditingValueState(nextEditingValue);\n }, []);\n\n const emitValue = useCallback(\n (nextValue: string) => {\n if (emittedValueRef.current !== nextValue) {\n emittedValueRef.current = nextValue;\n onValueChange?.(nextValue);\n }\n },\n [onValueChange],\n );\n\n const updateEditingValue = useCallback(\n (nextEditingValue: string) => {\n setEditingValue(nextEditingValue);\n emitValue(normalizeTimeValue(nextEditingValue, format));\n },\n [emitValue, format, setEditingValue],\n );\n\n const clearEditingValue = useCallback(() => {\n setEditingValue(EMPTY_VALUE);\n }, [setEditingValue]);\n\n const commitEditingValue = useCallback((): string => {\n const currentEditingValue = editingValueRef.current;\n\n if (isTimeDisplayEmpty(currentEditingValue)) {\n clearEditingValue();\n return EMPTY_VALUE;\n }\n\n const nextValue = normalizeTimeValue(currentEditingValue, format);\n\n setEditingValue(nextValue);\n emitValue(nextValue);\n\n return nextValue;\n }, [clearEditingValue, editingValueRef, emitValue, format, setEditingValue]);\n\n useEffect(() => {\n if (!isInputFocused) {\n setEditingValue(committedValue);\n } else if (committedValue !== emittedValueRef.current) {\n setEditingValue(committedValue);\n }\n }, [committedValue, isInputFocused, setEditingValue]);\n\n useEffect(() => {\n emittedValueRef.current = committedValue;\n }, [committedValue]);\n\n return {\n editingValue,\n editingValueRef,\n setEditingValue,\n updateEditingValue,\n commitEditingValue,\n clearEditingValue,\n };\n};\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC","sourcesContent":["export * from './TimePicker.js';\nexport type { TimeFormat, TimeSegment, TimeSlot } from './helpers/TimePicker.shared.js';\n"]}
|
package/index.d.ts
CHANGED
|
@@ -39,6 +39,7 @@ export * from './components/Sticky/index.js';
|
|
|
39
39
|
export * from './components/Switcher/index.js';
|
|
40
40
|
export * from './components/Tabs/index.js';
|
|
41
41
|
export * from './components/Textarea/index.js';
|
|
42
|
+
export * from './components/TimePicker/index.js';
|
|
42
43
|
export * from './components/Toast/index.js';
|
|
43
44
|
export * from './components/SingleToast/index.js';
|
|
44
45
|
export * from './components/Toggle/index.js';
|
package/index.js
CHANGED
|
@@ -39,6 +39,7 @@ export * from './components/Sticky/index.js';
|
|
|
39
39
|
export * from './components/Switcher/index.js';
|
|
40
40
|
export * from './components/Tabs/index.js';
|
|
41
41
|
export * from './components/Textarea/index.js';
|
|
42
|
+
export * from './components/TimePicker/index.js';
|
|
42
43
|
export * from './components/Toast/index.js';
|
|
43
44
|
export * from './components/SingleToast/index.js';
|
|
44
45
|
export * from './components/Toggle/index.js';
|