@cloud-ru/uikit-product-fields-predefined 2.4.8-preview-df188565.0 → 2.4.8-preview-611dfd0c.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 CHANGED
@@ -355,13 +355,15 @@
355
355
  ### Props
356
356
  | name | type | default value | description |
357
357
  |------|------|---------------|-------------|
358
- | codeLength* | `number` | - | Количество цифр в коде |
358
+ | codeLength* | `number` | - | Количество цифр в коде (ожидается целое ≥ 1) |
359
359
  | className | `string` | - | CSS-класс компонента |
360
360
  | cellClassName | `string` | - | CSS-класс ячейки кода |
361
361
  | spacing | `number[]` | - | Позиции, после которых нужно вставить пробел (индексы символов, после которых будет пробел) |
362
- | showEmptyChars | `boolean` | - | Подсветить пустые символы кода |
362
+ | showEmptyChars | `boolean` | - | Подсветить пустые символы кода. Если не задано, совпадает с `validate`. |
363
363
  | resendCode | `ResendCodeProps` | - | Компонент отправки нового кода |
364
364
  | focusEffects | `readonly FieldCodeFocusEffect[]` | - | Сценарии автофокуса; по умолчанию — первая ячейка при монтировании и после сброса (см. `FieldCodeFocusEffect`) |
365
+ | validate | `boolean` | - | Встроенная валидация: пустое значение и неполный код |
366
+ | invalidCode | `boolean` | - | Показать сообщение «неверный код» (учитывается, если нет своего `error` и нет ошибки валидации) |
365
367
  | value | `string` | - | Значение кода |
366
368
  | onChange | `(code: string) => void` | - | Колбек изменения значения |
367
369
  | onComplete | `(code: string) => void` | - | Колбек достижения ввода всех символов кода |
@@ -10,33 +10,29 @@ export type FieldCodeRef = {
10
10
  /** Сбросить значение кода */
11
11
  resetCode: () => void;
12
12
  };
13
- export type FieldCodeProps = {
13
+ /** Собственные пропсы `FieldCode` */
14
+ export type FieldCodeOwnProps = {
14
15
  /** CSS-класс компонента */
15
16
  className?: string;
16
17
  /** CSS-класс ячейки кода */
17
18
  cellClassName?: string;
18
19
  /** Позиции, после которых нужно вставить пробел (индексы символов, после которых будет пробел) */
19
20
  spacing?: number[];
20
- /** Подсветить пустые символы кода */
21
+ /**
22
+ * Подсветить пустые символы кода.
23
+ * Если не задано, совпадает с `validate`.
24
+ */
21
25
  showEmptyChars?: boolean;
22
26
  /** Компонент отправки нового кода */
23
27
  resendCode?: ResendCodeProps;
24
28
  /** Сценарии автофокуса; по умолчанию — первая ячейка при монтировании и после сброса (см. `FieldCodeFocusEffect`) */
25
29
  focusEffects?: readonly FieldCodeFocusEffect[];
26
- } & Omit<UseCodeInputParams, 'moveFocus'> & Pick<FieldDecoratorProps, 'size' | 'disabled' | 'label' | 'error' | 'data-test-id'>;
27
- export declare const FieldCode: import("react").ForwardRefExoticComponent<{
28
- /** CSS-класс компонента */
29
- className?: string;
30
- /** CSS-класс ячейки кода */
31
- cellClassName?: string;
32
- /** Позиции, после которых нужно вставить пробел (индексы символов, после которых будет пробел) */
33
- spacing?: number[];
34
- /** Подсветить пустые символы кода */
35
- showEmptyChars?: boolean;
36
- /** Компонент отправки нового кода */
37
- resendCode?: ResendCodeProps;
38
- /** Сценарии автофокуса; по умолчанию — первая ячейка при монтировании и после сброса (см. `FieldCodeFocusEffect`) */
39
- focusEffects?: readonly FieldCodeFocusEffect[];
40
- } & Omit<UseCodeInputParams, "moveFocus"> & Pick<FieldDecoratorProps, "size" | "label" | "error" | "disabled" | "data-test-id"> & import("react").RefAttributes<FieldCodeRef>>;
30
+ /** Встроенная валидация: пустое значение и неполный код */
31
+ validate?: boolean;
32
+ /** Показать сообщение «неверный код» (учитывается, если нет своего `error` и нет ошибки валидации) */
33
+ invalidCode?: boolean;
34
+ };
35
+ export type FieldCodeProps = FieldCodeOwnProps & Omit<UseCodeInputParams, 'moveFocus'> & Pick<FieldDecoratorProps, 'size' | 'disabled' | 'label' | 'error' | 'data-test-id'>;
36
+ export declare const FieldCode: import("react").ForwardRefExoticComponent<FieldCodeOwnProps & Omit<UseCodeInputParams, "moveFocus"> & Pick<FieldDecoratorProps, "size" | "label" | "error" | "disabled" | "data-test-id"> & import("react").RefAttributes<FieldCodeRef>>;
41
37
  export type { FieldCodeFocusEffect } from './constants';
42
38
  export { FIELD_CODE_DEFAULT_FOCUS_EFFECTS } from './constants';
@@ -15,7 +15,8 @@ const styles_module_scss_1 = __importDefault(require('./styles.module.css'));
15
15
  const utils_1 = require("./utils");
16
16
  exports.FieldCode = (0, react_1.forwardRef)(function FieldCode(props, ref) {
17
17
  var _a;
18
- const { codeLength, className, cellClassName, value, onChange, spacing, onComplete, size, disabled, label, error, showEmptyChars, resendCode, focusEffects = constants_1.FIELD_CODE_DEFAULT_FOCUS_EFFECTS, 'data-test-id': dataTestId, } = props;
18
+ const { codeLength, className, cellClassName, value, onChange, spacing, onComplete, size, disabled, label, error, validate = false, invalidCode = false, showEmptyChars: showEmptyCharsProp, resendCode, focusEffects = constants_1.FIELD_CODE_DEFAULT_FOCUS_EFFECTS, 'data-test-id': dataTestId, } = props;
19
+ const showEmptyChars = showEmptyCharsProp !== null && showEmptyCharsProp !== void 0 ? showEmptyCharsProp : validate;
19
20
  const { inputsRef, moveFocus, blurFields } = (0, hooks_1.useFocusCell)(codeLength);
20
21
  const { code, cellHandlers, onChangeCode } = (0, hooks_1.useCodeInput)({ value, onChange, codeLength, moveFocus, onComplete });
21
22
  const { resetCode } = (0, hooks_1.useFieldHelpers)({
@@ -31,17 +32,24 @@ exports.FieldCode = (0, react_1.forwardRef)(function FieldCode(props, ref) {
31
32
  blurFields,
32
33
  resetCode,
33
34
  }), [moveFocus, blurFields, resetCode]);
35
+ const resolvedError = (0, hooks_1.useFieldCodeError)({
36
+ validate,
37
+ value,
38
+ codeLength,
39
+ invalidCode,
40
+ error,
41
+ });
34
42
  const resolvedDecoratorProps = {
35
43
  label,
36
44
  disabled,
37
45
  size,
38
- error,
46
+ error: resolvedError,
39
47
  };
40
48
  return ((0, jsx_runtime_1.jsxs)("div", Object.assign({ className: (0, classnames_1.default)(styles_module_scss_1.default.fieldCode, className) }, (dataTestId ? { 'data-test-id': dataTestId } : undefined), { children: [(0, jsx_runtime_1.jsx)(fields_1.FieldDecorator, Object.assign({}, resolvedDecoratorProps, { 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)(components_1.Cell, { ref: inputRef => {
41
49
  if (inputRef) {
42
50
  inputsRef.current[index] = inputRef;
43
51
  }
44
- }, className: (0, classnames_1.default)((spacing === null || spacing === void 0 ? void 0 : spacing.includes(index)) && styles_module_scss_1.default.cellSpacing, cellClassName), size: size, value: char, disabled: disabled, autoComplete: index === 0 ? 'one-time-code' : undefined, onKeyDown: e => cellHandlers.onKeyDown(e, index), onPaste: cellHandlers.onPaste, onChange: e => cellHandlers.onChange(e, index), validationState: (0, utils_1.getCellValidationState)(char, showEmptyChars, Boolean(error)) }, index))) }) })), resendCode ? (0, jsx_runtime_1.jsx)(components_1.ResendCode, Object.assign({}, resendCode, { size: (_a = resendCode.size) !== null && _a !== void 0 ? _a : size })) : null] })));
52
+ }, className: (0, classnames_1.default)((spacing === null || spacing === void 0 ? void 0 : spacing.includes(index)) && styles_module_scss_1.default.cellSpacing, cellClassName), size: size, value: char, disabled: disabled, autoComplete: index === 0 ? 'one-time-code' : undefined, onKeyDown: e => cellHandlers.onKeyDown(e, index), onPaste: cellHandlers.onPaste, onChange: e => cellHandlers.onChange(e, index), validationState: (0, utils_1.getCellValidationState)(char, showEmptyChars, Boolean(resolvedError)) }, index))) }) })), resendCode ? (0, jsx_runtime_1.jsx)(components_1.ResendCode, Object.assign({}, resendCode, { size: (_a = resendCode.size) !== null && _a !== void 0 ? _a : size })) : null] })));
45
53
  });
46
54
  var constants_2 = require("./constants");
47
55
  Object.defineProperty(exports, "FIELD_CODE_DEFAULT_FOCUS_EFFECTS", { enumerable: true, get: function () { return constants_2.FIELD_CODE_DEFAULT_FOCUS_EFFECTS; } });
@@ -1,3 +1,4 @@
1
1
  export * from './useCodeInput';
2
+ export * from './useFieldCodeError';
2
3
  export * from './useFocusCell';
3
4
  export * from './useFieldHelpers';
@@ -15,5 +15,6 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./useCodeInput"), exports);
18
+ __exportStar(require("./useFieldCodeError"), exports);
18
19
  __exportStar(require("./useFocusCell"), exports);
19
20
  __exportStar(require("./useFieldHelpers"), exports);
@@ -1,6 +1,6 @@
1
1
  import { ClipboardEvent, KeyboardEvent } from 'react';
2
2
  export type UseCodeInputParams = {
3
- /** Количество цифр в коде */
3
+ /** Количество цифр в коде (ожидается целое ≥ 1) */
4
4
  codeLength: number;
5
5
  /** Значение кода */
6
6
  value?: string;
@@ -0,0 +1,14 @@
1
+ /** Параметры хука разрешения текста ошибки: `error` > валидация (`validate`) > `invalidCode`. */
2
+ export type UseFieldCodeErrorParams = {
3
+ /** Включить встроенную валидацию (пустое значение и неполный код) */
4
+ validate?: boolean;
5
+ /** Значение кода */
6
+ value?: string;
7
+ /** Ожидаемая длина кода */
8
+ codeLength: number;
9
+ /** Показать сообщение о неверном коде */
10
+ invalidCode?: boolean;
11
+ /** Ошибка снаружи */
12
+ error?: string;
13
+ };
14
+ export declare function useFieldCodeError(params: UseFieldCodeErrorParams): string | undefined;
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useFieldCodeError = useFieldCodeError;
4
+ const react_1 = require("react");
5
+ const uikit_product_locale_1 = require("@cloud-ru/uikit-product-locale");
6
+ const utils_1 = require("../utils");
7
+ function useFieldCodeError(params) {
8
+ var _a;
9
+ const { validate = false, value, codeLength, invalidCode = false, error } = params;
10
+ const { t } = (0, uikit_product_locale_1.useLocale)('FieldsPredefined');
11
+ const validationMessage = (0, react_1.useMemo)(() => {
12
+ if (!validate) {
13
+ return undefined;
14
+ }
15
+ const digits = (value !== null && value !== void 0 ? value : '').split('').filter(utils_1.isNumberChar).join('');
16
+ if (digits.length === 0) {
17
+ return t('FieldCode.required');
18
+ }
19
+ if (digits.length < codeLength) {
20
+ return t('FieldCode.minLength', { count: codeLength });
21
+ }
22
+ return undefined;
23
+ }, [codeLength, t, validate, value]);
24
+ const invalidCodeMessage = invalidCode ? t('FieldCode.invalidCode') : undefined;
25
+ return (_a = error !== null && error !== void 0 ? error : validationMessage) !== null && _a !== void 0 ? _a : invalidCodeMessage;
26
+ }
@@ -10,33 +10,29 @@ export type FieldCodeRef = {
10
10
  /** Сбросить значение кода */
11
11
  resetCode: () => void;
12
12
  };
13
- export type FieldCodeProps = {
13
+ /** Собственные пропсы `FieldCode` */
14
+ export type FieldCodeOwnProps = {
14
15
  /** CSS-класс компонента */
15
16
  className?: string;
16
17
  /** CSS-класс ячейки кода */
17
18
  cellClassName?: string;
18
19
  /** Позиции, после которых нужно вставить пробел (индексы символов, после которых будет пробел) */
19
20
  spacing?: number[];
20
- /** Подсветить пустые символы кода */
21
+ /**
22
+ * Подсветить пустые символы кода.
23
+ * Если не задано, совпадает с `validate`.
24
+ */
21
25
  showEmptyChars?: boolean;
22
26
  /** Компонент отправки нового кода */
23
27
  resendCode?: ResendCodeProps;
24
28
  /** Сценарии автофокуса; по умолчанию — первая ячейка при монтировании и после сброса (см. `FieldCodeFocusEffect`) */
25
29
  focusEffects?: readonly FieldCodeFocusEffect[];
26
- } & Omit<UseCodeInputParams, 'moveFocus'> & Pick<FieldDecoratorProps, 'size' | 'disabled' | 'label' | 'error' | 'data-test-id'>;
27
- export declare const FieldCode: import("react").ForwardRefExoticComponent<{
28
- /** CSS-класс компонента */
29
- className?: string;
30
- /** CSS-класс ячейки кода */
31
- cellClassName?: string;
32
- /** Позиции, после которых нужно вставить пробел (индексы символов, после которых будет пробел) */
33
- spacing?: number[];
34
- /** Подсветить пустые символы кода */
35
- showEmptyChars?: boolean;
36
- /** Компонент отправки нового кода */
37
- resendCode?: ResendCodeProps;
38
- /** Сценарии автофокуса; по умолчанию — первая ячейка при монтировании и после сброса (см. `FieldCodeFocusEffect`) */
39
- focusEffects?: readonly FieldCodeFocusEffect[];
40
- } & Omit<UseCodeInputParams, "moveFocus"> & Pick<FieldDecoratorProps, "size" | "label" | "error" | "disabled" | "data-test-id"> & import("react").RefAttributes<FieldCodeRef>>;
30
+ /** Встроенная валидация: пустое значение и неполный код */
31
+ validate?: boolean;
32
+ /** Показать сообщение «неверный код» (учитывается, если нет своего `error` и нет ошибки валидации) */
33
+ invalidCode?: boolean;
34
+ };
35
+ export type FieldCodeProps = FieldCodeOwnProps & Omit<UseCodeInputParams, 'moveFocus'> & Pick<FieldDecoratorProps, 'size' | 'disabled' | 'label' | 'error' | 'data-test-id'>;
36
+ export declare const FieldCode: import("react").ForwardRefExoticComponent<FieldCodeOwnProps & Omit<UseCodeInputParams, "moveFocus"> & Pick<FieldDecoratorProps, "size" | "label" | "error" | "disabled" | "data-test-id"> & import("react").RefAttributes<FieldCodeRef>>;
41
37
  export type { FieldCodeFocusEffect } from './constants';
42
38
  export { FIELD_CODE_DEFAULT_FOCUS_EFFECTS } from './constants';
@@ -4,12 +4,13 @@ import { forwardRef, useImperativeHandle } from 'react';
4
4
  import { FieldDecorator } from '@snack-uikit/fields';
5
5
  import { Cell, ResendCode } from './components';
6
6
  import { FIELD_CODE_DEFAULT_FOCUS_EFFECTS } from './constants';
7
- import { useCodeInput, useFieldHelpers, useFocusCell } from './hooks';
7
+ import { useCodeInput, useFieldCodeError, useFieldHelpers, useFocusCell } from './hooks';
8
8
  import styles from './styles.module.css';
9
9
  import { getCellValidationState } from './utils';
10
10
  export const FieldCode = forwardRef(function FieldCode(props, ref) {
11
11
  var _a;
12
- const { codeLength, className, cellClassName, value, onChange, spacing, onComplete, size, disabled, label, error, showEmptyChars, resendCode, focusEffects = FIELD_CODE_DEFAULT_FOCUS_EFFECTS, 'data-test-id': dataTestId, } = props;
12
+ const { codeLength, className, cellClassName, value, onChange, spacing, onComplete, size, disabled, label, error, validate = false, invalidCode = false, showEmptyChars: showEmptyCharsProp, resendCode, focusEffects = FIELD_CODE_DEFAULT_FOCUS_EFFECTS, 'data-test-id': dataTestId, } = props;
13
+ const showEmptyChars = showEmptyCharsProp !== null && showEmptyCharsProp !== void 0 ? showEmptyCharsProp : validate;
13
14
  const { inputsRef, moveFocus, blurFields } = useFocusCell(codeLength);
14
15
  const { code, cellHandlers, onChangeCode } = useCodeInput({ value, onChange, codeLength, moveFocus, onComplete });
15
16
  const { resetCode } = useFieldHelpers({
@@ -25,16 +26,23 @@ export const FieldCode = forwardRef(function FieldCode(props, ref) {
25
26
  blurFields,
26
27
  resetCode,
27
28
  }), [moveFocus, blurFields, resetCode]);
29
+ const resolvedError = useFieldCodeError({
30
+ validate,
31
+ value,
32
+ codeLength,
33
+ invalidCode,
34
+ error,
35
+ });
28
36
  const resolvedDecoratorProps = {
29
37
  label,
30
38
  disabled,
31
39
  size,
32
- error,
40
+ error: resolvedError,
33
41
  };
34
42
  return (_jsxs("div", Object.assign({ className: cn(styles.fieldCode, className) }, (dataTestId ? { 'data-test-id': dataTestId } : undefined), { children: [_jsx(FieldDecorator, Object.assign({}, resolvedDecoratorProps, { children: _jsx("div", { className: styles.codeContainer, "data-size": size, children: code.map((char, index) => (_jsx(Cell, { ref: inputRef => {
35
43
  if (inputRef) {
36
44
  inputsRef.current[index] = inputRef;
37
45
  }
38
- }, className: cn((spacing === null || spacing === void 0 ? void 0 : spacing.includes(index)) && styles.cellSpacing, cellClassName), size: size, value: char, disabled: disabled, autoComplete: index === 0 ? 'one-time-code' : undefined, onKeyDown: e => cellHandlers.onKeyDown(e, index), onPaste: cellHandlers.onPaste, onChange: e => cellHandlers.onChange(e, index), validationState: getCellValidationState(char, showEmptyChars, Boolean(error)) }, index))) }) })), resendCode ? _jsx(ResendCode, Object.assign({}, resendCode, { size: (_a = resendCode.size) !== null && _a !== void 0 ? _a : size })) : null] })));
46
+ }, className: cn((spacing === null || spacing === void 0 ? void 0 : spacing.includes(index)) && styles.cellSpacing, cellClassName), size: size, value: char, disabled: disabled, autoComplete: index === 0 ? 'one-time-code' : undefined, onKeyDown: e => cellHandlers.onKeyDown(e, index), onPaste: cellHandlers.onPaste, onChange: e => cellHandlers.onChange(e, index), validationState: getCellValidationState(char, showEmptyChars, Boolean(resolvedError)) }, index))) }) })), resendCode ? _jsx(ResendCode, Object.assign({}, resendCode, { size: (_a = resendCode.size) !== null && _a !== void 0 ? _a : size })) : null] })));
39
47
  });
40
48
  export { FIELD_CODE_DEFAULT_FOCUS_EFFECTS } from './constants';
@@ -1,3 +1,4 @@
1
1
  export * from './useCodeInput';
2
+ export * from './useFieldCodeError';
2
3
  export * from './useFocusCell';
3
4
  export * from './useFieldHelpers';
@@ -1,3 +1,4 @@
1
1
  export * from './useCodeInput';
2
+ export * from './useFieldCodeError';
2
3
  export * from './useFocusCell';
3
4
  export * from './useFieldHelpers';
@@ -1,6 +1,6 @@
1
1
  import { ClipboardEvent, KeyboardEvent } from 'react';
2
2
  export type UseCodeInputParams = {
3
- /** Количество цифр в коде */
3
+ /** Количество цифр в коде (ожидается целое ≥ 1) */
4
4
  codeLength: number;
5
5
  /** Значение кода */
6
6
  value?: string;
@@ -0,0 +1,14 @@
1
+ /** Параметры хука разрешения текста ошибки: `error` > валидация (`validate`) > `invalidCode`. */
2
+ export type UseFieldCodeErrorParams = {
3
+ /** Включить встроенную валидацию (пустое значение и неполный код) */
4
+ validate?: boolean;
5
+ /** Значение кода */
6
+ value?: string;
7
+ /** Ожидаемая длина кода */
8
+ codeLength: number;
9
+ /** Показать сообщение о неверном коде */
10
+ invalidCode?: boolean;
11
+ /** Ошибка снаружи */
12
+ error?: string;
13
+ };
14
+ export declare function useFieldCodeError(params: UseFieldCodeErrorParams): string | undefined;
@@ -0,0 +1,23 @@
1
+ import { useMemo } from 'react';
2
+ import { useLocale } from '@cloud-ru/uikit-product-locale';
3
+ import { isNumberChar } from '../utils';
4
+ export function useFieldCodeError(params) {
5
+ var _a;
6
+ const { validate = false, value, codeLength, invalidCode = false, error } = params;
7
+ const { t } = useLocale('FieldsPredefined');
8
+ const validationMessage = useMemo(() => {
9
+ if (!validate) {
10
+ return undefined;
11
+ }
12
+ const digits = (value !== null && value !== void 0 ? value : '').split('').filter(isNumberChar).join('');
13
+ if (digits.length === 0) {
14
+ return t('FieldCode.required');
15
+ }
16
+ if (digits.length < codeLength) {
17
+ return t('FieldCode.minLength', { count: codeLength });
18
+ }
19
+ return undefined;
20
+ }, [codeLength, t, validate, value]);
21
+ const invalidCodeMessage = invalidCode ? t('FieldCode.invalidCode') : undefined;
22
+ return (_a = error !== null && error !== void 0 ? error : validationMessage) !== null && _a !== void 0 ? _a : invalidCodeMessage;
23
+ }