@cloud-ru/uikit-product-fields-predefined 2.4.8-preview-5e39e65e.0 → 3.0.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.
Files changed (187) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +12 -326
  3. package/dist/cjs/components/index.d.ts +0 -2
  4. package/dist/cjs/components/index.js +0 -2
  5. package/dist/esm/components/index.d.ts +0 -2
  6. package/dist/esm/components/index.js +0 -2
  7. package/dist/tsconfig.cjs.tsbuildinfo +1 -1
  8. package/dist/tsconfig.esm.tsbuildinfo +1 -1
  9. package/package.json +3 -9
  10. package/src/components/index.ts +0 -2
  11. package/dist/cjs/components/FieldAi/FieldAi.d.ts +0 -24
  12. package/dist/cjs/components/FieldAi/FieldAi.js +0 -92
  13. package/dist/cjs/components/FieldAi/components/AlertButton/AlertButton.d.ts +0 -5
  14. package/dist/cjs/components/FieldAi/components/AlertButton/AlertButton.js +0 -12
  15. package/dist/cjs/components/FieldAi/components/AlertButton/index.d.ts +0 -1
  16. package/dist/cjs/components/FieldAi/components/AlertButton/index.js +0 -17
  17. package/dist/cjs/components/FieldAi/components/AlertButton/styles.module.css +0 -17
  18. package/dist/cjs/components/FieldAi/components/CheckItem/CheckItem.d.ts +0 -9
  19. package/dist/cjs/components/FieldAi/components/CheckItem/CheckItem.js +0 -28
  20. package/dist/cjs/components/FieldAi/components/CheckItem/index.d.ts +0 -1
  21. package/dist/cjs/components/FieldAi/components/CheckItem/index.js +0 -17
  22. package/dist/cjs/components/FieldAi/components/CheckItem/styles.module.css +0 -36
  23. package/dist/cjs/components/FieldAi/components/MobileFieldAi/MobileFieldAi.d.ts +0 -5
  24. package/dist/cjs/components/FieldAi/components/MobileFieldAi/MobileFieldAi.js +0 -31
  25. package/dist/cjs/components/FieldAi/components/MobileFieldAi/index.d.ts +0 -1
  26. package/dist/cjs/components/FieldAi/components/MobileFieldAi/index.js +0 -17
  27. package/dist/cjs/components/FieldAi/components/MobileFieldAi/styles.module.css +0 -78
  28. package/dist/cjs/components/FieldAi/components/PasswordValidation/PasswordValidation.d.ts +0 -7
  29. package/dist/cjs/components/FieldAi/components/PasswordValidation/PasswordValidation.js +0 -23
  30. package/dist/cjs/components/FieldAi/components/PasswordValidation/index.d.ts +0 -1
  31. package/dist/cjs/components/FieldAi/components/PasswordValidation/index.js +0 -17
  32. package/dist/cjs/components/FieldAi/components/PasswordValidation/styles.module.css +0 -36
  33. package/dist/cjs/components/FieldAi/components/TextArea/TextArea.d.ts +0 -39
  34. package/dist/cjs/components/FieldAi/components/TextArea/TextArea.js +0 -33
  35. package/dist/cjs/components/FieldAi/components/TextArea/index.d.ts +0 -1
  36. package/dist/cjs/components/FieldAi/components/TextArea/index.js +0 -17
  37. package/dist/cjs/components/FieldAi/components/TextArea/styles.module.css +0 -32
  38. package/dist/cjs/components/FieldAi/components/WithPasswordValidation/WithPasswordValidation.d.ts +0 -10
  39. package/dist/cjs/components/FieldAi/components/WithPasswordValidation/WithPasswordValidation.js +0 -23
  40. package/dist/cjs/components/FieldAi/components/WithPasswordValidation/index.d.ts +0 -1
  41. package/dist/cjs/components/FieldAi/components/WithPasswordValidation/index.js +0 -17
  42. package/dist/cjs/components/FieldAi/components/WithPasswordValidation/styles.module.css +0 -6
  43. package/dist/cjs/components/FieldAi/index.d.ts +0 -1
  44. package/dist/cjs/components/FieldAi/index.js +0 -17
  45. package/dist/cjs/components/FieldAi/styles.module.css +0 -61
  46. package/dist/cjs/components/FieldAi/utils.d.ts +0 -10
  47. package/dist/cjs/components/FieldAi/utils.js +0 -19
  48. package/dist/cjs/components/FieldCode/FieldCode.d.ts +0 -33
  49. package/dist/cjs/components/FieldCode/FieldCode.js +0 -48
  50. package/dist/cjs/components/FieldCode/components/Cell/Cell.d.ts +0 -5
  51. package/dist/cjs/components/FieldCode/components/Cell/Cell.js +0 -27
  52. package/dist/cjs/components/FieldCode/components/Cell/index.d.ts +0 -1
  53. package/dist/cjs/components/FieldCode/components/Cell/index.js +0 -17
  54. package/dist/cjs/components/FieldCode/components/Cell/styles.module.css +0 -3
  55. package/dist/cjs/components/FieldCode/components/ResendCode/ResendCode.d.ts +0 -8
  56. package/dist/cjs/components/FieldCode/components/ResendCode/ResendCode.js +0 -28
  57. package/dist/cjs/components/FieldCode/components/ResendCode/index.d.ts +0 -1
  58. package/dist/cjs/components/FieldCode/components/ResendCode/index.js +0 -17
  59. package/dist/cjs/components/FieldCode/components/ResendCode/utils.d.ts +0 -1
  60. package/dist/cjs/components/FieldCode/components/ResendCode/utils.js +0 -8
  61. package/dist/cjs/components/FieldCode/components/index.d.ts +0 -2
  62. package/dist/cjs/components/FieldCode/components/index.js +0 -18
  63. package/dist/cjs/components/FieldCode/constants.d.ts +0 -14
  64. package/dist/cjs/components/FieldCode/constants.js +0 -10
  65. package/dist/cjs/components/FieldCode/hooks/index.d.ts +0 -4
  66. package/dist/cjs/components/FieldCode/hooks/index.js +0 -20
  67. package/dist/cjs/components/FieldCode/hooks/useCodeInput.d.ts +0 -22
  68. package/dist/cjs/components/FieldCode/hooks/useCodeInput.js +0 -98
  69. package/dist/cjs/components/FieldCode/hooks/useFieldCodeValidate.d.ts +0 -8
  70. package/dist/cjs/components/FieldCode/hooks/useFieldCodeValidate.js +0 -24
  71. package/dist/cjs/components/FieldCode/hooks/useFieldHelpers.d.ts +0 -13
  72. package/dist/cjs/components/FieldCode/hooks/useFieldHelpers.js +0 -34
  73. package/dist/cjs/components/FieldCode/hooks/useFocusCell.d.ts +0 -5
  74. package/dist/cjs/components/FieldCode/hooks/useFocusCell.js +0 -22
  75. package/dist/cjs/components/FieldCode/index.d.ts +0 -2
  76. package/dist/cjs/components/FieldCode/index.js +0 -20
  77. package/dist/cjs/components/FieldCode/styles.module.css +0 -30
  78. package/dist/cjs/components/FieldCode/utils.d.ts +0 -6
  79. package/dist/cjs/components/FieldCode/utils.js +0 -21
  80. package/dist/esm/components/FieldAi/FieldAi.d.ts +0 -24
  81. package/dist/esm/components/FieldAi/FieldAi.js +0 -86
  82. package/dist/esm/components/FieldAi/components/AlertButton/AlertButton.d.ts +0 -5
  83. package/dist/esm/components/FieldAi/components/AlertButton/AlertButton.js +0 -6
  84. package/dist/esm/components/FieldAi/components/AlertButton/index.d.ts +0 -1
  85. package/dist/esm/components/FieldAi/components/AlertButton/index.js +0 -1
  86. package/dist/esm/components/FieldAi/components/AlertButton/styles.module.css +0 -17
  87. package/dist/esm/components/FieldAi/components/CheckItem/CheckItem.d.ts +0 -9
  88. package/dist/esm/components/FieldAi/components/CheckItem/CheckItem.js +0 -22
  89. package/dist/esm/components/FieldAi/components/CheckItem/index.d.ts +0 -1
  90. package/dist/esm/components/FieldAi/components/CheckItem/index.js +0 -1
  91. package/dist/esm/components/FieldAi/components/CheckItem/styles.module.css +0 -36
  92. package/dist/esm/components/FieldAi/components/MobileFieldAi/MobileFieldAi.d.ts +0 -5
  93. package/dist/esm/components/FieldAi/components/MobileFieldAi/MobileFieldAi.js +0 -25
  94. package/dist/esm/components/FieldAi/components/MobileFieldAi/index.d.ts +0 -1
  95. package/dist/esm/components/FieldAi/components/MobileFieldAi/index.js +0 -1
  96. package/dist/esm/components/FieldAi/components/MobileFieldAi/styles.module.css +0 -78
  97. package/dist/esm/components/FieldAi/components/PasswordValidation/PasswordValidation.d.ts +0 -7
  98. package/dist/esm/components/FieldAi/components/PasswordValidation/PasswordValidation.js +0 -17
  99. package/dist/esm/components/FieldAi/components/PasswordValidation/index.d.ts +0 -1
  100. package/dist/esm/components/FieldAi/components/PasswordValidation/index.js +0 -1
  101. package/dist/esm/components/FieldAi/components/PasswordValidation/styles.module.css +0 -36
  102. package/dist/esm/components/FieldAi/components/TextArea/TextArea.d.ts +0 -39
  103. package/dist/esm/components/FieldAi/components/TextArea/TextArea.js +0 -27
  104. package/dist/esm/components/FieldAi/components/TextArea/index.d.ts +0 -1
  105. package/dist/esm/components/FieldAi/components/TextArea/index.js +0 -1
  106. package/dist/esm/components/FieldAi/components/TextArea/styles.module.css +0 -32
  107. package/dist/esm/components/FieldAi/components/WithPasswordValidation/WithPasswordValidation.d.ts +0 -10
  108. package/dist/esm/components/FieldAi/components/WithPasswordValidation/WithPasswordValidation.js +0 -17
  109. package/dist/esm/components/FieldAi/components/WithPasswordValidation/index.d.ts +0 -1
  110. package/dist/esm/components/FieldAi/components/WithPasswordValidation/index.js +0 -1
  111. package/dist/esm/components/FieldAi/components/WithPasswordValidation/styles.module.css +0 -6
  112. package/dist/esm/components/FieldAi/index.d.ts +0 -1
  113. package/dist/esm/components/FieldAi/index.js +0 -1
  114. package/dist/esm/components/FieldAi/styles.module.css +0 -61
  115. package/dist/esm/components/FieldAi/utils.d.ts +0 -10
  116. package/dist/esm/components/FieldAi/utils.js +0 -15
  117. package/dist/esm/components/FieldCode/FieldCode.d.ts +0 -33
  118. package/dist/esm/components/FieldCode/FieldCode.js +0 -41
  119. package/dist/esm/components/FieldCode/components/Cell/Cell.d.ts +0 -5
  120. package/dist/esm/components/FieldCode/components/Cell/Cell.js +0 -21
  121. package/dist/esm/components/FieldCode/components/Cell/index.d.ts +0 -1
  122. package/dist/esm/components/FieldCode/components/Cell/index.js +0 -1
  123. package/dist/esm/components/FieldCode/components/Cell/styles.module.css +0 -3
  124. package/dist/esm/components/FieldCode/components/ResendCode/ResendCode.d.ts +0 -8
  125. package/dist/esm/components/FieldCode/components/ResendCode/ResendCode.js +0 -25
  126. package/dist/esm/components/FieldCode/components/ResendCode/index.d.ts +0 -1
  127. package/dist/esm/components/FieldCode/components/ResendCode/index.js +0 -1
  128. package/dist/esm/components/FieldCode/components/ResendCode/utils.d.ts +0 -1
  129. package/dist/esm/components/FieldCode/components/ResendCode/utils.js +0 -5
  130. package/dist/esm/components/FieldCode/components/index.d.ts +0 -2
  131. package/dist/esm/components/FieldCode/components/index.js +0 -2
  132. package/dist/esm/components/FieldCode/constants.d.ts +0 -14
  133. package/dist/esm/components/FieldCode/constants.js +0 -7
  134. package/dist/esm/components/FieldCode/hooks/index.d.ts +0 -4
  135. package/dist/esm/components/FieldCode/hooks/index.js +0 -4
  136. package/dist/esm/components/FieldCode/hooks/useCodeInput.d.ts +0 -22
  137. package/dist/esm/components/FieldCode/hooks/useCodeInput.js +0 -95
  138. package/dist/esm/components/FieldCode/hooks/useFieldCodeValidate.d.ts +0 -8
  139. package/dist/esm/components/FieldCode/hooks/useFieldCodeValidate.js +0 -21
  140. package/dist/esm/components/FieldCode/hooks/useFieldHelpers.d.ts +0 -13
  141. package/dist/esm/components/FieldCode/hooks/useFieldHelpers.js +0 -31
  142. package/dist/esm/components/FieldCode/hooks/useFocusCell.d.ts +0 -5
  143. package/dist/esm/components/FieldCode/hooks/useFocusCell.js +0 -19
  144. package/dist/esm/components/FieldCode/index.d.ts +0 -2
  145. package/dist/esm/components/FieldCode/index.js +0 -2
  146. package/dist/esm/components/FieldCode/styles.module.css +0 -30
  147. package/dist/esm/components/FieldCode/utils.d.ts +0 -6
  148. package/dist/esm/components/FieldCode/utils.js +0 -13
  149. package/src/components/FieldAi/FieldAi.tsx +0 -201
  150. package/src/components/FieldAi/components/AlertButton/AlertButton.tsx +0 -16
  151. package/src/components/FieldAi/components/AlertButton/index.ts +0 -1
  152. package/src/components/FieldAi/components/AlertButton/styles.module.scss +0 -20
  153. package/src/components/FieldAi/components/CheckItem/CheckItem.tsx +0 -45
  154. package/src/components/FieldAi/components/CheckItem/index.ts +0 -1
  155. package/src/components/FieldAi/components/CheckItem/styles.module.scss +0 -44
  156. package/src/components/FieldAi/components/MobileFieldAi/MobileFieldAi.tsx +0 -57
  157. package/src/components/FieldAi/components/MobileFieldAi/index.ts +0 -1
  158. package/src/components/FieldAi/components/MobileFieldAi/styles.module.scss +0 -90
  159. package/src/components/FieldAi/components/PasswordValidation/PasswordValidation.tsx +0 -85
  160. package/src/components/FieldAi/components/PasswordValidation/index.ts +0 -1
  161. package/src/components/FieldAi/components/PasswordValidation/styles.module.scss +0 -34
  162. package/src/components/FieldAi/components/TextArea/TextArea.tsx +0 -113
  163. package/src/components/FieldAi/components/TextArea/index.ts +0 -1
  164. package/src/components/FieldAi/components/TextArea/styles.module.scss +0 -35
  165. package/src/components/FieldAi/components/WithPasswordValidation/WithPasswordValidation.tsx +0 -63
  166. package/src/components/FieldAi/components/WithPasswordValidation/index.ts +0 -1
  167. package/src/components/FieldAi/components/WithPasswordValidation/styles.module.scss +0 -8
  168. package/src/components/FieldAi/index.ts +0 -1
  169. package/src/components/FieldAi/styles.module.scss +0 -85
  170. package/src/components/FieldAi/utils.ts +0 -27
  171. package/src/components/FieldCode/FieldCode.tsx +0 -125
  172. package/src/components/FieldCode/components/Cell/Cell.tsx +0 -32
  173. package/src/components/FieldCode/components/Cell/index.ts +0 -1
  174. package/src/components/FieldCode/components/Cell/styles.module.scss +0 -5
  175. package/src/components/FieldCode/components/ResendCode/ResendCode.tsx +0 -33
  176. package/src/components/FieldCode/components/ResendCode/index.ts +0 -1
  177. package/src/components/FieldCode/components/ResendCode/utils.ts +0 -5
  178. package/src/components/FieldCode/components/index.ts +0 -2
  179. package/src/components/FieldCode/constants.ts +0 -20
  180. package/src/components/FieldCode/hooks/index.ts +0 -4
  181. package/src/components/FieldCode/hooks/useCodeInput.ts +0 -147
  182. package/src/components/FieldCode/hooks/useFieldCodeValidate.ts +0 -35
  183. package/src/components/FieldCode/hooks/useFieldHelpers.ts +0 -52
  184. package/src/components/FieldCode/hooks/useFocusCell.ts +0 -29
  185. package/src/components/FieldCode/index.ts +0 -2
  186. package/src/components/FieldCode/styles.module.scss +0 -40
  187. package/src/components/FieldCode/utils.ts +0 -23
@@ -1,98 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.useCodeInput = useCodeInput;
4
- const reactuse_1 = require("@siberiacancode/reactuse");
5
- const react_1 = require("react");
6
- const utils_1 = require("@snack-uikit/utils");
7
- const constants_1 = require("../constants");
8
- const utils_2 = require("../utils");
9
- const buildCodeArray = (str, codeLength) => Array.from({ length: codeLength }, (_, idx) => str[idx] || constants_1.ZERO_WIDTH_SPACE);
10
- function useCodeInput(params) {
11
- const { value: valueProp, onChange: onChangeProp, codeLength, moveFocus, onComplete } = params;
12
- const [value = '', onChange] = (0, utils_1.useValueControl)({
13
- value: valueProp,
14
- onChange: onChangeProp,
15
- defaultValue: '',
16
- });
17
- const codeRef = (0, reactuse_1.useRefState)(buildCodeArray(value, codeLength));
18
- const updateCodeByIndex = (0, react_1.useCallback)((index, newChar) => {
19
- codeRef.current[index] = newChar;
20
- onChange === null || onChange === void 0 ? void 0 : onChange(codeRef.current.join(''));
21
- }, [codeRef, onChange]);
22
- const updateFullCode = (0, react_1.useCallback)((newCode) => {
23
- codeRef.current = newCode.split('');
24
- onChange === null || onChange === void 0 ? void 0 : onChange(newCode);
25
- moveFocus(codeLength - 1);
26
- onComplete === null || onComplete === void 0 ? void 0 : onComplete(newCode);
27
- }, [codeLength, codeRef, moveFocus, onChange, onComplete]);
28
- const handleAfterCellUpdate = (0, react_1.useCallback)((index) => {
29
- const normalizedCode = codeRef.current.join('');
30
- const isLastInput = index === codeLength - 1;
31
- const isAllInputsFilled = (0, utils_2.isStringCodeLength)(normalizedCode, codeLength);
32
- if (!isLastInput) {
33
- moveFocus(index + 1);
34
- }
35
- else if (isAllInputsFilled) {
36
- onComplete === null || onComplete === void 0 ? void 0 : onComplete(normalizedCode);
37
- }
38
- }, [codeLength, codeRef, moveFocus, onComplete]);
39
- const deleteChar = (0, react_1.useCallback)((index) => {
40
- if (codeRef.current[index] && !(0, utils_2.isZeroWidthSpace)(codeRef.current[index])) {
41
- updateCodeByIndex(index, constants_1.ZERO_WIDTH_SPACE);
42
- }
43
- else if (index > 0) {
44
- moveFocus(index - 1);
45
- }
46
- }, [codeRef, moveFocus, updateCodeByIndex]);
47
- const onAutoCompleteInput = (0, react_1.useCallback)((code, index) => {
48
- if ((0, utils_2.isStringCodeLength)(code, codeLength)) {
49
- updateFullCode(code);
50
- return;
51
- }
52
- if (!(0, utils_2.isNumberChar)(code)) {
53
- return;
54
- }
55
- updateCodeByIndex(index, code);
56
- handleAfterCellUpdate(index);
57
- }, [codeLength, handleAfterCellUpdate, updateCodeByIndex, updateFullCode]);
58
- const onKeyDown = (0, react_1.useCallback)((e, index) => {
59
- switch (e.key) {
60
- case 'ArrowLeft':
61
- moveFocus(index - 1);
62
- break;
63
- case 'ArrowRight':
64
- moveFocus(index + 1);
65
- break;
66
- case 'Backspace':
67
- deleteChar(index);
68
- break;
69
- default:
70
- if ((0, utils_2.isNumberChar)(e.key)) {
71
- e.preventDefault();
72
- updateCodeByIndex(index, e.key);
73
- handleAfterCellUpdate(index);
74
- }
75
- break;
76
- }
77
- }, [deleteChar, handleAfterCellUpdate, moveFocus, updateCodeByIndex]);
78
- const onPaste = (0, react_1.useCallback)((e) => {
79
- var _a;
80
- const codeInput = (_a = e === null || e === void 0 ? void 0 : e.clipboardData.getData('text')) !== null && _a !== void 0 ? _a : '';
81
- if (!(0, utils_2.isStringCodeLength)(codeInput, codeLength)) {
82
- return;
83
- }
84
- updateFullCode(codeInput);
85
- }, [codeLength, updateFullCode]);
86
- (0, react_1.useEffect)(() => {
87
- codeRef.current = buildCodeArray(value, codeLength);
88
- }, [codeLength, codeRef, value]);
89
- return {
90
- code: codeRef.current,
91
- cellHandlers: {
92
- onKeyDown,
93
- onPaste,
94
- onChange: onAutoCompleteInput,
95
- },
96
- onChangeCode: onChange,
97
- };
98
- }
@@ -1,8 +0,0 @@
1
- export type UseFieldCodeValidateParams = {
2
- /** Ожидаемая длина кода (цифр) */
3
- codeLength: number;
4
- };
5
- /**
6
- * Возвращает функцию валидации значения кода (пусто / неполный код).
7
- */
8
- export declare function useFieldCodeValidate(params: UseFieldCodeValidateParams): (value?: string | number) => string | undefined;
@@ -1,24 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.useFieldCodeValidate = useFieldCodeValidate;
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
- /**
8
- * Возвращает функцию валидации значения кода (пусто / неполный код).
9
- */
10
- function useFieldCodeValidate(params) {
11
- const { codeLength } = params;
12
- const { t } = (0, uikit_product_locale_1.useLocale)('FieldsPredefined');
13
- return (0, react_1.useCallback)((value) => {
14
- const str = value != null ? String(value) : '';
15
- const digits = str.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]);
24
- }
@@ -1,13 +0,0 @@
1
- import { type FieldCodeFocusEffect } from '../constants';
2
- type UseFieldHelpersParams = {
3
- onChangeCode: (code: string) => void;
4
- moveFocus: (index: number) => void;
5
- focusEffects: readonly FieldCodeFocusEffect[];
6
- showEmptyChars?: boolean;
7
- code: readonly string[];
8
- codeLength: number;
9
- };
10
- export declare function useFieldHelpers(params: UseFieldHelpersParams): {
11
- resetCode: () => void;
12
- };
13
- export {};
@@ -1,34 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.useFieldHelpers = useFieldHelpers;
4
- const react_1 = require("react");
5
- const utils_1 = require("../utils");
6
- function useFieldHelpers(params) {
7
- const { onChangeCode, moveFocus, focusEffects: focusEffectsProp, showEmptyChars, code, codeLength } = params;
8
- const focusEffectsKey = [...focusEffectsProp].sort().join(',');
9
- const focusEffects = (0, react_1.useMemo)(() => [...focusEffectsProp],
10
- // eslint-disable-next-line react-hooks/exhaustive-deps
11
- [focusEffectsKey]);
12
- const resetCode = (0, react_1.useCallback)(() => {
13
- onChangeCode('');
14
- if (focusEffects.includes('firstCellOnReset')) {
15
- moveFocus(0);
16
- }
17
- }, [focusEffects, moveFocus, onChangeCode]);
18
- (0, react_1.useEffect)(() => {
19
- if (focusEffects.includes('firstCellOnMount')) {
20
- moveFocus(0);
21
- }
22
- }, [focusEffects, moveFocus]);
23
- (0, react_1.useEffect)(() => {
24
- if (!focusEffects.includes('firstCellWhenShowEmptyChars') || !showEmptyChars) {
25
- return;
26
- }
27
- const emptyIndex = (0, utils_1.getFirstEmptyCellIndex)(code);
28
- if (emptyIndex >= 0) {
29
- moveFocus(emptyIndex);
30
- }
31
- // eslint-disable-next-line react-hooks/exhaustive-deps
32
- }, [showEmptyChars, focusEffects, moveFocus, codeLength]);
33
- return { resetCode };
34
- }
@@ -1,5 +0,0 @@
1
- export declare function useFocusCell(codeLength: number): {
2
- inputsRef: import("react").MutableRefObject<HTMLInputElement[]>;
3
- moveFocus: (newIndex: number) => void;
4
- blurFields: () => void;
5
- };
@@ -1,22 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.useFocusCell = useFocusCell;
4
- const react_1 = require("react");
5
- function useFocusCell(codeLength) {
6
- const inputsRef = (0, react_1.useRef)([]);
7
- const focusInput = (0, react_1.useCallback)((index) => {
8
- var _a;
9
- (_a = inputsRef.current[index]) === null || _a === void 0 ? void 0 : _a.focus();
10
- }, [inputsRef]);
11
- const moveFocus = (0, react_1.useCallback)((newIndex) => {
12
- if (newIndex >= 0 && newIndex < codeLength) {
13
- focusInput(newIndex);
14
- }
15
- }, [codeLength, focusInput]);
16
- const blurFields = (0, react_1.useCallback)(() => {
17
- inputsRef.current.forEach(input => {
18
- input === null || input === void 0 ? void 0 : input.blur();
19
- });
20
- }, [inputsRef]);
21
- return { inputsRef, moveFocus, blurFields };
22
- }
@@ -1,2 +0,0 @@
1
- export * from './FieldCode';
2
- export { useFieldCodeValidate, type UseFieldCodeValidateParams } from './hooks/useFieldCodeValidate';
@@ -1,20 +0,0 @@
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
- exports.useFieldCodeValidate = void 0;
18
- __exportStar(require("./FieldCode"), exports);
19
- var useFieldCodeValidate_1 = require("./hooks/useFieldCodeValidate");
20
- Object.defineProperty(exports, "useFieldCodeValidate", { enumerable: true, get: function () { return useFieldCodeValidate_1.useFieldCodeValidate; } });
@@ -1,30 +0,0 @@
1
- .fieldCode{
2
- display:flex;
3
- flex-direction:column;
4
- align-items:center;
5
- gap:8px;
6
- }
7
-
8
- .codeContainer{
9
- display:flex;
10
- justify-content:center;
11
- }
12
- .codeContainer[data-size=s]{
13
- gap:8px;
14
- }
15
- .codeContainer[data-size=m]{
16
- gap:8px;
17
- }
18
- .codeContainer[data-size=l]{
19
- gap:12px;
20
- }
21
-
22
- .cellSpacing[data-size=s]{
23
- margin-right:8px;
24
- }
25
- .cellSpacing[data-size=m]{
26
- margin-right:8px;
27
- }
28
- .cellSpacing[data-size=l]{
29
- margin-right:12px;
30
- }
@@ -1,6 +0,0 @@
1
- import { FieldTextProps } from '@snack-uikit/fields';
2
- export declare const isNumberChar: (char: string) => boolean;
3
- export declare const isStringCodeLength: (input: string, codeLength: number) => boolean;
4
- export declare const isZeroWidthSpace: (value: string) => value is "​";
5
- export declare function getFirstEmptyCellIndex(code: readonly string[]): number;
6
- export declare const getCellValidationState: (value: string, showEmptyChars?: boolean, error?: boolean) => FieldTextProps["validationState"];
@@ -1,21 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getCellValidationState = exports.isZeroWidthSpace = exports.isStringCodeLength = exports.isNumberChar = void 0;
4
- exports.getFirstEmptyCellIndex = getFirstEmptyCellIndex;
5
- const constants_1 = require("./constants");
6
- const isNumberChar = (char) => /^\d$/.test(char);
7
- exports.isNumberChar = isNumberChar;
8
- const isStringCodeLength = (input, codeLength) => new RegExp(`^\\d{${codeLength}}$`).test(input);
9
- exports.isStringCodeLength = isStringCodeLength;
10
- const isZeroWidthSpace = (value) => value === constants_1.ZERO_WIDTH_SPACE;
11
- exports.isZeroWidthSpace = isZeroWidthSpace;
12
- function getFirstEmptyCellIndex(code) {
13
- return code.findIndex(exports.isZeroWidthSpace);
14
- }
15
- const getCellValidationState = (value, showEmptyChars, error) => {
16
- if (showEmptyChars) {
17
- return (0, exports.isZeroWidthSpace)(value) ? 'error' : 'default';
18
- }
19
- return error ? 'error' : 'default';
20
- };
21
- exports.getCellValidationState = getCellValidationState;
@@ -1,24 +0,0 @@
1
- import { FieldTextAreaProps } from '@cloud-ru/uikit-product-mobile-fields';
2
- import { WithLayoutType } from '@cloud-ru/uikit-product-utils';
3
- export type FieldAiProps = WithLayoutType<Omit<FieldTextAreaProps, 'placeholder' | 'labelTooltip' | 'label' | 'required' | 'size' | 'spellCheck' | 'footer'> & {
4
- /** Режим ввода sensitive данных (пароля, API ключей, токенов, etc) */
5
- secure?: boolean | 'password';
6
- /** Колбек действия при отправке */
7
- onSubmit(value: string): void;
8
- /** Действие при клике по кнопке сброса контекста */
9
- onResetContextClick?(): void;
10
- /** Действие для отмены секьюрности поля */
11
- onCancelSecure?(): void;
12
- }>;
13
- export declare const FieldAi: import("react").ForwardRefExoticComponent<Omit<FieldTextAreaProps, "size" | "label" | "required" | "placeholder" | "spellCheck" | "labelTooltip" | "footer"> & {
14
- /** Режим ввода sensitive данных (пароля, API ключей, токенов, etc) */
15
- secure?: boolean | "password";
16
- /** Колбек действия при отправке */
17
- onSubmit(value: string): void;
18
- /** Действие при клике по кнопке сброса контекста */
19
- onResetContextClick?(): void;
20
- /** Действие для отмены секьюрности поля */
21
- onCancelSecure?(): void;
22
- } & {
23
- layoutType: import("@cloud-ru/uikit-product-utils/.").LayoutType;
24
- } & import("react").RefAttributes<HTMLTextAreaElement>>;
@@ -1,86 +0,0 @@
1
- var __rest = (this && this.__rest) || function (s, e) {
2
- var t = {};
3
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
4
- t[p] = s[p];
5
- if (s != null && typeof Object.getOwnPropertySymbols === "function")
6
- for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
7
- if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
8
- t[p[i]] = s[p[i]];
9
- }
10
- return t;
11
- };
12
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
13
- import cn from 'classnames';
14
- import { forwardRef, useEffect, useMemo, useRef, useState } from 'react';
15
- import { EyeClosedSVG, EyeSVG, PasswordLockSVG } from '@cloud-ru/uikit-product-icons';
16
- import { useLocale } from '@cloud-ru/uikit-product-locale';
17
- import { AdaptiveFieldTextArea, getAdaptiveFieldProps, } from '@cloud-ru/uikit-product-mobile-fields';
18
- import { ButtonFunction, ButtonOutline } from '@snack-uikit/button';
19
- import { themeVars } from '@snack-uikit/figma-tokens';
20
- import { Tooltip } from '@snack-uikit/tooltip';
21
- import { Typography } from '@snack-uikit/typography';
22
- import { FieldSubmitButton } from '../../helperComponents/FieldSubmitButton';
23
- import { TextAreaActionsFooter } from '../../helperComponents/TextAreaActionsFooter';
24
- import { isTouchDevice as isTouchDeviceHelper } from '../../helpers';
25
- import { AIDisclaimer } from '../AIDisclaimer/AIDisclaimer';
26
- import { AlertButton } from './components/AlertButton';
27
- import { MobileFieldAi } from './components/MobileFieldAi';
28
- import { WithPasswordValidation } from './components/WithPasswordValidation';
29
- import styles from './styles.module.css';
30
- import { getValidationPassword } from './utils';
31
- export const FieldAi = forwardRef((_a, ref) => {
32
- var { secure = false, onSubmit: handleSubmitProp, value, onResetContextClick, onCancelSecure, disabled, className } = _a, props = __rest(_a, ["secure", "onSubmit", "value", "onResetContextClick", "onCancelSecure", "disabled", "className"]);
33
- const { layoutType, validationState } = props;
34
- const { t } = useLocale('FieldsPredefined');
35
- const isTouchDevice = isTouchDeviceHelper(layoutType);
36
- const [isValueHidden, setIsValueHidden] = useState(true);
37
- const [animatedValidationKey, setAnimatedValidationKey] = useState(null);
38
- const timerRef = useRef(null);
39
- const isValueValid = typeof value === 'string' && value.trim().length > 0;
40
- const isPasswordMode = secure === 'password';
41
- const passwordValidation = useMemo(() => getValidationPassword(value), [value]);
42
- const isPasswordValid = isPasswordMode ? Object.values(passwordValidation).every(Boolean) : true;
43
- const showPasswordError = !isPasswordValid && secure && value;
44
- const showPasswordAlert = Boolean(onCancelSecure) && secure === 'password';
45
- useEffect(() => () => {
46
- if (timerRef.current) {
47
- clearTimeout(timerRef.current);
48
- }
49
- }, []);
50
- const handleSubmit = () => {
51
- if (isValueValid && isPasswordValid) {
52
- handleSubmitProp(value);
53
- }
54
- };
55
- const triggerValidationHighlight = (key) => {
56
- setAnimatedValidationKey(key);
57
- timerRef.current = setTimeout(() => {
58
- setAnimatedValidationKey(null);
59
- }, 1000);
60
- };
61
- const handleKeyDown = event => {
62
- if (isTouchDevice) {
63
- return;
64
- }
65
- if (isPasswordMode && event.key.length === 1) {
66
- const isLetter = /\p{L}/u.test(event.key);
67
- const isLatinLetter = /^[a-zA-Z]$/.test(event.key);
68
- if (isLetter && !isLatinLetter) {
69
- event.preventDefault();
70
- triggerValidationHighlight('onlyLatin');
71
- return;
72
- }
73
- }
74
- if (event.key === 'Enter' && !event.shiftKey) {
75
- event.preventDefault();
76
- if (!disabled) {
77
- handleSubmit();
78
- }
79
- return;
80
- }
81
- };
82
- if (isTouchDevice && !secure) {
83
- return (_jsx(MobileFieldAi, Object.assign({}, props, getAdaptiveFieldProps(props), { onSubmit: handleSubmit, submitEnabled: isValueValid && !disabled, ref: ref, value: value })));
84
- }
85
- return (_jsxs("div", { className: cn(styles.wrapper, isPasswordMode && styles.passwordWrapper, className), children: [showPasswordAlert && (_jsxs("div", { className: styles.alert, children: [_jsx(PasswordLockSVG, { size: 16, color: themeVars.sys.neutral.textSupport }), _jsx(Typography.SansBodyS, { className: styles.alertText, children: t('FieldAi.secret.alert.text') }), _jsx(AlertButton, { label: t('FieldAi.secret.alert.button'), onClick: onCancelSecure })] })), _jsx(WithPasswordValidation, { showValidation: isPasswordMode, passwordValidation: passwordValidation, layoutType: layoutType, animatedKey: animatedValidationKey, children: _jsx(AdaptiveFieldTextArea, Object.assign({}, props, { ref: ref, value: value, size: 'm', minRows: secure ? 1 : 2, maxRows: secure ? 1 : 4, placeholder: secure ? t('FieldAi.secret.placeholder') : t('FieldAi.regular.placeholder'), className: cn(styles.textarea, secure && isValueHidden ? styles.secured : undefined), onKeyDown: handleKeyDown, spellCheck: !secure, validationState: showPasswordError ? 'error' : validationState, footer: _jsx(TextAreaActionsFooter, { left: secure && (_jsx(ButtonFunction, { size: 'xs', icon: isValueHidden ? _jsx(EyeSVG, {}) : _jsx(EyeClosedSVG, {}), onClick: () => setIsValueHidden(prev => !prev) })), right: _jsxs(_Fragment, { children: [secure && onResetContextClick && (_jsx(Tooltip, { tip: t('FieldAi.resetContext.tooltip'), hoverDelayOpen: 600, children: _jsx(ButtonOutline, { size: 'xs', label: t('FieldAi.resetContext.label'), onClick: onResetContextClick, appearance: 'destructive' }) })), _jsx(FieldSubmitButton, { showTooltip: !isTouchDevice, className: cn(styles.submitButton, isTouchDevice ? styles.mobileSubmitButton : undefined), active: isValueValid && !disabled && isPasswordValid, handleClick: handleSubmit, size: isTouchDevice ? 's' : 'xs' })] }) }) })) }), !isPasswordMode && _jsx(AIDisclaimer, { layoutType: layoutType })] }));
86
- });
@@ -1,5 +0,0 @@
1
- export type AlertButtonProps = {
2
- label: string;
3
- onClick?(): void;
4
- };
5
- export declare function AlertButton({ label, onClick }: AlertButtonProps): import("react/jsx-runtime").JSX.Element;
@@ -1,6 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { Typography } from '@snack-uikit/typography';
3
- import styles from './styles.module.css';
4
- export function AlertButton({ label, onClick }) {
5
- return (_jsx("button", { onClick: onClick, className: styles.button, children: _jsx(Typography.SansLabelM, { className: styles.label, children: label }) }));
6
- }
@@ -1 +0,0 @@
1
- export * from './AlertButton';
@@ -1 +0,0 @@
1
- export * from './AlertButton';
@@ -1,17 +0,0 @@
1
- .button{
2
- outline:none;
3
- border:none;
4
- background-color:transparent;
5
- cursor:pointer;
6
- height:16px;
7
- display:flex;
8
- align-items:center;
9
- justify-content:center;
10
- }
11
-
12
- .label{
13
- color:var(--sys-blue-text-support, #4877b0);
14
- }
15
- .label:hover{
16
- color:var(--sys-neutral-text-main, #41424e);
17
- }
@@ -1,9 +0,0 @@
1
- import { WithLayoutType } from '@cloud-ru/uikit-product-utils';
2
- type CheckItemProps = WithLayoutType<{
3
- label: string;
4
- checked: boolean;
5
- shouldHide?: boolean;
6
- animated?: boolean;
7
- }>;
8
- export declare function CheckItem({ label, checked, layoutType, shouldHide, animated }: CheckItemProps): import("react/jsx-runtime").JSX.Element | null;
9
- export {};
@@ -1,22 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useEffect, useState } from 'react';
3
- import { CheckSVG } from '@cloud-ru/uikit-product-icons';
4
- import { Typography } from '@snack-uikit/typography';
5
- import styles from './styles.module.css';
6
- const CHECKED_ITEM_DISAPPEAR_TIMEOUT = 500;
7
- export function CheckItem({ label, checked, layoutType, shouldHide = false, animated = false }) {
8
- const [visible, setVisible] = useState(checked);
9
- useEffect(() => {
10
- if (checked) {
11
- const timeoutId = setTimeout(() => {
12
- setVisible(false);
13
- }, CHECKED_ITEM_DISAPPEAR_TIMEOUT);
14
- return () => clearTimeout(timeoutId);
15
- }
16
- setVisible(true);
17
- }, [checked]);
18
- if (shouldHide && !visible) {
19
- return null;
20
- }
21
- return (_jsxs("div", { className: styles.checkItem, "data-layout-type": layoutType, "data-animated": animated, children: [_jsx(CheckSVG, { size: 16, className: styles.icon, "data-checked": checked }), _jsx(Typography.SansBodyM, { className: styles.label, "data-checked": checked, children: label })] }));
22
- }
@@ -1 +0,0 @@
1
- export * from './CheckItem';
@@ -1 +0,0 @@
1
- export * from './CheckItem';
@@ -1,36 +0,0 @@
1
- @keyframes shake{
2
- 0%, 100%{
3
- transform:translateX(0);
4
- }
5
- 10%, 30%, 50%, 70%, 90%{
6
- transform:translateX(-4px);
7
- }
8
- 20%, 40%, 60%, 80%{
9
- transform:translateX(4px);
10
- }
11
- }
12
- .checkItem{
13
- display:inline-flex;
14
- gap:10px;
15
- }
16
- .checkItem[data-layout-type=mobile], .checkItem[data-layout-type=tablet]{
17
- gap:8px;
18
- }
19
- .checkItem[data-animated=true]{
20
- animation:shake 0.5s ease-in-out;
21
- }
22
-
23
- .icon{
24
- flex-shrink:0;
25
- color:var(--sys-neutral-accent-default, #787b8a);
26
- }
27
- .icon[data-checked=true]{
28
- color:var(--sys-primary-text-light, #6aaf90);
29
- }
30
-
31
- .label{
32
- color:var(--sys-neutral-text-light, #8b8e9b);
33
- }
34
- .label[data-checked=true]{
35
- color:var(--sys-primary-text-light, #6aaf90);
36
- }
@@ -1,5 +0,0 @@
1
- import { FieldTextAreaProps } from '@cloud-ru/uikit-product-mobile-fields';
2
- export declare const MobileFieldAi: import("react").ForwardRefExoticComponent<Omit<FieldTextAreaProps, "size" | "label" | "required" | "placeholder" | "spellCheck" | "labelTooltip" | "footer"> & {
3
- onSubmit(): void;
4
- submitEnabled: boolean;
5
- } & import("react").RefAttributes<HTMLTextAreaElement>>;
@@ -1,25 +0,0 @@
1
- var __rest = (this && this.__rest) || function (s, e) {
2
- var t = {};
3
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
4
- t[p] = s[p];
5
- if (s != null && typeof Object.getOwnPropertySymbols === "function")
6
- for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
7
- if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
8
- t[p[i]] = s[p[i]];
9
- }
10
- return t;
11
- };
12
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
13
- import { forwardRef } from 'react';
14
- import { useLocale } from '@cloud-ru/uikit-product-locale';
15
- import { Scroll } from '@snack-uikit/scroll';
16
- import { FieldSubmitButton } from '../../../../helperComponents/FieldSubmitButton';
17
- import { TextArea } from '../TextArea';
18
- import styles from './styles.module.css';
19
- const MIN_ROWS = 1;
20
- const MAX_ROWS = 6;
21
- export const MobileFieldAi = forwardRef((_a, ref) => {
22
- var { onSubmit, value, submitEnabled } = _a, props = __rest(_a, ["onSubmit", "value", "submitEnabled"]);
23
- const { t } = useLocale('FieldsPredefined');
24
- return (_jsxs("div", { className: styles.mobileInputWrapper, style: { '--max-rows': MAX_ROWS, '--min-rows': MIN_ROWS }, "data-size": 'm', children: [_jsx(Scroll, { className: styles.scrollContainer, size: 's', barHideStrategy: 'never', children: _jsx(TextArea, Object.assign({}, props, { className: styles.textarea, ref: ref, value: value, minRows: MIN_ROWS, placeholder: t('FieldAi.regular.placeholder'), spellCheck: true })) }), _jsx("div", { className: styles.mobileSubmitButtonWrapper, children: _jsx(FieldSubmitButton, { showTooltip: false, className: styles.mobileSubmitButton, fullWidth: true, active: submitEnabled, handleClick: onSubmit, size: 's' }) })] }));
25
- });
@@ -1 +0,0 @@
1
- export * from './MobileFieldAi';
@@ -1 +0,0 @@
1
- export * from './MobileFieldAi';
@@ -1,78 +0,0 @@
1
- .scrollContainer{
2
- border-radius:4px;
3
- padding:6px 0;
4
- }
5
- .scrollContainer [data-overlayscrollbars-contents]{
6
- display:flex;
7
- }
8
-
9
- .textarea{
10
- overflow:hidden;
11
- }
12
-
13
- .mobileInputWrapper{
14
- --max-rows:1000;
15
- --min-rows:3;
16
- display:flex;
17
- position:relative;
18
- gap:8px;
19
- }
20
- .mobileInputWrapper .scrollContainer{
21
- min-height:calc(var(--min-rows) * var(--row-height) + var(--horizontal-scroll-bar-height));
22
- max-height:calc(var(--max-rows) * var(--row-height) + var(--horizontal-scroll-bar-height));
23
- }
24
- .mobileInputWrapper .textarea{
25
- font-family:var(--sans-body-l-font-family, SB Sans Interface);
26
- font-weight:var(--sans-body-l-font-weight, Regular);
27
- line-height:var(--sans-body-l-line-height, 24px);
28
- font-size:var(--sans-body-l-font-size, 16px);
29
- letter-spacing:var(--sans-body-l-letter-spacing, 0.1px);
30
- paragraph-spacing:var(--sans-body-l-paragraph-spacing, 8.8px);
31
- padding-right:calc(42px + var(--size-fields-scroll-bar-width, 8px));
32
- }
33
- .mobileInputWrapper .textarea::-moz-placeholder{
34
- color:var(--sys-neutral-text-disabled, #aaaebd);
35
- }
36
- .mobileInputWrapper .textarea::placeholder{
37
- color:var(--sys-neutral-text-disabled, #aaaebd);
38
- }
39
- .mobileInputWrapper[data-size=s]{
40
- --row-height:var(--sans-body-s-line-height, 16px);
41
- --horizontal-scroll-bar-height:var(--dimension-050m, 4px);
42
- }
43
- .mobileInputWrapper[data-size=m]{
44
- --row-height:var(--sans-body-m-line-height, 20px);
45
- --horizontal-scroll-bar-height:calc(var(--dimension-025m, 2px) + var(--dimension-050m, 4px));
46
- }
47
- .mobileInputWrapper[data-size=l]{
48
- --row-height:var(--sans-body-l-line-height, 24px);
49
- --horizontal-scroll-bar-height:var(--dimension-1m, 8px);
50
- }
51
-
52
- .mobileSubmitButtonWrapper{
53
- width:32px;
54
- height:32px;
55
- display:flex;
56
- flex-shrink:0;
57
- position:absolute;
58
- bottom:0;
59
- right:10px;
60
- }
61
-
62
- .mobileSubmitButton{
63
- position:relative !important;
64
- flex-shrink:0 !important;
65
- }
66
- .mobileSubmitButton:not([data-disabled]){
67
- background:radial-gradient(229.88% 112% at 37.5% 77.08%, #26D07C 19.86%, #5FD7C2 50.73%, #7CB5F2 89.88%);
68
- }
69
- .mobileSubmitButton:after{
70
- content:"";
71
- display:flex;
72
- width:48px;
73
- height:48px;
74
- position:absolute;
75
- top:50%;
76
- left:50%;
77
- transform:translate(-50%, -50%);
78
- }