@borisj74/bv-ds 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +94 -0
- package/dist/index.cjs +33885 -0
- package/dist/index.d.cts +2715 -0
- package/dist/index.d.ts +2715 -0
- package/dist/index.js +33717 -0
- package/package.json +67 -0
- package/src/components/ActivityFeed/ActivityFeed.tsx +48 -0
- package/src/components/ActivityFeed/index.ts +2 -0
- package/src/components/ActivityGauge/ActivityGauge.tsx +155 -0
- package/src/components/ActivityGauge/index.ts +7 -0
- package/src/components/AdvancedFilterBar/AdvancedFilterBar.tsx +80 -0
- package/src/components/AdvancedFilterBar/index.ts +2 -0
- package/src/components/Alert/Alert.tsx +210 -0
- package/src/components/Alert/index.ts +2 -0
- package/src/components/Avatar/Avatar.tsx +111 -0
- package/src/components/Avatar/index.ts +2 -0
- package/src/components/AvatarAddButton/AvatarAddButton.tsx +65 -0
- package/src/components/AvatarAddButton/index.ts +5 -0
- package/src/components/AvatarGroup/AvatarGroup.tsx +79 -0
- package/src/components/AvatarGroup/index.ts +6 -0
- package/src/components/AvatarLabelGroup/AvatarLabelGroup.tsx +62 -0
- package/src/components/AvatarLabelGroup/index.ts +5 -0
- package/src/components/AvatarProfilePhoto/AvatarProfilePhoto.tsx +117 -0
- package/src/components/AvatarProfilePhoto/index.ts +5 -0
- package/src/components/Badge/ColorBadge.tsx +36 -0
- package/src/components/Badge/ModernBadge.tsx +38 -0
- package/src/components/Badge/PillBadge.tsx +36 -0
- package/src/components/Badge/badgeShared.tsx +139 -0
- package/src/components/Badge/index.ts +7 -0
- package/src/components/BadgeCloseX/BadgeCloseX.tsx +64 -0
- package/src/components/BadgeCloseX/index.ts +2 -0
- package/src/components/BadgeGroup/BadgeGroup.tsx +61 -0
- package/src/components/BadgeGroup/index.ts +7 -0
- package/src/components/BreadcrumbButtonBase/BreadcrumbButtonBase.tsx +75 -0
- package/src/components/BreadcrumbButtonBase/index.ts +5 -0
- package/src/components/Breadcrumbs/Breadcrumbs.tsx +62 -0
- package/src/components/Breadcrumbs/index.ts +2 -0
- package/src/components/Button/Button.tsx +71 -0
- package/src/components/Button/index.ts +2 -0
- package/src/components/ButtonCloseX/ButtonCloseX.tsx +54 -0
- package/src/components/ButtonCloseX/index.ts +2 -0
- package/src/components/ButtonDestructive/ButtonDestructive.tsx +67 -0
- package/src/components/ButtonDestructive/index.ts +6 -0
- package/src/components/ButtonGroup/ButtonGroup.tsx +28 -0
- package/src/components/ButtonGroup/index.ts +2 -0
- package/src/components/ButtonGroupSegment/ButtonGroupSegment.tsx +54 -0
- package/src/components/ButtonGroupSegment/index.ts +5 -0
- package/src/components/ButtonUtility/ButtonUtility.tsx +67 -0
- package/src/components/ButtonUtility/index.ts +6 -0
- package/src/components/CalendarCell/CalendarCell.tsx +82 -0
- package/src/components/CalendarCell/index.ts +2 -0
- package/src/components/CalendarCellDayWeekView/CalendarCellDayWeekView.tsx +56 -0
- package/src/components/CalendarCellDayWeekView/index.ts +2 -0
- package/src/components/CalendarColumnHeader/CalendarColumnHeader.tsx +45 -0
- package/src/components/CalendarColumnHeader/index.ts +5 -0
- package/src/components/CalendarDateIcon/CalendarDateIcon.tsx +25 -0
- package/src/components/CalendarDateIcon/index.ts +2 -0
- package/src/components/CalendarEvent/CalendarEvent.tsx +76 -0
- package/src/components/CalendarEvent/index.ts +2 -0
- package/src/components/CalendarEventDayWeekView/CalendarEventDayWeekView.tsx +76 -0
- package/src/components/CalendarEventDayWeekView/index.ts +2 -0
- package/src/components/CalendarHeader/CalendarHeader.tsx +47 -0
- package/src/components/CalendarHeader/index.ts +2 -0
- package/src/components/CalendarRowLabel/CalendarRowLabel.tsx +21 -0
- package/src/components/CalendarRowLabel/index.ts +2 -0
- package/src/components/CalendarTimemarker/CalendarTimemarker.tsx +46 -0
- package/src/components/CalendarTimemarker/index.ts +5 -0
- package/src/components/CalendarViewDropdown/CalendarViewDropdown.tsx +101 -0
- package/src/components/CalendarViewDropdown/index.ts +6 -0
- package/src/components/CardHeader/CardHeader.tsx +57 -0
- package/src/components/CardHeader/index.ts +2 -0
- package/src/components/CarouselArrow/CarouselArrow.tsx +47 -0
- package/src/components/CarouselArrow/index.ts +6 -0
- package/src/components/CarouselImage/CarouselImage.tsx +60 -0
- package/src/components/CarouselImage/index.ts +2 -0
- package/src/components/Change/Change.tsx +73 -0
- package/src/components/Change/index.ts +2 -0
- package/src/components/ChartLegend/ChartLegend.tsx +38 -0
- package/src/components/ChartLegend/index.ts +2 -0
- package/src/components/ChartMarker/ChartMarker.tsx +54 -0
- package/src/components/ChartMarker/index.ts +2 -0
- package/src/components/ChartMini/ChartMini.tsx +86 -0
- package/src/components/ChartMini/index.ts +2 -0
- package/src/components/ChartTooltip/ChartTooltip.tsx +44 -0
- package/src/components/ChartTooltip/index.ts +2 -0
- package/src/components/Checkbox/Checkbox.tsx +65 -0
- package/src/components/Checkbox/checkboxBase.tsx +81 -0
- package/src/components/Checkbox/index.ts +3 -0
- package/src/components/CodeSnippet/CodeSnippet.tsx +94 -0
- package/src/components/CodeSnippet/index.ts +2 -0
- package/src/components/CodeSnippetTabs/CodeSnippetTabs.tsx +44 -0
- package/src/components/CodeSnippetTabs/index.ts +2 -0
- package/src/components/CommandBar/CommandBar.tsx +80 -0
- package/src/components/CommandBar/index.ts +2 -0
- package/src/components/CommandBarFooter/CommandBarFooter.tsx +125 -0
- package/src/components/CommandBarFooter/index.ts +5 -0
- package/src/components/CommandBarMenuSection/CommandBarMenuSection.tsx +28 -0
- package/src/components/CommandBarMenuSection/index.ts +2 -0
- package/src/components/CommandBarNavigationIcon/CommandBarNavigationIcon.tsx +47 -0
- package/src/components/CommandBarNavigationIcon/index.ts +2 -0
- package/src/components/CommandDropdownMenuItem/CommandDropdownMenuItem.tsx +51 -0
- package/src/components/CommandDropdownMenuItem/index.ts +2 -0
- package/src/components/CommandInput/CommandInput.tsx +74 -0
- package/src/components/CommandInput/index.ts +2 -0
- package/src/components/CommandShortcut/CommandShortcut.tsx +26 -0
- package/src/components/CommandShortcut/index.ts +2 -0
- package/src/components/ContentDivider/ContentDivider.tsx +80 -0
- package/src/components/ContentDivider/index.ts +6 -0
- package/src/components/ContentFeatureText/ContentFeatureText.tsx +60 -0
- package/src/components/ContentFeatureText/index.ts +5 -0
- package/src/components/ContentHeading/ContentHeading.tsx +43 -0
- package/src/components/ContentHeading/index.ts +5 -0
- package/src/components/ContentParagraph/ContentParagraph.tsx +39 -0
- package/src/components/ContentParagraph/index.ts +5 -0
- package/src/components/ContentQuote/ContentQuote.tsx +114 -0
- package/src/components/ContentQuote/index.ts +6 -0
- package/src/components/ContentRule/ContentRule.tsx +31 -0
- package/src/components/ContentRule/index.ts +2 -0
- package/src/components/ContextMenu/ContextMenu.tsx +67 -0
- package/src/components/ContextMenu/index.ts +7 -0
- package/src/components/ContextMenu/useContextMenu.ts +41 -0
- package/src/components/DatePickerCell/DatePickerCell.tsx +77 -0
- package/src/components/DatePickerCell/index.ts +2 -0
- package/src/components/DatePickerListItem/DatePickerListItem.tsx +39 -0
- package/src/components/DatePickerListItem/index.ts +2 -0
- package/src/components/DatePickerMenu/DatePickerMenu.tsx +131 -0
- package/src/components/DatePickerMenu/index.ts +2 -0
- package/src/components/DropdownAccountListItem/DropdownAccountListItem.tsx +69 -0
- package/src/components/DropdownAccountListItem/index.ts +2 -0
- package/src/components/DropdownMenuFooter/DropdownMenuFooter.tsx +50 -0
- package/src/components/DropdownMenuFooter/index.ts +5 -0
- package/src/components/DropdownMenuHeader/DropdownMenuHeader.tsx +93 -0
- package/src/components/DropdownMenuHeader/index.ts +5 -0
- package/src/components/DropdownMenuItemInsetIcon/DropdownMenuItemInsetIcon.tsx +89 -0
- package/src/components/DropdownMenuItemInsetIcon/index.ts +5 -0
- package/src/components/DropdownMenuListItem/DropdownMenuListItem.tsx +84 -0
- package/src/components/DropdownMenuListItem/index.ts +2 -0
- package/src/components/EmptyState/EmptyState.tsx +65 -0
- package/src/components/EmptyState/index.ts +2 -0
- package/src/components/FeedItemBase/FeedItemBase.tsx +135 -0
- package/src/components/FeedItemBase/index.ts +2 -0
- package/src/components/FileUpload/FileUpload.tsx +112 -0
- package/src/components/FileUpload/index.ts +2 -0
- package/src/components/FileUploadBase/FileUploadBase.tsx +69 -0
- package/src/components/FileUploadBase/index.ts +2 -0
- package/src/components/FileUploadItemBase/FileUploadItemBase.tsx +190 -0
- package/src/components/FileUploadItemBase/index.ts +7 -0
- package/src/components/FilterBar/FilterBar.tsx +62 -0
- package/src/components/FilterBar/index.ts +2 -0
- package/src/components/FilterTabs/FilterTabs.tsx +41 -0
- package/src/components/FilterTabs/index.ts +2 -0
- package/src/components/FiltersDropdownMenu/FiltersDropdownMenu.tsx +104 -0
- package/src/components/FiltersDropdownMenu/index.ts +2 -0
- package/src/components/FiltersSlideoutMenu/FiltersSlideoutMenu.tsx +71 -0
- package/src/components/FiltersSlideoutMenu/index.ts +2 -0
- package/src/components/HeaderNavigation/HeaderNavigation.tsx +178 -0
- package/src/components/HeaderNavigation/index.ts +6 -0
- package/src/components/HelpIcon/HelpIcon.tsx +49 -0
- package/src/components/HelpIcon/index.ts +2 -0
- package/src/components/InputField/InputField.tsx +108 -0
- package/src/components/InputField/index.ts +3 -0
- package/src/components/InputField/inputFieldShared.tsx +68 -0
- package/src/components/LeadingInputField/LeadingInputField.tsx +60 -0
- package/src/components/LeadingInputField/index.ts +2 -0
- package/src/components/LineAndBarChart/LineAndBarChart.tsx +96 -0
- package/src/components/LineAndBarChart/index.ts +2 -0
- package/src/components/LinkMessage/LinkMessage.tsx +52 -0
- package/src/components/LinkMessage/index.ts +2 -0
- package/src/components/LoadingIndicator/LoadingIndicator.tsx +108 -0
- package/src/components/LoadingIndicator/index.ts +6 -0
- package/src/components/MediaMessage/MediaMessage.tsx +109 -0
- package/src/components/MediaMessage/index.ts +2 -0
- package/src/components/MegaInputFieldBase/MegaInputFieldBase.tsx +49 -0
- package/src/components/MegaInputFieldBase/index.ts +5 -0
- package/src/components/Message/Message.tsx +85 -0
- package/src/components/Message/index.ts +3 -0
- package/src/components/Message/messageShared.tsx +73 -0
- package/src/components/MessageAction/MessageAction.tsx +221 -0
- package/src/components/MessageAction/index.ts +2 -0
- package/src/components/MessageActionButton/MessageActionButton.tsx +36 -0
- package/src/components/MessageActionButton/index.ts +2 -0
- package/src/components/MessageActionPanel/MessageActionPanel.tsx +36 -0
- package/src/components/MessageActionPanel/index.ts +2 -0
- package/src/components/MessageReaction/MessageReaction.tsx +37 -0
- package/src/components/MessageReaction/index.ts +2 -0
- package/src/components/MessageStatusIcon/MessageStatusIcon.tsx +54 -0
- package/src/components/MessageStatusIcon/index.ts +2 -0
- package/src/components/MetricItem/MetricItem.tsx +147 -0
- package/src/components/MetricItem/index.ts +2 -0
- package/src/components/ModalActions/ModalActions.tsx +57 -0
- package/src/components/ModalActions/index.ts +2 -0
- package/src/components/ModalHeader/ModalHeader.tsx +99 -0
- package/src/components/ModalHeader/index.ts +2 -0
- package/src/components/MultiSelect/MultiSelect.tsx +118 -0
- package/src/components/MultiSelect/index.ts +2 -0
- package/src/components/NavAccountCard/NavAccountCard.tsx +124 -0
- package/src/components/NavAccountCard/index.ts +2 -0
- package/src/components/NavAccountCardMenuItem/NavAccountCardMenuItem.tsx +101 -0
- package/src/components/NavAccountCardMenuItem/index.ts +5 -0
- package/src/components/NavButton/NavButton.tsx +50 -0
- package/src/components/NavButton/index.ts +2 -0
- package/src/components/NavFeaturedCard/NavFeaturedCard.tsx +82 -0
- package/src/components/NavFeaturedCard/index.ts +2 -0
- package/src/components/NavItemBase/NavItemBase.tsx +79 -0
- package/src/components/NavItemBase/index.ts +2 -0
- package/src/components/NavItemDropdownBase/NavItemDropdownBase.tsx +74 -0
- package/src/components/NavItemDropdownBase/index.ts +2 -0
- package/src/components/NavMenuButton/NavMenuButton.tsx +47 -0
- package/src/components/NavMenuButton/index.ts +2 -0
- package/src/components/Notification/Notification.tsx +102 -0
- package/src/components/Notification/index.ts +2 -0
- package/src/components/NumberInput/NumberInput.tsx +114 -0
- package/src/components/NumberInput/index.ts +2 -0
- package/src/components/PageHeader/PageHeader.tsx +88 -0
- package/src/components/PageHeader/index.ts +2 -0
- package/src/components/Pagination/Pagination.tsx +124 -0
- package/src/components/Pagination/index.ts +2 -0
- package/src/components/PaginationButtonGroupBase/PaginationButtonGroupBase.tsx +69 -0
- package/src/components/PaginationButtonGroupBase/index.ts +5 -0
- package/src/components/PaginationCards/PaginationCards.tsx +72 -0
- package/src/components/PaginationCards/index.ts +2 -0
- package/src/components/PaginationDotGroup/PaginationDotGroup.tsx +66 -0
- package/src/components/PaginationDotGroup/index.ts +2 -0
- package/src/components/PaginationDotIndicator/PaginationDotIndicator.tsx +39 -0
- package/src/components/PaginationDotIndicator/index.ts +6 -0
- package/src/components/PaginationNumberBase/PaginationNumberBase.tsx +42 -0
- package/src/components/PaginationNumberBase/index.ts +5 -0
- package/src/components/PieChart/PieChart.tsx +73 -0
- package/src/components/PieChart/index.ts +2 -0
- package/src/components/ProgressBar/ProgressBar.tsx +75 -0
- package/src/components/ProgressBar/index.ts +2 -0
- package/src/components/ProgressCircle/ProgressCircle.tsx +89 -0
- package/src/components/ProgressCircle/index.ts +6 -0
- package/src/components/RadarChart/RadarChart.tsx +62 -0
- package/src/components/RadarChart/index.ts +2 -0
- package/src/components/Radio/Radio.tsx +55 -0
- package/src/components/Radio/index.ts +2 -0
- package/src/components/RadioGroup/RadioGroup.tsx +54 -0
- package/src/components/RadioGroup/index.ts +2 -0
- package/src/components/RadioGroupItem/RadioGroupItem.tsx +118 -0
- package/src/components/RadioGroupItem/index.ts +6 -0
- package/src/components/SectionFooter/SectionFooter.tsx +40 -0
- package/src/components/SectionFooter/index.ts +2 -0
- package/src/components/SectionHeader/SectionHeader.tsx +44 -0
- package/src/components/SectionHeader/index.ts +2 -0
- package/src/components/SectionLabel/SectionLabel.tsx +51 -0
- package/src/components/SectionLabel/index.ts +2 -0
- package/src/components/Select/Select.tsx +121 -0
- package/src/components/Select/index.ts +2 -0
- package/src/components/SelectMenuItem/SelectMenuItem.tsx +85 -0
- package/src/components/SelectMenuItem/index.ts +2 -0
- package/src/components/SidebarNavigation/SidebarNavigation.tsx +100 -0
- package/src/components/SidebarNavigation/index.ts +2 -0
- package/src/components/SlideOutMenuHeader/SlideOutMenuHeader.tsx +56 -0
- package/src/components/SlideOutMenuHeader/index.ts +2 -0
- package/src/components/Slider/Slider.tsx +125 -0
- package/src/components/Slider/index.ts +2 -0
- package/src/components/SocialButton/SocialButton.tsx +88 -0
- package/src/components/SocialButton/index.ts +2 -0
- package/src/components/StatusIcon/StatusIcon.tsx +75 -0
- package/src/components/StatusIcon/index.ts +2 -0
- package/src/components/StepBase/StepBase.tsx +90 -0
- package/src/components/StepBase/index.ts +2 -0
- package/src/components/StepIconBase/StepIconBase.tsx +65 -0
- package/src/components/StepIconBase/index.ts +7 -0
- package/src/components/TabButtonBase/TabButtonBase.tsx +88 -0
- package/src/components/TabButtonBase/index.ts +2 -0
- package/src/components/TableCell/TableCell.tsx +44 -0
- package/src/components/TableCell/index.ts +2 -0
- package/src/components/TableHeaderCell/TableHeaderCell.tsx +34 -0
- package/src/components/TableHeaderCell/index.ts +2 -0
- package/src/components/TableHeaderLabel/TableHeaderLabel.tsx +37 -0
- package/src/components/TableHeaderLabel/index.ts +2 -0
- package/src/components/Tabs/Tabs.tsx +80 -0
- package/src/components/Tabs/index.ts +2 -0
- package/src/components/Tag/Tag.tsx +91 -0
- package/src/components/Tag/index.ts +2 -0
- package/src/components/TagsInputField/TagsInputField.tsx +90 -0
- package/src/components/TagsInputField/index.ts +2 -0
- package/src/components/TextEditorToolbar/TextEditorToolbar.tsx +33 -0
- package/src/components/TextEditorToolbar/index.ts +2 -0
- package/src/components/TextEditorTooltip/TextEditorTooltip.tsx +28 -0
- package/src/components/TextEditorTooltip/index.ts +2 -0
- package/src/components/TextareaInputField/TextareaInputField.tsx +45 -0
- package/src/components/TextareaInputField/index.ts +2 -0
- package/src/components/Toggle/Toggle.tsx +87 -0
- package/src/components/Toggle/index.ts +2 -0
- package/src/components/Tooltip/Tooltip.tsx +59 -0
- package/src/components/Tooltip/index.ts +2 -0
- package/src/components/TrailingInputField/TrailingInputField.tsx +62 -0
- package/src/components/TrailingInputField/index.ts +2 -0
- package/src/components/TreeView/TreeView.tsx +86 -0
- package/src/components/TreeView/index.ts +2 -0
- package/src/components/TreeViewConnector/TreeViewConnector.tsx +36 -0
- package/src/components/TreeViewConnector/index.ts +2 -0
- package/src/components/TreeViewItem/TreeViewItem.tsx +111 -0
- package/src/components/TreeViewItem/index.ts +2 -0
- package/src/components/VerificationCodeInput/VerificationCodeInput.tsx +114 -0
- package/src/components/VerificationCodeInput/index.ts +2 -0
- package/src/illustrations/BoxIllustration.tsx +13 -0
- package/src/illustrations/CloudIllustration.tsx +18 -0
- package/src/illustrations/CreditCardIllustration.tsx +13 -0
- package/src/illustrations/DocumentsIllustration.tsx +13 -0
- package/src/illustrations/index.ts +4 -0
- package/src/index.ts +147 -0
- package/src/internal/chartTheme.ts +30 -0
- package/src/internal/ringBase.tsx +82 -0
- package/src/styles.css +3 -0
- package/tailwind-preset.js +295 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type ButtonHTMLAttributes, type ReactNode } from "react";
|
|
2
|
+
import clsx from "clsx";
|
|
3
|
+
|
|
4
|
+
export interface MessageActionButtonProps
|
|
5
|
+
extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
6
|
+
/** Icon glyph (16px). Use `currentColor` so the button can tint it. */
|
|
7
|
+
icon: ReactNode;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Single icon button used inside `MessageActionPanel` (the hover toolbar over a
|
|
12
|
+
* message). Sits on the dark panel surface — icon is light, hover lifts it to
|
|
13
|
+
* full white with a translucent backdrop. `rounded-xs` / `p-xxs` per Figma.
|
|
14
|
+
*/
|
|
15
|
+
export function MessageActionButton({
|
|
16
|
+
icon,
|
|
17
|
+
className,
|
|
18
|
+
type = "button",
|
|
19
|
+
...rest
|
|
20
|
+
}: MessageActionButtonProps) {
|
|
21
|
+
return (
|
|
22
|
+
<button
|
|
23
|
+
type={type}
|
|
24
|
+
className={clsx(
|
|
25
|
+
"flex items-center rounded-xs p-xxs",
|
|
26
|
+
"text-text-white/70 transition-colors hover:bg-white/10 hover:text-text-white",
|
|
27
|
+
className,
|
|
28
|
+
)}
|
|
29
|
+
{...rest}
|
|
30
|
+
>
|
|
31
|
+
<span className="flex size-4 items-center justify-center [&>svg]:size-4">
|
|
32
|
+
{icon}
|
|
33
|
+
</span>
|
|
34
|
+
</button>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type HTMLAttributes, type ReactNode } from "react";
|
|
2
|
+
import clsx from "clsx";
|
|
3
|
+
|
|
4
|
+
export interface MessageActionPanelProps
|
|
5
|
+
extends HTMLAttributes<HTMLDivElement> {
|
|
6
|
+
/** Action buttons — compose `MessageActionButton` elements here. */
|
|
7
|
+
children: ReactNode;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Floating dark toolbar that appears over a message on hover. Holds a row of
|
|
12
|
+
* `MessageActionButton`s (react / reply / regenerate / copy …). Composition
|
|
13
|
+
* shell only — the buttons are supplied as children, not reimplemented here.
|
|
14
|
+
*
|
|
15
|
+
* Surface uses `bg-bg-primary-solid` (#0a0a0a) for the dark floating panel;
|
|
16
|
+
* Figma's `bg-primary_alt` dark value is #171717 (token gap — see figma-map
|
|
17
|
+
* Pending token verification). `shadow-xl` is the Tailwind-default approximation
|
|
18
|
+
* of Figma's 3-layer xl stack.
|
|
19
|
+
*/
|
|
20
|
+
export function MessageActionPanel({
|
|
21
|
+
children,
|
|
22
|
+
className,
|
|
23
|
+
...rest
|
|
24
|
+
}: MessageActionPanelProps) {
|
|
25
|
+
return (
|
|
26
|
+
<div
|
|
27
|
+
className={clsx(
|
|
28
|
+
"inline-flex items-start gap-sm rounded-md bg-bg-primary-solid px-md py-sm shadow-xl",
|
|
29
|
+
className,
|
|
30
|
+
)}
|
|
31
|
+
{...rest}
|
|
32
|
+
>
|
|
33
|
+
{children}
|
|
34
|
+
</div>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { ButtonHTMLAttributes, ReactNode } from "react";
|
|
2
|
+
import clsx from "clsx";
|
|
3
|
+
|
|
4
|
+
export interface MessageReactionProps
|
|
5
|
+
extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
6
|
+
/** Emoji glyph (defaults to a heart). */
|
|
7
|
+
emoji?: ReactNode;
|
|
8
|
+
/** Reaction count — hidden when undefined. */
|
|
9
|
+
count?: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** A pill reaction chip shown under a chat message. */
|
|
13
|
+
export function MessageReaction({
|
|
14
|
+
emoji = "❤️",
|
|
15
|
+
count,
|
|
16
|
+
className,
|
|
17
|
+
...rest
|
|
18
|
+
}: MessageReactionProps) {
|
|
19
|
+
return (
|
|
20
|
+
<button
|
|
21
|
+
type="button"
|
|
22
|
+
className={clsx(
|
|
23
|
+
"flex h-6 items-center justify-center rounded-full border border-border-secondary bg-bg-secondary px-md py-xs font-body",
|
|
24
|
+
count != null ? "gap-xs" : "gap-0",
|
|
25
|
+
className,
|
|
26
|
+
)}
|
|
27
|
+
{...rest}
|
|
28
|
+
>
|
|
29
|
+
<span className="flex size-4 items-center justify-center text-[14px] leading-none">
|
|
30
|
+
{emoji}
|
|
31
|
+
</span>
|
|
32
|
+
{count != null ? (
|
|
33
|
+
<span className="text-sm font-medium text-text-secondary">{count}</span>
|
|
34
|
+
) : null}
|
|
35
|
+
</button>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import clsx from "clsx";
|
|
3
|
+
|
|
4
|
+
export type MessageStatus = "unread" | "read" | "failed";
|
|
5
|
+
|
|
6
|
+
export interface MessageStatusIconProps {
|
|
7
|
+
/** Delivery status. */
|
|
8
|
+
status?: MessageStatus;
|
|
9
|
+
/** Tooltip shown on hover (e.g. "Read 2:20 PM"). */
|
|
10
|
+
tooltip?: ReactNode;
|
|
11
|
+
className?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Delivery status glyph for a sent message — single check (unread), double
|
|
16
|
+
* check (read, brand), or an error circle (failed). Optional hover tooltip.
|
|
17
|
+
*/
|
|
18
|
+
export function MessageStatusIcon({
|
|
19
|
+
status = "unread",
|
|
20
|
+
tooltip,
|
|
21
|
+
className,
|
|
22
|
+
}: MessageStatusIconProps) {
|
|
23
|
+
let glyph: ReactNode;
|
|
24
|
+
if (status === "read") {
|
|
25
|
+
glyph = (
|
|
26
|
+
<svg viewBox="0 0 16 16" fill="none" className="size-4 text-fg-brand-primary" aria-hidden>
|
|
27
|
+
<path d="M1.5 8.5 4 11l5.5-6M7 11l.5.5 5.5-6" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
|
28
|
+
</svg>
|
|
29
|
+
);
|
|
30
|
+
} else if (status === "failed") {
|
|
31
|
+
glyph = (
|
|
32
|
+
<svg viewBox="0 0 16 16" fill="none" className="size-4 text-fg-error-primary" aria-hidden>
|
|
33
|
+
<path d="M8 5.333V8m0 2.667h.007M14.667 8A6.667 6.667 0 1 1 8 1.333 6.667 6.667 0 0 1 14.667 8Z" stroke="currentColor" strokeWidth="1.333" strokeLinecap="round" strokeLinejoin="round" />
|
|
34
|
+
</svg>
|
|
35
|
+
);
|
|
36
|
+
} else {
|
|
37
|
+
glyph = (
|
|
38
|
+
<svg viewBox="0 0 16 16" fill="none" className="size-4 text-fg-quaternary" aria-hidden>
|
|
39
|
+
<path d="m3 8.5 3 3 7-7" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
|
40
|
+
</svg>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<span className={clsx("group relative inline-flex size-4 items-center justify-center font-body", className)}>
|
|
46
|
+
{glyph}
|
|
47
|
+
{tooltip ? (
|
|
48
|
+
<span className="pointer-events-none absolute bottom-full left-1/2 mb-1.5 hidden -translate-x-1/2 whitespace-nowrap rounded-md bg-bg-primary-solid px-md py-sm text-xs font-semibold text-text-white shadow-lg group-hover:block">
|
|
49
|
+
{tooltip}
|
|
50
|
+
</span>
|
|
51
|
+
) : null}
|
|
52
|
+
</span>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { type HTMLAttributes, type ReactNode } from "react";
|
|
2
|
+
import clsx from "clsx";
|
|
3
|
+
|
|
4
|
+
export interface MetricItemProps extends HTMLAttributes<HTMLDivElement> {
|
|
5
|
+
/** Metric title, e.g. "Views 24 hours". */
|
|
6
|
+
label: ReactNode;
|
|
7
|
+
/** Big number, e.g. "2,000". Rendered in display-sm. */
|
|
8
|
+
value: ReactNode;
|
|
9
|
+
/** Trend chip — compose a `Change` element here. */
|
|
10
|
+
change?: ReactNode;
|
|
11
|
+
/** Caption beside the change, e.g. "vs last month" (chart layout only). */
|
|
12
|
+
changeText?: ReactNode;
|
|
13
|
+
/** Featured-icon slot, rendered top-left (consumer supplies the badge). */
|
|
14
|
+
icon?: ReactNode;
|
|
15
|
+
/** Sparkline slot, rendered right of the number (e.g. a Recharts chart). */
|
|
16
|
+
chart?: ReactNode;
|
|
17
|
+
/** Footer action, e.g. a "View report" button — renders a divided footer. */
|
|
18
|
+
action?: ReactNode;
|
|
19
|
+
/** Top-right options (dots) button — rendered only when handler is given. */
|
|
20
|
+
onMenuClick?: () => void;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const DotsVertical = ({ className }: { className?: string }) => (
|
|
24
|
+
<svg className={className} viewBox="0 0 20 20" fill="none" aria-hidden>
|
|
25
|
+
<path
|
|
26
|
+
d="M10 10.83a.83.83 0 1 0 0-1.66.83.83 0 0 0 0 1.66ZM10 5a.83.83 0 1 0 0-1.67A.83.83 0 0 0 10 5ZM10 16.67A.83.83 0 1 0 10 15a.83.83 0 0 0 0 1.67Z"
|
|
27
|
+
stroke="currentColor"
|
|
28
|
+
strokeWidth="1.67"
|
|
29
|
+
strokeLinecap="round"
|
|
30
|
+
strokeLinejoin="round"
|
|
31
|
+
/>
|
|
32
|
+
</svg>
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
const Label = ({ children }: { children: ReactNode }) => (
|
|
36
|
+
<p className="w-full text-sm font-medium text-text-tertiary">{children}</p>
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
const Value = ({ children }: { children: ReactNode }) => (
|
|
40
|
+
<span className="flex-1 font-display text-display-sm font-semibold text-text-primary">
|
|
41
|
+
{children}
|
|
42
|
+
</span>
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Metric / stat card. One component covering the Figma Simple / Icon / Chart
|
|
47
|
+
* types via optional slots:
|
|
48
|
+
* - bare → Simple (label + value + inline `change`).
|
|
49
|
+
* - `icon` → Icon (featured-icon badge on top).
|
|
50
|
+
* - `chart` → Chart (value + change/`changeText` left, sparkline right).
|
|
51
|
+
* - `action` → divided footer with the action node.
|
|
52
|
+
* - `onMenuClick` → dots-vertical button top-right.
|
|
53
|
+
*
|
|
54
|
+
* `icon` and `chart` are ReactNode slots, so the missing Figma "Featured icons"
|
|
55
|
+
* family and the deferred `_ChartMini` (Recharts) are non-blocking — consumers
|
|
56
|
+
* fill them. Note: Figma's Chart variant uses a text-md/semibold label; this
|
|
57
|
+
* component standardises on text-sm/medium for all types (see figma-map).
|
|
58
|
+
*/
|
|
59
|
+
export function MetricItem({
|
|
60
|
+
label,
|
|
61
|
+
value,
|
|
62
|
+
change,
|
|
63
|
+
changeText,
|
|
64
|
+
icon,
|
|
65
|
+
chart,
|
|
66
|
+
action,
|
|
67
|
+
onMenuClick,
|
|
68
|
+
className,
|
|
69
|
+
...rest
|
|
70
|
+
}: MetricItemProps) {
|
|
71
|
+
const hasChart = !!chart;
|
|
72
|
+
const hasIcon = !!icon;
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
<div
|
|
76
|
+
className={clsx(
|
|
77
|
+
"relative flex w-full flex-col rounded-xl border border-border-secondary bg-bg-primary shadow-xs",
|
|
78
|
+
className,
|
|
79
|
+
)}
|
|
80
|
+
{...rest}
|
|
81
|
+
>
|
|
82
|
+
<div
|
|
83
|
+
className={clsx(
|
|
84
|
+
"flex w-full flex-col p-2xl",
|
|
85
|
+
hasIcon || hasChart ? "gap-2xl" : "gap-md",
|
|
86
|
+
)}
|
|
87
|
+
>
|
|
88
|
+
{hasIcon && <div className="shrink-0">{icon}</div>}
|
|
89
|
+
|
|
90
|
+
{hasChart ? (
|
|
91
|
+
<>
|
|
92
|
+
<Label>{label}</Label>
|
|
93
|
+
<div className="flex w-full items-end gap-xl">
|
|
94
|
+
<div className="flex min-w-0 flex-1 flex-col gap-lg">
|
|
95
|
+
<Value>{value}</Value>
|
|
96
|
+
{(change || changeText) && (
|
|
97
|
+
<div className="flex w-full items-center gap-md">
|
|
98
|
+
{change}
|
|
99
|
+
{changeText && (
|
|
100
|
+
<span className="min-w-0 flex-1 truncate text-sm font-medium text-text-tertiary">
|
|
101
|
+
{changeText}
|
|
102
|
+
</span>
|
|
103
|
+
)}
|
|
104
|
+
</div>
|
|
105
|
+
)}
|
|
106
|
+
</div>
|
|
107
|
+
<div className="shrink-0">{chart}</div>
|
|
108
|
+
</div>
|
|
109
|
+
</>
|
|
110
|
+
) : hasIcon ? (
|
|
111
|
+
<div className="flex w-full flex-col gap-md">
|
|
112
|
+
<Label>{label}</Label>
|
|
113
|
+
<div className="flex w-full items-end gap-xl">
|
|
114
|
+
<Value>{value}</Value>
|
|
115
|
+
{change && <div className="shrink-0">{change}</div>}
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
) : (
|
|
119
|
+
<>
|
|
120
|
+
<Label>{label}</Label>
|
|
121
|
+
<div className="flex w-full items-end gap-xl">
|
|
122
|
+
<Value>{value}</Value>
|
|
123
|
+
{change && <div className="shrink-0">{change}</div>}
|
|
124
|
+
</div>
|
|
125
|
+
</>
|
|
126
|
+
)}
|
|
127
|
+
</div>
|
|
128
|
+
|
|
129
|
+
{action && (
|
|
130
|
+
<div className="flex w-full items-center justify-end border-t border-border-secondary px-2xl py-xl">
|
|
131
|
+
{action}
|
|
132
|
+
</div>
|
|
133
|
+
)}
|
|
134
|
+
|
|
135
|
+
{onMenuClick && (
|
|
136
|
+
<button
|
|
137
|
+
type="button"
|
|
138
|
+
onClick={onMenuClick}
|
|
139
|
+
aria-label="Options"
|
|
140
|
+
className="absolute right-[19px] top-[19px] text-fg-quaternary transition-colors hover:text-fg-quaternary-hover"
|
|
141
|
+
>
|
|
142
|
+
<DotsVertical className="size-5" />
|
|
143
|
+
</button>
|
|
144
|
+
)}
|
|
145
|
+
</div>
|
|
146
|
+
);
|
|
147
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { type HTMLAttributes, type ReactNode } from "react";
|
|
2
|
+
import clsx from "clsx";
|
|
3
|
+
|
|
4
|
+
export type ModalActionsType = "horizontal-fill" | "vertical-fill" | "right-aligned";
|
|
5
|
+
|
|
6
|
+
export interface ModalActionsProps extends HTMLAttributes<HTMLDivElement> {
|
|
7
|
+
/**
|
|
8
|
+
* - `horizontal-fill` — buttons share the row, each flex-1.
|
|
9
|
+
* - `vertical-fill` — buttons stacked full-width.
|
|
10
|
+
* - `right-aligned` — `leading` on the left, buttons pushed right.
|
|
11
|
+
*/
|
|
12
|
+
type?: ModalActionsType;
|
|
13
|
+
/** Top divider separating actions from the modal body. */
|
|
14
|
+
divider?: boolean;
|
|
15
|
+
/** Left-side content for `right-aligned` (e.g. a checkbox / tertiary button). */
|
|
16
|
+
leading?: ReactNode;
|
|
17
|
+
/** The action buttons — compose `Button` / `ButtonDestructive` here. */
|
|
18
|
+
children: ReactNode;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Modal footer button row. Layout shell only — buttons are supplied as children
|
|
23
|
+
* (composed from the library's Button components), laid out per `type`. Fill
|
|
24
|
+
* variants stretch their children; `right-aligned` pairs a `leading` slot with
|
|
25
|
+
* the right-pushed actions.
|
|
26
|
+
*/
|
|
27
|
+
export function ModalActions({
|
|
28
|
+
type = "horizontal-fill",
|
|
29
|
+
divider = true,
|
|
30
|
+
leading,
|
|
31
|
+
children,
|
|
32
|
+
className,
|
|
33
|
+
...rest
|
|
34
|
+
}: ModalActionsProps) {
|
|
35
|
+
return (
|
|
36
|
+
<div className={clsx("flex w-full flex-col", className)} {...rest}>
|
|
37
|
+
{divider && <div className="mt-4xl border-t border-border-secondary" />}
|
|
38
|
+
<div
|
|
39
|
+
className={clsx(
|
|
40
|
+
"flex px-3xl pb-3xl pt-3xl",
|
|
41
|
+
type === "horizontal-fill" && "gap-lg [&>*]:flex-1",
|
|
42
|
+
type === "vertical-fill" && "flex-col gap-lg [&>*]:w-full",
|
|
43
|
+
type === "right-aligned" && "items-center gap-lg",
|
|
44
|
+
)}
|
|
45
|
+
>
|
|
46
|
+
{type === "right-aligned" ? (
|
|
47
|
+
<>
|
|
48
|
+
<div className="flex flex-1 items-center gap-lg">{leading}</div>
|
|
49
|
+
<div className="flex items-center gap-lg">{children}</div>
|
|
50
|
+
</>
|
|
51
|
+
) : (
|
|
52
|
+
children
|
|
53
|
+
)}
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { type HTMLAttributes, type ReactNode } from "react";
|
|
2
|
+
import clsx from "clsx";
|
|
3
|
+
|
|
4
|
+
export type ModalHeaderType = "left" | "center" | "horizontal";
|
|
5
|
+
|
|
6
|
+
export interface ModalHeaderProps
|
|
7
|
+
extends Omit<HTMLAttributes<HTMLDivElement>, "title"> {
|
|
8
|
+
title: ReactNode;
|
|
9
|
+
description?: ReactNode;
|
|
10
|
+
/** Featured-icon glyph, rendered inside a 40px boxed badge. */
|
|
11
|
+
icon?: ReactNode;
|
|
12
|
+
/**
|
|
13
|
+
* - `left` — icon on top, text left-aligned.
|
|
14
|
+
* - `center` — icon on top, text centred.
|
|
15
|
+
* - `horizontal` — icon to the left of the text.
|
|
16
|
+
*/
|
|
17
|
+
type?: ModalHeaderType;
|
|
18
|
+
/** Bottom divider separating the header from the modal body. */
|
|
19
|
+
divider?: boolean;
|
|
20
|
+
/** Renders an x-close button top-right when provided. */
|
|
21
|
+
onClose?: () => void;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const FeaturedIcon = ({ children }: { children: ReactNode }) => (
|
|
25
|
+
<div className="flex size-10 shrink-0 items-center justify-center rounded-md border border-border-primary text-fg-secondary shadow-xs">
|
|
26
|
+
{children}
|
|
27
|
+
</div>
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
const XClose = () => (
|
|
31
|
+
<svg viewBox="0 0 20 20" fill="none" className="size-5" aria-hidden>
|
|
32
|
+
<path d="M15 5 5 15M5 5l10 10" stroke="currentColor" strokeWidth="1.67" strokeLinecap="round" strokeLinejoin="round" />
|
|
33
|
+
</svg>
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Modal header — featured-icon badge + title + supporting text, with an optional
|
|
38
|
+
* x-close button and bottom divider. `type` controls alignment (left / centred /
|
|
39
|
+
* icon-beside-text). The `icon` is a slot; consumer supplies the glyph.
|
|
40
|
+
*/
|
|
41
|
+
export function ModalHeader({
|
|
42
|
+
title,
|
|
43
|
+
description,
|
|
44
|
+
icon,
|
|
45
|
+
type = "left",
|
|
46
|
+
divider = true,
|
|
47
|
+
onClose,
|
|
48
|
+
className,
|
|
49
|
+
...rest
|
|
50
|
+
}: ModalHeaderProps) {
|
|
51
|
+
const isCenter = type === "center";
|
|
52
|
+
const isHorizontal = type === "horizontal";
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<div
|
|
56
|
+
className={clsx(
|
|
57
|
+
"relative flex w-full flex-col",
|
|
58
|
+
isHorizontal ? "items-start" : isCenter ? "items-center" : "items-start",
|
|
59
|
+
className,
|
|
60
|
+
)}
|
|
61
|
+
{...rest}
|
|
62
|
+
>
|
|
63
|
+
<div
|
|
64
|
+
className={clsx(
|
|
65
|
+
"flex w-full gap-xl px-3xl pt-3xl",
|
|
66
|
+
isHorizontal ? "items-start" : "flex-col",
|
|
67
|
+
isCenter && "items-center",
|
|
68
|
+
)}
|
|
69
|
+
>
|
|
70
|
+
{icon && <FeaturedIcon>{icon}</FeaturedIcon>}
|
|
71
|
+
<div
|
|
72
|
+
className={clsx(
|
|
73
|
+
"flex flex-col gap-xxs",
|
|
74
|
+
isHorizontal ? "flex-1" : "w-full",
|
|
75
|
+
isCenter && "items-center text-center",
|
|
76
|
+
)}
|
|
77
|
+
>
|
|
78
|
+
<p className="w-full text-md font-semibold text-text-primary">{title}</p>
|
|
79
|
+
{description && (
|
|
80
|
+
<p className="w-full text-sm text-text-tertiary">{description}</p>
|
|
81
|
+
)}
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
|
|
85
|
+
{onClose && (
|
|
86
|
+
<button
|
|
87
|
+
type="button"
|
|
88
|
+
onClick={onClose}
|
|
89
|
+
aria-label="Close"
|
|
90
|
+
className="absolute right-4 top-4 flex size-9 items-center justify-center rounded-md text-fg-quaternary transition-colors hover:text-fg-quaternary-hover"
|
|
91
|
+
>
|
|
92
|
+
<XClose />
|
|
93
|
+
</button>
|
|
94
|
+
)}
|
|
95
|
+
|
|
96
|
+
{divider && <div className="mt-2xl w-full border-t border-border-secondary" />}
|
|
97
|
+
</div>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { useState, type ReactNode } from "react";
|
|
2
|
+
import clsx from "clsx";
|
|
3
|
+
import { SelectMenuItem } from "../SelectMenuItem";
|
|
4
|
+
import { Tag } from "../Tag";
|
|
5
|
+
|
|
6
|
+
export interface MultiSelectOption {
|
|
7
|
+
value: string;
|
|
8
|
+
label: string;
|
|
9
|
+
supportingText?: ReactNode;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface MultiSelectProps {
|
|
13
|
+
options: MultiSelectOption[];
|
|
14
|
+
value?: string[];
|
|
15
|
+
onChange?: (value: string[]) => void;
|
|
16
|
+
open?: boolean;
|
|
17
|
+
onToggle?: () => void;
|
|
18
|
+
label?: ReactNode;
|
|
19
|
+
hint?: ReactNode;
|
|
20
|
+
placeholder?: string;
|
|
21
|
+
/** Show the in-trigger search input (default true). */
|
|
22
|
+
searchable?: boolean;
|
|
23
|
+
disabled?: boolean;
|
|
24
|
+
invalid?: boolean;
|
|
25
|
+
className?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Multi-select — trigger with selected-item `Tag` chips + a search input, over a
|
|
30
|
+
* shared dropdown of `SelectMenuItem` (`multi`) options with a search empty
|
|
31
|
+
* state. Composes the Select-family primitives rather than reimplementing a
|
|
32
|
+
* dropdown. Controlled: `value`/`onChange` (selection) and `open`/`onToggle`.
|
|
33
|
+
*/
|
|
34
|
+
export function MultiSelect({
|
|
35
|
+
options,
|
|
36
|
+
value = [],
|
|
37
|
+
onChange,
|
|
38
|
+
open = false,
|
|
39
|
+
onToggle,
|
|
40
|
+
label,
|
|
41
|
+
hint,
|
|
42
|
+
placeholder = "Select...",
|
|
43
|
+
searchable = true,
|
|
44
|
+
disabled = false,
|
|
45
|
+
invalid = false,
|
|
46
|
+
className,
|
|
47
|
+
}: MultiSelectProps) {
|
|
48
|
+
const [query, setQuery] = useState("");
|
|
49
|
+
const selected = options.filter((o) => value.includes(o.value));
|
|
50
|
+
const filtered = options.filter((o) => o.label.toLowerCase().includes(query.toLowerCase()));
|
|
51
|
+
|
|
52
|
+
const toggleValue = (v: string) =>
|
|
53
|
+
onChange?.(value.includes(v) ? value.filter((x) => x !== v) : [...value, v]);
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<div className={clsx("flex w-full flex-col gap-sm font-body", className)}>
|
|
57
|
+
{label && <span className="text-sm font-medium text-text-secondary">{label}</span>}
|
|
58
|
+
|
|
59
|
+
<div className="relative">
|
|
60
|
+
<div
|
|
61
|
+
className={clsx(
|
|
62
|
+
"flex min-h-[44px] w-full flex-wrap items-center gap-xs rounded-md border bg-bg-primary px-md py-1.5 shadow-xs",
|
|
63
|
+
invalid ? "border-border-error" : open ? "border-border-brand ring-2 ring-utility-brand-500/40" : "border-border-primary",
|
|
64
|
+
disabled && "cursor-not-allowed bg-bg-secondary opacity-60",
|
|
65
|
+
)}
|
|
66
|
+
onClick={() => !disabled && onToggle?.()}
|
|
67
|
+
>
|
|
68
|
+
{selected.map((o) => (
|
|
69
|
+
<Tag
|
|
70
|
+
key={o.value}
|
|
71
|
+
size="sm"
|
|
72
|
+
onClose={() => toggleValue(o.value)}
|
|
73
|
+
onClick={(e) => e.stopPropagation()}
|
|
74
|
+
>
|
|
75
|
+
{o.label}
|
|
76
|
+
</Tag>
|
|
77
|
+
))}
|
|
78
|
+
{searchable ? (
|
|
79
|
+
<input
|
|
80
|
+
value={query}
|
|
81
|
+
disabled={disabled}
|
|
82
|
+
placeholder={selected.length ? "" : placeholder}
|
|
83
|
+
onChange={(e) => setQuery(e.target.value)}
|
|
84
|
+
onClick={(e) => e.stopPropagation()}
|
|
85
|
+
className="min-w-[80px] flex-1 bg-transparent py-xxs text-md text-text-primary outline-none placeholder:text-text-placeholder"
|
|
86
|
+
/>
|
|
87
|
+
) : (
|
|
88
|
+
!selected.length && <span className="flex-1 text-md text-text-placeholder">{placeholder}</span>
|
|
89
|
+
)}
|
|
90
|
+
<svg viewBox="0 0 16 16" fill="none" className={clsx("size-4 shrink-0 text-fg-quaternary transition-transform", open && "rotate-180")} aria-hidden>
|
|
91
|
+
<path d="M4 6l4 4 4-4" stroke="currentColor" strokeWidth="1.33" strokeLinecap="round" strokeLinejoin="round" />
|
|
92
|
+
</svg>
|
|
93
|
+
</div>
|
|
94
|
+
|
|
95
|
+
{open && (
|
|
96
|
+
<div className="absolute left-0 right-0 top-full z-50 mt-xs flex max-h-[320px] flex-col gap-px overflow-auto rounded-md border border-border-secondary-alt bg-bg-primary p-xs shadow-lg">
|
|
97
|
+
{filtered.length ? (
|
|
98
|
+
filtered.map((o) => (
|
|
99
|
+
<SelectMenuItem
|
|
100
|
+
key={o.value}
|
|
101
|
+
multi
|
|
102
|
+
label={o.label}
|
|
103
|
+
supportingText={o.supportingText}
|
|
104
|
+
selected={value.includes(o.value)}
|
|
105
|
+
onClick={() => toggleValue(o.value)}
|
|
106
|
+
/>
|
|
107
|
+
))
|
|
108
|
+
) : (
|
|
109
|
+
<div className="px-md py-lg text-center text-sm text-text-tertiary">No results found</div>
|
|
110
|
+
)}
|
|
111
|
+
</div>
|
|
112
|
+
)}
|
|
113
|
+
</div>
|
|
114
|
+
|
|
115
|
+
{hint && <span className={clsx("text-sm", invalid ? "text-text-error-primary" : "text-text-tertiary")}>{hint}</span>}
|
|
116
|
+
</div>
|
|
117
|
+
);
|
|
118
|
+
}
|