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.
@@ -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(error);
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,3 @@
1
+ import { FC } from 'react';
2
+ import { FileAttachProps } from '../../types';
3
+ export declare const FileAttach: FC<FileAttachProps>;
@@ -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
- label && (React.createElement(Typography, { variant: "Caption", className: labelClasses }, label)),
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,3 @@
1
+ import { FC } from 'react';
2
+ import { LoaderProps } from '../../types';
3
+ export declare const Loader: FC<LoaderProps>;
@@ -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["progress-bar-percentage"] }, showValue && (React.createElement(Typography, { variant: "Subheading3", color: '#9CA0A7', style: { fontWeight: '300' }, className: styles["progress-bar-percentage"] },
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 colorsOptions = ['red', 'orange', 'yellow', 'green', 'purple', 'indigo', 'blue', 'teal', 'pink'];
9
- return (React.createElement("div", { style: { display: 'flex', gap: '10px', flexDirection: 'row', alignItems: 'center' } },
10
- React.createElement(Tag, { label: label, color: color }),
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: (color === null || color === void 0 ? void 0 : color.startsWith('#')) ? color : `var(--${color})`,
15
- } })) : (React.createElement(ColorPicker, { mainColor: color, onChange: onChange, recentColors: isHovered ? colorsOptions : [], setIsHovered: setIsHovered }))));
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,3 @@
1
+ import { FC } from 'react';
2
+ import { SpinnerProps } from '../../types';
3
+ export declare const Spinner: FC<SpinnerProps>;
@@ -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
- export const Tag = ({ label, color = 'red', closeButton = false, onClick }) => {
6
- const hexToRgba = (hex, alpha) => {
7
- //преобразуем в rgba для заднего фона
8
- const r = parseInt(hex.slice(1, 3), 16);
9
- const g = parseInt(hex.slice(3, 5), 16);
10
- const b = parseInt(hex.slice(5, 7), 16);
11
- return `rgba(${r}, ${g}, ${b}, ${alpha})`;
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: color,
16
- border: `1px solid ${color}`,
54
+ color: adjustedColor,
55
+ border: `1px solid ${adjustedColor}`,
17
56
  backgroundColor: hexToRgba(color, 0.2),
18
57
  }
19
58
  : {} },
20
- label,
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': 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 type { ButtonProps, InputProps, TagProps, SettingTagProps, ToggleButtonProps, BaseOptions, TOptions, DropdownProps, TypographyProps, ProgressBarProps, ProgressLoaderProps, RadioProps, TabsProps, ColorPickerProps, SnackbarProps } from './types';
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';