@iota/apps-ui-kit 0.0.0-experimental-20250613144451
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 +106 -0
- package/LICENSE +201 -0
- package/README.md +16 -0
- package/dist/index.d.ts +1726 -0
- package/dist/index.es.js +6923 -0
- package/dist/index.es.js.map +1 -0
- package/dist/index.umd.js +118 -0
- package/dist/index.umd.js.map +1 -0
- package/dist/style.css +1 -0
- package/package.json +75 -0
- package/src/lib/components/atoms/badge/Badge.tsx +37 -0
- package/src/lib/components/atoms/badge/badge.classes.ts +33 -0
- package/src/lib/components/atoms/badge/badge.enums.ts +11 -0
- package/src/lib/components/atoms/badge/index.ts +6 -0
- package/src/lib/components/atoms/button/Button.tsx +99 -0
- package/src/lib/components/atoms/button/button-variants/ButtonPill.tsx +23 -0
- package/src/lib/components/atoms/button/button-variants/ButtonUnstyled.tsx +27 -0
- package/src/lib/components/atoms/button/button-variants/buttonVariants.types.ts +11 -0
- package/src/lib/components/atoms/button/button-variants/index.ts +5 -0
- package/src/lib/components/atoms/button/button.classes.ts +53 -0
- package/src/lib/components/atoms/button/button.enums.ts +21 -0
- package/src/lib/components/atoms/button/index.ts +7 -0
- package/src/lib/components/atoms/button-segment/ButtonSegment.tsx +98 -0
- package/src/lib/components/atoms/button-segment/buttonSegment.classes.ts +45 -0
- package/src/lib/components/atoms/button-segment/buttonSegment.enums.ts +7 -0
- package/src/lib/components/atoms/button-segment/index.ts +5 -0
- package/src/lib/components/atoms/checkbox/Checkbox.tsx +114 -0
- package/src/lib/components/atoms/checkbox/index.ts +4 -0
- package/src/lib/components/atoms/divider/Divider.tsx +48 -0
- package/src/lib/components/atoms/divider/divider.classes.ts +11 -0
- package/src/lib/components/atoms/divider/divider.enums.ts +7 -0
- package/src/lib/components/atoms/divider/index.ts +6 -0
- package/src/lib/components/atoms/header/Header.tsx +73 -0
- package/src/lib/components/atoms/header/index.ts +4 -0
- package/src/lib/components/atoms/index.ts +23 -0
- package/src/lib/components/atoms/info-box/InfoBox.tsx +74 -0
- package/src/lib/components/atoms/info-box/index.ts +5 -0
- package/src/lib/components/atoms/info-box/infoBox.classes.ts +19 -0
- package/src/lib/components/atoms/info-box/infoBox.enums.ts +14 -0
- package/src/lib/components/atoms/key-value-info/KeyValueInfo.tsx +157 -0
- package/src/lib/components/atoms/key-value-info/index.ts +6 -0
- package/src/lib/components/atoms/key-value-info/keyValue.enums.ts +7 -0
- package/src/lib/components/atoms/label-text/LabelText.tsx +94 -0
- package/src/lib/components/atoms/label-text/index.ts +5 -0
- package/src/lib/components/atoms/label-text/labelText.classes.ts +22 -0
- package/src/lib/components/atoms/label-text/labelText.enums.ts +8 -0
- package/src/lib/components/atoms/list-item/ListItem.tsx +89 -0
- package/src/lib/components/atoms/list-item/index.ts +4 -0
- package/src/lib/components/atoms/loading-indicator/LoadingIndicator.tsx +38 -0
- package/src/lib/components/atoms/loading-indicator/index.ts +4 -0
- package/src/lib/components/atoms/panel/Panel.tsx +30 -0
- package/src/lib/components/atoms/panel/index.ts +4 -0
- package/src/lib/components/atoms/placeholder/Placeholder.tsx +27 -0
- package/src/lib/components/atoms/placeholder/index.ts +4 -0
- package/src/lib/components/atoms/radio-button/RadioButton.tsx +68 -0
- package/src/lib/components/atoms/radio-button/index.ts +4 -0
- package/src/lib/components/atoms/secondary-text/SecondaryText.tsx +28 -0
- package/src/lib/components/atoms/secondary-text/index.ts +4 -0
- package/src/lib/components/atoms/skeleton/Skeleton.tsx +47 -0
- package/src/lib/components/atoms/skeleton/index.ts +4 -0
- package/src/lib/components/atoms/snackbar/Snackbar.tsx +69 -0
- package/src/lib/components/atoms/snackbar/index.ts +5 -0
- package/src/lib/components/atoms/snackbar/snackbar.classes.ts +18 -0
- package/src/lib/components/atoms/snackbar/snackbar.enums.tsx +9 -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 +138 -0
- package/src/lib/components/atoms/tooltip/index.ts +5 -0
- package/src/lib/components/atoms/tooltip/tooltip.classes.ts +11 -0
- package/src/lib/components/atoms/tooltip/tooltip.enums.ts +9 -0
- package/src/lib/components/atoms/visual-asset-card/VisualAssetCard.tsx +101 -0
- package/src/lib/components/atoms/visual-asset-card/index.ts +5 -0
- package/src/lib/components/atoms/visual-asset-card/visualAssetCard.enums.ts +7 -0
- package/src/lib/components/index.ts +8 -0
- package/src/lib/components/molecules/account/Account.tsx +192 -0
- package/src/lib/components/molecules/account/index.ts +4 -0
- package/src/lib/components/molecules/address/Address.tsx +101 -0
- package/src/lib/components/molecules/address/index.ts +4 -0
- package/src/lib/components/molecules/card/Card.tsx +70 -0
- package/src/lib/components/molecules/card/CardAction.tsx +80 -0
- package/src/lib/components/molecules/card/CardBody.tsx +72 -0
- package/src/lib/components/molecules/card/CardImage.tsx +43 -0
- package/src/lib/components/molecules/card/CardImagePlaceholder.tsx +146 -0
- package/src/lib/components/molecules/card/card.classes.ts +27 -0
- package/src/lib/components/molecules/card/card.enums.ts +26 -0
- package/src/lib/components/molecules/card/index.ts +8 -0
- package/src/lib/components/molecules/chip/Chip.tsx +105 -0
- package/src/lib/components/molecules/chip/chip.classes.ts +32 -0
- package/src/lib/components/molecules/chip/chip.enums.ts +7 -0
- package/src/lib/components/molecules/chip/index.ts +4 -0
- package/src/lib/components/molecules/display-stats/DisplayStats.tsx +142 -0
- package/src/lib/components/molecules/display-stats/displayStats.classes.ts +36 -0
- package/src/lib/components/molecules/display-stats/displayStats.enums.ts +13 -0
- package/src/lib/components/molecules/display-stats/index.ts +5 -0
- package/src/lib/components/molecules/dropdown/Dropdown.tsx +10 -0
- package/src/lib/components/molecules/dropdown/dropdownPosition.enums.ts +7 -0
- package/src/lib/components/molecules/dropdown/index.ts +5 -0
- package/src/lib/components/molecules/index.ts +17 -0
- package/src/lib/components/molecules/input/Input.tsx +246 -0
- package/src/lib/components/molecules/input/InputWrapper.tsx +97 -0
- package/src/lib/components/molecules/input/TextArea.tsx +150 -0
- package/src/lib/components/molecules/input/index.ts +8 -0
- package/src/lib/components/molecules/input/input.classes.ts +14 -0
- package/src/lib/components/molecules/input/input.enums.ts +14 -0
- package/src/lib/components/molecules/input/input.types.ts +29 -0
- package/src/lib/components/molecules/navbar-item/NavbarItem.tsx +48 -0
- package/src/lib/components/molecules/navbar-item/NavbarItemHorizontal.tsx +65 -0
- package/src/lib/components/molecules/navbar-item/NavbarItemVertical.tsx +54 -0
- package/src/lib/components/molecules/navbar-item/index.ts +5 -0
- package/src/lib/components/molecules/navbar-item/navbarItem.classes.ts +16 -0
- package/src/lib/components/molecules/navbar-item/navbarItem.enums.ts +7 -0
- package/src/lib/components/molecules/search/Search.tsx +216 -0
- package/src/lib/components/molecules/search/index.ts +5 -0
- package/src/lib/components/molecules/search/search.classes.ts +20 -0
- package/src/lib/components/molecules/search/search.enums.ts +7 -0
- package/src/lib/components/molecules/segmented-button/SegmentedButton.tsx +38 -0
- package/src/lib/components/molecules/segmented-button/index.ts +6 -0
- package/src/lib/components/molecules/segmented-button/segmentedButton.classes.ts +12 -0
- package/src/lib/components/molecules/segmented-button/segmentedButton.enums.ts +8 -0
- package/src/lib/components/molecules/select/Select.tsx +222 -0
- package/src/lib/components/molecules/select/index.ts +5 -0
- package/src/lib/components/molecules/select/select.enums.ts +7 -0
- package/src/lib/components/molecules/table-cell/TableCell.tsx +62 -0
- package/src/lib/components/molecules/table-cell/index.ts +4 -0
- package/src/lib/components/molecules/table-header-cell/TableHeaderCell.tsx +140 -0
- package/src/lib/components/molecules/table-header-cell/index.ts +5 -0
- package/src/lib/components/molecules/table-header-cell/tableHeaderCell.enums.ts +7 -0
- package/src/lib/components/molecules/title/Title.tsx +78 -0
- package/src/lib/components/molecules/title/index.ts +5 -0
- package/src/lib/components/molecules/title/titleClasses.constants.ts +14 -0
- package/src/lib/components/molecules/title/titleSize.enums.ts +7 -0
- package/src/lib/components/organisms/accordion/Accordion.tsx +109 -0
- package/src/lib/components/organisms/accordion/accordion.classes.ts +4 -0
- package/src/lib/components/organisms/accordion/index.ts +4 -0
- package/src/lib/components/organisms/dialog/Dialog.tsx +142 -0
- package/src/lib/components/organisms/dialog/dialog.enums.ts +7 -0
- package/src/lib/components/organisms/dialog/index.ts +5 -0
- package/src/lib/components/organisms/index.ts +7 -0
- package/src/lib/components/organisms/navbar/Navbar.tsx +101 -0
- package/src/lib/components/organisms/navbar/NavbarSlideout.tsx +59 -0
- package/src/lib/components/organisms/navbar/index.ts +5 -0
- package/src/lib/components/organisms/table/Table.tsx +234 -0
- package/src/lib/components/organisms/table/TableContext.tsx +56 -0
- package/src/lib/components/organisms/table/index.ts +4 -0
- package/src/lib/constants/colors.constants.ts +133 -0
- package/src/lib/constants/index.ts +4 -0
- package/src/lib/enums/index.ts +4 -0
- package/src/lib/enums/screenSize.enums.ts +9 -0
- package/src/lib/index.ts +8 -0
- package/src/lib/styles/fonts.css +5 -0
- package/src/lib/styles/index.css +149 -0
- package/src/lib/tailwind/base.presets.ts +49 -0
- package/src/lib/tailwind/constants/fontSizes.constants.ts +201 -0
- package/src/lib/tailwind/constants/index.ts +6 -0
- package/src/lib/tailwind/constants/scaling.constants.ts +41 -0
- package/src/lib/tailwind/constants/variableSpacing.constants.ts +66 -0
- package/src/lib/tailwind/helpers.ts +20 -0
- package/src/lib/tailwind/index.ts +5 -0
- package/src/lib/tailwind/plugins.ts +8 -0
- package/src/lib/tailwind/responsive.presets.ts +36 -0
- package/src/lib/tailwind/static.presets.ts +25 -0
- package/src/lib/tailwind/types.ts +6 -0
- package/src/lib/utils/format.ts +8 -0
- package/src/lib/utils/index.ts +4 -0
- package/src/storybook/blocks/Showcase.tsx +20 -0
- package/src/storybook/blocks/TypeSet.tsx +34 -0
- package/src/storybook/blocks/TypeSetItem.tsx +74 -0
- package/src/storybook/blocks/index.ts +6 -0
- package/src/storybook/constants/index.ts +5 -0
- package/src/storybook/constants/typeset.constants.ts +39 -0
- package/src/storybook/constants/variableSpacing.constants.ts +6 -0
- package/src/storybook/helpers/getFontSizeLabel.ts +15 -0
- package/src/storybook/helpers/index.ts +4 -0
- package/src/storybook/stories/atoms/Badge.stories.tsx +32 -0
- package/src/storybook/stories/atoms/Button.stories.tsx +45 -0
- package/src/storybook/stories/atoms/ButtonSegment.stories.tsx +45 -0
- package/src/storybook/stories/atoms/Checkbox.stories.tsx +34 -0
- package/src/storybook/stories/atoms/CustomScrollbar.stories.tsx +22 -0
- package/src/storybook/stories/atoms/Divider.stories.tsx +41 -0
- package/src/storybook/stories/atoms/Header.stories.tsx +58 -0
- package/src/storybook/stories/atoms/InfoBox.stories.tsx +67 -0
- package/src/storybook/stories/atoms/KeyValueInfo.stories.tsx +59 -0
- package/src/storybook/stories/atoms/LabelText.stories.tsx +55 -0
- package/src/storybook/stories/atoms/ListItem.stories.tsx +41 -0
- package/src/storybook/stories/atoms/LoadingIndicator.stories.tsx +32 -0
- package/src/storybook/stories/atoms/Panel.stories.tsx +36 -0
- package/src/storybook/stories/atoms/Placeholder.stories.tsx +33 -0
- package/src/storybook/stories/atoms/RadioButton.stories.tsx +51 -0
- package/src/storybook/stories/atoms/Skeleton.stories.tsx +33 -0
- package/src/storybook/stories/atoms/Snackbar.stories.tsx +43 -0
- package/src/storybook/stories/atoms/Toggle.stories.tsx +59 -0
- package/src/storybook/stories/atoms/Tooltip.stories.tsx +57 -0
- package/src/storybook/stories/atoms/VisualAssetCard.stories.tsx +56 -0
- package/src/storybook/stories/components/VariableSpacingShowcase.tsx +46 -0
- package/src/storybook/stories/components/index.ts +4 -0
- package/src/storybook/stories/design-tokens/colors.stories.mdx +23 -0
- package/src/storybook/stories/design-tokens/icons.stories.mdx +17 -0
- package/src/storybook/stories/design-tokens/typeset.stories.mdx +15 -0
- package/src/storybook/stories/design-tokens/variable-spacing.stories.mdx +10 -0
- package/src/storybook/stories/molecules/Account.stories.tsx +69 -0
- package/src/storybook/stories/molecules/Address.stories.tsx +39 -0
- package/src/storybook/stories/molecules/Card.stories.tsx +112 -0
- package/src/storybook/stories/molecules/Chip.stories.tsx +122 -0
- package/src/storybook/stories/molecules/DisplayStats.stories.tsx +63 -0
- package/src/storybook/stories/molecules/Dropdown.stories.tsx +38 -0
- package/src/storybook/stories/molecules/Input.stories.tsx +162 -0
- package/src/storybook/stories/molecules/NavbarItem.stories.tsx +45 -0
- package/src/storybook/stories/molecules/Search.stories.tsx +101 -0
- package/src/storybook/stories/molecules/SegmentedButton.stories.tsx +108 -0
- package/src/storybook/stories/molecules/Select.stories.tsx +103 -0
- package/src/storybook/stories/molecules/TableCell.stories.tsx +46 -0
- package/src/storybook/stories/molecules/TableHeaderCell.stories.tsx +34 -0
- package/src/storybook/stories/molecules/TextArea.stories.tsx +80 -0
- package/src/storybook/stories/molecules/Title.stories.tsx +76 -0
- package/src/storybook/stories/organisms/Accordion.stories.tsx +70 -0
- package/src/storybook/stories/organisms/Dialog.stories.tsx +103 -0
- package/src/storybook/stories/organisms/Navbar.stories.tsx +78 -0
- package/src/storybook/stories/organisms/Table.stories.tsx +154 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// Copyright (c) 2025 IOTA Stiftung
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
import { ToggleSize } from './toggle.enums';
|
|
5
|
+
|
|
6
|
+
export const TOGGLE = 'relative inline-flex items-center p-xxs border rounded-full cursor-pointer';
|
|
7
|
+
|
|
8
|
+
export const TOGGLE_STATES = {
|
|
9
|
+
active: 'bg-primary-30 border-primary-30',
|
|
10
|
+
inactive: 'bg-primary-100 dark:bg-neutral-6 border-neutral-70 dark:border-neutral-40',
|
|
11
|
+
disabledActive: 'bg-neutral-70 border-neutral-70',
|
|
12
|
+
disabled: 'opacity-40 cursor-not-allowed',
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const TOGGLE_SIZE = {
|
|
16
|
+
[ToggleSize.Small]: 'h-5 w-10',
|
|
17
|
+
[ToggleSize.Default]: 'h-6 w-12',
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const TOGGLE_THUMB = 'absolute rounded-full transition-all duration-200 ease-in';
|
|
21
|
+
export const TOGGLE_THUMB_POSITION = {
|
|
22
|
+
[ToggleSize.Small]: {
|
|
23
|
+
unchecked: 'left-1',
|
|
24
|
+
checked: 'left-8 -translate-x-full',
|
|
25
|
+
},
|
|
26
|
+
[ToggleSize.Default]: {
|
|
27
|
+
unchecked: 'left-1',
|
|
28
|
+
checked: 'left-10 -translate-x-full',
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
export const TOGGLE_THUMB_COLOR = {
|
|
32
|
+
unchecked: 'bg-neutral-60',
|
|
33
|
+
checked: 'bg-white',
|
|
34
|
+
};
|
|
35
|
+
export const TOGGLE_THUMB_SIZE = {
|
|
36
|
+
[ToggleSize.Small]: 'h-3 w-3',
|
|
37
|
+
[ToggleSize.Default]: 'h-4 w-4',
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export const TOGGLE_CONTAINER = 'inline-flex items-center gap-2';
|
|
41
|
+
export const TOGGLE_LABEL = 'text-label-lg text-neutral-40 dark:text-neutral-60';
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
// Copyright (c) 2024 IOTA Stiftung
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
import { useRef, useState, useLayoutEffect } from 'react';
|
|
5
|
+
import type { PropsWithChildren } from 'react';
|
|
6
|
+
import { createPortal } from 'react-dom';
|
|
7
|
+
import cx from 'classnames';
|
|
8
|
+
import { TooltipPosition } from './tooltip.enums';
|
|
9
|
+
|
|
10
|
+
interface TooltipProps {
|
|
11
|
+
text: string;
|
|
12
|
+
position?: TooltipPosition;
|
|
13
|
+
maxWidth?: string;
|
|
14
|
+
offset?: number;
|
|
15
|
+
openDelay?: number;
|
|
16
|
+
closeDelay?: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function Tooltip({
|
|
20
|
+
text,
|
|
21
|
+
position = TooltipPosition.Top,
|
|
22
|
+
maxWidth = 'max-w-[200px]',
|
|
23
|
+
offset = 8,
|
|
24
|
+
openDelay = 0,
|
|
25
|
+
closeDelay = 100,
|
|
26
|
+
children,
|
|
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
|
+
|
|
103
|
+
return (
|
|
104
|
+
<>
|
|
105
|
+
<div
|
|
106
|
+
ref={triggerRef}
|
|
107
|
+
className="inline-block cursor-pointer"
|
|
108
|
+
onMouseEnter={open}
|
|
109
|
+
onFocus={open}
|
|
110
|
+
onMouseLeave={close}
|
|
111
|
+
onBlur={close}
|
|
112
|
+
>
|
|
113
|
+
{children}
|
|
114
|
+
</div>
|
|
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
|
+
</>
|
|
137
|
+
);
|
|
138
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Copyright (c) 2024 IOTA Stiftung
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
import { TooltipPosition } from './tooltip.enums';
|
|
5
|
+
|
|
6
|
+
export const TOOLTIP_POSITION: Record<TooltipPosition, string> = {
|
|
7
|
+
[TooltipPosition.Top]: 'bottom-full left-1/2 transform -translate-x-1/2 mb-1',
|
|
8
|
+
[TooltipPosition.Bottom]: 'top-full left-1/2 transform -translate-x-1/2 mt-1',
|
|
9
|
+
[TooltipPosition.Left]: 'top-1/2 right-full transform -translate-y-1/2 mr-1',
|
|
10
|
+
[TooltipPosition.Right]: 'top-1/2 left-full transform -translate-y-1/2 ml-1',
|
|
11
|
+
};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
// Copyright (c) 2024 IOTA Stiftung
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
import { VisualAssetType } from './visualAssetCard.enums';
|
|
5
|
+
import { ButtonUnstyled } from '../button';
|
|
6
|
+
import { MoreHoriz, PlaceholderReplace } from '@iota/apps-ui-icons';
|
|
7
|
+
import cx from 'classnames';
|
|
8
|
+
import { useState } from 'react';
|
|
9
|
+
|
|
10
|
+
export interface VisualAssetCardProps {
|
|
11
|
+
/**
|
|
12
|
+
* The type of the asset to be displayed.
|
|
13
|
+
*/
|
|
14
|
+
assetType?: VisualAssetType;
|
|
15
|
+
/**
|
|
16
|
+
* The source of the image to be displayed.
|
|
17
|
+
*/
|
|
18
|
+
assetSrc: string;
|
|
19
|
+
/**
|
|
20
|
+
* Alt text for the image.
|
|
21
|
+
*/
|
|
22
|
+
altText: string;
|
|
23
|
+
/**
|
|
24
|
+
* The onClick event for the icon.
|
|
25
|
+
*/
|
|
26
|
+
onIconClick?: (e: React.MouseEvent<HTMLButtonElement>) => void;
|
|
27
|
+
/**
|
|
28
|
+
* The onClick event for the card.
|
|
29
|
+
*/
|
|
30
|
+
onClick?: (e: React.MouseEvent<HTMLDivElement>) => void;
|
|
31
|
+
/**
|
|
32
|
+
* The icon to be displayed.
|
|
33
|
+
*/
|
|
34
|
+
icon?: React.ReactNode;
|
|
35
|
+
/**
|
|
36
|
+
* The title text to be displayed on hover.
|
|
37
|
+
*/
|
|
38
|
+
assetTitle?: string;
|
|
39
|
+
/**
|
|
40
|
+
* Whether the card is hoverable.
|
|
41
|
+
*/
|
|
42
|
+
isHoverable?: boolean;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function VisualAssetCard({
|
|
46
|
+
assetType = VisualAssetType.Image,
|
|
47
|
+
assetSrc,
|
|
48
|
+
altText,
|
|
49
|
+
onIconClick,
|
|
50
|
+
onClick,
|
|
51
|
+
icon = <MoreHoriz />,
|
|
52
|
+
assetTitle,
|
|
53
|
+
isHoverable = true,
|
|
54
|
+
}: VisualAssetCardProps): React.JSX.Element {
|
|
55
|
+
const [imageError, setImageError] = useState(false);
|
|
56
|
+
|
|
57
|
+
const handleIconClick = (event: React.MouseEvent<HTMLButtonElement>) => {
|
|
58
|
+
onIconClick?.(event);
|
|
59
|
+
event?.stopPropagation();
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<div
|
|
64
|
+
className={cx('relative aspect-square w-full overflow-hidden rounded-xl', {
|
|
65
|
+
'group cursor-pointer': isHoverable,
|
|
66
|
+
})}
|
|
67
|
+
onClick={onClick}
|
|
68
|
+
>
|
|
69
|
+
{assetType === VisualAssetType.Video ? (
|
|
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>
|
|
75
|
+
) : (
|
|
76
|
+
<img
|
|
77
|
+
src={assetSrc}
|
|
78
|
+
alt={altText}
|
|
79
|
+
className="h-full w-full object-cover"
|
|
80
|
+
onError={() => setImageError(true)}
|
|
81
|
+
/>
|
|
82
|
+
)}
|
|
83
|
+
{isHoverable && (
|
|
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" />
|
|
85
|
+
)}
|
|
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>
|
|
98
|
+
)}
|
|
99
|
+
</div>
|
|
100
|
+
);
|
|
101
|
+
}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
// Copyright (c) 2024 IOTA Stiftung
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
import cx from 'classnames';
|
|
5
|
+
import type { BadgeType } from '@/components/atoms';
|
|
6
|
+
import { Badge, Tooltip, TooltipPosition, ButtonUnstyled } from '@/components/atoms';
|
|
7
|
+
import {
|
|
8
|
+
LockLocked,
|
|
9
|
+
LockUnlocked,
|
|
10
|
+
MoreHoriz,
|
|
11
|
+
CheckmarkFilled,
|
|
12
|
+
RadioOff,
|
|
13
|
+
} from '@iota/apps-ui-icons';
|
|
14
|
+
import { Address } from '../address';
|
|
15
|
+
|
|
16
|
+
interface AccountProps {
|
|
17
|
+
/**
|
|
18
|
+
* The title of the account.
|
|
19
|
+
*/
|
|
20
|
+
title: string;
|
|
21
|
+
/**
|
|
22
|
+
* The subtitle of the account.
|
|
23
|
+
*/
|
|
24
|
+
subtitle: string;
|
|
25
|
+
/**
|
|
26
|
+
* Whether the account is unlocked.
|
|
27
|
+
*/
|
|
28
|
+
isLocked?: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Handler for more options click.
|
|
31
|
+
*/
|
|
32
|
+
onOptionsClick?: (e: React.MouseEvent<HTMLButtonElement>) => void;
|
|
33
|
+
/**
|
|
34
|
+
* Handler for the lock account icon click.
|
|
35
|
+
*/
|
|
36
|
+
onLockAccountClick?: (e: React.MouseEvent<HTMLButtonElement>) => void;
|
|
37
|
+
/**
|
|
38
|
+
* Handle for the unlock account icon click.
|
|
39
|
+
*/
|
|
40
|
+
onUnlockAccountClick?: (e: React.MouseEvent<HTMLButtonElement>) => void;
|
|
41
|
+
/**
|
|
42
|
+
* Function to render avatar content.
|
|
43
|
+
*/
|
|
44
|
+
avatarContent: ({ isLocked }: { isLocked?: boolean }) => React.JSX.Element;
|
|
45
|
+
/**
|
|
46
|
+
* The onCopy event of the Address (optional).
|
|
47
|
+
*/
|
|
48
|
+
onCopy?: (e: React.MouseEvent<HTMLButtonElement>) => void;
|
|
49
|
+
/**
|
|
50
|
+
* Text that need to be copied (optional).
|
|
51
|
+
*/
|
|
52
|
+
copyText?: string;
|
|
53
|
+
/**
|
|
54
|
+
* The onOpen event of the Address (optional).
|
|
55
|
+
*/
|
|
56
|
+
onOpen?: (e: React.MouseEvent<HTMLButtonElement>) => void;
|
|
57
|
+
/**
|
|
58
|
+
* Has copy icon (optional).
|
|
59
|
+
*/
|
|
60
|
+
isCopyable?: boolean;
|
|
61
|
+
/**
|
|
62
|
+
* Has open icon (optional).
|
|
63
|
+
*/
|
|
64
|
+
isExternal?: boolean;
|
|
65
|
+
/**
|
|
66
|
+
* The account is selected.
|
|
67
|
+
*/
|
|
68
|
+
isSelected?: boolean;
|
|
69
|
+
/**
|
|
70
|
+
* Show the selected checkbox.
|
|
71
|
+
*/
|
|
72
|
+
showSelected?: boolean;
|
|
73
|
+
/**
|
|
74
|
+
* Show background if account active (optional).
|
|
75
|
+
*/
|
|
76
|
+
isActive?: boolean;
|
|
77
|
+
/**
|
|
78
|
+
* The type of the badge.
|
|
79
|
+
*/
|
|
80
|
+
badgeType?: BadgeType;
|
|
81
|
+
/**
|
|
82
|
+
* The text of the badge.
|
|
83
|
+
*/
|
|
84
|
+
badgeText?: string;
|
|
85
|
+
/**
|
|
86
|
+
* The tooltip text of the badge.
|
|
87
|
+
*/
|
|
88
|
+
badgeTooltipText?: string;
|
|
89
|
+
/**
|
|
90
|
+
* The tooltip position of the badge.
|
|
91
|
+
*/
|
|
92
|
+
badgeTooltipPosition?: TooltipPosition;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function Account({
|
|
96
|
+
title,
|
|
97
|
+
subtitle,
|
|
98
|
+
badgeType,
|
|
99
|
+
badgeText,
|
|
100
|
+
isLocked,
|
|
101
|
+
avatarContent,
|
|
102
|
+
onOptionsClick,
|
|
103
|
+
onLockAccountClick,
|
|
104
|
+
onUnlockAccountClick,
|
|
105
|
+
onCopy,
|
|
106
|
+
copyText,
|
|
107
|
+
onOpen,
|
|
108
|
+
isCopyable,
|
|
109
|
+
isExternal,
|
|
110
|
+
isSelected,
|
|
111
|
+
isActive,
|
|
112
|
+
showSelected,
|
|
113
|
+
badgeTooltipText,
|
|
114
|
+
badgeTooltipPosition = TooltipPosition.Bottom,
|
|
115
|
+
}: AccountProps): React.JSX.Element {
|
|
116
|
+
const Avatar = avatarContent;
|
|
117
|
+
|
|
118
|
+
return (
|
|
119
|
+
<div
|
|
120
|
+
className={cx(
|
|
121
|
+
'state-layer group relative flex w-full items-center justify-between space-x-3 rounded-xl px-sm py-xs hover:cursor-pointer',
|
|
122
|
+
isActive && 'state-active',
|
|
123
|
+
{
|
|
124
|
+
'opacity-60': isLocked && showSelected,
|
|
125
|
+
},
|
|
126
|
+
)}
|
|
127
|
+
>
|
|
128
|
+
<div className="flex items-center space-x-3">
|
|
129
|
+
<Avatar isLocked={isLocked} />
|
|
130
|
+
<div className="flex flex-col items-start py-xs">
|
|
131
|
+
<div className="flex items-center space-x-2">
|
|
132
|
+
<span className="font-inter text-title-md text-neutral-10 dark:text-neutral-92">
|
|
133
|
+
{title}
|
|
134
|
+
</span>
|
|
135
|
+
{badgeText && badgeType ? (
|
|
136
|
+
badgeTooltipText ? (
|
|
137
|
+
<Tooltip text={badgeTooltipText} position={badgeTooltipPosition}>
|
|
138
|
+
<Badge type={badgeType} label={badgeText} />
|
|
139
|
+
</Tooltip>
|
|
140
|
+
) : (
|
|
141
|
+
<Badge type={badgeType} label={badgeText} />
|
|
142
|
+
)
|
|
143
|
+
) : null}
|
|
144
|
+
</div>
|
|
145
|
+
<Address
|
|
146
|
+
text={subtitle}
|
|
147
|
+
onCopySuccess={onCopy}
|
|
148
|
+
copyText={copyText}
|
|
149
|
+
onOpen={onOpen}
|
|
150
|
+
isCopyable={isCopyable}
|
|
151
|
+
isExternal={isExternal}
|
|
152
|
+
/>
|
|
153
|
+
</div>
|
|
154
|
+
</div>
|
|
155
|
+
<div className="z-10 ml-auto flex items-center space-x-2 [&_button]:h-5 [&_button]:w-5 [&_svg]:h-5 [&_svg]:w-5">
|
|
156
|
+
<div className="flex items-center space-x-2 [&_button:not(.locked)]:hidden group-hover:[&_button:not(.locked)]:flex [&_svg]:text-neutral-40 [&_svg]:dark:text-neutral-60">
|
|
157
|
+
{onOptionsClick && (
|
|
158
|
+
<ButtonUnstyled onClick={onOptionsClick}>
|
|
159
|
+
<MoreHoriz />
|
|
160
|
+
</ButtonUnstyled>
|
|
161
|
+
)}
|
|
162
|
+
{onLockAccountClick &&
|
|
163
|
+
onUnlockAccountClick &&
|
|
164
|
+
(isLocked ? (
|
|
165
|
+
<div className="flex items-center">
|
|
166
|
+
<ButtonUnstyled
|
|
167
|
+
onClick={onUnlockAccountClick}
|
|
168
|
+
testId="account-unlock"
|
|
169
|
+
className="locked"
|
|
170
|
+
>
|
|
171
|
+
<LockLocked />
|
|
172
|
+
</ButtonUnstyled>
|
|
173
|
+
</div>
|
|
174
|
+
) : (
|
|
175
|
+
<ButtonUnstyled onClick={onLockAccountClick} testId="account-lock">
|
|
176
|
+
<LockUnlocked />
|
|
177
|
+
</ButtonUnstyled>
|
|
178
|
+
))}
|
|
179
|
+
</div>
|
|
180
|
+
{showSelected && (
|
|
181
|
+
<ButtonUnstyled>
|
|
182
|
+
{isSelected ? (
|
|
183
|
+
<CheckmarkFilled className="h-5 w-5 text-primary-30 dark:text-primary-80" />
|
|
184
|
+
) : (
|
|
185
|
+
<RadioOff className="h-5 w-5 text-neutral-40 dark:text-neutral-60" />
|
|
186
|
+
)}
|
|
187
|
+
</ButtonUnstyled>
|
|
188
|
+
)}
|
|
189
|
+
</div>
|
|
190
|
+
</div>
|
|
191
|
+
);
|
|
192
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
// Copyright (c) 2024 IOTA Stiftung
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
import cx from 'classnames';
|
|
5
|
+
import { Copy, ArrowTopRight } from '@iota/apps-ui-icons';
|
|
6
|
+
import { ButtonUnstyled } from '@/components/atoms/button';
|
|
7
|
+
|
|
8
|
+
interface AddressProps {
|
|
9
|
+
/**
|
|
10
|
+
* The text of the address.
|
|
11
|
+
*/
|
|
12
|
+
text: string;
|
|
13
|
+
/**
|
|
14
|
+
* Has copy icon (optional).
|
|
15
|
+
*/
|
|
16
|
+
isCopyable?: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Has open icon (optional).
|
|
19
|
+
*/
|
|
20
|
+
isExternal?: boolean;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The link for external resource (optional).
|
|
24
|
+
*/
|
|
25
|
+
externalLink?: string;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Text that need to be copied (optional).
|
|
29
|
+
*/
|
|
30
|
+
copyText?: string;
|
|
31
|
+
/**
|
|
32
|
+
* The onCopySuccess event of the Address (optional).
|
|
33
|
+
*/
|
|
34
|
+
onCopySuccess?: (e: React.MouseEvent<HTMLButtonElement>, text: string) => void;
|
|
35
|
+
/**
|
|
36
|
+
* The onCopyError event of the Address (optional).
|
|
37
|
+
*/
|
|
38
|
+
onCopyError?: (e: unknown, text: string) => void;
|
|
39
|
+
/**
|
|
40
|
+
* The onOpen event of the Address (optional).
|
|
41
|
+
*/
|
|
42
|
+
onOpen?: (e: React.MouseEvent<HTMLButtonElement>) => void;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function Address({
|
|
46
|
+
text,
|
|
47
|
+
isCopyable,
|
|
48
|
+
isExternal,
|
|
49
|
+
externalLink,
|
|
50
|
+
copyText = text,
|
|
51
|
+
onCopySuccess,
|
|
52
|
+
onCopyError,
|
|
53
|
+
onOpen,
|
|
54
|
+
}: AddressProps): React.JSX.Element {
|
|
55
|
+
async function handleCopyClick(event: React.MouseEvent<HTMLButtonElement>) {
|
|
56
|
+
if (!navigator.clipboard) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
event?.stopPropagation();
|
|
61
|
+
try {
|
|
62
|
+
await navigator.clipboard.writeText(copyText);
|
|
63
|
+
onCopySuccess?.(event, copyText);
|
|
64
|
+
} catch (error) {
|
|
65
|
+
console.error('Failed to copy:', error);
|
|
66
|
+
onCopyError?.(error, copyText);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function handleOpenClick(event: React.MouseEvent<HTMLButtonElement>) {
|
|
71
|
+
event?.stopPropagation();
|
|
72
|
+
if (externalLink) {
|
|
73
|
+
const newWindow = window.open(externalLink, '_blank', 'noopener noreferrer');
|
|
74
|
+
if (newWindow) newWindow.opener = null;
|
|
75
|
+
} else {
|
|
76
|
+
onOpen?.(event);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return (
|
|
81
|
+
<div className="group flex flex-row items-center gap-1 text-neutral-40 dark:text-neutral-60">
|
|
82
|
+
<span className={cx('font-inter text-body-sm')}>{text}</span>
|
|
83
|
+
{isCopyable && (
|
|
84
|
+
<ButtonUnstyled
|
|
85
|
+
onClick={handleCopyClick}
|
|
86
|
+
className="opacity-0 group-hover:opacity-100"
|
|
87
|
+
>
|
|
88
|
+
<Copy />
|
|
89
|
+
</ButtonUnstyled>
|
|
90
|
+
)}
|
|
91
|
+
{isExternal && (
|
|
92
|
+
<ButtonUnstyled
|
|
93
|
+
onClick={handleOpenClick}
|
|
94
|
+
className="opacity-0 group-hover:opacity-100"
|
|
95
|
+
>
|
|
96
|
+
<ArrowTopRight />
|
|
97
|
+
</ButtonUnstyled>
|
|
98
|
+
)}
|
|
99
|
+
</div>
|
|
100
|
+
);
|
|
101
|
+
}
|