@iota/apps-ui-kit 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/index.d.ts +47 -19
  3. package/dist/index.es.js +3609 -3516
  4. package/dist/index.es.js.map +1 -1
  5. package/dist/index.umd.js +43 -43
  6. package/dist/index.umd.js.map +1 -1
  7. package/dist/style.css +1 -1
  8. package/package.json +3 -4
  9. package/src/lib/components/atoms/badge/Badge.tsx +1 -1
  10. package/src/lib/components/atoms/button/button-variants/ButtonPill.tsx +1 -1
  11. package/src/lib/components/atoms/button/button-variants/ButtonUnstyled.tsx +1 -1
  12. package/src/lib/components/atoms/button/button-variants/buttonVariants.types.ts +1 -1
  13. package/src/lib/components/atoms/button-segment/ButtonSegment.tsx +15 -3
  14. package/src/lib/components/atoms/info-box/InfoBox.tsx +2 -1
  15. package/src/lib/components/atoms/key-value-info/KeyValueInfo.tsx +3 -2
  16. package/src/lib/components/atoms/label-text/LabelText.tsx +2 -1
  17. package/src/lib/components/atoms/list-item/ListItem.tsx +2 -2
  18. package/src/lib/components/atoms/snackbar/Snackbar.tsx +1 -1
  19. package/src/lib/components/atoms/tooltip/Tooltip.tsx +115 -14
  20. package/src/lib/components/molecules/account/Account.tsx +21 -3
  21. package/src/lib/components/molecules/address/Address.tsx +1 -1
  22. package/src/lib/components/molecules/card/CardAction.tsx +1 -1
  23. package/src/lib/components/molecules/card/CardBody.tsx +3 -3
  24. package/src/lib/components/molecules/chip/Chip.tsx +1 -1
  25. package/src/lib/components/molecules/display-stats/DisplayStats.tsx +41 -3
  26. package/src/lib/components/molecules/input/Input.tsx +5 -4
  27. package/src/lib/components/molecules/input/InputWrapper.tsx +1 -1
  28. package/src/lib/components/molecules/input/TextArea.tsx +3 -2
  29. package/src/lib/components/molecules/input/input.types.ts +3 -3
  30. package/src/lib/components/molecules/navbar-item/NavbarItemHorizontal.tsx +2 -2
  31. package/src/lib/components/molecules/navbar-item/NavbarItemVertical.tsx +2 -2
  32. package/src/lib/components/molecules/search/Search.tsx +2 -9
  33. package/src/lib/components/molecules/segmented-button/SegmentedButton.tsx +7 -3
  34. package/src/lib/components/molecules/select/Select.tsx +2 -3
  35. package/src/lib/components/molecules/table-cell/TableCell.tsx +2 -2
  36. package/src/lib/components/molecules/table-header-cell/TableHeaderCell.tsx +1 -1
  37. package/src/lib/components/molecules/title/Title.tsx +2 -1
  38. package/src/lib/components/organisms/accordion/Accordion.tsx +2 -2
  39. package/src/lib/components/organisms/dialog/Dialog.tsx +20 -1
  40. package/src/lib/components/organisms/navbar/Navbar.tsx +2 -1
  41. package/src/lib/components/organisms/navbar/NavbarSlideout.tsx +3 -2
  42. package/src/lib/components/organisms/table/Table.tsx +6 -12
  43. package/src/lib/components/organisms/table/TableContext.tsx +2 -1
  44. package/src/lib/tailwind/base.presets.ts +1 -1
  45. package/src/lib/tailwind/constants/fontSizes.constants.ts +1 -1
  46. package/src/lib/tailwind/helpers.ts +1 -1
  47. package/src/lib/tailwind/responsive.presets.ts +1 -1
  48. package/src/lib/tailwind/types.ts +1 -1
  49. package/src/storybook/blocks/TypeSet.tsx +1 -1
  50. package/src/storybook/stories/atoms/Snackbar.stories.tsx +2 -1
  51. package/src/storybook/stories/molecules/Card.stories.tsx +1 -4
  52. package/src/storybook/stories/molecules/Dropdown.stories.tsx +1 -1
  53. package/src/storybook/stories/molecules/Input.stories.tsx +2 -1
  54. package/src/storybook/stories/molecules/Search.stories.tsx +2 -1
  55. package/src/storybook/stories/molecules/SegmentedButton.stories.tsx +2 -1
  56. package/src/storybook/stories/molecules/Select.stories.tsx +2 -1
  57. 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.5.0",
3
+ "version": "0.6.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "github.com:iotaledger/iota.git"
@@ -30,21 +30,20 @@
30
30
  "react": "^18.3.1",
31
31
  "react-dom": "^18.3.1",
32
32
  "react-number-format": "^5.2.2",
33
- "@iota/apps-ui-icons": "0.3.0"
33
+ "@iota/apps-ui-icons": "0.3.1"
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",
46
+ "@types/react-dom": "^18.3.0",
48
47
  "@vitejs/plugin-react-swc": "^3.3.2",
49
48
  "autoprefixer": "^10.4.19",
50
49
  "cross-env": "^7.0.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 {
@@ -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'>;
@@ -38,6 +38,10 @@ interface ButtonSegmentProps {
38
38
  * The type of the button.
39
39
  */
40
40
  type?: ButtonSegmentType;
41
+ /**
42
+ * If the button is nested inside a parent button.
43
+ */
44
+ isNested?: boolean;
41
45
  }
42
46
 
43
47
  export function ButtonSegment({
@@ -47,6 +51,7 @@ export function ButtonSegment({
47
51
  disabled,
48
52
  onClick,
49
53
  type = ButtonSegmentType.Rounded,
54
+ isNested = false,
50
55
  }: ButtonSegmentProps): React.JSX.Element {
51
56
  const isUnderlined = type === ButtonSegmentType.Underlined;
52
57
 
@@ -58,9 +63,16 @@ export function ButtonSegment({
58
63
 
59
64
  const underlined = isUnderlined ? (selected ? UNDERLINED_SELECTED : UNDERLINED) : '';
60
65
  const textColors = selected ? TEXT_COLORS_SELECTED : TEXT_COLORS;
61
- const padding = isUnderlined ? 'px-lg py-md' : 'px-sm py-[6px]';
66
+ const padding = isUnderlined ? (isNested ? 'px-md py-sm' : 'px-lg py-md') : 'px-sm py-[6px]';
62
67
  const borderRadius = isUnderlined ? '' : 'rounded-full';
63
- const textSize = isUnderlined ? 'text-title-md' : 'text-label-lg';
68
+ const textSize = isNested
69
+ ? isUnderlined
70
+ ? 'text-title-sm'
71
+ : 'text-label-md'
72
+ : isUnderlined
73
+ ? 'text-title-md'
74
+ : 'text-label-lg';
75
+
64
76
  return (
65
77
  <ButtonUnstyled
66
78
  onClick={onClick}
@@ -79,7 +91,7 @@ export function ButtonSegment({
79
91
  >
80
92
  <div className={cx('flex flex-row items-center justify-center gap-2', textSize)}>
81
93
  {icon && <span>{icon}</span>}
82
- <span className="font-inter">{label}</span>
94
+ <span className="shrink-0 font-inter">{label}</span>
83
95
  </div>
84
96
  </ButtonUnstyled>
85
97
  );
@@ -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 {
@@ -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 {
@@ -1,37 +1,138 @@
1
1
  // Copyright (c) 2024 IOTA Stiftung
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
- import { PropsWithChildren } from 'react';
4
+ import { useRef, useState, useLayoutEffect } from 'react';
5
+ import type { PropsWithChildren } from 'react';
6
+ import { createPortal } from 'react-dom';
5
7
  import cx from 'classnames';
6
8
  import { TooltipPosition } from './tooltip.enums';
7
- import { TOOLTIP_POSITION } from './tooltip.classes';
8
9
 
9
10
  interface TooltipProps {
10
11
  text: string;
11
12
  position?: TooltipPosition;
12
13
  maxWidth?: string;
14
+ offset?: number;
15
+ openDelay?: number;
16
+ closeDelay?: number;
13
17
  }
14
18
 
15
19
  export function Tooltip({
16
20
  text,
17
21
  position = TooltipPosition.Top,
18
22
  maxWidth = 'max-w-[200px]',
23
+ offset = 8,
24
+ openDelay = 0,
25
+ closeDelay = 100,
19
26
  children,
20
- }: PropsWithChildren<TooltipProps>): React.JSX.Element {
21
- const tooltipPositionClass = TOOLTIP_POSITION[position];
27
+ }: PropsWithChildren<TooltipProps>) {
28
+ const triggerRef = useRef<HTMLDivElement>(null);
29
+ const tooltipRef = useRef<HTMLDivElement>(null);
30
+
31
+ const [visible, setVisible] = useState(false);
32
+ const [coords, setCoords] = useState<{ top: number; left: number }>({ top: 0, left: 0 });
33
+
34
+ const openTimer = useRef<ReturnType<typeof setTimeout>>();
35
+ const closeTimer = useRef<ReturnType<typeof setTimeout>>();
36
+
37
+ const clearTimers = () => {
38
+ clearTimeout(openTimer.current);
39
+ clearTimeout(closeTimer.current);
40
+ };
41
+
42
+ const open = () => {
43
+ clearTimers();
44
+ openTimer.current = setTimeout(() => setVisible(true), openDelay);
45
+ };
46
+
47
+ const close = () => {
48
+ clearTimers();
49
+ closeTimer.current = setTimeout(() => setVisible(false), closeDelay);
50
+ };
51
+
52
+ useLayoutEffect(() => {
53
+ if (!visible) return;
54
+
55
+ const rect = triggerRef.current?.getBoundingClientRect();
56
+ if (!rect) return;
57
+
58
+ const pos = {
59
+ [TooltipPosition.Top]: {
60
+ top: rect.top - offset,
61
+ left: rect.left + rect.width / 2,
62
+ transform: 'translate(-50%, -100%)',
63
+ },
64
+ [TooltipPosition.Bottom]: {
65
+ top: rect.bottom + offset,
66
+ left: rect.left + rect.width / 2,
67
+ transform: 'translate(-50%, 0)',
68
+ },
69
+ [TooltipPosition.Left]: {
70
+ top: rect.top + rect.height / 2,
71
+ left: rect.left - offset,
72
+ transform: 'translate(-100%, -50%)',
73
+ },
74
+ [TooltipPosition.Right]: {
75
+ top: rect.top + rect.height / 2,
76
+ left: rect.right + offset,
77
+ transform: 'translate(0, -50%)',
78
+ },
79
+ }[position];
80
+
81
+ setCoords({ top: pos.top, left: pos.left });
82
+ tooltipRef.current!.style.transform = pos.transform;
83
+ }, [visible, position, offset]);
84
+
85
+ useLayoutEffect(() => {
86
+ if (!visible) return;
87
+ const update = () => {
88
+ const rect = triggerRef.current?.getBoundingClientRect();
89
+ if (!rect) return;
90
+ setCoords((prev) => ({ ...prev, left: rect.left + rect.width / 2 }));
91
+ };
92
+ window.addEventListener('scroll', update, true);
93
+ window.addEventListener('resize', update);
94
+ return () => {
95
+ window.removeEventListener('scroll', update, true);
96
+ window.removeEventListener('resize', update);
97
+ };
98
+ }, [visible]);
99
+
100
+ const base =
101
+ 'z-[999] w-max rounded bg-neutral-80 p-xs text-neutral-10 dark:bg-neutral-30 dark:text-neutral-92';
102
+
22
103
  return (
23
- <div className="group relative inline-block">
24
- {children}
104
+ <>
25
105
  <div
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',
28
- tooltipPositionClass,
29
- maxWidth,
30
- )}
31
- role="tooltip"
106
+ ref={triggerRef}
107
+ className="inline-block cursor-pointer"
108
+ onMouseEnter={open}
109
+ onFocus={open}
110
+ onMouseLeave={close}
111
+ onBlur={close}
32
112
  >
33
- <p className="w-full break-words">{text}</p>
113
+ {children}
34
114
  </div>
35
- </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
+ </>
36
137
  );
37
138
  }
@@ -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,9 +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';
6
- import { Tooltip, TooltipPosition } from '../../atoms';
4
+ import type { ReactNode } from 'react';
5
+ import { Tooltip } from '@/components/atoms/tooltip';
6
+ import type { TooltipPosition } from '@/components/atoms/tooltip';
7
7
 
8
8
  export type CardBodyProps = {
9
9
  title: string;
@@ -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 {
@@ -2,7 +2,7 @@
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
4
  import cx from 'classnames';
5
- import { SecondaryText } from '../../atoms/secondary-text';
5
+ import { SecondaryText } from '@/components/atoms/secondary-text';
6
6
  import { LABEL_CLASSES } from './input.classes';
7
7
  import { createElement } from 'react';
8
8
 
@@ -2,7 +2,8 @@
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
4
  import { forwardRef, useEffect, useState } from 'react';
5
- import { InputWrapper, InputWrapperProps } from './InputWrapper';
5
+ import type { InputWrapperProps } from './InputWrapper';
6
+ import { InputWrapper } from './InputWrapper';
6
7
  import {
7
8
  BORDER_CLASSES,
8
9
  INPUT_CLASSES,
@@ -10,7 +11,7 @@ import {
10
11
  INPUT_PLACEHOLDER_CLASSES,
11
12
  } from './input.classes';
12
13
  import cx from 'classnames';
13
- import { ButtonUnstyled } from '../../atoms/button';
14
+ import { ButtonUnstyled } from '@/components/atoms/button';
14
15
  import { VisibilityOff, VisibilityOn } from '@iota/apps-ui-icons';
15
16
 
16
17
  type TextAreaProps = Omit<
@@ -1,9 +1,9 @@
1
1
  // Copyright (c) 2024 IOTA Stiftung
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
- import { ComponentProps } from 'react';
5
- import { InputType } from '.';
6
- import { NumericFormat } from 'react-number-format';
4
+ import type { ComponentProps } from 'react';
5
+ import type { InputType } from '.';
6
+ import type { NumericFormat } from 'react-number-format';
7
7
 
8
8
  type InputElementProps = Omit<
9
9
  React.ComponentProps<'input'>,
@@ -13,8 +13,8 @@ import {
13
13
  UNSELECTED_ICON,
14
14
  UNSELECTED_TEXT,
15
15
  } from './navbarItem.classes';
16
- import { Badge, BadgeType } from '../../atoms';
17
- import { NavbarItemProps } from './NavbarItem';
16
+ import { Badge, BadgeType } from '@/components/atoms';
17
+ import type { NavbarItemProps } from './NavbarItem';
18
18
 
19
19
  export function NavbarItemHorizontal({
20
20
  icon,
@@ -9,8 +9,8 @@ import {
9
9
  UNSELECTED_ICON,
10
10
  UNSELECTED_TEXT,
11
11
  } from './navbarItem.classes';
12
- import { Badge, BadgeType } from '../../atoms';
13
- import { NavbarItemProps } from './NavbarItem';
12
+ import { Badge, BadgeType } from '@/components/atoms';
13
+ import type { NavbarItemProps } from './NavbarItem';
14
14
 
15
15
  export function NavbarItemVertical({
16
16
  icon,
@@ -4,7 +4,8 @@
4
4
  import { useEffect, useRef, useState } from 'react';
5
5
  import cx from 'classnames';
6
6
  import { Loader, Search as SearchIcon } from '@iota/apps-ui-icons';
7
- import { Divider, SearchBarType } from '@/components';
7
+ import { Divider } from '@/components/atoms';
8
+ import { SearchBarType } from './search.enums';
8
9
  import {
9
10
  BACKGROUND_COLORS,
10
11
  SUGGESTIONS_WRAPPER_STYLE,
@@ -43,10 +44,6 @@ export interface SearchProps {
43
44
  * Are the suggestions loading.
44
45
  */
45
46
  isLoading: boolean;
46
- /**
47
- * Callback when a key is pressed.
48
- */
49
- onKeyDown?: (event: React.KeyboardEvent<HTMLInputElement>) => void;
50
47
  /**
51
48
  * The type of the search bar. Can be 'outlined' or 'filled'.
52
49
  */
@@ -64,7 +61,6 @@ export function Search({
64
61
  onSuggestionClick,
65
62
  placeholder,
66
63
  isLoading = false,
67
- onKeyDown,
68
64
  type = SearchBarType.Outlined,
69
65
  renderSuggestion,
70
66
  }: SearchProps): React.JSX.Element {
@@ -157,9 +153,6 @@ export function Search({
157
153
  }
158
154
  }
159
155
  }
160
- if (onKeyDown) {
161
- onKeyDown(event);
162
- }
163
156
  };
164
157
 
165
158
  return (