kamotive_ui 1.2.3 → 1.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Icons/IconFile/IconFile.d.ts +6 -0
- package/dist/Icons/IconFile/IconFile.js +5 -0
- package/dist/Icons/IconUpload/IconUpload.d.ts +6 -0
- package/dist/Icons/IconUpload/IconUpload.js +5 -0
- package/dist/Icons/index.d.ts +2 -0
- package/dist/Icons/index.js +2 -0
- package/dist/colors.css +5 -1
- package/dist/components/Button/Button.js +86 -31
- package/dist/components/Button/Button.module.css +101 -38
- package/dist/components/ColorPicker/ColorPicker.js +84 -40
- package/dist/components/Dropdown/Dropdown.js +12 -3
- package/dist/components/FileAttach/FileAttach.d.ts +3 -0
- package/dist/components/FileAttach/FileAttach.js +79 -0
- package/dist/components/FileAttach/FileAttach.module.css +36 -0
- package/dist/components/Input/Input.js +12 -2
- package/dist/components/Loader/Loader.d.ts +3 -0
- package/dist/components/Loader/Loader.js +18 -0
- package/dist/components/Loader/Loader.module.css +75 -0
- package/dist/components/ProgressBar/ProgressBar.js +1 -1
- package/dist/components/SettingTag/SettingTag.js +6 -6
- package/dist/components/Spinner/Spinner.d.ts +3 -0
- package/dist/components/Spinner/Spinner.js +30 -0
- package/dist/components/Spinner/Spinner.module.css +20 -0
- package/dist/components/Tag/Tag.js +72 -12
- package/dist/components/Tag/Tag.module.css +21 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.js +3 -0
- package/dist/types/index.d.ts +54 -5
- package/package.json +3 -2
|
@@ -84,10 +84,11 @@ export const DropdownListItem = ({ item, getOptionLabel, size = 'md', selectedIt
|
|
|
84
84
|
})))));
|
|
85
85
|
};
|
|
86
86
|
export const Dropdown = ({ id, label, placeholder, size = 'lg', options, getOptionLabel, value, defaultValue, style = 'text', className, disabled = false, readOnly = false, isOpened = false, noOptionsText = 'Нет вариатов для выбора', isLeftLabel = false, error = false, helperText, onChange, onClose, clearable = true, required = false, isDivider = false, }) => {
|
|
87
|
+
var _a;
|
|
87
88
|
const [isOpen, setIsOpen] = useState(isOpened);
|
|
88
89
|
const [modifiedOptions, setModifiedOptions] = useState([]);
|
|
89
90
|
const [selectedItem, setSelectedItem] = useState(null);
|
|
90
|
-
const [errorInput, setErrorInput] = useState(
|
|
91
|
+
const [errorInput, setErrorInput] = useState(false);
|
|
91
92
|
const [errorInputHelperText, setErrorInputHelperText] = useState(helperText);
|
|
92
93
|
const [activeIndex, setActiveIndex] = useState(-1);
|
|
93
94
|
const containerRef = useRef(null);
|
|
@@ -154,6 +155,8 @@ export const Dropdown = ({ id, label, placeholder, size = 'lg', options, getOpti
|
|
|
154
155
|
};
|
|
155
156
|
//для сброса выбранного значения
|
|
156
157
|
const handleReset = (event) => {
|
|
158
|
+
event.preventDefault();
|
|
159
|
+
event.stopPropagation();
|
|
157
160
|
const startValue = defaultValue
|
|
158
161
|
? checkItem(defaultValue)
|
|
159
162
|
: null;
|
|
@@ -189,7 +192,7 @@ export const Dropdown = ({ id, label, placeholder, size = 'lg', options, getOpti
|
|
|
189
192
|
});
|
|
190
193
|
const selectedItemClassess = classNames({
|
|
191
194
|
[styles['item-selected']]: selectedItem,
|
|
192
|
-
[styles['item-placeholder']]: !selectedItem && (placeholder !== null && placeholder !== void 0 ? placeholder : label),
|
|
195
|
+
[styles['item-placeholder']]: !selectedItem && ((placeholder !== null && placeholder !== void 0 ? placeholder : label) || (!placeholder && !label)),
|
|
193
196
|
[styles['button--icons--item-selected']]: style === 'icons' && (selectedItem === null || selectedItem === void 0 ? void 0 : selectedItem.icon),
|
|
194
197
|
});
|
|
195
198
|
const getDropdownMenu = () => {
|
|
@@ -248,7 +251,13 @@ export const Dropdown = ({ id, label, placeholder, size = 'lg', options, getOpti
|
|
|
248
251
|
: null;
|
|
249
252
|
setSelectedItem(startValue !== null && startValue !== void 0 ? startValue : null);
|
|
250
253
|
}
|
|
254
|
+
else {
|
|
255
|
+
setSelectedItem(null);
|
|
256
|
+
}
|
|
251
257
|
}, [value, defaultValue, checkItem]);
|
|
258
|
+
useEffect(() => {
|
|
259
|
+
setErrorInput(error);
|
|
260
|
+
}, [error]);
|
|
252
261
|
return (React.createElement("div", { id: id, className: wrapperClassess, ref: containerRef, style: { width: isLeftLabel && containerWidth ? `${containerWidth}px` : '100%' } },
|
|
253
262
|
label && (React.createElement(Typography, { variant: "Caption", className: labelClasses }, label)),
|
|
254
263
|
React.createElement("button", { className: buttonClassess, onClick: readOnly ? undefined : handleToggle, disabled: disabled, tabIndex: 0, onKeyDown: handleKeyDown },
|
|
@@ -258,7 +267,7 @@ export const Dropdown = ({ id, label, placeholder, size = 'lg', options, getOpti
|
|
|
258
267
|
React.cloneElement(selectedItem.icon, {
|
|
259
268
|
strokeWidth: size === 'lg' ? '0.5' : size === 'md' ? '0.3' : '0.0',
|
|
260
269
|
}),
|
|
261
|
-
selectedItem ? selectedItem.value : (placeholder !== null && placeholder !== void 0 ? placeholder : label)),
|
|
270
|
+
selectedItem ? selectedItem.value : ((_a = placeholder !== null && placeholder !== void 0 ? placeholder : label) !== null && _a !== void 0 ? _a : 'Выберите значение')),
|
|
262
271
|
clearable && !readOnly && !disabled && selectedItem && (React.createElement("div", { className: styles.resetButton },
|
|
263
272
|
React.createElement(IconClose10, { strokeWidth: "0.2", htmlColor: "var(--text-light)", onClick: handleReset }))),
|
|
264
273
|
React.createElement("div", { className: styles.dropdownIcon }, !isOpen ? (React.createElement(ChevronDown10, { strokeWidth: size === 'lg' ? '0.5' : '0.3' })) : (React.createElement(ChevronUp10, { strokeWidth: size === 'lg' ? '0.5' : '0.3' }))),
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { useDropzone } from 'react-dropzone';
|
|
3
|
+
import styles from './FileAttach.module.css';
|
|
4
|
+
import { Typography } from '../Typography/Typography';
|
|
5
|
+
import { IconUpload } from '../../Icons';
|
|
6
|
+
import { Loader } from '../Loader/Loader';
|
|
7
|
+
export const FileAttach = ({ maxFileSize = 2, maxFileCount = 10, acceptedFormats = {
|
|
8
|
+
'image/*': ['.png', '.gif', '.jpeg', '.jpg'],
|
|
9
|
+
'application/pdf': ['.pdf'],
|
|
10
|
+
'application/msword': ['.doc', '.docx'],
|
|
11
|
+
}, addedFiles, setAddedFiles, disabled = false, }) => {
|
|
12
|
+
const [errorFiles, setErrorFiles] = useState([]);
|
|
13
|
+
const fileValidator = (file) => {
|
|
14
|
+
if (file.size > maxFileSize * 1024 * 1024 * 1024) {
|
|
15
|
+
return {
|
|
16
|
+
code: 'name-too-large',
|
|
17
|
+
message: `Максимальный размер файла ${maxFileSize.toFixed(0)} ГБ`,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
if (addedFiles.find((addedFile) => addedFile.name === file.name)) {
|
|
21
|
+
return {
|
|
22
|
+
code: 'repeating-file-name',
|
|
23
|
+
message: `Файл уже добавлен`,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
if (addedFiles.length > maxFileCount - 1) {
|
|
27
|
+
return {
|
|
28
|
+
code: 'files-count-too-large',
|
|
29
|
+
message: `Максимальное количество файлов ${maxFileCount}`,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
};
|
|
34
|
+
const { getRootProps, getInputProps } = useDropzone({
|
|
35
|
+
onDrop: (acceptedFiles, fileRejections) => {
|
|
36
|
+
setAddedFiles([...addedFiles, ...acceptedFiles]);
|
|
37
|
+
setErrorFiles([...errorFiles, ...fileRejections]);
|
|
38
|
+
},
|
|
39
|
+
validator: fileValidator,
|
|
40
|
+
accept: acceptedFormats,
|
|
41
|
+
maxFiles: maxFileCount,
|
|
42
|
+
disabled: disabled,
|
|
43
|
+
});
|
|
44
|
+
const acceptedFileItems = addedFiles.map((file, index) => (React.createElement(Loader, { name: file.name, size: file.size, onClick: () => deleteAcceptedFile(addedFiles, setAddedFiles, file.name), key: index })));
|
|
45
|
+
const fileRejectionItems = errorFiles.map(({ file, errors }) => (React.createElement(Loader, { name: file.name, size: file.size, error: errors[0].message, onClick: () => deleteRejectedFile(errorFiles, setErrorFiles, file.name), key: file.path })));
|
|
46
|
+
const deleteAcceptedFile = (addedFiles, setAddedFiles, fileName) => {
|
|
47
|
+
setAddedFiles(addedFiles.filter((file) => file.name !== fileName));
|
|
48
|
+
};
|
|
49
|
+
const deleteRejectedFile = (errorFiles, setErrorFiles, fileName) => {
|
|
50
|
+
setErrorFiles(errorFiles.filter(({ file }) => file.name !== fileName));
|
|
51
|
+
};
|
|
52
|
+
// Функция для получения всех доступных форматов в виде строки
|
|
53
|
+
const getAcceptedFormatsString = (acceptedFormats) => {
|
|
54
|
+
const formats = [];
|
|
55
|
+
for (const key in acceptedFormats) {
|
|
56
|
+
if (acceptedFormats.hasOwnProperty(key)) {
|
|
57
|
+
formats.push(...acceptedFormats[key].map(format => format.replace('.', '')));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return formats.join(', ');
|
|
61
|
+
};
|
|
62
|
+
return (React.createElement("section", { className: styles['fileAttach'] },
|
|
63
|
+
React.createElement("div", Object.assign({}, getRootProps({ className: `${styles['dropzone']} ${disabled ? styles['disabled'] : ''}` })),
|
|
64
|
+
React.createElement("input", Object.assign({}, getInputProps())),
|
|
65
|
+
React.createElement(IconUpload, { htmlColor: disabled ? 'var(--grey-medium)' : 'var(--icons-grey)' }),
|
|
66
|
+
React.createElement(Typography, { variant: "Body2-Medium", color: disabled ? 'var(--grey-medium)' : 'var(--text-dark)' },
|
|
67
|
+
React.createElement("span", { style: { textDecoration: 'underline' } }, "\u041D\u0430\u0436\u043C\u0438\u0442\u0435 \u043D\u0430 \u043E\u0431\u043B\u0430\u0441\u0442\u044C"),
|
|
68
|
+
React.createElement("span", null, " \u0438\u043B\u0438 \u043F\u0435\u0440\u0435\u0442\u0430\u0449\u0438\u0442\u0435 \u0444\u0430\u0439\u043B\u044B")),
|
|
69
|
+
React.createElement("div", null,
|
|
70
|
+
maxFileSize && (React.createElement(Typography, { variant: "Caption", color: "var(--grey-medium)" },
|
|
71
|
+
`Максимальный размер файла ${maxFileSize.toFixed(0)} ГБ`,
|
|
72
|
+
" ",
|
|
73
|
+
React.createElement("br", null))),
|
|
74
|
+
maxFileCount && (React.createElement(Typography, { variant: "Caption", color: "var(--grey-medium)" }, `За раз можно загрузить ${maxFileCount} ${maxFileCount > 1 ? `файлов` : `файл`}`)))),
|
|
75
|
+
acceptedFormats && (React.createElement(Typography, { variant: "Caption", color: "var(--grey-medium)" }, `Поддерживаемые форматы: ${getAcceptedFormatsString(acceptedFormats)}`)),
|
|
76
|
+
(addedFiles === null || addedFiles === void 0 ? void 0 : addedFiles.length) > 0 || (errorFiles === null || errorFiles === void 0 ? void 0 : errorFiles.length) > 0 ? (React.createElement("div", { className: styles['addedFiles'] },
|
|
77
|
+
acceptedFileItems,
|
|
78
|
+
fileRejectionItems)) : (React.createElement(Typography, { variant: "Body2-Medium", color: "var(--text-dark)" }, "\u0424\u0430\u0439\u043B\u044B \u043D\u0435 \u0434\u043E\u0431\u0430\u0432\u043B\u0435\u043D\u044B"))));
|
|
79
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
.fileAttach {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
height: inherit;
|
|
5
|
+
gap: 10px;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.dropzone {
|
|
9
|
+
display: flex;
|
|
10
|
+
flex-direction: column;
|
|
11
|
+
align-items: center;
|
|
12
|
+
justify-content: center;
|
|
13
|
+
|
|
14
|
+
gap: 10px;
|
|
15
|
+
padding: 30px 20px;
|
|
16
|
+
border: 1px dashed var(--blue-main);
|
|
17
|
+
border-radius: 15px;
|
|
18
|
+
cursor: pointer;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.dropzone:hover {
|
|
22
|
+
background-color: var(--fills-active);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.dropzone.disabled {
|
|
26
|
+
background-color: var(--fills-disabled);
|
|
27
|
+
border-color: var(--grey-medium);
|
|
28
|
+
cursor: not-allowed;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.addedFiles {
|
|
32
|
+
display: flex;
|
|
33
|
+
flex-direction: column;
|
|
34
|
+
gap: 10px;
|
|
35
|
+
overflow-y: auto;
|
|
36
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
2
|
;
|
|
3
3
|
import styles from './Input.module.css';
|
|
4
4
|
import classNames from 'classnames';
|
|
@@ -7,9 +7,19 @@ import { Typography } from '../Typography/Typography';
|
|
|
7
7
|
* Компонент Input для создания текстовых полей ввода различных стилей и размеров.
|
|
8
8
|
*/
|
|
9
9
|
export const Input = ({ id, label, placeholder, size = 'lg', value, className, multiline = false, rows = 4, resize = false, disabled = false, readOnly = false, isLeftLabel = false, icon, error = false, helperText, onChange, required = false, }) => {
|
|
10
|
+
const [inputLabel, setInputLabel] = useState(label);
|
|
10
11
|
const handleChange = (event) => {
|
|
11
12
|
event.stopPropagation();
|
|
12
13
|
onChange === null || onChange === void 0 ? void 0 : onChange(event);
|
|
14
|
+
if (label) {
|
|
15
|
+
setInputLabel(label);
|
|
16
|
+
}
|
|
17
|
+
else if (placeholder && event.target.value) {
|
|
18
|
+
setInputLabel(placeholder);
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
setInputLabel('');
|
|
22
|
+
}
|
|
13
23
|
};
|
|
14
24
|
const wrapperClassess = classNames(styles['wrapper--input'], {
|
|
15
25
|
[styles['wrapper--left']]: isLeftLabel,
|
|
@@ -31,7 +41,7 @@ export const Input = ({ id, label, placeholder, size = 'lg', value, className, m
|
|
|
31
41
|
[styles['label--required']]: required,
|
|
32
42
|
});
|
|
33
43
|
return (React.createElement("div", { className: wrapperClassess },
|
|
34
|
-
|
|
44
|
+
inputLabel && (React.createElement(Typography, { variant: "Caption", className: labelClasses }, inputLabel)),
|
|
35
45
|
icon && React.createElement("div", { className: styles.icon }, icon),
|
|
36
46
|
multiline ? (React.createElement("textarea", { id: id, className: inputClassess, value: value, placeholder: placeholder, onChange: handleChange, disabled: disabled, style: { height: `${rows * 20}px` } })) : (React.createElement("input", { id: id, className: inputClassess, value: value, placeholder: placeholder, onChange: handleChange, disabled: disabled, readOnly: readOnly })),
|
|
37
47
|
error && helperText && (React.createElement(Typography, { variant: "Caption", className: classNames(styles.helperText, styles[size]) }, helperText))));
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import styles from './Loader.module.css';
|
|
3
|
+
import { Typography } from '../Typography/Typography';
|
|
4
|
+
import { IconFile } from '../../Icons/IconFile/IconFile';
|
|
5
|
+
import { ProgressBar } from '../ProgressBar/ProgressBar';
|
|
6
|
+
export const Loader = ({ name, size = 0, loading = false, error = '', onClick }) => {
|
|
7
|
+
return (React.createElement("div", { className: `${styles['loader']} ${error ? styles['error'] : ''}` },
|
|
8
|
+
React.createElement("div", { className: styles['loaderFile'] },
|
|
9
|
+
React.createElement("div", { className: styles['loaderInfo'] },
|
|
10
|
+
React.createElement("div", { className: styles['loaderIcon'] },
|
|
11
|
+
React.createElement(IconFile, { htmlColor: "var(--icons-grey)" })),
|
|
12
|
+
React.createElement("div", { className: styles['loaderName'] },
|
|
13
|
+
React.createElement(Typography, { variant: "Body2-Medium", color: "var(--text-dark)" }, name),
|
|
14
|
+
size !== 0 && React.createElement(Typography, { variant: "Caption", color: "var(--grey-medium)" }, `${(size / (1024)).toFixed(1)} кБ`))),
|
|
15
|
+
React.createElement("button", { type: "button", "aria-label": "\u0417\u0430\u043A\u0440\u044B\u0442\u044C", title: "\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0444\u0430\u0439\u043B", onClick: onClick })),
|
|
16
|
+
loading && React.createElement(ProgressBar, { animated: true, size: "sm", value: 100 }),
|
|
17
|
+
error && (React.createElement(Typography, { variant: "Caption", color: "var(--error-main)" }, error))));
|
|
18
|
+
};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
|
|
2
|
+
.loader {
|
|
3
|
+
display: flex;
|
|
4
|
+
flex-direction: column;
|
|
5
|
+
gap: 5px;
|
|
6
|
+
justify-content: space-between;
|
|
7
|
+
|
|
8
|
+
padding: 10px 15px;
|
|
9
|
+
border: 1px solid var(--info-secondary);
|
|
10
|
+
border-radius: 10px;
|
|
11
|
+
|
|
12
|
+
}
|
|
13
|
+
.error {
|
|
14
|
+
border-color: var(--error-main);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.loaderFile{
|
|
18
|
+
display: flex;
|
|
19
|
+
flex-direction: row;
|
|
20
|
+
align-items: center;
|
|
21
|
+
gap: 5px;
|
|
22
|
+
}
|
|
23
|
+
.loaderInfo {
|
|
24
|
+
display: flex;
|
|
25
|
+
flex-direction: row;
|
|
26
|
+
gap: 5px;
|
|
27
|
+
flex-grow: 1;
|
|
28
|
+
align-items: flex-start;
|
|
29
|
+
overflow: hidden;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.loaderIcon {
|
|
33
|
+
flex-shrink: 0
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.loaderName {
|
|
37
|
+
display: flex;
|
|
38
|
+
flex-direction: column;
|
|
39
|
+
flex-grow: 1;
|
|
40
|
+
align-items: flex-start;
|
|
41
|
+
overflow: auto;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.loaderButton{
|
|
45
|
+
position: absolute;
|
|
46
|
+
left: 50%;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.loader button[aria-label="Закрыть"] {
|
|
50
|
+
background: none;
|
|
51
|
+
border: none;
|
|
52
|
+
padding: 0;
|
|
53
|
+
cursor: pointer;
|
|
54
|
+
position: relative;
|
|
55
|
+
width: 10px;
|
|
56
|
+
height: 10px;
|
|
57
|
+
flex-shrink: 0
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.loader button[aria-label="Закрыть"]::before,
|
|
61
|
+
.loader button[aria-label="Закрыть"]::after {
|
|
62
|
+
content: '';
|
|
63
|
+
position: absolute;
|
|
64
|
+
top: 50%;
|
|
65
|
+
left: 50%;
|
|
66
|
+
transform: translate(-50%, -50%) rotate(45deg);
|
|
67
|
+
height: 1px;
|
|
68
|
+
width: 100%;
|
|
69
|
+
background-color: var(--icons-grey);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.loader button[aria-label="Закрыть"]::after {
|
|
73
|
+
transform: translate(-50%, -50%) rotate(-45deg);
|
|
74
|
+
}
|
|
75
|
+
|
|
@@ -36,7 +36,7 @@ export const ProgressBar = ({ value = 0, max = 100, size = 'md', showValue = tru
|
|
|
36
36
|
}, [animated, validPercentage]);
|
|
37
37
|
return (React.createElement("div", { className: styles["progress-bar--wrapper"] },
|
|
38
38
|
React.createElement("progress", { id: "linear-progress", className: progressBarClasses, value: percent, max: max }),
|
|
39
|
-
React.createElement("label", { htmlFor: "progress", className: styles[
|
|
39
|
+
React.createElement("label", { htmlFor: "progress", className: styles['progress-bar-percentage'] }, showValue && (React.createElement(Typography, { variant: "Caption", color: '#9CA0A7', style: { fontWeight: '300' }, className: styles["progress-bar-percentage"] },
|
|
40
40
|
percent,
|
|
41
41
|
"%")))));
|
|
42
42
|
};
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import React, { useState } from 'react';
|
|
2
2
|
import { Tag } from '../Tag/Tag';
|
|
3
3
|
import ColorPicker from '../ColorPicker/ColorPicker';
|
|
4
|
-
;
|
|
5
4
|
import styles from './SettingTag.module.css';
|
|
6
5
|
export const SettingTag = ({ label, color, onChange }) => {
|
|
7
6
|
const [isHovered, setIsHovered] = useState(false);
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
const [currentColor, setCurrentColor] = useState(color);
|
|
8
|
+
const colorsOptions = ['red', 'orange', 'yellow', 'green', 'teal', 'blue', 'indigo', 'purple', 'pink'];
|
|
9
|
+
return (React.createElement("div", { style: { display: 'flex', gap: `${isHovered ? '5px' : '10px'}`, flexDirection: 'row', alignItems: 'center' } },
|
|
10
|
+
React.createElement(Tag, { label: label, color: currentColor, editable: true, onChange: onChange }),
|
|
11
11
|
!isHovered ? (React.createElement("div", { className: styles.circle, onMouseEnter: () => setIsHovered(true), style: {
|
|
12
12
|
width: 10,
|
|
13
13
|
height: 10,
|
|
14
|
-
backgroundColor: (
|
|
15
|
-
} })) : (React.createElement(ColorPicker, { mainColor:
|
|
14
|
+
backgroundColor: (currentColor === null || currentColor === void 0 ? void 0 : currentColor.startsWith('#')) ? currentColor : `var(--${currentColor})`,
|
|
15
|
+
} })) : (React.createElement(ColorPicker, { mainColor: currentColor, recentColors: isHovered ? colorsOptions : [], setIsHovered: setIsHovered, onChange: onChange, onColorChange: setCurrentColor }))));
|
|
16
16
|
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import styles from './Spinner.module.css';
|
|
3
|
+
;
|
|
4
|
+
/**
|
|
5
|
+
* Компонент Spinner отображает индикатор загрузки.
|
|
6
|
+
*/
|
|
7
|
+
const spinnerSizes = { lg: 54, md: 34, sm: 24, xs: 8 };
|
|
8
|
+
export const Spinner = ({ size = 'md', }) => {
|
|
9
|
+
const spinnerSize = typeof size === 'string' ? spinnerSizes[size] : size;
|
|
10
|
+
const viewBoxSize = 100;
|
|
11
|
+
const strokeWidth = size === 'lg' || size === 'md' ? 12 : 10;
|
|
12
|
+
const adjustedSize = spinnerSize + strokeWidth * 5;
|
|
13
|
+
return (React.createElement("div", { className: styles['spinner-wrapper'], style: {
|
|
14
|
+
width: adjustedSize,
|
|
15
|
+
height: adjustedSize,
|
|
16
|
+
padding: strokeWidth
|
|
17
|
+
} },
|
|
18
|
+
React.createElement("svg", { id: "spinner", viewBox: `0 0 ${viewBoxSize} ${viewBoxSize}`, className: styles["spinner"], fill: "none", color: 'var(--blue-main)' },
|
|
19
|
+
React.createElement("defs", null,
|
|
20
|
+
React.createElement("linearGradient", { id: "spinner-secondHalf" },
|
|
21
|
+
React.createElement("stop", { offset: "0%", stopOpacity: "0", stopColor: "currentColor" }),
|
|
22
|
+
React.createElement("stop", { offset: "100%", stopOpacity: "0.9", stopColor: "currentColor" })),
|
|
23
|
+
React.createElement("linearGradient", { id: "spinner-firstHalf" },
|
|
24
|
+
React.createElement("stop", { offset: "0%", stopOpacity: "1", stopColor: "currentColor" }),
|
|
25
|
+
React.createElement("stop", { offset: "100%", stopOpacity: "0.9", stopColor: "currentColor" }))),
|
|
26
|
+
React.createElement("g", { strokeWidth: strokeWidth, className: styles.spinnerRotate },
|
|
27
|
+
React.createElement("path", { stroke: "url(#spinner-secondHalf)", d: "M 15 50 A 35 35 0 0 1 85 50" }),
|
|
28
|
+
React.createElement("path", { stroke: "url(#spinner-firstHalf)", d: "M 85 50 A 35 35 0 0 1 15 50" }),
|
|
29
|
+
React.createElement("path", { stroke: "currentColor", strokeLinecap: "round", d: "M 15 50 A 35 35 0 0 1 15 48" })))));
|
|
30
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
.spinner-wrapper{
|
|
2
|
+
display: flex;
|
|
3
|
+
justify-content: center;
|
|
4
|
+
align-items: center;
|
|
5
|
+
/* height: 100vh; */
|
|
6
|
+
/* background-color: #f5f5f5; */
|
|
7
|
+
}
|
|
8
|
+
.spinnerRotate {
|
|
9
|
+
transform-origin: center;
|
|
10
|
+
animation: rotate 1300ms linear infinite;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
@keyframes rotate {
|
|
14
|
+
from {
|
|
15
|
+
transform: rotate(0deg);
|
|
16
|
+
}
|
|
17
|
+
to {
|
|
18
|
+
transform: rotate(360deg);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -1,24 +1,84 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
2
2
|
;
|
|
3
3
|
import styles from './Tag.module.css';
|
|
4
4
|
import classNames from 'classnames';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
5
|
+
const hexToRgba = (hex, alpha) => {
|
|
6
|
+
//преобразуем в rgba для заднего фона
|
|
7
|
+
const r = parseInt(hex.slice(1, 3), 16);
|
|
8
|
+
const g = parseInt(hex.slice(3, 5), 16);
|
|
9
|
+
const b = parseInt(hex.slice(5, 7), 16);
|
|
10
|
+
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
|
|
11
|
+
};
|
|
12
|
+
// функция для расчёта относительной яркости
|
|
13
|
+
const getLuminance = (hexColor) => {
|
|
14
|
+
var _a;
|
|
15
|
+
const rgb = (_a = hexColor
|
|
16
|
+
.replace('#', '')
|
|
17
|
+
.match(/.{2}/g)) === null || _a === void 0 ? void 0 : _a.map((c) => parseInt(c, 16) / 255);
|
|
18
|
+
if (!rgb)
|
|
19
|
+
return 0;
|
|
20
|
+
const [r, g, b] = rgb.map((c) => c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4));
|
|
21
|
+
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
|
22
|
+
};
|
|
23
|
+
// функция для проверки контрастности
|
|
24
|
+
const getContrastRatio = (color1, color2) => {
|
|
25
|
+
const lum1 = getLuminance(color1);
|
|
26
|
+
const lum2 = getLuminance(color2);
|
|
27
|
+
return (Math.max(lum1, lum2) + 0.05) / (Math.min(lum1, lum2) + 0.05);
|
|
28
|
+
};
|
|
29
|
+
// проверка и смену цвета текста
|
|
30
|
+
const adjustTextColor = (backgroundColor) => {
|
|
31
|
+
const white = '#ffffff';
|
|
32
|
+
const black = hexToRgba('#000000', 0.4);
|
|
33
|
+
const contrastWithWhite = getContrastRatio(backgroundColor, white);
|
|
34
|
+
return contrastWithWhite < 1.5 ? black : "";
|
|
35
|
+
};
|
|
36
|
+
export const Tag = ({ label, color = 'red', closeButton = false, editable = 'false', onClick, onChange, }) => {
|
|
37
|
+
const [newLabel, setNewLabel] = useState(label);
|
|
38
|
+
const [width, setWidth] = useState(0);
|
|
39
|
+
const measurementDivRef = useRef(null);
|
|
40
|
+
const adjustedColor = adjustTextColor(color) || color;
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
if (measurementDivRef.current) {
|
|
43
|
+
const textWidth = measurementDivRef.current.clientWidth;
|
|
44
|
+
setWidth(textWidth);
|
|
45
|
+
}
|
|
46
|
+
}, [newLabel]);
|
|
47
|
+
const handleBlur = () => {
|
|
48
|
+
if (onChange) {
|
|
49
|
+
onChange(newLabel);
|
|
50
|
+
}
|
|
12
51
|
};
|
|
13
52
|
return (React.createElement("span", { className: classNames(styles.tag, !color.startsWith('#') && styles[color]), style: color.startsWith('#')
|
|
14
53
|
? {
|
|
15
|
-
color:
|
|
16
|
-
border: `1px solid ${
|
|
54
|
+
color: adjustedColor,
|
|
55
|
+
border: `1px solid ${adjustedColor}`,
|
|
17
56
|
backgroundColor: hexToRgba(color, 0.2),
|
|
18
57
|
}
|
|
19
58
|
: {} },
|
|
20
|
-
|
|
59
|
+
editable ? (React.createElement("div", { style: { position: "relative" } },
|
|
60
|
+
React.createElement("input", { type: "text", placeholder: label, value: newLabel, onChange: (e) => {
|
|
61
|
+
setNewLabel(e.target.value);
|
|
62
|
+
}, onBlur: handleBlur, style: {
|
|
63
|
+
color: (color === null || color === void 0 ? void 0 : color.startsWith('#')) ? adjustedColor : `var(--${color})`,
|
|
64
|
+
'--placeholder-color': (color === null || color === void 0 ? void 0 : color.startsWith('#')) ? adjustedColor : `var(--${color})`,
|
|
65
|
+
width: `${width}px`,
|
|
66
|
+
minWidth: '25px',
|
|
67
|
+
} }),
|
|
68
|
+
React.createElement("div", { ref: measurementDivRef, style: {
|
|
69
|
+
position: 'absolute',
|
|
70
|
+
visibility: 'hidden',
|
|
71
|
+
height: 0,
|
|
72
|
+
whiteSpace: 'pre',
|
|
73
|
+
fontSize: 'inherit',
|
|
74
|
+
fontFamily: 'inherit',
|
|
75
|
+
fontWeight: 'inherit',
|
|
76
|
+
letterSpacing: 'inherit',
|
|
77
|
+
} }, newLabel || 'Item'))) : (React.createElement(React.Fragment, null,
|
|
78
|
+
" ",
|
|
79
|
+
label,
|
|
80
|
+
" ")),
|
|
21
81
|
closeButton && (React.createElement("button", { type: "button", "aria-label": "\u0417\u0430\u043A\u0440\u044B\u0442\u044C", style: color.startsWith('#')
|
|
22
|
-
? { '--close-color':
|
|
82
|
+
? { '--close-color': adjustedColor }
|
|
23
83
|
: { '--close-color': `var(--${color})` }, onClick: onClick }))));
|
|
24
84
|
};
|
|
@@ -48,6 +48,27 @@
|
|
|
48
48
|
transform: translate(-50%, -50%) rotate(-45deg);
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
.tag input {
|
|
52
|
+
background: none;
|
|
53
|
+
border: none;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.tag input:hover {
|
|
57
|
+
background: none;
|
|
58
|
+
border: none;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.tag input:focus {
|
|
62
|
+
outline: none;
|
|
63
|
+
border: none;
|
|
64
|
+
background-color: none;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.tag input::placeholder {
|
|
68
|
+
color: var(--placeholder-color);
|
|
69
|
+
opacity: 0.5;
|
|
70
|
+
}
|
|
71
|
+
|
|
51
72
|
.tag.red {
|
|
52
73
|
color: var(--red);
|
|
53
74
|
background-color: var(--redBackground);
|
package/dist/index.d.ts
CHANGED
|
@@ -13,5 +13,8 @@ export { Tabs as Tabs } from './components/Tabs/Tabs';
|
|
|
13
13
|
export { Tag as Tag } from './components/Tag/Tag';
|
|
14
14
|
export { ToggleButton as ToggleButton } from './components/ToggleButton/ToggleButton';
|
|
15
15
|
export { Typography as Typography } from './components/Typography/Typography';
|
|
16
|
-
export
|
|
16
|
+
export { Loader as Loader } from './components/Loader/Loader';
|
|
17
|
+
export { FileAttach as FileAttach } from './components/FileAttach/FileAttach';
|
|
18
|
+
export { Spinner as Spinner } from './components/Spinner/Spinner';
|
|
19
|
+
export type { ButtonProps, InputProps, TagProps, SettingTagProps, ToggleButtonProps, BaseOptions, TOptions, DropdownProps, TypographyProps, ProgressBarProps, ProgressLoaderProps, RadioProps, TabsProps, ColorPickerProps, SnackbarProps, LoaderProps, FileAttachProps, SpinnerProps } from './types';
|
|
17
20
|
import './fonts.css';
|
package/dist/index.js
CHANGED
|
@@ -13,4 +13,7 @@ export { Tabs as Tabs } from './components/Tabs/Tabs';
|
|
|
13
13
|
export { Tag as Tag } from './components/Tag/Tag';
|
|
14
14
|
export { ToggleButton as ToggleButton } from './components/ToggleButton/ToggleButton';
|
|
15
15
|
export { Typography as Typography } from './components/Typography/Typography';
|
|
16
|
+
export { Loader as Loader } from './components/Loader/Loader';
|
|
17
|
+
export { FileAttach as FileAttach } from './components/FileAttach/FileAttach';
|
|
18
|
+
export { Spinner as Spinner } from './components/Spinner/Spinner';
|
|
16
19
|
import './fonts.css';
|