@iota/apps-ui-kit 0.5.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/dist/index.d.ts +9 -2
- package/dist/index.es.js +3200 -3131
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +36 -36
- package/dist/index.umd.js.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +3 -2
- package/src/lib/components/atoms/button-segment/ButtonSegment.tsx +15 -3
- package/src/lib/components/atoms/tooltip/Tooltip.tsx +114 -13
- package/src/lib/components/molecules/segmented-button/SegmentedButton.tsx +5 -1
- package/src/lib/components/organisms/table/Table.tsx +2 -2
- package/src/lib/tailwind/responsive.presets.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@iota/apps-ui-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "github.com:iotaledger/iota.git"
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"react": "^18.3.1",
|
|
31
31
|
"react-dom": "^18.3.1",
|
|
32
32
|
"react-number-format": "^5.2.2",
|
|
33
|
-
"@iota/apps-ui-icons": "0.3.
|
|
33
|
+
"@iota/apps-ui-icons": "0.3.1"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@chromatic-com/storybook": "^1.6.0",
|
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
"@types/lodash.merge": "^4.6.9",
|
|
44
44
|
"@types/node": "^20.14.10",
|
|
45
45
|
"@types/react": "^18.3.3",
|
|
46
|
+
"@types/react-dom": "^18.3.0",
|
|
46
47
|
"@vitejs/plugin-react-swc": "^3.3.2",
|
|
47
48
|
"autoprefixer": "^10.4.19",
|
|
48
49
|
"cross-env": "^7.0.3",
|
|
@@ -38,6 +38,10 @@ interface ButtonSegmentProps {
|
|
|
38
38
|
* The type of the button.
|
|
39
39
|
*/
|
|
40
40
|
type?: ButtonSegmentType;
|
|
41
|
+
/**
|
|
42
|
+
* If the button is nested inside a parent button.
|
|
43
|
+
*/
|
|
44
|
+
isNested?: boolean;
|
|
41
45
|
}
|
|
42
46
|
|
|
43
47
|
export function ButtonSegment({
|
|
@@ -47,6 +51,7 @@ export function ButtonSegment({
|
|
|
47
51
|
disabled,
|
|
48
52
|
onClick,
|
|
49
53
|
type = ButtonSegmentType.Rounded,
|
|
54
|
+
isNested = false,
|
|
50
55
|
}: ButtonSegmentProps): React.JSX.Element {
|
|
51
56
|
const isUnderlined = type === ButtonSegmentType.Underlined;
|
|
52
57
|
|
|
@@ -58,9 +63,16 @@ export function ButtonSegment({
|
|
|
58
63
|
|
|
59
64
|
const underlined = isUnderlined ? (selected ? UNDERLINED_SELECTED : UNDERLINED) : '';
|
|
60
65
|
const textColors = selected ? TEXT_COLORS_SELECTED : TEXT_COLORS;
|
|
61
|
-
const padding = isUnderlined ? 'px-lg py-md' : 'px-sm py-[6px]';
|
|
66
|
+
const padding = isUnderlined ? (isNested ? 'px-md py-sm' : 'px-lg py-md') : 'px-sm py-[6px]';
|
|
62
67
|
const borderRadius = isUnderlined ? '' : 'rounded-full';
|
|
63
|
-
const textSize =
|
|
68
|
+
const textSize = isNested
|
|
69
|
+
? isUnderlined
|
|
70
|
+
? 'text-title-sm'
|
|
71
|
+
: 'text-label-md'
|
|
72
|
+
: isUnderlined
|
|
73
|
+
? 'text-title-md'
|
|
74
|
+
: 'text-label-lg';
|
|
75
|
+
|
|
64
76
|
return (
|
|
65
77
|
<ButtonUnstyled
|
|
66
78
|
onClick={onClick}
|
|
@@ -79,7 +91,7 @@ export function ButtonSegment({
|
|
|
79
91
|
>
|
|
80
92
|
<div className={cx('flex flex-row items-center justify-center gap-2', textSize)}>
|
|
81
93
|
{icon && <span>{icon}</span>}
|
|
82
|
-
<span className="font-inter">{label}</span>
|
|
94
|
+
<span className="shrink-0 font-inter">{label}</span>
|
|
83
95
|
</div>
|
|
84
96
|
</ButtonUnstyled>
|
|
85
97
|
);
|
|
@@ -1,37 +1,138 @@
|
|
|
1
1
|
// Copyright (c) 2024 IOTA Stiftung
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
|
|
4
|
+
import { useRef, useState, useLayoutEffect } from 'react';
|
|
4
5
|
import type { PropsWithChildren } from 'react';
|
|
6
|
+
import { createPortal } from 'react-dom';
|
|
5
7
|
import cx from 'classnames';
|
|
6
8
|
import { TooltipPosition } from './tooltip.enums';
|
|
7
|
-
import { TOOLTIP_POSITION } from './tooltip.classes';
|
|
8
9
|
|
|
9
10
|
interface TooltipProps {
|
|
10
11
|
text: string;
|
|
11
12
|
position?: TooltipPosition;
|
|
12
13
|
maxWidth?: string;
|
|
14
|
+
offset?: number;
|
|
15
|
+
openDelay?: number;
|
|
16
|
+
closeDelay?: number;
|
|
13
17
|
}
|
|
14
18
|
|
|
15
19
|
export function Tooltip({
|
|
16
20
|
text,
|
|
17
21
|
position = TooltipPosition.Top,
|
|
18
22
|
maxWidth = 'max-w-[200px]',
|
|
23
|
+
offset = 8,
|
|
24
|
+
openDelay = 0,
|
|
25
|
+
closeDelay = 100,
|
|
19
26
|
children,
|
|
20
|
-
}: PropsWithChildren<TooltipProps>)
|
|
21
|
-
const
|
|
27
|
+
}: PropsWithChildren<TooltipProps>) {
|
|
28
|
+
const triggerRef = useRef<HTMLDivElement>(null);
|
|
29
|
+
const tooltipRef = useRef<HTMLDivElement>(null);
|
|
30
|
+
|
|
31
|
+
const [visible, setVisible] = useState(false);
|
|
32
|
+
const [coords, setCoords] = useState<{ top: number; left: number }>({ top: 0, left: 0 });
|
|
33
|
+
|
|
34
|
+
const openTimer = useRef<ReturnType<typeof setTimeout>>();
|
|
35
|
+
const closeTimer = useRef<ReturnType<typeof setTimeout>>();
|
|
36
|
+
|
|
37
|
+
const clearTimers = () => {
|
|
38
|
+
clearTimeout(openTimer.current);
|
|
39
|
+
clearTimeout(closeTimer.current);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const open = () => {
|
|
43
|
+
clearTimers();
|
|
44
|
+
openTimer.current = setTimeout(() => setVisible(true), openDelay);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const close = () => {
|
|
48
|
+
clearTimers();
|
|
49
|
+
closeTimer.current = setTimeout(() => setVisible(false), closeDelay);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
useLayoutEffect(() => {
|
|
53
|
+
if (!visible) return;
|
|
54
|
+
|
|
55
|
+
const rect = triggerRef.current?.getBoundingClientRect();
|
|
56
|
+
if (!rect) return;
|
|
57
|
+
|
|
58
|
+
const pos = {
|
|
59
|
+
[TooltipPosition.Top]: {
|
|
60
|
+
top: rect.top - offset,
|
|
61
|
+
left: rect.left + rect.width / 2,
|
|
62
|
+
transform: 'translate(-50%, -100%)',
|
|
63
|
+
},
|
|
64
|
+
[TooltipPosition.Bottom]: {
|
|
65
|
+
top: rect.bottom + offset,
|
|
66
|
+
left: rect.left + rect.width / 2,
|
|
67
|
+
transform: 'translate(-50%, 0)',
|
|
68
|
+
},
|
|
69
|
+
[TooltipPosition.Left]: {
|
|
70
|
+
top: rect.top + rect.height / 2,
|
|
71
|
+
left: rect.left - offset,
|
|
72
|
+
transform: 'translate(-100%, -50%)',
|
|
73
|
+
},
|
|
74
|
+
[TooltipPosition.Right]: {
|
|
75
|
+
top: rect.top + rect.height / 2,
|
|
76
|
+
left: rect.right + offset,
|
|
77
|
+
transform: 'translate(0, -50%)',
|
|
78
|
+
},
|
|
79
|
+
}[position];
|
|
80
|
+
|
|
81
|
+
setCoords({ top: pos.top, left: pos.left });
|
|
82
|
+
tooltipRef.current!.style.transform = pos.transform;
|
|
83
|
+
}, [visible, position, offset]);
|
|
84
|
+
|
|
85
|
+
useLayoutEffect(() => {
|
|
86
|
+
if (!visible) return;
|
|
87
|
+
const update = () => {
|
|
88
|
+
const rect = triggerRef.current?.getBoundingClientRect();
|
|
89
|
+
if (!rect) return;
|
|
90
|
+
setCoords((prev) => ({ ...prev, left: rect.left + rect.width / 2 }));
|
|
91
|
+
};
|
|
92
|
+
window.addEventListener('scroll', update, true);
|
|
93
|
+
window.addEventListener('resize', update);
|
|
94
|
+
return () => {
|
|
95
|
+
window.removeEventListener('scroll', update, true);
|
|
96
|
+
window.removeEventListener('resize', update);
|
|
97
|
+
};
|
|
98
|
+
}, [visible]);
|
|
99
|
+
|
|
100
|
+
const base =
|
|
101
|
+
'z-[999] w-max rounded bg-neutral-80 p-xs text-neutral-10 dark:bg-neutral-30 dark:text-neutral-92';
|
|
102
|
+
|
|
22
103
|
return (
|
|
23
|
-
|
|
24
|
-
{children}
|
|
104
|
+
<>
|
|
25
105
|
<div
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
106
|
+
ref={triggerRef}
|
|
107
|
+
className="inline-block cursor-pointer"
|
|
108
|
+
onMouseEnter={open}
|
|
109
|
+
onFocus={open}
|
|
110
|
+
onMouseLeave={close}
|
|
111
|
+
onBlur={close}
|
|
32
112
|
>
|
|
33
|
-
|
|
113
|
+
{children}
|
|
34
114
|
</div>
|
|
35
|
-
|
|
115
|
+
|
|
116
|
+
{visible &&
|
|
117
|
+
createPortal(
|
|
118
|
+
<div
|
|
119
|
+
ref={tooltipRef}
|
|
120
|
+
role="tooltip"
|
|
121
|
+
style={{
|
|
122
|
+
position: 'fixed',
|
|
123
|
+
top: coords.top,
|
|
124
|
+
left: coords.left,
|
|
125
|
+
transition: 'opacity .15s ease',
|
|
126
|
+
opacity: 1,
|
|
127
|
+
}}
|
|
128
|
+
className={cx(base, maxWidth)}
|
|
129
|
+
onMouseEnter={open}
|
|
130
|
+
onMouseLeave={close}
|
|
131
|
+
>
|
|
132
|
+
<p className="w-full break-words">{text}</p>
|
|
133
|
+
</div>,
|
|
134
|
+
document.body,
|
|
135
|
+
)}
|
|
136
|
+
</>
|
|
36
137
|
);
|
|
37
138
|
}
|
|
@@ -26,8 +26,12 @@ export function SegmentedButton({
|
|
|
26
26
|
const backgroundColors = BACKGROUND_COLORS[type];
|
|
27
27
|
const borderColors = type === SegmentedButtonType.Outlined ? OUTLINED_BORDER : '';
|
|
28
28
|
const borderShape = shape === ButtonSegmentType.Rounded ? 'rounded-full gap-1 p-xxs' : '';
|
|
29
|
+
const scrollable =
|
|
30
|
+
shape === ButtonSegmentType.Rounded ? 'flex-wrap' : 'flex-nowrap overflow-x-auto';
|
|
29
31
|
return (
|
|
30
|
-
<div
|
|
32
|
+
<div
|
|
33
|
+
className={cx('flex flex-row', backgroundColors, borderColors, borderShape, scrollable)}
|
|
34
|
+
>
|
|
31
35
|
{children}
|
|
32
36
|
</div>
|
|
33
37
|
);
|
|
@@ -103,9 +103,9 @@ export function Table({
|
|
|
103
103
|
</table>
|
|
104
104
|
</div>
|
|
105
105
|
<div
|
|
106
|
-
className={cx('flex w-full items-center gap-
|
|
106
|
+
className={cx('flex w-full items-center gap-sm pt-md', {
|
|
107
107
|
hidden: !supportingLabel && !paginationOptions && !action,
|
|
108
|
-
'justify-between': paginationOptions,
|
|
108
|
+
'flex-col justify-between sm:flex-row': paginationOptions,
|
|
109
109
|
'justify-end': !paginationOptions && action,
|
|
110
110
|
})}
|
|
111
111
|
>
|