@iota/apps-ui-kit 0.2.1 → 0.5.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 +51 -0
- package/README.md +16 -0
- package/dist/index.d.ts +91 -15
- package/dist/index.es.js +4536 -4297
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +52 -52
- package/dist/index.umd.js.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +3 -3
- package/src/lib/components/atoms/badge/badge.classes.ts +9 -0
- package/src/lib/components/atoms/badge/badge.enums.ts +3 -0
- package/src/lib/components/atoms/button/button.classes.ts +4 -4
- package/src/lib/components/atoms/index.ts +1 -0
- package/src/lib/components/atoms/info-box/InfoBox.tsx +1 -1
- 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/toggle/Toggle.tsx +140 -0
- package/src/lib/components/atoms/toggle/index.ts +6 -0
- package/src/lib/components/atoms/toggle/toggle.classes.ts +41 -0
- package/src/lib/components/atoms/toggle/toggle.enums.ts +12 -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/card/CardBody.tsx +12 -1
- package/src/lib/components/molecules/card/card.classes.ts +1 -0
- package/src/lib/components/molecules/card/card.enums.ts +1 -0
- package/src/lib/components/molecules/input/input.classes.ts +2 -2
- package/src/lib/components/molecules/search/Search.tsx +39 -5
- package/src/lib/components/molecules/table-header-cell/TableHeaderCell.tsx +27 -13
- package/src/lib/components/molecules/title/Title.tsx +9 -1
- 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/components/organisms/table/Table.tsx +19 -3
- package/src/lib/constants/colors.constants.ts +6 -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/atoms/Toggle.stories.tsx +59 -0
- package/src/storybook/stories/organisms/Navbar.stories.tsx +2 -2
|
@@ -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
|
);
|
|
@@ -36,6 +36,10 @@ interface TitleProps {
|
|
|
36
36
|
* The size of the component
|
|
37
37
|
*/
|
|
38
38
|
size?: TitleSize;
|
|
39
|
+
/**
|
|
40
|
+
* The 'data-testid' attribute value (used in e2e tests)
|
|
41
|
+
*/
|
|
42
|
+
testId?: string;
|
|
39
43
|
}
|
|
40
44
|
|
|
41
45
|
export function Title({
|
|
@@ -46,9 +50,13 @@ export function Title({
|
|
|
46
50
|
supportingElement,
|
|
47
51
|
tooltipPosition,
|
|
48
52
|
size = TitleSize.Medium,
|
|
53
|
+
testId,
|
|
49
54
|
}: TitleProps) {
|
|
50
55
|
return (
|
|
51
|
-
<div
|
|
56
|
+
<div
|
|
57
|
+
className={cx('flex flex-row items-center justify-between', TITLE_PADDINGS[size])}
|
|
58
|
+
data-testid={testId}
|
|
59
|
+
>
|
|
52
60
|
<div className="flex flex-row items-center gap-x-xxxs">
|
|
53
61
|
<div className="flex flex-col justify-start">
|
|
54
62
|
<div className="flex flex-row items-center gap-x-0.5 text-neutral-10 dark:text-neutral-92">
|
|
@@ -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
|
>
|
|
@@ -75,6 +75,11 @@ export type TableProps = {
|
|
|
75
75
|
* The page size selector component.
|
|
76
76
|
*/
|
|
77
77
|
pageSizeSelector?: ReactNode;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* If the table should take the full height of the parent.
|
|
81
|
+
*/
|
|
82
|
+
heightFull?: boolean;
|
|
78
83
|
};
|
|
79
84
|
|
|
80
85
|
export function Table({
|
|
@@ -85,12 +90,23 @@ export function Table({
|
|
|
85
90
|
rowIndexes,
|
|
86
91
|
children,
|
|
87
92
|
pageSizeSelector,
|
|
93
|
+
heightFull = false,
|
|
88
94
|
}: PropsWithChildren<TableProps>): JSX.Element {
|
|
89
95
|
return (
|
|
90
96
|
<TableProvider selectedRowIndexes={selectedRowIndexes} rowIndexes={rowIndexes}>
|
|
91
|
-
<div className=
|
|
92
|
-
<div
|
|
93
|
-
|
|
97
|
+
<div className={cx('w-full', { 'h-full': heightFull })}>
|
|
98
|
+
<div
|
|
99
|
+
className={cx('overflow-auto', {
|
|
100
|
+
'h-full': heightFull,
|
|
101
|
+
})}
|
|
102
|
+
>
|
|
103
|
+
<table
|
|
104
|
+
className={cx('w-full table-auto', {
|
|
105
|
+
'h-full': heightFull,
|
|
106
|
+
})}
|
|
107
|
+
>
|
|
108
|
+
{children}
|
|
109
|
+
</table>
|
|
94
110
|
</div>
|
|
95
111
|
<div
|
|
96
112
|
className={cx('flex w-full items-center gap-2 pt-md', {
|
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: {
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// Copyright (c) 2024 IOTA Stiftung
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
5
|
+
import { Toggle, ToggleLabelPosition, ToggleSize } from '@/components';
|
|
6
|
+
import { useState } from 'react';
|
|
7
|
+
|
|
8
|
+
const meta: Meta<typeof Toggle> = {
|
|
9
|
+
component: Toggle,
|
|
10
|
+
tags: ['autodocs'],
|
|
11
|
+
argTypes: {
|
|
12
|
+
isToggled: {
|
|
13
|
+
control: { type: 'boolean' },
|
|
14
|
+
},
|
|
15
|
+
label: {
|
|
16
|
+
control: { type: 'text' },
|
|
17
|
+
},
|
|
18
|
+
labelPosition: {
|
|
19
|
+
control: { type: 'select' },
|
|
20
|
+
options: Object.values(ToggleLabelPosition),
|
|
21
|
+
},
|
|
22
|
+
isDisabled: {
|
|
23
|
+
control: { type: 'boolean' },
|
|
24
|
+
},
|
|
25
|
+
size: {
|
|
26
|
+
control: { type: 'select' },
|
|
27
|
+
options: Object.values(ToggleSize),
|
|
28
|
+
},
|
|
29
|
+
onChange: {
|
|
30
|
+
action: 'changed',
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
args: {
|
|
34
|
+
isToggled: false,
|
|
35
|
+
isDisabled: false,
|
|
36
|
+
size: ToggleSize.Default,
|
|
37
|
+
labelPosition: ToggleLabelPosition.Right,
|
|
38
|
+
},
|
|
39
|
+
} satisfies Meta<typeof Toggle>;
|
|
40
|
+
|
|
41
|
+
export default meta;
|
|
42
|
+
|
|
43
|
+
type Story = StoryObj<typeof meta>;
|
|
44
|
+
|
|
45
|
+
export const Default: Story = {
|
|
46
|
+
args: {
|
|
47
|
+
label: 'Label',
|
|
48
|
+
},
|
|
49
|
+
render: (args) => {
|
|
50
|
+
const [isToggled, setIsToggled] = useState(args.isToggled);
|
|
51
|
+
|
|
52
|
+
const handleToggleChange = (newState: boolean) => {
|
|
53
|
+
console.log('Toggle state changed:', newState);
|
|
54
|
+
setIsToggled(newState);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
return <Toggle {...args} isToggled={isToggled} onChange={handleToggleChange} />;
|
|
58
|
+
},
|
|
59
|
+
};
|
|
@@ -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)}
|