@cloud-ru/ds-fields 2.2.3-preview-7425cb27.0 → 2.2.3-preview-8025692f.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/README.md +1 -1
- package/dist/cjs/segments/useSegmentedMask.d.ts +1 -3
- package/dist/cjs/segments/useSegmentedMask.js +9 -18
- package/dist/esm/segments/useSegmentedMask.d.ts +1 -3
- package/dist/esm/segments/useSegmentedMask.js +9 -18
- package/dist/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/tsconfig.esm.tsbuildinfo +1 -1
- package/package.json +8 -8
- package/src/segments/useSegmentedMask.ts +9 -18
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloud-ru/ds-fields",
|
|
3
|
-
"version": "2.2.3-preview-
|
|
3
|
+
"version": "2.2.3-preview-8025692f.0",
|
|
4
4
|
"description": "Поля ввода с единой обёрткой label/caption/hint/error и общими осями size/validationState — текст, пароль, число, ползунок, дата, время, выбор, цвет.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -39,24 +39,24 @@
|
|
|
39
39
|
"classnames": "^2.5.1",
|
|
40
40
|
"merge-refs": "^2.0.0",
|
|
41
41
|
"react-textarea-autosize": "^8.5.9",
|
|
42
|
-
"@cloud-ru/ds-adaptive": "1.0.1",
|
|
43
42
|
"@cloud-ru/ds-bottom-sheet": "1.1.1",
|
|
44
|
-
"@cloud-ru/ds-
|
|
45
|
-
"@cloud-ru/ds-calendar": "1.0.14",
|
|
43
|
+
"@cloud-ru/ds-adaptive": "1.0.1",
|
|
46
44
|
"@cloud-ru/ds-color-picker": "1.0.6",
|
|
45
|
+
"@cloud-ru/ds-calendar": "1.0.15-preview-8025692f.0",
|
|
47
46
|
"@cloud-ru/ds-divider": "1.0.1",
|
|
47
|
+
"@cloud-ru/ds-dropdown": "1.0.8",
|
|
48
|
+
"@cloud-ru/ds-button": "1.0.2",
|
|
48
49
|
"@cloud-ru/ds-field-decorator": "1.0.5",
|
|
49
50
|
"@cloud-ru/ds-icons": "1.1.0",
|
|
50
|
-
"@cloud-ru/ds-list": "2.1.
|
|
51
|
-
"@cloud-ru/ds-dropdown": "1.0.8",
|
|
51
|
+
"@cloud-ru/ds-list": "2.1.4-preview-8025692f.0",
|
|
52
52
|
"@cloud-ru/ds-input-private": "1.0.4",
|
|
53
53
|
"@cloud-ru/ds-loader": "1.0.1",
|
|
54
54
|
"@cloud-ru/ds-materials": "1.0.1",
|
|
55
55
|
"@cloud-ru/ds-scroll": "1.0.2",
|
|
56
|
-
"@cloud-ru/ds-skeleton": "1.0.1",
|
|
57
56
|
"@cloud-ru/ds-slider": "1.0.4",
|
|
58
|
-
"@cloud-ru/ds-
|
|
57
|
+
"@cloud-ru/ds-skeleton": "1.0.1",
|
|
59
58
|
"@cloud-ru/ds-tooltip": "1.0.4",
|
|
59
|
+
"@cloud-ru/ds-tag": "1.1.1",
|
|
60
60
|
"@cloud-ru/ds-utils": "1.1.0"
|
|
61
61
|
}
|
|
62
62
|
}
|
|
@@ -45,9 +45,7 @@ type UseSegmentedMaskParams = {
|
|
|
45
45
|
* Сегментный ввод даты/времени (порт `useDateField` из @snack-uikit/fields). Каретка ходит по
|
|
46
46
|
* сегментам (`setSelectionRange` подсвечивает текущий), цифры заполняют сегмент с авто-переходом,
|
|
47
47
|
* стрелки двигают по сегментам, Backspace очищает сегмент до плейсхолдера.
|
|
48
|
-
*
|
|
49
|
-
* Навигация идёт через `handleKeyDown`, правка значения — через нативные `beforeinput`/`input`:
|
|
50
|
-
* `nativeInputRef` нужно передать в `ref` того же input'а.
|
|
48
|
+
* `nativeInputRef` передаётся в `ref` того же input'а: значение правится через нативные `beforeinput`/`input`.
|
|
51
49
|
*/
|
|
52
50
|
export function useSegmentedMask({
|
|
53
51
|
inputRef,
|
|
@@ -190,8 +188,7 @@ export function useSegmentedMask({
|
|
|
190
188
|
[clearSlot, isValidDate, lastSlot, nextSlot, selectSlot, slots, tryToComplete],
|
|
191
189
|
);
|
|
192
190
|
|
|
193
|
-
//
|
|
194
|
-
// набрали `1` или `0` + `1`, а от этого зависит, ждать ли вторую цифру.
|
|
191
|
+
// Цифры сегмента храним отдельно от текста: по `01` в поле не отличить `1` от `0` + `1`.
|
|
195
192
|
const typedRef = useRef<{ key: SegmentKey; digits: string } | null>(null);
|
|
196
193
|
const resetTyped = useCallback(() => {
|
|
197
194
|
typedRef.current = null;
|
|
@@ -202,12 +199,11 @@ export function useSegmentedMask({
|
|
|
202
199
|
const maxLength = slot.max.toString().length;
|
|
203
200
|
const prev = typedRef.current?.key === slot.key ? typedRef.current.digits : '';
|
|
204
201
|
const appended = prev + key;
|
|
205
|
-
//
|
|
202
|
+
// Как в React Aria и MUI X: `13` в месяце → `03`.
|
|
206
203
|
const overflow = Number(appended) > slot.max;
|
|
207
204
|
const digits = overflow ? key : appended;
|
|
208
205
|
const value = Number(digits);
|
|
209
206
|
const complete = overflow || digits.length >= maxLength || value * 10 > slot.max;
|
|
210
|
-
// Значение ниже min (`0` в дне) не показываем, но помним цифру до следующей.
|
|
211
207
|
const accepted = value >= slot.min;
|
|
212
208
|
|
|
213
209
|
if (accepted) updateSlot(slot, value);
|
|
@@ -256,7 +252,6 @@ export function useSegmentedMask({
|
|
|
256
252
|
const input = inputRef.current;
|
|
257
253
|
if (!input || readonly || disabled) return;
|
|
258
254
|
|
|
259
|
-
// Навигация и правка начинают набор сегмента заново. Цифры, `Unidentified` (Android) и модификаторы — нет.
|
|
260
255
|
if (RESET_TYPED_KEYS.has(event.key)) resetTyped();
|
|
261
256
|
|
|
262
257
|
// Tab уводит фокус из поля — не перехватываем.
|
|
@@ -275,12 +270,12 @@ export function useSegmentedMask({
|
|
|
275
270
|
return;
|
|
276
271
|
}
|
|
277
272
|
|
|
278
|
-
//
|
|
273
|
+
// Копирование и вставку не блокируем: правку поля перехватит beforeinput.
|
|
279
274
|
if (event.ctrlKey || event.metaKey || event.altKey) return;
|
|
280
275
|
|
|
281
276
|
onEdit?.();
|
|
282
277
|
|
|
283
|
-
//
|
|
278
|
+
// Экранные клавиатуры присылают `Unidentified` вместо цифры, её данные есть только в beforeinput.
|
|
284
279
|
if (/^\d$/.test(event.key) || event.key === 'Unidentified') return;
|
|
285
280
|
|
|
286
281
|
if (event.key === 'Escape') {
|
|
@@ -343,7 +338,6 @@ export function useSegmentedMask({
|
|
|
343
338
|
],
|
|
344
339
|
);
|
|
345
340
|
|
|
346
|
-
// Раскладывает цифры по сегментам слева направо полными кусками — для вставки и автозаполнения.
|
|
347
341
|
const fillFromDigits = useCallback(
|
|
348
342
|
(digits: string) => {
|
|
349
343
|
resetTyped();
|
|
@@ -369,8 +363,6 @@ export function useSegmentedMask({
|
|
|
369
363
|
[ensureMask, lastSlot, nextSlot, resetTyped, selectSlot, slots, tryToComplete, updateSlot],
|
|
370
364
|
);
|
|
371
365
|
|
|
372
|
-
// Вставка (Ctrl+V) полной строки даты/времени. Нативный onChange у поля игнорируется (движок
|
|
373
|
-
// controlled), поэтому без этого обработчика вставка не работала бы вовсе.
|
|
374
366
|
const handlePaste = useCallback(
|
|
375
367
|
(event: ClipboardEvent<HTMLInputElement>) => {
|
|
376
368
|
if (readonly || disabled) return;
|
|
@@ -399,7 +391,7 @@ export function useSegmentedMask({
|
|
|
399
391
|
const digits = text.replace(/\D/g, '');
|
|
400
392
|
if (!inputType.startsWith('insert') || !digits) return;
|
|
401
393
|
|
|
402
|
-
//
|
|
394
|
+
// Автозаполнение, `fill` и drop присылают строку целиком.
|
|
403
395
|
if (text.length > 1) {
|
|
404
396
|
fillFromDigits(digits);
|
|
405
397
|
} else {
|
|
@@ -410,7 +402,7 @@ export function useSegmentedMask({
|
|
|
410
402
|
[clearSlot, emit, fillFromDigits, inputRef, onEdit, resetTyped, slotFromIndex, typeDigit],
|
|
411
403
|
);
|
|
412
404
|
|
|
413
|
-
//
|
|
405
|
+
// Композицию IME браузер не даёт отменить — откатываем её в input.
|
|
414
406
|
const pendingEditRef = useRef<{ value: string; start: number; end: number } | null>(null);
|
|
415
407
|
|
|
416
408
|
const handleBeforeInput = useCallback(
|
|
@@ -438,18 +430,17 @@ export function useSegmentedMask({
|
|
|
438
430
|
pendingEditRef.current = null;
|
|
439
431
|
|
|
440
432
|
if (pending) {
|
|
441
|
-
// Браузер уже изменил значение: вырезаем вставленное, возвращаем прежнее и применяем как обычный ввод.
|
|
442
433
|
const inputType = event instanceof InputEvent ? event.inputType : '';
|
|
443
434
|
const inserted = input.value.slice(pending.start, input.value.length - (pending.value.length - pending.end));
|
|
444
435
|
input.value = pending.value;
|
|
445
436
|
setValue(pending.value);
|
|
446
437
|
input.setSelectionRange(pending.start, pending.end);
|
|
447
|
-
// Цифру
|
|
438
|
+
// Цифру композиции вводит её фиксация (insertText), иначе цифра запишется дважды.
|
|
448
439
|
if (!inputType.includes('Composition')) applyEdit(inputType, inserted);
|
|
449
440
|
return;
|
|
450
441
|
}
|
|
451
442
|
|
|
452
|
-
//
|
|
443
|
+
// Автозаполнение меняет значение без beforeinput.
|
|
453
444
|
const digits = input.value.replace(/\D/g, '');
|
|
454
445
|
input.value = mask;
|
|
455
446
|
setValue(mask);
|