@iota/apps-ui-kit 0.3.0 → 0.5.1

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.
Files changed (67) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/README.md +16 -0
  3. package/dist/index.d.ts +105 -22
  4. package/dist/index.es.js +3194 -3035
  5. package/dist/index.es.js.map +1 -1
  6. package/dist/index.umd.js +44 -44
  7. package/dist/index.umd.js.map +1 -1
  8. package/dist/style.css +1 -1
  9. package/package.json +2 -4
  10. package/src/lib/components/atoms/badge/Badge.tsx +1 -1
  11. package/src/lib/components/atoms/badge/badge.classes.ts +9 -0
  12. package/src/lib/components/atoms/badge/badge.enums.ts +3 -0
  13. package/src/lib/components/atoms/button/button-variants/ButtonPill.tsx +1 -1
  14. package/src/lib/components/atoms/button/button-variants/ButtonUnstyled.tsx +1 -1
  15. package/src/lib/components/atoms/button/button-variants/buttonVariants.types.ts +1 -1
  16. package/src/lib/components/atoms/index.ts +1 -0
  17. package/src/lib/components/atoms/info-box/InfoBox.tsx +3 -2
  18. package/src/lib/components/atoms/key-value-info/KeyValueInfo.tsx +3 -2
  19. package/src/lib/components/atoms/label-text/LabelText.tsx +2 -1
  20. package/src/lib/components/atoms/list-item/ListItem.tsx +2 -2
  21. package/src/lib/components/atoms/snackbar/Snackbar.tsx +1 -1
  22. package/src/lib/components/atoms/toggle/Toggle.tsx +140 -0
  23. package/src/lib/components/atoms/toggle/index.ts +6 -0
  24. package/src/lib/components/atoms/toggle/toggle.classes.ts +41 -0
  25. package/src/lib/components/atoms/toggle/toggle.enums.ts +12 -0
  26. package/src/lib/components/atoms/tooltip/Tooltip.tsx +3 -3
  27. package/src/lib/components/molecules/account/Account.tsx +21 -3
  28. package/src/lib/components/molecules/address/Address.tsx +1 -1
  29. package/src/lib/components/molecules/card/CardAction.tsx +1 -1
  30. package/src/lib/components/molecules/card/CardBody.tsx +14 -3
  31. package/src/lib/components/molecules/card/card.classes.ts +1 -0
  32. package/src/lib/components/molecules/card/card.enums.ts +1 -0
  33. package/src/lib/components/molecules/chip/Chip.tsx +1 -1
  34. package/src/lib/components/molecules/display-stats/DisplayStats.tsx +41 -3
  35. package/src/lib/components/molecules/input/Input.tsx +5 -4
  36. package/src/lib/components/molecules/input/InputWrapper.tsx +1 -1
  37. package/src/lib/components/molecules/input/TextArea.tsx +3 -2
  38. package/src/lib/components/molecules/input/input.types.ts +3 -3
  39. package/src/lib/components/molecules/navbar-item/NavbarItemHorizontal.tsx +2 -2
  40. package/src/lib/components/molecules/navbar-item/NavbarItemVertical.tsx +2 -2
  41. package/src/lib/components/molecules/search/Search.tsx +4 -8
  42. package/src/lib/components/molecules/segmented-button/SegmentedButton.tsx +2 -2
  43. package/src/lib/components/molecules/select/Select.tsx +2 -3
  44. package/src/lib/components/molecules/table-cell/TableCell.tsx +2 -2
  45. package/src/lib/components/molecules/table-header-cell/TableHeaderCell.tsx +1 -1
  46. package/src/lib/components/molecules/title/Title.tsx +11 -2
  47. package/src/lib/components/organisms/accordion/Accordion.tsx +2 -2
  48. package/src/lib/components/organisms/dialog/Dialog.tsx +20 -1
  49. package/src/lib/components/organisms/navbar/Navbar.tsx +2 -1
  50. package/src/lib/components/organisms/navbar/NavbarSlideout.tsx +3 -2
  51. package/src/lib/components/organisms/table/Table.tsx +23 -13
  52. package/src/lib/components/organisms/table/TableContext.tsx +2 -1
  53. package/src/lib/constants/colors.constants.ts +1 -0
  54. package/src/lib/tailwind/base.presets.ts +1 -1
  55. package/src/lib/tailwind/constants/fontSizes.constants.ts +1 -1
  56. package/src/lib/tailwind/helpers.ts +1 -1
  57. package/src/lib/tailwind/types.ts +1 -1
  58. package/src/storybook/blocks/TypeSet.tsx +1 -1
  59. package/src/storybook/stories/atoms/Snackbar.stories.tsx +2 -1
  60. package/src/storybook/stories/atoms/Toggle.stories.tsx +59 -0
  61. package/src/storybook/stories/molecules/Card.stories.tsx +1 -4
  62. package/src/storybook/stories/molecules/Dropdown.stories.tsx +1 -1
  63. package/src/storybook/stories/molecules/Input.stories.tsx +2 -1
  64. package/src/storybook/stories/molecules/Search.stories.tsx +2 -1
  65. package/src/storybook/stories/molecules/SegmentedButton.stories.tsx +2 -1
  66. package/src/storybook/stories/molecules/Select.stories.tsx +2 -1
  67. package/src/storybook/stories/organisms/Navbar.stories.tsx +2 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iota/apps-ui-kit",
3
- "version": "0.3.0",
3
+ "version": "0.5.1",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "github.com:iotaledger/iota.git"
@@ -30,18 +30,16 @@
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.2"
33
+ "@iota/apps-ui-icons": "0.3.0"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@chromatic-com/storybook": "^1.6.0",
37
37
  "@storybook/addon-essentials": "^7.1.0",
38
38
  "@storybook/addon-interactions": "^7.1.0",
39
39
  "@storybook/addon-links": "^7.1.0",
40
- "@storybook/addon-onboarding": "^8.1.11",
41
40
  "@storybook/blocks": "^7.1.0",
42
41
  "@storybook/react": "^7.1.0",
43
42
  "@storybook/react-vite": "^7.1.0",
44
- "@storybook/test": "^8.1.11",
45
43
  "@types/lodash.merge": "^4.6.9",
46
44
  "@types/node": "^20.14.10",
47
45
  "@types/react": "^18.3.3",
@@ -2,7 +2,7 @@
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
4
  import cx from 'classnames';
5
- import { BadgeType } from './badge.enums';
5
+ import type { BadgeType } from './badge.enums';
6
6
  import { BACKGROUND_COLORS, BADGE_TEXT_CLASS, BORDER_COLORS, TEXT_COLORS } from './badge.classes';
7
7
 
8
8
  interface BadgeProps {
@@ -7,18 +7,27 @@ export const BACKGROUND_COLORS = {
7
7
  [BadgeType.PrimarySolid]: 'bg-primary-30',
8
8
  [BadgeType.Neutral]: 'bg-neutral-92 dark:bg-neutral-12',
9
9
  [BadgeType.PrimarySoft]: 'bg-primary-90 dark:bg-primary-10',
10
+ [BadgeType.Success]: 'bg-tertiary-90 dark:bg-tertiary-10',
11
+ [BadgeType.Warning]: 'bg-warning-90 dark:bg-warning-20',
12
+ [BadgeType.Error]: 'bg-error-90 dark:bg-error-10',
10
13
  };
11
14
 
12
15
  export const TEXT_COLORS: Record<BadgeType, string> = {
13
16
  [BadgeType.PrimarySolid]: 'text-primary-100',
14
17
  [BadgeType.Neutral]: 'text-neutral-10 dark:text-neutral-92',
15
18
  [BadgeType.PrimarySoft]: 'text-primary-20 dark:text-primary-90',
19
+ [BadgeType.Success]: 'text-tertiary-20 dark:text-tertiary-90',
20
+ [BadgeType.Warning]: 'text-warning-10 dark:text-warning-90',
21
+ [BadgeType.Error]: 'text-error-20 dark:text-error-90',
16
22
  };
17
23
 
18
24
  export const BORDER_COLORS: Record<BadgeType, string> = {
19
25
  [BadgeType.PrimarySolid]: 'border-primary-30',
20
26
  [BadgeType.Neutral]: 'border-neutral-92 dark:border-neutral-12',
21
27
  [BadgeType.PrimarySoft]: 'border-primary-90 dark:border-primary-10',
28
+ [BadgeType.Success]: 'border-tertiary-90 dark:border-tertiary-10',
29
+ [BadgeType.Warning]: 'border-warning-90 dark:border-warning-20',
30
+ [BadgeType.Error]: 'border-error-90 dark:border-error-10',
22
31
  };
23
32
 
24
33
  export const BADGE_TEXT_CLASS = 'text-label-md';
@@ -5,4 +5,7 @@ export enum BadgeType {
5
5
  Neutral = 'neutral',
6
6
  PrimarySolid = 'primary-solid',
7
7
  PrimarySoft = 'primary-fill',
8
+ Success = 'success',
9
+ Warning = 'warning',
10
+ Error = 'error',
8
11
  }
@@ -2,7 +2,7 @@
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
4
  import { ButtonHtmlType } from '../button.enums';
5
- import { ButtonVariantProps } from './buttonVariants.types';
5
+ import type { ButtonVariantProps } from './buttonVariants.types';
6
6
 
7
7
  export function ButtonPill({
8
8
  htmlType = ButtonHtmlType.Button,
@@ -2,7 +2,7 @@
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
4
  import { ButtonHtmlType } from '../button.enums';
5
- import { ButtonVariantProps } from './buttonVariants.types';
5
+ import type { ButtonVariantProps } from './buttonVariants.types';
6
6
  import cx from 'classnames';
7
7
 
8
8
  export function ButtonUnstyled({
@@ -1,7 +1,7 @@
1
1
  // Copyright (c) 2024 IOTA Stiftung
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
- import { ButtonProps } from '../Button';
4
+ import type { ButtonProps } from '../Button';
5
5
 
6
6
  type ButtonPickedProps = Pick<ButtonProps, 'htmlType' | 'testId'>;
7
7
  type PropsFromButtonElement = Omit<React.HTMLProps<HTMLButtonElement>, 'type'>;
@@ -20,3 +20,4 @@ export * from './visual-asset-card';
20
20
  export * from './loading-indicator';
21
21
  export * from './placeholder';
22
22
  export * from './skeleton';
23
+ export * from './toggle';
@@ -2,7 +2,8 @@
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
4
  import cx from 'classnames';
5
- import { InfoBoxStyle, InfoBoxType } from './infoBox.enums';
5
+ import type { InfoBoxType } from './infoBox.enums';
6
+ import { InfoBoxStyle } from './infoBox.enums';
6
7
  import { BACKGROUND_COLORS, ICON_COLORS } from './infoBox.classes';
7
8
 
8
9
  export interface InfoBoxProps {
@@ -17,7 +18,7 @@ export interface InfoBoxProps {
17
18
  /**
18
19
  * The supporting text of the info box (optional).
19
20
  */
20
- supportingText?: string;
21
+ supportingText?: React.ReactNode;
21
22
  /**
22
23
  * The type of the info box.
23
24
  */
@@ -1,11 +1,12 @@
1
1
  // Copyright (c) 2024 IOTA Stiftung
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
- import { ReactNode } from 'react';
4
+ import type { ReactNode } from 'react';
5
5
  import cx from 'classnames';
6
6
  import { Copy, Info } from '@iota/apps-ui-icons';
7
7
  import { ValueSize } from './keyValue.enums';
8
- import { Tooltip, TooltipPosition } from '../tooltip';
8
+ import type { TooltipPosition } from '../tooltip';
9
+ import { Tooltip } from '../tooltip';
9
10
  import { ButtonUnstyled } from '../button';
10
11
 
11
12
  interface KeyValueProps {
@@ -5,7 +5,8 @@ import cx from 'classnames';
5
5
  import { LabelTextSize } from './labelText.enums';
6
6
  import { LABEL_TEXT_SIZE, SUPPORTING_TEXT_SIZE, TEXT_SIZE } from './labelText.classes';
7
7
  import { Info } from '@iota/apps-ui-icons';
8
- import { TooltipPosition, Tooltip } from '../tooltip';
8
+ import type { TooltipPosition } from '../tooltip';
9
+ import { Tooltip } from '../tooltip';
9
10
 
10
11
  interface LabelTextProps {
11
12
  /**
@@ -1,10 +1,10 @@
1
1
  // Copyright (c) 2024 IOTA Stiftung
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
- import { PropsWithChildren } from 'react';
4
+ import type { PropsWithChildren } from 'react';
5
5
  import cx from 'classnames';
6
6
  import { ArrowRight } from '@iota/apps-ui-icons';
7
- import { Button, ButtonSize, ButtonType } from '@/components';
7
+ import { Button, ButtonSize, ButtonType } from '../button';
8
8
 
9
9
  export interface ListItemProps {
10
10
  /**
@@ -5,7 +5,7 @@ import { useEffect } from 'react';
5
5
  import cx from 'classnames';
6
6
  import { Close } from '@iota/apps-ui-icons';
7
7
  import { SnackbarType } from './snackbar.enums';
8
- import { BACKGROUND_COLOR, TEXT_COLOR } from '@/components/atoms/snackbar/snackbar.classes';
8
+ import { BACKGROUND_COLOR, TEXT_COLOR } from './snackbar.classes';
9
9
 
10
10
  type Renderable = JSX.Element | string | null;
11
11
  export interface SnackbarProps {
@@ -0,0 +1,140 @@
1
+ // Copyright (c) 2025 IOTA Stiftung
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import { forwardRef, useRef } from 'react';
5
+ import { ToggleLabelPosition, ToggleSize } from './toggle.enums';
6
+ import {
7
+ TOGGLE,
8
+ TOGGLE_LABEL,
9
+ TOGGLE_SIZE,
10
+ TOGGLE_STATES,
11
+ TOGGLE_THUMB,
12
+ TOGGLE_THUMB_COLOR,
13
+ TOGGLE_THUMB_POSITION,
14
+ TOGGLE_THUMB_SIZE,
15
+ TOGGLE_CONTAINER,
16
+ } from './toggle.classes';
17
+ import cx from 'classnames';
18
+
19
+ interface ToggleProps {
20
+ /**
21
+ * The label for the toggle.
22
+ */
23
+ label?: string | React.ReactNode;
24
+ /**
25
+ * The state of the toggle (on or off).
26
+ */
27
+ isToggled: boolean;
28
+ /**
29
+ * Whether the label should be placed before the toggle.
30
+ */
31
+ labelPosition?: ToggleLabelPosition;
32
+ /**
33
+ * If true, the toggle will be disabled.
34
+ */
35
+ isDisabled?: boolean;
36
+ /**
37
+ * The callback to call when the toggle state changes.
38
+ */
39
+ onChange?: (isToggled: boolean, event: React.ChangeEvent<HTMLInputElement>) => void;
40
+ /**
41
+ * The name and id of the toggle input.
42
+ */
43
+ name?: string;
44
+ /**
45
+ * The size of the toggle.
46
+ */
47
+ size?: ToggleSize;
48
+ /**
49
+ * The 'data-testid' attribute value (used in e2e tests)
50
+ */
51
+ testId?: string;
52
+ }
53
+
54
+ export const Toggle = forwardRef<HTMLInputElement, ToggleProps>(
55
+ (
56
+ {
57
+ label,
58
+ isToggled,
59
+ labelPosition = ToggleLabelPosition.Right,
60
+ isDisabled = false,
61
+ onChange,
62
+ name,
63
+ size = ToggleSize.Default,
64
+ testId,
65
+ }: ToggleProps,
66
+ ref,
67
+ ) => {
68
+ const inputRef = useRef<HTMLInputElement | null>(null);
69
+
70
+ function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
71
+ const newChecked = e.target.checked;
72
+ onChange?.(newChecked, e);
73
+ }
74
+
75
+ function assignRefs(element: HTMLInputElement) {
76
+ if (ref) {
77
+ if (typeof ref === 'function') {
78
+ ref(element);
79
+ } else {
80
+ ref.current = element;
81
+ }
82
+ }
83
+ inputRef.current = element;
84
+ }
85
+
86
+ const toggleClasses = cx(TOGGLE, {
87
+ [TOGGLE_STATES.active]: isToggled && !isDisabled,
88
+ [TOGGLE_STATES.inactive]: !isToggled,
89
+ [TOGGLE_STATES.disabledActive]: isDisabled && isToggled,
90
+ [TOGGLE_STATES.disabled]: isDisabled,
91
+ [TOGGLE_SIZE[size]]: true,
92
+ });
93
+
94
+ const thumbClasses = cx(TOGGLE_THUMB, {
95
+ [TOGGLE_THUMB_POSITION[size].unchecked]: !isToggled,
96
+ [TOGGLE_THUMB_POSITION[size].checked]: isToggled,
97
+ [TOGGLE_THUMB_COLOR.unchecked]: !isToggled,
98
+ [TOGGLE_THUMB_COLOR.checked]: isToggled,
99
+ [TOGGLE_THUMB_SIZE[size]]: true,
100
+ });
101
+
102
+ const containerClasses = cx(TOGGLE_CONTAINER, {
103
+ 'flex-row-reverse': labelPosition === ToggleLabelPosition.Left,
104
+ 'cursor-not-allowed': isDisabled,
105
+ });
106
+ const labelClasses = cx(TOGGLE_LABEL, {
107
+ 'opacity-40': isDisabled && !isToggled,
108
+ });
109
+
110
+ return (
111
+ <div className={containerClasses}>
112
+ <input
113
+ id={name}
114
+ name={name}
115
+ type="checkbox"
116
+ className="sr-only"
117
+ checked={isToggled}
118
+ ref={assignRefs}
119
+ disabled={isDisabled}
120
+ onChange={handleChange}
121
+ data-testid={testId}
122
+ />
123
+
124
+ <span
125
+ role="switch"
126
+ onClick={() => inputRef.current?.click()}
127
+ className={toggleClasses}
128
+ >
129
+ <span className={thumbClasses} />
130
+ </span>
131
+
132
+ {label && (
133
+ <label htmlFor={name} className={labelClasses}>
134
+ {label}
135
+ </label>
136
+ )}
137
+ </div>
138
+ );
139
+ },
140
+ );
@@ -0,0 +1,6 @@
1
+ // Copyright (c) 2025 IOTA Stiftung
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ export { Toggle } from './Toggle';
5
+
6
+ export * from './toggle.enums';
@@ -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,12 @@
1
+ // Copyright (c) 2025 IOTA Stiftung
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ export enum ToggleSize {
5
+ Small = 'small',
6
+ Default = 'default',
7
+ }
8
+
9
+ export enum ToggleLabelPosition {
10
+ Left = 'left',
11
+ Right = 'right',
12
+ }
@@ -1,7 +1,7 @@
1
1
  // Copyright (c) 2024 IOTA Stiftung
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
- import { PropsWithChildren } from 'react';
4
+ import type { PropsWithChildren } from 'react';
5
5
  import cx from 'classnames';
6
6
  import { TooltipPosition } from './tooltip.enums';
7
7
  import { TOOLTIP_POSITION } from './tooltip.classes';
@@ -20,11 +20,11 @@ export function Tooltip({
20
20
  }: PropsWithChildren<TooltipProps>): React.JSX.Element {
21
21
  const tooltipPositionClass = TOOLTIP_POSITION[position];
22
22
  return (
23
- <div className="group relative inline-block">
23
+ <div className="group/tooltip relative inline-block">
24
24
  {children}
25
25
  <div
26
26
  className={cx(
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',
27
+ 'absolute z-[999] hidden w-max rounded bg-neutral-80 p-xs text-neutral-10 opacity-0 transition-opacity duration-300 group-hover/tooltip:block group-hover/tooltip:opacity-100 group-focus/tooltip:opacity-100 dark:bg-neutral-30 dark:text-neutral-92',
28
28
  tooltipPositionClass,
29
29
  maxWidth,
30
30
  )}
@@ -2,8 +2,8 @@
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
4
  import cx from 'classnames';
5
- import { ButtonUnstyled } from '../../atoms/button';
6
- import { Badge, BadgeType } from '../../atoms';
5
+ import type { BadgeType } from '@/components/atoms';
6
+ import { Badge, Tooltip, TooltipPosition, ButtonUnstyled } from '@/components/atoms';
7
7
  import {
8
8
  LockLocked,
9
9
  LockUnlocked,
@@ -82,6 +82,14 @@ interface AccountProps {
82
82
  * The text of the badge.
83
83
  */
84
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;
85
93
  }
86
94
 
87
95
  export function Account({
@@ -102,6 +110,8 @@ export function Account({
102
110
  isSelected,
103
111
  isActive,
104
112
  showSelected,
113
+ badgeTooltipText,
114
+ badgeTooltipPosition = TooltipPosition.Bottom,
105
115
  }: AccountProps): React.JSX.Element {
106
116
  const Avatar = avatarContent;
107
117
 
@@ -122,7 +132,15 @@ export function Account({
122
132
  <span className="font-inter text-title-md text-neutral-10 dark:text-neutral-92">
123
133
  {title}
124
134
  </span>
125
- {badgeType && badgeText && <Badge type={badgeType} label={badgeText} />}
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}
126
144
  </div>
127
145
  <Address
128
146
  text={subtitle}
@@ -3,7 +3,7 @@
3
3
 
4
4
  import cx from 'classnames';
5
5
  import { Copy, ArrowTopRight } from '@iota/apps-ui-icons';
6
- import { ButtonUnstyled } from '../../atoms/button';
6
+ import { ButtonUnstyled } from '@/components/atoms/button';
7
7
 
8
8
  interface AddressProps {
9
9
  /**
@@ -3,7 +3,7 @@
3
3
 
4
4
  import { ArrowRight } from '@iota/apps-ui-icons';
5
5
  import { CardActionType } from './card.enums';
6
- import { Button, ButtonSize, ButtonType } from '../../atoms/button';
6
+ import { Button, ButtonSize, ButtonType } from '@/components/atoms/button';
7
7
 
8
8
  export type CardActionProps = {
9
9
  title?: string;
@@ -1,8 +1,9 @@
1
1
  // Copyright (c) 2024 IOTA Stiftung
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
  import cx from 'classnames';
4
-
5
- import { ReactNode } from 'react';
4
+ import type { ReactNode } from 'react';
5
+ import { Tooltip } from '@/components/atoms/tooltip';
6
+ import type { TooltipPosition } from '@/components/atoms/tooltip';
6
7
 
7
8
  export type CardBodyProps = {
8
9
  title: string;
@@ -10,6 +11,8 @@ export type CardBodyProps = {
10
11
  clickableAction?: React.ReactNode;
11
12
  icon?: React.ReactNode;
12
13
  isTextTruncated?: boolean;
14
+ tooltipText?: string;
15
+ tooltipPosition?: TooltipPosition;
13
16
  };
14
17
 
15
18
  export function CardBody({
@@ -18,6 +21,8 @@ export function CardBody({
18
21
  clickableAction,
19
22
  icon,
20
23
  isTextTruncated,
24
+ tooltipText,
25
+ tooltipPosition,
21
26
  }: CardBodyProps) {
22
27
  const handleActionCardBodyClick = (event: React.MouseEvent) => {
23
28
  event?.stopPropagation();
@@ -40,7 +45,13 @@ export function CardBody({
40
45
  >
41
46
  {title}
42
47
  </div>
43
- {icon && <div className="flex items-center">{icon}</div>}
48
+ {tooltipText ? (
49
+ <Tooltip text={tooltipText} position={tooltipPosition}>
50
+ {icon}
51
+ </Tooltip>
52
+ ) : (
53
+ <div className="flex items-center">{icon}</div>
54
+ )}
44
55
  {clickableAction && (
45
56
  <div onClick={handleActionCardBodyClick} className="flex items-center">
46
57
  {clickableAction}
@@ -15,6 +15,7 @@ export const IMAGE_VARIANT_CLASSES: { [key in ImageShape]: string } = {
15
15
  export const IMAGE_BG_CLASSES: { [key in ImageType]: string } = {
16
16
  [ImageType.Placeholder]: '',
17
17
  [ImageType.BgSolid]: 'bg-neutral-96 dark:bg-neutral-10',
18
+ [ImageType.BgWhite]: 'bg-white dark:bg-neutral-6',
18
19
  [ImageType.BgTransparent]: '',
19
20
  };
20
21
 
@@ -10,6 +10,7 @@ export enum CardType {
10
10
  export enum ImageType {
11
11
  Placeholder = 'placeholder',
12
12
  BgSolid = 'bg-solid',
13
+ BgWhite = 'bg-white',
13
14
  BgTransparent = 'bg-transparent',
14
15
  }
15
16
 
@@ -11,7 +11,7 @@ import {
11
11
  TEXT_COLOR,
12
12
  FOCUS_CLASSES,
13
13
  } from './chip.classes';
14
- import { ButtonUnstyled } from '../../atoms/button';
14
+ import { ButtonUnstyled } from '@/components/atoms/button';
15
15
  import { Close } from '@iota/apps-ui-icons';
16
16
 
17
17
  interface ChipProps {
@@ -1,9 +1,10 @@
1
1
  // Copyright (c) 2024 IOTA Stiftung
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
- import { ReactNode } from 'react';
5
- import { Tooltip, TooltipPosition } from '../../atoms';
6
- import { Info } from '@iota/apps-ui-icons';
4
+ import type { ReactNode } from 'react';
5
+ import type { TooltipPosition } from '@/components/atoms';
6
+ import { ButtonUnstyled, Tooltip } from '@/components/atoms';
7
+ import { Copy, Info } from '@iota/apps-ui-icons';
7
8
  import { DisplayStatsType, DisplayStatsSize } from './displayStats.enums';
8
9
  import cx from 'classnames';
9
10
  import {
@@ -48,6 +49,18 @@ interface DisplayStatsProps {
48
49
  * Add icon to the right of the label.
49
50
  */
50
51
  icon?: React.ReactNode;
52
+ /**
53
+ * Text that need to be copied (optional).
54
+ */
55
+ copyText?: string;
56
+ /**
57
+ * The onCopySuccess event of the KeyValue (optional).
58
+ */
59
+ onCopySuccess?: (e: React.MouseEvent<HTMLButtonElement>, text: string) => void;
60
+ /**
61
+ * The onCopyError event of the KeyValue (optional).
62
+ */
63
+ onCopyError?: (e: unknown, text: string) => void;
51
64
  }
52
65
 
53
66
  export function DisplayStats({
@@ -59,6 +72,9 @@ export function DisplayStats({
59
72
  type = DisplayStatsType.Default,
60
73
  size = DisplayStatsSize.Default,
61
74
  icon,
75
+ copyText,
76
+ onCopySuccess,
77
+ onCopyError,
62
78
  }: DisplayStatsProps): React.JSX.Element {
63
79
  const backgroundClass = BACKGROUND_CLASSES[type];
64
80
  const sizeClass = SIZE_CLASSES[size];
@@ -67,6 +83,21 @@ export function DisplayStats({
67
83
  const labelClass = LABEL_TEXT_CLASSES[size];
68
84
  const supportingLabelTextClass = SUPPORTING_LABEL_TEXT_CLASSES[size];
69
85
 
86
+ async function handleCopyClick(event: React.MouseEvent<HTMLButtonElement>) {
87
+ if (!navigator.clipboard) {
88
+ return;
89
+ }
90
+
91
+ if (copyText) {
92
+ try {
93
+ await navigator.clipboard.writeText(copyText);
94
+ onCopySuccess?.(event, copyText);
95
+ } catch (error) {
96
+ console.error('Failed to copy:', error);
97
+ onCopyError?.(error, copyText);
98
+ }
99
+ }
100
+ }
70
101
  return (
71
102
  <div
72
103
  className={cx(
@@ -98,6 +129,13 @@ export function DisplayStats({
98
129
  {supportingLabel}
99
130
  </span>
100
131
  )}
132
+ {copyText && (
133
+ <div className="self-center">
134
+ <ButtonUnstyled onClick={handleCopyClick}>
135
+ <Copy className="text-neutral-60 dark:text-neutral-40" />
136
+ </ButtonUnstyled>
137
+ </div>
138
+ )}
101
139
  </div>
102
140
  </div>
103
141
  );
@@ -3,7 +3,8 @@
3
3
 
4
4
  import { forwardRef, Fragment, useEffect, useRef, useState } from 'react';
5
5
  import cx from 'classnames';
6
- import { InputWrapper, InputWrapperProps } from './InputWrapper';
6
+ import type { InputWrapperProps } from './InputWrapper';
7
+ import { InputWrapper } from './InputWrapper';
7
8
  import {
8
9
  BORDER_CLASSES,
9
10
  INPUT_CLASSES,
@@ -12,10 +13,10 @@ import {
12
13
  INPUT_PLACEHOLDER_CLASSES,
13
14
  } from './input.classes';
14
15
  import { InputType } from './input.enums';
15
- import { SecondaryText } from '../../atoms/secondary-text';
16
+ import { SecondaryText } from '@/components/atoms/secondary-text';
16
17
  import { Close, VisibilityOff, VisibilityOn } from '@iota/apps-ui-icons';
17
- import { ButtonUnstyled } from '../../atoms/button';
18
- import { InputPropsByType, NumericFormatInputProps } from './input.types';
18
+ import { ButtonUnstyled } from '@/components/atoms/button';
19
+ import type { InputPropsByType, NumericFormatInputProps } from './input.types';
19
20
  import { NumericFormat } from 'react-number-format';
20
21
 
21
22
  export interface BaseInputProps extends InputWrapperProps {