@cloud-ru/uikit-product-fields-predefined 3.0.3-preview-7cd1981e.0 → 3.0.3-preview-30fdf48b.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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@cloud-ru/uikit-product-fields-predefined",
3
3
  "title": "Fields Predefined",
4
- "version": "3.0.3-preview-7cd1981e.0",
4
+ "version": "3.0.3-preview-30fdf48b.0",
5
5
  "sideEffects": [
6
6
  "*.css",
7
7
  "*.woff",
@@ -62,5 +62,5 @@
62
62
  "react-hook-form": ">=7.51.0",
63
63
  "yup": ">=0.32.0"
64
64
  },
65
- "gitHead": "d449ad65a8304ef339fdf8679b1a1efc5a58d3f4"
65
+ "gitHead": "e099ff0e265dbde19b4985b2766c327517fd9836"
66
66
  }
@@ -1,10 +1,11 @@
1
1
  import cn from 'classnames';
2
2
  import { forwardRef, useImperativeHandle } from 'react';
3
3
 
4
+ import { WithLayoutType } from '@cloud-ru/uikit-product-utils';
4
5
  import { FieldDecorator, FieldDecoratorProps } from '@snack-uikit/fields';
5
6
 
6
7
  import { Cell, ResendCode, type ResendCodeProps } from './components';
7
- import { useCodeInput, UseCodeInputParams, useFieldHelpers, useFocusCell } from './hooks';
8
+ import { useCodeInput, UseCodeInputParams, useFieldCodeOverflow, useFieldHelpers, useFocusCell } from './hooks';
8
9
  import styles from './styles.module.scss';
9
10
  import { getCellValidationState } from './utils';
10
11
 
@@ -18,7 +19,7 @@ export type FieldCodeRef = {
18
19
  };
19
20
 
20
21
  /** Собственные пропсы `FieldCode` */
21
- export type FieldCodeOwnProps = {
22
+ export type FieldCodeOwnProps = WithLayoutType<{
22
23
  /** CSS-класс компонента */
23
24
  className?: string;
24
25
  /** CSS-класс ячейки кода */
@@ -29,13 +30,11 @@ export type FieldCodeOwnProps = {
29
30
  showEmptyChars?: boolean;
30
31
  /** Компонент отправки нового кода */
31
32
  resendCode?: ResendCodeProps;
32
- /** Отключить автофокус (при монтировании, сбросе и при ошибке валидации) */
33
- isMobile: boolean;
34
33
  /** Сообщение при неверном коде, если не передан свой `error` */
35
34
  invalidCode?: string;
36
35
  /** Растягивать ячейки на всю доступную ширину; иначе фиксированная ширина по `size` */
37
36
  stretchCells?: boolean;
38
- };
37
+ }>;
39
38
 
40
39
  export type FieldCodeProps = FieldCodeOwnProps &
41
40
  Omit<UseCodeInputParams, 'moveFocus'> &
@@ -57,11 +56,13 @@ export const FieldCode = forwardRef<FieldCodeRef, FieldCodeProps>(function Field
57
56
  invalidCode,
58
57
  showEmptyChars,
59
58
  resendCode,
60
- isMobile,
59
+ layoutType,
61
60
  stretchCells = false,
62
61
  'data-test-id': dataTestId,
63
62
  } = props;
64
63
 
64
+ const isMobile = layoutType === 'mobile';
65
+
65
66
  const { inputsRef, moveFocus, blurFields } = useFocusCell(codeLength);
66
67
  const { code, cellHandlers, onChangeCode } = useCodeInput({ value, onChange, codeLength, moveFocus, onComplete });
67
68
  const { resetCode } = useFieldHelpers({
@@ -73,6 +74,8 @@ export const FieldCode = forwardRef<FieldCodeRef, FieldCodeProps>(function Field
73
74
  codeLength,
74
75
  });
75
76
 
77
+ const { rootRef, codeContainerRef, hasOverflow } = useFieldCodeOverflow();
78
+
76
79
  useImperativeHandle(
77
80
  ref,
78
81
  () => ({
@@ -94,12 +97,18 @@ export const FieldCode = forwardRef<FieldCodeRef, FieldCodeProps>(function Field
94
97
 
95
98
  return (
96
99
  <div
97
- className={cn(styles.fieldCode, className)}
100
+ ref={rootRef}
101
+ className={cn(styles.fieldCode, hasOverflow && styles.fieldCodeScrollable, className)}
98
102
  data-stretch-cells={stretchCells || undefined}
99
103
  {...(dataTestId ? { 'data-test-id': dataTestId } : undefined)}
100
104
  >
101
- <FieldDecorator className={styles.fieldDecorator} {...resolvedDecoratorProps}>
102
- <div className={styles.codeContainer} data-size={size} data-stretch-cells={stretchCells || undefined}>
105
+ <FieldDecorator className={cn(!hasOverflow && styles.fieldDecorator)} {...resolvedDecoratorProps}>
106
+ <div
107
+ ref={codeContainerRef}
108
+ className={styles.codeContainer}
109
+ data-size={size}
110
+ data-stretch-cells={stretchCells || undefined}
111
+ >
103
112
  {code.map((char, index) => (
104
113
  <Cell
105
114
  ref={inputRef => {
@@ -1,4 +1,5 @@
1
1
  export * from './useCodeInput';
2
+ export * from './useFieldCodeOverflow';
2
3
  export * from './useFieldCodeValidate';
3
4
  export * from './useFocusCell';
4
5
  export * from './useFieldHelpers';
@@ -0,0 +1,54 @@
1
+ import { useCallback, useRef, useState } from 'react';
2
+
3
+ import { useLayoutEffect } from '@snack-uikit/utils';
4
+
5
+ function hasCodeOverflow(root: HTMLElement, codeContainer: HTMLElement): boolean {
6
+ return codeContainer.scrollWidth > root.clientWidth;
7
+ }
8
+
9
+ /** Fallback на крайний случай: включает горизонтальный скролл, если ряд ячеек шире контейнера. */
10
+ export function useFieldCodeOverflow() {
11
+ const [hasOverflow, setHasOverflow] = useState(false);
12
+
13
+ const rootRef = useRef<HTMLDivElement>(null);
14
+ const codeContainerRef = useRef<HTMLDivElement>(null);
15
+
16
+ const measure = useCallback(() => {
17
+ const root = rootRef.current;
18
+ const codeContainer = codeContainerRef.current;
19
+
20
+ if (!root || !codeContainer) {
21
+ return;
22
+ }
23
+
24
+ setHasOverflow(prev => {
25
+ const next = hasCodeOverflow(root, codeContainer);
26
+ return prev === next ? prev : next;
27
+ });
28
+ }, []);
29
+
30
+ useLayoutEffect(() => {
31
+ const root = rootRef.current;
32
+ const codeContainer = codeContainerRef.current;
33
+
34
+ if (!root || !codeContainer) {
35
+ return;
36
+ }
37
+
38
+ measure();
39
+
40
+ const resizeObserver = new ResizeObserver(measure);
41
+ resizeObserver.observe(root);
42
+ resizeObserver.observe(codeContainer);
43
+
44
+ const mutationObserver = new MutationObserver(measure);
45
+ mutationObserver.observe(codeContainer, { childList: true });
46
+
47
+ return () => {
48
+ resizeObserver.disconnect();
49
+ mutationObserver.disconnect();
50
+ };
51
+ }, [measure]);
52
+
53
+ return { rootRef, codeContainerRef, hasOverflow };
54
+ }
@@ -18,13 +18,17 @@ $container-gaps: (
18
18
  }
19
19
  }
20
20
 
21
+ .fieldCodeScrollable {
22
+ overflow-x: auto;
23
+ }
24
+
21
25
  .fieldDecorator {
22
26
  width: unset;
23
27
  }
24
28
 
25
29
  .codeContainer {
26
30
  display: flex;
27
- justify-content: center;
31
+ justify-content: safe center;
28
32
 
29
33
  @each $size, $gap in $container-gaps {
30
34
  &[data-size='#{$size}'] {