@cloud-ru/uikit-product-fields-predefined 2.4.3 → 2.4.4-preview-347b88f7.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 +19 -0
- package/dist/cjs/components/FieldCode/FieldCode.d.ts +50 -0
- package/dist/cjs/components/FieldCode/FieldCode.js +66 -0
- package/dist/cjs/components/FieldCode/hooks/index.d.ts +4 -0
- package/dist/cjs/components/FieldCode/hooks/index.js +20 -0
- package/dist/cjs/components/FieldCode/hooks/useCodeInput.d.ts +21 -0
- package/dist/cjs/components/FieldCode/hooks/useCodeInput.js +129 -0
- package/dist/cjs/components/FieldCode/hooks/useCodeInputEffects.d.ts +17 -0
- package/dist/cjs/components/FieldCode/hooks/useCodeInputEffects.js +62 -0
- package/dist/cjs/components/FieldCode/hooks/useFocusFields.d.ts +7 -0
- package/dist/cjs/components/FieldCode/hooks/useFocusFields.js +16 -0
- package/dist/cjs/components/FieldCode/hooks/useStateWithRef.d.ts +2 -0
- package/dist/cjs/components/FieldCode/hooks/useStateWithRef.js +21 -0
- package/dist/cjs/components/FieldCode/index.d.ts +1 -0
- package/dist/cjs/components/FieldCode/index.js +17 -0
- package/dist/cjs/components/FieldCode/styles.module.css +42 -0
- package/dist/cjs/components/FieldCode/utils.d.ts +13 -0
- package/dist/cjs/components/FieldCode/utils.js +33 -0
- package/dist/cjs/components/index.d.ts +1 -0
- package/dist/cjs/components/index.js +1 -0
- package/dist/esm/components/FieldCode/FieldCode.d.ts +50 -0
- package/dist/esm/components/FieldCode/FieldCode.js +60 -0
- package/dist/esm/components/FieldCode/hooks/index.d.ts +4 -0
- package/dist/esm/components/FieldCode/hooks/index.js +4 -0
- package/dist/esm/components/FieldCode/hooks/useCodeInput.d.ts +21 -0
- package/dist/esm/components/FieldCode/hooks/useCodeInput.js +126 -0
- package/dist/esm/components/FieldCode/hooks/useCodeInputEffects.d.ts +17 -0
- package/dist/esm/components/FieldCode/hooks/useCodeInputEffects.js +59 -0
- package/dist/esm/components/FieldCode/hooks/useFocusFields.d.ts +7 -0
- package/dist/esm/components/FieldCode/hooks/useFocusFields.js +13 -0
- package/dist/esm/components/FieldCode/hooks/useStateWithRef.d.ts +2 -0
- package/dist/esm/components/FieldCode/hooks/useStateWithRef.js +18 -0
- package/dist/esm/components/FieldCode/index.d.ts +1 -0
- package/dist/esm/components/FieldCode/index.js +1 -0
- package/dist/esm/components/FieldCode/styles.module.css +42 -0
- package/dist/esm/components/FieldCode/utils.d.ts +13 -0
- package/dist/esm/components/FieldCode/utils.js +22 -0
- package/dist/esm/components/index.d.ts +1 -0
- package/dist/esm/components/index.js +1 -0
- package/dist/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/tsconfig.esm.tsbuildinfo +1 -1
- package/package.json +5 -3
- package/src/components/FieldCode/FieldCode.tsx +130 -0
- package/src/components/FieldCode/hooks/index.ts +4 -0
- package/src/components/FieldCode/hooks/useCodeInput.ts +192 -0
- package/src/components/FieldCode/hooks/useCodeInputEffects.ts +96 -0
- package/src/components/FieldCode/hooks/useFocusFields.ts +26 -0
- package/src/components/FieldCode/hooks/useStateWithRef.ts +22 -0
- package/src/components/FieldCode/index.ts +1 -0
- package/src/components/FieldCode/styles.module.scss +63 -0
- package/src/components/FieldCode/utils.ts +40 -0
- package/src/components/index.ts +1 -0
package/README.md
CHANGED
|
@@ -349,6 +349,25 @@
|
|
|
349
349
|
| onClearButtonClick | `() => void` | - | Колбек клика по кнопке очистки поля |
|
|
350
350
|
| value | `string` | - | |
|
|
351
351
|
| onChange | `(value: string, mask: InputMask<Record<string, unknown>>) => void` | - | |
|
|
352
|
+
## FieldCode
|
|
353
|
+
### Props
|
|
354
|
+
| name | type | default value | description |
|
|
355
|
+
|------|------|---------------|-------------|
|
|
356
|
+
| codeLength* | `number` | - | Количество цифр в коде |
|
|
357
|
+
| className | `string` | - | CSS-класс |
|
|
358
|
+
| showEmptyChars | `boolean` | - | Подсветить пустые символы кода |
|
|
359
|
+
| loading | `boolean` | - | отправка кода в процессе |
|
|
360
|
+
| incorrectCodeState | `boolean` | - | Подсветить все символы кода |
|
|
361
|
+
| onChange | `(code: string) => void` | - | Колбек изменения значения |
|
|
362
|
+
| value | `string` | - | |
|
|
363
|
+
| onComplete | `(code: string) => void` | - | Колбек достижения ввода всех символов кода |
|
|
364
|
+
| withoutFocus | `boolean` | - | Отключить автофокус |
|
|
365
|
+
| fieldClassName | `string` | - | |
|
|
366
|
+
| withoutErrorFocus | `boolean` | - | Отключить автофокус при ошибке (по умолчанию false) |
|
|
367
|
+
| spacing | `number[]` | - | Позиции, после которых нужно вставить пробел (индексы символов, после которых будет пробел) |
|
|
368
|
+
| disabled | `boolean` | - | Деактивирован ли элемент Является ли поле деактивированным |
|
|
369
|
+
| readonly | `boolean` | - | Является ли поле доступным только на чтение Доступно ли поле только на чтение |
|
|
370
|
+
| size | enum Size: `"s"`, `"m"`, `"l"` | l | Размер |
|
|
352
371
|
| ref | `LegacyRef<HTMLInputElement>` | - | Allows getting a ref to the component instance. Once the component unmounts, React will set `ref.current` to `null` (or call the ref with `null` if you passed a callback ref). @see {@link https://react.dev/learn/referencing-values-with-refs#refs-and-the-dom React Docs} |
|
|
353
372
|
| key | `Key` | - | |
|
|
354
373
|
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { FieldDecoratorProps } from '@snack-uikit/fields';
|
|
2
|
+
export type FieldCodeRef = {
|
|
3
|
+
blur: () => void;
|
|
4
|
+
};
|
|
5
|
+
export type FieldCodeProps = {
|
|
6
|
+
/** Количество цифр в коде */
|
|
7
|
+
codeLength: number;
|
|
8
|
+
className?: string;
|
|
9
|
+
/** Подсветить пустые символы кода */
|
|
10
|
+
showEmptyChars?: boolean;
|
|
11
|
+
/** отправка кода в процессе */
|
|
12
|
+
loading?: boolean;
|
|
13
|
+
/** Подсветить все символы кода */
|
|
14
|
+
incorrectCodeState?: boolean;
|
|
15
|
+
/** Колбек изменения значения */
|
|
16
|
+
onChange?: (code: string) => void;
|
|
17
|
+
value?: string;
|
|
18
|
+
/** Колбек достижения ввода всех символов кода */
|
|
19
|
+
onComplete?: (code: string) => void;
|
|
20
|
+
/** Отключить автофокус */
|
|
21
|
+
withoutFocus?: boolean;
|
|
22
|
+
fieldClassName?: string;
|
|
23
|
+
/** Отключить автофокус при ошибке (по умолчанию false) */
|
|
24
|
+
withoutErrorFocus?: boolean;
|
|
25
|
+
/** Позиции, после которых нужно вставить пробел (индексы символов, после которых будет пробел) */
|
|
26
|
+
spacing?: number[];
|
|
27
|
+
} & Pick<FieldDecoratorProps, 'size' | 'disabled' | 'readonly'>;
|
|
28
|
+
export declare const FieldCode: import("react").ForwardRefExoticComponent<{
|
|
29
|
+
/** Количество цифр в коде */
|
|
30
|
+
codeLength: number;
|
|
31
|
+
className?: string;
|
|
32
|
+
/** Подсветить пустые символы кода */
|
|
33
|
+
showEmptyChars?: boolean;
|
|
34
|
+
/** отправка кода в процессе */
|
|
35
|
+
loading?: boolean;
|
|
36
|
+
/** Подсветить все символы кода */
|
|
37
|
+
incorrectCodeState?: boolean;
|
|
38
|
+
/** Колбек изменения значения */
|
|
39
|
+
onChange?: (code: string) => void;
|
|
40
|
+
value?: string;
|
|
41
|
+
/** Колбек достижения ввода всех символов кода */
|
|
42
|
+
onComplete?: (code: string) => void;
|
|
43
|
+
/** Отключить автофокус */
|
|
44
|
+
withoutFocus?: boolean;
|
|
45
|
+
fieldClassName?: string;
|
|
46
|
+
/** Отключить автофокус при ошибке (по умолчанию false) */
|
|
47
|
+
withoutErrorFocus?: boolean;
|
|
48
|
+
/** Позиции, после которых нужно вставить пробел (индексы символов, после которых будет пробел) */
|
|
49
|
+
spacing?: number[];
|
|
50
|
+
} & Pick<FieldDecoratorProps, "size" | "disabled" | "readonly"> & import("react").RefAttributes<FieldCodeRef>>;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
3
|
+
var t = {};
|
|
4
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
5
|
+
t[p] = s[p];
|
|
6
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
7
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
8
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
9
|
+
t[p[i]] = s[p[i]];
|
|
10
|
+
}
|
|
11
|
+
return t;
|
|
12
|
+
};
|
|
13
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
14
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.FieldCode = void 0;
|
|
18
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
19
|
+
const classnames_1 = __importDefault(require("classnames"));
|
|
20
|
+
const react_1 = require("react");
|
|
21
|
+
const fields_1 = require("@snack-uikit/fields");
|
|
22
|
+
const hooks_1 = require("./hooks");
|
|
23
|
+
const styles_module_scss_1 = __importDefault(require('./styles.module.css'));
|
|
24
|
+
const utils_1 = require("./utils");
|
|
25
|
+
exports.FieldCode = (0, react_1.forwardRef)(function FieldCode(_a, ref) {
|
|
26
|
+
var { codeLength, className, showEmptyChars, incorrectCodeState, loading, onChange: onChangeProp, onComplete, withoutFocus, value: valueProp, fieldClassName, withoutErrorFocus = false, size = 'l', spacing } = _a, decoratorProps = __rest(_a, ["codeLength", "className", "showEmptyChars", "incorrectCodeState", "loading", "onChange", "onComplete", "withoutFocus", "value", "fieldClassName", "withoutErrorFocus", "size", "spacing"]);
|
|
27
|
+
const inputsRef = (0, react_1.useRef)([]);
|
|
28
|
+
const moveFocus = (0, hooks_1.useFocusFields)({ inputsRef, codeLength });
|
|
29
|
+
const { code, setCode, buildCodeArray, handleInputChange, handleKeyDown, handlePaste } = (0, hooks_1.useCodeInput)({
|
|
30
|
+
codeLength,
|
|
31
|
+
value: valueProp,
|
|
32
|
+
onChange: onChangeProp,
|
|
33
|
+
onComplete,
|
|
34
|
+
moveFocus,
|
|
35
|
+
});
|
|
36
|
+
const blurFields = () => {
|
|
37
|
+
inputsRef.current.forEach(input => input === null || input === void 0 ? void 0 : input.blur());
|
|
38
|
+
};
|
|
39
|
+
(0, react_1.useImperativeHandle)(ref, () => ({
|
|
40
|
+
blur: blurFields,
|
|
41
|
+
}));
|
|
42
|
+
(0, hooks_1.useCodeInputEffects)({
|
|
43
|
+
codeLength,
|
|
44
|
+
code,
|
|
45
|
+
valueProp,
|
|
46
|
+
loading,
|
|
47
|
+
incorrectCodeState,
|
|
48
|
+
showEmptyChars,
|
|
49
|
+
withoutFocus,
|
|
50
|
+
withoutErrorFocus,
|
|
51
|
+
buildCodeArray,
|
|
52
|
+
setCode,
|
|
53
|
+
onChange: onChangeProp,
|
|
54
|
+
moveFocus,
|
|
55
|
+
inputsRef,
|
|
56
|
+
});
|
|
57
|
+
const resolvedDecoratorProps = Object.assign(Object.assign({}, decoratorProps), { size });
|
|
58
|
+
// Определяем индексы полей, после которых нужен отступ (0-based индексы)
|
|
59
|
+
const spacingIndices = (0, react_1.useMemo)(() => {
|
|
60
|
+
if (!spacing || spacing.length === 0) {
|
|
61
|
+
return new Set();
|
|
62
|
+
}
|
|
63
|
+
return new Set(spacing.filter(pos => pos >= 0 && pos < codeLength - 1));
|
|
64
|
+
}, [spacing, codeLength]);
|
|
65
|
+
return ((0, jsx_runtime_1.jsx)(fields_1.FieldDecorator, Object.assign({}, resolvedDecoratorProps, { className: className, children: (0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.codeContainer, "data-size": size, children: code.map((char, index) => ((0, jsx_runtime_1.jsx)(fields_1.FieldText, { inputMode: 'numeric', autoComplete: 'one-time-code', className: (0, classnames_1.default)(styles_module_scss_1.default.codeInput, spacingIndices.has(index) && styles_module_scss_1.default.codeInputWithSpacing, fieldClassName), "data-size": size, showClearButton: false, value: char === utils_1.ZERO_WIDTH_SPACE ? '' : char, validationState: (0, utils_1.getValidationState)({ char, incorrectCodeState, showEmptyChars }), ref: ref => (inputsRef.current[index] = ref), onKeyDown: e => handleKeyDown(e, index), onChange: code => handleInputChange(code, index), onPaste: e => handlePaste({ e }), size: resolvedDecoratorProps.size, disabled: resolvedDecoratorProps.disabled, readonly: resolvedDecoratorProps.readonly }, index))) }) })));
|
|
66
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./useCodeInput"), exports);
|
|
18
|
+
__exportStar(require("./useCodeInputEffects"), exports);
|
|
19
|
+
__exportStar(require("./useFocusFields"), exports);
|
|
20
|
+
__exportStar(require("./useStateWithRef"), exports);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { ClipboardEvent, KeyboardEvent } from 'react';
|
|
2
|
+
type UseCodeInputParams = {
|
|
3
|
+
codeLength: number;
|
|
4
|
+
value?: string;
|
|
5
|
+
onChange?: (code: string) => void;
|
|
6
|
+
onComplete?: (code: string) => void;
|
|
7
|
+
moveFocus: (index: number) => void;
|
|
8
|
+
};
|
|
9
|
+
export declare function useCodeInput({ codeLength, value: valueProp, onChange: onChangeProp, onComplete, moveFocus, }: UseCodeInputParams): {
|
|
10
|
+
code: string[];
|
|
11
|
+
setCode: (newValue: string[] | ((prevValue: string[]) => string[])) => void;
|
|
12
|
+
codeRef: import("react").MutableRefObject<string[]>;
|
|
13
|
+
buildCodeArray: (str: string) => string[];
|
|
14
|
+
handleInputChange: (char: string, index: number) => void;
|
|
15
|
+
handleKeyDown: ({ key }: KeyboardEvent<HTMLInputElement>, index: number) => void;
|
|
16
|
+
handlePaste: ({ e, fullCode }: {
|
|
17
|
+
e?: ClipboardEvent<HTMLInputElement>;
|
|
18
|
+
fullCode?: string;
|
|
19
|
+
}) => void;
|
|
20
|
+
};
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useCodeInput = useCodeInput;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
const utils_1 = require("@snack-uikit/utils");
|
|
6
|
+
const utils_2 = require("../utils");
|
|
7
|
+
const useStateWithRef_1 = require("./useStateWithRef");
|
|
8
|
+
function useCodeInput({ codeLength, value: valueProp, onChange: onChangeProp, onComplete, moveFocus, }) {
|
|
9
|
+
const buildCodeArray = (0, react_1.useCallback)((str) => Array.from({ length: codeLength }, (_, idx) => { var _a; return (_a = str[idx]) !== null && _a !== void 0 ? _a : ''; }), [codeLength]);
|
|
10
|
+
const [value = '', onChange] = (0, utils_1.useValueControl)({
|
|
11
|
+
value: valueProp,
|
|
12
|
+
onChange: onChangeProp,
|
|
13
|
+
defaultValue: '',
|
|
14
|
+
});
|
|
15
|
+
const [code, setCode, codeRef] = (0, useStateWithRef_1.useStateWithRef)(buildCodeArray(value));
|
|
16
|
+
const pasteFlag = (0, react_1.useRef)(false);
|
|
17
|
+
const keyInputFlag = (0, react_1.useRef)(false);
|
|
18
|
+
const updateCode = (0, react_1.useCallback)((index, newChar) => {
|
|
19
|
+
const updatedCode = [...codeRef.current];
|
|
20
|
+
updatedCode[index] = newChar;
|
|
21
|
+
setCode(updatedCode);
|
|
22
|
+
const normalizedCode = (0, utils_2.getPaddedValue)(updatedCode).join('');
|
|
23
|
+
onChange === null || onChange === void 0 ? void 0 : onChange(normalizedCode);
|
|
24
|
+
return updatedCode;
|
|
25
|
+
}, [codeRef, setCode, onChange]);
|
|
26
|
+
const handleAutoComplete = (0, react_1.useCallback)((char, charIndex) => {
|
|
27
|
+
for (const idx of (0, utils_2.generateRangeWithStep)(1, charIndex)) {
|
|
28
|
+
updateCode(idx - 1, codeRef.current[idx]);
|
|
29
|
+
}
|
|
30
|
+
updateCode(charIndex, char);
|
|
31
|
+
const updatedCode = codeRef.current.join('');
|
|
32
|
+
if ((0, utils_2.isStringCodeLength)(updatedCode, codeLength)) {
|
|
33
|
+
onComplete === null || onComplete === void 0 ? void 0 : onComplete(updatedCode);
|
|
34
|
+
}
|
|
35
|
+
}, [codeLength, codeRef, onComplete, updateCode]);
|
|
36
|
+
const handlePaste = (0, react_1.useCallback)(({ e, fullCode }) => {
|
|
37
|
+
var _a;
|
|
38
|
+
if (e) {
|
|
39
|
+
pasteFlag.current = true;
|
|
40
|
+
}
|
|
41
|
+
const codeInput = (_a = (fullCode || (e === null || e === void 0 ? void 0 : e.clipboardData.getData('text')))) !== null && _a !== void 0 ? _a : '';
|
|
42
|
+
if (!(0, utils_2.isStringCodeLength)(codeInput, codeLength)) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const codeArray = codeInput.split('');
|
|
46
|
+
setCode(codeArray);
|
|
47
|
+
onChange === null || onChange === void 0 ? void 0 : onChange(codeInput);
|
|
48
|
+
onComplete === null || onComplete === void 0 ? void 0 : onComplete(codeInput);
|
|
49
|
+
moveFocus(codeLength - 1);
|
|
50
|
+
}, [codeLength, moveFocus, onChange, onComplete, setCode]);
|
|
51
|
+
const deleteChar = (0, react_1.useCallback)((index) => {
|
|
52
|
+
if (codeRef.current[index] && !(0, utils_2.isZeroWidthSpace)(codeRef.current[index])) {
|
|
53
|
+
updateCode(index, '');
|
|
54
|
+
}
|
|
55
|
+
else if (index > 0) {
|
|
56
|
+
moveFocus(index - 1);
|
|
57
|
+
}
|
|
58
|
+
}, [codeRef, moveFocus, updateCode]);
|
|
59
|
+
const handleInputChange = (0, react_1.useCallback)((char, index) => {
|
|
60
|
+
var _a;
|
|
61
|
+
if (pasteFlag.current || keyInputFlag.current) {
|
|
62
|
+
pasteFlag.current = false;
|
|
63
|
+
keyInputFlag.current = false;
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if ((0, utils_2.isStringCodeLength)(char, codeLength)) {
|
|
67
|
+
handlePaste({ fullCode: char });
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
// autocomplete если фокус был не на первой ячейке
|
|
71
|
+
if ((0, utils_2.isOutsideAutoComplete)(char, index, codeRef.current)) {
|
|
72
|
+
handleAutoComplete((_a = char.at(-1)) !== null && _a !== void 0 ? _a : '', index);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
let lastChar = char;
|
|
76
|
+
if (char.length > 1) {
|
|
77
|
+
const prev = codeRef.current[index];
|
|
78
|
+
const candidates = Array.from(char);
|
|
79
|
+
const found = candidates.find(c => c !== prev && (0, utils_2.isNumberChar)(c));
|
|
80
|
+
const newDigit = found !== null && found !== void 0 ? found : candidates[candidates.length - 1];
|
|
81
|
+
lastChar = (0, utils_2.isNumberChar)(newDigit) ? newDigit : '';
|
|
82
|
+
}
|
|
83
|
+
if (char === '') {
|
|
84
|
+
deleteChar(index);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
if (!(0, utils_2.isNumberChar)(lastChar)) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const updated = updateCode(index, lastChar);
|
|
91
|
+
const cleaned = (0, utils_2.getCleanValue)(updated).join('');
|
|
92
|
+
const isLastInput = index === codeLength - 1;
|
|
93
|
+
const isAllInputsFilled = (0, utils_2.isStringCodeLength)(cleaned, codeLength);
|
|
94
|
+
if (!isLastInput) {
|
|
95
|
+
moveFocus(index + 1);
|
|
96
|
+
}
|
|
97
|
+
else if (isAllInputsFilled) {
|
|
98
|
+
onComplete === null || onComplete === void 0 ? void 0 : onComplete(cleaned);
|
|
99
|
+
}
|
|
100
|
+
}, [codeLength, codeRef, deleteChar, handleAutoComplete, handlePaste, moveFocus, onComplete, updateCode]);
|
|
101
|
+
const handleKeyDown = (0, react_1.useCallback)(({ key }, index) => {
|
|
102
|
+
switch (key) {
|
|
103
|
+
case 'ArrowLeft':
|
|
104
|
+
moveFocus(index - 1);
|
|
105
|
+
break;
|
|
106
|
+
case 'ArrowRight':
|
|
107
|
+
moveFocus(index + 1);
|
|
108
|
+
break;
|
|
109
|
+
case 'Backspace':
|
|
110
|
+
deleteChar(index);
|
|
111
|
+
break;
|
|
112
|
+
default:
|
|
113
|
+
if ((0, utils_2.isNumberChar)(key)) {
|
|
114
|
+
handleInputChange(key, index);
|
|
115
|
+
keyInputFlag.current = true;
|
|
116
|
+
}
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
}, [deleteChar, handleInputChange, moveFocus]);
|
|
120
|
+
return {
|
|
121
|
+
code,
|
|
122
|
+
setCode,
|
|
123
|
+
codeRef,
|
|
124
|
+
buildCodeArray,
|
|
125
|
+
handleInputChange,
|
|
126
|
+
handleKeyDown,
|
|
127
|
+
handlePaste,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
type UseCodeInputEffectsParams = {
|
|
2
|
+
codeLength: number;
|
|
3
|
+
code: string[];
|
|
4
|
+
valueProp?: string;
|
|
5
|
+
loading?: boolean;
|
|
6
|
+
incorrectCodeState?: boolean;
|
|
7
|
+
showEmptyChars?: boolean;
|
|
8
|
+
withoutFocus?: boolean;
|
|
9
|
+
withoutErrorFocus?: boolean;
|
|
10
|
+
buildCodeArray: (str: string) => string[];
|
|
11
|
+
setCode: (code: string[]) => void;
|
|
12
|
+
onChange?: (code: string) => void;
|
|
13
|
+
moveFocus: (index: number) => void;
|
|
14
|
+
inputsRef: React.MutableRefObject<(HTMLInputElement | null)[]>;
|
|
15
|
+
};
|
|
16
|
+
export declare function useCodeInputEffects({ codeLength, code, valueProp, loading, incorrectCodeState, showEmptyChars, withoutFocus, withoutErrorFocus, buildCodeArray, setCode, onChange, moveFocus, inputsRef, }: UseCodeInputEffectsParams): void;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useCodeInputEffects = useCodeInputEffects;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
const utils_1 = require("../utils");
|
|
6
|
+
function useCodeInputEffects({ codeLength, code, valueProp, loading, incorrectCodeState, showEmptyChars, withoutFocus, withoutErrorFocus, buildCodeArray, setCode, onChange, moveFocus, inputsRef, }) {
|
|
7
|
+
const blurFields = (0, react_1.useCallback)(() => {
|
|
8
|
+
inputsRef.current.forEach(input => input === null || input === void 0 ? void 0 : input.blur());
|
|
9
|
+
}, [inputsRef]);
|
|
10
|
+
// Фокус при загрузке
|
|
11
|
+
(0, react_1.useEffect)(() => {
|
|
12
|
+
if (withoutFocus || loading)
|
|
13
|
+
return;
|
|
14
|
+
requestAnimationFrame(() => {
|
|
15
|
+
moveFocus(0);
|
|
16
|
+
});
|
|
17
|
+
}, [loading, moveFocus, withoutFocus]);
|
|
18
|
+
// Фокус при ошибке (если withoutErrorFocus = false)
|
|
19
|
+
(0, react_1.useEffect)(() => {
|
|
20
|
+
var _a;
|
|
21
|
+
if (withoutErrorFocus)
|
|
22
|
+
return;
|
|
23
|
+
if (showEmptyChars) {
|
|
24
|
+
const idx = (0, utils_1.getCleanValue)(code).findIndex(c => c === '');
|
|
25
|
+
const focusIndex = idx >= 0 ? idx : codeLength - 1;
|
|
26
|
+
(_a = inputsRef.current[focusIndex]) === null || _a === void 0 ? void 0 : _a.focus();
|
|
27
|
+
}
|
|
28
|
+
}, [code, codeLength, incorrectCodeState, showEmptyChars, withoutErrorFocus, inputsRef]);
|
|
29
|
+
// Фокус при ошибке (если withoutErrorFocus = true)
|
|
30
|
+
(0, react_1.useEffect)(() => {
|
|
31
|
+
var _a;
|
|
32
|
+
if (incorrectCodeState) {
|
|
33
|
+
(_a = inputsRef.current[0]) === null || _a === void 0 ? void 0 : _a.focus();
|
|
34
|
+
}
|
|
35
|
+
}, [incorrectCodeState, inputsRef]);
|
|
36
|
+
// Блюр при ошибке (если withoutErrorFocus = true)
|
|
37
|
+
(0, react_1.useEffect)(() => {
|
|
38
|
+
if (!withoutErrorFocus)
|
|
39
|
+
return;
|
|
40
|
+
if (incorrectCodeState || showEmptyChars) {
|
|
41
|
+
blurFields();
|
|
42
|
+
}
|
|
43
|
+
}, [incorrectCodeState, showEmptyChars, withoutErrorFocus, blurFields]);
|
|
44
|
+
// Сброс кода при изменении длины
|
|
45
|
+
(0, react_1.useEffect)(() => {
|
|
46
|
+
setCode(Array(codeLength).fill(''));
|
|
47
|
+
}, [codeLength, setCode]);
|
|
48
|
+
// Обновление кода при изменении valueProp (только если valueProp не пустой)
|
|
49
|
+
(0, react_1.useEffect)(() => {
|
|
50
|
+
if (valueProp === null || valueProp === void 0 ? void 0 : valueProp.length) {
|
|
51
|
+
setCode(buildCodeArray(valueProp));
|
|
52
|
+
}
|
|
53
|
+
}, [buildCodeArray, setCode, valueProp]);
|
|
54
|
+
// Сброс кода при очистке valueProp
|
|
55
|
+
(0, react_1.useEffect)(() => {
|
|
56
|
+
if (!(valueProp === null || valueProp === void 0 ? void 0 : valueProp.length)) {
|
|
57
|
+
setCode(Array(codeLength).fill(''));
|
|
58
|
+
onChange === null || onChange === void 0 ? void 0 : onChange(utils_1.ZERO_WIDTH_SPACE.repeat(codeLength));
|
|
59
|
+
moveFocus(0);
|
|
60
|
+
}
|
|
61
|
+
}, [valueProp, codeLength, moveFocus, setCode, onChange]);
|
|
62
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { MutableRefObject } from 'react';
|
|
2
|
+
type UseFocusFields = {
|
|
3
|
+
inputsRef: MutableRefObject<(HTMLInputElement | null)[]>;
|
|
4
|
+
codeLength: number;
|
|
5
|
+
};
|
|
6
|
+
export declare function useFocusFields({ inputsRef, codeLength }: UseFocusFields): (newIndex: number) => void;
|
|
7
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useFocusFields = useFocusFields;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
function useFocusFields({ inputsRef, codeLength }) {
|
|
6
|
+
const focusInput = (0, react_1.useCallback)((index) => {
|
|
7
|
+
var _a;
|
|
8
|
+
(_a = inputsRef.current[index]) === null || _a === void 0 ? void 0 : _a.focus();
|
|
9
|
+
}, [inputsRef]);
|
|
10
|
+
const moveFocus = (0, react_1.useCallback)((newIndex) => {
|
|
11
|
+
if (newIndex >= 0 && newIndex < codeLength) {
|
|
12
|
+
focusInput(newIndex);
|
|
13
|
+
}
|
|
14
|
+
}, [codeLength, focusInput]);
|
|
15
|
+
return moveFocus;
|
|
16
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useStateWithRef = useStateWithRef;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
function useStateWithRef(initialValue) {
|
|
6
|
+
const [state, setState] = (0, react_1.useState)(initialValue);
|
|
7
|
+
const ref = (0, react_1.useRef)(state);
|
|
8
|
+
const setStateAndRef = (0, react_1.useCallback)((newValue) => {
|
|
9
|
+
if (typeof newValue === 'function') {
|
|
10
|
+
const updater = newValue;
|
|
11
|
+
const updatedValue = updater(ref.current);
|
|
12
|
+
setState(updatedValue);
|
|
13
|
+
ref.current = updatedValue;
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
setState(newValue);
|
|
17
|
+
ref.current = newValue;
|
|
18
|
+
}
|
|
19
|
+
}, []);
|
|
20
|
+
return [state, setStateAndRef, ref];
|
|
21
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './FieldCode';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./FieldCode"), exports);
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
.codeContainer{
|
|
2
|
+
display:flex;
|
|
3
|
+
justify-content:center;
|
|
4
|
+
}
|
|
5
|
+
.codeContainer[data-size=s]{
|
|
6
|
+
gap:6px;
|
|
7
|
+
}
|
|
8
|
+
.codeContainer[data-size=m]{
|
|
9
|
+
gap:8px;
|
|
10
|
+
}
|
|
11
|
+
.codeContainer[data-size=l]{
|
|
12
|
+
gap:12px;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.codeInputWithSpacing[data-size=s]{
|
|
16
|
+
margin-right:2px;
|
|
17
|
+
}
|
|
18
|
+
.codeInputWithSpacing[data-size=m]{
|
|
19
|
+
margin-right:8px;
|
|
20
|
+
}
|
|
21
|
+
.codeInputWithSpacing[data-size=l]{
|
|
22
|
+
margin-right:12px;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.codeInput{
|
|
26
|
+
width:100%;
|
|
27
|
+
}
|
|
28
|
+
.codeInput input{
|
|
29
|
+
text-align:center;
|
|
30
|
+
}
|
|
31
|
+
.codeInput input[data-size=s]{
|
|
32
|
+
font-size:16px;
|
|
33
|
+
line-height:22px;
|
|
34
|
+
}
|
|
35
|
+
.codeInput input[data-size=m]{
|
|
36
|
+
font-size:18px;
|
|
37
|
+
line-height:26px;
|
|
38
|
+
}
|
|
39
|
+
.codeInput input[data-size=l]{
|
|
40
|
+
font-size:20px;
|
|
41
|
+
line-height:28px;
|
|
42
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare const ZERO_WIDTH_SPACE = "\u200B";
|
|
2
|
+
export declare const getValidationState: ({ char, incorrectCodeState, showEmptyChars, }: {
|
|
3
|
+
char: string;
|
|
4
|
+
incorrectCodeState?: boolean;
|
|
5
|
+
showEmptyChars?: boolean;
|
|
6
|
+
}) => "error" | "default";
|
|
7
|
+
export declare const isNumberChar: (char: string) => boolean;
|
|
8
|
+
export declare const isStringCodeLength: (input: string, codeLength: number) => boolean;
|
|
9
|
+
export declare const isOutsideAutoComplete: (input: string, index: number, code: string[]) => boolean;
|
|
10
|
+
export declare const getPaddedValue: (value: string[]) => string[];
|
|
11
|
+
export declare const getCleanValue: (value: string[]) => string[];
|
|
12
|
+
export declare const isZeroWidthSpace: (value: string) => value is "";
|
|
13
|
+
export declare const generateRangeWithStep: (start: number, end: number) => number[];
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateRangeWithStep = exports.isZeroWidthSpace = exports.getCleanValue = exports.getPaddedValue = exports.isOutsideAutoComplete = exports.isStringCodeLength = exports.isNumberChar = exports.getValidationState = exports.ZERO_WIDTH_SPACE = void 0;
|
|
4
|
+
exports.ZERO_WIDTH_SPACE = '\u200B';
|
|
5
|
+
const getValidationState = ({ char, incorrectCodeState, showEmptyChars, }) => {
|
|
6
|
+
if (incorrectCodeState) {
|
|
7
|
+
return 'error';
|
|
8
|
+
}
|
|
9
|
+
if (!showEmptyChars) {
|
|
10
|
+
return 'default';
|
|
11
|
+
}
|
|
12
|
+
return char && char !== exports.ZERO_WIDTH_SPACE ? 'default' : 'error';
|
|
13
|
+
};
|
|
14
|
+
exports.getValidationState = getValidationState;
|
|
15
|
+
const isStringCode = (input) => new RegExp(`^\\d+$`).test(input);
|
|
16
|
+
const isNumberChar = (char) => /^\d$/.test(char);
|
|
17
|
+
exports.isNumberChar = isNumberChar;
|
|
18
|
+
const isStringCodeLength = (input, codeLength) => new RegExp(`^\\d{${codeLength}}$`).test(input);
|
|
19
|
+
exports.isStringCodeLength = isStringCodeLength;
|
|
20
|
+
const isOutsideAutoComplete = (input, index, code) => input.length > 1 && isStringCode(input) && input.at(0) === code.at(index);
|
|
21
|
+
exports.isOutsideAutoComplete = isOutsideAutoComplete;
|
|
22
|
+
const getPaddedValue = (value) => value.map(char => (char === '' ? exports.ZERO_WIDTH_SPACE : char));
|
|
23
|
+
exports.getPaddedValue = getPaddedValue;
|
|
24
|
+
const getCleanValue = (value) => value.map(char => (char !== exports.ZERO_WIDTH_SPACE ? char : ''));
|
|
25
|
+
exports.getCleanValue = getCleanValue;
|
|
26
|
+
const isZeroWidthSpace = (value) => value === exports.ZERO_WIDTH_SPACE;
|
|
27
|
+
exports.isZeroWidthSpace = isZeroWidthSpace;
|
|
28
|
+
const generateRangeWithStep = (start, end) => {
|
|
29
|
+
const step = start <= end ? 1 : -1;
|
|
30
|
+
const length = Math.abs(end - start) + 1;
|
|
31
|
+
return Array.from({ length }, (_, i) => start + i * step);
|
|
32
|
+
};
|
|
33
|
+
exports.generateRangeWithStep = generateRangeWithStep;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { FieldDecoratorProps } from '@snack-uikit/fields';
|
|
2
|
+
export type FieldCodeRef = {
|
|
3
|
+
blur: () => void;
|
|
4
|
+
};
|
|
5
|
+
export type FieldCodeProps = {
|
|
6
|
+
/** Количество цифр в коде */
|
|
7
|
+
codeLength: number;
|
|
8
|
+
className?: string;
|
|
9
|
+
/** Подсветить пустые символы кода */
|
|
10
|
+
showEmptyChars?: boolean;
|
|
11
|
+
/** отправка кода в процессе */
|
|
12
|
+
loading?: boolean;
|
|
13
|
+
/** Подсветить все символы кода */
|
|
14
|
+
incorrectCodeState?: boolean;
|
|
15
|
+
/** Колбек изменения значения */
|
|
16
|
+
onChange?: (code: string) => void;
|
|
17
|
+
value?: string;
|
|
18
|
+
/** Колбек достижения ввода всех символов кода */
|
|
19
|
+
onComplete?: (code: string) => void;
|
|
20
|
+
/** Отключить автофокус */
|
|
21
|
+
withoutFocus?: boolean;
|
|
22
|
+
fieldClassName?: string;
|
|
23
|
+
/** Отключить автофокус при ошибке (по умолчанию false) */
|
|
24
|
+
withoutErrorFocus?: boolean;
|
|
25
|
+
/** Позиции, после которых нужно вставить пробел (индексы символов, после которых будет пробел) */
|
|
26
|
+
spacing?: number[];
|
|
27
|
+
} & Pick<FieldDecoratorProps, 'size' | 'disabled' | 'readonly'>;
|
|
28
|
+
export declare const FieldCode: import("react").ForwardRefExoticComponent<{
|
|
29
|
+
/** Количество цифр в коде */
|
|
30
|
+
codeLength: number;
|
|
31
|
+
className?: string;
|
|
32
|
+
/** Подсветить пустые символы кода */
|
|
33
|
+
showEmptyChars?: boolean;
|
|
34
|
+
/** отправка кода в процессе */
|
|
35
|
+
loading?: boolean;
|
|
36
|
+
/** Подсветить все символы кода */
|
|
37
|
+
incorrectCodeState?: boolean;
|
|
38
|
+
/** Колбек изменения значения */
|
|
39
|
+
onChange?: (code: string) => void;
|
|
40
|
+
value?: string;
|
|
41
|
+
/** Колбек достижения ввода всех символов кода */
|
|
42
|
+
onComplete?: (code: string) => void;
|
|
43
|
+
/** Отключить автофокус */
|
|
44
|
+
withoutFocus?: boolean;
|
|
45
|
+
fieldClassName?: string;
|
|
46
|
+
/** Отключить автофокус при ошибке (по умолчанию false) */
|
|
47
|
+
withoutErrorFocus?: boolean;
|
|
48
|
+
/** Позиции, после которых нужно вставить пробел (индексы символов, после которых будет пробел) */
|
|
49
|
+
spacing?: number[];
|
|
50
|
+
} & Pick<FieldDecoratorProps, "size" | "disabled" | "readonly"> & import("react").RefAttributes<FieldCodeRef>>;
|