@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,54 @@
|
|
|
1
|
+
// Copyright (c) 2024 IOTA Stiftung
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
import cx from 'classnames';
|
|
5
|
+
import {
|
|
6
|
+
SELECTED_BACKGROUND,
|
|
7
|
+
SELECTED_ICON,
|
|
8
|
+
SELECTED_TEXT,
|
|
9
|
+
UNSELECTED_ICON,
|
|
10
|
+
UNSELECTED_TEXT,
|
|
11
|
+
} from './navbarItem.classes';
|
|
12
|
+
import { Badge, BadgeType } from '@/components/atoms';
|
|
13
|
+
import type { NavbarItemProps } from './NavbarItem';
|
|
14
|
+
|
|
15
|
+
export function NavbarItemVertical({
|
|
16
|
+
icon,
|
|
17
|
+
text,
|
|
18
|
+
isSelected,
|
|
19
|
+
hasBadge,
|
|
20
|
+
onClick,
|
|
21
|
+
isDisabled = false,
|
|
22
|
+
}: Omit<NavbarItemProps, 'type'>): React.JSX.Element {
|
|
23
|
+
const fillClasses = isSelected ? SELECTED_ICON : UNSELECTED_ICON;
|
|
24
|
+
const backgroundColors = isSelected && SELECTED_BACKGROUND;
|
|
25
|
+
const textClasses = isSelected ? SELECTED_TEXT : UNSELECTED_TEXT;
|
|
26
|
+
const disabledClasses = isDisabled
|
|
27
|
+
? 'cursor-not-allowed opacity-60'
|
|
28
|
+
: 'state-layer-secondary cursor-pointer ';
|
|
29
|
+
const handleClick = (e: React.MouseEvent<HTMLDivElement>) => {
|
|
30
|
+
if (isDisabled) {
|
|
31
|
+
e.preventDefault();
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
onClick?.(e);
|
|
35
|
+
};
|
|
36
|
+
return (
|
|
37
|
+
<div
|
|
38
|
+
onClick={handleClick}
|
|
39
|
+
className={cx(
|
|
40
|
+
'relative inline-flex w-full flex-row items-center justify-between rounded-full p-sm',
|
|
41
|
+
backgroundColors,
|
|
42
|
+
disabledClasses,
|
|
43
|
+
)}
|
|
44
|
+
>
|
|
45
|
+
<div className="flex items-center gap-3">
|
|
46
|
+
<div className={cx('inline-flex [&_svg]:h-6 [&_svg]:w-6', fillClasses)}>{icon}</div>
|
|
47
|
+
{text && (
|
|
48
|
+
<span className={cx('text-center text-label-lg', textClasses)}>{text}</span>
|
|
49
|
+
)}
|
|
50
|
+
</div>
|
|
51
|
+
{hasBadge && <Badge type={BadgeType.PrimarySolid} />}
|
|
52
|
+
</div>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Copyright (c) 2024 IOTA Stiftung
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
export const SELECTED_ICON = 'navbar-item-icon-selected-color';
|
|
5
|
+
export const UNSELECTED_ICON = 'navbar-item-icon-color';
|
|
6
|
+
|
|
7
|
+
export const PADDING_WITH_TEXT = 'py-xxs px-sm';
|
|
8
|
+
export const PADDING_WITHOUT_TEXT = 'p-sm';
|
|
9
|
+
|
|
10
|
+
export const SELECTED_BACKGROUND = 'bg-shader-primary-light-12 dark:bg-shader-primary-dark-12';
|
|
11
|
+
|
|
12
|
+
export const BADGE_WITH_TEXT = '-top-3 right-3';
|
|
13
|
+
export const BADGE_WITHOUT_TEXT = '-top-1 right-3';
|
|
14
|
+
|
|
15
|
+
export const SELECTED_TEXT = 'navbar-item-label-selected-color';
|
|
16
|
+
export const UNSELECTED_TEXT = 'navbar-item-label-color';
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
// Copyright (c) 2024 IOTA Stiftung
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
import { useEffect, useRef, useState } from 'react';
|
|
5
|
+
import cx from 'classnames';
|
|
6
|
+
import { Loader, Search as SearchIcon } from '@iota/apps-ui-icons';
|
|
7
|
+
import { Divider } from '@/components/atoms';
|
|
8
|
+
import { SearchBarType } from './search.enums';
|
|
9
|
+
import {
|
|
10
|
+
BACKGROUND_COLORS,
|
|
11
|
+
SUGGESTIONS_WRAPPER_STYLE,
|
|
12
|
+
SEARCH_WRAPPER_STYLE,
|
|
13
|
+
} from './search.classes';
|
|
14
|
+
|
|
15
|
+
export interface Suggestion {
|
|
16
|
+
id: string;
|
|
17
|
+
label: string;
|
|
18
|
+
supportingText?: string;
|
|
19
|
+
type?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface SearchProps {
|
|
23
|
+
/**
|
|
24
|
+
* The value of the search input.
|
|
25
|
+
*/
|
|
26
|
+
searchValue: string;
|
|
27
|
+
/**
|
|
28
|
+
* Callback when the search input value changes.
|
|
29
|
+
*/
|
|
30
|
+
onSearchValueChange: (value: string) => void;
|
|
31
|
+
/**
|
|
32
|
+
* List of suggestions to display (optional).
|
|
33
|
+
*/
|
|
34
|
+
suggestions?: Suggestion[];
|
|
35
|
+
/**
|
|
36
|
+
* Callback when a suggestion is clicked.
|
|
37
|
+
*/
|
|
38
|
+
onSuggestionClick?: (suggestion: Suggestion) => void;
|
|
39
|
+
/**
|
|
40
|
+
* Placeholder text for the search input.
|
|
41
|
+
*/
|
|
42
|
+
placeholder: string;
|
|
43
|
+
/**
|
|
44
|
+
* Are the suggestions loading.
|
|
45
|
+
*/
|
|
46
|
+
isLoading: boolean;
|
|
47
|
+
/**
|
|
48
|
+
* The type of the search bar. Can be 'outlined' or 'filled'.
|
|
49
|
+
*/
|
|
50
|
+
type?: SearchBarType;
|
|
51
|
+
/**
|
|
52
|
+
* Render suggestion.
|
|
53
|
+
*/
|
|
54
|
+
renderSuggestion?: (suggestion: Suggestion, index: number) => React.ReactNode;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function Search({
|
|
58
|
+
searchValue,
|
|
59
|
+
suggestions,
|
|
60
|
+
onSearchValueChange,
|
|
61
|
+
onSuggestionClick,
|
|
62
|
+
placeholder,
|
|
63
|
+
isLoading = false,
|
|
64
|
+
type = SearchBarType.Outlined,
|
|
65
|
+
renderSuggestion,
|
|
66
|
+
}: SearchProps): React.JSX.Element {
|
|
67
|
+
const inputRef = useRef<HTMLInputElement>(null);
|
|
68
|
+
const suggestionsListRef = useRef<HTMLDivElement>(null);
|
|
69
|
+
const [isSuggestionsVisible, setIsSuggestionsVisible] = useState(true);
|
|
70
|
+
const [selectedIndex, setSelectedIndex] = useState<number | null>(null);
|
|
71
|
+
|
|
72
|
+
function handleChange(event: React.ChangeEvent<HTMLInputElement>) {
|
|
73
|
+
const value = event.target.value;
|
|
74
|
+
onSearchValueChange(value);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Hide suggestions on escape key press
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
const handler = (e: KeyboardEvent) => {
|
|
80
|
+
if (e.key === 'Escape') {
|
|
81
|
+
setIsSuggestionsVisible(false);
|
|
82
|
+
inputRef.current?.blur();
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
document.addEventListener('keydown', handler);
|
|
87
|
+
|
|
88
|
+
return () => {
|
|
89
|
+
document.removeEventListener('keydown', handler);
|
|
90
|
+
};
|
|
91
|
+
}, []);
|
|
92
|
+
|
|
93
|
+
// Hide suggestions on click outside
|
|
94
|
+
useEffect(() => {
|
|
95
|
+
const listener = (event: MouseEvent | TouchEvent) => {
|
|
96
|
+
const el = inputRef?.current;
|
|
97
|
+
if (!el || el.contains(event?.target as Node)) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (suggestionsListRef.current?.contains(event.target as Node)) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
setIsSuggestionsVisible(false);
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
document.addEventListener('click', listener, true);
|
|
108
|
+
document.addEventListener('touchstart', listener, true);
|
|
109
|
+
|
|
110
|
+
return () => {
|
|
111
|
+
document.removeEventListener('click', listener, true);
|
|
112
|
+
document.removeEventListener('touchstart', listener, true);
|
|
113
|
+
};
|
|
114
|
+
}, [inputRef]);
|
|
115
|
+
|
|
116
|
+
const showSuggestions = isSuggestionsVisible && suggestions && suggestions.length > 0;
|
|
117
|
+
|
|
118
|
+
const roundedStyleWithSuggestions = showSuggestions
|
|
119
|
+
? cx(
|
|
120
|
+
'rounded-t-3xl border-b',
|
|
121
|
+
type === SearchBarType.Outlined
|
|
122
|
+
? '[&:not(.dark_*,.names_*)]:border-b-transparent'
|
|
123
|
+
: 'border-b-transparent',
|
|
124
|
+
)
|
|
125
|
+
: type === SearchBarType.Outlined
|
|
126
|
+
? 'rounded-3xl border-b'
|
|
127
|
+
: 'rounded-full';
|
|
128
|
+
const searchTypeClass = SEARCH_WRAPPER_STYLE[type];
|
|
129
|
+
const backgroundColorClass = BACKGROUND_COLORS[type];
|
|
130
|
+
const suggestionsStyle = SUGGESTIONS_WRAPPER_STYLE[type];
|
|
131
|
+
|
|
132
|
+
const handleOnSuggestionClick = (suggestion: Suggestion) => {
|
|
133
|
+
onSuggestionClick?.(suggestion);
|
|
134
|
+
onSearchValueChange('');
|
|
135
|
+
setIsSuggestionsVisible(false);
|
|
136
|
+
setSelectedIndex(null);
|
|
137
|
+
inputRef.current?.blur();
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
|
|
141
|
+
if (suggestions && suggestions?.length > 0) {
|
|
142
|
+
if (event.key === 'ArrowDown') {
|
|
143
|
+
event.preventDefault();
|
|
144
|
+
setSelectedIndex((prev) =>
|
|
145
|
+
prev === null || prev >= suggestions.length - 1 ? 0 : prev + 1,
|
|
146
|
+
);
|
|
147
|
+
} else if (event.key === 'ArrowUp') {
|
|
148
|
+
event.preventDefault();
|
|
149
|
+
setSelectedIndex((prev) =>
|
|
150
|
+
prev === null || prev <= 0 ? suggestions.length - 1 : prev - 1,
|
|
151
|
+
);
|
|
152
|
+
} else if (event.key === 'Enter') {
|
|
153
|
+
event.preventDefault();
|
|
154
|
+
if (selectedIndex !== null && suggestions[selectedIndex]) {
|
|
155
|
+
handleOnSuggestionClick(suggestions[selectedIndex]);
|
|
156
|
+
} else if (suggestions.length === 1) {
|
|
157
|
+
handleOnSuggestionClick(suggestions[0]);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
return (
|
|
164
|
+
<div className="relative w-full">
|
|
165
|
+
<div
|
|
166
|
+
className={cx(
|
|
167
|
+
'search-text-color flex items-center overflow-hidden px-md py-sm [&_svg]:h-6 [&_svg]:w-6',
|
|
168
|
+
roundedStyleWithSuggestions,
|
|
169
|
+
searchTypeClass,
|
|
170
|
+
)}
|
|
171
|
+
>
|
|
172
|
+
<input
|
|
173
|
+
ref={inputRef}
|
|
174
|
+
type="text"
|
|
175
|
+
value={searchValue}
|
|
176
|
+
onChange={handleChange}
|
|
177
|
+
onKeyDown={handleKeyDown}
|
|
178
|
+
onFocus={() => setIsSuggestionsVisible(true)}
|
|
179
|
+
placeholder={placeholder}
|
|
180
|
+
className={cx(
|
|
181
|
+
'search-placeholder-color w-full flex-1 outline-none',
|
|
182
|
+
backgroundColorClass,
|
|
183
|
+
)}
|
|
184
|
+
/>
|
|
185
|
+
<SearchIcon />
|
|
186
|
+
</div>
|
|
187
|
+
{showSuggestions && renderSuggestion && (
|
|
188
|
+
<div
|
|
189
|
+
ref={suggestionsListRef}
|
|
190
|
+
className={cx(
|
|
191
|
+
'absolute left-0 top-full flex w-full flex-col items-center overflow-hidden',
|
|
192
|
+
suggestionsStyle,
|
|
193
|
+
)}
|
|
194
|
+
>
|
|
195
|
+
<Divider width="w-11/12" />
|
|
196
|
+
{isLoading ? (
|
|
197
|
+
<div className="px-md py-sm">
|
|
198
|
+
<Loader className="animate-spin" />
|
|
199
|
+
</div>
|
|
200
|
+
) : (
|
|
201
|
+
suggestions.map((suggestion, index) => (
|
|
202
|
+
<div
|
|
203
|
+
key={suggestion.id}
|
|
204
|
+
onClick={() => handleOnSuggestionClick(suggestion)}
|
|
205
|
+
onMouseEnter={() => setSelectedIndex(index)}
|
|
206
|
+
className={cx(
|
|
207
|
+
'w-full cursor-pointer px-md py-sm',
|
|
208
|
+
selectedIndex === index ? 'search-selected-index-bg-color' : '',
|
|
209
|
+
)}
|
|
210
|
+
>
|
|
211
|
+
{renderSuggestion(suggestion, index)}
|
|
212
|
+
</div>
|
|
213
|
+
))
|
|
214
|
+
)}
|
|
215
|
+
</div>
|
|
216
|
+
)}
|
|
217
|
+
</div>
|
|
218
|
+
);
|
|
219
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Copyright (c) 2024 IOTA Stiftung
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
import { SearchBarType } from './search.enums';
|
|
5
|
+
|
|
6
|
+
export const BACKGROUND_COLORS: Record<SearchBarType, string> = {
|
|
7
|
+
[SearchBarType.Outlined]: 'bg-transparent',
|
|
8
|
+
[SearchBarType.Filled]: 'search-bg-color-filled',
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const SEARCH_WRAPPER_STYLE: Record<SearchBarType, string> = {
|
|
12
|
+
[SearchBarType.Outlined]: 'border-l border-r border-t search-border-color',
|
|
13
|
+
[SearchBarType.Filled]: 'search-bg-color-filled',
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const SUGGESTIONS_WRAPPER_STYLE: Record<SearchBarType, string> = {
|
|
17
|
+
[SearchBarType.Outlined]:
|
|
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
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Copyright (c) 2024 IOTA Stiftung
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
import type { PropsWithChildren } from 'react';
|
|
5
|
+
import { BACKGROUND_COLORS, OUTLINED_BORDER } from './segmentedButton.classes';
|
|
6
|
+
import cx from 'classnames';
|
|
7
|
+
import { SegmentedButtonType } from './segmentedButton.enums';
|
|
8
|
+
import { ButtonSegmentType } from '@/components/atoms';
|
|
9
|
+
|
|
10
|
+
interface SegmentedButtonProps {
|
|
11
|
+
/**
|
|
12
|
+
* The type of the button
|
|
13
|
+
*/
|
|
14
|
+
type?: SegmentedButtonType;
|
|
15
|
+
/**
|
|
16
|
+
* The shape of the button
|
|
17
|
+
*/
|
|
18
|
+
shape?: ButtonSegmentType;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function SegmentedButton({
|
|
22
|
+
type = SegmentedButtonType.Filled,
|
|
23
|
+
children,
|
|
24
|
+
shape = ButtonSegmentType.Rounded,
|
|
25
|
+
}: PropsWithChildren<SegmentedButtonProps>): React.JSX.Element {
|
|
26
|
+
const backgroundColors = BACKGROUND_COLORS[type];
|
|
27
|
+
const borderColors = type === SegmentedButtonType.Outlined ? OUTLINED_BORDER : '';
|
|
28
|
+
const borderShape = shape === ButtonSegmentType.Rounded ? 'rounded-full gap-1 p-xxs' : '';
|
|
29
|
+
const scrollable =
|
|
30
|
+
shape === ButtonSegmentType.Rounded ? 'flex-wrap' : 'flex-nowrap overflow-x-auto';
|
|
31
|
+
return (
|
|
32
|
+
<div
|
|
33
|
+
className={cx('flex flex-row', backgroundColors, borderColors, borderShape, scrollable)}
|
|
34
|
+
>
|
|
35
|
+
{children}
|
|
36
|
+
</div>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Copyright (c) 2024 IOTA Stiftung
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
import { SegmentedButtonType } from './segmentedButton.enums';
|
|
5
|
+
|
|
6
|
+
export const BACKGROUND_COLORS: Record<SegmentedButtonType, string> = {
|
|
7
|
+
[SegmentedButtonType.Outlined]: 'bg-transparent',
|
|
8
|
+
[SegmentedButtonType.Filled]: 'segmented-filled-bg-color',
|
|
9
|
+
[SegmentedButtonType.Transparent]: 'bg-transparent',
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const OUTLINED_BORDER = 'border segmented-outlined-border';
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
// Copyright (c) 2024 IOTA Stiftung
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
import { TriangleDown } from '@iota/apps-ui-icons';
|
|
5
|
+
import cx from 'classnames';
|
|
6
|
+
import { forwardRef, useEffect, useRef, useState } from 'react';
|
|
7
|
+
import { Dropdown } from '../dropdown/Dropdown';
|
|
8
|
+
import { SecondaryText } from '@/components/atoms/secondary-text';
|
|
9
|
+
import { InputWrapper, LabelHtmlTag } from '../input/InputWrapper';
|
|
10
|
+
import { ListItem, ButtonUnstyled } from '@/components/atoms';
|
|
11
|
+
import { DropdownPosition } from '../dropdown';
|
|
12
|
+
import { SelectSize } from './select.enums';
|
|
13
|
+
|
|
14
|
+
export type SelectOption =
|
|
15
|
+
| string
|
|
16
|
+
| { id: string; renderLabel: () => React.JSX.Element }
|
|
17
|
+
| { id: string; label: React.ReactNode };
|
|
18
|
+
|
|
19
|
+
interface SelectProps extends Pick<React.HTMLProps<HTMLSelectElement>, 'disabled'> {
|
|
20
|
+
/**
|
|
21
|
+
* The selected option value.
|
|
22
|
+
*/
|
|
23
|
+
value?: string;
|
|
24
|
+
/**
|
|
25
|
+
* The field label.
|
|
26
|
+
*/
|
|
27
|
+
label?: string;
|
|
28
|
+
/**
|
|
29
|
+
* The field caption.
|
|
30
|
+
*/
|
|
31
|
+
caption?: string;
|
|
32
|
+
/**
|
|
33
|
+
* The dropdown elements to render.
|
|
34
|
+
*/
|
|
35
|
+
options: SelectOption[];
|
|
36
|
+
/**
|
|
37
|
+
* The icon to show on the left of the field.
|
|
38
|
+
*/
|
|
39
|
+
leadingIcon?: React.ReactNode;
|
|
40
|
+
/**
|
|
41
|
+
* The supporting text to shown at the end of the selector.
|
|
42
|
+
*/
|
|
43
|
+
supportingText?: string;
|
|
44
|
+
/**
|
|
45
|
+
* The error message to show under the field.
|
|
46
|
+
*/
|
|
47
|
+
errorMessage?: string;
|
|
48
|
+
/**
|
|
49
|
+
* Placeholder for the selector
|
|
50
|
+
*/
|
|
51
|
+
placeholder?: SelectOption;
|
|
52
|
+
/**
|
|
53
|
+
* The callback to call when the value changes.
|
|
54
|
+
*/
|
|
55
|
+
onValueChange?: (id: string) => void;
|
|
56
|
+
/**
|
|
57
|
+
* The callback to call when the option is clicked.
|
|
58
|
+
*/
|
|
59
|
+
onOptionClick?: (id: string) => void;
|
|
60
|
+
/**
|
|
61
|
+
* The dropdown position
|
|
62
|
+
*/
|
|
63
|
+
dropdownPosition?: DropdownPosition;
|
|
64
|
+
/**
|
|
65
|
+
* The size of the select.
|
|
66
|
+
*/
|
|
67
|
+
size?: SelectSize;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export const Select = forwardRef<HTMLButtonElement, SelectProps>(
|
|
71
|
+
(
|
|
72
|
+
{
|
|
73
|
+
disabled,
|
|
74
|
+
label,
|
|
75
|
+
leadingIcon,
|
|
76
|
+
supportingText,
|
|
77
|
+
errorMessage,
|
|
78
|
+
caption,
|
|
79
|
+
options,
|
|
80
|
+
placeholder,
|
|
81
|
+
onValueChange,
|
|
82
|
+
onOptionClick,
|
|
83
|
+
value,
|
|
84
|
+
dropdownPosition = DropdownPosition.Bottom,
|
|
85
|
+
size = SelectSize.Default,
|
|
86
|
+
},
|
|
87
|
+
ref,
|
|
88
|
+
) => {
|
|
89
|
+
const [isOpen, setIsOpen] = useState<boolean>(false);
|
|
90
|
+
const selectedValue = findValueByProps(value, options);
|
|
91
|
+
const wrapperRef = useRef<HTMLDivElement>(null);
|
|
92
|
+
|
|
93
|
+
const selectorText = selectedValue || placeholder;
|
|
94
|
+
const selectPadding = size === SelectSize.Small ? 'px-sm' : 'px-md';
|
|
95
|
+
const textSize = size === SelectSize.Small ? 'text-body-md' : 'text-body-lg';
|
|
96
|
+
|
|
97
|
+
useEffect(() => {
|
|
98
|
+
if (disabled && isOpen) {
|
|
99
|
+
closeDropdown();
|
|
100
|
+
}
|
|
101
|
+
}, [disabled, isOpen]);
|
|
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
|
+
|
|
120
|
+
function findValueByProps(value: SelectProps['value'], options: SelectOption[] = []) {
|
|
121
|
+
return (
|
|
122
|
+
options.find((option) =>
|
|
123
|
+
typeof option === 'string' ? option === value : option.id === value,
|
|
124
|
+
) ?? options[0]
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function onSelectorClick() {
|
|
129
|
+
setIsOpen((prev) => !prev);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function handleOptionClick(option: SelectOption) {
|
|
133
|
+
closeDropdown();
|
|
134
|
+
const clickedOption = typeof option === 'string' ? option : option.id;
|
|
135
|
+
onOptionClick?.(clickedOption);
|
|
136
|
+
|
|
137
|
+
if (option !== selectedValue) {
|
|
138
|
+
onValueChange?.(clickedOption);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function closeDropdown() {
|
|
143
|
+
setIsOpen(false);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return (
|
|
147
|
+
<InputWrapper
|
|
148
|
+
label={label}
|
|
149
|
+
caption={caption}
|
|
150
|
+
disabled={disabled}
|
|
151
|
+
errorMessage={errorMessage}
|
|
152
|
+
labelHtmlTag={LabelHtmlTag.Div}
|
|
153
|
+
>
|
|
154
|
+
<div className="relative flex w-full flex-col" ref={wrapperRef}>
|
|
155
|
+
<ButtonUnstyled
|
|
156
|
+
ref={ref}
|
|
157
|
+
onClick={onSelectorClick}
|
|
158
|
+
disabled={disabled}
|
|
159
|
+
className={cx(
|
|
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',
|
|
161
|
+
selectPadding,
|
|
162
|
+
)}
|
|
163
|
+
>
|
|
164
|
+
{leadingIcon && <span className="select-icon-color">{leadingIcon}</span>}
|
|
165
|
+
|
|
166
|
+
<div className="flex w-full flex-row items-baseline gap-x-3">
|
|
167
|
+
{selectorText && (
|
|
168
|
+
<div
|
|
169
|
+
className={cx(
|
|
170
|
+
'select-label-color block w-full text-start',
|
|
171
|
+
textSize,
|
|
172
|
+
)}
|
|
173
|
+
>
|
|
174
|
+
<OptionLabel option={selectorText} />
|
|
175
|
+
</div>
|
|
176
|
+
)}
|
|
177
|
+
|
|
178
|
+
{supportingText && (
|
|
179
|
+
<div className="ml-auto">
|
|
180
|
+
<SecondaryText>{supportingText}</SecondaryText>
|
|
181
|
+
</div>
|
|
182
|
+
)}
|
|
183
|
+
</div>
|
|
184
|
+
|
|
185
|
+
<TriangleDown
|
|
186
|
+
className={cx('select-icon-color transition-transform', {
|
|
187
|
+
' rotate-180': isOpen,
|
|
188
|
+
})}
|
|
189
|
+
/>
|
|
190
|
+
</ButtonUnstyled>
|
|
191
|
+
|
|
192
|
+
{isOpen && (
|
|
193
|
+
<div
|
|
194
|
+
className="fixed left-0 top-0 z-[49] bg-transparent"
|
|
195
|
+
onClick={closeDropdown}
|
|
196
|
+
/>
|
|
197
|
+
)}
|
|
198
|
+
<div
|
|
199
|
+
className={cx('absolute z-50 min-w-full', {
|
|
200
|
+
hidden: !isOpen,
|
|
201
|
+
'top-full':
|
|
202
|
+
!dropdownPosition || dropdownPosition === DropdownPosition.Bottom,
|
|
203
|
+
'bottom-full': dropdownPosition === DropdownPosition.Top,
|
|
204
|
+
})}
|
|
205
|
+
>
|
|
206
|
+
<Dropdown>
|
|
207
|
+
{options.map((option) => {
|
|
208
|
+
const optionIsString = typeof option === 'string';
|
|
209
|
+
return (
|
|
210
|
+
<ListItem
|
|
211
|
+
onClick={() => handleOptionClick(option)}
|
|
212
|
+
hideBottomBorder
|
|
213
|
+
key={optionIsString ? option : option.id}
|
|
214
|
+
>
|
|
215
|
+
<OptionLabel option={option} />
|
|
216
|
+
</ListItem>
|
|
217
|
+
);
|
|
218
|
+
})}
|
|
219
|
+
</Dropdown>
|
|
220
|
+
</div>
|
|
221
|
+
</div>
|
|
222
|
+
</InputWrapper>
|
|
223
|
+
);
|
|
224
|
+
},
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
function OptionLabel({ option }: { option: SelectOption }) {
|
|
228
|
+
if (typeof option === 'string') {
|
|
229
|
+
return option;
|
|
230
|
+
} else if ('renderLabel' in option) {
|
|
231
|
+
return option.renderLabel();
|
|
232
|
+
} else {
|
|
233
|
+
return option.label;
|
|
234
|
+
}
|
|
235
|
+
}
|