@cloud-ru/uikit-product-fields-predefined 0.14.0 → 0.15.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 (29) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +239 -3
  3. package/dist/cjs/components/FieldAi/FieldAi.js +26 -4
  4. package/dist/cjs/components/FieldAi/components/CheckItem/CheckItem.d.ts +2 -1
  5. package/dist/cjs/components/FieldAi/components/CheckItem/CheckItem.js +2 -2
  6. package/dist/cjs/components/FieldAi/components/CheckItem/styles.module.css +14 -0
  7. package/dist/cjs/components/FieldAi/components/PasswordValidation/PasswordValidation.d.ts +3 -2
  8. package/dist/cjs/components/FieldAi/components/PasswordValidation/PasswordValidation.js +2 -2
  9. package/dist/cjs/components/FieldAi/components/WithPasswordValidation/WithPasswordValidation.d.ts +3 -2
  10. package/dist/cjs/components/FieldAi/components/WithPasswordValidation/WithPasswordValidation.js +3 -3
  11. package/dist/cjs/components/FieldAi/utils.d.ts +1 -0
  12. package/dist/esm/components/FieldAi/FieldAi.js +27 -5
  13. package/dist/esm/components/FieldAi/components/CheckItem/CheckItem.d.ts +2 -1
  14. package/dist/esm/components/FieldAi/components/CheckItem/CheckItem.js +2 -2
  15. package/dist/esm/components/FieldAi/components/CheckItem/styles.module.css +14 -0
  16. package/dist/esm/components/FieldAi/components/PasswordValidation/PasswordValidation.d.ts +3 -2
  17. package/dist/esm/components/FieldAi/components/PasswordValidation/PasswordValidation.js +2 -2
  18. package/dist/esm/components/FieldAi/components/WithPasswordValidation/WithPasswordValidation.d.ts +3 -2
  19. package/dist/esm/components/FieldAi/components/WithPasswordValidation/WithPasswordValidation.js +3 -3
  20. package/dist/esm/components/FieldAi/utils.d.ts +1 -0
  21. package/dist/tsconfig.cjs.tsbuildinfo +1 -1
  22. package/dist/tsconfig.esm.tsbuildinfo +1 -1
  23. package/package.json +8 -8
  24. package/src/components/FieldAi/FieldAi.tsx +36 -5
  25. package/src/components/FieldAi/components/CheckItem/CheckItem.tsx +3 -2
  26. package/src/components/FieldAi/components/CheckItem/styles.module.scss +16 -0
  27. package/src/components/FieldAi/components/PasswordValidation/PasswordValidation.tsx +9 -2
  28. package/src/components/FieldAi/components/WithPasswordValidation/WithPasswordValidation.tsx +15 -3
  29. package/src/components/FieldAi/utils.ts +2 -0
package/CHANGELOG.md CHANGED
@@ -3,6 +3,28 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # 0.15.0 (2025-12-11)
7
+
8
+
9
+ ### Features
10
+
11
+ * **AINFR-4597:** latin symbols for password and tooltip keys animation ([a3c533f](https://gitverse.ru/cloud-ru-tech/uikit-product/commits/a3c533f05551ad665af52a5e6788a134c6dbb7b5))
12
+
13
+
14
+
15
+
16
+
17
+ ## 0.14.1 (2025-12-08)
18
+
19
+
20
+ ### Bug Fixes
21
+
22
+ * **UIIAAS-1251:** docs ([0226b84](https://gitverse.ru/cloud-ru-tech/uikit-product/commits/0226b8456be07d407c614979552ce88023eb8bd9))
23
+
24
+
25
+
26
+
27
+
6
28
  # 0.14.0 (2025-12-03)
7
29
 
8
30
 
package/README.md CHANGED
@@ -14,6 +14,40 @@
14
14
  ### Props
15
15
  | name | type | default value | description |
16
16
  |------|------|---------------|-------------|
17
+ | layoutType* | enum LayoutType: `"mobile"`, `"tablet"`, `"desktop"`, `"desktopSmall"` | - | |
18
+ | value | `string` | - | Значение input |
19
+ | onChange | `((value: string, e?: ChangeEvent<HTMLInputElement>) => void) & ((value: string) => void)` | - | Колбек смены значения |
20
+ | id | `string` | - | Значение html-атрибута id |
21
+ | name | `string` | - | Значение html-атрибута name |
22
+ | disabled | `boolean` | false | Является ли поле деактивированным |
23
+ | readonly | `boolean` | false | Является ли поле доступным только для чтения |
24
+ | onFocus | `FocusEventHandler<HTMLInputElement>` | - | Колбек обработки получения фокуса |
25
+ | onBlur | `FocusEventHandler<HTMLInputElement>` | - | Колбек обработки потери фокуса |
26
+ | autoComplete | `string \| boolean` | false | Включен ли автокомплит для поля |
27
+ | autoFocus | `boolean` | false | Включен ли авто-фокус для поля |
28
+ | onPaste | `ClipboardEventHandler<HTMLInputElement>` | - | Колбек обработки вставки значения |
29
+ | spellCheck | `boolean` | true | Значение атрибута spellcheck (проверка орфографии) |
30
+ | pattern | `string` | - | Регулярное выражение валидного инпута |
31
+ | className | `string` | - | CSS-класс |
32
+ | label | `string` | - | Лейбл |
33
+ | labelTooltip | `ReactNode` | - | Всплывающая подсказка лейбла |
34
+ | required | `boolean` | - | Является ли поле обязательным |
35
+ | caption | `string` | - | Подпись справа от лейбла |
36
+ | hint | `string` | - | Подсказка внизу |
37
+ | showHintIcon | `boolean` | - | Отображать иконку подсказки |
38
+ | size | enum Size: `"s"`, `"m"`, `"l"` | - | Размер |
39
+ | validationState | enum ValidationState: `"error"`, `"default"`, `"warning"`, `"success"` | - | Состояние валидации |
40
+ | labelTooltipPlacement | enum Placement: `"left"`, `"left-start"`, `"left-end"`, `"right"`, `"right-start"`, `"right-end"`, `"top"`, `"top-start"`, `"top-end"`, `"bottom"`, `"bottom-start"`, `"bottom-end"` | top | Расположение подсказки лейбла |
41
+ | error | `string` | - | |
42
+ | type | "text" \| "tel" \| "email" | - | |
43
+ | showCopyButton | `boolean` | - | Отображение кнопки Копировать для поля (актуально только для `readonly = true`) |
44
+ | onCopyButtonClick | `() => void` | - | Колбек клика по кнопке Копировать для поля |
45
+ | showClearButton | `boolean` | true | Отображение кнопки очистки поля |
46
+ | onClearButtonClick | `() => void` | - | Колбек клика по кнопке очистки поля |
47
+ | scrollList | `boolean` | - | Включить скролл для основной части списка стран |
48
+ | searchPlaceholder | `string` | - | |
49
+ | onChangeCountry | `(country: FieldPhoneOptionsProps) => void` | - | |
50
+ | options | `CountrySettings` | - | options — объект конфигурации для изменения стандартного списка стран |
17
51
  | 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} |
18
52
  | key | `Key` | - | |
19
53
  ## useCountries
@@ -27,25 +61,131 @@
27
61
  ### Props
28
62
  | name | type | default value | description |
29
63
  |------|------|---------------|-------------|
64
+ | selectProps* | `FieldSelectProps` | - | Пропсы прокидываемые в селект |
65
+ | submitHandler* | `() => Promise<string \| void>` | - | Коллбек создания новой опции, при успешном выполнении возвращает value новой опции |
66
+ | entityName* | `EntityName` | - | Тип объекта для создания новой опции (в единственном числе вин.падеже для кнопки Создать <entityName> и множественном числе) |
67
+ | createLayoutProps* | `CreateLayoutModalProps \| CreateLayoutDrawerProps` | - | Пропсы передаваемые в модалку или дровер создания новой опции |
68
+ | createLayoutType* | "modal" \| "drawer" | - | По клику на кнопку создания открывать модальное окно или дровер |
69
+ | layoutType* | enum LayoutType: `"mobile"`, `"tablet"`, `"desktop"`, `"desktopSmall"` | - | |
70
+ | onRefetch | `VoidFunction` | - | Коллбек рефетча запроса на получение списка опций в случае ошибки (при передаче dataError в selectProps). |
71
+ | className | `string` | - | CSS-класс |
72
+ | afterClose | `VoidFunction` | - | Коллбек после закрытия модального окна/дровера |
73
+ | entityIcon | `JSXElementConstructor<{ size?: number; className?: string; }>` | - | Иконка сервиса |
74
+ | permission | "none" \| "canRead" \| "canCreate" | - | Управление состоянием компонента в зависимости от прав пользователя (по дефолту permission = 'canCreate') |
30
75
  ## FieldAi
31
76
  ### Props
32
77
  | name | type | default value | description |
33
78
  |------|------|---------------|-------------|
34
- | ref | `LegacyRef<HTMLTextAreaElement>` | - | 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} |
79
+ | layoutType* | enum LayoutType: `"mobile"`, `"tablet"`, `"desktop"`, `"desktopSmall"` | - | |
80
+ | onSubmit* | `(value: string) => void` | - | Колбек действия при отправке |
81
+ | value | `string` | - | HTML-аттрибут value |
82
+ | onChange | `(value: string, e?: ChangeEvent<HTMLTextAreaElement>) => void` | - | Колбек смены значения |
83
+ | inputMode | enum InputMode: `"search"`, `"none"`, `"text"`, `"tel"`, `"email"`, `"decimal"`, `"numeric"`, `"url"` | - | Режим работы экранной клавиатуры |
84
+ | id | `string` | - | HTML-аттрибут id |
85
+ | name | `string` | - | HTML-аттрибут name |
86
+ | maxLength | `number` | - | Максимальное кол-во символов |
87
+ | disabled | `boolean` | - | Является ли поле деактивированным |
88
+ | readonly | `boolean` | - | Является ли поле доступным только на чтение |
89
+ | onFocus | `FocusEventHandler<HTMLTextAreaElement>` | - | Колбек получения фокуса |
90
+ | onBlur | `FocusEventHandler<HTMLTextAreaElement>` | - | Колбек потери фокуса |
91
+ | autoFocus | `boolean` | - | Включен ли авто-фокус |
92
+ | onKeyDown | `KeyboardEventHandler<HTMLTextAreaElement>` | - | Колбек нажатия клавиши клавиатуры |
93
+ | className | `string` | - | CSS-класс |
94
+ | caption | `string` | - | Подпись справа от лейбла |
95
+ | hint | `string` | - | Подсказка внизу |
96
+ | showHintIcon | `boolean` | - | Отображать иконку подсказки |
97
+ | validationState | enum ValidationState: `"error"`, `"default"`, `"warning"`, `"success"` | - | Состояние валидации |
98
+ | labelTooltipPlacement | enum Placement: `"left"`, `"left-start"`, `"left-end"`, `"right"`, `"right-start"`, `"right-end"`, `"top"`, `"top-start"`, `"top-end"`, `"bottom"`, `"bottom-start"`, `"bottom-end"` | top | Расположение подсказки лейбла |
99
+ | error | `string` | - | |
100
+ | allowMoreThanMaxLength | `boolean` | - | Можно ли вводить больше разрешённого кол-ва символов |
101
+ | showCopyButton | `boolean` | - | Отображение кнопки Копировать для поля (актуально только для `readonly = true`) |
102
+ | onCopyButtonClick | `() => void` | - | Колбек клика по кнопке Копировать для поля |
103
+ | showClearButton | `boolean` | true | Отображение кнопки очистки поля |
104
+ | minRows | `number` | 3 | Минимальное кол-во строк, до которого размер поля может быть увеличен |
105
+ | maxRows | `number` | 1000 | Максимальное кол-во строк, до которого размер поля может быть увеличен |
106
+ | resizable | `boolean` | - | Может ли ли пользователь изменять размеры поля (если св-во не включено, поле автоматически меняет свой размер) |
107
+ | secure | `boolean \| "password"` | - | Режим ввода sensitive данных (пароля, API ключей, токенов, etc) |
108
+ | onResetContextClick | `() => void` | - | Действие при клике по кнопке сброса контекста |
109
+ | 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} |
35
110
  | key | `Key` | - | |
36
111
  ## FieldChat
37
112
  ### Props
38
113
  | name | type | default value | description |
39
114
  |------|------|---------------|-------------|
115
+ | layoutType* | enum LayoutType: `"mobile"`, `"tablet"`, `"desktop"`, `"desktopSmall"` | - | |
116
+ | handleSubmit* | `(value: string) => void` | - | Колбек действия при отправке |
117
+ | value | `string` | - | HTML-аттрибут value |
118
+ | onChange | `(value: string, e?: ChangeEvent<HTMLTextAreaElement>) => void` | - | Колбек смены значения |
119
+ | inputMode | enum InputMode: `"search"`, `"none"`, `"text"`, `"tel"`, `"email"`, `"decimal"`, `"numeric"`, `"url"` | - | Режим работы экранной клавиатуры |
120
+ | id | `string` | - | HTML-аттрибут id |
121
+ | name | `string` | - | HTML-аттрибут name |
122
+ | maxLength | `number` | - | Максимальное кол-во символов |
123
+ | disabled | `boolean` | - | Является ли поле деактивированным |
124
+ | readonly | `boolean` | - | Является ли поле доступным только на чтение |
125
+ | onFocus | `FocusEventHandler<HTMLTextAreaElement>` | - | Колбек получения фокуса |
126
+ | onBlur | `FocusEventHandler<HTMLTextAreaElement>` | - | Колбек потери фокуса |
127
+ | autoFocus | `boolean` | - | Включен ли авто-фокус |
128
+ | onKeyDown | `KeyboardEventHandler<HTMLTextAreaElement>` | - | Колбек нажатия клавиши клавиатуры |
129
+ | className | `string` | - | CSS-класс |
130
+ | caption | `string` | - | Подпись справа от лейбла |
131
+ | showHintIcon | `boolean` | - | Отображать иконку подсказки |
132
+ | validationState | enum ValidationState: `"error"`, `"default"`, `"warning"`, `"success"` | - | Состояние валидации |
133
+ | labelTooltipPlacement | enum Placement: `"left"`, `"left-start"`, `"left-end"`, `"right"`, `"right-start"`, `"right-end"`, `"top"`, `"top-start"`, `"top-end"`, `"bottom"`, `"bottom-start"`, `"bottom-end"` | top | Расположение подсказки лейбла |
134
+ | error | `string` | - | |
135
+ | allowMoreThanMaxLength | `boolean` | - | Можно ли вводить больше разрешённого кол-ва символов |
136
+ | showCopyButton | `boolean` | - | Отображение кнопки Копировать для поля (актуально только для `readonly = true`) |
137
+ | onCopyButtonClick | `() => void` | - | Колбек клика по кнопке Копировать для поля |
138
+ | showClearButton | `boolean` | true | Отображение кнопки очистки поля |
139
+ | minRows | `number` | 3 | Минимальное кол-во строк, до которого размер поля может быть увеличен |
140
+ | maxRows | `number` | 1000 | Максимальное кол-во строк, до которого размер поля может быть увеличен |
141
+ | resizable | `boolean` | - | Может ли ли пользователь изменять размеры поля (если св-во не включено, поле автоматически меняет свой размер) |
142
+ | attachment | `Pick<FileUploadProps, "onFilesUpload" \| "accept"> & { files?: File[]; onFileDelete: (file?: File) => void; }` | - | |
40
143
  ## AIDisclaimer
41
144
  ### Props
42
145
  | name | type | default value | description |
43
146
  |------|------|---------------|-------------|
147
+ | layoutType* | enum LayoutType: `"mobile"`, `"tablet"`, `"desktop"`, `"desktopSmall"` | - | |
44
148
  ## FieldName
45
149
  Поле имя c локальным стейтом и валидацией
46
150
  ### Props
47
151
  | name | type | default value | description |
48
152
  |------|------|---------------|-------------|
153
+ | layoutType* | enum LayoutType: `"mobile"`, `"tablet"`, `"desktop"`, `"desktopSmall"` | - | |
154
+ | value | `string` | - | Значение input |
155
+ | onChange | `(value: string, e?: ChangeEvent<HTMLInputElement>) => void` | - | Колбек смены значения |
156
+ | id | `string` | - | Значение html-атрибута id |
157
+ | name | `string` | - | Значение html-атрибута name |
158
+ | maxLength | `number` | - | Максимальная длина вводимого значения |
159
+ | disabled | `boolean` | false | Является ли поле деактивированным |
160
+ | readonly | `boolean` | false | Является ли поле доступным только для чтения |
161
+ | onFocus | `FocusEventHandler<HTMLInputElement>` | - | Колбек обработки получения фокуса |
162
+ | onBlur | `FocusEventHandler<HTMLInputElement>` | - | Колбек обработки потери фокуса |
163
+ | autoComplete | `string \| boolean` | false | Включен ли автокомплит для поля |
164
+ | autoFocus | `boolean` | false | Включен ли авто-фокус для поля |
165
+ | onPaste | `ClipboardEventHandler<HTMLInputElement>` | - | Колбек обработки вставки значения |
166
+ | onKeyDown | `KeyboardEventHandler<HTMLInputElement>` | - | Колбек обработки начала нажатия клавиши клавиатуры |
167
+ | spellCheck | `boolean` | true | Значение атрибута spellcheck (проверка орфографии) |
168
+ | pattern | `string` | - | Регулярное выражение валидного инпута |
169
+ | className | `string` | - | CSS-класс |
170
+ | labelTooltip | `ReactNode` | - | Всплывающая подсказка лейбла |
171
+ | required | `boolean` | - | Является ли поле обязательным |
172
+ | showHintIcon | `boolean` | - | Отображать иконку подсказки |
173
+ | size | enum Size: `"s"`, `"m"`, `"l"` | - | Размер |
174
+ | validationState | enum ValidationState: `"error"`, `"default"`, `"warning"`, `"success"` | - | Состояние валидации |
175
+ | labelTooltipPlacement | enum Placement: `"left"`, `"left-start"`, `"left-end"`, `"right"`, `"right-start"`, `"right-end"`, `"top"`, `"top-start"`, `"top-end"`, `"bottom"`, `"bottom-start"`, `"bottom-end"` | top | Расположение подсказки лейбла |
176
+ | error | `string` | - | |
177
+ | prefix | `ReactNode` | - | Произвольный префикс для поля |
178
+ | prefixIcon | `ReactElement<any, string \| JSXElementConstructor<any>>` | - | Иконка-префикс для поля |
179
+ | postfix | `ReactNode` | - | Произвольный постфикс для поля |
180
+ | allowMoreThanMaxLength | `boolean` | - | Можно ли вводить больше разрешённого кол-ва символов |
181
+ | button | `Button` | - | Кнопка действия внутри поля |
182
+ | showCopyButton | `boolean` | - | Отображение кнопки Копировать для поля (актуально только для `readonly = true`) |
183
+ | onCopyButtonClick | `() => void` | - | Колбек клика по кнопке Копировать для поля |
184
+ | showClearButton | `boolean` | true | Отображение кнопки очистки поля |
185
+ | onClearButtonClick | `() => void` | - | Колбек клика по кнопке очистки поля |
186
+ | showLabel | `boolean` | - | |
187
+ | customSchema | `StringSchema<string, AnyObject, undefined, "">` | - | |
188
+ | onValidationError | `(error: ValidationError) => void` | - | Колбэк, вызываемый при изменении ошибки валидации |
49
189
  | 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} |
50
190
  | key | `Key` | - | |
51
191
  ## FieldNameRHF
@@ -53,6 +193,42 @@
53
193
  ### Props
54
194
  | name | type | default value | description |
55
195
  |------|------|---------------|-------------|
196
+ | layoutType* | enum LayoutType: `"mobile"`, `"tablet"`, `"desktop"`, `"desktopSmall"` | - | |
197
+ | controllerProps* | `Omit<ControllerProps<FieldValues>, "disabled" \| "render" \| "rules">` | - | Режим контроллера с использованием react-hook-form |
198
+ | value | `string` | - | Значение input |
199
+ | onChange | `(value: string, e?: ChangeEvent<HTMLInputElement>) => void` | - | Колбек смены значения |
200
+ | id | `string` | - | Значение html-атрибута id |
201
+ | name | `string` | - | Значение html-атрибута name |
202
+ | maxLength | `number` | - | Максимальная длина вводимого значения |
203
+ | disabled | `boolean` | false | Является ли поле деактивированным |
204
+ | readonly | `boolean` | false | Является ли поле доступным только для чтения |
205
+ | onFocus | `FocusEventHandler<HTMLInputElement>` | - | Колбек обработки получения фокуса |
206
+ | onBlur | `FocusEventHandler<HTMLInputElement>` | - | Колбек обработки потери фокуса |
207
+ | autoComplete | `string \| boolean` | false | Включен ли автокомплит для поля |
208
+ | autoFocus | `boolean` | false | Включен ли авто-фокус для поля |
209
+ | onPaste | `ClipboardEventHandler<HTMLInputElement>` | - | Колбек обработки вставки значения |
210
+ | onKeyDown | `KeyboardEventHandler<HTMLInputElement>` | - | Колбек обработки начала нажатия клавиши клавиатуры |
211
+ | spellCheck | `boolean` | true | Значение атрибута spellcheck (проверка орфографии) |
212
+ | pattern | `string` | - | Регулярное выражение валидного инпута |
213
+ | className | `string` | - | CSS-класс |
214
+ | labelTooltip | `ReactNode` | - | Всплывающая подсказка лейбла |
215
+ | required | `boolean` | - | Является ли поле обязательным |
216
+ | showHintIcon | `boolean` | - | Отображать иконку подсказки |
217
+ | size | enum Size: `"s"`, `"m"`, `"l"` | - | Размер |
218
+ | validationState | enum ValidationState: `"error"`, `"default"`, `"warning"`, `"success"` | - | Состояние валидации |
219
+ | labelTooltipPlacement | enum Placement: `"left"`, `"left-start"`, `"left-end"`, `"right"`, `"right-start"`, `"right-end"`, `"top"`, `"top-start"`, `"top-end"`, `"bottom"`, `"bottom-start"`, `"bottom-end"` | top | Расположение подсказки лейбла |
220
+ | error | `string` | - | |
221
+ | prefix | `ReactNode` | - | Произвольный префикс для поля |
222
+ | prefixIcon | `ReactElement<any, string \| JSXElementConstructor<any>>` | - | Иконка-префикс для поля |
223
+ | postfix | `ReactNode` | - | Произвольный постфикс для поля |
224
+ | allowMoreThanMaxLength | `boolean` | - | Можно ли вводить больше разрешённого кол-ва символов |
225
+ | button | `Button` | - | Кнопка действия внутри поля |
226
+ | showCopyButton | `boolean` | - | Отображение кнопки Копировать для поля (актуально только для `readonly = true`) |
227
+ | onCopyButtonClick | `() => void` | - | Колбек клика по кнопке Копировать для поля |
228
+ | showClearButton | `boolean` | true | Отображение кнопки очистки поля |
229
+ | onClearButtonClick | `() => void` | - | Колбек клика по кнопке очистки поля |
230
+ | showLabel | `boolean` | - | |
231
+ | customSchema | `StringSchema<string, AnyObject, undefined, "">` | - | |
56
232
  | 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} |
57
233
  | key | `Key` | - | |
58
234
  ## FieldDescription
@@ -60,14 +236,74 @@
60
236
  ### Props
61
237
  | name | type | default value | description |
62
238
  |------|------|---------------|-------------|
63
- | ref | `LegacyRef<HTMLTextAreaElement>` | - | 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} |
239
+ | layoutType* | enum LayoutType: `"mobile"`, `"tablet"`, `"desktop"`, `"desktopSmall"` | - | |
240
+ | value | `string` | - | HTML-аттрибут value |
241
+ | onChange | `(value: string, e?: ChangeEvent<HTMLTextAreaElement>) => void` | - | Колбек смены значения |
242
+ | id | `string` | - | HTML-аттрибут id |
243
+ | maxLength | `number` | 255 | Максимальное кол-во символов |
244
+ | disabled | `boolean` | - | Является ли поле деактивированным |
245
+ | readonly | `boolean` | - | Является ли поле доступным только на чтение |
246
+ | onFocus | `FocusEventHandler<HTMLTextAreaElement>` | - | Колбек получения фокуса |
247
+ | onBlur | `FocusEventHandler<HTMLTextAreaElement>` | - | Колбек потери фокуса |
248
+ | autoFocus | `boolean` | - | Включен ли авто-фокус |
249
+ | onKeyDown | `KeyboardEventHandler<HTMLTextAreaElement>` | - | Колбек нажатия клавиши клавиатуры |
250
+ | spellCheck | `boolean` | true | Включение проверки орфографии |
251
+ | className | `string` | - | CSS-класс |
252
+ | labelTooltip | `ReactNode` | - | Всплывающая подсказка лейбла |
253
+ | required | `boolean` | - | Является ли поле обязательным |
254
+ | showHintIcon | `boolean` | - | Отображать иконку подсказки |
255
+ | size | enum Size: `"s"`, `"m"`, `"l"` | m | Размер |
256
+ | validationState | enum ValidationState: `"error"`, `"default"`, `"warning"`, `"success"` | - | Состояние валидации |
257
+ | labelTooltipPlacement | enum Placement: `"left"`, `"left-start"`, `"left-end"`, `"right"`, `"right-start"`, `"right-end"`, `"top"`, `"top-start"`, `"top-end"`, `"bottom"`, `"bottom-start"`, `"bottom-end"` | top | Расположение подсказки лейбла |
258
+ | error | `string` | - | |
259
+ | allowMoreThanMaxLength | `boolean` | - | Можно ли вводить больше разрешённого кол-ва символов |
260
+ | showCopyButton | `boolean` | - | Отображение кнопки Копировать для поля (актуально только для `readonly = true`) |
261
+ | onCopyButtonClick | `() => void` | - | Колбек клика по кнопке Копировать для поля |
262
+ | showClearButton | `boolean` | true | Отображение кнопки очистки поля |
263
+ | minRows | `number` | 3 | Минимальное кол-во строк, до которого размер поля может быть увеличен |
264
+ | maxRows | `number` | 1000 | Максимальное кол-во строк, до которого размер поля может быть увеличен |
265
+ | resizable | `boolean` | true | Может ли ли пользователь изменять размеры поля (если св-во не включено, поле автоматически меняет свой размер) |
266
+ | customSchema | `StringSchema<string, AnyObject, undefined, "">` | - | |
267
+ | addButton | `boolean` | - | Поле появляется по кнопке "Добавить описание" (только для опционального поля) |
268
+ | onValidationError | `(error: ValidationError) => void` | - | Колбэк, вызываемый при изменении ошибки валидации (только в standalone режиме) |
269
+ | 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} |
64
270
  | key | `Key` | - | |
65
271
  ## FieldDescriptionRHF
66
272
  Поле описание c оберткой для React Hook Form
67
273
  ### Props
68
274
  | name | type | default value | description |
69
275
  |------|------|---------------|-------------|
70
- | ref | `LegacyRef<HTMLTextAreaElement>` | - | 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} |
276
+ | layoutType* | enum LayoutType: `"mobile"`, `"tablet"`, `"desktop"`, `"desktopSmall"` | - | |
277
+ | controllerProps* | `Omit<ControllerProps<FieldValues>, "disabled" \| "render" \| "rules">` | - | Режим контроллера с использованием react-hook-form |
278
+ | value | `string` | - | HTML-аттрибут value |
279
+ | onChange | `(value: string, e?: ChangeEvent<HTMLTextAreaElement>) => void` | - | Колбек смены значения |
280
+ | id | `string` | - | HTML-аттрибут id |
281
+ | maxLength | `number` | 255 | Максимальное кол-во символов |
282
+ | disabled | `boolean` | - | Является ли поле деактивированным |
283
+ | readonly | `boolean` | - | Является ли поле доступным только на чтение |
284
+ | onFocus | `FocusEventHandler<HTMLTextAreaElement>` | - | Колбек получения фокуса |
285
+ | onBlur | `FocusEventHandler<HTMLTextAreaElement>` | - | Колбек потери фокуса |
286
+ | autoFocus | `boolean` | - | Включен ли авто-фокус |
287
+ | onKeyDown | `KeyboardEventHandler<HTMLTextAreaElement>` | - | Колбек нажатия клавиши клавиатуры |
288
+ | spellCheck | `boolean` | true | Включение проверки орфографии |
289
+ | className | `string` | - | CSS-класс |
290
+ | labelTooltip | `ReactNode` | - | Всплывающая подсказка лейбла |
291
+ | required | `boolean` | - | Является ли поле обязательным |
292
+ | showHintIcon | `boolean` | - | Отображать иконку подсказки |
293
+ | size | enum Size: `"s"`, `"m"`, `"l"` | m | Размер |
294
+ | validationState | enum ValidationState: `"error"`, `"default"`, `"warning"`, `"success"` | - | Состояние валидации |
295
+ | labelTooltipPlacement | enum Placement: `"left"`, `"left-start"`, `"left-end"`, `"right"`, `"right-start"`, `"right-end"`, `"top"`, `"top-start"`, `"top-end"`, `"bottom"`, `"bottom-start"`, `"bottom-end"` | top | Расположение подсказки лейбла |
296
+ | error | `string` | - | |
297
+ | allowMoreThanMaxLength | `boolean` | - | Можно ли вводить больше разрешённого кол-ва символов |
298
+ | showCopyButton | `boolean` | - | Отображение кнопки Копировать для поля (актуально только для `readonly = true`) |
299
+ | onCopyButtonClick | `() => void` | - | Колбек клика по кнопке Копировать для поля |
300
+ | showClearButton | `boolean` | true | Отображение кнопки очистки поля |
301
+ | minRows | `number` | 3 | Минимальное кол-во строк, до которого размер поля может быть увеличен |
302
+ | maxRows | `number` | 1000 | Максимальное кол-во строк, до которого размер поля может быть увеличен |
303
+ | resizable | `boolean` | true | Может ли ли пользователь изменять размеры поля (если св-во не включено, поле автоматически меняет свой размер) |
304
+ | customSchema | `StringSchema<string, AnyObject, undefined, "">` | - | |
305
+ | addButton | `boolean` | - | Поле появляется по кнопке "Добавить описание" (только для опционального поля) |
306
+ | 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} |
71
307
  | key | `Key` | - | |
72
308
 
73
309
 
@@ -37,22 +37,44 @@ exports.FieldAi = (0, react_1.forwardRef)((_a, ref) => {
37
37
  const { t } = (0, uikit_product_locale_1.useLocale)('FieldsPredefined');
38
38
  const isTouchDevice = (0, helpers_1.isTouchDevice)(layoutType);
39
39
  const [isValueHidden, setIsValueHidden] = (0, react_1.useState)(true);
40
+ const [animatedValidationKey, setAnimatedValidationKey] = (0, react_1.useState)(null);
41
+ const timerRef = (0, react_1.useRef)(null);
40
42
  const isValueValid = typeof value === 'string' && value.trim().length > 0;
41
43
  const isPasswordMode = secure === 'password';
42
44
  const passwordValidation = (0, react_1.useMemo)(() => (0, utils_1.getValidationPassword)(value), [value]);
43
45
  const isPasswordValid = isPasswordMode ? Object.values(passwordValidation).every(Boolean) : true;
44
46
  const showPasswordError = !isPasswordValid && secure && value;
47
+ (0, react_1.useEffect)(() => () => {
48
+ if (timerRef.current) {
49
+ clearTimeout(timerRef.current);
50
+ }
51
+ }, []);
45
52
  const handleSubmit = () => {
46
53
  if (isValueValid && isPasswordValid) {
47
54
  handleSubmitProp(value);
48
55
  }
49
56
  };
50
- const handleKeyDown = e => {
57
+ const triggerValidationHighlight = (key) => {
58
+ setAnimatedValidationKey(key);
59
+ timerRef.current = setTimeout(() => {
60
+ setAnimatedValidationKey(null);
61
+ }, 1000);
62
+ };
63
+ const handleKeyDown = event => {
51
64
  if (isTouchDevice) {
52
65
  return;
53
66
  }
54
- if (e.key === 'Enter' && !e.shiftKey) {
55
- e.preventDefault();
67
+ if (isPasswordMode && event.key.length === 1) {
68
+ const isLetter = /\p{L}/u.test(event.key);
69
+ const isLatinLetter = /^[a-zA-Z]$/.test(event.key);
70
+ if (isLetter && !isLatinLetter) {
71
+ event.preventDefault();
72
+ triggerValidationHighlight('onlyLatin');
73
+ return;
74
+ }
75
+ }
76
+ if (event.key === 'Enter' && !event.shiftKey) {
77
+ event.preventDefault();
56
78
  if (!disabled) {
57
79
  handleSubmit();
58
80
  }
@@ -62,5 +84,5 @@ exports.FieldAi = (0, react_1.forwardRef)((_a, ref) => {
62
84
  if (isTouchDevice && !secure) {
63
85
  return ((0, jsx_runtime_1.jsx)(MobileFieldAi_1.MobileFieldAi, Object.assign({}, props, (0, uikit_product_mobile_fields_1.getAdaptiveFieldProps)(props), { onSubmit: handleSubmit, submitEnabled: isValueValid && !disabled, ref: ref, value: value })));
64
86
  }
65
- return ((0, jsx_runtime_1.jsxs)("div", { className: (0, classnames_1.default)(styles_module_scss_1.default.wrapper, className), children: [(0, jsx_runtime_1.jsx)(WithPasswordValidation_1.WithPasswordValidation, { showValidation: isPasswordMode, passwordValidation: passwordValidation, layoutType: layoutType, children: (0, jsx_runtime_1.jsx)(uikit_product_mobile_fields_1.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: (0, classnames_1.default)(styles_module_scss_1.default.textarea, secure && isValueHidden ? styles_module_scss_1.default.secured : undefined), onKeyDown: handleKeyDown, spellCheck: !secure, validationState: showPasswordError ? 'error' : validationState, footer: (0, jsx_runtime_1.jsx)(TextAreaActionsFooter_1.TextAreaActionsFooter, { left: secure && ((0, jsx_runtime_1.jsx)(button_1.ButtonFunction, { size: 'xs', icon: isValueHidden ? (0, jsx_runtime_1.jsx)(uikit_product_icons_1.EyeSVG, {}) : (0, jsx_runtime_1.jsx)(uikit_product_icons_1.EyeClosedSVG, {}), onClick: () => setIsValueHidden(prev => !prev) })), right: (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [secure && onResetContextClick && ((0, jsx_runtime_1.jsx)(tooltip_1.Tooltip, { tip: t('FieldAi.resetContext.tooltip'), hoverDelayOpen: 600, children: (0, jsx_runtime_1.jsx)(button_1.ButtonOutline, { size: 'xs', label: t('FieldAi.resetContext.label'), onClick: onResetContextClick, appearance: 'destructive' }) })), (0, jsx_runtime_1.jsx)(FieldSubmitButton_1.FieldSubmitButton, { showTooltip: !isTouchDevice, className: (0, classnames_1.default)(styles_module_scss_1.default.submitButton, isTouchDevice ? styles_module_scss_1.default.mobileSubmitButton : undefined), active: isValueValid && !disabled && isPasswordValid, handleClick: handleSubmit, size: isTouchDevice ? 's' : 'xs' })] }) }) })) }), !isPasswordMode && (0, jsx_runtime_1.jsx)(AIDisclaimer_1.AIDisclaimer, { layoutType: layoutType })] }));
87
+ return ((0, jsx_runtime_1.jsxs)("div", { className: (0, classnames_1.default)(styles_module_scss_1.default.wrapper, className), children: [(0, jsx_runtime_1.jsx)(WithPasswordValidation_1.WithPasswordValidation, { showValidation: isPasswordMode, passwordValidation: passwordValidation, layoutType: layoutType, animatedKey: animatedValidationKey, children: (0, jsx_runtime_1.jsx)(uikit_product_mobile_fields_1.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: (0, classnames_1.default)(styles_module_scss_1.default.textarea, secure && isValueHidden ? styles_module_scss_1.default.secured : undefined), onKeyDown: handleKeyDown, spellCheck: !secure, validationState: showPasswordError ? 'error' : validationState, footer: (0, jsx_runtime_1.jsx)(TextAreaActionsFooter_1.TextAreaActionsFooter, { left: secure && ((0, jsx_runtime_1.jsx)(button_1.ButtonFunction, { size: 'xs', icon: isValueHidden ? (0, jsx_runtime_1.jsx)(uikit_product_icons_1.EyeSVG, {}) : (0, jsx_runtime_1.jsx)(uikit_product_icons_1.EyeClosedSVG, {}), onClick: () => setIsValueHidden(prev => !prev) })), right: (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [secure && onResetContextClick && ((0, jsx_runtime_1.jsx)(tooltip_1.Tooltip, { tip: t('FieldAi.resetContext.tooltip'), hoverDelayOpen: 600, children: (0, jsx_runtime_1.jsx)(button_1.ButtonOutline, { size: 'xs', label: t('FieldAi.resetContext.label'), onClick: onResetContextClick, appearance: 'destructive' }) })), (0, jsx_runtime_1.jsx)(FieldSubmitButton_1.FieldSubmitButton, { showTooltip: !isTouchDevice, className: (0, classnames_1.default)(styles_module_scss_1.default.submitButton, isTouchDevice ? styles_module_scss_1.default.mobileSubmitButton : undefined), active: isValueValid && !disabled && isPasswordValid, handleClick: handleSubmit, size: isTouchDevice ? 's' : 'xs' })] }) }) })) }), !isPasswordMode && (0, jsx_runtime_1.jsx)(AIDisclaimer_1.AIDisclaimer, { layoutType: layoutType })] }));
66
88
  });
@@ -3,6 +3,7 @@ type CheckItemProps = WithLayoutType<{
3
3
  label: string;
4
4
  checked: boolean;
5
5
  shouldHide?: boolean;
6
+ animated?: boolean;
6
7
  }>;
7
- export declare function CheckItem({ label, checked, layoutType, shouldHide }: CheckItemProps): import("react/jsx-runtime").JSX.Element | null;
8
+ export declare function CheckItem({ label, checked, layoutType, shouldHide, animated }: CheckItemProps): import("react/jsx-runtime").JSX.Element | null;
8
9
  export {};
@@ -10,7 +10,7 @@ const uikit_product_icons_1 = require("@cloud-ru/uikit-product-icons");
10
10
  const typography_1 = require("@snack-uikit/typography");
11
11
  const styles_module_scss_1 = __importDefault(require('./styles.module.css'));
12
12
  const CHECKED_ITEM_DISAPPEAR_TIMEOUT = 500;
13
- function CheckItem({ label, checked, layoutType, shouldHide = false }) {
13
+ function CheckItem({ label, checked, layoutType, shouldHide = false, animated = false }) {
14
14
  const [visible, setVisible] = (0, react_1.useState)(checked);
15
15
  (0, react_1.useEffect)(() => {
16
16
  if (checked) {
@@ -24,5 +24,5 @@ function CheckItem({ label, checked, layoutType, shouldHide = false }) {
24
24
  if (shouldHide && !visible) {
25
25
  return null;
26
26
  }
27
- return ((0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.checkItem, "data-layout-type": layoutType, children: [(0, jsx_runtime_1.jsx)(uikit_product_icons_1.CheckSVG, { size: 20, className: styles_module_scss_1.default.icon, "data-checked": checked }), (0, jsx_runtime_1.jsx)(typography_1.Typography.SansBodyM, { className: styles_module_scss_1.default.label, "data-checked": checked, children: label })] }));
27
+ return ((0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.checkItem, "data-layout-type": layoutType, "data-animated": animated, children: [(0, jsx_runtime_1.jsx)(uikit_product_icons_1.CheckSVG, { size: 20, className: styles_module_scss_1.default.icon, "data-checked": checked }), (0, jsx_runtime_1.jsx)(typography_1.Typography.SansBodyM, { className: styles_module_scss_1.default.label, "data-checked": checked, children: label })] }));
28
28
  }
@@ -1,3 +1,14 @@
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
+ }
1
12
  .checkItem{
2
13
  display:inline-flex;
3
14
  gap:10px;
@@ -5,6 +16,9 @@
5
16
  .checkItem[data-layout-type=mobile], .checkItem[data-layout-type=tablet]{
6
17
  gap:8px;
7
18
  }
19
+ .checkItem[data-animated=true]{
20
+ animation:shake 0.5s ease-in-out;
21
+ }
8
22
 
9
23
  .icon{
10
24
  flex-shrink:0;
@@ -1,6 +1,7 @@
1
1
  import { WithLayoutType } from '@cloud-ru/uikit-product-utils';
2
- import { ValidationPassword } from '../../utils';
2
+ import { ValidationPassword, ValidationPasswordKey } from '../../utils';
3
3
  export type WithPasswordTooltipProps = WithLayoutType<{
4
4
  passwordValidation: ValidationPassword;
5
+ animatedKey?: ValidationPasswordKey | null;
5
6
  }>;
6
- export declare function PasswordValidation({ passwordValidation, layoutType }: WithPasswordTooltipProps): import("react/jsx-runtime").JSX.Element;
7
+ export declare function PasswordValidation({ passwordValidation, layoutType, animatedKey }: WithPasswordTooltipProps): import("react/jsx-runtime").JSX.Element;
@@ -10,7 +10,7 @@ const uikit_product_locale_1 = require("@cloud-ru/uikit-product-locale");
10
10
  const helpers_1 = require("../../../../helpers");
11
11
  const CheckItem_1 = require("../CheckItem");
12
12
  const styles_module_scss_1 = __importDefault(require('./styles.module.css'));
13
- function PasswordValidation({ passwordValidation, layoutType }) {
13
+ function PasswordValidation({ passwordValidation, layoutType, animatedKey }) {
14
14
  const { t } = (0, uikit_product_locale_1.useLocale)('FieldsPredefined');
15
15
  const allCriteriaMet = (0, react_1.useMemo)(() => Object.values(passwordValidation).every(item => item), [passwordValidation]);
16
16
  const isTouchDevice = (0, helpers_1.isTouchDevice)(layoutType);
@@ -19,5 +19,5 @@ function PasswordValidation({ passwordValidation, layoutType }) {
19
19
  return ((0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.validationItemsContainer, "data-layout-type": layoutType, children: (0, jsx_runtime_1.jsx)(CheckItem_1.CheckItem, { checked: true, label: t('FieldAi.secret.passwordTooltip.titleSuccess'), layoutType: layoutType, shouldHide: false }) }));
20
20
  }
21
21
  }
22
- return ((0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.validationItemsContainer, "data-layout-type": layoutType, children: (0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.validationList, children: [(0, jsx_runtime_1.jsx)(CheckItem_1.CheckItem, { checked: passwordValidation.minLength, label: t('FieldAi.secret.passwordTooltip.minLength'), layoutType: layoutType, shouldHide: isTouchDevice }), (0, jsx_runtime_1.jsx)(CheckItem_1.CheckItem, { checked: passwordValidation.onlyLatin, label: t('FieldAi.secret.passwordTooltip.onlyLatin'), layoutType: layoutType, shouldHide: isTouchDevice }), (0, jsx_runtime_1.jsx)(CheckItem_1.CheckItem, { checked: passwordValidation.hasLetterCases, label: t('FieldAi.secret.passwordTooltip.hasLetterCases'), layoutType: layoutType, shouldHide: isTouchDevice }), (0, jsx_runtime_1.jsx)(CheckItem_1.CheckItem, { checked: passwordValidation.hasNumber, label: t('FieldAi.secret.passwordTooltip.hasNumber'), layoutType: layoutType, shouldHide: isTouchDevice }), (0, jsx_runtime_1.jsx)(CheckItem_1.CheckItem, { checked: passwordValidation.hasSymbol, label: t('FieldAi.secret.passwordTooltip.hasSymbol'), layoutType: layoutType, shouldHide: isTouchDevice }), (0, jsx_runtime_1.jsx)(CheckItem_1.CheckItem, { checked: passwordValidation.noSpaces, label: t('FieldAi.secret.passwordTooltip.noSpaces'), layoutType: layoutType, shouldHide: isTouchDevice })] }) }));
22
+ return ((0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.validationItemsContainer, "data-layout-type": layoutType, children: (0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.validationList, children: [(0, jsx_runtime_1.jsx)(CheckItem_1.CheckItem, { checked: passwordValidation.minLength, label: t('FieldAi.secret.passwordTooltip.minLength'), layoutType: layoutType, shouldHide: isTouchDevice, animated: animatedKey === 'minLength' }), (0, jsx_runtime_1.jsx)(CheckItem_1.CheckItem, { checked: passwordValidation.onlyLatin, label: t('FieldAi.secret.passwordTooltip.onlyLatin'), layoutType: layoutType, shouldHide: isTouchDevice, animated: animatedKey === 'onlyLatin' }), (0, jsx_runtime_1.jsx)(CheckItem_1.CheckItem, { checked: passwordValidation.hasLetterCases, label: t('FieldAi.secret.passwordTooltip.hasLetterCases'), layoutType: layoutType, shouldHide: isTouchDevice, animated: animatedKey === 'hasLetterCases' }), (0, jsx_runtime_1.jsx)(CheckItem_1.CheckItem, { checked: passwordValidation.hasNumber, label: t('FieldAi.secret.passwordTooltip.hasNumber'), layoutType: layoutType, shouldHide: isTouchDevice, animated: animatedKey === 'hasNumber' }), (0, jsx_runtime_1.jsx)(CheckItem_1.CheckItem, { checked: passwordValidation.hasSymbol, label: t('FieldAi.secret.passwordTooltip.hasSymbol'), layoutType: layoutType, shouldHide: isTouchDevice, animated: animatedKey === 'hasSymbol' }), (0, jsx_runtime_1.jsx)(CheckItem_1.CheckItem, { checked: passwordValidation.noSpaces, label: t('FieldAi.secret.passwordTooltip.noSpaces'), layoutType: layoutType, shouldHide: isTouchDevice, animated: animatedKey === 'noSpaces' })] }) }));
23
23
  }
@@ -1,9 +1,10 @@
1
1
  import { ReactNode } from 'react';
2
2
  import { WithLayoutType } from '@cloud-ru/uikit-product-utils';
3
- import { ValidationPassword } from '../../utils';
3
+ import { ValidationPassword, ValidationPasswordKey } from '../../utils';
4
4
  export type WithPasswordTooltipProps = WithLayoutType<{
5
5
  showValidation?: boolean;
6
6
  children: ReactNode;
7
7
  passwordValidation: ValidationPassword;
8
+ animatedKey?: ValidationPasswordKey | null;
8
9
  }>;
9
- export declare function WithPasswordValidation({ showValidation, passwordValidation, layoutType, children, }: WithPasswordTooltipProps): string | number | boolean | import("react/jsx-runtime").JSX.Element | Iterable<ReactNode> | null | undefined;
10
+ export declare function WithPasswordValidation({ showValidation, passwordValidation, layoutType, children, animatedKey, }: WithPasswordTooltipProps): string | number | boolean | import("react/jsx-runtime").JSX.Element | Iterable<ReactNode> | null | undefined;
@@ -9,15 +9,15 @@ const uikit_product_mobile_tooltip_1 = require("@cloud-ru/uikit-product-mobile-t
9
9
  const helpers_1 = require("../../../../helpers");
10
10
  const PasswordValidation_1 = require("../PasswordValidation");
11
11
  const styles_module_scss_1 = __importDefault(require('./styles.module.css'));
12
- function WithPasswordValidation({ showValidation, passwordValidation, layoutType, children, }) {
12
+ function WithPasswordValidation({ showValidation, passwordValidation, layoutType, children, animatedKey, }) {
13
13
  if ((0, helpers_1.isTouchDevice)(layoutType)) {
14
14
  if (showValidation) {
15
- return ((0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.validationContainer, children: [(0, jsx_runtime_1.jsx)(PasswordValidation_1.PasswordValidation, { passwordValidation: passwordValidation, layoutType: layoutType }), children] }));
15
+ return ((0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.validationContainer, children: [(0, jsx_runtime_1.jsx)(PasswordValidation_1.PasswordValidation, { passwordValidation: passwordValidation, layoutType: layoutType, animatedKey: animatedKey }), children] }));
16
16
  }
17
17
  return children;
18
18
  }
19
19
  if (showValidation) {
20
- return ((0, jsx_runtime_1.jsx)(uikit_product_mobile_tooltip_1.AdaptiveTooltip, { placement: 'left-end', layoutType: layoutType, tip: (0, jsx_runtime_1.jsx)(PasswordValidation_1.PasswordValidation, { passwordValidation: passwordValidation, layoutType: layoutType }), offset: 8, children: children }));
20
+ return ((0, jsx_runtime_1.jsx)(uikit_product_mobile_tooltip_1.AdaptiveTooltip, { placement: 'left-end', layoutType: layoutType, tip: (0, jsx_runtime_1.jsx)(PasswordValidation_1.PasswordValidation, { passwordValidation: passwordValidation, layoutType: layoutType, animatedKey: animatedKey }), offset: 8, children: children }));
21
21
  }
22
22
  return children;
23
23
  }
@@ -6,4 +6,5 @@ export type ValidationPassword = {
6
6
  hasSymbol: boolean;
7
7
  noSpaces: boolean;
8
8
  };
9
+ export type ValidationPasswordKey = keyof ValidationPassword;
9
10
  export declare const getValidationPassword: (password?: string) => ValidationPassword;
@@ -11,7 +11,7 @@ var __rest = (this && this.__rest) || function (s, e) {
11
11
  };
12
12
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
13
13
  import cn from 'classnames';
14
- import { forwardRef, useMemo, useState } from 'react';
14
+ import { forwardRef, useEffect, useMemo, useRef, useState } from 'react';
15
15
  import { EyeClosedSVG, EyeSVG } from '@cloud-ru/uikit-product-icons';
16
16
  import { useLocale } from '@cloud-ru/uikit-product-locale';
17
17
  import { AdaptiveFieldTextArea, getAdaptiveFieldProps, } from '@cloud-ru/uikit-product-mobile-fields';
@@ -31,22 +31,44 @@ export const FieldAi = forwardRef((_a, ref) => {
31
31
  const { t } = useLocale('FieldsPredefined');
32
32
  const isTouchDevice = isTouchDeviceHelper(layoutType);
33
33
  const [isValueHidden, setIsValueHidden] = useState(true);
34
+ const [animatedValidationKey, setAnimatedValidationKey] = useState(null);
35
+ const timerRef = useRef(null);
34
36
  const isValueValid = typeof value === 'string' && value.trim().length > 0;
35
37
  const isPasswordMode = secure === 'password';
36
38
  const passwordValidation = useMemo(() => getValidationPassword(value), [value]);
37
39
  const isPasswordValid = isPasswordMode ? Object.values(passwordValidation).every(Boolean) : true;
38
40
  const showPasswordError = !isPasswordValid && secure && value;
41
+ useEffect(() => () => {
42
+ if (timerRef.current) {
43
+ clearTimeout(timerRef.current);
44
+ }
45
+ }, []);
39
46
  const handleSubmit = () => {
40
47
  if (isValueValid && isPasswordValid) {
41
48
  handleSubmitProp(value);
42
49
  }
43
50
  };
44
- const handleKeyDown = e => {
51
+ const triggerValidationHighlight = (key) => {
52
+ setAnimatedValidationKey(key);
53
+ timerRef.current = setTimeout(() => {
54
+ setAnimatedValidationKey(null);
55
+ }, 1000);
56
+ };
57
+ const handleKeyDown = event => {
45
58
  if (isTouchDevice) {
46
59
  return;
47
60
  }
48
- if (e.key === 'Enter' && !e.shiftKey) {
49
- e.preventDefault();
61
+ if (isPasswordMode && event.key.length === 1) {
62
+ const isLetter = /\p{L}/u.test(event.key);
63
+ const isLatinLetter = /^[a-zA-Z]$/.test(event.key);
64
+ if (isLetter && !isLatinLetter) {
65
+ event.preventDefault();
66
+ triggerValidationHighlight('onlyLatin');
67
+ return;
68
+ }
69
+ }
70
+ if (event.key === 'Enter' && !event.shiftKey) {
71
+ event.preventDefault();
50
72
  if (!disabled) {
51
73
  handleSubmit();
52
74
  }
@@ -56,5 +78,5 @@ export const FieldAi = forwardRef((_a, ref) => {
56
78
  if (isTouchDevice && !secure) {
57
79
  return (_jsx(MobileFieldAi, Object.assign({}, props, getAdaptiveFieldProps(props), { onSubmit: handleSubmit, submitEnabled: isValueValid && !disabled, ref: ref, value: value })));
58
80
  }
59
- return (_jsxs("div", { className: cn(styles.wrapper, className), children: [_jsx(WithPasswordValidation, { showValidation: isPasswordMode, passwordValidation: passwordValidation, layoutType: layoutType, 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 })] }));
81
+ return (_jsxs("div", { className: cn(styles.wrapper, className), children: [_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 })] }));
60
82
  });
@@ -3,6 +3,7 @@ type CheckItemProps = WithLayoutType<{
3
3
  label: string;
4
4
  checked: boolean;
5
5
  shouldHide?: boolean;
6
+ animated?: boolean;
6
7
  }>;
7
- export declare function CheckItem({ label, checked, layoutType, shouldHide }: CheckItemProps): import("react/jsx-runtime").JSX.Element | null;
8
+ export declare function CheckItem({ label, checked, layoutType, shouldHide, animated }: CheckItemProps): import("react/jsx-runtime").JSX.Element | null;
8
9
  export {};