@skbkontur/react-ui 6.1.7-1c902.0 → 6.1.7-c386f.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/MenuItem/MenuItem.d.ts +1 -1
- package/components/MenuItem/MenuItem.js +0 -1
- package/components/MenuItem/MenuItem.js.map +1 -1
- package/components/MenuItem/MenuItem.styles.d.ts +0 -1
- package/components/MenuItem/MenuItem.styles.js +7 -10
- package/components/MenuItem/MenuItem.styles.js.map +1 -1
- package/components/TimePicker/TimePicker.d.ts +21 -5
- package/components/TimePicker/TimePicker.js +169 -78
- package/components/TimePicker/TimePicker.js.map +1 -1
- package/components/TimePicker/TimePicker.styles.d.ts +2 -0
- package/components/TimePicker/TimePicker.styles.js +7 -1
- package/components/TimePicker/TimePicker.styles.js.map +1 -1
- package/components/TimePicker/TimePickerItems.js +27 -20
- package/components/TimePicker/TimePickerItems.js.map +1 -1
- package/components/TimePicker/TimePickerMobilePopup.js +2 -2
- package/components/TimePicker/TimePickerMobilePopup.js.map +1 -1
- package/components/TimePicker/TimePickerPopup.js +2 -2
- package/components/TimePicker/TimePickerPopup.js.map +1 -1
- package/components/TimePicker/helpers/TimePicker.constants.d.ts +5 -0
- package/components/TimePicker/helpers/TimePicker.constants.js +5 -0
- package/components/TimePicker/helpers/TimePicker.constants.js.map +1 -1
- package/components/TimePicker/helpers/TimePicker.shared.d.ts +9 -0
- package/components/TimePicker/helpers/TimePicker.shared.js +17 -0
- package/components/TimePicker/helpers/TimePicker.shared.js.map +1 -1
- package/components/TimePicker/helpers/TimePicker.value.d.ts +21 -0
- package/components/TimePicker/helpers/TimePicker.value.js +67 -5
- package/components/TimePicker/helpers/TimePicker.value.js.map +1 -1
- package/components/TimePicker/hooks/useTimePickerDropdown.d.ts +6 -1
- package/components/TimePicker/hooks/useTimePickerDropdown.js +7 -9
- package/components/TimePicker/hooks/useTimePickerDropdown.js.map +1 -1
- package/components/TimePicker/hooks/useTimePickerSource.d.ts +27 -0
- package/components/TimePicker/hooks/useTimePickerSource.js +59 -0
- package/components/TimePicker/hooks/useTimePickerSource.js.map +1 -0
- package/components/TimePicker/hooks/useTimePickerValue.d.ts +6 -0
- package/components/TimePicker/hooks/useTimePickerValue.js +27 -6
- package/components/TimePicker/hooks/useTimePickerValue.js.map +1 -1
- package/components/TimePicker/index.d.ts +2 -0
- package/components/TimePicker/index.js +1 -0
- package/components/TimePicker/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -11,8 +11,8 @@ export var TimePickerPopup = function (props) {
|
|
|
11
11
|
var id = props.id, anchorElement = props.anchorElement, menuPos = props.menuPos, menuAlign = props.menuAlign, menuWidth = props.menuWidth, popupMaxHeight = props.popupMaxHeight, format = props.format, size = props.size, resolvedItems = props.resolvedItems, renderItem = props.renderItem, isLoading = props.isLoading, highlightedItemIndex = props.highlightedItemIndex, normalizedValue = props.normalizedValue, itemRefs = props.itemRefs, onSelectItem = props.onSelectItem;
|
|
12
12
|
var theme = useContext(ThemeContext);
|
|
13
13
|
var styles = useStyles(getStyles);
|
|
14
|
-
return (React.createElement(Popup, {
|
|
15
|
-
React.createElement("div", { className: styles.popup(theme), onMouseDown: function (event) { return event.preventDefault(); } },
|
|
14
|
+
return (React.createElement(Popup, { opened: true, hasShadow: true, anchorElement: anchorElement, "data-tid": TimePickerDataTids.popup, priority: ZIndex.priorities.PopupMenu, margin: parseInt(theme.timePickerMenuOffsetY), width: menuWidth, minWidth: menuWidth === undefined ? '100%' : undefined, positions: getMenuPositions(menuPos, menuAlign) },
|
|
15
|
+
React.createElement("div", { id: id, role: 'listbox', className: styles.popup(theme), onMouseDown: function (event) { return event.preventDefault(); } },
|
|
16
16
|
React.createElement(TimePickerItems, { itemIdPrefix: id, format: format, size: size, resolvedItems: resolvedItems, renderItem: renderItem, isLoading: isLoading, highlightedItemIndex: highlightedItemIndex, normalizedValue: normalizedValue, itemRefs: itemRefs, maxHeight: popupMaxHeight, onSelectItem: onSelectItem }))));
|
|
17
17
|
};
|
|
18
18
|
//# sourceMappingURL=TimePickerPopup.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TimePickerPopup.js","sourceRoot":"","sources":["TimePickerPopup.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAE1C,OAAO,EAAE,KAAK,EAAE,MAAM,+BAA+B,CAAC;AACtD,OAAO,EAAE,MAAM,EAAE,MAAM,gCAAgC,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,SAAS,EAAE,MAAM,sCAAsC,CAAC;AACjE,OAAO,EAAE,YAAY,EAAE,MAAM,mCAAmC,CAAC;AAGjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AAEvE,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAoBvD,MAAM,CAAC,IAAM,eAAe,GAAG,UAA0B,KAA8B;IAEnF,IAAA,EAAE,GAeA,KAAK,GAfL,EACF,aAAa,GAcX,KAAK,cAdM,EACb,OAAO,GAaL,KAAK,QAbA,EACP,SAAS,GAYP,KAAK,UAZE,EACT,SAAS,GAWP,KAAK,UAXE,EACT,cAAc,GAUZ,KAAK,eAVO,EACd,MAAM,GASJ,KAAK,OATD,EACN,IAAI,GAQF,KAAK,KARH,EACJ,aAAa,GAOX,KAAK,cAPM,EACb,UAAU,GAMR,KAAK,WANG,EACV,SAAS,GAKP,KAAK,UALE,EACT,oBAAoB,GAIlB,KAAK,qBAJa,EACpB,eAAe,GAGb,KAAK,gBAHQ,EACf,QAAQ,GAEN,KAAK,SAFC,EACR,YAAY,GACV,KAAK,aADK,CACJ;IAEV,IAAM,KAAK,GAAG,UAAU,CAAC,YAAY,CAAC,CAAC;IACvC,IAAM,MAAM,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC;IAEpC,OAAO,CACL,oBAAC,KAAK,IACJ,
|
|
1
|
+
{"version":3,"file":"TimePickerPopup.js","sourceRoot":"","sources":["TimePickerPopup.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAE1C,OAAO,EAAE,KAAK,EAAE,MAAM,+BAA+B,CAAC;AACtD,OAAO,EAAE,MAAM,EAAE,MAAM,gCAAgC,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,SAAS,EAAE,MAAM,sCAAsC,CAAC;AACjE,OAAO,EAAE,YAAY,EAAE,MAAM,mCAAmC,CAAC;AAGjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AAEvE,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAoBvD,MAAM,CAAC,IAAM,eAAe,GAAG,UAA0B,KAA8B;IAEnF,IAAA,EAAE,GAeA,KAAK,GAfL,EACF,aAAa,GAcX,KAAK,cAdM,EACb,OAAO,GAaL,KAAK,QAbA,EACP,SAAS,GAYP,KAAK,UAZE,EACT,SAAS,GAWP,KAAK,UAXE,EACT,cAAc,GAUZ,KAAK,eAVO,EACd,MAAM,GASJ,KAAK,OATD,EACN,IAAI,GAQF,KAAK,KARH,EACJ,aAAa,GAOX,KAAK,cAPM,EACb,UAAU,GAMR,KAAK,WANG,EACV,SAAS,GAKP,KAAK,UALE,EACT,oBAAoB,GAIlB,KAAK,qBAJa,EACpB,eAAe,GAGb,KAAK,gBAHQ,EACf,QAAQ,GAEN,KAAK,SAFC,EACR,YAAY,GACV,KAAK,aADK,CACJ;IAEV,IAAM,KAAK,GAAG,UAAU,CAAC,YAAY,CAAC,CAAC;IACvC,IAAM,MAAM,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC;IAEpC,OAAO,CACL,oBAAC,KAAK,IACJ,MAAM,QACN,SAAS,QACT,aAAa,EAAE,aAAa,cAClB,kBAAkB,CAAC,KAAK,EAClC,QAAQ,EAAE,MAAM,CAAC,UAAU,CAAC,SAAS,EACrC,MAAM,EAAE,QAAQ,CAAC,KAAK,CAAC,qBAAqB,CAAC,EAC7C,KAAK,EAAE,SAAS,EAChB,QAAQ,EAAE,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,EACtD,SAAS,EAAE,gBAAgB,CAAC,OAAO,EAAE,SAAS,CAAC;QAE/C,6BAAK,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,WAAW,EAAE,UAAC,KAAK,IAAK,OAAA,KAAK,CAAC,cAAc,EAAE,EAAtB,CAAsB;YAC1G,oBAAC,eAAe,IACd,YAAY,EAAE,EAAE,EAChB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,IAAI,EACV,aAAa,EAAE,aAAa,EAC5B,UAAU,EAAE,UAAU,EACtB,SAAS,EAAE,SAAS,EACpB,oBAAoB,EAAE,oBAAoB,EAC1C,eAAe,EAAE,eAAe,EAChC,QAAQ,EAAE,QAAQ,EAClB,SAAS,EAAE,cAAc,EACzB,YAAY,EAAE,YAAY,GAC1B,CACE,CACA,CACT,CAAC;AACJ,CAAC,CAAC","sourcesContent":["import React, { useContext } from 'react';\n\nimport { Popup } from '../../internal/Popup/index.js';\nimport { ZIndex } from '../../internal/ZIndex/index.js';\nimport { getMenuPositions } from '../../lib/getMenuPositions.js';\nimport { useStyles } from '../../lib/renderEnvironment/index.js';\nimport { ThemeContext } from '../../lib/theming/ThemeContext.js';\nimport type { SizeProp } from '../../lib/types/props.js';\nimport type { MenuItemState } from '../MenuItem/index.js';\nimport { TimePickerDataTids } from './helpers/TimePicker.constants.js';\nimport type { TimeFormat, TimeItemValue, TimePickerMenuItem } from './helpers/TimePicker.shared.js';\nimport { getStyles } from './TimePicker.styles.js';\nimport { TimePickerItems } from './TimePickerItems.js';\n\ninterface TimePickerPopupProps<T extends TimeItemValue> {\n id?: string;\n anchorElement: HTMLElement;\n menuPos?: 'top' | 'bottom';\n menuAlign?: 'left' | 'right';\n menuWidth?: React.CSSProperties['width'];\n popupMaxHeight: string;\n format: TimeFormat;\n size: SizeProp;\n resolvedItems: Array<TimePickerMenuItem<T>>;\n renderItem?: (item: T, state: MenuItemState) => React.ReactNode;\n isLoading?: boolean;\n highlightedItemIndex: number | null;\n normalizedValue: string;\n itemRefs: React.RefObject<Map<number, HTMLSpanElement>>;\n onSelectItem(item: T): void;\n}\n\nexport const TimePickerPopup = <T extends TimeItemValue>(props: TimePickerPopupProps<T>) => {\n const {\n id,\n anchorElement,\n menuPos,\n menuAlign,\n menuWidth,\n popupMaxHeight,\n format,\n size,\n resolvedItems,\n renderItem,\n isLoading,\n highlightedItemIndex,\n normalizedValue,\n itemRefs,\n onSelectItem,\n } = props;\n\n const theme = useContext(ThemeContext);\n const styles = useStyles(getStyles);\n\n return (\n <Popup\n opened\n hasShadow\n anchorElement={anchorElement}\n data-tid={TimePickerDataTids.popup}\n priority={ZIndex.priorities.PopupMenu}\n margin={parseInt(theme.timePickerMenuOffsetY)}\n width={menuWidth}\n minWidth={menuWidth === undefined ? '100%' : undefined}\n positions={getMenuPositions(menuPos, menuAlign)}\n >\n <div id={id} role={'listbox'} className={styles.popup(theme)} onMouseDown={(event) => event.preventDefault()}>\n <TimePickerItems\n itemIdPrefix={id}\n format={format}\n size={size}\n resolvedItems={resolvedItems}\n renderItem={renderItem}\n isLoading={isLoading}\n highlightedItemIndex={highlightedItemIndex}\n normalizedValue={normalizedValue}\n itemRefs={itemRefs}\n maxHeight={popupMaxHeight}\n onSelectItem={onSelectItem}\n />\n </div>\n </Popup>\n );\n};\n"]}
|
|
@@ -20,5 +20,10 @@ export declare const EMPTY_VALUE = "";
|
|
|
20
20
|
export declare const DIGIT_REGEXP: RegExp;
|
|
21
21
|
export declare const NON_DIGIT_REGEXP: RegExp;
|
|
22
22
|
export declare const EMPTY_SEGMENT: string;
|
|
23
|
+
export declare const MAX_TIME_SEGMENTS_COUNT = 3;
|
|
24
|
+
/** Сегменты времени через любой разделитель: `12:30`, `9:5`, `1:23:45`, `12.30`. */
|
|
25
|
+
export declare const TIME_LIKE_SEGMENTS_REGEXP: RegExp;
|
|
26
|
+
/** Время без разделителей: `1`, `12`, `1230`, `123045`. */
|
|
27
|
+
export declare const TIME_LIKE_DIGITS_REGEXP: RegExp;
|
|
23
28
|
export declare const TIME_SEGMENTS_BY_FORMAT: Record<TimeFormat, TimeSegment[]>;
|
|
24
29
|
export declare const FIRST_DIGIT_MAX_BY_SEGMENT: Record<TimeSegment, number>;
|
|
@@ -21,6 +21,11 @@ export var EMPTY_VALUE = '';
|
|
|
21
21
|
export var DIGIT_REGEXP = /\d/;
|
|
22
22
|
export var NON_DIGIT_REGEXP = /\D/g;
|
|
23
23
|
export var EMPTY_SEGMENT = TIME_PLACEHOLDER_CHAR.repeat(TIME_SEGMENT_LENGTH);
|
|
24
|
+
export var MAX_TIME_SEGMENTS_COUNT = 3;
|
|
25
|
+
/** Сегменты времени через любой разделитель: `12:30`, `9:5`, `1:23:45`, `12.30`. */
|
|
26
|
+
export var TIME_LIKE_SEGMENTS_REGEXP = new RegExp("^\\d{1,2}(?:\\D{1,3}\\d{1,2}){0,".concat(MAX_TIME_SEGMENTS_COUNT - 1, "}$"));
|
|
27
|
+
/** Время без разделителей: `1`, `12`, `1230`, `123045`. */
|
|
28
|
+
export var TIME_LIKE_DIGITS_REGEXP = new RegExp("^\\d{1,".concat(MAX_TIME_SEGMENTS_COUNT * TIME_SEGMENT_LENGTH, "}$"));
|
|
24
29
|
export var TIME_SEGMENTS_BY_FORMAT = {
|
|
25
30
|
'HH:mm': ['hours', 'minutes'],
|
|
26
31
|
'HH:mm:ss': ['hours', 'minutes', 'seconds'],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TimePicker.constants.js","sourceRoot":"","sources":["TimePicker.constants.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,IAAM,kBAAkB,GAAG;IAChC,IAAI,EAAE,kBAAkB;IACxB,KAAK,EAAE,mBAAmB;IAC1B,WAAW,EAAE,yBAAyB;IACtC,WAAW,EAAE,yBAAyB;IACtC,KAAK,EAAE,mBAAmB;IAC1B,WAAW,EAAE,yBAAyB;IACtC,IAAI,EAAE,kBAAkB;IACxB,OAAO,EAAE,qBAAqB;IAC9B,YAAY,EAAE,0BAA0B;CAChC,CAAC;AAEX,IAAM,qBAAqB,GAAG,CAAC,CAAC;AAChC,IAAM,mCAAmC,GAAG,CAAC,CAAC;AAC9C,MAAM,CAAC,IAAM,qBAAqB,GAAG,QAAQ,CAAC;AAC9C,MAAM,CAAC,IAAM,cAAc,GAAG,GAAG,CAAC;AAClC,MAAM,CAAC,IAAM,mBAAmB,GAAG,CAAC,CAAC;AACrC,MAAM,CAAC,IAAM,eAAe,GAAG,EAAE,CAAC;AAClC,MAAM,CAAC,IAAM,6BAA6B,GAAG,EAAE,CAAC;AAChD,MAAM,CAAC,IAAM,aAAa,GAAG,GAAG,CAAC;AACjC,MAAM,CAAC,IAAM,WAAW,GAAG,EAAE,CAAC;AAC9B,MAAM,CAAC,IAAM,YAAY,GAAG,IAAI,CAAC;AACjC,MAAM,CAAC,IAAM,gBAAgB,GAAG,KAAK,CAAC;AACtC,MAAM,CAAC,IAAM,aAAa,GAAG,qBAAqB,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC;AAE/E,MAAM,CAAC,IAAM,uBAAuB,GAAsC;IACxE,OAAO,EAAE,CAAC,OAAO,EAAE,SAAS,CAAC;IAC7B,UAAU,EAAE,CAAC,OAAO,EAAE,SAAS,EAAE,SAAS,CAAC;CAC5C,CAAC;AAEF,MAAM,CAAC,IAAM,0BAA0B,GAAgC;IACrE,KAAK,EAAE,qBAAqB;IAC5B,OAAO,EAAE,mCAAmC;IAC5C,OAAO,EAAE,mCAAmC;CAC7C,CAAC","sourcesContent":["import type { TimeFormat, TimeSegment } from './TimePicker.shared.js';\n\nexport const TimePickerDataTids = {\n root: 'TimePicker__root',\n input: 'TimePicker__input',\n mobileInput: 'TimePicker__mobileInput',\n nativeInput: 'TimePicker__nativeInput',\n popup: 'TimePicker__popup',\n mobilePopup: 'TimePicker__mobilePopup',\n item: 'TimePicker__item',\n loading: 'TimePicker__loading',\n inputLoading: 'TimePicker__inputLoading',\n} as const;\n\nconst HOURS_FIRST_DIGIT_MAX = 2;\nconst MINUTES_AND_SECONDS_FIRST_DIGIT_MAX = 5;\nexport const TIME_PLACEHOLDER_CHAR = '\\u2212';\nexport const TIME_SEPARATOR = ':';\nexport const TIME_SEGMENT_LENGTH = 2;\nexport const HOURS_MAX_VALUE = 23;\nexport const MINUTES_AND_SECONDS_MAX_VALUE = 59;\nexport const ZERO_PAD_CHAR = '0';\nexport const EMPTY_VALUE = '';\nexport const DIGIT_REGEXP = /\\d/;\nexport const NON_DIGIT_REGEXP = /\\D/g;\nexport const EMPTY_SEGMENT = TIME_PLACEHOLDER_CHAR.repeat(TIME_SEGMENT_LENGTH);\n\nexport const TIME_SEGMENTS_BY_FORMAT: Record<TimeFormat, TimeSegment[]> = {\n 'HH:mm': ['hours', 'minutes'],\n 'HH:mm:ss': ['hours', 'minutes', 'seconds'],\n};\n\nexport const FIRST_DIGIT_MAX_BY_SEGMENT: Record<TimeSegment, number> = {\n hours: HOURS_FIRST_DIGIT_MAX,\n minutes: MINUTES_AND_SECONDS_FIRST_DIGIT_MAX,\n seconds: MINUTES_AND_SECONDS_FIRST_DIGIT_MAX,\n};\n"]}
|
|
1
|
+
{"version":3,"file":"TimePicker.constants.js","sourceRoot":"","sources":["TimePicker.constants.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,IAAM,kBAAkB,GAAG;IAChC,IAAI,EAAE,kBAAkB;IACxB,KAAK,EAAE,mBAAmB;IAC1B,WAAW,EAAE,yBAAyB;IACtC,WAAW,EAAE,yBAAyB;IACtC,KAAK,EAAE,mBAAmB;IAC1B,WAAW,EAAE,yBAAyB;IACtC,IAAI,EAAE,kBAAkB;IACxB,OAAO,EAAE,qBAAqB;IAC9B,YAAY,EAAE,0BAA0B;CAChC,CAAC;AAEX,IAAM,qBAAqB,GAAG,CAAC,CAAC;AAChC,IAAM,mCAAmC,GAAG,CAAC,CAAC;AAC9C,MAAM,CAAC,IAAM,qBAAqB,GAAG,QAAQ,CAAC;AAC9C,MAAM,CAAC,IAAM,cAAc,GAAG,GAAG,CAAC;AAClC,MAAM,CAAC,IAAM,mBAAmB,GAAG,CAAC,CAAC;AACrC,MAAM,CAAC,IAAM,eAAe,GAAG,EAAE,CAAC;AAClC,MAAM,CAAC,IAAM,6BAA6B,GAAG,EAAE,CAAC;AAChD,MAAM,CAAC,IAAM,aAAa,GAAG,GAAG,CAAC;AACjC,MAAM,CAAC,IAAM,WAAW,GAAG,EAAE,CAAC;AAC9B,MAAM,CAAC,IAAM,YAAY,GAAG,IAAI,CAAC;AACjC,MAAM,CAAC,IAAM,gBAAgB,GAAG,KAAK,CAAC;AACtC,MAAM,CAAC,IAAM,aAAa,GAAG,qBAAqB,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC;AAE/E,MAAM,CAAC,IAAM,uBAAuB,GAAG,CAAC,CAAC;AACzC,oFAAoF;AACpF,MAAM,CAAC,IAAM,yBAAyB,GAAG,IAAI,MAAM,CAAC,0CAAmC,uBAAuB,GAAG,CAAC,OAAI,CAAC,CAAC;AACxH,2DAA2D;AAC3D,MAAM,CAAC,IAAM,uBAAuB,GAAG,IAAI,MAAM,CAAC,iBAAU,uBAAuB,GAAG,mBAAmB,OAAI,CAAC,CAAC;AAE/G,MAAM,CAAC,IAAM,uBAAuB,GAAsC;IACxE,OAAO,EAAE,CAAC,OAAO,EAAE,SAAS,CAAC;IAC7B,UAAU,EAAE,CAAC,OAAO,EAAE,SAAS,EAAE,SAAS,CAAC;CAC5C,CAAC;AAEF,MAAM,CAAC,IAAM,0BAA0B,GAAgC;IACrE,KAAK,EAAE,qBAAqB;IAC5B,OAAO,EAAE,mCAAmC;IAC5C,OAAO,EAAE,mCAAmC;CAC7C,CAAC","sourcesContent":["import type { TimeFormat, TimeSegment } from './TimePicker.shared.js';\n\nexport const TimePickerDataTids = {\n root: 'TimePicker__root',\n input: 'TimePicker__input',\n mobileInput: 'TimePicker__mobileInput',\n nativeInput: 'TimePicker__nativeInput',\n popup: 'TimePicker__popup',\n mobilePopup: 'TimePicker__mobilePopup',\n item: 'TimePicker__item',\n loading: 'TimePicker__loading',\n inputLoading: 'TimePicker__inputLoading',\n} as const;\n\nconst HOURS_FIRST_DIGIT_MAX = 2;\nconst MINUTES_AND_SECONDS_FIRST_DIGIT_MAX = 5;\nexport const TIME_PLACEHOLDER_CHAR = '\\u2212';\nexport const TIME_SEPARATOR = ':';\nexport const TIME_SEGMENT_LENGTH = 2;\nexport const HOURS_MAX_VALUE = 23;\nexport const MINUTES_AND_SECONDS_MAX_VALUE = 59;\nexport const ZERO_PAD_CHAR = '0';\nexport const EMPTY_VALUE = '';\nexport const DIGIT_REGEXP = /\\d/;\nexport const NON_DIGIT_REGEXP = /\\D/g;\nexport const EMPTY_SEGMENT = TIME_PLACEHOLDER_CHAR.repeat(TIME_SEGMENT_LENGTH);\n\nexport const MAX_TIME_SEGMENTS_COUNT = 3;\n/** Сегменты времени через любой разделитель: `12:30`, `9:5`, `1:23:45`, `12.30`. */\nexport const TIME_LIKE_SEGMENTS_REGEXP = new RegExp(`^\\\\d{1,2}(?:\\\\D{1,3}\\\\d{1,2}){0,${MAX_TIME_SEGMENTS_COUNT - 1}}$`);\n/** Время без разделителей: `1`, `12`, `1230`, `123045`. */\nexport const TIME_LIKE_DIGITS_REGEXP = new RegExp(`^\\\\d{1,${MAX_TIME_SEGMENTS_COUNT * TIME_SEGMENT_LENGTH}}$`);\n\nexport const TIME_SEGMENTS_BY_FORMAT: Record<TimeFormat, TimeSegment[]> = {\n 'HH:mm': ['hours', 'minutes'],\n 'HH:mm:ss': ['hours', 'minutes', 'seconds'],\n};\n\nexport const FIRST_DIGIT_MAX_BY_SEGMENT: Record<TimeSegment, number> = {\n hours: HOURS_FIRST_DIGIT_MAX,\n minutes: MINUTES_AND_SECONDS_FIRST_DIGIT_MAX,\n seconds: MINUTES_AND_SECONDS_FIRST_DIGIT_MAX,\n};\n"]}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ReactElement, ReactNode } from 'react';
|
|
2
|
+
import type { MenuItemProps } from '../../MenuItem/index.js';
|
|
2
3
|
export type TimeFormat = 'HH:mm' | 'HH:mm:ss';
|
|
3
4
|
export type TimeSegment = 'hours' | 'minutes' | 'seconds';
|
|
4
5
|
export interface TimeItem {
|
|
@@ -28,6 +29,14 @@ export type TimePickerMenuItem<T extends TimeItemValue = TimeItemValue> = TimePi
|
|
|
28
29
|
export declare const isTimeItem: <T extends TimeItemValue>(item: TimePickerExtendedItem<T>) => item is T;
|
|
29
30
|
/** Проверяет, что элемент выпадающего списка содержит время, а не является React-элементом. */
|
|
30
31
|
export declare const isTimeMenuItem: <T extends TimeItemValue>(item: TimePickerMenuItem<T>) => item is TimePickerResolvedItem<T>;
|
|
32
|
+
/**
|
|
33
|
+
* Проверяет, что элемент выпадающего списка — это `MenuItem`, по которому можно ходить с клавиатуры.
|
|
34
|
+
* Заблокированные и невыбираемые пункты, как и элементы-функции, в навигации не участвуют:
|
|
35
|
+
* их не выбирает и клик мышью.
|
|
36
|
+
*/
|
|
37
|
+
export declare const isNavigableMenuElement: <T extends TimeItemValue>(item: TimePickerMenuItem<T>) => item is ReactElement<MenuItemProps>;
|
|
38
|
+
/** Проверяет, что на элементе выпадающего списка может стоять выделение с клавиатуры. */
|
|
39
|
+
export declare const isHighlightableMenuItem: <T extends TimeItemValue>(item: TimePickerMenuItem<T>) => boolean;
|
|
31
40
|
/** Возвращает время элемента. */
|
|
32
41
|
export declare const getTimeItemValue: (item: TimeItemValue) => string;
|
|
33
42
|
/** Возвращает подпись элемента. У элемента-строки подписи нет. */
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isValidElement } from 'react';
|
|
2
|
+
import { isMenuItem } from '../../MenuItem/index.js';
|
|
2
3
|
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';
|
|
3
4
|
/** Проверяет, что элемент источника содержит время, а не является React-элементом. */
|
|
4
5
|
export var isTimeItem = function (item) {
|
|
@@ -8,6 +9,22 @@ export var isTimeItem = function (item) {
|
|
|
8
9
|
export var isTimeMenuItem = function (item) {
|
|
9
10
|
return typeof item !== 'function' && !isValidElement(item);
|
|
10
11
|
};
|
|
12
|
+
/**
|
|
13
|
+
* Проверяет, что элемент выпадающего списка — это `MenuItem`, по которому можно ходить с клавиатуры.
|
|
14
|
+
* Заблокированные и невыбираемые пункты, как и элементы-функции, в навигации не участвуют:
|
|
15
|
+
* их не выбирает и клик мышью.
|
|
16
|
+
*/
|
|
17
|
+
export var isNavigableMenuElement = function (item) {
|
|
18
|
+
if (typeof item === 'function' || !isValidElement(item) || !isMenuItem(item)) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
var _a = item.props, disabled = _a.disabled, isNotSelectable = _a.isNotSelectable;
|
|
22
|
+
return !disabled && !isNotSelectable;
|
|
23
|
+
};
|
|
24
|
+
/** Проверяет, что на элементе выпадающего списка может стоять выделение с клавиатуры. */
|
|
25
|
+
export var isHighlightableMenuItem = function (item) {
|
|
26
|
+
return isTimeMenuItem(item) ? !item.disabled : isNavigableMenuElement(item);
|
|
27
|
+
};
|
|
11
28
|
/** Возвращает время элемента. */
|
|
12
29
|
export var getTimeItemValue = function (item) { return (typeof item === 'string' ? item : item.value); };
|
|
13
30
|
/** Возвращает подпись элемента. У элемента-строки подписи нет. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TimePicker.shared.js","sourceRoot":"","sources":["TimePicker.shared.ts"],"names":[],"mappings":"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","sourcesContent":["import { isValidElement } from 'react';\nimport type { ReactElement, 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 = 'HH:mm' | 'HH:mm:ss';\nexport type TimeSegment = 'hours' | 'minutes' | 'seconds';\n\nexport interface TimeItem {\n disabled?: boolean;\n value: string;\n label?: ReactNode;\n}\n\n/** Элемент со временем: строка в формате `HH:mm[:ss]` или объект. */\nexport type TimeItemValue = string | TimeItem;\n\n/** Элемент, который можно передать в `source`: элемент со временем или React-элемент. */\nexport type TimePickerExtendedItem<T extends TimeItemValue = TimeItemValue> = T | ReactElement | (() => ReactElement);\n\nexport type TimePickerSource<T extends TimeItemValue = TimeItemValue> =\n | Array<TimePickerExtendedItem<T>>\n | ((query: string) => Array<TimePickerExtendedItem<T>> | Promise<Array<TimePickerExtendedItem<T>>>);\n\n/** Значение поля времени: элемент, строка в формате `HH:mm[:ss]` или пустое значение. */\nexport type TimePickerValue<T extends TimeItemValue = TimeItemValue> = T | string | null;\n\n/** Преобразует введенное пользователем время в элемент. */\nexport type TimePickerValueToItem<T extends TimeItemValue = TimeItemValue> = (value: string) => T;\n\n/** Разобранный элемент выпадающего списка: исходный элемент, его время, подпись и доступность. */\nexport interface TimePickerResolvedItem<T extends TimeItemValue = TimeItemValue> {\n item: T;\n value: string;\n label?: ReactNode;\n disabled: boolean;\n}\n\n/** Элемент выпадающего списка: разобранный элемент со временем или React-элемент. */\nexport type TimePickerMenuItem<T extends TimeItemValue = TimeItemValue> =\n | TimePickerResolvedItem<T>\n | ReactElement\n | (() => ReactElement);\n\n/** Проверяет, что элемент источника содержит время, а не является React-элементом. */\nexport const isTimeItem = <T extends TimeItemValue>(item: TimePickerExtendedItem<T>): item is T => {\n return typeof item === 'string' || (typeof item !== 'function' && !isValidElement(item));\n};\n\n/** Проверяет, что элемент выпадающего списка содержит время, а не является React-элементом. */\nexport const isTimeMenuItem = <T extends TimeItemValue>(\n item: TimePickerMenuItem<T>,\n): item is TimePickerResolvedItem<T> => {\n return typeof item !== 'function' && !isValidElement(item);\n};\n\n/** Возвращает время элемента. */\nexport const getTimeItemValue = (item: TimeItemValue): string => (typeof item === 'string' ? item : item.value);\n\n/** Возвращает подпись элемента. У элемента-строки подписи нет. */\nexport const getTimeItemLabel = (item: TimeItemValue): ReactNode => (typeof item === 'string' ? undefined : item.label);\n\n/** Проверяет, заблокирован ли элемент. Элемент-строка заблокированным быть не может. */\nexport const isTimeItemDisabled = (item: TimeItemValue): boolean => typeof item !== 'string' && Boolean(item.disabled);\n\n/** Проверяет, задан ли элемент объектом. */\nexport const isTimeItemObject = (item: TimeItemValue): item is TimeItem => typeof item !== 'string';\n\n/**\n * Дефолтное преобразование времени в элемент.\n * Компонент повторяет форму элементов источника: строку оставляет строкой, а объекту задает только `value`.\n */\nexport const timeValueToItem = <T extends TimeItemValue>(value: string, asObject: boolean): T =>\n (asObject ? { value } : value) as T;\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"]}
|
|
1
|
+
{"version":3,"file":"TimePicker.shared.js","sourceRoot":"","sources":["TimePicker.shared.ts"],"names":[],"mappings":"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","sourcesContent":["import { isValidElement } from 'react';\nimport type { ReactElement, ReactNode } from 'react';\n\nimport type { MenuItemProps } from '../../MenuItem/index.js';\nimport { isMenuItem } from '../../MenuItem/index.js';\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 = 'HH:mm' | 'HH:mm:ss';\nexport type TimeSegment = 'hours' | 'minutes' | 'seconds';\n\nexport interface TimeItem {\n disabled?: boolean;\n value: string;\n label?: ReactNode;\n}\n\n/** Элемент со временем: строка в формате `HH:mm[:ss]` или объект. */\nexport type TimeItemValue = string | TimeItem;\n\n/** Элемент, который можно передать в `source`: элемент со временем или React-элемент. */\nexport type TimePickerExtendedItem<T extends TimeItemValue = TimeItemValue> = T | ReactElement | (() => ReactElement);\n\nexport type TimePickerSource<T extends TimeItemValue = TimeItemValue> =\n | Array<TimePickerExtendedItem<T>>\n | ((query: string) => Array<TimePickerExtendedItem<T>> | Promise<Array<TimePickerExtendedItem<T>>>);\n\n/** Значение поля времени: элемент, строка в формате `HH:mm[:ss]` или пустое значение. */\nexport type TimePickerValue<T extends TimeItemValue = TimeItemValue> = T | string | null;\n\n/** Преобразует введенное пользователем время в элемент. */\nexport type TimePickerValueToItem<T extends TimeItemValue = TimeItemValue> = (value: string) => T;\n\n/** Разобранный элемент выпадающего списка: исходный элемент, его время, подпись и доступность. */\nexport interface TimePickerResolvedItem<T extends TimeItemValue = TimeItemValue> {\n item: T;\n value: string;\n label?: ReactNode;\n disabled: boolean;\n}\n\n/** Элемент выпадающего списка: разобранный элемент со временем или React-элемент. */\nexport type TimePickerMenuItem<T extends TimeItemValue = TimeItemValue> =\n | TimePickerResolvedItem<T>\n | ReactElement\n | (() => ReactElement);\n\n/** Проверяет, что элемент источника содержит время, а не является React-элементом. */\nexport const isTimeItem = <T extends TimeItemValue>(item: TimePickerExtendedItem<T>): item is T => {\n return typeof item === 'string' || (typeof item !== 'function' && !isValidElement(item));\n};\n\n/** Проверяет, что элемент выпадающего списка содержит время, а не является React-элементом. */\nexport const isTimeMenuItem = <T extends TimeItemValue>(\n item: TimePickerMenuItem<T>,\n): item is TimePickerResolvedItem<T> => {\n return typeof item !== 'function' && !isValidElement(item);\n};\n\n/**\n * Проверяет, что элемент выпадающего списка — это `MenuItem`, по которому можно ходить с клавиатуры.\n * Заблокированные и невыбираемые пункты, как и элементы-функции, в навигации не участвуют:\n * их не выбирает и клик мышью.\n */\nexport const isNavigableMenuElement = <T extends TimeItemValue>(\n item: TimePickerMenuItem<T>,\n): item is ReactElement<MenuItemProps> => {\n if (typeof item === 'function' || !isValidElement(item) || !isMenuItem(item)) {\n return false;\n }\n\n const { disabled, isNotSelectable } = item.props as MenuItemProps;\n\n return !disabled && !isNotSelectable;\n};\n\n/** Проверяет, что на элементе выпадающего списка может стоять выделение с клавиатуры. */\nexport const isHighlightableMenuItem = <T extends TimeItemValue>(item: TimePickerMenuItem<T>): boolean =>\n isTimeMenuItem(item) ? !item.disabled : isNavigableMenuElement(item);\n\n/** Возвращает время элемента. */\nexport const getTimeItemValue = (item: TimeItemValue): string => (typeof item === 'string' ? item : item.value);\n\n/** Возвращает подпись элемента. У элемента-строки подписи нет. */\nexport const getTimeItemLabel = (item: TimeItemValue): ReactNode => (typeof item === 'string' ? undefined : item.label);\n\n/** Проверяет, заблокирован ли элемент. Элемент-строка заблокированным быть не может. */\nexport const isTimeItemDisabled = (item: TimeItemValue): boolean => typeof item !== 'string' && Boolean(item.disabled);\n\n/** Проверяет, задан ли элемент объектом. */\nexport const isTimeItemObject = (item: TimeItemValue): item is TimeItem => typeof item !== 'string';\n\n/**\n * Дефолтное преобразование времени в элемент.\n * Компонент повторяет форму элементов источника: строку оставляет строкой, а объекту задает только `value`.\n */\nexport const timeValueToItem = <T extends TimeItemValue>(value: string, asObject: boolean): T =>\n (asObject ? { value } : value) as T;\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"]}
|
|
@@ -10,6 +10,12 @@ export declare const getTimeValueString: (value: TimeItemValue | null | undefine
|
|
|
10
10
|
* React-элементы остаются на своих местах.
|
|
11
11
|
*/
|
|
12
12
|
export declare const resolveTimeItems: <T extends TimeItemValue>(items: Array<TimePickerExtendedItem<T>>, format: TimeFormat, minTime?: string, maxTime?: string) => Array<TimePickerMenuItem<T>>;
|
|
13
|
+
/**
|
|
14
|
+
* Оставляет только элементы, которые компонент умеет показать в меню.
|
|
15
|
+
* Пустые значения — результат условного рендера вроде `isVisible && item` или `items.length && item` —
|
|
16
|
+
* молча отбрасываются, а элементы, из которых не получается собрать время, отбрасываются с предупреждением.
|
|
17
|
+
*/
|
|
18
|
+
export declare const sanitizeTimeItems: <T extends TimeItemValue>(items: Array<TimePickerExtendedItem<T>>, format: TimeFormat) => Array<TimePickerExtendedItem<T>>;
|
|
13
19
|
/**
|
|
14
20
|
* Оставляет в списке только первый элемент для каждого времени.
|
|
15
21
|
* Времена сравниваются после нормализации к текущему формату,
|
|
@@ -34,10 +40,25 @@ export declare const normalizeTimeValue: (value: string, format: TimeFormat) =>
|
|
|
34
40
|
export declare const serializeTimeValue: (value: string, format: TimeFormat) => string;
|
|
35
41
|
/** Возвращает display-значение с плейсхолдерами из partial или committed value. */
|
|
36
42
|
export declare const getTimeDisplayValue: (value: string, format: TimeFormat) => string;
|
|
43
|
+
/**
|
|
44
|
+
* Возвращает display-значение внешнего `value`.
|
|
45
|
+
* Сегменты, записанные одной цифрой, дополняются нулем, поэтому `9:00` показывается как `09:00` —
|
|
46
|
+
* так же, как компонент показывает время элементов источника.
|
|
47
|
+
* Значение без разделителя остается частично введенным: `1` — это первая цифра часов, а не `01:00`.
|
|
48
|
+
*/
|
|
49
|
+
export declare const getExternalTimeDisplayValue: (value: string, format: TimeFormat) => string;
|
|
37
50
|
/** Заменяет значение выбранного сегмента, сохраняя остальные сегменты display-значения без изменений. */
|
|
38
51
|
export declare const replaceTimeSegment: (value: string, segment: TimeSegment, nextSegmentValue: string, format: TimeFormat) => string;
|
|
52
|
+
/**
|
|
53
|
+
* Проверяет, что вставленное значение похоже на время:
|
|
54
|
+
* это либо сегменты из одной-двух цифр через любой разделитель — `12:30`, `9:5`, `1:23:45`, `12.30`, —
|
|
55
|
+
* либо цифры подряд — `9`, `1230`, `123045`.
|
|
56
|
+
* Строки, из которых время собралось бы случайно — `2026-08-01`, `hello 42`, — временем не считаются.
|
|
57
|
+
*/
|
|
58
|
+
export declare const isTimeLikePastedValue: (value: string) => boolean;
|
|
39
59
|
/**
|
|
40
60
|
* Парсит вставленное пользователем значение и сразу преобразует его в committed-форму.
|
|
61
|
+
* Значение, не похожее на время, отдается как пустое: такую вставку компонент считает некорректным вводом.
|
|
41
62
|
*/
|
|
42
63
|
export declare const parsePastedTimeValue: (value: string, format: TimeFormat) => string;
|
|
43
64
|
/** Возвращает текущее display-значение сегмента или пустой сегмент, если индекс отсутствует. */
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
1
|
+
import warning from 'warning';
|
|
2
|
+
import { DIGIT_REGEXP, EMPTY_SEGMENT, EMPTY_VALUE, NON_DIGIT_REGEXP, TIME_LIKE_DIGITS_REGEXP, TIME_LIKE_SEGMENTS_REGEXP, TIME_SEPARATOR, ZERO_PAD_CHAR, } from './TimePicker.constants.js';
|
|
3
|
+
import { getDigits, getDisplaySegments, getTimeSegments, normalizeTimeSegment, sanitizeSegment, getTimeItemLabel, getTimeItemValue, isTimeItemDisabled, isTimeItem, isTimeItemObject, } from './TimePicker.shared.js';
|
|
3
4
|
/** Проверяет, содержит ли display-значение хотя бы одну введенную цифру. */
|
|
4
5
|
export var isTimeDisplayEmpty = function (value) { return !DIGIT_REGEXP.test(value); };
|
|
5
6
|
/** Приводит значение поля — элемент, строку или пустое значение — к строке времени. */
|
|
@@ -29,6 +30,34 @@ export var resolveTimeItems = function (items, format, minTime, maxTime) {
|
|
|
29
30
|
};
|
|
30
31
|
});
|
|
31
32
|
};
|
|
33
|
+
/**
|
|
34
|
+
* Оставляет только элементы, которые компонент умеет показать в меню.
|
|
35
|
+
* Пустые значения — результат условного рендера вроде `isVisible && item` или `items.length && item` —
|
|
36
|
+
* молча отбрасываются, а элементы, из которых не получается собрать время, отбрасываются с предупреждением.
|
|
37
|
+
*/
|
|
38
|
+
export var sanitizeTimeItems = function (items, format) {
|
|
39
|
+
if (!Array.isArray(items)) {
|
|
40
|
+
warning(false, '[TimePicker]: the source function is expected to return an array or a Promise of an array.');
|
|
41
|
+
return [];
|
|
42
|
+
}
|
|
43
|
+
return items.filter(function (item) {
|
|
44
|
+
if (!item) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
if (!isTimeItem(item)) {
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
if (isTimeItemObject(item) && typeof item.value !== 'string') {
|
|
51
|
+
warning(false, '[TimePicker]: a source item is expected to be a time string or an object with a time value.');
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
if (normalizeTimeValue(getTimeItemValue(item), format) === EMPTY_VALUE) {
|
|
55
|
+
warning(false, "[TimePicker]: the source item value \"".concat(getTimeItemValue(item), "\" is not a time."));
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
return true;
|
|
59
|
+
});
|
|
60
|
+
};
|
|
32
61
|
/**
|
|
33
62
|
* Оставляет в списке только первый элемент для каждого времени.
|
|
34
63
|
* Времена сравниваются после нормализации к текущему формату,
|
|
@@ -74,8 +103,9 @@ export var normalizeTimeValue = function (value, format) {
|
|
|
74
103
|
if (isTimeDisplayEmpty(value)) {
|
|
75
104
|
return EMPTY_VALUE;
|
|
76
105
|
}
|
|
106
|
+
var displaySegments = getDisplaySegments(value, format);
|
|
77
107
|
return getTimeSegments(format)
|
|
78
|
-
.map(function (segment, index) { return normalizeTimeSegment(
|
|
108
|
+
.map(function (segment, index) { return normalizeTimeSegment(displaySegments[index], segment); })
|
|
79
109
|
.join(TIME_SEPARATOR);
|
|
80
110
|
};
|
|
81
111
|
var normalizeTimeRangeValue = function (value, format) {
|
|
@@ -110,6 +140,22 @@ export var getTimeDisplayValue = function (value, format) {
|
|
|
110
140
|
}
|
|
111
141
|
return getDisplaySegments(value, format).join(TIME_SEPARATOR);
|
|
112
142
|
};
|
|
143
|
+
/**
|
|
144
|
+
* Возвращает display-значение внешнего `value`.
|
|
145
|
+
* Сегменты, записанные одной цифрой, дополняются нулем, поэтому `9:00` показывается как `09:00` —
|
|
146
|
+
* так же, как компонент показывает время элементов источника.
|
|
147
|
+
* Значение без разделителя остается частично введенным: `1` — это первая цифра часов, а не `01:00`.
|
|
148
|
+
*/
|
|
149
|
+
export var getExternalTimeDisplayValue = function (value, format) {
|
|
150
|
+
if (!value.includes(TIME_SEPARATOR)) {
|
|
151
|
+
return getTimeDisplayValue(value, format);
|
|
152
|
+
}
|
|
153
|
+
var paddedSegments = value.split(TIME_SEPARATOR).map(function (segmentValue) {
|
|
154
|
+
var digits = getDigits(segmentValue);
|
|
155
|
+
return digits ? digits.padStart(EMPTY_SEGMENT.length, ZERO_PAD_CHAR) : EMPTY_VALUE;
|
|
156
|
+
});
|
|
157
|
+
return getTimeDisplayValue(paddedSegments.join(TIME_SEPARATOR), format);
|
|
158
|
+
};
|
|
113
159
|
/** Заменяет значение выбранного сегмента, сохраняя остальные сегменты display-значения без изменений. */
|
|
114
160
|
export var replaceTimeSegment = function (value, segment, nextSegmentValue, format) {
|
|
115
161
|
var segments = getDisplaySegments(value, format);
|
|
@@ -117,14 +163,30 @@ export var replaceTimeSegment = function (value, segment, nextSegmentValue, form
|
|
|
117
163
|
segments[index] = sanitizeSegment(nextSegmentValue);
|
|
118
164
|
return segments.join(TIME_SEPARATOR);
|
|
119
165
|
};
|
|
166
|
+
/**
|
|
167
|
+
* Проверяет, что вставленное значение похоже на время:
|
|
168
|
+
* это либо сегменты из одной-двух цифр через любой разделитель — `12:30`, `9:5`, `1:23:45`, `12.30`, —
|
|
169
|
+
* либо цифры подряд — `9`, `1230`, `123045`.
|
|
170
|
+
* Строки, из которых время собралось бы случайно — `2026-08-01`, `hello 42`, — временем не считаются.
|
|
171
|
+
*/
|
|
172
|
+
export var isTimeLikePastedValue = function (value) {
|
|
173
|
+
var trimmedValue = value.trim();
|
|
174
|
+
return TIME_LIKE_SEGMENTS_REGEXP.test(trimmedValue) || TIME_LIKE_DIGITS_REGEXP.test(trimmedValue);
|
|
175
|
+
};
|
|
120
176
|
/**
|
|
121
177
|
* Парсит вставленное пользователем значение и сразу преобразует его в committed-форму.
|
|
178
|
+
* Значение, не похожее на время, отдается как пустое: такую вставку компонент считает некорректным вводом.
|
|
122
179
|
*/
|
|
123
180
|
export var parsePastedTimeValue = function (value, format) {
|
|
124
|
-
|
|
181
|
+
var trimmedValue = value.trim();
|
|
182
|
+
if (!DIGIT_REGEXP.test(trimmedValue) || !isTimeLikePastedValue(trimmedValue)) {
|
|
125
183
|
return EMPTY_VALUE;
|
|
126
184
|
}
|
|
127
|
-
|
|
185
|
+
// Разделители сегментов приводятся к одному виду, чтобы `9.5` разбирался так же, как `9:5`.
|
|
186
|
+
var normalizedSeparators = TIME_LIKE_SEGMENTS_REGEXP.test(trimmedValue)
|
|
187
|
+
? trimmedValue.split(NON_DIGIT_REGEXP).join(TIME_SEPARATOR)
|
|
188
|
+
: trimmedValue;
|
|
189
|
+
var segments = getDisplaySegments(normalizedSeparators, format);
|
|
128
190
|
return normalizeTimeValue(segments.join(TIME_SEPARATOR), format);
|
|
129
191
|
};
|
|
130
192
|
/** Возвращает текущее display-значение сегмента или пустой сегмент, если индекс отсутствует. */
|
|
@@ -1 +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 getDigits,\n getDisplaySegments,\n getTimeSegments,\n normalizeTimeSegment,\n sanitizeSegment,\n getTimeItemLabel,\n getTimeItemValue,\n isTimeItemDisabled,\n type TimeFormat,\n type TimeItemValue,\n type TimePickerExtendedItem,\n type TimePickerMenuItem,\n type TimeSegment,\n isTimeItem,\n} from './TimePicker.shared.js';\n\n/** Проверяет, содержит ли display-значение хотя бы одну введенную цифру. */\nexport const isTimeDisplayEmpty = (value: string): boolean => !DIGIT_REGEXP.test(value);\n\n/** Приводит значение поля — элемент, строку или пустое значение — к строке времени. */\nexport const getTimeValueString = (value: TimeItemValue | null | undefined): string => {\n if (value === null || value === undefined) {\n return EMPTY_VALUE;\n }\n\n return getTimeItemValue(value);\n};\n\n/**\n * Разбирает элементы источника для отрисовки: достает время и подпись,\n * а `disabled` дополняет проверкой диапазона `minTime`/`maxTime`.\n * Исходный элемент сохраняется как есть — именно он уходит в `onValueChange`.\n * React-элементы остаются на своих местах.\n */\nexport const resolveTimeItems = <T extends TimeItemValue>(\n items: Array<TimePickerExtendedItem<T>>,\n format: TimeFormat,\n minTime?: string,\n maxTime?: string,\n): Array<TimePickerMenuItem<T>> =>\n items.map((item) => {\n if (!isTimeItem(item)) {\n return item;\n }\n\n const value = getTimeItemValue(item);\n\n return {\n item,\n value,\n label: getTimeItemLabel(item),\n disabled: isTimeItemDisabled(item) || isTimeValueOutOfRange(value, format, minTime, maxTime),\n };\n });\n\n/**\n * Оставляет в списке только первый элемент для каждого времени.\n * Времена сравниваются после нормализации к текущему формату,\n * поэтому в формате `HH:mm` элементы `12:00` и `12:00:30` считаются одинаковыми.\n * React-элементы не участвуют в сравнении и остаются на своих местах.\n */\nexport const dedupeTimeItems = <T extends TimeItemValue>(\n items: Array<TimePickerExtendedItem<T>>,\n format: TimeFormat,\n): Array<TimePickerExtendedItem<T>> => {\n const usedValues = new Set<string>();\n\n return items.filter((item) => {\n if (!isTimeItem(item)) {\n return true;\n }\n\n const normalizedValue = normalizeTimeValue(getTimeItemValue(item), format);\n\n if (usedValues.has(normalizedValue)) {\n return false;\n }\n\n usedValues.add(normalizedValue);\n\n return true;\n });\n};\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/**\n * Преобразует display-значение в partial raw value для `onValueChange`.\n * Заполненные сегменты дополняются до двух цифр, пропущенные сегменты до последнего заполненного становятся `00`.\n */\nexport const serializeTimeValue = (value: string, format: TimeFormat): string => {\n const serializedSegments = getDisplaySegments(value, format).map((segmentValue) => {\n const digits = getDigits(segmentValue);\n\n return digits ? digits.padStart(EMPTY_SEGMENT.length, '0') : EMPTY_VALUE;\n });\n\n const lastFilledIndex = serializedSegments.reduce(\n (result, segmentValue, index) => (segmentValue ? index : result),\n -1,\n );\n\n if (lastFilledIndex < 0) {\n return EMPTY_VALUE;\n }\n\n return serializedSegments\n .slice(0, lastFilledIndex + 1)\n .map((segmentValue) => segmentValue || '00')\n .join(TIME_SEPARATOR);\n};\n\n/** Возвращает display-значение с плейсхолдерами из partial или committed value. */\nexport const getTimeDisplayValue = (value: string, format: TimeFormat): string => {\n if (value === EMPTY_VALUE) {\n return EMPTY_VALUE;\n }\n\n return getDisplaySegments(value, format).join(TIME_SEPARATOR);\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\n/**\n * Преобразует display-значение в поисковый запрос для фильтрации элементов.\n * Сегменты содержат только введенные цифры, незаполненный хвост отбрасывается: `12:3_` => `12:3`, `1_:__` => `1`.\n * Позиции сегментов сохраняются, поэтому ведущие незаполненные сегменты остаются пустыми: `__:34` => `:34`.\n * Это нужно для посегментного сравнения в `filterTimeItems` и передается как есть в пользовательскую `source`.\n */\nexport const getTimeFilterQuery = (value: string, format: TimeFormat): string => {\n const segments = getDisplaySegments(value, format).map(getDigits);\n\n const lastFilledIndex = segments.reduce((result, segmentValue, index) => (segmentValue ? index : result), -1);\n\n if (lastFilledIndex < 0) {\n return EMPTY_VALUE;\n }\n\n return segments.slice(0, lastFilledIndex + 1).join(TIME_SEPARATOR);\n};\n\n/**\n * Дефолтная функция фильтрации элементов по запросу из поля ввода.\n * Сравнивает запрос посегментно: введенные цифры сегмента должны быть префиксом соответствующего сегмента элемента.\n * Пустой запрос возвращает все элементы.\n */\nexport const filterTimeItems = <T extends TimeItemValue>(\n items: Array<TimePickerExtendedItem<T>>,\n query: string,\n format: TimeFormat,\n): Array<TimePickerExtendedItem<T>> => {\n const querySegments = query.split(TIME_SEPARATOR).map(getDigits);\n\n if (querySegments.every((segmentValue) => !segmentValue)) {\n return items;\n }\n\n return items.filter((item) => {\n if (!isTimeItem(item)) {\n return true;\n }\n\n const itemSegments = getDisplaySegments(normalizeTimeValue(getTimeItemValue(item), format), format);\n\n return querySegments.every(\n (segmentValue, index) => !segmentValue || (itemSegments[index] ?? EMPTY_VALUE).startsWith(segmentValue),\n );\n });\n};\n"]}
|
|
1
|
+
{"version":3,"file":"TimePicker.value.js","sourceRoot":"","sources":["TimePicker.value.ts"],"names":[],"mappings":"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","sourcesContent":["import warning from 'warning';\n\nimport {\n DIGIT_REGEXP,\n EMPTY_SEGMENT,\n EMPTY_VALUE,\n NON_DIGIT_REGEXP,\n TIME_LIKE_DIGITS_REGEXP,\n TIME_LIKE_SEGMENTS_REGEXP,\n TIME_SEPARATOR,\n ZERO_PAD_CHAR,\n} from './TimePicker.constants.js';\nimport {\n getDigits,\n getDisplaySegments,\n getTimeSegments,\n normalizeTimeSegment,\n sanitizeSegment,\n getTimeItemLabel,\n getTimeItemValue,\n isTimeItemDisabled,\n type TimeFormat,\n type TimeItemValue,\n type TimePickerExtendedItem,\n type TimePickerMenuItem,\n type TimeSegment,\n isTimeItem,\n isTimeItemObject,\n} from './TimePicker.shared.js';\n\n/** Проверяет, содержит ли display-значение хотя бы одну введенную цифру. */\nexport const isTimeDisplayEmpty = (value: string): boolean => !DIGIT_REGEXP.test(value);\n\n/** Приводит значение поля — элемент, строку или пустое значение — к строке времени. */\nexport const getTimeValueString = (value: TimeItemValue | null | undefined): string => {\n if (value === null || value === undefined) {\n return EMPTY_VALUE;\n }\n\n return getTimeItemValue(value);\n};\n\n/**\n * Разбирает элементы источника для отрисовки: достает время и подпись,\n * а `disabled` дополняет проверкой диапазона `minTime`/`maxTime`.\n * Исходный элемент сохраняется как есть — именно он уходит в `onValueChange`.\n * React-элементы остаются на своих местах.\n */\nexport const resolveTimeItems = <T extends TimeItemValue>(\n items: Array<TimePickerExtendedItem<T>>,\n format: TimeFormat,\n minTime?: string,\n maxTime?: string,\n): Array<TimePickerMenuItem<T>> =>\n items.map((item) => {\n if (!isTimeItem(item)) {\n return item;\n }\n\n const value = getTimeItemValue(item);\n\n return {\n item,\n value,\n label: getTimeItemLabel(item),\n disabled: isTimeItemDisabled(item) || isTimeValueOutOfRange(value, format, minTime, maxTime),\n };\n });\n\n/**\n * Оставляет только элементы, которые компонент умеет показать в меню.\n * Пустые значения — результат условного рендера вроде `isVisible && item` или `items.length && item` —\n * молча отбрасываются, а элементы, из которых не получается собрать время, отбрасываются с предупреждением.\n */\nexport const sanitizeTimeItems = <T extends TimeItemValue>(\n items: Array<TimePickerExtendedItem<T>>,\n format: TimeFormat,\n): Array<TimePickerExtendedItem<T>> => {\n if (!Array.isArray(items)) {\n warning(false, '[TimePicker]: the source function is expected to return an array or a Promise of an array.');\n\n return [];\n }\n\n return items.filter((item) => {\n if (!item) {\n return false;\n }\n\n if (!isTimeItem(item)) {\n return true;\n }\n\n if (isTimeItemObject(item) && typeof item.value !== 'string') {\n warning(false, '[TimePicker]: a source item is expected to be a time string or an object with a time value.');\n\n return false;\n }\n\n if (normalizeTimeValue(getTimeItemValue(item), format) === EMPTY_VALUE) {\n warning(false, `[TimePicker]: the source item value \"${getTimeItemValue(item)}\" is not a time.`);\n\n return false;\n }\n\n return true;\n });\n};\n\n/**\n * Оставляет в списке только первый элемент для каждого времени.\n * Времена сравниваются после нормализации к текущему формату,\n * поэтому в формате `HH:mm` элементы `12:00` и `12:00:30` считаются одинаковыми.\n * React-элементы не участвуют в сравнении и остаются на своих местах.\n */\nexport const dedupeTimeItems = <T extends TimeItemValue>(\n items: Array<TimePickerExtendedItem<T>>,\n format: TimeFormat,\n): Array<TimePickerExtendedItem<T>> => {\n const usedValues = new Set<string>();\n\n return items.filter((item) => {\n if (!isTimeItem(item)) {\n return true;\n }\n\n const normalizedValue = normalizeTimeValue(getTimeItemValue(item), format);\n\n if (usedValues.has(normalizedValue)) {\n return false;\n }\n\n usedValues.add(normalizedValue);\n\n return true;\n });\n};\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 const displaySegments = getDisplaySegments(value, format);\n\n return getTimeSegments(format)\n .map((segment, index) => normalizeTimeSegment(displaySegments[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/**\n * Преобразует display-значение в partial raw value для `onValueChange`.\n * Заполненные сегменты дополняются до двух цифр, пропущенные сегменты до последнего заполненного становятся `00`.\n */\nexport const serializeTimeValue = (value: string, format: TimeFormat): string => {\n const serializedSegments = getDisplaySegments(value, format).map((segmentValue) => {\n const digits = getDigits(segmentValue);\n\n return digits ? digits.padStart(EMPTY_SEGMENT.length, '0') : EMPTY_VALUE;\n });\n\n const lastFilledIndex = serializedSegments.reduce(\n (result, segmentValue, index) => (segmentValue ? index : result),\n -1,\n );\n\n if (lastFilledIndex < 0) {\n return EMPTY_VALUE;\n }\n\n return serializedSegments\n .slice(0, lastFilledIndex + 1)\n .map((segmentValue) => segmentValue || '00')\n .join(TIME_SEPARATOR);\n};\n\n/** Возвращает display-значение с плейсхолдерами из partial или committed value. */\nexport const getTimeDisplayValue = (value: string, format: TimeFormat): string => {\n if (value === EMPTY_VALUE) {\n return EMPTY_VALUE;\n }\n\n return getDisplaySegments(value, format).join(TIME_SEPARATOR);\n};\n\n/**\n * Возвращает display-значение внешнего `value`.\n * Сегменты, записанные одной цифрой, дополняются нулем, поэтому `9:00` показывается как `09:00` —\n * так же, как компонент показывает время элементов источника.\n * Значение без разделителя остается частично введенным: `1` — это первая цифра часов, а не `01:00`.\n */\nexport const getExternalTimeDisplayValue = (value: string, format: TimeFormat): string => {\n if (!value.includes(TIME_SEPARATOR)) {\n return getTimeDisplayValue(value, format);\n }\n\n const paddedSegments = value.split(TIME_SEPARATOR).map((segmentValue) => {\n const digits = getDigits(segmentValue);\n\n return digits ? digits.padStart(EMPTY_SEGMENT.length, ZERO_PAD_CHAR) : EMPTY_VALUE;\n });\n\n return getTimeDisplayValue(paddedSegments.join(TIME_SEPARATOR), format);\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 * Проверяет, что вставленное значение похоже на время:\n * это либо сегменты из одной-двух цифр через любой разделитель — `12:30`, `9:5`, `1:23:45`, `12.30`, —\n * либо цифры подряд — `9`, `1230`, `123045`.\n * Строки, из которых время собралось бы случайно — `2026-08-01`, `hello 42`, — временем не считаются.\n */\nexport const isTimeLikePastedValue = (value: string): boolean => {\n const trimmedValue = value.trim();\n\n return TIME_LIKE_SEGMENTS_REGEXP.test(trimmedValue) || TIME_LIKE_DIGITS_REGEXP.test(trimmedValue);\n};\n\n/**\n * Парсит вставленное пользователем значение и сразу преобразует его в committed-форму.\n * Значение, не похожее на время, отдается как пустое: такую вставку компонент считает некорректным вводом.\n */\nexport const parsePastedTimeValue = (value: string, format: TimeFormat): string => {\n const trimmedValue = value.trim();\n\n if (!DIGIT_REGEXP.test(trimmedValue) || !isTimeLikePastedValue(trimmedValue)) {\n return EMPTY_VALUE;\n }\n\n // Разделители сегментов приводятся к одному виду, чтобы `9.5` разбирался так же, как `9:5`.\n const normalizedSeparators = TIME_LIKE_SEGMENTS_REGEXP.test(trimmedValue)\n ? trimmedValue.split(NON_DIGIT_REGEXP).join(TIME_SEPARATOR)\n : trimmedValue;\n\n const segments = getDisplaySegments(normalizedSeparators, 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\n/**\n * Преобразует display-значение в поисковый запрос для фильтрации элементов.\n * Сегменты содержат только введенные цифры, незаполненный хвост отбрасывается: `12:3_` => `12:3`, `1_:__` => `1`.\n * Позиции сегментов сохраняются, поэтому ведущие незаполненные сегменты остаются пустыми: `__:34` => `:34`.\n * Это нужно для посегментного сравнения в `filterTimeItems` и передается как есть в пользовательскую `source`.\n */\nexport const getTimeFilterQuery = (value: string, format: TimeFormat): string => {\n const segments = getDisplaySegments(value, format).map(getDigits);\n\n const lastFilledIndex = segments.reduce((result, segmentValue, index) => (segmentValue ? index : result), -1);\n\n if (lastFilledIndex < 0) {\n return EMPTY_VALUE;\n }\n\n return segments.slice(0, lastFilledIndex + 1).join(TIME_SEPARATOR);\n};\n\n/**\n * Дефолтная функция фильтрации элементов по запросу из поля ввода.\n * Сравнивает запрос посегментно: введенные цифры сегмента должны быть префиксом соответствующего сегмента элемента.\n * Пустой запрос возвращает все элементы.\n */\nexport const filterTimeItems = <T extends TimeItemValue>(\n items: Array<TimePickerExtendedItem<T>>,\n query: string,\n format: TimeFormat,\n): Array<TimePickerExtendedItem<T>> => {\n const querySegments = query.split(TIME_SEPARATOR).map(getDigits);\n\n if (querySegments.every((segmentValue) => !segmentValue)) {\n return items;\n }\n\n return items.filter((item) => {\n if (!isTimeItem(item)) {\n return true;\n }\n\n const itemSegments = getDisplaySegments(normalizeTimeValue(getTimeItemValue(item), format), format);\n\n return querySegments.every(\n (segmentValue, index) => !segmentValue || (itemSegments[index] ?? EMPTY_VALUE).startsWith(segmentValue),\n );\n });\n};\n"]}
|
|
@@ -5,9 +5,14 @@ interface UseTimePickerDropdownOptions<T extends TimeItemValue> {
|
|
|
5
5
|
items: Array<TimePickerMenuItem<T>>;
|
|
6
6
|
selectedItemIndex: number | null;
|
|
7
7
|
autoHighlightKey: string | null;
|
|
8
|
+
/**
|
|
9
|
+
* Состоянием открытости владеет компонент: от него зависит запрос элементов,
|
|
10
|
+
* а элементы, в свою очередь, нужны этому хуку для навигации.
|
|
11
|
+
*/
|
|
12
|
+
isDropdownOpened: boolean;
|
|
13
|
+
setIsDropdownOpened(isOpened: boolean): void;
|
|
8
14
|
}
|
|
9
15
|
interface UseTimePickerDropdownResult {
|
|
10
|
-
isDropdownOpened: boolean;
|
|
11
16
|
highlightedItemIndex: number | null;
|
|
12
17
|
openDropdown(): void;
|
|
13
18
|
closeDropdown(): void;
|
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
|
-
import { isTimeMenuItem } from '../helpers/TimePicker.shared.js';
|
|
2
|
+
import { isHighlightableMenuItem, isTimeMenuItem, } from '../helpers/TimePicker.shared.js';
|
|
3
3
|
export var useTimePickerDropdown = function (options) {
|
|
4
|
-
var hasDropdown = options.hasDropdown, disabled = options.disabled, items = options.items, selectedItemIndex = options.selectedItemIndex, autoHighlightKey = options.autoHighlightKey;
|
|
5
|
-
var _a = useState(
|
|
6
|
-
var _b = useState(null), highlightedItemIndex = _b[0], setHighlightedItemIndex = _b[1];
|
|
4
|
+
var hasDropdown = options.hasDropdown, disabled = options.disabled, items = options.items, selectedItemIndex = options.selectedItemIndex, autoHighlightKey = options.autoHighlightKey, isDropdownOpened = options.isDropdownOpened, setIsDropdownOpened = options.setIsDropdownOpened;
|
|
5
|
+
var _a = useState(null), highlightedItemIndex = _a[0], setHighlightedItemIndex = _a[1];
|
|
7
6
|
var lastAutoHighlightKeyRef = useRef(null);
|
|
8
7
|
var openDropdown = useCallback(function () {
|
|
9
8
|
if (!hasDropdown || disabled) {
|
|
10
9
|
return;
|
|
11
10
|
}
|
|
12
11
|
setIsDropdownOpened(true);
|
|
13
|
-
}, [disabled, hasDropdown]);
|
|
12
|
+
}, [disabled, hasDropdown, setIsDropdownOpened]);
|
|
14
13
|
var closeDropdown = useCallback(function () {
|
|
15
14
|
setIsDropdownOpened(false);
|
|
16
15
|
setHighlightedItemIndex(null);
|
|
17
|
-
}, []);
|
|
16
|
+
}, [setIsDropdownOpened]);
|
|
18
17
|
var resetHighlightedItem = useCallback(function () {
|
|
19
18
|
setHighlightedItemIndex(null);
|
|
20
19
|
}, []);
|
|
@@ -36,7 +35,7 @@ export var useTimePickerDropdown = function (options) {
|
|
|
36
35
|
return current;
|
|
37
36
|
}
|
|
38
37
|
var item = items[current];
|
|
39
|
-
return !item || !
|
|
38
|
+
return !item || !isHighlightableMenuItem(item) ? null : current;
|
|
40
39
|
});
|
|
41
40
|
}, [items]);
|
|
42
41
|
var tryNavigateItems = useCallback(function (step) {
|
|
@@ -47,7 +46,7 @@ export var useTimePickerDropdown = function (options) {
|
|
|
47
46
|
.map(function (item, index) { return ({ item: item, index: index }); })
|
|
48
47
|
.filter(function (_a) {
|
|
49
48
|
var item = _a.item;
|
|
50
|
-
return
|
|
49
|
+
return isHighlightableMenuItem(item);
|
|
51
50
|
})
|
|
52
51
|
.map(function (_a) {
|
|
53
52
|
var index = _a.index;
|
|
@@ -74,7 +73,6 @@ export var useTimePickerDropdown = function (options) {
|
|
|
74
73
|
return true;
|
|
75
74
|
}, [hasDropdown, highlightedItemIndex, isDropdownOpened, selectedItemIndex, items]);
|
|
76
75
|
return {
|
|
77
|
-
isDropdownOpened: isDropdownOpened,
|
|
78
76
|
highlightedItemIndex: highlightedItemIndex,
|
|
79
77
|
openDropdown: openDropdown,
|
|
80
78
|
closeDropdown: closeDropdown,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useTimePickerDropdown.js","sourceRoot":"","sources":["useTimePickerDropdown.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAEjE,OAAO,
|
|
1
|
+
{"version":3,"file":"useTimePickerDropdown.js","sourceRoot":"","sources":["useTimePickerDropdown.ts"],"names":[],"mappings":"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","sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\n\nimport {\n isHighlightableMenuItem,\n isTimeMenuItem,\n type TimeItemValue,\n type TimePickerMenuItem,\n} from '../helpers/TimePicker.shared.js';\n\ninterface UseTimePickerDropdownOptions<T extends TimeItemValue> {\n hasDropdown: boolean;\n disabled?: boolean;\n items: Array<TimePickerMenuItem<T>>;\n selectedItemIndex: number | null;\n autoHighlightKey: string | null;\n /**\n * Состоянием открытости владеет компонент: от него зависит запрос элементов,\n * а элементы, в свою очередь, нужны этому хуку для навигации.\n */\n isDropdownOpened: boolean;\n setIsDropdownOpened(isOpened: boolean): void;\n}\n\ninterface UseTimePickerDropdownResult {\n highlightedItemIndex: number | null;\n openDropdown(): void;\n closeDropdown(): void;\n resetHighlightedItem(): void;\n tryNavigateItems(step: 1 | -1): boolean;\n}\n\nexport const useTimePickerDropdown = <T extends TimeItemValue>(\n options: UseTimePickerDropdownOptions<T>,\n): UseTimePickerDropdownResult => {\n const { hasDropdown, disabled, items, selectedItemIndex, autoHighlightKey, isDropdownOpened, setIsDropdownOpened } =\n options;\n\n const [highlightedItemIndex, setHighlightedItemIndex] = useState<number | null>(null);\n const lastAutoHighlightKeyRef = useRef<string | null>(null);\n\n const openDropdown = useCallback(() => {\n if (!hasDropdown || disabled) {\n return;\n }\n\n setIsDropdownOpened(true);\n }, [disabled, hasDropdown, setIsDropdownOpened]);\n\n const closeDropdown = useCallback(() => {\n setIsDropdownOpened(false);\n setHighlightedItemIndex(null);\n }, [setIsDropdownOpened]);\n\n const resetHighlightedItem = useCallback(() => {\n setHighlightedItemIndex(null);\n }, []);\n\n useEffect(() => {\n if (lastAutoHighlightKeyRef.current === autoHighlightKey) {\n return;\n }\n\n lastAutoHighlightKeyRef.current = autoHighlightKey;\n\n if (autoHighlightKey === null) {\n setHighlightedItemIndex(null);\n return;\n }\n\n const firstEnabledItemIndex = items.findIndex((item) => isTimeMenuItem(item) && !item.disabled);\n setHighlightedItemIndex(firstEnabledItemIndex >= 0 ? firstEnabledItemIndex : null);\n }, [autoHighlightKey, items]);\n\n useEffect(() => {\n setHighlightedItemIndex((current) => {\n if (current === null) {\n return current;\n }\n\n const item = items[current];\n\n return !item || !isHighlightableMenuItem(item) ? null : current;\n });\n }, [items]);\n\n const tryNavigateItems = useCallback(\n (step: 1 | -1) => {\n if (!hasDropdown || !isDropdownOpened) {\n return false;\n }\n\n const enabledIndexes = items\n .map((item, index) => ({ item, index }))\n .filter(({ item }) => isHighlightableMenuItem(item))\n .map(({ index }) => index);\n\n if (enabledIndexes.length === 0) {\n return false;\n }\n\n if (highlightedItemIndex === null) {\n if (selectedItemIndex !== null) {\n const selectedEnabledIndex = enabledIndexes.indexOf(selectedItemIndex);\n\n if (selectedEnabledIndex >= 0) {\n const nextEnabledIndex = enabledIndexes[selectedEnabledIndex + step];\n\n setHighlightedItemIndex(\n nextEnabledIndex ?? (step > 0 ? enabledIndexes[0] : enabledIndexes[enabledIndexes.length - 1]),\n );\n return true;\n }\n }\n\n setHighlightedItemIndex(step > 0 ? enabledIndexes[0] : enabledIndexes[enabledIndexes.length - 1]);\n return true;\n }\n\n const currentIndex = enabledIndexes.indexOf(highlightedItemIndex);\n const nextEnabledIndex = enabledIndexes[currentIndex + step];\n\n setHighlightedItemIndex(\n nextEnabledIndex ?? (step > 0 ? enabledIndexes[0] : enabledIndexes[enabledIndexes.length - 1]),\n );\n\n return true;\n },\n [hasDropdown, highlightedItemIndex, isDropdownOpened, selectedItemIndex, items],\n );\n\n return {\n highlightedItemIndex,\n openDropdown,\n closeDropdown,\n resetHighlightedItem,\n tryNavigateItems,\n };\n};\n"]}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { TimeFormat, TimeItemValue, TimePickerExtendedItem, TimePickerSource } from '../helpers/TimePicker.shared.js';
|
|
2
|
+
interface UseTimePickerSourceOptions<T extends TimeItemValue> {
|
|
3
|
+
source?: TimePickerSource<T>;
|
|
4
|
+
format: TimeFormat;
|
|
5
|
+
isDropdownOpened: boolean;
|
|
6
|
+
/** Текущий запрос: введенные цифры сегментов с сохранением их позиций. */
|
|
7
|
+
filterQuery: string;
|
|
8
|
+
}
|
|
9
|
+
interface UseTimePickerSourceResult<T extends TimeItemValue> {
|
|
10
|
+
/** Элементы источника, готовые к фильтрации и отрисовке. */
|
|
11
|
+
items: Array<TimePickerExtendedItem<T>>;
|
|
12
|
+
/** Запрос, для которого получены текущие элементы функции-источника. */
|
|
13
|
+
fetchedQuery: string | null;
|
|
14
|
+
isLoading: boolean;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Отдает элементы выпадающего списка из массива или функции-источника.
|
|
18
|
+
*
|
|
19
|
+
* Функция вызывается только для открытого меню и на каждый новый запрос;
|
|
20
|
+
* ответ на устаревший запрос отбрасывается, а отказ трактуется как пустой результат.
|
|
21
|
+
* При закрытии меню результаты функции сбрасываются, чтобы следующее открытие
|
|
22
|
+
* не показывало элементы для старого запроса.
|
|
23
|
+
* Замена самой функции уже открытый список не перезапрашивает: актуальная функция
|
|
24
|
+
* используется при следующем запросе.
|
|
25
|
+
*/
|
|
26
|
+
export declare const useTimePickerSource: <T extends TimeItemValue>(options: UseTimePickerSourceOptions<T>) => UseTimePickerSourceResult<T>;
|
|
27
|
+
export {};
|