@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.
- package/CHANGELOG.md +145 -0
- package/LICENSE +201 -0
- package/README.md +16 -0
- package/dist/index.d.ts +1911 -0
- package/dist/index.es.js +6486 -0
- package/dist/index.es.js.map +1 -0
- package/dist/index.umd.js +66 -0
- package/dist/index.umd.js.map +1 -0
- package/dist/style.css +1 -0
- package/package.json +80 -0
- package/src/lib/components/atoms/badge/Badge.tsx +37 -0
- package/src/lib/components/atoms/badge/badge.classes.ts +33 -0
- package/src/lib/components/atoms/badge/badge.enums.ts +11 -0
- package/src/lib/components/atoms/badge/index.ts +6 -0
- package/src/lib/components/atoms/button/Button.tsx +110 -0
- package/src/lib/components/atoms/button/button-variants/ButtonPill.tsx +23 -0
- package/src/lib/components/atoms/button/button-variants/ButtonUnstyled.tsx +27 -0
- package/src/lib/components/atoms/button/button-variants/buttonVariants.types.ts +11 -0
- package/src/lib/components/atoms/button/button-variants/index.ts +5 -0
- package/src/lib/components/atoms/button/button.classes.ts +53 -0
- package/src/lib/components/atoms/button/button.enums.ts +21 -0
- package/src/lib/components/atoms/button/index.ts +7 -0
- package/src/lib/components/atoms/button-segment/ButtonSegment.tsx +98 -0
- package/src/lib/components/atoms/button-segment/buttonSegment.classes.ts +24 -0
- package/src/lib/components/atoms/button-segment/buttonSegment.enums.ts +7 -0
- package/src/lib/components/atoms/button-segment/index.ts +5 -0
- package/src/lib/components/atoms/checkbox/Checkbox.tsx +124 -0
- package/src/lib/components/atoms/checkbox/index.ts +4 -0
- package/src/lib/components/atoms/divider/Divider.tsx +48 -0
- package/src/lib/components/atoms/divider/divider.classes.ts +11 -0
- package/src/lib/components/atoms/divider/divider.enums.ts +7 -0
- package/src/lib/components/atoms/divider/index.ts +6 -0
- package/src/lib/components/atoms/header/Header.tsx +73 -0
- package/src/lib/components/atoms/header/index.ts +4 -0
- package/src/lib/components/atoms/image/Image.tsx +32 -0
- package/src/lib/components/atoms/image/index.ts +4 -0
- package/src/lib/components/atoms/index.ts +26 -0
- package/src/lib/components/atoms/info-box/InfoBox.tsx +68 -0
- package/src/lib/components/atoms/info-box/index.ts +5 -0
- package/src/lib/components/atoms/info-box/infoBox.classes.ts +18 -0
- package/src/lib/components/atoms/info-box/infoBox.enums.ts +14 -0
- package/src/lib/components/atoms/key-value-info/KeyValueInfo.tsx +157 -0
- package/src/lib/components/atoms/key-value-info/index.ts +6 -0
- package/src/lib/components/atoms/key-value-info/keyValue.enums.ts +7 -0
- package/src/lib/components/atoms/label-text/LabelText.tsx +92 -0
- package/src/lib/components/atoms/label-text/index.ts +5 -0
- package/src/lib/components/atoms/label-text/labelText.classes.ts +22 -0
- package/src/lib/components/atoms/label-text/labelText.enums.ts +8 -0
- package/src/lib/components/atoms/list-item/ListItem.tsx +88 -0
- package/src/lib/components/atoms/list-item/index.ts +4 -0
- package/src/lib/components/atoms/loading-indicator/LoadingIndicator.tsx +38 -0
- package/src/lib/components/atoms/loading-indicator/index.ts +4 -0
- package/src/lib/components/atoms/media-fallback/MediaFallback.tsx +12 -0
- package/src/lib/components/atoms/media-fallback/index.ts +4 -0
- package/src/lib/components/atoms/panel/Panel.tsx +28 -0
- package/src/lib/components/atoms/panel/index.ts +4 -0
- package/src/lib/components/atoms/placeholder/Placeholder.tsx +27 -0
- package/src/lib/components/atoms/placeholder/index.ts +4 -0
- package/src/lib/components/atoms/radio-button/RadioButton.tsx +64 -0
- package/src/lib/components/atoms/radio-button/index.ts +4 -0
- package/src/lib/components/atoms/secondary-text/SecondaryText.tsx +29 -0
- package/src/lib/components/atoms/secondary-text/index.ts +4 -0
- package/src/lib/components/atoms/skeleton/Skeleton.tsx +30 -0
- package/src/lib/components/atoms/skeleton/index.ts +4 -0
- package/src/lib/components/atoms/snackbar/Snackbar.tsx +69 -0
- package/src/lib/components/atoms/snackbar/index.ts +5 -0
- package/src/lib/components/atoms/snackbar/snackbar.classes.ts +18 -0
- package/src/lib/components/atoms/snackbar/snackbar.enums.tsx +9 -0
- package/src/lib/components/atoms/toggle/Toggle.tsx +140 -0
- package/src/lib/components/atoms/toggle/index.ts +6 -0
- package/src/lib/components/atoms/toggle/toggle.classes.ts +41 -0
- package/src/lib/components/atoms/toggle/toggle.enums.ts +12 -0
- package/src/lib/components/atoms/tooltip/Tooltip.tsx +139 -0
- package/src/lib/components/atoms/tooltip/index.ts +5 -0
- package/src/lib/components/atoms/tooltip/tooltip.classes.ts +11 -0
- package/src/lib/components/atoms/tooltip/tooltip.enums.ts +9 -0
- package/src/lib/components/atoms/video/Video.tsx +33 -0
- package/src/lib/components/atoms/video/index.ts +4 -0
- package/src/lib/components/atoms/video/video.constants.ts +31 -0
- package/src/lib/components/index.ts +6 -0
- package/src/lib/components/molecules/account/Account.tsx +195 -0
- package/src/lib/components/molecules/account/index.ts +4 -0
- package/src/lib/components/molecules/address/Address.tsx +101 -0
- package/src/lib/components/molecules/address/index.ts +4 -0
- package/src/lib/components/molecules/asset-media-renderer/AssetMediaRenderer.tsx +50 -0
- package/src/lib/components/molecules/asset-media-renderer/index.ts +4 -0
- package/src/lib/components/molecules/card/Card.tsx +70 -0
- package/src/lib/components/molecules/card/CardAction.tsx +78 -0
- package/src/lib/components/molecules/card/CardBody.tsx +73 -0
- package/src/lib/components/molecules/card/CardImage.tsx +43 -0
- package/src/lib/components/molecules/card/CardImagePlaceholder.tsx +146 -0
- package/src/lib/components/molecules/card/card.classes.ts +27 -0
- package/src/lib/components/molecules/card/card.enums.ts +26 -0
- package/src/lib/components/molecules/card/index.ts +8 -0
- package/src/lib/components/molecules/chip/Chip.tsx +146 -0
- package/src/lib/components/molecules/chip/chip.classes.ts +54 -0
- package/src/lib/components/molecules/chip/chip.enums.ts +15 -0
- package/src/lib/components/molecules/chip/index.ts +5 -0
- package/src/lib/components/molecules/display-stats/DisplayStats.tsx +142 -0
- package/src/lib/components/molecules/display-stats/displayStats.classes.ts +36 -0
- package/src/lib/components/molecules/display-stats/displayStats.enums.ts +13 -0
- package/src/lib/components/molecules/display-stats/index.ts +5 -0
- package/src/lib/components/molecules/dropdown/Dropdown.tsx +10 -0
- package/src/lib/components/molecules/dropdown/dropdownPosition.enums.ts +7 -0
- package/src/lib/components/molecules/dropdown/index.ts +5 -0
- package/src/lib/components/molecules/index.ts +19 -0
- package/src/lib/components/molecules/input/Input.tsx +261 -0
- package/src/lib/components/molecules/input/InputWrapper.tsx +97 -0
- package/src/lib/components/molecules/input/TextArea.tsx +148 -0
- package/src/lib/components/molecules/input/index.ts +8 -0
- package/src/lib/components/molecules/input/input.classes.ts +13 -0
- package/src/lib/components/molecules/input/input.enums.ts +14 -0
- package/src/lib/components/molecules/input/input.types.ts +29 -0
- package/src/lib/components/molecules/navbar-item/NavbarItem.tsx +47 -0
- package/src/lib/components/molecules/navbar-item/NavbarItemHorizontal.tsx +65 -0
- package/src/lib/components/molecules/navbar-item/NavbarItemVertical.tsx +54 -0
- package/src/lib/components/molecules/navbar-item/index.ts +5 -0
- package/src/lib/components/molecules/navbar-item/navbarItem.classes.ts +16 -0
- package/src/lib/components/molecules/navbar-item/navbarItem.enums.ts +7 -0
- package/src/lib/components/molecules/search/Search.tsx +219 -0
- package/src/lib/components/molecules/search/index.ts +5 -0
- package/src/lib/components/molecules/search/search.classes.ts +20 -0
- package/src/lib/components/molecules/search/search.enums.ts +7 -0
- package/src/lib/components/molecules/segmented-button/SegmentedButton.tsx +38 -0
- package/src/lib/components/molecules/segmented-button/index.ts +6 -0
- package/src/lib/components/molecules/segmented-button/segmentedButton.classes.ts +12 -0
- package/src/lib/components/molecules/segmented-button/segmentedButton.enums.ts +8 -0
- package/src/lib/components/molecules/select/Select.tsx +235 -0
- package/src/lib/components/molecules/select/index.ts +5 -0
- package/src/lib/components/molecules/select/select.enums.ts +7 -0
- package/src/lib/components/molecules/table-cell/TableCell.tsx +62 -0
- package/src/lib/components/molecules/table-cell/index.ts +4 -0
- package/src/lib/components/molecules/table-header-cell/TableHeaderCell.tsx +140 -0
- package/src/lib/components/molecules/table-header-cell/index.ts +5 -0
- package/src/lib/components/molecules/table-header-cell/tableHeaderCell.enums.ts +7 -0
- package/src/lib/components/molecules/title/Title.tsx +78 -0
- package/src/lib/components/molecules/title/index.ts +5 -0
- package/src/lib/components/molecules/title/titleClasses.constants.ts +14 -0
- package/src/lib/components/molecules/title/titleSize.enums.ts +7 -0
- package/src/lib/components/molecules/visual-asset-card/VisualAssetCard.tsx +89 -0
- package/src/lib/components/molecules/visual-asset-card/index.ts +5 -0
- package/src/lib/components/molecules/visual-asset-card/visualAssetCard.enums.ts +7 -0
- package/src/lib/components/organisms/accordion/Accordion.tsx +108 -0
- package/src/lib/components/organisms/accordion/accordion.classes.ts +4 -0
- package/src/lib/components/organisms/accordion/index.ts +4 -0
- package/src/lib/components/organisms/dialog/Dialog.tsx +157 -0
- package/src/lib/components/organisms/dialog/dialog.enums.ts +7 -0
- package/src/lib/components/organisms/dialog/index.ts +5 -0
- package/src/lib/components/organisms/index.ts +7 -0
- package/src/lib/components/organisms/navbar/Navbar.tsx +101 -0
- package/src/lib/components/organisms/navbar/NavbarSlideout.tsx +59 -0
- package/src/lib/components/organisms/navbar/index.ts +5 -0
- package/src/lib/components/organisms/table/Table.tsx +234 -0
- package/src/lib/components/organisms/table/TableContext.tsx +56 -0
- package/src/lib/components/organisms/table/index.ts +4 -0
- package/src/lib/constants/colors.constants.ts +239 -0
- package/src/lib/constants/index.ts +4 -0
- package/src/lib/enums/index.ts +5 -0
- package/src/lib/enums/screenSize.enums.ts +9 -0
- package/src/lib/enums/theme.enums.ts +8 -0
- package/src/lib/index.ts +10 -0
- package/src/lib/styles/base.css +144 -0
- package/src/lib/styles/components.css +735 -0
- package/src/lib/styles/fonts.css +7 -0
- package/src/lib/styles/index.css +18 -0
- package/src/lib/styles/utilities.css +61 -0
- package/src/lib/styles/variables/dark-variables.css +262 -0
- package/src/lib/styles/variables/general-variables.css +9 -0
- package/src/lib/styles/variables/light-variables.css +276 -0
- package/src/lib/styles/variables/names-variables.css +270 -0
- package/src/lib/tailwind/base.presets.ts +58 -0
- package/src/lib/tailwind/constants/customColors.constants.ts +13 -0
- package/src/lib/tailwind/constants/fontFamily.constants.ts +8 -0
- package/src/lib/tailwind/constants/fontSizes.constants.ts +201 -0
- package/src/lib/tailwind/constants/gradients.constants.ts +6 -0
- package/src/lib/tailwind/constants/index.ts +7 -0
- package/src/lib/tailwind/constants/scaling.constants.ts +41 -0
- package/src/lib/tailwind/constants/variableSpacing.constants.ts +66 -0
- package/src/lib/tailwind/helpers.ts +20 -0
- package/src/lib/tailwind/index.ts +5 -0
- package/src/lib/tailwind/plugins.ts +12 -0
- package/src/lib/tailwind/responsive.presets.ts +36 -0
- package/src/lib/tailwind/static.presets.ts +25 -0
- package/src/lib/tailwind/types.ts +6 -0
- package/src/lib/utils/format.ts +8 -0
- package/src/lib/utils/index.ts +4 -0
- package/src/storybook/blocks/Showcase.tsx +20 -0
- package/src/storybook/blocks/TypeSet.tsx +34 -0
- package/src/storybook/blocks/TypeSetItem.tsx +74 -0
- package/src/storybook/blocks/index.ts +6 -0
- package/src/storybook/constants/gradients.constants.ts +8 -0
- package/src/storybook/constants/index.ts +5 -0
- package/src/storybook/constants/typeset.constants.ts +39 -0
- package/src/storybook/constants/variableSpacing.constants.ts +6 -0
- package/src/storybook/helpers/formatGradientNames.ts +15 -0
- package/src/storybook/helpers/getFontSizeLabel.ts +15 -0
- package/src/storybook/helpers/index.ts +4 -0
- package/src/storybook/stories/atoms/Badge.stories.tsx +32 -0
- package/src/storybook/stories/atoms/Button.stories.tsx +45 -0
- package/src/storybook/stories/atoms/ButtonSegment.stories.tsx +45 -0
- package/src/storybook/stories/atoms/Checkbox.stories.tsx +34 -0
- package/src/storybook/stories/atoms/CustomScrollbar.stories.tsx +22 -0
- package/src/storybook/stories/atoms/Divider.stories.tsx +41 -0
- package/src/storybook/stories/atoms/Header.stories.tsx +58 -0
- package/src/storybook/stories/atoms/InfoBox.stories.tsx +67 -0
- package/src/storybook/stories/atoms/KeyValueInfo.stories.tsx +59 -0
- package/src/storybook/stories/atoms/LabelText.stories.tsx +55 -0
- package/src/storybook/stories/atoms/ListItem.stories.tsx +41 -0
- package/src/storybook/stories/atoms/LoadingIndicator.stories.tsx +32 -0
- package/src/storybook/stories/atoms/Panel.stories.tsx +36 -0
- package/src/storybook/stories/atoms/Placeholder.stories.tsx +33 -0
- package/src/storybook/stories/atoms/RadioButton.stories.tsx +51 -0
- package/src/storybook/stories/atoms/Skeleton.stories.tsx +33 -0
- package/src/storybook/stories/atoms/Snackbar.stories.tsx +43 -0
- package/src/storybook/stories/atoms/Toggle.stories.tsx +59 -0
- package/src/storybook/stories/atoms/Tooltip.stories.tsx +59 -0
- package/src/storybook/stories/atoms/VisualAssetCard.stories.tsx +57 -0
- package/src/storybook/stories/components/DocsSyncTheme.tsx +20 -0
- package/src/storybook/stories/components/VariableSpacingShowcase.tsx +46 -0
- package/src/storybook/stories/components/index.ts +5 -0
- package/src/storybook/stories/design-tokens/colors.stories.mdx +44 -0
- package/src/storybook/stories/design-tokens/icons.stories.mdx +19 -0
- package/src/storybook/stories/design-tokens/typeset.stories.mdx +17 -0
- package/src/storybook/stories/design-tokens/variable-spacing.stories.mdx +11 -0
- package/src/storybook/stories/molecules/Account.stories.tsx +71 -0
- package/src/storybook/stories/molecules/Address.stories.tsx +39 -0
- package/src/storybook/stories/molecules/Card.stories.tsx +112 -0
- package/src/storybook/stories/molecules/Chip.stories.tsx +127 -0
- package/src/storybook/stories/molecules/DisplayStats.stories.tsx +63 -0
- package/src/storybook/stories/molecules/Dropdown.stories.tsx +38 -0
- package/src/storybook/stories/molecules/Input.stories.tsx +239 -0
- package/src/storybook/stories/molecules/NavbarItem.stories.tsx +45 -0
- package/src/storybook/stories/molecules/Search.stories.tsx +101 -0
- package/src/storybook/stories/molecules/SegmentedButton.stories.tsx +108 -0
- package/src/storybook/stories/molecules/Select.stories.tsx +103 -0
- package/src/storybook/stories/molecules/TableCell.stories.tsx +46 -0
- package/src/storybook/stories/molecules/TableHeaderCell.stories.tsx +34 -0
- package/src/storybook/stories/molecules/TextArea.stories.tsx +80 -0
- package/src/storybook/stories/molecules/Title.stories.tsx +76 -0
- package/src/storybook/stories/organisms/Accordion.stories.tsx +70 -0
- package/src/storybook/stories/organisms/Dialog.stories.tsx +129 -0
- package/src/storybook/stories/organisms/Navbar.stories.tsx +78 -0
- 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,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
|
+
}
|