@cloud-ru/uikit-product-claudia 1.6.2

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 (94) hide show
  1. package/CHANGELOG.md +112 -0
  2. package/LICENSE +201 -0
  3. package/README.md +586 -0
  4. package/package.json +60 -0
  5. package/src/components/ButtonClaudia/ButtonClaudia.tsx +33 -0
  6. package/src/components/ButtonClaudia/constants.ts +29 -0
  7. package/src/components/ButtonClaudia/helperComponents/ButtonPrivate/ButtonPrivate.tsx +99 -0
  8. package/src/components/ButtonClaudia/helperComponents/ButtonPrivate/constants.ts +13 -0
  9. package/src/components/ButtonClaudia/helperComponents/ButtonPrivate/index.ts +1 -0
  10. package/src/components/ButtonClaudia/helperComponents/ButtonPrivate/styles.module.scss +46 -0
  11. package/src/components/ButtonClaudia/helperComponents/ButtonPrivate/utils.tsx +92 -0
  12. package/src/components/ButtonClaudia/helperComponents/index.ts +1 -0
  13. package/src/components/ButtonClaudia/index.ts +1 -0
  14. package/src/components/ButtonClaudia/styles.module.scss +141 -0
  15. package/src/components/ButtonClaudia/types.ts +63 -0
  16. package/src/components/ButtonClaudia/utils.ts +15 -0
  17. package/src/components/ButtonGiga/ButtonGigaFunction/ButtonGigaFunction.tsx +43 -0
  18. package/src/components/ButtonGiga/ButtonGigaFunction/index.ts +1 -0
  19. package/src/components/ButtonGiga/ButtonGigaFunction/styles.module.scss +179 -0
  20. package/src/components/ButtonGiga/ButtonGigaFunction/types.ts +43 -0
  21. package/src/components/ButtonGiga/ButtonGigaFunction/utils.ts +16 -0
  22. package/src/components/ButtonGiga/ButtonGigaMama/ButtonGigaMama.tsx +29 -0
  23. package/src/components/ButtonGiga/ButtonGigaMama/index.ts +1 -0
  24. package/src/components/ButtonGiga/ButtonGigaMama/styles.module.scss +180 -0
  25. package/src/components/ButtonGiga/ButtonGigaMama/utils.ts +15 -0
  26. package/src/components/ButtonGiga/ButtonGigaOutline/ButtonGigaOutline.tsx +43 -0
  27. package/src/components/ButtonGiga/ButtonGigaOutline/index.ts +1 -0
  28. package/src/components/ButtonGiga/ButtonGigaOutline/styles.module.scss +223 -0
  29. package/src/components/ButtonGiga/ButtonGigaOutline/types.ts +63 -0
  30. package/src/components/ButtonGiga/ButtonGigaOutline/utils.ts +16 -0
  31. package/src/components/ButtonGiga/constants.ts +29 -0
  32. package/src/components/ButtonGiga/helperComponents/ButtonPrivate/ButtonPrivate.tsx +99 -0
  33. package/src/components/ButtonGiga/helperComponents/ButtonPrivate/constants.ts +15 -0
  34. package/src/components/ButtonGiga/helperComponents/ButtonPrivate/index.ts +1 -0
  35. package/src/components/ButtonGiga/helperComponents/ButtonPrivate/styles.module.scss +46 -0
  36. package/src/components/ButtonGiga/helperComponents/ButtonPrivate/types.ts +63 -0
  37. package/src/components/ButtonGiga/helperComponents/ButtonPrivate/utils.tsx +92 -0
  38. package/src/components/ButtonGiga/helperComponents/index.ts +1 -0
  39. package/src/components/ButtonGiga/index.ts +3 -0
  40. package/src/components/ButtonGiga/types.ts +43 -0
  41. package/src/components/ChatStatusAnnouncement/ChatStatusAnnouncement.tsx +109 -0
  42. package/src/components/ChatStatusAnnouncement/constants.ts +1 -0
  43. package/src/components/ChatStatusAnnouncement/helperComponents/AlertButton/AlertButton.tsx +24 -0
  44. package/src/components/ChatStatusAnnouncement/helperComponents/AlertButton/index.ts +1 -0
  45. package/src/components/ChatStatusAnnouncement/helperComponents/AlertButton/styles.module.scss +27 -0
  46. package/src/components/ChatStatusAnnouncement/helperComponents/TextContent/TextContent.tsx +18 -0
  47. package/src/components/ChatStatusAnnouncement/helperComponents/TextContent/index.ts +1 -0
  48. package/src/components/ChatStatusAnnouncement/index.ts +1 -0
  49. package/src/components/ChatStatusAnnouncement/styled.module.scss +65 -0
  50. package/src/components/ChatStatusAnnouncement/types.ts +17 -0
  51. package/src/components/ChatStatusAnnouncement/utils/index.ts +52 -0
  52. package/src/components/IconGiga/IconGiga.tsx +64 -0
  53. package/src/components/IconGiga/constants.ts +23 -0
  54. package/src/components/IconGiga/index.ts +1 -0
  55. package/src/components/RecommendPannel/RecommendPanel.tsx +131 -0
  56. package/src/components/RecommendPannel/helperComponents/Chip/Chip.tsx +47 -0
  57. package/src/components/RecommendPannel/helperComponents/Chip/index.ts +1 -0
  58. package/src/components/RecommendPannel/helperComponents/Chip/styles.module.scss +45 -0
  59. package/src/components/RecommendPannel/helperComponents/ClaudiaChip/ClaudiaChip.tsx +40 -0
  60. package/src/components/RecommendPannel/helperComponents/ClaudiaChip/index.ts +1 -0
  61. package/src/components/RecommendPannel/helperComponents/CloseChip/CloseChip.tsx +106 -0
  62. package/src/components/RecommendPannel/helperComponents/CloseChip/index.ts +1 -0
  63. package/src/components/RecommendPannel/helperComponents/CloseChip/styles.module.scss +73 -0
  64. package/src/components/RecommendPannel/helperComponents/DropdownChip/DropdownChip.tsx +112 -0
  65. package/src/components/RecommendPannel/helperComponents/DropdownChip/index.ts +1 -0
  66. package/src/components/RecommendPannel/helperComponents/DropdownChip/styles.module.scss +56 -0
  67. package/src/components/RecommendPannel/hooks/index.ts +15 -0
  68. package/src/components/RecommendPannel/index.ts +1 -0
  69. package/src/components/RecommendPannel/styles.module.scss +4 -0
  70. package/src/components/RecommendPannel/types.ts +21 -0
  71. package/src/components/RecommendPannel/utils/gitVisibleChipsCount.ts +57 -0
  72. package/src/components/SshField/SshField.tsx +222 -0
  73. package/src/components/SshField/components/MobileFieldAi/MobileFieldAi.tsx +71 -0
  74. package/src/components/SshField/components/MobileFieldAi/index.ts +1 -0
  75. package/src/components/SshField/components/MobileFieldAi/styles.module.scss +80 -0
  76. package/src/components/SshField/components/TextArea/TextArea.tsx +113 -0
  77. package/src/components/SshField/components/TextArea/index.ts +1 -0
  78. package/src/components/SshField/components/TextArea/styles.module.scss +35 -0
  79. package/src/components/SshField/helperComponents/DropZoneContent/DropZoneContent.tsx +15 -0
  80. package/src/components/SshField/helperComponents/DropZoneContent/index.ts +1 -0
  81. package/src/components/SshField/helperComponents/DropZoneContent/styles.module.scss +17 -0
  82. package/src/components/SshField/helperComponents/FieldSubmitButton/FieldSubmitButton.tsx +45 -0
  83. package/src/components/SshField/helperComponents/FieldSubmitButton/index.ts +1 -0
  84. package/src/components/SshField/helperComponents/TextAreaActionsFooter/TextAreaActionsFooter.tsx +18 -0
  85. package/src/components/SshField/helperComponents/TextAreaActionsFooter/index.ts +1 -0
  86. package/src/components/SshField/helperComponents/TextAreaActionsFooter/styles.module.scss +23 -0
  87. package/src/components/SshField/index.ts +1 -0
  88. package/src/components/SshField/styles.module.scss +54 -0
  89. package/src/components/SshField/utils/handleFileError.ts +41 -0
  90. package/src/components/SshField/utils/isTouchDevice.ts +5 -0
  91. package/src/components/SshField/utils/readFileContent.ts +23 -0
  92. package/src/components/SshField/utils/validateSSHKey.ts +84 -0
  93. package/src/components/index.ts +6 -0
  94. package/src/index.ts +1 -0
@@ -0,0 +1,222 @@
1
+ import cn from 'classnames';
2
+ import { DragEvent, forwardRef, KeyboardEventHandler, useState } from 'react';
3
+
4
+ import { themeVars } from '@sbercloud/figma-tokens-cloud-platform';
5
+ import { AttachmentSVG, EyeClosedSVG, EyeSVG, PasswordLockSVG } from '@cloud-ru/uikit-product-icons';
6
+ import { useLocale } from '@cloud-ru/uikit-product-locale';
7
+ import {
8
+ AdaptiveFieldTextArea,
9
+ FieldTextAreaProps,
10
+ getAdaptiveFieldProps,
11
+ } from '@cloud-ru/uikit-product-mobile-fields';
12
+ import { WithLayoutType } from '@cloud-ru/uikit-product-utils';
13
+ import { ButtonFunction } from '@snack-uikit/button';
14
+ import { DropZone, FileUpload } from '@snack-uikit/drop-zone';
15
+ import { Tooltip } from '@snack-uikit/tooltip';
16
+
17
+ import { ChatStatusAnnouncement } from '../ChatStatusAnnouncement';
18
+ import { MobileFieldAi } from './components/MobileFieldAi';
19
+ import { DropZoneContent } from './helperComponents/DropZoneContent';
20
+ import { FieldSubmitButton } from './helperComponents/FieldSubmitButton';
21
+ import { TextAreaActionsFooter } from './helperComponents/TextAreaActionsFooter';
22
+ import styles from './styles.module.scss';
23
+ import { FileErrorType, getFileErrorType } from './utils/handleFileError';
24
+ import { isTouchDevice as isTouchDeviceHelper } from './utils/isTouchDevice';
25
+ import { readFileContent } from './utils/readFileContent';
26
+ import { validateFileSize, validateFileType, validateSSHKeyContent } from './utils/validateSSHKey';
27
+
28
+ export type SshFieldProps = WithLayoutType<
29
+ Omit<FieldTextAreaProps, 'placeholder' | 'labelTooltip' | 'label' | 'required' | 'size' | 'spellCheck' | 'footer'> & {
30
+ /** Колбек действия при отправке */
31
+ onSubmit(value: string): void;
32
+ }
33
+ >;
34
+
35
+ export const SshField = forwardRef<HTMLTextAreaElement, SshFieldProps>(
36
+ ({ onSubmit: handleSubmitProp, value, disabled, className, ...props }, ref) => {
37
+ const { layoutType, validationState, onChange } = props;
38
+ const { t } = useLocale('Claudia');
39
+ const isTouchDevice = isTouchDeviceHelper(layoutType);
40
+ const [isLoading, setIsLoading] = useState<boolean>(false);
41
+ const [isDragOver, setIsDragOver] = useState(false);
42
+ const [isValueHidden, setIsValueHidden] = useState<boolean>(true);
43
+ const [fileErrorType, setFileErrorType] = useState<FileErrorType | null>(null);
44
+
45
+ const isValueValid = typeof value === 'string' && value.trim().length > 0;
46
+ const showFileError = Boolean(fileErrorType);
47
+
48
+ const getErrorMessage = (errorType: FileErrorType): string => {
49
+ switch (errorType) {
50
+ case 'EMPTY_FILE':
51
+ return t('SshField.errors.emptyFile');
52
+ case 'BINARY_DATA':
53
+ return t('SshField.errors.binaryData');
54
+ case 'INVALID_SSH_KEY':
55
+ return t('SshField.errors.invalidSSHKey');
56
+ case 'INVALID_EXTENSION':
57
+ case 'INVALID_MIME_TYPE':
58
+ case 'INVALID_FILE_TYPE':
59
+ return t('SshField.errors.invalidFileExtension');
60
+ case 'FILE_TOO_LARGE':
61
+ return t('SshField.errors.fileTooLarge');
62
+ case 'READ_ERROR':
63
+ return t('SshField.errors.readError');
64
+ case 'UNKNOWN_ERROR':
65
+ default:
66
+ return t('SshField.errors.unknownError');
67
+ }
68
+ };
69
+
70
+ const handleChange = (newValue: string) => {
71
+ if (fileErrorType) {
72
+ setFileErrorType(null);
73
+ }
74
+ if (onChange) {
75
+ onChange(newValue);
76
+ }
77
+ };
78
+
79
+ const handleDragOver = (event: DragEvent<HTMLDivElement>) => {
80
+ event.preventDefault();
81
+ setIsDragOver(true);
82
+ };
83
+
84
+ const handleDragLeave = (event: DragEvent<HTMLDivElement>) => {
85
+ event.preventDefault();
86
+ setIsDragOver(false);
87
+ };
88
+
89
+ const handleSubmit = () => {
90
+ if (isValueValid) {
91
+ handleSubmitProp(value);
92
+ }
93
+ };
94
+
95
+ const handleKeyDown: KeyboardEventHandler<HTMLTextAreaElement> = e => {
96
+ if (isTouchDevice) {
97
+ return;
98
+ }
99
+
100
+ if (e.key === 'Enter' && !e.shiftKey) {
101
+ e.preventDefault();
102
+
103
+ if (!disabled) {
104
+ handleSubmit();
105
+ }
106
+
107
+ return;
108
+ }
109
+ };
110
+
111
+ const onFileUpload = async (file: File) => {
112
+ try {
113
+ setIsLoading(true);
114
+ setFileErrorType(null);
115
+ validateFileType(file);
116
+ validateFileSize(file);
117
+
118
+ const fileContent = await readFileContent(file);
119
+
120
+ validateSSHKeyContent(fileContent);
121
+
122
+ if (onChange) {
123
+ onChange(fileContent);
124
+ }
125
+ } catch (err) {
126
+ const errorType = getFileErrorType(err);
127
+ setFileErrorType(errorType);
128
+ } finally {
129
+ setIsLoading(false);
130
+ setIsDragOver(false);
131
+ }
132
+ };
133
+
134
+ if (isTouchDevice) {
135
+ return (
136
+ <MobileFieldAi
137
+ {...props}
138
+ {...getAdaptiveFieldProps(props)}
139
+ onSubmit={handleSubmit}
140
+ submitEnabled={isValueValid && !disabled}
141
+ ref={ref}
142
+ value={value}
143
+ />
144
+ );
145
+ }
146
+
147
+ return (
148
+ <div className={cn(styles.wrapper, className)} onDragOver={handleDragOver} onDragLeave={handleDragLeave}>
149
+ <ChatStatusAnnouncement
150
+ className={styles.chatStatus}
151
+ layoutType={layoutType}
152
+ icon={<PasswordLockSVG size={16} color={themeVars.sys.neutral.textSupport} />}
153
+ content={t('SshField.chatStatusAnnouncement.content')}
154
+ actionLabel={t('SshField.chatStatusAnnouncement.cancel')}
155
+ onActionClick={() => {}}
156
+ />
157
+ {isDragOver ? (
158
+ <DropZone
159
+ description={<DropZoneContent />}
160
+ className={styles.dropZone}
161
+ mode='single'
162
+ onFilesUpload={(files: File[]) => onFileUpload(files[0])}
163
+ />
164
+ ) : (
165
+ <AdaptiveFieldTextArea
166
+ {...props}
167
+ ref={ref}
168
+ value={value}
169
+ onChange={handleChange}
170
+ size='m'
171
+ disabled={isLoading}
172
+ minRows={2}
173
+ maxRows={4}
174
+ placeholder={t('SshField.placeholder')}
175
+ className={isValueHidden ? styles.secured : undefined}
176
+ onKeyDown={handleKeyDown}
177
+ validationState={showFileError ? 'error' : validationState}
178
+ hint={showFileError && fileErrorType ? getErrorMessage(fileErrorType) : props.hint}
179
+ footer={
180
+ <TextAreaActionsFooter
181
+ left={
182
+ <ButtonFunction
183
+ size='xs'
184
+ icon={isValueHidden ? <EyeSVG /> : <EyeClosedSVG />}
185
+ onClick={() => setIsValueHidden(prev => !prev)}
186
+ disabled={isLoading}
187
+ />
188
+ }
189
+ right={
190
+ <>
191
+ <Tooltip
192
+ tip={t('SshField.attachFileTooltip')}
193
+ hoverDelayOpen={600}
194
+ triggerClassName={styles.uploadTooltip}
195
+ open={isTouchDevice ? false : undefined}
196
+ >
197
+ <FileUpload mode='single' onFilesUpload={(files: File[]) => onFileUpload(files[0])}>
198
+ <ButtonFunction
199
+ disabled={isLoading}
200
+ size={isTouchDevice ? 's' : 'xs'}
201
+ icon={<AttachmentSVG />}
202
+ />
203
+ </FileUpload>
204
+ </Tooltip>
205
+ <FieldSubmitButton
206
+ disabled={isLoading}
207
+ showTooltip={!isTouchDevice}
208
+ className={isTouchDevice ? styles.mobileSubmitButton : undefined}
209
+ active={isValueValid && !disabled}
210
+ handleClick={handleSubmit}
211
+ size={isTouchDevice ? 's' : 'xs'}
212
+ />
213
+ </>
214
+ }
215
+ />
216
+ }
217
+ />
218
+ )}
219
+ </div>
220
+ );
221
+ },
222
+ );
@@ -0,0 +1,71 @@
1
+ import { forwardRef } from 'react';
2
+
3
+ import { AttachmentSVG } from '@cloud-ru/uikit-product-icons';
4
+ import { useLocale } from '@cloud-ru/uikit-product-locale';
5
+ import { FieldTextAreaProps } from '@cloud-ru/uikit-product-mobile-fields';
6
+ import { ButtonFunction } from '@snack-uikit/button';
7
+ import { FileUpload } from '@snack-uikit/drop-zone';
8
+ import { Scroll } from '@snack-uikit/scroll';
9
+ import { Tooltip } from '@snack-uikit/tooltip';
10
+
11
+ import { FieldSubmitButton } from '../../helperComponents/FieldSubmitButton';
12
+ import { TextArea } from '../TextArea';
13
+ import styles from './styles.module.scss';
14
+
15
+ type MobileFieldAiProps = Omit<
16
+ FieldTextAreaProps,
17
+ 'placeholder' | 'labelTooltip' | 'label' | 'required' | 'size' | 'spellCheck' | 'footer'
18
+ > & {
19
+ onSubmit(): void;
20
+ submitEnabled: boolean;
21
+ };
22
+
23
+ const MIN_ROWS = 1;
24
+ const MAX_ROWS = 6;
25
+
26
+ export const MobileFieldAi = forwardRef<HTMLTextAreaElement, MobileFieldAiProps>(
27
+ ({ onSubmit, value, submitEnabled, ...props }, ref) => {
28
+ const { t } = useLocale('FieldsPredefined');
29
+
30
+ return (
31
+ <div
32
+ className={styles.mobileInputWrapper}
33
+ style={{ '--max-rows': MAX_ROWS, '--min-rows': MIN_ROWS }}
34
+ data-size='m'
35
+ >
36
+ <Scroll className={styles.scrollContainer} size='s' barHideStrategy='never'>
37
+ <TextArea
38
+ {...props}
39
+ className={styles.textarea}
40
+ ref={ref}
41
+ value={value}
42
+ minRows={MIN_ROWS}
43
+ placeholder={t('FieldAi.regular.placeholder')}
44
+ spellCheck={true}
45
+ />
46
+ </Scroll>
47
+
48
+ <div className={styles.mobileSubmitButtonWrapper}>
49
+ <Tooltip
50
+ disableSpanWrapper
51
+ tip={t('FieldChat.attachFileTooltip')}
52
+ hoverDelayOpen={600}
53
+ triggerClassName={styles.uploadTooltip}
54
+ >
55
+ <FileUpload mode='multiple' onFilesUpload={() => {}}>
56
+ <ButtonFunction size={'s'} icon={<AttachmentSVG />} />
57
+ </FileUpload>
58
+ </Tooltip>
59
+ <FieldSubmitButton
60
+ showTooltip={false}
61
+ className={styles.mobileSubmitButton}
62
+ fullWidth={true}
63
+ active={submitEnabled}
64
+ handleClick={onSubmit}
65
+ size='s'
66
+ />
67
+ </div>
68
+ </div>
69
+ );
70
+ },
71
+ );
@@ -0,0 +1 @@
1
+ export * from './MobileFieldAi';
@@ -0,0 +1,80 @@
1
+ @use '@sbercloud/figma-tokens-cloud-platform/build/scss/components/styles-tokens-fields' as ste;
2
+
3
+ $padding-right: (
4
+ 's': ste.$fields-buttons-s,
5
+ 'm': ste.$fields-buttons-m,
6
+ 'l': ste.$fields-buttons-m,
7
+ );
8
+ $scroll-bar-heights: (
9
+ 's': ste.$dimension-050m,
10
+ 'm': calc(#{ste.$dimension-025m} + #{ste.$dimension-050m}),
11
+ 'l': ste.$dimension-1m,
12
+ );
13
+ $sizes: 's', 'm', 'l';
14
+
15
+ .scrollContainer {
16
+ border-radius: 4px;
17
+ padding: 6px 0;
18
+
19
+ [data-overlayscrollbars-contents] {
20
+ display: flex;
21
+ }
22
+ }
23
+
24
+ .textarea {
25
+ overflow: hidden;
26
+ }
27
+
28
+ .mobileInputWrapper {
29
+ --max-rows: 1000;
30
+ --min-rows: 3;
31
+
32
+ display: flex;
33
+ position: relative;
34
+ gap: 8px;
35
+
36
+ .scrollContainer {
37
+ min-height: calc(var(--min-rows) * var(--row-height) + var(--horizontal-scroll-bar-height));
38
+ max-height: calc(var(--max-rows) * var(--row-height) + var(--horizontal-scroll-bar-height));
39
+ }
40
+
41
+ @each $size in $sizes {
42
+ &[data-size='#{$size}'] {
43
+ --row-height: #{ste.simple-var(ste.$theme-variables, 'sans', 'body', $size, 'line-height')};
44
+ --horizontal-scroll-bar-height: #{ste.simple-var($scroll-bar-heights, $size)};
45
+
46
+ .textarea {
47
+ @include ste.composite-var(ste.$theme-variables, 'sans', 'body', $size);
48
+
49
+ padding-right: calc(42px + ste.simple-var(ste.$fields-scroll-bar-width, 'width'));
50
+ }
51
+ }
52
+ }
53
+ }
54
+
55
+ .mobileSubmitButtonWrapper {
56
+ height: 32px;
57
+ display: flex;
58
+ flex-shrink: 0;
59
+ position: absolute;
60
+ bottom: 0;
61
+ right: 10px;
62
+ }
63
+
64
+ .mobileSubmitButton {
65
+ /* stylelint-disable-next-line declaration-no-important */
66
+ position: relative !important;
67
+ /* stylelint-disable-next-line declaration-no-important */
68
+ flex-shrink: 0 !important;
69
+
70
+ &:after {
71
+ content: '';
72
+ display: flex;
73
+ width: 48px;
74
+ height: 48px;
75
+ position: absolute;
76
+ top: 50%;
77
+ left: 50%;
78
+ transform: translate(-50%, -50%);
79
+ }
80
+ }
@@ -0,0 +1,113 @@
1
+ import cn from 'classnames';
2
+ import {
3
+ ChangeEvent,
4
+ ChangeEventHandler,
5
+ FocusEventHandler,
6
+ forwardRef,
7
+ KeyboardEventHandler,
8
+ MouseEventHandler,
9
+ RefAttributes,
10
+ useState,
11
+ } from 'react';
12
+ import TextareaAutosize from 'react-textarea-autosize';
13
+
14
+ import { InputPrivateProps } from '@snack-uikit/input-private';
15
+ import { extractSupportProps, useLayoutEffect, WithSupportProps } from '@snack-uikit/utils';
16
+
17
+ import styles from './styles.module.scss';
18
+
19
+ export type TextAreaProps = RefAttributes<HTMLTextAreaElement> &
20
+ WithSupportProps<{
21
+ /** HTML-аттрибут name */
22
+ name?: string;
23
+ /** HTML-аттрибут value */
24
+ value?: string;
25
+ /** Колбек смены значения */
26
+ onChange?(value: string, e: ChangeEvent<HTMLTextAreaElement>): void;
27
+ /** HTML-аттрибут id */
28
+ id?: string;
29
+ className?: string;
30
+ /** Плейсхолдер */
31
+ placeholder?: string;
32
+ /** Является ли поле доступным только на чтение */
33
+ readonly?: boolean;
34
+ /** Является ли поле деактивированным */
35
+ disabled?: boolean;
36
+ /** Включен ли автокомплит */
37
+ autoComplete?: boolean;
38
+ /** Максимальное кол-во символов */
39
+ maxLength?: number;
40
+ /** Колбек получения фокуса */
41
+ onFocus?: FocusEventHandler<HTMLTextAreaElement>;
42
+ /** Колбек потери фокуса */
43
+ onBlur?: FocusEventHandler<HTMLTextAreaElement>;
44
+ /** Колбек нажатия клавиши клавиатуры */
45
+ onKeyDown?: KeyboardEventHandler<HTMLTextAreaElement>;
46
+ /** HTML-аттрибут tab-index */
47
+ tabIndex?: number;
48
+ /** Минимальное кол-во строк, до которого размер поля может быть увеличен @default 3*/
49
+ minRows?: number;
50
+ /** Включение проверки орфографии @default true*/
51
+ spellCheck?: boolean;
52
+ /** Режим работы экранной клавиатуры */
53
+ inputMode?: InputPrivateProps['inputMode'];
54
+ }>;
55
+
56
+ export const TextArea = forwardRef<HTMLTextAreaElement, TextAreaProps>(
57
+ (
58
+ {
59
+ name,
60
+ value = '',
61
+ onChange,
62
+ placeholder,
63
+ id,
64
+ className,
65
+ disabled = false,
66
+ readonly = false,
67
+ autoComplete = false,
68
+ maxLength,
69
+ onFocus,
70
+ onBlur,
71
+ onKeyDown,
72
+ tabIndex,
73
+ minRows = 3,
74
+ spellCheck,
75
+ inputMode,
76
+ ...rest
77
+ },
78
+ ref,
79
+ ) => {
80
+ // fix of height on the initial render
81
+ // see https://github.com/Andarist/react-textarea-autosize/issues/337#issuecomment-1024980737
82
+ const [, setIsRerendered] = useState(false);
83
+ useLayoutEffect(() => setIsRerendered(true), []);
84
+
85
+ const onChangeHandler: ChangeEventHandler<HTMLTextAreaElement> = e => onChange?.(e.target.value, e);
86
+ const stopPropagation: MouseEventHandler<HTMLTextAreaElement> = e => e.stopPropagation();
87
+
88
+ return (
89
+ <TextareaAutosize
90
+ id={id}
91
+ name={name}
92
+ value={value}
93
+ ref={ref}
94
+ className={cn(className, styles.textarea)}
95
+ autoComplete={autoComplete ? 'on' : 'off'}
96
+ placeholder={placeholder}
97
+ disabled={disabled}
98
+ readOnly={readonly}
99
+ maxLength={maxLength}
100
+ onChange={onChangeHandler}
101
+ onClick={stopPropagation}
102
+ onFocus={onFocus}
103
+ onBlur={onBlur}
104
+ onKeyDown={onKeyDown}
105
+ tabIndex={tabIndex}
106
+ minRows={minRows}
107
+ spellCheck={spellCheck}
108
+ inputMode={inputMode}
109
+ {...extractSupportProps(rest)}
110
+ />
111
+ );
112
+ },
113
+ );
@@ -0,0 +1 @@
1
+ export * from './TextArea';
@@ -0,0 +1,35 @@
1
+ @use '@sbercloud/figma-tokens-cloud-platform/build/scss/components/styles-tokens-element' as ste;
2
+
3
+ .textarea {
4
+ resize: none;
5
+
6
+ box-sizing: border-box;
7
+ width: 100%;
8
+ max-width: 100%;
9
+ margin: 0;
10
+ padding: 0;
11
+
12
+ color: ste.simple-var(ste.$sys-neutral-text-main);
13
+
14
+ background-color: transparent;
15
+ border: none;
16
+ border-radius: 0;
17
+ outline: 0;
18
+
19
+ &::placeholder {
20
+ color: ste.simple-var(ste.$sys-neutral-text-disabled);
21
+ }
22
+
23
+ &::-webkit-scrollbar {
24
+ width: 0;
25
+ max-width: 0;
26
+ }
27
+
28
+ &:read-only {
29
+ color: ste.simple-var(ste.$sys-neutral-text-support);
30
+ }
31
+
32
+ &[disabled] {
33
+ color: ste.simple-var(ste.$sys-neutral-text-disabled);
34
+ }
35
+ }
@@ -0,0 +1,15 @@
1
+ import { useLocale } from '@cloud-ru/uikit-product-locale';
2
+ import { Typography } from '@snack-uikit/typography';
3
+
4
+ import styles from './styles.module.scss';
5
+
6
+ export function DropZoneContent() {
7
+ const { t } = useLocale('Claudia');
8
+
9
+ return (
10
+ <div className={styles.container}>
11
+ <Typography.SansTitleS className={styles.container}>{t('SshField.dropZone.title')}</Typography.SansTitleS>
12
+ <Typography.SansBodyS className={styles.container}>{t('SshField.dropZone.description')}</Typography.SansBodyS>
13
+ </div>
14
+ );
15
+ }
@@ -0,0 +1 @@
1
+ export * from './DropZoneContent';
@@ -0,0 +1,17 @@
1
+ @use '@sbercloud/figma-tokens-cloud-platform/build/scss/styles-theme-variables' as stv;
2
+
3
+ .container {
4
+ display: flex;
5
+ flex-direction: column;
6
+ justify-content: center;
7
+ align-items: center;
8
+ gap: 4px;
9
+ }
10
+
11
+ .defaultText {
12
+ color: stv.$sys-neutral-text-main;
13
+ }
14
+
15
+ .disclaimerText {
16
+ color: stv.$sys-green-text-support;
17
+ }
@@ -0,0 +1,45 @@
1
+ import { ArrowUpSVG } from '@cloud-ru/uikit-product-icons';
2
+ import { useLocale } from '@cloud-ru/uikit-product-locale';
3
+ import { ButtonFilled } from '@snack-uikit/button';
4
+ import { Tooltip } from '@snack-uikit/tooltip';
5
+
6
+ export type FieldSubmitButtonProps = {
7
+ active: boolean;
8
+ handleClick(): void;
9
+ size?: 'xs' | 's';
10
+ className?: string;
11
+ fullWidth?: boolean;
12
+ showTooltip?: boolean;
13
+ disabled?: boolean;
14
+ };
15
+
16
+ export function FieldSubmitButton({
17
+ active,
18
+ handleClick,
19
+ size = 'xs',
20
+ className,
21
+ fullWidth,
22
+ showTooltip = true,
23
+ disabled,
24
+ }: FieldSubmitButtonProps) {
25
+ const { t } = useLocale('FieldsPredefined');
26
+
27
+ if (active) {
28
+ return (
29
+ <Tooltip tip={t('FieldAi.submit.tooltip')} hoverDelayOpen={600} open={showTooltip ? undefined : false}>
30
+ <ButtonFilled
31
+ fullWidth={fullWidth}
32
+ icon={<ArrowUpSVG />}
33
+ size={size}
34
+ appearance='primary'
35
+ type='submit'
36
+ onClick={handleClick}
37
+ className={className}
38
+ disabled={disabled}
39
+ />
40
+ </Tooltip>
41
+ );
42
+ }
43
+
44
+ return <ButtonFilled icon={<ArrowUpSVG />} size={size} disabled className={className} fullWidth={fullWidth} />;
45
+ }
@@ -0,0 +1 @@
1
+ export * from './FieldSubmitButton';
@@ -0,0 +1,18 @@
1
+ import cn from 'classnames';
2
+ import { ReactNode } from 'react';
3
+
4
+ import styles from './styles.module.scss';
5
+
6
+ type TextAreaActionsFooterProps = {
7
+ left?: ReactNode;
8
+ right?: ReactNode;
9
+ };
10
+
11
+ export function TextAreaActionsFooter({ left, right }: TextAreaActionsFooterProps) {
12
+ return (
13
+ <div className={styles.actionsFooter}>
14
+ <div className={styles.actionsWrapper}>{left}</div>
15
+ <div className={cn(styles.actionsWrapper, styles.right)}>{right}</div>
16
+ </div>
17
+ );
18
+ }
@@ -0,0 +1 @@
1
+ export * from './TextAreaActionsFooter';
@@ -0,0 +1,23 @@
1
+ @use '@sbercloud/figma-tokens-cloud-platform/build/scss/styles-theme-variables' as ste;
2
+
3
+ .actionsFooter {
4
+ display: flex;
5
+ flex-direction: row;
6
+ justify-content: space-between;
7
+ align-items: center;
8
+ width: 100%;
9
+ padding-bottom: 7px;
10
+ }
11
+
12
+ .actionsWrapper {
13
+ display: flex;
14
+ flex-direction: row;
15
+ align-items: center;
16
+ gap: ste.$dimension-050m;
17
+ }
18
+
19
+ .right {
20
+ justify-content: flex-end;
21
+ width: 100%;
22
+ padding-right: 8px;
23
+ }
@@ -0,0 +1 @@
1
+ export * from './SshField';