@iota/apps-ui-kit 0.0.0-dev-20251015065834

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 (243) hide show
  1. package/CHANGELOG.md +145 -0
  2. package/LICENSE +201 -0
  3. package/README.md +16 -0
  4. package/dist/index.d.ts +1911 -0
  5. package/dist/index.es.js +6486 -0
  6. package/dist/index.es.js.map +1 -0
  7. package/dist/index.umd.js +66 -0
  8. package/dist/index.umd.js.map +1 -0
  9. package/dist/style.css +1 -0
  10. package/package.json +80 -0
  11. package/src/lib/components/atoms/badge/Badge.tsx +37 -0
  12. package/src/lib/components/atoms/badge/badge.classes.ts +33 -0
  13. package/src/lib/components/atoms/badge/badge.enums.ts +11 -0
  14. package/src/lib/components/atoms/badge/index.ts +6 -0
  15. package/src/lib/components/atoms/button/Button.tsx +110 -0
  16. package/src/lib/components/atoms/button/button-variants/ButtonPill.tsx +23 -0
  17. package/src/lib/components/atoms/button/button-variants/ButtonUnstyled.tsx +27 -0
  18. package/src/lib/components/atoms/button/button-variants/buttonVariants.types.ts +11 -0
  19. package/src/lib/components/atoms/button/button-variants/index.ts +5 -0
  20. package/src/lib/components/atoms/button/button.classes.ts +53 -0
  21. package/src/lib/components/atoms/button/button.enums.ts +21 -0
  22. package/src/lib/components/atoms/button/index.ts +7 -0
  23. package/src/lib/components/atoms/button-segment/ButtonSegment.tsx +98 -0
  24. package/src/lib/components/atoms/button-segment/buttonSegment.classes.ts +24 -0
  25. package/src/lib/components/atoms/button-segment/buttonSegment.enums.ts +7 -0
  26. package/src/lib/components/atoms/button-segment/index.ts +5 -0
  27. package/src/lib/components/atoms/checkbox/Checkbox.tsx +124 -0
  28. package/src/lib/components/atoms/checkbox/index.ts +4 -0
  29. package/src/lib/components/atoms/divider/Divider.tsx +48 -0
  30. package/src/lib/components/atoms/divider/divider.classes.ts +11 -0
  31. package/src/lib/components/atoms/divider/divider.enums.ts +7 -0
  32. package/src/lib/components/atoms/divider/index.ts +6 -0
  33. package/src/lib/components/atoms/header/Header.tsx +73 -0
  34. package/src/lib/components/atoms/header/index.ts +4 -0
  35. package/src/lib/components/atoms/image/Image.tsx +32 -0
  36. package/src/lib/components/atoms/image/index.ts +4 -0
  37. package/src/lib/components/atoms/index.ts +26 -0
  38. package/src/lib/components/atoms/info-box/InfoBox.tsx +68 -0
  39. package/src/lib/components/atoms/info-box/index.ts +5 -0
  40. package/src/lib/components/atoms/info-box/infoBox.classes.ts +18 -0
  41. package/src/lib/components/atoms/info-box/infoBox.enums.ts +14 -0
  42. package/src/lib/components/atoms/key-value-info/KeyValueInfo.tsx +157 -0
  43. package/src/lib/components/atoms/key-value-info/index.ts +6 -0
  44. package/src/lib/components/atoms/key-value-info/keyValue.enums.ts +7 -0
  45. package/src/lib/components/atoms/label-text/LabelText.tsx +92 -0
  46. package/src/lib/components/atoms/label-text/index.ts +5 -0
  47. package/src/lib/components/atoms/label-text/labelText.classes.ts +22 -0
  48. package/src/lib/components/atoms/label-text/labelText.enums.ts +8 -0
  49. package/src/lib/components/atoms/list-item/ListItem.tsx +88 -0
  50. package/src/lib/components/atoms/list-item/index.ts +4 -0
  51. package/src/lib/components/atoms/loading-indicator/LoadingIndicator.tsx +38 -0
  52. package/src/lib/components/atoms/loading-indicator/index.ts +4 -0
  53. package/src/lib/components/atoms/media-fallback/MediaFallback.tsx +12 -0
  54. package/src/lib/components/atoms/media-fallback/index.ts +4 -0
  55. package/src/lib/components/atoms/panel/Panel.tsx +28 -0
  56. package/src/lib/components/atoms/panel/index.ts +4 -0
  57. package/src/lib/components/atoms/placeholder/Placeholder.tsx +27 -0
  58. package/src/lib/components/atoms/placeholder/index.ts +4 -0
  59. package/src/lib/components/atoms/radio-button/RadioButton.tsx +64 -0
  60. package/src/lib/components/atoms/radio-button/index.ts +4 -0
  61. package/src/lib/components/atoms/secondary-text/SecondaryText.tsx +29 -0
  62. package/src/lib/components/atoms/secondary-text/index.ts +4 -0
  63. package/src/lib/components/atoms/skeleton/Skeleton.tsx +30 -0
  64. package/src/lib/components/atoms/skeleton/index.ts +4 -0
  65. package/src/lib/components/atoms/snackbar/Snackbar.tsx +69 -0
  66. package/src/lib/components/atoms/snackbar/index.ts +5 -0
  67. package/src/lib/components/atoms/snackbar/snackbar.classes.ts +18 -0
  68. package/src/lib/components/atoms/snackbar/snackbar.enums.tsx +9 -0
  69. package/src/lib/components/atoms/toggle/Toggle.tsx +140 -0
  70. package/src/lib/components/atoms/toggle/index.ts +6 -0
  71. package/src/lib/components/atoms/toggle/toggle.classes.ts +41 -0
  72. package/src/lib/components/atoms/toggle/toggle.enums.ts +12 -0
  73. package/src/lib/components/atoms/tooltip/Tooltip.tsx +139 -0
  74. package/src/lib/components/atoms/tooltip/index.ts +5 -0
  75. package/src/lib/components/atoms/tooltip/tooltip.classes.ts +11 -0
  76. package/src/lib/components/atoms/tooltip/tooltip.enums.ts +9 -0
  77. package/src/lib/components/atoms/video/Video.tsx +33 -0
  78. package/src/lib/components/atoms/video/index.ts +4 -0
  79. package/src/lib/components/atoms/video/video.constants.ts +31 -0
  80. package/src/lib/components/index.ts +6 -0
  81. package/src/lib/components/molecules/account/Account.tsx +195 -0
  82. package/src/lib/components/molecules/account/index.ts +4 -0
  83. package/src/lib/components/molecules/address/Address.tsx +101 -0
  84. package/src/lib/components/molecules/address/index.ts +4 -0
  85. package/src/lib/components/molecules/asset-media-renderer/AssetMediaRenderer.tsx +50 -0
  86. package/src/lib/components/molecules/asset-media-renderer/index.ts +4 -0
  87. package/src/lib/components/molecules/card/Card.tsx +70 -0
  88. package/src/lib/components/molecules/card/CardAction.tsx +78 -0
  89. package/src/lib/components/molecules/card/CardBody.tsx +73 -0
  90. package/src/lib/components/molecules/card/CardImage.tsx +43 -0
  91. package/src/lib/components/molecules/card/CardImagePlaceholder.tsx +146 -0
  92. package/src/lib/components/molecules/card/card.classes.ts +27 -0
  93. package/src/lib/components/molecules/card/card.enums.ts +26 -0
  94. package/src/lib/components/molecules/card/index.ts +8 -0
  95. package/src/lib/components/molecules/chip/Chip.tsx +146 -0
  96. package/src/lib/components/molecules/chip/chip.classes.ts +54 -0
  97. package/src/lib/components/molecules/chip/chip.enums.ts +15 -0
  98. package/src/lib/components/molecules/chip/index.ts +5 -0
  99. package/src/lib/components/molecules/display-stats/DisplayStats.tsx +142 -0
  100. package/src/lib/components/molecules/display-stats/displayStats.classes.ts +36 -0
  101. package/src/lib/components/molecules/display-stats/displayStats.enums.ts +13 -0
  102. package/src/lib/components/molecules/display-stats/index.ts +5 -0
  103. package/src/lib/components/molecules/dropdown/Dropdown.tsx +10 -0
  104. package/src/lib/components/molecules/dropdown/dropdownPosition.enums.ts +7 -0
  105. package/src/lib/components/molecules/dropdown/index.ts +5 -0
  106. package/src/lib/components/molecules/index.ts +19 -0
  107. package/src/lib/components/molecules/input/Input.tsx +261 -0
  108. package/src/lib/components/molecules/input/InputWrapper.tsx +97 -0
  109. package/src/lib/components/molecules/input/TextArea.tsx +148 -0
  110. package/src/lib/components/molecules/input/index.ts +8 -0
  111. package/src/lib/components/molecules/input/input.classes.ts +13 -0
  112. package/src/lib/components/molecules/input/input.enums.ts +14 -0
  113. package/src/lib/components/molecules/input/input.types.ts +29 -0
  114. package/src/lib/components/molecules/navbar-item/NavbarItem.tsx +47 -0
  115. package/src/lib/components/molecules/navbar-item/NavbarItemHorizontal.tsx +65 -0
  116. package/src/lib/components/molecules/navbar-item/NavbarItemVertical.tsx +54 -0
  117. package/src/lib/components/molecules/navbar-item/index.ts +5 -0
  118. package/src/lib/components/molecules/navbar-item/navbarItem.classes.ts +16 -0
  119. package/src/lib/components/molecules/navbar-item/navbarItem.enums.ts +7 -0
  120. package/src/lib/components/molecules/search/Search.tsx +219 -0
  121. package/src/lib/components/molecules/search/index.ts +5 -0
  122. package/src/lib/components/molecules/search/search.classes.ts +20 -0
  123. package/src/lib/components/molecules/search/search.enums.ts +7 -0
  124. package/src/lib/components/molecules/segmented-button/SegmentedButton.tsx +38 -0
  125. package/src/lib/components/molecules/segmented-button/index.ts +6 -0
  126. package/src/lib/components/molecules/segmented-button/segmentedButton.classes.ts +12 -0
  127. package/src/lib/components/molecules/segmented-button/segmentedButton.enums.ts +8 -0
  128. package/src/lib/components/molecules/select/Select.tsx +235 -0
  129. package/src/lib/components/molecules/select/index.ts +5 -0
  130. package/src/lib/components/molecules/select/select.enums.ts +7 -0
  131. package/src/lib/components/molecules/table-cell/TableCell.tsx +62 -0
  132. package/src/lib/components/molecules/table-cell/index.ts +4 -0
  133. package/src/lib/components/molecules/table-header-cell/TableHeaderCell.tsx +140 -0
  134. package/src/lib/components/molecules/table-header-cell/index.ts +5 -0
  135. package/src/lib/components/molecules/table-header-cell/tableHeaderCell.enums.ts +7 -0
  136. package/src/lib/components/molecules/title/Title.tsx +78 -0
  137. package/src/lib/components/molecules/title/index.ts +5 -0
  138. package/src/lib/components/molecules/title/titleClasses.constants.ts +14 -0
  139. package/src/lib/components/molecules/title/titleSize.enums.ts +7 -0
  140. package/src/lib/components/molecules/visual-asset-card/VisualAssetCard.tsx +89 -0
  141. package/src/lib/components/molecules/visual-asset-card/index.ts +5 -0
  142. package/src/lib/components/molecules/visual-asset-card/visualAssetCard.enums.ts +7 -0
  143. package/src/lib/components/organisms/accordion/Accordion.tsx +108 -0
  144. package/src/lib/components/organisms/accordion/accordion.classes.ts +4 -0
  145. package/src/lib/components/organisms/accordion/index.ts +4 -0
  146. package/src/lib/components/organisms/dialog/Dialog.tsx +157 -0
  147. package/src/lib/components/organisms/dialog/dialog.enums.ts +7 -0
  148. package/src/lib/components/organisms/dialog/index.ts +5 -0
  149. package/src/lib/components/organisms/index.ts +7 -0
  150. package/src/lib/components/organisms/navbar/Navbar.tsx +101 -0
  151. package/src/lib/components/organisms/navbar/NavbarSlideout.tsx +59 -0
  152. package/src/lib/components/organisms/navbar/index.ts +5 -0
  153. package/src/lib/components/organisms/table/Table.tsx +234 -0
  154. package/src/lib/components/organisms/table/TableContext.tsx +56 -0
  155. package/src/lib/components/organisms/table/index.ts +4 -0
  156. package/src/lib/constants/colors.constants.ts +239 -0
  157. package/src/lib/constants/index.ts +4 -0
  158. package/src/lib/enums/index.ts +5 -0
  159. package/src/lib/enums/screenSize.enums.ts +9 -0
  160. package/src/lib/enums/theme.enums.ts +8 -0
  161. package/src/lib/index.ts +10 -0
  162. package/src/lib/styles/base.css +144 -0
  163. package/src/lib/styles/components.css +735 -0
  164. package/src/lib/styles/fonts.css +7 -0
  165. package/src/lib/styles/index.css +18 -0
  166. package/src/lib/styles/utilities.css +61 -0
  167. package/src/lib/styles/variables/dark-variables.css +262 -0
  168. package/src/lib/styles/variables/general-variables.css +9 -0
  169. package/src/lib/styles/variables/light-variables.css +276 -0
  170. package/src/lib/styles/variables/names-variables.css +270 -0
  171. package/src/lib/tailwind/base.presets.ts +58 -0
  172. package/src/lib/tailwind/constants/customColors.constants.ts +13 -0
  173. package/src/lib/tailwind/constants/fontFamily.constants.ts +8 -0
  174. package/src/lib/tailwind/constants/fontSizes.constants.ts +201 -0
  175. package/src/lib/tailwind/constants/gradients.constants.ts +6 -0
  176. package/src/lib/tailwind/constants/index.ts +7 -0
  177. package/src/lib/tailwind/constants/scaling.constants.ts +41 -0
  178. package/src/lib/tailwind/constants/variableSpacing.constants.ts +66 -0
  179. package/src/lib/tailwind/helpers.ts +20 -0
  180. package/src/lib/tailwind/index.ts +5 -0
  181. package/src/lib/tailwind/plugins.ts +12 -0
  182. package/src/lib/tailwind/responsive.presets.ts +36 -0
  183. package/src/lib/tailwind/static.presets.ts +25 -0
  184. package/src/lib/tailwind/types.ts +6 -0
  185. package/src/lib/utils/format.ts +8 -0
  186. package/src/lib/utils/index.ts +4 -0
  187. package/src/storybook/blocks/Showcase.tsx +20 -0
  188. package/src/storybook/blocks/TypeSet.tsx +34 -0
  189. package/src/storybook/blocks/TypeSetItem.tsx +74 -0
  190. package/src/storybook/blocks/index.ts +6 -0
  191. package/src/storybook/constants/gradients.constants.ts +8 -0
  192. package/src/storybook/constants/index.ts +5 -0
  193. package/src/storybook/constants/typeset.constants.ts +39 -0
  194. package/src/storybook/constants/variableSpacing.constants.ts +6 -0
  195. package/src/storybook/helpers/formatGradientNames.ts +15 -0
  196. package/src/storybook/helpers/getFontSizeLabel.ts +15 -0
  197. package/src/storybook/helpers/index.ts +4 -0
  198. package/src/storybook/stories/atoms/Badge.stories.tsx +32 -0
  199. package/src/storybook/stories/atoms/Button.stories.tsx +45 -0
  200. package/src/storybook/stories/atoms/ButtonSegment.stories.tsx +45 -0
  201. package/src/storybook/stories/atoms/Checkbox.stories.tsx +34 -0
  202. package/src/storybook/stories/atoms/CustomScrollbar.stories.tsx +22 -0
  203. package/src/storybook/stories/atoms/Divider.stories.tsx +41 -0
  204. package/src/storybook/stories/atoms/Header.stories.tsx +58 -0
  205. package/src/storybook/stories/atoms/InfoBox.stories.tsx +67 -0
  206. package/src/storybook/stories/atoms/KeyValueInfo.stories.tsx +59 -0
  207. package/src/storybook/stories/atoms/LabelText.stories.tsx +55 -0
  208. package/src/storybook/stories/atoms/ListItem.stories.tsx +41 -0
  209. package/src/storybook/stories/atoms/LoadingIndicator.stories.tsx +32 -0
  210. package/src/storybook/stories/atoms/Panel.stories.tsx +36 -0
  211. package/src/storybook/stories/atoms/Placeholder.stories.tsx +33 -0
  212. package/src/storybook/stories/atoms/RadioButton.stories.tsx +51 -0
  213. package/src/storybook/stories/atoms/Skeleton.stories.tsx +33 -0
  214. package/src/storybook/stories/atoms/Snackbar.stories.tsx +43 -0
  215. package/src/storybook/stories/atoms/Toggle.stories.tsx +59 -0
  216. package/src/storybook/stories/atoms/Tooltip.stories.tsx +59 -0
  217. package/src/storybook/stories/atoms/VisualAssetCard.stories.tsx +57 -0
  218. package/src/storybook/stories/components/DocsSyncTheme.tsx +20 -0
  219. package/src/storybook/stories/components/VariableSpacingShowcase.tsx +46 -0
  220. package/src/storybook/stories/components/index.ts +5 -0
  221. package/src/storybook/stories/design-tokens/colors.stories.mdx +44 -0
  222. package/src/storybook/stories/design-tokens/icons.stories.mdx +19 -0
  223. package/src/storybook/stories/design-tokens/typeset.stories.mdx +17 -0
  224. package/src/storybook/stories/design-tokens/variable-spacing.stories.mdx +11 -0
  225. package/src/storybook/stories/molecules/Account.stories.tsx +71 -0
  226. package/src/storybook/stories/molecules/Address.stories.tsx +39 -0
  227. package/src/storybook/stories/molecules/Card.stories.tsx +112 -0
  228. package/src/storybook/stories/molecules/Chip.stories.tsx +127 -0
  229. package/src/storybook/stories/molecules/DisplayStats.stories.tsx +63 -0
  230. package/src/storybook/stories/molecules/Dropdown.stories.tsx +38 -0
  231. package/src/storybook/stories/molecules/Input.stories.tsx +239 -0
  232. package/src/storybook/stories/molecules/NavbarItem.stories.tsx +45 -0
  233. package/src/storybook/stories/molecules/Search.stories.tsx +101 -0
  234. package/src/storybook/stories/molecules/SegmentedButton.stories.tsx +108 -0
  235. package/src/storybook/stories/molecules/Select.stories.tsx +103 -0
  236. package/src/storybook/stories/molecules/TableCell.stories.tsx +46 -0
  237. package/src/storybook/stories/molecules/TableHeaderCell.stories.tsx +34 -0
  238. package/src/storybook/stories/molecules/TextArea.stories.tsx +80 -0
  239. package/src/storybook/stories/molecules/Title.stories.tsx +76 -0
  240. package/src/storybook/stories/organisms/Accordion.stories.tsx +70 -0
  241. package/src/storybook/stories/organisms/Dialog.stories.tsx +129 -0
  242. package/src/storybook/stories/organisms/Navbar.stories.tsx +78 -0
  243. package/src/storybook/stories/organisms/Table.stories.tsx +154 -0
@@ -0,0 +1,261 @@
1
+ // Copyright (c) 2024 IOTA Stiftung
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import { forwardRef, Fragment, useEffect, useRef, useState } from 'react';
5
+ import cx from 'classnames';
6
+ import type { InputWrapperProps } from './InputWrapper';
7
+ import { InputWrapper } from './InputWrapper';
8
+ import {
9
+ BORDER_CLASSES,
10
+ INPUT_CLASSES,
11
+ INPUT_TEXT_CLASSES,
12
+ INPUT_NUMBER_CLASSES,
13
+ INPUT_PLACEHOLDER_CLASSES,
14
+ } from './input.classes';
15
+ import { InputType } from './input.enums';
16
+ import { SecondaryText } from '@/components/atoms/secondary-text';
17
+ import { Close, VisibilityOff, VisibilityOn } from '@iota/apps-ui-icons';
18
+ import { ButtonUnstyled } from '@/components/atoms/button';
19
+ import type { InputPropsByType, NumericFormatInputProps } from './input.types';
20
+ import { NumericFormat } from 'react-number-format';
21
+
22
+ export interface BaseInputProps extends InputWrapperProps {
23
+ /**
24
+ * A leading icon that is shown before the input
25
+ */
26
+ leadingIcon?: React.ReactNode;
27
+ /**
28
+ * Supporting text that is shown at the end of the input component.
29
+ */
30
+ supportingText?: string;
31
+ /**
32
+ * Amount counter that is shown at the side of the caption text.
33
+ */
34
+ amountCounter?: string | number;
35
+ /**
36
+ * Trailing element that is shown after the input
37
+ */
38
+ trailingElement?: React.ReactNode;
39
+ /**
40
+ * Is the content of the input visible
41
+ */
42
+ isContentVisible?: boolean;
43
+ /**
44
+ * Value of the input
45
+ */
46
+ value?: string | number;
47
+ /**
48
+ * Text that is shown below the value of the input.
49
+ */
50
+ supportingValue?: string | null;
51
+ /**
52
+ * Default value of the input
53
+ */
54
+ defaultValue?: string | number;
55
+ /**
56
+ * onClearInput function that is called when the clear button is clicked
57
+ */
58
+ onClearInput?: () => void;
59
+ /**
60
+ * Shows toggle button to show/hide the content of the input field
61
+ */
62
+ isVisibilityToggleEnabled?: boolean;
63
+ }
64
+
65
+ export type InputProps = BaseInputProps & InputPropsByType;
66
+
67
+ export const Input = forwardRef<HTMLInputElement, InputProps>(function InputComponent(
68
+ {
69
+ label,
70
+ caption,
71
+ disabled,
72
+ errorMessage,
73
+ leadingIcon,
74
+ supportingText,
75
+ amountCounter,
76
+ trailingElement,
77
+ isContentVisible,
78
+ value,
79
+ supportingValue,
80
+ defaultValue,
81
+ onClearInput,
82
+ isVisibilityToggleEnabled,
83
+ type,
84
+ ...inputProps
85
+ },
86
+ forwardRef,
87
+ ) {
88
+ isVisibilityToggleEnabled ??= type === InputType.Password;
89
+ const inputWrapperRef = useRef<HTMLDivElement | null>(null);
90
+
91
+ const [isInputContentVisible, setIsInputContentVisible] = useState<boolean>(
92
+ isContentVisible ?? type !== InputType.Password,
93
+ );
94
+
95
+ useEffect(() => {
96
+ setIsInputContentVisible(isContentVisible ?? type !== InputType.Password);
97
+ }, [type, isContentVisible]);
98
+
99
+ function onToggleButtonClick() {
100
+ setIsInputContentVisible((prev) => !prev);
101
+ }
102
+
103
+ function focusOnInput() {
104
+ if (inputWrapperRef.current) {
105
+ inputWrapperRef.current.querySelector('input')?.focus();
106
+ }
107
+ }
108
+
109
+ return (
110
+ <InputWrapper
111
+ label={label}
112
+ caption={caption}
113
+ disabled={disabled}
114
+ errorMessage={errorMessage}
115
+ amountCounter={amountCounter}
116
+ required={inputProps.required}
117
+ >
118
+ <div
119
+ className={cx(
120
+ 'input-container relative flex flex-row items-center gap-x-3',
121
+ BORDER_CLASSES,
122
+ )}
123
+ onClick={focusOnInput}
124
+ ref={inputWrapperRef}
125
+ >
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>
149
+ )}
150
+ </div>
151
+
152
+ {supportingText && <SecondaryText>{supportingText}</SecondaryText>}
153
+ <InputTrailingElement
154
+ value={value}
155
+ type={type}
156
+ onClearInput={onClearInput}
157
+ isContentVisible={isInputContentVisible}
158
+ trailingElement={trailingElement}
159
+ isVisibilityToggleEnabled={isVisibilityToggleEnabled}
160
+ onToggleButtonClick={onToggleButtonClick}
161
+ />
162
+ </div>
163
+ </InputWrapper>
164
+ );
165
+ });
166
+
167
+ function InputElement({
168
+ type,
169
+ inputRef,
170
+ ...inputProps
171
+ }: InputProps & {
172
+ inputRef: React.ForwardedRef<HTMLInputElement>;
173
+ className: string;
174
+ }) {
175
+ function preventScrollInputChange(e: React.WheelEvent<HTMLInputElement>) {
176
+ if (type === InputType.Number) {
177
+ const input = e.currentTarget;
178
+
179
+ input.blur();
180
+ e.stopPropagation();
181
+ setTimeout(() => {
182
+ input.focus({ preventScroll: true });
183
+ }, 0);
184
+ }
185
+ }
186
+ return type === InputType.NumericFormat ? (
187
+ <NumericFormatInput inputRef={inputRef} {...inputProps} type={type} />
188
+ ) : (
189
+ <input
190
+ ref={inputRef}
191
+ {...inputProps}
192
+ type={type}
193
+ onWheel={(e) => {
194
+ preventScrollInputChange(e);
195
+ inputProps.onWheel?.(e);
196
+ }}
197
+ />
198
+ );
199
+ }
200
+
201
+ function NumericFormatInput({
202
+ inputRef,
203
+ className,
204
+ type,
205
+ ...inputProps
206
+ }: NumericFormatInputProps &
207
+ InputProps & {
208
+ inputRef: React.ForwardedRef<HTMLInputElement>;
209
+ className: string;
210
+ value?: string | number;
211
+ }) {
212
+ return (
213
+ <NumericFormat
214
+ className={className}
215
+ valueIsNumericString
216
+ getInputRef={inputRef}
217
+ {...inputProps}
218
+ />
219
+ );
220
+ }
221
+
222
+ function InputTrailingElement({
223
+ value,
224
+ type,
225
+ onClearInput,
226
+ isContentVisible,
227
+ trailingElement,
228
+ onToggleButtonClick,
229
+ isVisibilityToggleEnabled,
230
+ }: BaseInputProps & {
231
+ onToggleButtonClick: (e: React.MouseEvent<HTMLButtonElement>) => void;
232
+ type: InputPropsByType['type'];
233
+ }) {
234
+ const showClearInput = Boolean(type === InputType.Text && value && onClearInput);
235
+ const showPasswordToggle = Boolean(type === InputType.Password && isVisibilityToggleEnabled);
236
+ const showTrailingElement = Boolean(trailingElement && !showClearInput && !showPasswordToggle);
237
+
238
+ if (showClearInput) {
239
+ return (
240
+ <ButtonUnstyled
241
+ className="input-icon-color [&_svg]:h-5 [&_svg]:w-5"
242
+ onClick={onClearInput}
243
+ tabIndex={-1}
244
+ >
245
+ <Close />
246
+ </ButtonUnstyled>
247
+ );
248
+ } else if (showPasswordToggle) {
249
+ return (
250
+ <ButtonUnstyled
251
+ onClick={onToggleButtonClick}
252
+ className="input-icon-color [&_svg]:h-5 [&_svg]:w-5"
253
+ tabIndex={-1}
254
+ >
255
+ {isContentVisible ? <VisibilityOn /> : <VisibilityOff />}
256
+ </ButtonUnstyled>
257
+ );
258
+ } else if (showTrailingElement) {
259
+ return <Fragment>{trailingElement}</Fragment>;
260
+ }
261
+ }
@@ -0,0 +1,97 @@
1
+ // Copyright (c) 2024 IOTA Stiftung
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import cx from 'classnames';
5
+ import { SecondaryText } from '@/components/atoms/secondary-text';
6
+ import { LABEL_CLASSES } from './input.classes';
7
+ import { createElement } from 'react';
8
+
9
+ export enum LabelHtmlTag {
10
+ Label = 'label',
11
+ Div = 'div',
12
+ }
13
+
14
+ export interface InputWrapperProps {
15
+ /**
16
+ * Shows a label with the text above the input.
17
+ */
18
+ label?: string;
19
+ /**
20
+ * Shows a caption with the text below the input.
21
+ */
22
+ caption?: string;
23
+ /**
24
+ * Error Message. Overrides the caption.
25
+ */
26
+ errorMessage?: string;
27
+ /**
28
+ * Amount counter that is shown at the side of the caption text.
29
+ */
30
+ amountCounter?: string | number;
31
+ /**
32
+ * Is the input required
33
+ */
34
+ required?: boolean;
35
+ /**
36
+ * Is the input disabled
37
+ */
38
+ disabled?: boolean;
39
+ /**
40
+ * Use a div as a label instead of a label element
41
+ */
42
+ labelHtmlTag?: LabelHtmlTag;
43
+ }
44
+
45
+ export function InputWrapper({
46
+ label,
47
+ caption,
48
+ disabled,
49
+ errorMessage,
50
+ amountCounter,
51
+ required,
52
+ labelHtmlTag = LabelHtmlTag.Label,
53
+ children,
54
+ }: React.PropsWithChildren<InputWrapperProps>) {
55
+ return (
56
+ <div
57
+ className={cx('group flex w-full flex-col gap-y-2', {
58
+ 'cursor-not-allowed opacity-40': disabled,
59
+ errored: errorMessage,
60
+ enabled: !disabled,
61
+ required: required,
62
+ })}
63
+ >
64
+ {label ? (
65
+ <LabelWrapper labelHtmlTag={labelHtmlTag}>
66
+ {label}
67
+ {children}
68
+ </LabelWrapper>
69
+ ) : (
70
+ children
71
+ )}
72
+
73
+ {(errorMessage || caption || amountCounter) && (
74
+ <div
75
+ className={cx(
76
+ 'flex flex-row items-center',
77
+ caption || errorMessage ? 'justify-between gap-md' : 'justify-end',
78
+ )}
79
+ >
80
+ {(errorMessage || caption) && (
81
+ <SecondaryText hasErrorStyles>{errorMessage || caption}</SecondaryText>
82
+ )}
83
+ {amountCounter && <SecondaryText>{amountCounter}</SecondaryText>}
84
+ </div>
85
+ )}
86
+ </div>
87
+ );
88
+ }
89
+
90
+ function LabelWrapper({
91
+ labelHtmlTag,
92
+ children,
93
+ }: Required<Pick<InputWrapperProps, 'labelHtmlTag'>> & {
94
+ children: React.ReactNode;
95
+ }) {
96
+ return createElement(labelHtmlTag, { className: LABEL_CLASSES }, children);
97
+ }
@@ -0,0 +1,148 @@
1
+ // Copyright (c) 2024 IOTA Stiftung
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import { forwardRef, useEffect, useState } from 'react';
5
+ import type { InputWrapperProps } from './InputWrapper';
6
+ import { InputWrapper } from './InputWrapper';
7
+ import {
8
+ BORDER_CLASSES,
9
+ INPUT_CLASSES,
10
+ INPUT_TEXT_CLASSES,
11
+ INPUT_PLACEHOLDER_CLASSES,
12
+ } from './input.classes';
13
+ import cx from 'classnames';
14
+ import { ButtonUnstyled } from '@/components/atoms/button';
15
+ import { VisibilityOff, VisibilityOn } from '@iota/apps-ui-icons';
16
+
17
+ type TextAreaProps = Omit<
18
+ React.TextareaHTMLAttributes<HTMLTextAreaElement>,
19
+ 'cols' | 'resize' | 'className'
20
+ >;
21
+
22
+ interface TextFieldBaseProps extends TextAreaProps, InputWrapperProps {
23
+ /**
24
+ * Shows a label with the text above the input field.
25
+ */
26
+ label?: string;
27
+ /**
28
+ * Shows a caption with the text below the input field.
29
+ */
30
+ caption?: string;
31
+ /**
32
+ * Error Message. Overrides the caption.
33
+ */
34
+ errorMessage?: string;
35
+ /**
36
+ * Amount counter that is shown at the side of the caption text.
37
+ */
38
+ amountCounter?: string | number;
39
+ /**
40
+ * Shows toggle button to show/hide the content of the input field
41
+ */
42
+ isVisibilityToggleEnabled?: boolean;
43
+ /**
44
+ * Is the content of the input visible
45
+ */
46
+ isContentVisible?: boolean;
47
+ /**
48
+ * If true the textarea is resizable vertically
49
+ */
50
+ isResizeEnabled?: boolean;
51
+ }
52
+
53
+ export const TextArea = forwardRef<HTMLTextAreaElement, TextFieldBaseProps>(function TextArea(
54
+ {
55
+ label,
56
+ caption,
57
+ disabled,
58
+ errorMessage,
59
+ value,
60
+ amountCounter,
61
+ isVisibilityToggleEnabled,
62
+ isResizeEnabled,
63
+ required,
64
+ isContentVisible,
65
+ defaultValue,
66
+ ...textareaProps
67
+ },
68
+ ref,
69
+ ) {
70
+ const [isInputContentVisible, setIsInputContentVisible] = useState<boolean>(
71
+ isContentVisible ?? true,
72
+ );
73
+
74
+ useEffect(() => {
75
+ setIsInputContentVisible(isContentVisible ?? true);
76
+ }, [isContentVisible]);
77
+
78
+ function onToggleButtonClick() {
79
+ setIsInputContentVisible((prev) => !prev);
80
+ }
81
+
82
+ return (
83
+ <InputWrapper
84
+ label={label}
85
+ caption={caption}
86
+ disabled={disabled || !isInputContentVisible}
87
+ errorMessage={errorMessage}
88
+ amountCounter={amountCounter}
89
+ required={required}
90
+ >
91
+ <div className="relative">
92
+ <textarea
93
+ disabled={disabled || !isInputContentVisible}
94
+ required={required}
95
+ ref={ref}
96
+ className={cx(
97
+ 'text-area-container peer block min-h-[50px]',
98
+ BORDER_CLASSES,
99
+ INPUT_CLASSES,
100
+ INPUT_TEXT_CLASSES,
101
+ INPUT_PLACEHOLDER_CLASSES,
102
+ isInputContentVisible && isResizeEnabled ? 'resize-y' : 'resize-none',
103
+ !isInputContentVisible && 'not-visible select-none text-transparent',
104
+ )}
105
+ defaultValue={isInputContentVisible && defaultValue ? defaultValue : undefined}
106
+ value={isInputContentVisible && value ? value : undefined}
107
+ {...textareaProps}
108
+ />
109
+ {!isInputContentVisible && (
110
+ <div className="absolute left-0 top-0 flex h-full w-full flex-col items-stretch gap-y-2 px-md py-sm peer-[.not-visible]:select-none">
111
+ <VisibilityOffBar rows={3} halfWidthRow={3} />
112
+ </div>
113
+ )}
114
+
115
+ {isVisibilityToggleEnabled && (
116
+ <span className="absolute bottom-4 right-4 flex">
117
+ <ButtonUnstyled
118
+ onClick={onToggleButtonClick}
119
+ className="input-icon-color [&_svg]:h-5 [&_svg]:w-5"
120
+ tabIndex={-1}
121
+ >
122
+ {isInputContentVisible ? <VisibilityOn /> : <VisibilityOff />}
123
+ </ButtonUnstyled>
124
+ </span>
125
+ )}
126
+ </div>
127
+ </InputWrapper>
128
+ );
129
+ });
130
+
131
+ interface VisibilityOffBarProps {
132
+ rows: number;
133
+ halfWidthRow?: number;
134
+ }
135
+
136
+ function VisibilityOffBar({ rows, halfWidthRow }: VisibilityOffBarProps) {
137
+ return (
138
+ <>
139
+ {Array.from({ length: rows }).map((_, index) => {
140
+ const isHalfWidth = halfWidthRow === index + 1;
141
+ const width = isHalfWidth ? 'w-1/2' : 'w-full';
142
+ return (
143
+ <div key={index} className={`input-visibility-bar h-2.5 rounded ${width}`} />
144
+ );
145
+ })}
146
+ </>
147
+ );
148
+ }
@@ -0,0 +1,8 @@
1
+ // Copyright (c) 2024 IOTA Stiftung
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ export * from './Input';
5
+ export * from './TextArea';
6
+
7
+ export * from './input.types';
8
+ export * from './input.enums';
@@ -0,0 +1,13 @@
1
+ // Copyright (c) 2024 IOTA Stiftung
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ export const INPUT_CLASSES =
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';
8
+ export const INPUT_NUMBER_CLASSES =
9
+ '[appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none disabled:cursor-not-allowed';
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';
@@ -0,0 +1,14 @@
1
+ // Copyright (c) 2024 IOTA Stiftung
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ export enum InputType {
5
+ Text = 'text',
6
+ Password = 'password',
7
+ Number = 'number',
8
+ NumericFormat = 'numericFormat',
9
+ }
10
+
11
+ export enum Resize {
12
+ None = 'none',
13
+ Vertical = 'vertical',
14
+ }
@@ -0,0 +1,29 @@
1
+ // Copyright (c) 2024 IOTA Stiftung
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import type { ComponentProps } from 'react';
5
+ import type { InputType } from '.';
6
+ import type { NumericFormat } from 'react-number-format';
7
+
8
+ type InputElementProps = Omit<
9
+ React.ComponentProps<'input'>,
10
+ 'type' | 'className' | 'ref' | 'value' | 'defaultValue'
11
+ >;
12
+
13
+ type GenericInputProps = {
14
+ type: InputType.Text | InputType.Password | InputType.Number;
15
+ };
16
+
17
+ type NumericFormatProps = ComponentProps<typeof NumericFormat>;
18
+
19
+ export type NumericFormatInputProps = {
20
+ type: InputType.NumericFormat;
21
+ suffix?: NumericFormatProps['suffix'];
22
+ prefix?: NumericFormatProps['prefix'];
23
+ decimalScale?: NumericFormatProps['decimalScale'];
24
+ allowNegative?: NumericFormatProps['allowNegative'];
25
+ thousandSeparator?: NumericFormatProps['thousandSeparator'];
26
+ onValueChange?: NumericFormatProps['onValueChange'];
27
+ };
28
+
29
+ export type InputPropsByType = InputElementProps & (NumericFormatInputProps | GenericInputProps);
@@ -0,0 +1,47 @@
1
+ // Copyright (c) 2024 IOTA Stiftung
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import { NavbarItemHorizontal } from './NavbarItemHorizontal';
5
+ import { NavbarItemVertical } from './NavbarItemVertical';
6
+ import { NavbarItemType } from './navbarItem.enums';
7
+
8
+ export interface NavbarItemProps {
9
+ /**
10
+ * The type of the navbar item.
11
+ */
12
+ type?: NavbarItemType;
13
+ /**
14
+ * The icon of the navbar item.
15
+ */
16
+ icon: React.ReactNode;
17
+ /**
18
+ * The text of the navbar item.
19
+ */
20
+ text?: string;
21
+ /**
22
+ * Indicates if the navbar item is selected.
23
+ */
24
+ isSelected?: boolean;
25
+ /**
26
+ * Indicates if the navbar item has a badge.
27
+ */
28
+ hasBadge?: boolean;
29
+ /**
30
+ * The onClick event of the navbar item.
31
+ */
32
+ onClick?: (e: React.MouseEvent<HTMLDivElement>) => void;
33
+ /**
34
+ * Indicates if the navbar item is disabled.
35
+ */
36
+ isDisabled?: boolean;
37
+ }
38
+
39
+ export function NavbarItem(props: NavbarItemProps): React.JSX.Element {
40
+ const { type, text } = props;
41
+
42
+ if (type === NavbarItemType.Vertical && !!text) {
43
+ return <NavbarItemVertical {...props} />;
44
+ }
45
+
46
+ return <NavbarItemHorizontal {...props} />;
47
+ }
@@ -0,0 +1,65 @@
1
+ // Copyright (c) 2024 IOTA Stiftung
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import cx from 'classnames';
5
+ import {
6
+ BADGE_WITH_TEXT,
7
+ BADGE_WITHOUT_TEXT,
8
+ PADDING_WITH_TEXT,
9
+ PADDING_WITHOUT_TEXT,
10
+ SELECTED_BACKGROUND,
11
+ SELECTED_ICON,
12
+ SELECTED_TEXT,
13
+ UNSELECTED_ICON,
14
+ UNSELECTED_TEXT,
15
+ } from './navbarItem.classes';
16
+ import { Badge, BadgeType } from '@/components/atoms';
17
+ import type { NavbarItemProps } from './NavbarItem';
18
+
19
+ export function NavbarItemHorizontal({
20
+ icon,
21
+ text,
22
+ isSelected,
23
+ hasBadge,
24
+ onClick,
25
+ isDisabled = false,
26
+ }: Omit<NavbarItemProps, 'type'>): React.JSX.Element {
27
+ const fillClasses = isSelected ? SELECTED_ICON : UNSELECTED_ICON;
28
+ const paddingClasses = text ? PADDING_WITH_TEXT : PADDING_WITHOUT_TEXT;
29
+ const backgroundColors = isSelected && SELECTED_BACKGROUND;
30
+ const badgePositionClasses = text ? BADGE_WITH_TEXT : BADGE_WITHOUT_TEXT;
31
+ const textClasses = isSelected ? SELECTED_TEXT : UNSELECTED_TEXT;
32
+ const disabledClasses = isDisabled ? 'cursor-not-allowed opacity-60' : 'state-layer-secondary';
33
+ const handleClick = (e: React.MouseEvent<HTMLDivElement>) => {
34
+ if (isDisabled) {
35
+ e.preventDefault();
36
+ return;
37
+ }
38
+ onClick?.(e);
39
+ };
40
+ return (
41
+ <div
42
+ onClick={handleClick}
43
+ className={cx(
44
+ 'inline-flex cursor-pointer flex-col items-center justify-center space-y-1',
45
+ )}
46
+ >
47
+ <div
48
+ className={cx(
49
+ 'relative inline-flex rounded-full',
50
+ paddingClasses,
51
+ backgroundColors,
52
+ disabledClasses,
53
+ )}
54
+ >
55
+ <div className={cx('inline-flex [&_svg]:h-6 [&_svg]:w-6', fillClasses)}>{icon}</div>
56
+ {hasBadge && (
57
+ <div className={cx('absolute', badgePositionClasses)}>
58
+ <Badge type={BadgeType.PrimarySolid} />
59
+ </div>
60
+ )}
61
+ </div>
62
+ {text && <span className={cx('text-center text-label-md', textClasses)}>{text}</span>}
63
+ </div>
64
+ );
65
+ }