kamotive_ui 1.2.14 → 1.2.15
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/IconAdd/IconAdd.d.ts +6 -0
- package/dist/Icons/IconAdd/IconAdd.js +5 -0
- package/dist/Icons/index.d.ts +1 -0
- package/dist/Icons/index.js +1 -0
- package/dist/components/Checkbox/Checkbox.stories.d.ts +66 -0
- package/dist/components/Checkbox/Checkbox.stories.js +75 -0
- package/dist/components/FileItem/FileItem.js +3 -3
- package/dist/components/IconButton/IconButton.js +4 -2
- package/dist/components/List/List.js +6 -4
- package/dist/components/Tag/Tag.d.ts +1 -0
- package/dist/components/Tag/Tag.js +1 -1
- package/dist/components/Tooltip/Tooltip.d.ts +3 -0
- package/dist/components/Tooltip/Tooltip.js +198 -0
- package/dist/components/Tooltip/Tooltip.module.css +22 -0
- package/dist/components/Typography/Typography.module.css +9 -9
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -0
- package/dist/types/index.d.ts +32 -4
- package/package.json +1 -1
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export const IconAdd = ({ color = 'inherit', htmlColor, strokeWidth, }) => {
|
|
3
|
+
return (React.createElement("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", className: color },
|
|
4
|
+
React.createElement("path", { fill: htmlColor || 'currentColor', stroke: htmlColor || 'currentColor', strokeWidth: strokeWidth || '0', d: "M5,13V12H11V6H12V12H18V13H12V19H11V13H5Z" })));
|
|
5
|
+
};
|
package/dist/Icons/index.d.ts
CHANGED
|
@@ -19,3 +19,4 @@ export { IconColorPicker10 } from './IconColorPicker/IconColorPicker10';
|
|
|
19
19
|
export { IconFile } from './IconFile/IconFile';
|
|
20
20
|
export { IconUpload } from './IconUpload/IconUpload';
|
|
21
21
|
export { IconDownload } from './IconDownload/IconDownload';
|
|
22
|
+
export { IconAdd } from './IconAdd/IconAdd';
|
package/dist/Icons/index.js
CHANGED
|
@@ -19,3 +19,4 @@ export { IconColorPicker10 } from './IconColorPicker/IconColorPicker10';
|
|
|
19
19
|
export { IconFile } from './IconFile/IconFile';
|
|
20
20
|
export { IconUpload } from './IconUpload/IconUpload';
|
|
21
21
|
export { IconDownload } from './IconDownload/IconDownload';
|
|
22
|
+
export { IconAdd } from './IconAdd/IconAdd';
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { ChangeEventHandler } from 'react';
|
|
2
|
+
import type { Meta } from '@storybook/react';
|
|
3
|
+
export interface CheckboxProps {
|
|
4
|
+
/** Идентификатор */
|
|
5
|
+
checked?: boolean;
|
|
6
|
+
/** Обработчик изменения состояния */
|
|
7
|
+
onChange?: ChangeEventHandler<HTMLInputElement>;
|
|
8
|
+
/** Заблокированный чекбокс */
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
/** Размер чекбокса */
|
|
11
|
+
size?: 'sm' | 'md';
|
|
12
|
+
/** Текст лейбла */
|
|
13
|
+
label?: string;
|
|
14
|
+
}
|
|
15
|
+
declare const meta: Meta<CheckboxProps>;
|
|
16
|
+
export default meta;
|
|
17
|
+
export declare const CheckboxOff: {
|
|
18
|
+
(argTypes: CheckboxProps): JSX.Element;
|
|
19
|
+
storyName: string;
|
|
20
|
+
};
|
|
21
|
+
export declare const CheckboxLabel: {
|
|
22
|
+
(argTypes: CheckboxProps): JSX.Element;
|
|
23
|
+
storyName: string;
|
|
24
|
+
args: {
|
|
25
|
+
label: string;
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
export declare const CheckboxChecked: {
|
|
29
|
+
(argTypes: CheckboxProps): JSX.Element;
|
|
30
|
+
storyName: string;
|
|
31
|
+
args: {
|
|
32
|
+
checked: boolean;
|
|
33
|
+
disabled: boolean;
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
export declare const CheckboxDisabled: {
|
|
37
|
+
(argTypes: CheckboxProps): JSX.Element;
|
|
38
|
+
storyName: string;
|
|
39
|
+
args: {
|
|
40
|
+
checked: boolean;
|
|
41
|
+
disabled: boolean;
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
export declare const CheckboxCheckedDisabled: {
|
|
45
|
+
(argTypes: CheckboxProps): JSX.Element;
|
|
46
|
+
storyName: string;
|
|
47
|
+
args: {
|
|
48
|
+
checked: boolean;
|
|
49
|
+
disabled: boolean;
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
export declare const CheckboxCustomColor: {
|
|
53
|
+
(argTypes: CheckboxProps): JSX.Element;
|
|
54
|
+
storyName: string;
|
|
55
|
+
args: {
|
|
56
|
+
color: string;
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
export declare const CheckboxCustomColorFilled: {
|
|
60
|
+
(argTypes: CheckboxProps): JSX.Element;
|
|
61
|
+
storyName: string;
|
|
62
|
+
args: {
|
|
63
|
+
color: string;
|
|
64
|
+
filled: boolean;
|
|
65
|
+
};
|
|
66
|
+
};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { Checkbox } from './Checkbox';
|
|
3
|
+
const meta = {
|
|
4
|
+
component: Checkbox,
|
|
5
|
+
parameters: {
|
|
6
|
+
layout: 'centered',
|
|
7
|
+
},
|
|
8
|
+
tags: ['autodocs'],
|
|
9
|
+
decorators: [
|
|
10
|
+
(Story) => (React.createElement("div", { style: {
|
|
11
|
+
backgroundColor: 'var(--white)',
|
|
12
|
+
padding: '30px',
|
|
13
|
+
borderRadius: '10px',
|
|
14
|
+
width: '900px'
|
|
15
|
+
} },
|
|
16
|
+
React.createElement(Story, null))),
|
|
17
|
+
],
|
|
18
|
+
args: {
|
|
19
|
+
size: 'sm',
|
|
20
|
+
disabled: false,
|
|
21
|
+
},
|
|
22
|
+
argTypes: {
|
|
23
|
+
size: {
|
|
24
|
+
description: 'Свойство, позволяющее регулировать размер чекбокса',
|
|
25
|
+
control: { type: 'radio' },
|
|
26
|
+
options: ['sm', 'md'],
|
|
27
|
+
},
|
|
28
|
+
disabled: { description: 'Устанавливает атрибут disabled', control: { type: 'boolean' } },
|
|
29
|
+
checked: { description: 'Задаёт включённое состояние для компонента' },
|
|
30
|
+
label: { description: 'Текст лейбла чекбокса', type: 'string' },
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
export default meta;
|
|
34
|
+
export const CheckboxOff = (argTypes) => {
|
|
35
|
+
const [checked, setChecked] = useState(false);
|
|
36
|
+
const handleChange = () => {
|
|
37
|
+
setChecked(prev => !prev);
|
|
38
|
+
};
|
|
39
|
+
return (React.createElement(Checkbox, Object.assign({ checked: checked, onChange: handleChange }, argTypes)));
|
|
40
|
+
};
|
|
41
|
+
CheckboxOff.storyName = 'Checkbox по умолчанию';
|
|
42
|
+
export const CheckboxLabel = (argTypes) => React.createElement(Checkbox, Object.assign({}, argTypes));
|
|
43
|
+
CheckboxLabel.storyName = 'Checkbox c label';
|
|
44
|
+
CheckboxLabel.args = {
|
|
45
|
+
label: 'Чекбокс',
|
|
46
|
+
};
|
|
47
|
+
export const CheckboxChecked = (argTypes) => React.createElement(Checkbox, Object.assign({}, argTypes));
|
|
48
|
+
CheckboxChecked.storyName = 'Checkbox выбран';
|
|
49
|
+
CheckboxChecked.args = {
|
|
50
|
+
checked: true,
|
|
51
|
+
disabled: false,
|
|
52
|
+
};
|
|
53
|
+
export const CheckboxDisabled = (argTypes) => React.createElement(Checkbox, Object.assign({}, argTypes));
|
|
54
|
+
CheckboxDisabled.storyName = 'Checkbox заблокирован';
|
|
55
|
+
CheckboxDisabled.args = {
|
|
56
|
+
checked: false,
|
|
57
|
+
disabled: true,
|
|
58
|
+
};
|
|
59
|
+
export const CheckboxCheckedDisabled = (argTypes) => React.createElement(Checkbox, Object.assign({}, argTypes));
|
|
60
|
+
CheckboxCheckedDisabled.storyName = 'Checkbox выбран и заблокирован';
|
|
61
|
+
CheckboxCheckedDisabled.args = {
|
|
62
|
+
checked: true,
|
|
63
|
+
disabled: true,
|
|
64
|
+
};
|
|
65
|
+
export const CheckboxCustomColor = (argTypes) => React.createElement(Checkbox, Object.assign({}, argTypes));
|
|
66
|
+
CheckboxCustomColor.storyName = 'Checkbox с кастомным цветом';
|
|
67
|
+
CheckboxCustomColor.args = {
|
|
68
|
+
color: 'red',
|
|
69
|
+
};
|
|
70
|
+
export const CheckboxCustomColorFilled = (argTypes) => React.createElement(Checkbox, Object.assign({}, argTypes));
|
|
71
|
+
CheckboxCustomColorFilled.storyName = 'Checkbox с кастомным цветом заполненный';
|
|
72
|
+
CheckboxCustomColorFilled.args = {
|
|
73
|
+
color: 'red',
|
|
74
|
+
filled: true,
|
|
75
|
+
};
|
|
@@ -4,14 +4,14 @@ import { Typography } from '../Typography/Typography';
|
|
|
4
4
|
import { ProgressBar } from '../ProgressBar/ProgressBar';
|
|
5
5
|
import { IconButton } from '../IconButton/IconButton';
|
|
6
6
|
import { IconClose10, IconDownload, IconFile } from '../../Icons';
|
|
7
|
-
export const FileItem = ({ name, size = 0, loading = false, error = '', onDownload, onDelete }) => {
|
|
8
|
-
return (React.createElement("div", { className: `${styles['fileItem']} ${error ? styles['error'] : ''}
|
|
7
|
+
export const FileItem = ({ name, size = 0, loading = false, error = '', onDownload, onDelete, style }) => {
|
|
8
|
+
return (React.createElement("div", { className: `${styles['fileItem']} ${error ? styles['error'] : ''}`, style: style },
|
|
9
9
|
React.createElement("div", { className: styles['fileItemFile'] },
|
|
10
10
|
React.createElement("div", { className: styles['fileItemInfo'] },
|
|
11
11
|
React.createElement("div", { className: styles['fileItemIcon'] },
|
|
12
12
|
React.createElement(IconFile, { htmlColor: 'var(--icons-grey)' })),
|
|
13
13
|
React.createElement("div", { className: styles['fileItemName'] },
|
|
14
|
-
React.createElement(Typography, { variant: "Body1
|
|
14
|
+
React.createElement(Typography, { variant: "Body1", color: "var(--text-dark)" }, name),
|
|
15
15
|
size !== 0 && (React.createElement(Typography, { variant: "Caption", color: "var(--grey-medium)" }, `${(size / 1024).toFixed(1)} кБ`)))),
|
|
16
16
|
onDownload && (React.createElement(IconButton, { icon: React.createElement(IconDownload, null), onClick: onDownload, color: 'var(--icons-grey)', className: styles['fileIcon'] })),
|
|
17
17
|
onDelete && (React.createElement(IconButton, { icon: React.createElement(IconClose10, null), onClick: onDelete, color: 'var(--icons-grey)' }))),
|
|
@@ -2,8 +2,10 @@ import React from 'react';
|
|
|
2
2
|
import classNames from 'classnames';
|
|
3
3
|
import styles from './IconButton.module.css';
|
|
4
4
|
export const IconButton = ({ icon, size = 'md', color, style, disabled = false, onClick, children, className }) => {
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
const validChildren = React.Children.toArray(children).filter(child => React.isValidElement(child));
|
|
6
|
+
const renderIcon = icon || validChildren[0];
|
|
7
|
+
return (React.createElement("button", { className: classNames(styles['iconButton'], styles[`iconButton--${size}`], className), disabled: disabled, "aria-disabled": disabled, type: "button", onClick: onClick, style: style }, (renderIcon) &&
|
|
8
|
+
React.cloneElement(renderIcon, {
|
|
7
9
|
htmlColor: color,
|
|
8
10
|
strokeWidth: size === 'lg' ? '0.5' : size === 'md' ? '0.3' : '0.0',
|
|
9
11
|
})));
|
|
@@ -5,8 +5,8 @@ import { Typography } from '../Typography/Typography';
|
|
|
5
5
|
import { Checkbox } from '../Checkbox/Checkbox';
|
|
6
6
|
import { RadioButton } from '../RadioButton/RadioButton';
|
|
7
7
|
import { ChevronDown10 } from '../../Icons';
|
|
8
|
-
export const List = ({ onClick, onCheck, onRadioSelect, checked = false, selected = false, disabled = false, label, id, collapsible = false, withCheckbox = false, checkboxColor, checkboxFilled, withRadioButton = false, customBullet, customItemBullet, bulletClassName, children, isHeader = false, parentChecked = false, }) => {
|
|
9
|
-
const [isOpen, setIsOpen] = useState(
|
|
8
|
+
export const List = ({ onClick, onCheck, onRadioSelect, checked = false, selected = false, disabled = false, label, id, style, className, collapsible = false, open = false, withCheckbox = false, checkboxColor, checkboxFilled, withRadioButton = false, customBullet, customItemBullet, bulletClassName, children, isHeader = false, parentChecked = false, }) => {
|
|
9
|
+
const [isOpen, setIsOpen] = useState(open);
|
|
10
10
|
const [isChecked, setIsChecked] = useState(checked || parentChecked);
|
|
11
11
|
const childIds = [];
|
|
12
12
|
React.Children.forEach(children, (child) => {
|
|
@@ -54,10 +54,10 @@ export const List = ({ onClick, onCheck, onRadioSelect, checked = false, selecte
|
|
|
54
54
|
useEffect(() => {
|
|
55
55
|
setIsChecked(parentChecked || checked);
|
|
56
56
|
}, [parentChecked, checked]);
|
|
57
|
-
const headerClassNames = classNames(styles.header);
|
|
57
|
+
const headerClassNames = classNames(styles.header, className);
|
|
58
58
|
const contentClassNames = classNames(styles.content, isOpen ? styles['content--expanded'] : styles['content--collapsed']);
|
|
59
59
|
return (React.createElement("div", { className: styles.collapsibleList },
|
|
60
|
-
label && (React.createElement("div", { className: headerClassNames, onClick: handleClick },
|
|
60
|
+
label && (React.createElement("div", { className: headerClassNames, onClick: handleClick, style: style },
|
|
61
61
|
!isHeader && (React.createElement("div", null,
|
|
62
62
|
withCheckbox && (React.createElement("span", { onClick: handleCheckboxClick },
|
|
63
63
|
React.createElement(Checkbox, { checked: isChecked, color: checkboxColor, filled: checkboxFilled, disabled: disabled }))),
|
|
@@ -82,6 +82,8 @@ export const List = ({ onClick, onCheck, onRadioSelect, checked = false, selecte
|
|
|
82
82
|
onRadioSelect: handleChildRadioSelect,
|
|
83
83
|
parentChecked: isChecked,
|
|
84
84
|
selected: child.props.selected,
|
|
85
|
+
style: child.props.style || style,
|
|
86
|
+
className: child.props.className,
|
|
85
87
|
});
|
|
86
88
|
}
|
|
87
89
|
return child;
|
|
@@ -2,7 +2,7 @@ import React, { useEffect, useRef, useState } from 'react';
|
|
|
2
2
|
;
|
|
3
3
|
import styles from './Tag.module.css';
|
|
4
4
|
import classNames from 'classnames';
|
|
5
|
-
const hexToRgba = (hex, alpha) => {
|
|
5
|
+
export const hexToRgba = (hex, alpha) => {
|
|
6
6
|
//преобразуем в rgba для заднего фона
|
|
7
7
|
const r = parseInt(hex.slice(1, 3), 16);
|
|
8
8
|
const g = parseInt(hex.slice(3, 5), 16);
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
2
|
+
import styles from './Tooltip.module.css';
|
|
3
|
+
import { Typography } from '../Typography/Typography';
|
|
4
|
+
import classNames from 'classnames';
|
|
5
|
+
import ReactDOM from 'react-dom';
|
|
6
|
+
import { hexToRgba } from '../Tag/Tag';
|
|
7
|
+
export const Tooltip = ({ label, children, className, style, overlayChildren = false, textSize = 'sm', position = 'none', displayDelay = 750, hideDelay = 500, opacity = 0.4, color, followCursor = false, }) => {
|
|
8
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
9
|
+
const [isVisible, setIsVisible] = useState(false);
|
|
10
|
+
const [coords, setCoords] = useState({ x: 0, y: 0 });
|
|
11
|
+
const [childrenRect, setChildrenRect] = useState({
|
|
12
|
+
x: 0,
|
|
13
|
+
y: 0,
|
|
14
|
+
width: 0,
|
|
15
|
+
height: 0,
|
|
16
|
+
contentX: 0,
|
|
17
|
+
contentY: 0,
|
|
18
|
+
contentWidth: 0,
|
|
19
|
+
contentHeight: 0,
|
|
20
|
+
});
|
|
21
|
+
const timeoutRef = useRef(null);
|
|
22
|
+
const mousePositionRef = useRef({ x: 0, y: 0 });
|
|
23
|
+
const childrenRef = useRef(null);
|
|
24
|
+
const tooltipElementRef = useRef(null);
|
|
25
|
+
const updateContainerRect = () => {
|
|
26
|
+
if (childrenRef.current) {
|
|
27
|
+
const childElement = childrenRef.current.firstElementChild;
|
|
28
|
+
const rect = childElement.getBoundingClientRect();
|
|
29
|
+
const computedStyle = window.getComputedStyle(childElement);
|
|
30
|
+
const paddingTop = parseFloat(computedStyle.paddingTop);
|
|
31
|
+
const paddingBottom = parseFloat(computedStyle.paddingBottom);
|
|
32
|
+
const paddingLeft = parseFloat(computedStyle.paddingLeft);
|
|
33
|
+
const paddingRight = parseFloat(computedStyle.paddingRight);
|
|
34
|
+
const contentX = rect.left + paddingLeft;
|
|
35
|
+
const contentY = rect.top + paddingTop;
|
|
36
|
+
const contentHeight = rect.height - paddingTop - paddingBottom;
|
|
37
|
+
const contentWidth = rect.width - paddingLeft - paddingRight;
|
|
38
|
+
setChildrenRect({
|
|
39
|
+
x: rect.left,
|
|
40
|
+
y: rect.top,
|
|
41
|
+
width: rect.width,
|
|
42
|
+
height: rect.height,
|
|
43
|
+
contentX: contentX,
|
|
44
|
+
contentY: contentY,
|
|
45
|
+
contentWidth: contentWidth,
|
|
46
|
+
contentHeight: contentHeight,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
updateContainerRect();
|
|
52
|
+
window.addEventListener('resize', updateContainerRect);
|
|
53
|
+
window.addEventListener('scroll', updateContainerRect);
|
|
54
|
+
return () => {
|
|
55
|
+
window.removeEventListener('resize', updateContainerRect);
|
|
56
|
+
window.removeEventListener('scroll', updateContainerRect);
|
|
57
|
+
};
|
|
58
|
+
}, []);
|
|
59
|
+
const adjustToViewPort = (posX, posY) => {
|
|
60
|
+
if (tooltipElementRef.current) {
|
|
61
|
+
const OFFSET = 15;
|
|
62
|
+
const tooltipWidth = tooltipElementRef.current.offsetWidth;
|
|
63
|
+
const tooltipHeight = tooltipElementRef.current.offsetHeight;
|
|
64
|
+
const viewportWidth = window.innerWidth;
|
|
65
|
+
const viewportHeight = window.innerHeight;
|
|
66
|
+
if (posX + tooltipWidth > viewportWidth) {
|
|
67
|
+
posX = viewportWidth - tooltipWidth - OFFSET;
|
|
68
|
+
}
|
|
69
|
+
if (posY + tooltipHeight > viewportHeight) {
|
|
70
|
+
posY = mousePositionRef.current.y - tooltipHeight - OFFSET;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return [posX, posY];
|
|
74
|
+
};
|
|
75
|
+
const updateCoords = (mouseX, mouseY) => {
|
|
76
|
+
const OFFSET_X = 8;
|
|
77
|
+
const OFFSET_Y = 15;
|
|
78
|
+
const CONTENT_OFFSET_Y = 5;
|
|
79
|
+
const cursorX = mouseX !== undefined ? mouseX : mousePositionRef.current.x;
|
|
80
|
+
const cursorY = mouseY !== undefined ? mouseY : mousePositionRef.current.y;
|
|
81
|
+
// Если followCursor активен, всегда используем позицию курсора
|
|
82
|
+
if (followCursor) {
|
|
83
|
+
let posX = cursorX + OFFSET_X;
|
|
84
|
+
let posY = cursorY + OFFSET_Y;
|
|
85
|
+
// Проверяем границы экрана
|
|
86
|
+
[posX, posY] = adjustToViewPort(posX, posY);
|
|
87
|
+
setCoords({
|
|
88
|
+
x: posX,
|
|
89
|
+
y: posY
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
// Оригинальная логика позиционирования
|
|
94
|
+
let posX = cursorX + OFFSET_X;
|
|
95
|
+
let posY = overlayChildren || cursorY > childrenRect.contentY + childrenRect.contentHeight
|
|
96
|
+
? cursorY + OFFSET_Y
|
|
97
|
+
: childrenRect.contentY + childrenRect.contentHeight + CONTENT_OFFSET_Y;
|
|
98
|
+
if (position === 'bottom-center' || position === 'bottom-right' || position === 'bottom-left') {
|
|
99
|
+
posY = childrenRect.contentY + childrenRect.contentHeight + CONTENT_OFFSET_Y;
|
|
100
|
+
}
|
|
101
|
+
else if (position !== 'none') {
|
|
102
|
+
posY = childrenRect.contentY - childrenRect.contentHeight - CONTENT_OFFSET_Y;
|
|
103
|
+
}
|
|
104
|
+
setCoords({
|
|
105
|
+
x: posX,
|
|
106
|
+
y: posY
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
if (!isOpen) {
|
|
110
|
+
setIsOpen(true);
|
|
111
|
+
setTimeout(() => {
|
|
112
|
+
setIsVisible(true);
|
|
113
|
+
}, 10);
|
|
114
|
+
}
|
|
115
|
+
if (!followCursor) {
|
|
116
|
+
/** положение подсказки
|
|
117
|
+
* x - всегда зависит от положения курсора в момент наведения
|
|
118
|
+
* y - если пропс overlayChildren true, подсказка будет поверх дочерних компонентов в месте наведения,
|
|
119
|
+
* в ином случае подсказка всплывает под дочерним элементом
|
|
120
|
+
*/
|
|
121
|
+
let posX = mousePositionRef.current.x + OFFSET_X;
|
|
122
|
+
let posY = overlayChildren || mousePositionRef.current.y > childrenRect.contentY + childrenRect.contentHeight
|
|
123
|
+
? mousePositionRef.current.y + OFFSET_Y
|
|
124
|
+
: childrenRect.contentY + childrenRect.contentHeight + CONTENT_OFFSET_Y;
|
|
125
|
+
if (position === 'bottom-center' || position === 'bottom-right' || position === 'bottom-left') {
|
|
126
|
+
posY = childrenRect.contentY + childrenRect.contentHeight + CONTENT_OFFSET_Y;
|
|
127
|
+
}
|
|
128
|
+
else if (position !== 'none') {
|
|
129
|
+
posY = childrenRect.contentY - childrenRect.contentHeight - CONTENT_OFFSET_Y;
|
|
130
|
+
}
|
|
131
|
+
setCoords({
|
|
132
|
+
x: posX,
|
|
133
|
+
y: posY
|
|
134
|
+
});
|
|
135
|
+
setIsOpen(true);
|
|
136
|
+
setTimeout(() => {
|
|
137
|
+
setIsVisible(true);
|
|
138
|
+
}, 10);
|
|
139
|
+
setTimeout(() => {
|
|
140
|
+
if (tooltipElementRef.current) {
|
|
141
|
+
const tooltipWidth = tooltipElementRef.current.offsetWidth;
|
|
142
|
+
if (position === 'bottom-center' || position === 'top-center') {
|
|
143
|
+
posX = childrenRect.x + (childrenRect.width - tooltipWidth) / 2;
|
|
144
|
+
}
|
|
145
|
+
else if (position === 'bottom-right' || position === 'top-right') {
|
|
146
|
+
posX = childrenRect.contentX + childrenRect.contentWidth - tooltipWidth;
|
|
147
|
+
}
|
|
148
|
+
else if (position !== 'none') {
|
|
149
|
+
posX = childrenRect.contentX;
|
|
150
|
+
}
|
|
151
|
+
adjustToViewPort(posX, posY);
|
|
152
|
+
setCoords({
|
|
153
|
+
x: posX,
|
|
154
|
+
y: posY
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
}, 0);
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
const handleMouseEnter = (e) => {
|
|
161
|
+
updateContainerRect();
|
|
162
|
+
mousePositionRef.current = {
|
|
163
|
+
x: e.clientX,
|
|
164
|
+
y: e.clientY,
|
|
165
|
+
};
|
|
166
|
+
if (timeoutRef.current) {
|
|
167
|
+
clearTimeout(timeoutRef.current);
|
|
168
|
+
}
|
|
169
|
+
timeoutRef.current = window.setTimeout(() => {
|
|
170
|
+
updateCoords();
|
|
171
|
+
}, displayDelay);
|
|
172
|
+
};
|
|
173
|
+
const handleMouseLeave = () => {
|
|
174
|
+
if (timeoutRef.current) {
|
|
175
|
+
clearTimeout(timeoutRef.current);
|
|
176
|
+
timeoutRef.current = null;
|
|
177
|
+
}
|
|
178
|
+
setIsVisible(false);
|
|
179
|
+
setTimeout(() => {
|
|
180
|
+
setIsOpen(false);
|
|
181
|
+
}, hideDelay);
|
|
182
|
+
};
|
|
183
|
+
const handleMouseMove = (e) => {
|
|
184
|
+
mousePositionRef.current = {
|
|
185
|
+
x: e.clientX,
|
|
186
|
+
y: e.clientY,
|
|
187
|
+
};
|
|
188
|
+
if (isOpen && followCursor) {
|
|
189
|
+
updateCoords(e.clientX, e.clientY);
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
const tooltipStyles = Object.assign(Object.assign({}, style), { position: 'fixed', left: `${coords.x}px`, top: `${coords.y}px`, backgroundColor: color ? hexToRgba(color, opacity) : `rgba(0, 0, 0, ${opacity})`, zIndex: 1000 });
|
|
193
|
+
const tooltipClassNames = classNames(styles.tooltip, isVisible && styles['tooltip--visible'], className);
|
|
194
|
+
return (React.createElement(React.Fragment, null,
|
|
195
|
+
React.createElement("div", { onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, onMouseMove: handleMouseMove, className: styles.wrapper, ref: childrenRef }, children),
|
|
196
|
+
isOpen && ReactDOM.createPortal(React.createElement("div", { ref: tooltipElementRef, className: tooltipClassNames, style: tooltipStyles },
|
|
197
|
+
React.createElement(Typography, { variant: textSize === 'sm' ? "Caption-Medium" : textSize === 'md' ? "Body2-Medium" : "Body1-Medium" }, label)), document.body)));
|
|
198
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
.tooltip {
|
|
2
|
+
backdrop-filter: blur(5px);
|
|
3
|
+
border-radius: 8px;
|
|
4
|
+
padding: 5px 8px;
|
|
5
|
+
width: fit-content;
|
|
6
|
+
font-family: var(--font-family-content);
|
|
7
|
+
color: white;
|
|
8
|
+
display: flex;
|
|
9
|
+
align-items: center;
|
|
10
|
+
pointer-events: none;
|
|
11
|
+
box-shadow: 0 4px 14.9px -3px rgba(0, 0, 0, 0.3);
|
|
12
|
+
opacity: 0;
|
|
13
|
+
transition: opacity 0.1s ease-out;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.tooltip--visible {
|
|
17
|
+
opacity: 1;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.wrapper {
|
|
21
|
+
width: fit-content;
|
|
22
|
+
}
|
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
.typography--variant-Body-Medium {
|
|
82
82
|
font-family: var(--font-family-content);
|
|
83
83
|
font-size: var(--font-size-16);
|
|
84
|
-
font-weight: var(--font-weight-
|
|
84
|
+
font-weight: var(--font-weight-medium);
|
|
85
85
|
line-height: var(--lineHeight-16);
|
|
86
86
|
letter-spacing: var(--letterSpacing);
|
|
87
87
|
text-decoration: none;
|
|
@@ -161,7 +161,7 @@
|
|
|
161
161
|
.typography--variant-Body2-Medium {
|
|
162
162
|
font-family: var(--font-family-content);
|
|
163
163
|
font-size: var(--font-size-12);
|
|
164
|
-
font-weight: var(--font-weight-
|
|
164
|
+
font-weight: var(--font-weight-medium);
|
|
165
165
|
line-height: var(--lineHeight-12);
|
|
166
166
|
letter-spacing: var(--letterSpacing);
|
|
167
167
|
text-decoration: none;
|
|
@@ -184,25 +184,25 @@
|
|
|
184
184
|
}
|
|
185
185
|
.typography--variant-Caption {
|
|
186
186
|
font-family: var(--font-family-content);
|
|
187
|
-
font-size: var(--font-size-
|
|
187
|
+
font-size: var(--font-size-10);
|
|
188
188
|
font-weight: var(--font-weight-regular);
|
|
189
|
-
line-height: var(--lineHeight-
|
|
189
|
+
line-height: var(--lineHeight-10);
|
|
190
190
|
letter-spacing: var(--letterSpacing);
|
|
191
191
|
text-decoration: none;
|
|
192
192
|
}
|
|
193
193
|
.typography--variant-Caption-Medium {
|
|
194
194
|
font-family: var(--font-family-content);
|
|
195
|
-
font-size: var(--font-size-
|
|
196
|
-
font-weight: var(--font-weight-
|
|
197
|
-
line-height: var(--lineHeight-
|
|
195
|
+
font-size: var(--font-size-10);
|
|
196
|
+
font-weight: var(--font-weight-medium);
|
|
197
|
+
line-height: var(--lineHeight-10);
|
|
198
198
|
letter-spacing: var(--letterSpacing);
|
|
199
199
|
text-decoration: none;
|
|
200
200
|
}
|
|
201
201
|
.typography--variant-Caption-Bold {
|
|
202
202
|
font-family: var(--font-family-content);
|
|
203
|
-
font-size: var(--font-size-
|
|
203
|
+
font-size: var(--font-size-10);
|
|
204
204
|
font-weight: var(--font-weight-bold);
|
|
205
|
-
line-height: var(--lineHeight-
|
|
205
|
+
line-height: var(--lineHeight-10);
|
|
206
206
|
letter-spacing: var(--letterSpacing);
|
|
207
207
|
text-decoration: none;
|
|
208
208
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -23,5 +23,6 @@ export { List as List } from './components/List/List';
|
|
|
23
23
|
export { ListItem as ListItem } from './components/ListItem/ListItem';
|
|
24
24
|
export { Breadcrumb as Breadcrumb } from './components/Breadcrumb/Breadcrumb';
|
|
25
25
|
export { Breadcrumbs as Breadcrumbs } from './components/Breadcrumbs/Breadcrumbs';
|
|
26
|
-
export
|
|
26
|
+
export { Tooltip as Tooltip } from './components/Tooltip/Tooltip';
|
|
27
|
+
export type { ButtonProps, InputProps, DateInputProps, TagProps, SettingTagProps, ToggleButtonProps, BaseOptions, TOptions, DropdownProps, TypographyProps, ProgressBarProps, ProgressLoaderProps, RadioProps, TabsProps, ColorPickerProps, SnackbarProps, FileItemProps, FileAttachProps, SpinnerProps, DialogProps, IconButtonProps, BaseListProps, ListProps, ListItemProps, BreadcrumbProps, BreadcrumbsProps, TooltipProps, } from './types';
|
|
27
28
|
import './fonts.css';
|
package/dist/index.js
CHANGED
|
@@ -23,4 +23,5 @@ export { List as List } from './components/List/List';
|
|
|
23
23
|
export { ListItem as ListItem } from './components/ListItem/ListItem';
|
|
24
24
|
export { Breadcrumb as Breadcrumb } from './components/Breadcrumb/Breadcrumb';
|
|
25
25
|
export { Breadcrumbs as Breadcrumbs } from './components/Breadcrumbs/Breadcrumbs';
|
|
26
|
+
export { Tooltip as Tooltip } from './components/Tooltip/Tooltip';
|
|
26
27
|
import './fonts.css';
|
package/dist/types/index.d.ts
CHANGED
|
@@ -425,6 +425,10 @@ export interface IconButtonProps {
|
|
|
425
425
|
export interface BaseListProps {
|
|
426
426
|
/** Идентификатор */
|
|
427
427
|
id?: string;
|
|
428
|
+
/** Стиль элемента списка */
|
|
429
|
+
style?: CSSProperties;
|
|
430
|
+
/** Дополнительный класс */
|
|
431
|
+
className?: string;
|
|
428
432
|
/** Обработчик клика */
|
|
429
433
|
onClick?: () => void;
|
|
430
434
|
/** Обработчик выбора чекбокса */
|
|
@@ -455,10 +459,6 @@ export interface BaseListProps {
|
|
|
455
459
|
bulletClassName?: string;
|
|
456
460
|
}
|
|
457
461
|
export interface ListItemProps extends BaseListProps {
|
|
458
|
-
/** Стиль элемента списка */
|
|
459
|
-
style?: CSSProperties;
|
|
460
|
-
/** Дополнительный класс */
|
|
461
|
-
className?: string;
|
|
462
462
|
/** Дочерние элементы */
|
|
463
463
|
children?: ReactNode;
|
|
464
464
|
}
|
|
@@ -471,4 +471,32 @@ export interface ListProps extends BaseListProps {
|
|
|
471
471
|
customItemBullet?: React.ReactNode;
|
|
472
472
|
/** Внешний компонент без буллитов/чекбоксов */
|
|
473
473
|
isHeader?: boolean;
|
|
474
|
+
/** Раскрытый список */
|
|
475
|
+
open?: boolean;
|
|
476
|
+
}
|
|
477
|
+
export interface TooltipProps {
|
|
478
|
+
/** Текст подсказки */
|
|
479
|
+
label: string;
|
|
480
|
+
/** Дочерние элементы */
|
|
481
|
+
children: React.ReactNode;
|
|
482
|
+
/** Дополнительный класс */
|
|
483
|
+
className?: string;
|
|
484
|
+
/** Стиль подсказки */
|
|
485
|
+
style?: CSSProperties;
|
|
486
|
+
/** Положение подсказки */
|
|
487
|
+
overlayChildren?: boolean;
|
|
488
|
+
/** Размер текста */
|
|
489
|
+
textSize?: 'sm' | 'md' | 'lg';
|
|
490
|
+
/** Позиция подсказки */
|
|
491
|
+
position?: 'none' | 'bottom-center' | 'bottom-right' | 'bottom-left' | 'top-center' | 'top-right' | 'top-left';
|
|
492
|
+
/** Время, через которое будет показана подсказка */
|
|
493
|
+
displayDelay?: number;
|
|
494
|
+
/** Время, через которое будет скрыта подсказка */
|
|
495
|
+
hideDelay?: number;
|
|
496
|
+
/** Прозрачность подсказки (значение от 0 до 1) */
|
|
497
|
+
opacity?: number;
|
|
498
|
+
/** Цвет подсказки */
|
|
499
|
+
color?: string;
|
|
500
|
+
/** Подсказка, следующая за курсором */
|
|
501
|
+
followCursor?: boolean;
|
|
474
502
|
}
|