@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
|
@@ -37,26 +37,30 @@ import { DIGIT_REGEXP, EMPTY_SEGMENT, EMPTY_VALUE, TimePickerDataTids } from './
|
|
|
37
37
|
import { commitTimeSegmentOnLeave, deleteTimeSegmentDigit, formatDigitToTimeSegment, shiftTimeSegmentValue, } from './helpers/TimePicker.editing.js';
|
|
38
38
|
import { getTimePickerPopupMaxHeight } from './helpers/TimePicker.layout.js';
|
|
39
39
|
import { getNextTimeSegment, getPreviousTimeSegment } from './helpers/TimePicker.selection.js';
|
|
40
|
-
import { isTimeItem, isTimeItemObject, isTimeMenuItem, timeValueToItem, } from './helpers/TimePicker.shared.js';
|
|
40
|
+
import { isNavigableMenuElement, isTimeItem, isTimeItemObject, isTimeMenuItem, timeValueToItem, } from './helpers/TimePicker.shared.js';
|
|
41
41
|
import { dedupeTimeItems, filterTimeItems, getEmptyDisplayValue, getTimeFilterQuery, getTimeValueString, isTimeDisplayEmpty, normalizeTimeValue, parsePastedTimeValue, replaceTimeSegment, resolveTimeItems, } from './helpers/TimePicker.value.js';
|
|
42
42
|
import { validateTimePicker } from './helpers/validateTimePicker.js';
|
|
43
43
|
import { useTimePickerDropdown } from './hooks/useTimePickerDropdown.js';
|
|
44
44
|
import { useTimePickerSelection } from './hooks/useTimePickerSelection.js';
|
|
45
|
+
import { useTimePickerSource } from './hooks/useTimePickerSource.js';
|
|
45
46
|
import { useTimePickerValue } from './hooks/useTimePickerValue.js';
|
|
46
47
|
import { TimePickerLocaleHelper } from './locale/index.js';
|
|
47
48
|
import { getStyles } from './TimePicker.styles.js';
|
|
48
49
|
import { TimePickerMobilePopup } from './TimePickerMobilePopup.js';
|
|
49
50
|
import { TimePickerPopup } from './TimePickerPopup.js';
|
|
51
|
+
/** Пустой список для клавиатуры: не создает новую ссылку на каждый рендер. */
|
|
52
|
+
var EMPTY_MENU_ITEMS = [];
|
|
50
53
|
/** Поле с временем помогает пользователю быстро и удобно указать время в правильном формате.
|
|
51
54
|
* В поле можно ввести время с клавиатуры либо выбрать из выпадающего списка,
|
|
52
55
|
* передав массив или функцию в проп `source`.
|
|
53
56
|
*/
|
|
54
57
|
export var TimePicker = Object.assign(forwardRefAndName('TimePicker', function (props, ref) {
|
|
55
|
-
var
|
|
58
|
+
var _a;
|
|
59
|
+
var disabled = props.disabled, _b = props.useMobileNativeTimePicker, useMobileNativeTimePicker = _b === void 0 ? false : _b, _c = props.format, format = _c === void 0 ? 'HH:mm' : _c, _d = props.size, size = _d === void 0 ? 'small' : _d, source = props.source, renderItem = props.renderItem, menuPos = props.menuPos, menuAlign = props.menuAlign, menuWidth = props.menuWidth, minTime = props.minTime, maxTime = props.maxTime, rightIcon = props.rightIcon, suffix = props.suffix, corners = props.corners, value = props.value, valueToItem = props.valueToItem, onValueChange = props.onValueChange, onInputValueChange = props.onInputValueChange, onUnexpectedInput = props.onUnexpectedInput, onFocus = props.onFocus, onBlur = props.onBlur, onClick = props.onClick, onKeyDown = props.onKeyDown, onPaste = props.onPaste, inputProps = __rest(props, ["disabled", "useMobileNativeTimePicker", "format", "size", "source", "renderItem", "menuPos", "menuAlign", "menuWidth", "minTime", "maxTime", "rightIcon", "suffix", "corners", "value", "valueToItem", "onValueChange", "onInputValueChange", "onUnexpectedInput", "onFocus", "onBlur", "onClick", "onKeyDown", "onPaste"]);
|
|
56
60
|
var theme = useContext(ThemeContext);
|
|
57
61
|
var styles = useStyles(getStyles);
|
|
58
62
|
var locale = useLocaleForControl('TimePicker', TimePickerLocaleHelper);
|
|
59
|
-
var
|
|
63
|
+
var _e = useState(false), isInputFocused = _e[0], setIsInputFocused = _e[1];
|
|
60
64
|
var inputRef = useRef(null);
|
|
61
65
|
var mobileInputRef = useRef(null);
|
|
62
66
|
var nativeInputRef = useRef(null);
|
|
@@ -66,28 +70,30 @@ export var TimePicker = Object.assign(forwardRefAndName('TimePicker', function (
|
|
|
66
70
|
var isMouseDownOnFragmentRef = useRef(false);
|
|
67
71
|
var mouseDownSegmentRef = useRef(null);
|
|
68
72
|
var mobileBlurEventRef = useRef(null);
|
|
73
|
+
var shouldSkipOpenOnFocusRef = useRef(false);
|
|
69
74
|
var popupIdRef = useRef(TimePickerDataTids.popup + getRandomID());
|
|
70
|
-
var
|
|
71
|
-
var
|
|
75
|
+
var _f = useState(false), isMobileDevice = _f[0], setIsMobileDevice = _f[1];
|
|
76
|
+
var _g = useState(false), canUseMobileNativeTimePicker = _g[0], setCanUseMobileNativeTimePicker = _g[1];
|
|
72
77
|
var isSourceFunction = typeof source === 'function';
|
|
73
|
-
var
|
|
74
|
-
var
|
|
75
|
-
var
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
78
|
+
var hasDropdown = isSourceFunction || (Array.isArray(source) && source.length > 0);
|
|
79
|
+
var _h = useState(false), isDropdownOpened = _h[0], setIsDropdownOpened = _h[1];
|
|
80
|
+
var _j = useState(false), isEditedAfterOpen = _j[0], setIsEditedAfterOpen = _j[1];
|
|
81
|
+
/**
|
|
82
|
+
* Форма элементов функции-источника: последняя, о которой компонент знает.
|
|
83
|
+
* Форма запоминается, потому что элементы функции очищаются при закрытии меню,
|
|
84
|
+
* а коммит значения после закрытия должен отдать наружу элемент той же формы.
|
|
85
|
+
* Для массива форма всегда считается по текущим элементам — запоминать нечего.
|
|
86
|
+
*/
|
|
87
|
+
var lastSourceItemShapeRef = useRef(null);
|
|
80
88
|
/**
|
|
81
89
|
* Форма элемента, который компонент собирает для времени без элемента.
|
|
82
90
|
* Повторяет форму элементов источника, а если их нет — форму внешнего `value`,
|
|
83
91
|
* чтобы наружу уходило то же, что пришло внутрь.
|
|
92
|
+
* Значение читается при коммите, поэтому обновляется в эффекте: так его не меняет отброшенный рендер.
|
|
84
93
|
*/
|
|
85
|
-
var
|
|
86
|
-
(value !== null && value !== undefined && isTimeItemObject(value));
|
|
94
|
+
var shouldBuildItemAsObjectRef = useRef(false);
|
|
87
95
|
var valueToItemRef = useRef(valueToItem);
|
|
88
96
|
valueToItemRef.current = valueToItem;
|
|
89
|
-
var shouldBuildItemAsObjectRef = useRef(shouldBuildItemAsObject);
|
|
90
|
-
shouldBuildItemAsObjectRef.current = shouldBuildItemAsObject;
|
|
91
97
|
var handleCommitValue = useCallback(function (committedValue, item) {
|
|
92
98
|
var _a, _b;
|
|
93
99
|
if (!onValueChange) {
|
|
@@ -100,14 +106,23 @@ export var TimePicker = Object.assign(forwardRefAndName('TimePicker', function (
|
|
|
100
106
|
var nextItem = (_b = item !== null && item !== void 0 ? item : (_a = valueToItemRef.current) === null || _a === void 0 ? void 0 : _a.call(valueToItemRef, committedValue)) !== null && _b !== void 0 ? _b : timeValueToItem(committedValue, shouldBuildItemAsObjectRef.current);
|
|
101
107
|
onValueChange(nextItem, committedValue);
|
|
102
108
|
}, [onValueChange]);
|
|
103
|
-
var
|
|
109
|
+
var _k = useTimePickerValue({
|
|
104
110
|
isInputFocused: isInputFocused,
|
|
105
111
|
format: format,
|
|
106
112
|
externalValue: getTimeValueString(value),
|
|
113
|
+
isValueControlled: value !== undefined,
|
|
107
114
|
onCommitValue: handleCommitValue,
|
|
108
115
|
onInputValueChange: onInputValueChange,
|
|
109
|
-
}), editingValue =
|
|
116
|
+
}), editingValue = _k.editingValue, editingValueRef = _k.editingValueRef, updateEditingValue = _k.updateEditingValue, commitEditingValue = _k.commitEditingValue, commitValue = _k.commitValue, commitItem = _k.commitItem;
|
|
110
117
|
var filterQuery = isEditedAfterOpen ? getTimeFilterQuery(editingValue, format) : EMPTY_VALUE;
|
|
118
|
+
var _l = useTimePickerSource({ source: source, format: format, isDropdownOpened: isDropdownOpened, filterQuery: filterQuery }), sourceValueItems = _l.items, fetchedItemsQuery = _l.fetchedQuery, isLoadingItems = _l.isLoading;
|
|
119
|
+
useEffect(function () {
|
|
120
|
+
if (!isSourceFunction || sourceValueItems.some(isTimeItem)) {
|
|
121
|
+
lastSourceItemShapeRef.current = sourceValueItems.some(function (item) { return isTimeItem(item) && isTimeItemObject(item); });
|
|
122
|
+
}
|
|
123
|
+
shouldBuildItemAsObjectRef.current =
|
|
124
|
+
lastSourceItemShapeRef.current === true || (value !== null && value !== undefined && isTimeItemObject(value));
|
|
125
|
+
}, [isSourceFunction, sourceValueItems, value]);
|
|
111
126
|
var filteredItems = useMemo(function () { return filterTimeItems(dedupeTimeItems(sourceValueItems, format), filterQuery, format); }, [filterQuery, format, sourceValueItems]);
|
|
112
127
|
/**
|
|
113
128
|
* Элементы для отрисовки: время и подпись достаются из элемента,
|
|
@@ -131,7 +146,17 @@ export var TimePicker = Object.assign(forwardRefAndName('TimePicker', function (
|
|
|
131
146
|
}
|
|
132
147
|
}
|
|
133
148
|
var canUseMobileDropdownTimePicker = hasDropdown && isMobileDevice && !canUseMobileNativeTimePicker;
|
|
134
|
-
|
|
149
|
+
/**
|
|
150
|
+
* Меню с одними лишь заголовками и разделителями показывать не о чем,
|
|
151
|
+
* поэтому при активном фильтре нужен хотя бы один подошедший элемент со временем.
|
|
152
|
+
*/
|
|
153
|
+
var hasDropdownContent = isLoadingItems || (filterQuery === EMPTY_VALUE ? resolvedItems.length > 0 : resolvedItems.some(isTimeMenuItem));
|
|
154
|
+
/**
|
|
155
|
+
* Элементы, с которыми работает клавиатура.
|
|
156
|
+
* Пока меню скрыто, их нет: иначе стрелки ходили бы по невидимому списку,
|
|
157
|
+
* а Enter выбирал бы невидимый элемент вместо коммита введенного времени.
|
|
158
|
+
*/
|
|
159
|
+
var menuItems = hasDropdownContent ? resolvedItems : EMPTY_MENU_ITEMS;
|
|
135
160
|
var inputRightIcon = isLoadingItems && resolvedItems.length > 0 ? (React.createElement("span", { "data-tid": TimePickerDataTids.inputLoading },
|
|
136
161
|
React.createElement(LoadingIcon, { size: size }))) : (rightIcon);
|
|
137
162
|
var displayValue = editingValue;
|
|
@@ -147,11 +172,11 @@ export var TimePicker = Object.assign(forwardRefAndName('TimePicker', function (
|
|
|
147
172
|
mouseDownSegmentRef.current = null;
|
|
148
173
|
}, []);
|
|
149
174
|
var updateEditingValueAfterUserEdit = useCallback(function (nextEditingValue) {
|
|
150
|
-
if (editingValueRef.current !== nextEditingValue) {
|
|
175
|
+
if (hasDropdown && editingValueRef.current !== nextEditingValue) {
|
|
151
176
|
setIsEditedAfterOpen(true);
|
|
152
177
|
}
|
|
153
178
|
updateEditingValue(nextEditingValue);
|
|
154
|
-
}, [editingValueRef, updateEditingValue]);
|
|
179
|
+
}, [editingValueRef, hasDropdown, updateEditingValue]);
|
|
155
180
|
var normalizeCurrentSegmentIfNeeded = useCallback(function (segment) {
|
|
156
181
|
var nextDisplayValue = commitTimeSegmentOnLeave(editingValue === EMPTY_VALUE ? getEmptyDisplayValue(format) : editingValue, segment, format);
|
|
157
182
|
updateEditingValueAfterUserEdit(nextDisplayValue);
|
|
@@ -171,46 +196,28 @@ export var TimePicker = Object.assign(forwardRefAndName('TimePicker', function (
|
|
|
171
196
|
var _m = useTimePickerDropdown({
|
|
172
197
|
disabled: disabled,
|
|
173
198
|
hasDropdown: hasDropdown,
|
|
174
|
-
items:
|
|
199
|
+
items: menuItems,
|
|
175
200
|
selectedItemIndex: selectedItemIndex >= 0 ? selectedItemIndex : null,
|
|
176
201
|
autoHighlightKey: autoHighlightKey,
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
}
|
|
186
|
-
var cancelled = false;
|
|
187
|
-
setIsLoadingItems(true);
|
|
188
|
-
new Promise(function (resolve) { return resolve(currentSource(filterQuery)); }).then(function (nextItems) {
|
|
189
|
-
if (!cancelled) {
|
|
190
|
-
setFetchedItems(nextItems);
|
|
191
|
-
setFetchedItemsQuery(filterQuery);
|
|
192
|
-
setIsLoadingItems(false);
|
|
193
|
-
}
|
|
194
|
-
}, function () {
|
|
195
|
-
if (!cancelled) {
|
|
196
|
-
setFetchedItems([]);
|
|
197
|
-
setFetchedItemsQuery(filterQuery);
|
|
198
|
-
setIsLoadingItems(false);
|
|
199
|
-
}
|
|
200
|
-
});
|
|
201
|
-
return function () {
|
|
202
|
-
cancelled = true;
|
|
203
|
-
};
|
|
204
|
-
}, [filterQuery, isDropdownOpened, isSourceFunction]);
|
|
202
|
+
isDropdownOpened: isDropdownOpened,
|
|
203
|
+
setIsDropdownOpened: setIsDropdownOpened,
|
|
204
|
+
}), highlightedItemIndex = _m.highlightedItemIndex, openDropdown = _m.openDropdown, closeDropdown = _m.closeDropdown, resetHighlightedItem = _m.resetHighlightedItem, tryNavigateItems = _m.tryNavigateItems;
|
|
205
|
+
/**
|
|
206
|
+
* Фильтрация начинается только после правки значения при открытом меню.
|
|
207
|
+
* Состояние живет ровно столько, сколько открыто меню: правка, которая заново открывает меню,
|
|
208
|
+
* сохраняет свой запрос, а правка при закрытом меню — например стрелками — на список не влияет.
|
|
209
|
+
*/
|
|
205
210
|
useEffect(function () {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
setFetchedItems([]);
|
|
209
|
-
setFetchedItemsQuery(null);
|
|
211
|
+
if (!isDropdownOpened && isEditedAfterOpen) {
|
|
212
|
+
setIsEditedAfterOpen(false);
|
|
210
213
|
}
|
|
211
|
-
}, [isDropdownOpened,
|
|
214
|
+
}, [isDropdownOpened, isEditedAfterOpen]);
|
|
212
215
|
var popupId = popupIdRef.current;
|
|
213
|
-
var
|
|
216
|
+
var highlightedMenuItem = highlightedItemIndex === null ? undefined : menuItems[highlightedItemIndex];
|
|
217
|
+
/** Кастомный `MenuItem` может принести свой `id` — тогда указывать нужно именно на него. */
|
|
218
|
+
var activeDescendantId = hasDropdown && highlightedItemIndex !== null && highlightedMenuItem
|
|
219
|
+
? ((_a = (isNavigableMenuElement(highlightedMenuItem) ? highlightedMenuItem.props.id : undefined)) !== null && _a !== void 0 ? _a : "".concat(popupId, "-item-").concat(highlightedItemIndex))
|
|
220
|
+
: undefined;
|
|
214
221
|
var isMobileDropdownActive = canUseMobileDropdownTimePicker && isDropdownOpened;
|
|
215
222
|
var getActiveInput = useCallback(function () { return (canUseMobileDropdownTimePicker && isDropdownOpened ? mobileInputRef.current : inputRef.current); }, [canUseMobileDropdownTimePicker, isDropdownOpened]);
|
|
216
223
|
var _o = useTimePickerSelection({
|
|
@@ -226,14 +233,24 @@ export var TimePicker = Object.assign(forwardRefAndName('TimePicker', function (
|
|
|
226
233
|
/**
|
|
227
234
|
* Сообщает о некорректном вводе: нажатой клавише или вставленном значении, которое не может быть временем.
|
|
228
235
|
* Если обработчик не задан, поле вспыхивает само.
|
|
236
|
+
* Обработчик может подставить значение вместо некорректного ввода:
|
|
237
|
+
* элемент коммитится как есть, `null` очищает поле, а `undefined` оставляет значение прежним.
|
|
229
238
|
*/
|
|
230
239
|
var handleUnexpectedInput = useCallback(function (unexpectedValue) {
|
|
231
|
-
if (onUnexpectedInput) {
|
|
232
|
-
|
|
240
|
+
if (!onUnexpectedInput) {
|
|
241
|
+
blinkInputValue();
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
var returnedValue = onUnexpectedInput(unexpectedValue, blinkInputValue);
|
|
245
|
+
if (returnedValue === undefined) {
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
if (returnedValue === null) {
|
|
249
|
+
commitValue(EMPTY_VALUE);
|
|
233
250
|
return;
|
|
234
251
|
}
|
|
235
|
-
|
|
236
|
-
}, [blinkInputValue, onUnexpectedInput]);
|
|
252
|
+
commitItem(returnedValue);
|
|
253
|
+
}, [blinkInputValue, commitItem, commitValue, onUnexpectedInput]);
|
|
237
254
|
var closeOpenedDropdown = useCallback(function () {
|
|
238
255
|
var _a;
|
|
239
256
|
if (canUseMobileDropdownTimePicker && isDropdownOpened) {
|
|
@@ -267,21 +284,51 @@ export var TimePicker = Object.assign(forwardRefAndName('TimePicker', function (
|
|
|
267
284
|
closeDropdown();
|
|
268
285
|
}
|
|
269
286
|
}, [canUseMobileDropdownTimePicker, closeDropdown, closeOpenedDropdown, commitItem]);
|
|
270
|
-
var isSelectHighlightedItem = useCallback(function () {
|
|
287
|
+
var isSelectHighlightedItem = useCallback(function (event) {
|
|
288
|
+
var _a, _b;
|
|
271
289
|
if (highlightedItemIndex === null) {
|
|
272
290
|
return false;
|
|
273
291
|
}
|
|
274
|
-
var resolvedItem =
|
|
275
|
-
if (!resolvedItem
|
|
292
|
+
var resolvedItem = menuItems[highlightedItemIndex];
|
|
293
|
+
if (!resolvedItem) {
|
|
294
|
+
return true;
|
|
295
|
+
}
|
|
296
|
+
if (isNavigableMenuElement(resolvedItem)) {
|
|
297
|
+
(_b = (_a = resolvedItem.props).onClick) === null || _b === void 0 ? void 0 : _b.call(_a, event);
|
|
298
|
+
return true;
|
|
299
|
+
}
|
|
300
|
+
if (!isTimeMenuItem(resolvedItem) || resolvedItem.disabled) {
|
|
276
301
|
return true;
|
|
277
302
|
}
|
|
278
303
|
selectItem(resolvedItem.item);
|
|
279
304
|
return true;
|
|
280
|
-
}, [highlightedItemIndex,
|
|
281
|
-
var focusInput = useCallback(function () {
|
|
305
|
+
}, [highlightedItemIndex, menuItems, selectItem]);
|
|
306
|
+
var focusInput = useCallback(function (options) {
|
|
282
307
|
var _a;
|
|
308
|
+
if (disabled) {
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
shouldSkipOpenOnFocusRef.current = Boolean(options === null || options === void 0 ? void 0 : options.withoutOpenDropdown);
|
|
283
312
|
(_a = getActiveInput()) === null || _a === void 0 ? void 0 : _a.focus();
|
|
284
|
-
|
|
313
|
+
shouldSkipOpenOnFocusRef.current = false;
|
|
314
|
+
}, [disabled, getActiveInput]);
|
|
315
|
+
/**
|
|
316
|
+
* Открывает выпадающее меню так же, как это делает пользователь: через фокус в поле.
|
|
317
|
+
* Меню закрывается по потере фокуса, поэтому открытое без фокуса меню было бы нечем закрыть.
|
|
318
|
+
* В режиме нативного пикера открывается системный пикер — своего меню в этом режиме нет.
|
|
319
|
+
*/
|
|
320
|
+
var openInputDropdown = useCallback(function () {
|
|
321
|
+
var _a;
|
|
322
|
+
if (disabled) {
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
if (canUseMobileNativeTimePicker) {
|
|
326
|
+
openNativeTimePicker();
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
(_a = getActiveInput()) === null || _a === void 0 ? void 0 : _a.focus();
|
|
330
|
+
openDropdown();
|
|
331
|
+
}, [canUseMobileNativeTimePicker, disabled, getActiveInput, openDropdown, openNativeTimePicker]);
|
|
285
332
|
var blurInput = useCallback(function () {
|
|
286
333
|
var _a;
|
|
287
334
|
if (canUseMobileDropdownTimePicker && isDropdownOpened) {
|
|
@@ -301,9 +348,10 @@ export var TimePicker = Object.assign(forwardRefAndName('TimePicker', function (
|
|
|
301
348
|
focus: focusInput,
|
|
302
349
|
blur: blurInput,
|
|
303
350
|
blink: blinkInput,
|
|
351
|
+
open: openInputDropdown,
|
|
304
352
|
close: closeInputDropdown,
|
|
305
353
|
getRootNode: function () { return rootRef.current; },
|
|
306
|
-
}); }, [blinkInput, blurInput, closeInputDropdown, focusInput,
|
|
354
|
+
}); }, [blinkInput, blurInput, closeInputDropdown, focusInput, openInputDropdown]);
|
|
307
355
|
useLayoutEffect(function () {
|
|
308
356
|
if (!isDropdownOpened) {
|
|
309
357
|
return;
|
|
@@ -317,11 +365,36 @@ export var TimePicker = Object.assign(forwardRefAndName('TimePicker', function (
|
|
|
317
365
|
scrollSelectedItemIntoView(targetNode);
|
|
318
366
|
}
|
|
319
367
|
}, [highlightedItemIndex, isDropdownOpened, selectedItemIndex]);
|
|
368
|
+
/**
|
|
369
|
+
* Блокировка контрола посреди ввода завершает его так же, как потеря фокуса:
|
|
370
|
+
* меню закрывается, а недоведенное значение коммитится, а не остается висеть в поле.
|
|
371
|
+
*/
|
|
320
372
|
useEffect(function () {
|
|
321
|
-
if (disabled
|
|
373
|
+
if (!disabled) {
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
if (canUseMobileDropdownTimePicker && isDropdownOpened) {
|
|
322
377
|
closeOpenedDropdown();
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
if (isDropdownOpened) {
|
|
381
|
+
closeDropdown();
|
|
382
|
+
}
|
|
383
|
+
if (isInputFocused) {
|
|
384
|
+
setIsInputFocused(false);
|
|
385
|
+
resetMouseInteractionState();
|
|
386
|
+
commitEditingValue();
|
|
323
387
|
}
|
|
324
|
-
}, [
|
|
388
|
+
}, [
|
|
389
|
+
canUseMobileDropdownTimePicker,
|
|
390
|
+
closeDropdown,
|
|
391
|
+
closeOpenedDropdown,
|
|
392
|
+
commitEditingValue,
|
|
393
|
+
disabled,
|
|
394
|
+
isDropdownOpened,
|
|
395
|
+
isInputFocused,
|
|
396
|
+
resetMouseInteractionState,
|
|
397
|
+
]);
|
|
325
398
|
useEffect(function () {
|
|
326
399
|
setIsMobileDevice(Boolean(isMobile));
|
|
327
400
|
setCanUseMobileNativeTimePicker(useMobileNativeTimePicker && Boolean(isMobile));
|
|
@@ -330,17 +403,25 @@ export var TimePicker = Object.assign(forwardRefAndName('TimePicker', function (
|
|
|
330
403
|
closeOpenedDropdown();
|
|
331
404
|
}, [closeOpenedDropdown]);
|
|
332
405
|
var handleFocusBySource = function (event, isMobilePopupInput) {
|
|
406
|
+
var shouldSkipOpenDropdown = shouldSkipOpenOnFocusRef.current;
|
|
407
|
+
shouldSkipOpenOnFocusRef.current = false;
|
|
333
408
|
if (canUseMobileNativeTimePicker) {
|
|
334
|
-
|
|
409
|
+
if (!shouldSkipOpenDropdown) {
|
|
410
|
+
openNativeTimePicker();
|
|
411
|
+
}
|
|
335
412
|
onFocus === null || onFocus === void 0 ? void 0 : onFocus(event);
|
|
336
413
|
return;
|
|
337
414
|
}
|
|
338
415
|
if (canUseMobileDropdownTimePicker && !isMobilePopupInput) {
|
|
339
|
-
|
|
416
|
+
if (!shouldSkipOpenDropdown) {
|
|
417
|
+
openDropdown();
|
|
418
|
+
}
|
|
340
419
|
return;
|
|
341
420
|
}
|
|
342
421
|
setIsInputFocused(true);
|
|
343
|
-
|
|
422
|
+
if (!shouldSkipOpenDropdown) {
|
|
423
|
+
openDropdown();
|
|
424
|
+
}
|
|
344
425
|
if (isMouseDownOnFragmentRef.current && !isInputFocused) {
|
|
345
426
|
if (isTimeDisplayEmpty(editingValueRef.current)) {
|
|
346
427
|
selectSegment('hours');
|
|
@@ -477,20 +558,24 @@ export var TimePicker = Object.assign(forwardRefAndName('TimePicker', function (
|
|
|
477
558
|
}
|
|
478
559
|
if (shouldHandleAsAllSelection && (isKeyBackspace(event) || isKeyDelete(event))) {
|
|
479
560
|
event.preventDefault();
|
|
561
|
+
openDropdown();
|
|
480
562
|
updateEditingValueAfterUserEdit(getEmptyDisplayValue(format));
|
|
481
563
|
selectSegment('hours');
|
|
482
564
|
return;
|
|
483
565
|
}
|
|
484
566
|
if (DIGIT_REGEXP.test(event.key)) {
|
|
485
567
|
event.preventDefault();
|
|
486
|
-
|
|
568
|
+
openDropdown();
|
|
487
569
|
var nextDigitInputValue = shouldHandleAsAllSelection ? getEmptyDisplayValue(format) : currentDisplayValue;
|
|
488
570
|
var nextDigitInputSegment = shouldHandleAsAllSelection ? 'hours' : currentSegment;
|
|
489
571
|
var result = formatDigitToTimeSegment(nextDigitInputValue, nextDigitInputSegment, event.key, format);
|
|
572
|
+
// Отклоненная цифра не меняет ни значение, ни список, поэтому и подсветку трогать нельзя:
|
|
573
|
+
// автоподсветка не восстановится, а Enter выбрал бы не тот элемент, который видит пользователь.
|
|
490
574
|
if (result.shouldBlink) {
|
|
491
575
|
handleUnexpectedInput(event.key);
|
|
492
576
|
return;
|
|
493
577
|
}
|
|
578
|
+
resetHighlightedItem();
|
|
494
579
|
updateEditingValueAfterUserEdit(result.nextValue);
|
|
495
580
|
selectSegment(result.selectedSegment);
|
|
496
581
|
return;
|
|
@@ -538,6 +623,7 @@ export var TimePicker = Object.assign(forwardRefAndName('TimePicker', function (
|
|
|
538
623
|
}
|
|
539
624
|
if (isKeyBackspace(event)) {
|
|
540
625
|
event.preventDefault();
|
|
626
|
+
openDropdown();
|
|
541
627
|
var nextDisplayValue = deleteTimeSegmentDigit(currentDisplayValue, currentSegment, format);
|
|
542
628
|
var nextSegment = currentSegment;
|
|
543
629
|
if (nextDisplayValue === currentDisplayValue) {
|
|
@@ -553,6 +639,7 @@ export var TimePicker = Object.assign(forwardRefAndName('TimePicker', function (
|
|
|
553
639
|
}
|
|
554
640
|
if (isKeyDelete(event)) {
|
|
555
641
|
event.preventDefault();
|
|
642
|
+
openDropdown();
|
|
556
643
|
updateEditingValueAfterUserEdit(replaceTimeSegment(currentDisplayValue, currentSegment, EMPTY_SEGMENT, format));
|
|
557
644
|
selectSegment(currentSegment);
|
|
558
645
|
return;
|
|
@@ -570,7 +657,7 @@ export var TimePicker = Object.assign(forwardRefAndName('TimePicker', function (
|
|
|
570
657
|
return;
|
|
571
658
|
}
|
|
572
659
|
if (isKeyEnter(event)) {
|
|
573
|
-
if (isSelectHighlightedItem()) {
|
|
660
|
+
if (isSelectHighlightedItem(event)) {
|
|
574
661
|
event.preventDefault();
|
|
575
662
|
return;
|
|
576
663
|
}
|
|
@@ -610,7 +697,7 @@ export var TimePicker = Object.assign(forwardRefAndName('TimePicker', function (
|
|
|
610
697
|
var handleKeyDown = function (event) { return handleKeyDownBySource(event, false); };
|
|
611
698
|
var handleMobileKeyDown = function (event) { return handleKeyDownBySource(event, true); };
|
|
612
699
|
var handlePasteBySource = function (event, isMobilePopupInput) {
|
|
613
|
-
var _a;
|
|
700
|
+
var _a, _b;
|
|
614
701
|
onPaste === null || onPaste === void 0 ? void 0 : onPaste(event);
|
|
615
702
|
if (event.defaultPrevented || disabled) {
|
|
616
703
|
return;
|
|
@@ -618,13 +705,17 @@ export var TimePicker = Object.assign(forwardRefAndName('TimePicker', function (
|
|
|
618
705
|
if (canUseMobileNativeTimePicker || (canUseMobileDropdownTimePicker && !isMobilePopupInput)) {
|
|
619
706
|
return;
|
|
620
707
|
}
|
|
621
|
-
var pastedValue = (_a = event.clipboardData) === null || _a === void 0 ? void 0 : _a.getData('text');
|
|
622
|
-
var nextDisplayValue = parsePastedTimeValue(pastedValue, format);
|
|
708
|
+
var pastedValue = (_b = (_a = event.clipboardData) === null || _a === void 0 ? void 0 : _a.getData('text')) !== null && _b !== void 0 ? _b : EMPTY_VALUE;
|
|
623
709
|
event.preventDefault();
|
|
710
|
+
if (pastedValue.trim() === EMPTY_VALUE) {
|
|
711
|
+
return;
|
|
712
|
+
}
|
|
713
|
+
var nextDisplayValue = parsePastedTimeValue(pastedValue, format);
|
|
624
714
|
if (isTimeDisplayEmpty(nextDisplayValue)) {
|
|
625
715
|
handleUnexpectedInput(pastedValue);
|
|
626
716
|
return;
|
|
627
717
|
}
|
|
718
|
+
openDropdown();
|
|
628
719
|
updateEditingValueAfterUserEdit(nextDisplayValue);
|
|
629
720
|
selectSegment('hours');
|
|
630
721
|
};
|
|
@@ -637,7 +728,7 @@ export var TimePicker = Object.assign(forwardRefAndName('TimePicker', function (
|
|
|
637
728
|
React.createElement("span", { ref: rootRef, className: styles.root(), "data-tid": TimePickerDataTids.root },
|
|
638
729
|
React.createElement(TimeInput, __assign({}, inputProps, { "data-tid": TimePickerDataTids.input, ref: inputRef, disabled: disabled, hasDropdown: hasDropdown, size: size, format: format, corners: corners, rightIcon: inputRightIcon, suffix: suffix, value: displayValue, "aria-haspopup": hasDropdown ? 'listbox' : undefined, "aria-expanded": !isMobileDropdownActive && hasDropdown && isDropdownOpened && hasDropdownContent ? true : undefined, "aria-controls": !isMobileDropdownActive && hasDropdown && isDropdownOpened && hasDropdownContent ? popupId : undefined, "aria-activedescendant": isMobileDropdownActive ? undefined : activeDescendantId, "aria-placeholder": ariaPlaceholder, "aria-busy": isLoadingItems || undefined, onClick: handleClick, onBlur: handleBlur, onFocus: handleFocus, onMouseDownCapture: handleMouseDownCapture, onMouseUp: handleMouseUp, onKeyDown: handleKeyDown, onSelectSegment: handleSelectSegmentByMouse, onPaste: handlePaste })),
|
|
639
730
|
canUseMobileNativeTimePicker && (React.createElement(NativeTimeInput, { "data-tid": TimePickerDataTids.nativeInput, disabled: disabled, ref: nativeInputRef, format: format, minTime: minTime, maxTime: maxTime, value: editingValue, onValueChange: handleNativeValueChange })),
|
|
640
|
-
canUseMobileDropdownTimePicker && isDropdownOpened && (React.createElement(TimePickerMobilePopup, { id: popupId, opened: isDropdownOpened, value: displayValue, inputRef: mobileInputRef, disabled: disabled, format: format, size: size, rightIcon: inputRightIcon, suffix: suffix, resolvedItems: resolvedItems, renderItem: renderItem, isLoading: isLoadingItems, highlightedItemIndex: highlightedItemIndex, normalizedValue: normalizedValue, itemRefs: itemRefs, error: props.error, warning: props.warning, "aria-describedby": props['aria-describedby'], "aria-label": props['aria-label'], "aria-placeholder": ariaPlaceholder, "aria-haspopup": 'listbox', "aria-expanded":
|
|
731
|
+
canUseMobileDropdownTimePicker && isDropdownOpened && (React.createElement(TimePickerMobilePopup, { id: popupId, opened: isDropdownOpened, value: displayValue, inputRef: mobileInputRef, disabled: disabled, format: format, size: size, rightIcon: inputRightIcon, suffix: suffix, resolvedItems: resolvedItems, renderItem: renderItem, isLoading: isLoadingItems, highlightedItemIndex: highlightedItemIndex, normalizedValue: normalizedValue, itemRefs: itemRefs, error: props.error, warning: props.warning, "aria-describedby": props['aria-describedby'], "aria-label": props['aria-label'], "aria-placeholder": ariaPlaceholder, "aria-haspopup": 'listbox', "aria-expanded": resolvedItems.length > 0 || isLoadingItems ? true : undefined, "aria-controls": resolvedItems.length > 0 || isLoadingItems ? popupId : undefined, "aria-activedescendant": activeDescendantId, onFocus: handleMobileFocus, onBlur: handleMobileBlur, onClick: handleMobileClick, onKeyDown: handleMobileKeyDown, onMouseDownCapture: handleMobileMouseDownCapture, onMouseUp: handleMobileMouseUp, onPaste: handleMobilePaste, onSelectSegment: handleMobileSelectSegmentByMouse, onSelectItem: selectItem, onCloseRequest: handleCloseMobileDropdown })),
|
|
641
732
|
hasDropdown &&
|
|
642
733
|
!canUseMobileNativeTimePicker &&
|
|
643
734
|
!canUseMobileDropdownTimePicker &&
|