@iota/apps-ui-kit 0.2.1 → 0.3.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 +25 -0
- package/dist/index.d.ts +23 -9
- package/dist/index.es.js +4093 -3989
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +43 -43
- package/dist/index.umd.js.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +3 -3
- package/src/lib/components/atoms/button/button.classes.ts +4 -4
- package/src/lib/components/atoms/info-box/infoBox.classes.ts +7 -2
- package/src/lib/components/atoms/info-box/infoBox.enums.ts +2 -0
- package/src/lib/components/atoms/snackbar/snackbar.classes.ts +5 -1
- package/src/lib/components/atoms/snackbar/snackbar.enums.tsx +2 -0
- package/src/lib/components/atoms/tooltip/Tooltip.tsx +4 -1
- package/src/lib/components/atoms/visual-asset-card/VisualAssetCard.tsx +26 -18
- package/src/lib/components/molecules/address/Address.tsx +1 -1
- package/src/lib/components/molecules/card/CardAction.tsx +4 -2
- package/src/lib/components/molecules/input/input.classes.ts +2 -2
- package/src/lib/components/molecules/search/Search.tsx +34 -3
- package/src/lib/components/molecules/table-header-cell/TableHeaderCell.tsx +27 -13
- package/src/lib/components/organisms/accordion/Accordion.tsx +3 -5
- package/src/lib/components/organisms/dialog/Dialog.tsx +6 -5
- package/src/lib/components/organisms/navbar/Navbar.tsx +10 -10
- package/src/lib/constants/colors.constants.ts +5 -0
- package/src/lib/styles/index.css +13 -0
- package/src/lib/tailwind/base.presets.ts +2 -0
- package/src/lib/tailwind/plugins.ts +8 -0
- package/src/storybook/stories/organisms/Navbar.stories.tsx +2 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@iota/apps-ui-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.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.2.
|
|
33
|
+
"@iota/apps-ui-icons": "0.2.2"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@chromatic-com/storybook": "^1.6.0",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"storybook-dark-mode": "^4.0.2",
|
|
55
55
|
"tailwindcss": "^3.3.3",
|
|
56
56
|
"typescript": "^5.5.3",
|
|
57
|
-
"vite": "^5.
|
|
57
|
+
"vite": "^5.4.12",
|
|
58
58
|
"vite-plugin-dts": "^4.3.0",
|
|
59
59
|
"vite-tsconfig-paths": "^4.2.0"
|
|
60
60
|
},
|
|
@@ -18,7 +18,7 @@ export const BACKGROUND_COLORS: Record<ButtonType, string> = {
|
|
|
18
18
|
[ButtonType.Secondary]: 'bg-neutral-90 dark:bg-neutral-20',
|
|
19
19
|
[ButtonType.Ghost]: 'bg-transparent',
|
|
20
20
|
[ButtonType.Outlined]: 'bg-transparent border border-neutral-50',
|
|
21
|
-
[ButtonType.Destructive]: 'bg-error-90',
|
|
21
|
+
[ButtonType.Destructive]: 'bg-error-90 dark:bg-error-20',
|
|
22
22
|
};
|
|
23
23
|
|
|
24
24
|
export const DISABLED_BACKGROUND_COLORS: Record<ButtonType, string> = {
|
|
@@ -26,7 +26,7 @@ export const DISABLED_BACKGROUND_COLORS: Record<ButtonType, string> = {
|
|
|
26
26
|
[ButtonType.Secondary]: 'bg-neutral-90 dark:bg-neutral-20',
|
|
27
27
|
[ButtonType.Ghost]: 'bg-transparent',
|
|
28
28
|
[ButtonType.Outlined]: 'bg-transparent border border-neutral-50',
|
|
29
|
-
[ButtonType.Destructive]: 'bg-error-90',
|
|
29
|
+
[ButtonType.Destructive]: 'bg-error-90 dark:bg-error-20',
|
|
30
30
|
};
|
|
31
31
|
|
|
32
32
|
const DEFAULT_TEXT_COLORS: string = 'text-neutral-10 dark:text-neutral-92';
|
|
@@ -36,7 +36,7 @@ export const TEXT_COLORS: Record<ButtonType, string> = {
|
|
|
36
36
|
[ButtonType.Secondary]: DEFAULT_TEXT_COLORS,
|
|
37
37
|
[ButtonType.Ghost]: DEFAULT_TEXT_COLORS,
|
|
38
38
|
[ButtonType.Outlined]: DEFAULT_TEXT_COLORS,
|
|
39
|
-
[ButtonType.Destructive]: 'text-error-20',
|
|
39
|
+
[ButtonType.Destructive]: 'text-error-20 dark:text-error-90',
|
|
40
40
|
};
|
|
41
41
|
|
|
42
42
|
export const TEXT_CLASSES: Record<ButtonSize, string> = {
|
|
@@ -49,5 +49,5 @@ export const TEXT_COLOR_DISABLED: Record<ButtonType, string> = {
|
|
|
49
49
|
[ButtonType.Secondary]: DEFAULT_TEXT_COLORS,
|
|
50
50
|
[ButtonType.Ghost]: DEFAULT_TEXT_COLORS,
|
|
51
51
|
[ButtonType.Outlined]: DEFAULT_TEXT_COLORS,
|
|
52
|
-
[ButtonType.Destructive]: 'text-error-20',
|
|
52
|
+
[ButtonType.Destructive]: 'text-error-20 dark:text-error-90',
|
|
53
53
|
};
|
|
@@ -4,11 +4,16 @@
|
|
|
4
4
|
import { InfoBoxType } from './infoBox.enums';
|
|
5
5
|
|
|
6
6
|
export const ICON_COLORS: Record<InfoBoxType, string> = {
|
|
7
|
-
[InfoBoxType.Default]: 'bg-
|
|
7
|
+
[InfoBoxType.Default]: 'bg-primary-90 dark:bg-primary-10 text-primary-20 dark:text-primary-90',
|
|
8
8
|
[InfoBoxType.Error]: 'bg-error-90 dark:bg-error-10 text-error-20 dark:text-error-90',
|
|
9
|
+
[InfoBoxType.Success]:
|
|
10
|
+
'bg-tertiary-90 dark:bg-tertiary-10 text-tertiary-20 dark:text-tertiary-90',
|
|
11
|
+
[InfoBoxType.Warning]: 'bg-warning-90 dark:bg-warning-20 text-warning-10 dark:text-warning-90',
|
|
9
12
|
};
|
|
10
13
|
|
|
11
14
|
export const BACKGROUND_COLORS: Record<InfoBoxType, string> = {
|
|
12
|
-
[InfoBoxType.Default]: 'bg-
|
|
15
|
+
[InfoBoxType.Default]: 'bg-primary-90 dark:bg-primary-10',
|
|
13
16
|
[InfoBoxType.Error]: 'bg-error-90 dark:bg-error-10',
|
|
17
|
+
[InfoBoxType.Success]: 'bg-tertiary-90 dark:bg-tertiary-10',
|
|
18
|
+
[InfoBoxType.Warning]: 'bg-warning-90 dark:bg-warning-20',
|
|
14
19
|
};
|
|
@@ -6,9 +6,13 @@ import { SnackbarType } from './snackbar.enums';
|
|
|
6
6
|
export const TEXT_COLOR: Record<SnackbarType, string> = {
|
|
7
7
|
[SnackbarType.Default]: 'text-neutral-10 dark:text-neutral-92',
|
|
8
8
|
[SnackbarType.Error]: 'text-error-20 dark:text-error-90',
|
|
9
|
+
[SnackbarType.Warning]: 'text-warning-10 dark:text-warning-90',
|
|
10
|
+
[SnackbarType.Success]: 'text-tertiary-20 dark:text-tertiary-90',
|
|
9
11
|
};
|
|
10
12
|
|
|
11
13
|
export const BACKGROUND_COLOR: Record<SnackbarType, string> = {
|
|
12
|
-
[SnackbarType.Default]: 'bg-
|
|
14
|
+
[SnackbarType.Default]: 'bg-primary-90 dark:bg-primary-10',
|
|
13
15
|
[SnackbarType.Error]: 'bg-error-90 dark:bg-error-10',
|
|
16
|
+
[SnackbarType.Warning]: 'bg-warning-90 dark:bg-warning-20',
|
|
17
|
+
[SnackbarType.Success]: 'bg-tertiary-90 dark:bg-tertiary-10',
|
|
14
18
|
};
|
|
@@ -9,11 +9,13 @@ import { TOOLTIP_POSITION } from './tooltip.classes';
|
|
|
9
9
|
interface TooltipProps {
|
|
10
10
|
text: string;
|
|
11
11
|
position?: TooltipPosition;
|
|
12
|
+
maxWidth?: string;
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
export function Tooltip({
|
|
15
16
|
text,
|
|
16
17
|
position = TooltipPosition.Top,
|
|
18
|
+
maxWidth = 'max-w-[200px]',
|
|
17
19
|
children,
|
|
18
20
|
}: PropsWithChildren<TooltipProps>): React.JSX.Element {
|
|
19
21
|
const tooltipPositionClass = TOOLTIP_POSITION[position];
|
|
@@ -22,8 +24,9 @@ export function Tooltip({
|
|
|
22
24
|
{children}
|
|
23
25
|
<div
|
|
24
26
|
className={cx(
|
|
25
|
-
'absolute z-[999] hidden w-max
|
|
27
|
+
'absolute z-[999] hidden w-max rounded bg-neutral-80 p-xs text-neutral-10 opacity-0 transition-opacity duration-300 group-hover:block group-hover:opacity-100 group-focus:opacity-100 dark:bg-neutral-30 dark:text-neutral-92',
|
|
26
28
|
tooltipPositionClass,
|
|
29
|
+
maxWidth,
|
|
27
30
|
)}
|
|
28
31
|
role="tooltip"
|
|
29
32
|
>
|
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
|
|
4
4
|
import { VisualAssetType } from './visualAssetCard.enums';
|
|
5
5
|
import { ButtonUnstyled } from '../button';
|
|
6
|
-
import { MoreHoriz } from '@iota/apps-ui-icons';
|
|
6
|
+
import { MoreHoriz, PlaceholderReplace } from '@iota/apps-ui-icons';
|
|
7
7
|
import cx from 'classnames';
|
|
8
|
+
import { useState } from 'react';
|
|
8
9
|
|
|
9
10
|
export interface VisualAssetCardProps {
|
|
10
11
|
/**
|
|
@@ -51,6 +52,8 @@ export function VisualAssetCard({
|
|
|
51
52
|
assetTitle,
|
|
52
53
|
isHoverable = true,
|
|
53
54
|
}: VisualAssetCardProps): React.JSX.Element {
|
|
55
|
+
const [imageError, setImageError] = useState(false);
|
|
56
|
+
|
|
54
57
|
const handleIconClick = (event: React.MouseEvent<HTMLButtonElement>) => {
|
|
55
58
|
onIconClick?.(event);
|
|
56
59
|
event?.stopPropagation();
|
|
@@ -65,28 +68,33 @@ export function VisualAssetCard({
|
|
|
65
68
|
>
|
|
66
69
|
{assetType === VisualAssetType.Video ? (
|
|
67
70
|
<video src={assetSrc} className="h-full w-full object-cover" autoPlay loop muted />
|
|
71
|
+
) : imageError ? (
|
|
72
|
+
<div className="flex h-full w-full items-center justify-center bg-neutral-96 dark:bg-neutral-10">
|
|
73
|
+
<PlaceholderReplace className="h-4 w-4 text-neutral-40 dark:text-neutral-60" />
|
|
74
|
+
</div>
|
|
68
75
|
) : (
|
|
69
|
-
<img
|
|
76
|
+
<img
|
|
77
|
+
src={assetSrc}
|
|
78
|
+
alt={altText}
|
|
79
|
+
className="h-full w-full object-cover"
|
|
80
|
+
onError={() => setImageError(true)}
|
|
81
|
+
/>
|
|
70
82
|
)}
|
|
71
83
|
{isHoverable && (
|
|
72
84
|
<div className="absolute left-0 top-0 h-full w-full bg-cover bg-center bg-no-repeat group-hover:bg-shader-neutral-light-48 group-hover:transition group-hover:duration-300 group-hover:ease-in-out group-hover:dark:bg-shader-primary-dark-48" />
|
|
73
85
|
)}
|
|
74
|
-
{isHoverable && (
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
<
|
|
85
|
-
|
|
86
|
-
<span className="text-title-md text-neutral-100">{assetTitle}</span>
|
|
87
|
-
)}
|
|
88
|
-
</div>
|
|
89
|
-
</>
|
|
86
|
+
{isHoverable && onIconClick && (
|
|
87
|
+
<ButtonUnstyled
|
|
88
|
+
className="absolute right-2 top-2 h-9 w-9 cursor-pointer rounded-full p-xs opacity-0 transition-opacity duration-300 group-hover:bg-shader-neutral-light-72 group-hover:opacity-100 [&_svg]:h-5 [&_svg]:w-5 [&_svg]:text-primary-100"
|
|
89
|
+
onClick={handleIconClick}
|
|
90
|
+
>
|
|
91
|
+
{icon}
|
|
92
|
+
</ButtonUnstyled>
|
|
93
|
+
)}
|
|
94
|
+
{isHoverable && assetTitle && (
|
|
95
|
+
<div className="absolute bottom-0 flex items-center justify-center p-xs opacity-0 transition-opacity duration-300 group-hover:opacity-100">
|
|
96
|
+
<span className="text-title-md text-neutral-100">{assetTitle}</span>
|
|
97
|
+
</div>
|
|
90
98
|
)}
|
|
91
99
|
</div>
|
|
92
100
|
);
|
|
@@ -70,7 +70,7 @@ export function Address({
|
|
|
70
70
|
function handleOpenClick(event: React.MouseEvent<HTMLButtonElement>) {
|
|
71
71
|
event?.stopPropagation();
|
|
72
72
|
if (externalLink) {
|
|
73
|
-
const newWindow = window.open(externalLink, '_blank', 'noopener
|
|
73
|
+
const newWindow = window.open(externalLink, '_blank', 'noopener noreferrer');
|
|
74
74
|
if (newWindow) newWindow.opener = null;
|
|
75
75
|
} else {
|
|
76
76
|
onOpen?.(event);
|
|
@@ -27,8 +27,10 @@ export function CardAction({
|
|
|
27
27
|
buttonDisabled,
|
|
28
28
|
}: CardActionProps) {
|
|
29
29
|
function handleActionClick(event: React.MouseEvent) {
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
if (onClick) {
|
|
31
|
+
event.stopPropagation();
|
|
32
|
+
onClick();
|
|
33
|
+
}
|
|
32
34
|
}
|
|
33
35
|
|
|
34
36
|
if (type === CardActionType.Link) {
|
|
@@ -5,9 +5,9 @@ export const INPUT_CLASSES =
|
|
|
5
5
|
'w-full bg-transparent text-body-lg caret-primary-30 dark:caret-primary-80 focus:outline-none focus-visible:outline-none disabled:cursor-not-allowed';
|
|
6
6
|
export const INPUT_TEXT_CLASSES = 'text-neutral-10 dark:text-neutral-92';
|
|
7
7
|
export const INPUT_PLACEHOLDER_CLASSES =
|
|
8
|
-
'
|
|
8
|
+
'enabled:placeholder:text-neutral-40/40 dark:placeholder:text-neutral-60/40 enabled:dark:placeholder:text-neutral-60/40';
|
|
9
9
|
export const BORDER_CLASSES =
|
|
10
|
-
'px-md py-sm rounded-lg border border-neutral-80 group-[.enabled]:cursor-text group-[.errored]:border-error-30 hover:group-[.enabled]:border-neutral-50
|
|
10
|
+
'px-md py-sm rounded-lg border border-neutral-80 group-[.enabled]:cursor-text group-[.errored]:border-error-30 hover:group-[.enabled]:border-neutral-50 dark:border-neutral-20 dark:hover:border-neutral-60 dark:group-[.errored]:border-error-80 [&:has(input:focus)]:!border-primary-30 [&:has(input:focus)]:dark:!border-primary-80';
|
|
11
11
|
export const INPUT_NUMBER_CLASSES =
|
|
12
12
|
'[appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none disabled:cursor-not-allowed';
|
|
13
13
|
export const LABEL_CLASSES =
|
|
@@ -71,6 +71,7 @@ export function Search({
|
|
|
71
71
|
const inputRef = useRef<HTMLInputElement>(null);
|
|
72
72
|
const suggestionsListRef = useRef<HTMLDivElement>(null);
|
|
73
73
|
const [isSuggestionsVisible, setIsSuggestionsVisible] = useState(true);
|
|
74
|
+
const [selectedIndex, setSelectedIndex] = useState<number | null>(null);
|
|
74
75
|
|
|
75
76
|
function handleChange(event: React.ChangeEvent<HTMLInputElement>) {
|
|
76
77
|
const value = event.target.value;
|
|
@@ -131,9 +132,33 @@ export function Search({
|
|
|
131
132
|
onSuggestionClick?.(suggestion);
|
|
132
133
|
onSearchValueChange('');
|
|
133
134
|
setIsSuggestionsVisible(false);
|
|
135
|
+
setSelectedIndex(null);
|
|
134
136
|
inputRef.current?.blur();
|
|
135
137
|
};
|
|
136
138
|
|
|
139
|
+
const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
|
|
140
|
+
if (suggestions && suggestions?.length > 0) {
|
|
141
|
+
if (event.key === 'ArrowDown') {
|
|
142
|
+
event.preventDefault();
|
|
143
|
+
setSelectedIndex((prev) =>
|
|
144
|
+
prev === null || prev >= suggestions.length - 1 ? 0 : prev + 1,
|
|
145
|
+
);
|
|
146
|
+
} else if (event.key === 'ArrowUp') {
|
|
147
|
+
event.preventDefault();
|
|
148
|
+
setSelectedIndex((prev) =>
|
|
149
|
+
prev === null || prev <= 0 ? suggestions.length - 1 : prev - 1,
|
|
150
|
+
);
|
|
151
|
+
} else if (event.key === 'Enter') {
|
|
152
|
+
event.preventDefault();
|
|
153
|
+
if (selectedIndex !== null && suggestions[selectedIndex]) {
|
|
154
|
+
handleOnSuggestionClick(suggestions[selectedIndex]);
|
|
155
|
+
} else if (suggestions.length === 1) {
|
|
156
|
+
handleOnSuggestionClick(suggestions[0]);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
|
|
137
162
|
return (
|
|
138
163
|
<div className="relative w-full">
|
|
139
164
|
<div
|
|
@@ -148,7 +173,7 @@ export function Search({
|
|
|
148
173
|
type="text"
|
|
149
174
|
value={searchValue}
|
|
150
175
|
onChange={handleChange}
|
|
151
|
-
onKeyDown={
|
|
176
|
+
onKeyDown={handleKeyDown}
|
|
152
177
|
onFocus={() => setIsSuggestionsVisible(true)}
|
|
153
178
|
placeholder={placeholder}
|
|
154
179
|
className={cx(
|
|
@@ -168,15 +193,21 @@ export function Search({
|
|
|
168
193
|
>
|
|
169
194
|
<Divider width="w-11/12" />
|
|
170
195
|
{isLoading ? (
|
|
171
|
-
<div className="
|
|
196
|
+
<div className="px-md py-sm">
|
|
172
197
|
<Loader className="animate-spin" />
|
|
173
198
|
</div>
|
|
174
199
|
) : (
|
|
175
200
|
suggestions.map((suggestion, index) => (
|
|
176
201
|
<div
|
|
177
|
-
className="w-full"
|
|
178
202
|
key={suggestion.id}
|
|
179
203
|
onClick={() => handleOnSuggestionClick(suggestion)}
|
|
204
|
+
onMouseEnter={() => setSelectedIndex(index)}
|
|
205
|
+
className={cx(
|
|
206
|
+
'w-full cursor-pointer px-md py-sm',
|
|
207
|
+
selectedIndex === index
|
|
208
|
+
? 'bg-shader-primary-dark-16 dark:bg-shader-inverted-dark-16'
|
|
209
|
+
: '',
|
|
210
|
+
)}
|
|
180
211
|
>
|
|
181
212
|
{renderSuggestion(suggestion, index)}
|
|
182
213
|
</div>
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// Copyright (c) 2024 IOTA Stiftung
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
|
|
4
|
-
import { useState } from 'react';
|
|
5
4
|
import { SortByDown, SortByUp } from '@iota/apps-ui-icons';
|
|
6
5
|
import cx from 'classnames';
|
|
7
6
|
import { Checkbox } from '@/lib';
|
|
@@ -52,6 +51,10 @@ export interface TableHeaderCellProps {
|
|
|
52
51
|
* Whether the cell content should be centered.
|
|
53
52
|
*/
|
|
54
53
|
isContentCentered?: boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Sort order when cell is initialized
|
|
56
|
+
*/
|
|
57
|
+
sortOrder?: TableHeaderCellSortOrder;
|
|
55
58
|
}
|
|
56
59
|
|
|
57
60
|
export function TableHeaderCell({
|
|
@@ -64,17 +67,13 @@ export function TableHeaderCell({
|
|
|
64
67
|
isContentCentered,
|
|
65
68
|
onSortClick,
|
|
66
69
|
onCheckboxChange,
|
|
70
|
+
sortOrder,
|
|
67
71
|
}: TableHeaderCellProps): JSX.Element {
|
|
68
|
-
const [sortOrder, setSortOrder] = useState<TableHeaderCellSortOrder | null>(
|
|
69
|
-
TableHeaderCellSortOrder.Asc,
|
|
70
|
-
);
|
|
71
|
-
|
|
72
72
|
const handleSort = () => {
|
|
73
73
|
const newSortOrder =
|
|
74
74
|
sortOrder === TableHeaderCellSortOrder.Asc
|
|
75
75
|
? TableHeaderCellSortOrder.Desc
|
|
76
76
|
: TableHeaderCellSortOrder.Asc;
|
|
77
|
-
setSortOrder(newSortOrder);
|
|
78
77
|
if (onSortClick) {
|
|
79
78
|
onSortClick(columnKey, newSortOrder);
|
|
80
79
|
}
|
|
@@ -83,10 +82,30 @@ export function TableHeaderCell({
|
|
|
83
82
|
const textColorClass = 'text-neutral-10 dark:text-neutral-92';
|
|
84
83
|
const textSizeClass = 'text-label-lg';
|
|
85
84
|
|
|
85
|
+
const sortElement = (() => {
|
|
86
|
+
if (!hasSort) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (sortOrder === TableHeaderCellSortOrder.Asc) {
|
|
91
|
+
return <SortByUp className="shrink-0" />;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (sortOrder === TableHeaderCellSortOrder.Desc) {
|
|
95
|
+
return <SortByDown className="shrink-0" />;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return <SortByUp className="invisible shrink-0 group-hover:visible" />;
|
|
99
|
+
})();
|
|
100
|
+
|
|
86
101
|
return (
|
|
87
102
|
<th
|
|
103
|
+
onClick={hasSort ? handleSort : undefined}
|
|
88
104
|
className={cx(
|
|
89
|
-
'state-layer relative h-14 border-b border-shader-neutral-light-8 px-md after:pointer-events-none dark:border-shader-neutral-dark-8',
|
|
105
|
+
'state-layer group relative h-14 border-b border-shader-neutral-light-8 px-md after:pointer-events-none dark:border-shader-neutral-dark-8',
|
|
106
|
+
{
|
|
107
|
+
'cursor-pointer': hasSort,
|
|
108
|
+
},
|
|
90
109
|
)}
|
|
91
110
|
>
|
|
92
111
|
<div
|
|
@@ -114,12 +133,7 @@ export function TableHeaderCell({
|
|
|
114
133
|
{label}
|
|
115
134
|
</span>
|
|
116
135
|
)}
|
|
117
|
-
{
|
|
118
|
-
<SortByUp className="cursor-pointer" onClick={handleSort} />
|
|
119
|
-
)}
|
|
120
|
-
{hasSort && sortOrder === TableHeaderCellSortOrder.Desc && (
|
|
121
|
-
<SortByDown className="cursor-pointer" onClick={handleSort} />
|
|
122
|
-
)}
|
|
136
|
+
{sortElement}
|
|
123
137
|
</div>
|
|
124
138
|
</th>
|
|
125
139
|
);
|
|
@@ -44,16 +44,14 @@ export function AccordionHeader({
|
|
|
44
44
|
children,
|
|
45
45
|
isExpanded,
|
|
46
46
|
hideArrow,
|
|
47
|
-
hideBorder,
|
|
48
47
|
}: PropsWithChildren<AccordionHeaderProps>) {
|
|
49
48
|
return (
|
|
50
49
|
<div
|
|
51
50
|
onClick={onToggle}
|
|
52
51
|
className={cx(
|
|
53
|
-
'state-layer relative flex cursor-pointer items-center justify-between gap-md py-sm--rs',
|
|
52
|
+
'state-layer relative flex cursor-pointer items-center justify-between gap-md overflow-hidden rounded-xl py-sm--rs',
|
|
54
53
|
{
|
|
55
54
|
'pr-md--rs': !hideArrow,
|
|
56
|
-
'rounded-xl': hideBorder,
|
|
57
55
|
},
|
|
58
56
|
)}
|
|
59
57
|
>
|
|
@@ -100,8 +98,8 @@ export function Accordion({
|
|
|
100
98
|
}): React.JSX.Element {
|
|
101
99
|
return (
|
|
102
100
|
<div
|
|
103
|
-
className={cx('
|
|
104
|
-
'
|
|
101
|
+
className={cx('rounded-xl bg-neutral-100 dark:bg-neutral-6', {
|
|
102
|
+
'border border-shader-neutral-light-8 dark:border-shader-neutral-dark-8':
|
|
105
103
|
!hideBorder,
|
|
106
104
|
})}
|
|
107
105
|
>
|
|
@@ -68,17 +68,18 @@ const DialogContent = React.forwardRef<
|
|
|
68
68
|
}, [containerId]);
|
|
69
69
|
const positionClass =
|
|
70
70
|
position === DialogPosition.Right
|
|
71
|
-
? 'right-0 h-screen top-0 w-full'
|
|
72
|
-
: '
|
|
71
|
+
? 'overflow-hidden right-0 h-screen top-0 w-full'
|
|
72
|
+
: 'overflow-y-auto overflow-x-hidden left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 rounded-xl';
|
|
73
73
|
const widthClass =
|
|
74
74
|
position === DialogPosition.Right ? 'md:w-96 max-w-[500px]' : customWidth;
|
|
75
|
+
const heightClass = position === DialogPosition.Right ? 'h-screen' : 'max-h-[80vh] h-full';
|
|
75
76
|
return (
|
|
76
77
|
<RadixDialog.Portal container={containerElement}>
|
|
77
78
|
<DialogOverlay showCloseIcon={showCloseOnOverlay} position={position} />
|
|
78
79
|
<RadixDialog.Content
|
|
79
80
|
ref={ref}
|
|
80
81
|
className={cx(
|
|
81
|
-
'fixed z-[99999] flex flex-col justify-center
|
|
82
|
+
'fixed z-[99999] flex flex-col justify-center bg-primary-100 dark:bg-neutral-6',
|
|
82
83
|
positionClass,
|
|
83
84
|
widthClass,
|
|
84
85
|
)}
|
|
@@ -88,7 +89,7 @@ const DialogContent = React.forwardRef<
|
|
|
88
89
|
<RadixDialog.Title />
|
|
89
90
|
<RadixDialog.Description />
|
|
90
91
|
</VisuallyHidden.Root>
|
|
91
|
-
{children}
|
|
92
|
+
<div className={cx('flex flex-1 flex-col', heightClass)}>{children}</div>
|
|
92
93
|
</RadixDialog.Content>
|
|
93
94
|
</RadixDialog.Portal>
|
|
94
95
|
);
|
|
@@ -112,7 +113,7 @@ const DialogBody = React.forwardRef<React.ElementRef<'div'>, React.ComponentProp
|
|
|
112
113
|
(props, ref) => (
|
|
113
114
|
<div
|
|
114
115
|
ref={ref}
|
|
115
|
-
className="p-md--rs text-body-sm text-neutral-40 dark:text-neutral-60"
|
|
116
|
+
className="flex-1 overflow-y-auto p-md--rs text-body-sm text-neutral-40 dark:text-neutral-60"
|
|
116
117
|
{...props}
|
|
117
118
|
/>
|
|
118
119
|
),
|
|
@@ -9,9 +9,9 @@ export type NavbarItemWithId = NavbarItemProps & { id: string };
|
|
|
9
9
|
|
|
10
10
|
export interface NavbarProps {
|
|
11
11
|
/**
|
|
12
|
-
* If this flag is true we need to leave only the icon and
|
|
12
|
+
* If this flag is true we need to leave only the icon and collapsible button
|
|
13
13
|
*/
|
|
14
|
-
|
|
14
|
+
isCollapsible?: boolean;
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* List of elements to be displayed in the navbar.
|
|
@@ -29,7 +29,7 @@ export interface NavbarProps {
|
|
|
29
29
|
onClickItem: (id: string) => void;
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
|
-
* If the navbar is
|
|
32
|
+
* If the navbar is collapsible, this flag indicates if it is open or not.
|
|
33
33
|
*/
|
|
34
34
|
isOpen?: boolean;
|
|
35
35
|
|
|
@@ -43,16 +43,16 @@ export function Navbar({
|
|
|
43
43
|
items,
|
|
44
44
|
activeId,
|
|
45
45
|
onClickItem,
|
|
46
|
-
|
|
46
|
+
isCollapsible = false,
|
|
47
47
|
onToggleNavbar,
|
|
48
48
|
}: NavbarProps) {
|
|
49
49
|
return (
|
|
50
50
|
<div
|
|
51
51
|
className={cx('flex h-fit w-full', {
|
|
52
|
-
'flex-col px-md py-xs sm:h-full sm:w-auto sm:px-none sm:py-xl':
|
|
52
|
+
'flex-col px-md py-xs sm:h-full sm:w-auto sm:px-none sm:py-xl': isCollapsible,
|
|
53
53
|
})}
|
|
54
54
|
>
|
|
55
|
-
{
|
|
55
|
+
{isCollapsible && (
|
|
56
56
|
<div className="flex w-full items-center justify-between sm:mb-[48px] sm:flex-col">
|
|
57
57
|
<div className="flex justify-center [&_svg]:h-[38px] [&_svg]:w-[38px]">
|
|
58
58
|
<IotaLogoMark className="text-neutral-10 dark:text-neutral-92" />
|
|
@@ -67,16 +67,16 @@ export function Navbar({
|
|
|
67
67
|
)}
|
|
68
68
|
<div
|
|
69
69
|
className={cx({
|
|
70
|
-
'flex w-full justify-between px-sm py-xxs': !
|
|
71
|
-
'hidden sm:flex sm:flex-col sm:gap-2':
|
|
70
|
+
'flex w-full justify-between px-sm py-xxs': !isCollapsible,
|
|
71
|
+
'hidden sm:flex sm:flex-col sm:gap-2': isCollapsible,
|
|
72
72
|
})}
|
|
73
73
|
>
|
|
74
74
|
{items.map((item) => (
|
|
75
75
|
<div
|
|
76
76
|
key={item.id}
|
|
77
77
|
className={cx('flex items-center', {
|
|
78
|
-
'px-xs py-xxs': !
|
|
79
|
-
'py-xxs pl-xs pr-sm':
|
|
78
|
+
'px-xs py-xxs': !isCollapsible,
|
|
79
|
+
'py-xxs pl-xs pr-sm': isCollapsible,
|
|
80
80
|
})}
|
|
81
81
|
data-testid={`nav-${item.id}`}
|
|
82
82
|
>
|
package/src/lib/styles/index.css
CHANGED
|
@@ -120,6 +120,19 @@
|
|
|
120
120
|
}
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
+
@-moz-document url-prefix() {
|
|
124
|
+
* {
|
|
125
|
+
scrollbar-width: thin;
|
|
126
|
+
scrollbar-color: theme('colors.neutral.30') transparent;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
:root:not(.dark) {
|
|
130
|
+
* {
|
|
131
|
+
scrollbar-color: theme('colors.neutral.80') transparent;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
123
136
|
@supports selector(::-webkit-scrollbar) {
|
|
124
137
|
*::-webkit-scrollbar {
|
|
125
138
|
@apply w-3.5;
|
|
@@ -10,10 +10,12 @@ import {
|
|
|
10
10
|
OPACITY,
|
|
11
11
|
generateVariableSpacing,
|
|
12
12
|
} from './constants';
|
|
13
|
+
import { firefoxPlugin } from './plugins';
|
|
13
14
|
|
|
14
15
|
export const BASE_CONFIG: Config = {
|
|
15
16
|
content: ['./src/**/*.{html,js,jsx,ts,tsx}'],
|
|
16
17
|
darkMode: 'class',
|
|
18
|
+
plugins: [firefoxPlugin],
|
|
17
19
|
theme: {
|
|
18
20
|
extend: {
|
|
19
21
|
fontSize: {
|
|
@@ -47,7 +47,7 @@ type Story = StoryObj<typeof meta>;
|
|
|
47
47
|
|
|
48
48
|
export const Default: Story = {};
|
|
49
49
|
|
|
50
|
-
export const
|
|
50
|
+
export const Collapsible: Story = {
|
|
51
51
|
args: {},
|
|
52
52
|
argTypes: {},
|
|
53
53
|
render: (args) => {
|
|
@@ -57,7 +57,7 @@ export const Collapsable: Story = {
|
|
|
57
57
|
return (
|
|
58
58
|
<div className="flex h-96">
|
|
59
59
|
<Navbar
|
|
60
|
-
|
|
60
|
+
isCollapsible
|
|
61
61
|
items={NAVBAR_ITEMS}
|
|
62
62
|
activeId={activeId}
|
|
63
63
|
onClickItem={(id) => setActiveId(id)}
|