@iota/apps-ui-kit 0.6.0 → 0.7.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 (121) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/dist/index.d.ts +219 -34
  3. package/dist/index.es.js +3429 -6082
  4. package/dist/index.es.js.map +1 -1
  5. package/dist/index.umd.js +28 -160
  6. package/dist/index.umd.js.map +1 -1
  7. package/dist/style.css +1 -1
  8. package/package.json +11 -4
  9. package/src/lib/components/atoms/badge/badge.classes.ts +18 -18
  10. package/src/lib/components/atoms/badge/badge.enums.ts +1 -1
  11. package/src/lib/components/atoms/button/Button.tsx +1 -0
  12. package/src/lib/components/atoms/button/button-variants/ButtonPill.tsx +1 -1
  13. package/src/lib/components/atoms/button/button.classes.ts +12 -12
  14. package/src/lib/components/atoms/button-segment/ButtonSegment.tsx +1 -1
  15. package/src/lib/components/atoms/button-segment/buttonSegment.classes.ts +15 -36
  16. package/src/lib/components/atoms/checkbox/Checkbox.tsx +19 -9
  17. package/src/lib/components/atoms/divider/divider.classes.ts +1 -1
  18. package/src/lib/components/atoms/header/Header.tsx +1 -1
  19. package/src/lib/components/atoms/image/Image.tsx +32 -0
  20. package/src/lib/components/atoms/image/index.ts +4 -0
  21. package/src/lib/components/atoms/index.ts +4 -1
  22. package/src/lib/components/atoms/info-box/InfoBox.tsx +2 -8
  23. package/src/lib/components/atoms/info-box/infoBox.classes.ts +8 -9
  24. package/src/lib/components/atoms/key-value-info/KeyValueInfo.tsx +5 -5
  25. package/src/lib/components/atoms/label-text/LabelText.tsx +4 -6
  26. package/src/lib/components/atoms/list-item/ListItem.tsx +4 -5
  27. package/src/lib/components/atoms/loading-indicator/LoadingIndicator.tsx +1 -1
  28. package/src/lib/components/atoms/media-fallback/MediaFallback.tsx +12 -0
  29. package/src/lib/components/atoms/media-fallback/index.ts +4 -0
  30. package/src/lib/components/atoms/panel/Panel.tsx +2 -4
  31. package/src/lib/components/atoms/radio-button/RadioButton.tsx +5 -9
  32. package/src/lib/components/atoms/secondary-text/SecondaryText.tsx +3 -2
  33. package/src/lib/components/atoms/skeleton/Skeleton.tsx +6 -23
  34. package/src/lib/components/atoms/snackbar/snackbar.classes.ts +8 -8
  35. package/src/lib/components/atoms/toggle/toggle.classes.ts +6 -6
  36. package/src/lib/components/atoms/tooltip/Tooltip.tsx +3 -2
  37. package/src/lib/components/atoms/video/Video.tsx +33 -0
  38. package/src/lib/components/atoms/video/index.ts +4 -0
  39. package/src/lib/components/atoms/video/video.constants.ts +31 -0
  40. package/src/lib/components/index.ts +0 -2
  41. package/src/lib/components/molecules/account/Account.tsx +4 -4
  42. package/src/lib/components/molecules/address/Address.tsx +1 -1
  43. package/src/lib/components/molecules/asset-media-renderer/AssetMediaRenderer.tsx +50 -0
  44. package/src/lib/components/molecules/asset-media-renderer/index.ts +4 -0
  45. package/src/lib/components/molecules/card/CardAction.tsx +3 -5
  46. package/src/lib/components/molecules/card/CardBody.tsx +3 -2
  47. package/src/lib/components/molecules/card/card.classes.ts +3 -3
  48. package/src/lib/components/molecules/chip/Chip.tsx +59 -18
  49. package/src/lib/components/molecules/chip/chip.classes.ts +37 -15
  50. package/src/lib/components/molecules/chip/chip.enums.ts +10 -2
  51. package/src/lib/components/molecules/chip/index.ts +1 -0
  52. package/src/lib/components/molecules/display-stats/DisplayStats.tsx +2 -2
  53. package/src/lib/components/molecules/display-stats/displayStats.classes.ts +6 -6
  54. package/src/lib/components/molecules/dropdown/Dropdown.tsx +1 -1
  55. package/src/lib/components/molecules/index.ts +2 -0
  56. package/src/lib/components/molecules/input/Input.tsx +37 -22
  57. package/src/lib/components/molecules/input/InputWrapper.tsx +1 -1
  58. package/src/lib/components/molecules/input/TextArea.tsx +4 -8
  59. package/src/lib/components/molecules/input/input.classes.ts +7 -8
  60. package/src/lib/components/molecules/navbar-item/NavbarItem.tsx +0 -1
  61. package/src/lib/components/molecules/navbar-item/NavbarItemHorizontal.tsx +1 -1
  62. package/src/lib/components/molecules/navbar-item/NavbarItemVertical.tsx +1 -1
  63. package/src/lib/components/molecules/navbar-item/navbarItem.classes.ts +4 -4
  64. package/src/lib/components/molecules/search/Search.tsx +10 -7
  65. package/src/lib/components/molecules/search/search.classes.ts +5 -5
  66. package/src/lib/components/molecules/segmented-button/segmentedButton.classes.ts +2 -2
  67. package/src/lib/components/molecules/select/Select.tsx +29 -16
  68. package/src/lib/components/molecules/table-cell/TableCell.tsx +2 -2
  69. package/src/lib/components/molecules/table-header-cell/TableHeaderCell.tsx +2 -2
  70. package/src/lib/components/molecules/title/Title.tsx +3 -3
  71. package/src/lib/components/{atoms → molecules}/visual-asset-card/VisualAssetCard.tsx +26 -40
  72. package/src/lib/components/organisms/accordion/Accordion.tsx +2 -3
  73. package/src/lib/components/organisms/accordion/accordion.classes.ts +1 -1
  74. package/src/lib/components/organisms/dialog/Dialog.tsx +39 -24
  75. package/src/lib/components/organisms/navbar/Navbar.tsx +2 -2
  76. package/src/lib/components/organisms/navbar/NavbarSlideout.tsx +1 -1
  77. package/src/lib/components/organisms/table/Table.tsx +2 -2
  78. package/src/lib/constants/colors.constants.ts +114 -8
  79. package/src/lib/enums/index.ts +1 -0
  80. package/src/lib/enums/theme.enums.ts +8 -0
  81. package/src/lib/index.ts +2 -0
  82. package/src/lib/styles/base.css +137 -0
  83. package/src/lib/styles/components.css +735 -0
  84. package/src/lib/styles/fonts.css +2 -0
  85. package/src/lib/styles/index.css +10 -141
  86. package/src/lib/styles/utilities.css +61 -0
  87. package/src/lib/styles/variables/dark-variables.css +262 -0
  88. package/src/lib/styles/variables/general-variables.css +9 -0
  89. package/src/lib/styles/variables/light-variables.css +276 -0
  90. package/src/lib/styles/variables/names-variables.css +270 -0
  91. package/src/lib/tailwind/base.presets.ts +17 -8
  92. package/src/lib/tailwind/constants/customColors.constants.ts +13 -0
  93. package/src/lib/tailwind/constants/fontFamily.constants.ts +8 -0
  94. package/src/lib/tailwind/constants/fontSizes.constants.ts +9 -9
  95. package/src/lib/tailwind/constants/gradients.constants.ts +6 -0
  96. package/src/lib/tailwind/constants/index.ts +1 -0
  97. package/src/lib/tailwind/plugins.ts +4 -0
  98. package/src/storybook/blocks/Showcase.tsx +2 -2
  99. package/src/storybook/blocks/TypeSet.tsx +3 -3
  100. package/src/storybook/blocks/TypeSetItem.tsx +3 -3
  101. package/src/storybook/constants/gradients.constants.ts +8 -0
  102. package/src/storybook/helpers/formatGradientNames.ts +15 -0
  103. package/src/storybook/stories/atoms/Skeleton.stories.tsx +6 -6
  104. package/src/storybook/stories/atoms/Tooltip.stories.tsx +4 -2
  105. package/src/storybook/stories/atoms/VisualAssetCard.stories.tsx +3 -2
  106. package/src/storybook/stories/components/DocsSyncTheme.tsx +20 -0
  107. package/src/storybook/stories/components/VariableSpacingShowcase.tsx +2 -2
  108. package/src/storybook/stories/components/index.ts +1 -0
  109. package/src/storybook/stories/design-tokens/colors.stories.mdx +21 -0
  110. package/src/storybook/stories/design-tokens/icons.stories.mdx +2 -0
  111. package/src/storybook/stories/design-tokens/typeset.stories.mdx +2 -0
  112. package/src/storybook/stories/design-tokens/variable-spacing.stories.mdx +2 -1
  113. package/src/storybook/stories/molecules/Account.stories.tsx +3 -1
  114. package/src/storybook/stories/molecules/Chip.stories.tsx +5 -0
  115. package/src/storybook/stories/molecules/Input.stories.tsx +79 -2
  116. package/src/storybook/stories/molecules/Search.stories.tsx +2 -2
  117. package/src/storybook/stories/molecules/TableCell.stories.tsx +1 -1
  118. package/src/storybook/stories/molecules/TextArea.stories.tsx +1 -1
  119. package/src/storybook/stories/organisms/Dialog.stories.tsx +54 -28
  120. /package/src/lib/components/{atoms → molecules}/visual-asset-card/index.ts +0 -0
  121. /package/src/lib/components/{atoms → molecules}/visual-asset-card/visualAssetCard.enums.ts +0 -0
@@ -2,6 +2,7 @@
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
4
  export * from './address';
5
+ export * from './asset-media-renderer';
5
6
  export * from './chip';
6
7
  export * from './segmented-button';
7
8
  export * from './title';
@@ -15,3 +16,4 @@ export * from './table-header-cell';
15
16
  export * from './search';
16
17
  export * from './card';
17
18
  export * from './display-stats';
19
+ export * from './visual-asset-card';
@@ -23,7 +23,7 @@ export interface BaseInputProps extends InputWrapperProps {
23
23
  /**
24
24
  * A leading icon that is shown before the input
25
25
  */
26
- leadingIcon?: React.JSX.Element;
26
+ leadingIcon?: React.ReactNode;
27
27
  /**
28
28
  * Supporting text that is shown at the end of the input component.
29
29
  */
@@ -35,7 +35,7 @@ export interface BaseInputProps extends InputWrapperProps {
35
35
  /**
36
36
  * Trailing element that is shown after the input
37
37
  */
38
- trailingElement?: React.JSX.Element;
38
+ trailingElement?: React.ReactNode;
39
39
  /**
40
40
  * Is the content of the input visible
41
41
  */
@@ -44,6 +44,10 @@ export interface BaseInputProps extends InputWrapperProps {
44
44
  * Value of the input
45
45
  */
46
46
  value?: string | number;
47
+ /**
48
+ * Text that is shown below the value of the input.
49
+ */
50
+ supportingValue?: string | null;
47
51
  /**
48
52
  * Default value of the input
49
53
  */
@@ -72,6 +76,7 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(function InputComp
72
76
  trailingElement,
73
77
  isContentVisible,
74
78
  value,
79
+ supportingValue,
75
80
  defaultValue,
76
81
  onClearInput,
77
82
  isVisibilityToggleEnabled,
@@ -111,28 +116,38 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(function InputComp
111
116
  required={inputProps.required}
112
117
  >
113
118
  <div
114
- className={cx('relative flex flex-row items-center gap-x-3', BORDER_CLASSES)}
119
+ className={cx(
120
+ 'input-container relative flex flex-row items-center gap-x-3',
121
+ BORDER_CLASSES,
122
+ )}
115
123
  onClick={focusOnInput}
116
124
  ref={inputWrapperRef}
117
125
  >
118
- {leadingIcon && (
119
- <span className="text-neutral-10 dark:text-neutral-92">{leadingIcon}</span>
120
- )}
121
- <InputElement
122
- {...inputProps}
123
- inputRef={forwardRef}
124
- value={value}
125
- type={
126
- type === InputType.Password && isInputContentVisible ? InputType.Text : type
127
- }
128
- disabled={disabled}
129
- className={cx(
130
- INPUT_CLASSES,
131
- INPUT_TEXT_CLASSES,
132
- INPUT_PLACEHOLDER_CLASSES,
133
- INPUT_NUMBER_CLASSES,
126
+ {leadingIcon && <span className="input-icon-color">{leadingIcon}</span>}
127
+ <div className="flex flex-1 flex-col items-start">
128
+ <InputElement
129
+ {...inputProps}
130
+ inputRef={forwardRef}
131
+ value={value}
132
+ type={
133
+ type === InputType.Password && isInputContentVisible
134
+ ? InputType.Text
135
+ : type
136
+ }
137
+ disabled={disabled}
138
+ className={cx(
139
+ INPUT_CLASSES,
140
+ INPUT_TEXT_CLASSES,
141
+ INPUT_PLACEHOLDER_CLASSES,
142
+ INPUT_NUMBER_CLASSES,
143
+ )}
144
+ />
145
+ {supportingValue && (
146
+ <span className="text-label-md text-iota-neutral-60 names:text-iota-neutral-60 dark:text-iota-neutral-40">
147
+ {supportingValue}
148
+ </span>
134
149
  )}
135
- />
150
+ </div>
136
151
 
137
152
  {supportingText && <SecondaryText>{supportingText}</SecondaryText>}
138
153
  <InputTrailingElement
@@ -223,7 +238,7 @@ function InputTrailingElement({
223
238
  if (showClearInput) {
224
239
  return (
225
240
  <ButtonUnstyled
226
- className="text-neutral-10 dark:text-neutral-92 [&_svg]:h-5 [&_svg]:w-5"
241
+ className="input-icon-color [&_svg]:h-5 [&_svg]:w-5"
227
242
  onClick={onClearInput}
228
243
  tabIndex={-1}
229
244
  >
@@ -234,7 +249,7 @@ function InputTrailingElement({
234
249
  return (
235
250
  <ButtonUnstyled
236
251
  onClick={onToggleButtonClick}
237
- className="text-neutral-10 dark:text-neutral-92 [&_svg]:h-5 [&_svg]:w-5"
252
+ className="input-icon-color [&_svg]:h-5 [&_svg]:w-5"
238
253
  tabIndex={-1}
239
254
  >
240
255
  {isContentVisible ? <VisibilityOn /> : <VisibilityOff />}
@@ -74,7 +74,7 @@ export function InputWrapper({
74
74
  <div
75
75
  className={cx(
76
76
  'flex flex-row items-center',
77
- caption || errorMessage ? 'justify-between' : 'justify-end',
77
+ caption || errorMessage ? 'justify-between gap-md' : 'justify-end',
78
78
  )}
79
79
  >
80
80
  {(errorMessage || caption) && (
@@ -93,14 +93,13 @@ export const TextArea = forwardRef<HTMLTextAreaElement, TextFieldBaseProps>(func
93
93
  required={required}
94
94
  ref={ref}
95
95
  className={cx(
96
- 'peer block min-h-[50px]',
96
+ 'text-area-container peer block min-h-[50px]',
97
97
  BORDER_CLASSES,
98
98
  INPUT_CLASSES,
99
99
  INPUT_TEXT_CLASSES,
100
100
  INPUT_PLACEHOLDER_CLASSES,
101
101
  isInputContentVisible && isResizeEnabled ? 'resize-y' : 'resize-none',
102
- !isInputContentVisible &&
103
- 'not-visible select-none text-transparent dark:text-transparent',
102
+ !isInputContentVisible && 'not-visible select-none text-transparent',
104
103
  )}
105
104
  value={isInputContentVisible ? value : ''}
106
105
  {...textareaProps}
@@ -115,7 +114,7 @@ export const TextArea = forwardRef<HTMLTextAreaElement, TextFieldBaseProps>(func
115
114
  <span className="absolute bottom-4 right-4 flex">
116
115
  <ButtonUnstyled
117
116
  onClick={onToggleButtonClick}
118
- className="text-neutral-10 dark:text-neutral-92 [&_svg]:h-5 [&_svg]:w-5"
117
+ className="input-icon-color [&_svg]:h-5 [&_svg]:w-5"
119
118
  tabIndex={-1}
120
119
  >
121
120
  {isInputContentVisible ? <VisibilityOn /> : <VisibilityOff />}
@@ -139,10 +138,7 @@ function VisibilityOffBar({ rows, halfWidthRow }: VisibilityOffBarProps) {
139
138
  const isHalfWidth = halfWidthRow === index + 1;
140
139
  const width = isHalfWidth ? 'w-1/2' : 'w-full';
141
140
  return (
142
- <div
143
- key={index}
144
- className={`h-2.5 rounded bg-neutral-92/60 dark:bg-neutral-10/60 ${width}`}
145
- />
141
+ <div key={index} className={`input-visibility-bar h-2.5 rounded ${width}`} />
146
142
  );
147
143
  })}
148
144
  </>
@@ -2,13 +2,12 @@
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
4
  export const INPUT_CLASSES =
5
- 'w-full bg-transparent text-body-lg caret-primary-30 dark:caret-primary-80 focus:outline-none focus-visible:outline-none disabled:cursor-not-allowed';
6
- export const INPUT_TEXT_CLASSES = 'text-neutral-10 dark:text-neutral-92';
7
- export const INPUT_PLACEHOLDER_CLASSES =
8
- 'enabled:placeholder:text-neutral-40/40 dark:placeholder:text-neutral-60/40 enabled:dark:placeholder:text-neutral-60/40';
9
- export const BORDER_CLASSES =
10
- 'px-md py-sm rounded-lg border border-neutral-80 group-[.enabled]:cursor-text group-[.errored]:border-error-30 hover:group-[.enabled]:border-neutral-50 dark:border-neutral-20 dark:hover:border-neutral-60 dark:group-[.errored]:border-error-80 [&:has(input:focus)]:!border-primary-30 [&:has(input:focus)]:dark:!border-primary-80';
5
+ 'w-full input-caret-color bg-transparent text-body-lg caret-input-caret-color focus:outline-none focus-visible:outline-none disabled:cursor-not-allowed';
6
+ export const INPUT_TEXT_CLASSES = 'input-text-color';
7
+ export const INPUT_PLACEHOLDER_CLASSES = 'input-placeholder-color';
11
8
  export const INPUT_NUMBER_CLASSES =
12
9
  '[appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none disabled:cursor-not-allowed';
13
- export const LABEL_CLASSES =
14
- 'flex flex-col gap-y-2 text-label-lg text-neutral-40 dark:text-neutral-60';
10
+ export const BORDER_CLASSES =
11
+ 'px-md py-sm rounded-lg border input-border-color input-border-color-disabled group-[.enabled]:cursor-text input-border-error-color hover:group-[.enabled]:input-border-color-hover input-border-color-focus';
12
+
13
+ export const LABEL_CLASSES = 'flex flex-col gap-y-2 text-label-lg input-label-color';
@@ -10,7 +10,6 @@ export interface NavbarItemProps {
10
10
  * The type of the navbar item.
11
11
  */
12
12
  type?: NavbarItemType;
13
-
14
13
  /**
15
14
  * The icon of the navbar item.
16
15
  */
@@ -29,7 +29,7 @@ export function NavbarItemHorizontal({
29
29
  const backgroundColors = isSelected && SELECTED_BACKGROUND;
30
30
  const badgePositionClasses = text ? BADGE_WITH_TEXT : BADGE_WITHOUT_TEXT;
31
31
  const textClasses = isSelected ? SELECTED_TEXT : UNSELECTED_TEXT;
32
- const disabledClasses = isDisabled ? 'cursor-not-allowed opacity-60' : 'state-layer';
32
+ const disabledClasses = isDisabled ? 'cursor-not-allowed opacity-60' : 'state-layer-secondary';
33
33
  const handleClick = (e: React.MouseEvent<HTMLDivElement>) => {
34
34
  if (isDisabled) {
35
35
  e.preventDefault();
@@ -25,7 +25,7 @@ export function NavbarItemVertical({
25
25
  const textClasses = isSelected ? SELECTED_TEXT : UNSELECTED_TEXT;
26
26
  const disabledClasses = isDisabled
27
27
  ? 'cursor-not-allowed opacity-60'
28
- : 'state-layer cursor-pointer ';
28
+ : 'state-layer-secondary cursor-pointer ';
29
29
  const handleClick = (e: React.MouseEvent<HTMLDivElement>) => {
30
30
  if (isDisabled) {
31
31
  e.preventDefault();
@@ -1,8 +1,8 @@
1
1
  // Copyright (c) 2024 IOTA Stiftung
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
- export const SELECTED_ICON = 'text-neutral-10 dark:text-neutral-92';
5
- export const UNSELECTED_ICON = 'text-neutral-40 dark:text-neutral-60';
4
+ export const SELECTED_ICON = 'navbar-item-icon-selected-color';
5
+ export const UNSELECTED_ICON = 'navbar-item-icon-color';
6
6
 
7
7
  export const PADDING_WITH_TEXT = 'py-xxs px-sm';
8
8
  export const PADDING_WITHOUT_TEXT = 'p-sm';
@@ -12,5 +12,5 @@ export const SELECTED_BACKGROUND = 'bg-shader-primary-light-12 dark:bg-shader-pr
12
12
  export const BADGE_WITH_TEXT = '-top-3 right-3';
13
13
  export const BADGE_WITHOUT_TEXT = '-top-1 right-3';
14
14
 
15
- export const SELECTED_TEXT = 'text-neutral-10 dark:text-neutral-92';
16
- export const UNSELECTED_TEXT = 'text-neutral-60 dark:text-neutral-40';
15
+ export const SELECTED_TEXT = 'navbar-item-label-selected-color';
16
+ export const UNSELECTED_TEXT = 'navbar-item-label-color';
@@ -116,7 +116,12 @@ export function Search({
116
116
  const showSuggestions = isSuggestionsVisible && suggestions && suggestions.length > 0;
117
117
 
118
118
  const roundedStyleWithSuggestions = showSuggestions
119
- ? 'rounded-t-3xl border-b border-b-transparent'
119
+ ? cx(
120
+ 'rounded-t-3xl border-b',
121
+ type === SearchBarType.Outlined
122
+ ? '[&:not(.dark_*,.names_*)]:border-b-transparent'
123
+ : 'border-b-transparent',
124
+ )
120
125
  : type === SearchBarType.Outlined
121
126
  ? 'rounded-3xl border-b'
122
127
  : 'rounded-full';
@@ -159,7 +164,7 @@ export function Search({
159
164
  <div className="relative w-full">
160
165
  <div
161
166
  className={cx(
162
- 'flex items-center overflow-hidden px-md py-sm text-neutral-10 dark:text-neutral-92 [&_svg]:h-6 [&_svg]:w-6',
167
+ 'search-text-color flex items-center overflow-hidden px-md py-sm [&_svg]:h-6 [&_svg]:w-6',
163
168
  roundedStyleWithSuggestions,
164
169
  searchTypeClass,
165
170
  )}
@@ -173,7 +178,7 @@ export function Search({
173
178
  onFocus={() => setIsSuggestionsVisible(true)}
174
179
  placeholder={placeholder}
175
180
  className={cx(
176
- 'w-full flex-1 outline-none placeholder:text-neutral-40 placeholder:dark:text-neutral-60',
181
+ 'search-placeholder-color w-full flex-1 outline-none',
177
182
  backgroundColorClass,
178
183
  )}
179
184
  />
@@ -183,7 +188,7 @@ export function Search({
183
188
  <div
184
189
  ref={suggestionsListRef}
185
190
  className={cx(
186
- 'absolute left-0 top-full flex w-full flex-col items-center overflow-hidden bg-neutral-100 dark:bg-neutral-10',
191
+ 'absolute left-0 top-full flex w-full flex-col items-center overflow-hidden',
187
192
  suggestionsStyle,
188
193
  )}
189
194
  >
@@ -200,9 +205,7 @@ export function Search({
200
205
  onMouseEnter={() => setSelectedIndex(index)}
201
206
  className={cx(
202
207
  'w-full cursor-pointer px-md py-sm',
203
- selectedIndex === index
204
- ? 'bg-shader-primary-dark-16 dark:bg-shader-inverted-dark-16'
205
- : '',
208
+ selectedIndex === index ? 'search-selected-index-bg-color' : '',
206
209
  )}
207
210
  >
208
211
  {renderSuggestion(suggestion, index)}
@@ -5,16 +5,16 @@ import { SearchBarType } from './search.enums';
5
5
 
6
6
  export const BACKGROUND_COLORS: Record<SearchBarType, string> = {
7
7
  [SearchBarType.Outlined]: 'bg-transparent',
8
- [SearchBarType.Filled]: 'bg-neutral-92 dark:bg-neutral-12',
8
+ [SearchBarType.Filled]: 'search-bg-color-filled',
9
9
  };
10
10
 
11
11
  export const SEARCH_WRAPPER_STYLE: Record<SearchBarType, string> = {
12
- [SearchBarType.Outlined]: 'border-l border-r border-t border-neutral-70 dark:border-neutral-40',
13
- [SearchBarType.Filled]: 'bg-neutral-92 dark:bg-neutral-12',
12
+ [SearchBarType.Outlined]: 'border-l border-r border-t search-border-color',
13
+ [SearchBarType.Filled]: 'search-bg-color-filled',
14
14
  };
15
15
 
16
16
  export const SUGGESTIONS_WRAPPER_STYLE: Record<SearchBarType, string> = {
17
17
  [SearchBarType.Outlined]:
18
- 'rounded-b-3xl border-b border-l border-r border-neutral-70 dark:border-neutral-40',
19
- [SearchBarType.Filled]: 'rounded-b-3xl bg-neutral-92 dark:bg-neutral-12',
18
+ 'rounded-b-3xl border-b border-l border-r search-border-color search-suggestion-bg-color',
19
+ [SearchBarType.Filled]: 'rounded-b-3xl search-bg-color-filled',
20
20
  };
@@ -5,8 +5,8 @@ import { SegmentedButtonType } from './segmentedButton.enums';
5
5
 
6
6
  export const BACKGROUND_COLORS: Record<SegmentedButtonType, string> = {
7
7
  [SegmentedButtonType.Outlined]: 'bg-transparent',
8
- [SegmentedButtonType.Filled]: 'bg-neutral-96 dark:bg-neutral-10',
8
+ [SegmentedButtonType.Filled]: 'segmented-filled-bg-color',
9
9
  [SegmentedButtonType.Transparent]: 'bg-transparent',
10
10
  };
11
11
 
12
- export const OUTLINED_BORDER = 'border border-neutral-70 dark:border-neutral-40';
12
+ export const OUTLINED_BORDER = 'border segmented-outlined-border';
@@ -3,7 +3,7 @@
3
3
 
4
4
  import { TriangleDown } from '@iota/apps-ui-icons';
5
5
  import cx from 'classnames';
6
- import { forwardRef, useEffect, useState } from 'react';
6
+ import { forwardRef, useEffect, useRef, useState } from 'react';
7
7
  import { Dropdown } from '../dropdown/Dropdown';
8
8
  import { SecondaryText } from '@/components/atoms/secondary-text';
9
9
  import { InputWrapper, LabelHtmlTag } from '../input/InputWrapper';
@@ -88,16 +88,35 @@ export const Select = forwardRef<HTMLButtonElement, SelectProps>(
88
88
  ) => {
89
89
  const [isOpen, setIsOpen] = useState<boolean>(false);
90
90
  const selectedValue = findValueByProps(value, options);
91
+ const wrapperRef = useRef<HTMLDivElement>(null);
91
92
 
92
93
  const selectorText = selectedValue || placeholder;
93
94
  const selectPadding = size === SelectSize.Small ? 'px-sm' : 'px-md';
94
95
  const textSize = size === SelectSize.Small ? 'text-body-md' : 'text-body-lg';
96
+
95
97
  useEffect(() => {
96
98
  if (disabled && isOpen) {
97
99
  closeDropdown();
98
100
  }
99
101
  }, [disabled, isOpen]);
100
102
 
103
+ useEffect(() => {
104
+ if (!isOpen) return;
105
+
106
+ const handleClickOutside = (e: MouseEvent) => {
107
+ const target = e.target as Node;
108
+ if (wrapperRef.current && !wrapperRef.current.contains(target)) {
109
+ closeDropdown();
110
+ }
111
+ };
112
+
113
+ document.addEventListener('mousedown', handleClickOutside);
114
+
115
+ return () => {
116
+ document.removeEventListener('mousedown', handleClickOutside);
117
+ };
118
+ }, [isOpen]);
119
+
101
120
  function findValueByProps(value: SelectProps['value'], options: SelectOption[] = []) {
102
121
  return (
103
122
  options.find((option) =>
@@ -123,6 +142,7 @@ export const Select = forwardRef<HTMLButtonElement, SelectProps>(
123
142
  function closeDropdown() {
124
143
  setIsOpen(false);
125
144
  }
145
+
126
146
  return (
127
147
  <InputWrapper
128
148
  label={label}
@@ -131,27 +151,23 @@ export const Select = forwardRef<HTMLButtonElement, SelectProps>(
131
151
  errorMessage={errorMessage}
132
152
  labelHtmlTag={LabelHtmlTag.Div}
133
153
  >
134
- <div className="relative flex w-full flex-col">
154
+ <div className="relative flex w-full flex-col" ref={wrapperRef}>
135
155
  <ButtonUnstyled
136
156
  ref={ref}
137
157
  onClick={onSelectorClick}
138
158
  disabled={disabled}
139
159
  className={cx(
140
- 'flex flex-row items-center gap-x-3 rounded-lg border border-neutral-80 py-sm hover:enabled:border-neutral-50 focus-visible:enabled:border-primary-30 active:enabled:border-primary-30 disabled:cursor-not-allowed group-[.errored]:border-error-30 group-[.opened]:border-primary-30 dark:border-neutral-20 dark:hover:enabled:border-neutral-60 dark:group-[.errored]:border-error-80 dark:group-[.opened]:border-primary-80 [&:is(:focus,_:focus-visible,_:active)]:enabled:border-primary-30 dark:[&:is(:focus,_:focus-visible,_:active)]:enabled:border-primary-80 [&_svg]:h-5 [&_svg]:w-5',
160
+ 'select-container select-border-color focus-visible:enabled:select-border-focus-color active:enabled:select-border-focus-color group-[.errored]:select-border-error-color group-[.opened]:select-border-focus-color [&:is(:focus,_:focus-visible,_:active)]:enabled:select-border-focus-color hover:enabled:select-border-hover-color flex flex-row items-center gap-x-3 rounded-lg border py-sm disabled:cursor-not-allowed [&_svg]:h-5 [&_svg]:w-5',
141
161
  selectPadding,
142
162
  )}
143
163
  >
144
- {leadingIcon && (
145
- <span className="text-neutral-10 dark:text-neutral-92">
146
- {leadingIcon}
147
- </span>
148
- )}
164
+ {leadingIcon && <span className="select-icon-color">{leadingIcon}</span>}
149
165
 
150
166
  <div className="flex w-full flex-row items-baseline gap-x-3">
151
167
  {selectorText && (
152
168
  <div
153
169
  className={cx(
154
- 'block w-full text-start text-neutral-10 dark:text-neutral-92',
170
+ 'select-label-color block w-full text-start',
155
171
  textSize,
156
172
  )}
157
173
  >
@@ -167,18 +183,15 @@ export const Select = forwardRef<HTMLButtonElement, SelectProps>(
167
183
  </div>
168
184
 
169
185
  <TriangleDown
170
- className={cx(
171
- 'text-neutral-10 transition-transform dark:text-neutral-92',
172
- {
173
- ' rotate-180': isOpen,
174
- },
175
- )}
186
+ className={cx('select-icon-color transition-transform', {
187
+ ' rotate-180': isOpen,
188
+ })}
176
189
  />
177
190
  </ButtonUnstyled>
178
191
 
179
192
  {isOpen && (
180
193
  <div
181
- className="fixed left-0 top-0 z-[49] h-screen w-screen bg-transparent"
194
+ className="fixed left-0 top-0 z-[49] bg-transparent"
182
195
  onClick={closeDropdown}
183
196
  />
184
197
  )}
@@ -27,7 +27,7 @@ export function TableCellBase({
27
27
  return (
28
28
  <td
29
29
  className={cx(
30
- 'h-14 border-b border-shader-neutral-light-8 px-md dark:border-shader-neutral-dark-8',
30
+ 'table-cell-border-color h-14 border-b px-md',
31
31
  { 'last:border-none': hasLastBorderNoneClass },
32
32
  { 'flex items-center justify-center': isContentCentered },
33
33
  )}
@@ -49,7 +49,7 @@ export function TableCellText({
49
49
  <div className="flex flex-row items-baseline gap-1">
50
50
  <span>{children}</span>
51
51
  {supportingLabel && (
52
- <span className="text-body-sm text-neutral-60 dark:text-neutral-40">
52
+ <span className="table-cell-supporting-label-color text-body-sm">
53
53
  {supportingLabel}
54
54
  </span>
55
55
  )}
@@ -79,7 +79,7 @@ export function TableHeaderCell({
79
79
  }
80
80
  };
81
81
 
82
- const textColorClass = 'text-neutral-10 dark:text-neutral-92';
82
+ const textColorClass = 'table-header-text-color';
83
83
  const textSizeClass = 'text-label-lg';
84
84
 
85
85
  const sortElement = (() => {
@@ -102,7 +102,7 @@ export function TableHeaderCell({
102
102
  <th
103
103
  onClick={hasSort ? handleSort : undefined}
104
104
  className={cx(
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',
105
+ 'state-layer table-cell-border-color group relative h-14 border-b px-md after:pointer-events-none',
106
106
  {
107
107
  'cursor-pointer': hasSort,
108
108
  },
@@ -60,15 +60,15 @@ export function Title({
60
60
  >
61
61
  <div className="flex flex-row items-center gap-x-xxxs">
62
62
  <div className="flex flex-col justify-start">
63
- <div className="flex flex-row items-center gap-x-0.5 text-neutral-10 dark:text-neutral-92">
63
+ <div className="title-main-color flex flex-row items-center gap-x-0.5">
64
64
  <h4 className={cx(TITLE_SIZE[size])}>{title}</h4>
65
65
  {tooltipText && (
66
66
  <Tooltip text={tooltipText} position={tooltipPosition}>
67
- <Info />
67
+ <Info className="title-icon-color" />
68
68
  </Tooltip>
69
69
  )}
70
70
  </div>
71
- <p className="text-label-md text-neutral-60 dark:text-neutral-40">{subtitle}</p>
71
+ <p className="title-subtitle-color text-label-md">{subtitle}</p>
72
72
  </div>
73
73
  {supportingElement}
74
74
  </div>
@@ -1,25 +1,13 @@
1
1
  // Copyright (c) 2024 IOTA Stiftung
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
- import { VisualAssetType } from './visualAssetCard.enums';
5
- import { ButtonUnstyled } from '../button';
6
- import { MoreHoriz, PlaceholderReplace } from '@iota/apps-ui-icons';
4
+ import { ButtonUnstyled } from '../../atoms/button';
5
+ import { MoreHoriz } from '@iota/apps-ui-icons';
7
6
  import cx from 'classnames';
8
- import { useState } from 'react';
7
+ import { AssetMediaRenderer } from '../../molecules/asset-media-renderer';
8
+ import type { AssetMediaRendererProps } from '../../molecules/asset-media-renderer';
9
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;
10
+ interface VisualAssetBaseProps {
23
11
  /**
24
12
  * The onClick event for the icon.
25
13
  */
@@ -42,23 +30,31 @@ export interface VisualAssetCardProps {
42
30
  isHoverable?: boolean;
43
31
  }
44
32
 
33
+ interface CustomAssetDisplayProps {
34
+ /**
35
+ * The media to be displayed inside the card.
36
+ */
37
+ renderAsset?: React.ReactNode;
38
+ }
39
+
40
+ export type VisualAssetCardProps = VisualAssetBaseProps &
41
+ (AssetMediaRendererProps | CustomAssetDisplayProps);
42
+
45
43
  export function VisualAssetCard({
46
- assetType = VisualAssetType.Image,
47
- assetSrc,
48
- altText,
49
44
  onIconClick,
50
45
  onClick,
51
46
  icon = <MoreHoriz />,
52
47
  assetTitle,
53
48
  isHoverable = true,
54
- }: VisualAssetCardProps): React.JSX.Element {
55
- const [imageError, setImageError] = useState(false);
56
-
49
+ ...assetProps
50
+ }: VisualAssetBaseProps & (AssetMediaRendererProps | CustomAssetDisplayProps)): React.JSX.Element {
57
51
  const handleIconClick = (event: React.MouseEvent<HTMLButtonElement>) => {
58
52
  onIconClick?.(event);
59
53
  event?.stopPropagation();
60
54
  };
61
55
 
56
+ const isCustomAsset = !('src' in assetProps);
57
+
62
58
  return (
63
59
  <div
64
60
  className={cx('relative aspect-square w-full overflow-hidden rounded-xl', {
@@ -66,26 +62,16 @@ export function VisualAssetCard({
66
62
  })}
67
63
  onClick={onClick}
68
64
  >
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
- )}
65
+ {!isCustomAsset && <AssetMediaRenderer {...assetProps} />}
66
+
67
+ {isCustomAsset && assetProps.renderAsset}
68
+
83
69
  {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" />
70
+ <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:names:bg-shader-inverted-light-48 group-hover:dark:bg-shader-primary-dark-48" />
85
71
  )}
86
72
  {isHoverable && onIconClick && (
87
73
  <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"
74
+ className="visual-asset-icon-color 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"
89
75
  onClick={handleIconClick}
90
76
  >
91
77
  {icon}
@@ -93,7 +79,7 @@ export function VisualAssetCard({
93
79
  )}
94
80
  {isHoverable && assetTitle && (
95
81
  <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>
82
+ <span className="visual-asset-title-color text-title-md">{assetTitle}</span>
97
83
  </div>
98
84
  )}
99
85
  </div>
@@ -98,9 +98,8 @@ export function Accordion({
98
98
  }): React.JSX.Element {
99
99
  return (
100
100
  <div
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':
103
- !hideBorder,
101
+ className={cx('accordion-bg rounded-xl', {
102
+ 'accordion-border-color border': !hideBorder,
104
103
  })}
105
104
  >
106
105
  {children}
@@ -1,4 +1,4 @@
1
1
  // Copyright (c) 2024 IOTA Stiftung
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
- export const ICON_STYLE = 'text-neutral-10 dark:text-neutral-92 transition-transform ease-linear';
4
+ export const ICON_STYLE = 'accordion-icon-color transition-transform ease-linear';