@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,39 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import clsx from "clsx";
|
|
3
|
+
|
|
4
|
+
export type ContentParagraphSize = "sm" | "md" | "lg" | "xl";
|
|
5
|
+
|
|
6
|
+
export interface ContentParagraphProps {
|
|
7
|
+
/** Type scale. */
|
|
8
|
+
size?: ContentParagraphSize;
|
|
9
|
+
children: ReactNode;
|
|
10
|
+
className?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// lg verified = text-lg (18/28); sm/md/xl follow the body ramp
|
|
14
|
+
// (sm→text-sm, md→text-md, xl→text-xl) — inferred, not individually pulled.
|
|
15
|
+
const sizeClasses: Record<ContentParagraphSize, string> = {
|
|
16
|
+
sm: "text-sm",
|
|
17
|
+
md: "text-md",
|
|
18
|
+
lg: "text-lg",
|
|
19
|
+
xl: "text-xl",
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/** Long-form content paragraph (article/marketing body copy). */
|
|
23
|
+
export function ContentParagraph({
|
|
24
|
+
size = "md",
|
|
25
|
+
children,
|
|
26
|
+
className,
|
|
27
|
+
}: ContentParagraphProps) {
|
|
28
|
+
return (
|
|
29
|
+
<p
|
|
30
|
+
className={clsx(
|
|
31
|
+
"font-body font-normal text-text-tertiary",
|
|
32
|
+
sizeClasses[size],
|
|
33
|
+
className,
|
|
34
|
+
)}
|
|
35
|
+
>
|
|
36
|
+
{children}
|
|
37
|
+
</p>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import clsx from "clsx";
|
|
3
|
+
|
|
4
|
+
export type ContentQuoteAlign = "left" | "center";
|
|
5
|
+
export type ContentQuoteSize = "sm" | "md" | "lg" | "xl" | "2xl";
|
|
6
|
+
|
|
7
|
+
export interface ContentQuoteProps {
|
|
8
|
+
/** The quotation text. */
|
|
9
|
+
quote: ReactNode;
|
|
10
|
+
/** Left-bordered (default) or centred layout. */
|
|
11
|
+
align?: ContentQuoteAlign;
|
|
12
|
+
/** Quote type scale. */
|
|
13
|
+
size?: ContentQuoteSize;
|
|
14
|
+
/** Author avatar — compose an Avatar. */
|
|
15
|
+
avatar?: ReactNode;
|
|
16
|
+
/** Author name. */
|
|
17
|
+
authorName?: ReactNode;
|
|
18
|
+
/** Author role / supporting line. */
|
|
19
|
+
authorRole?: ReactNode;
|
|
20
|
+
className?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// md verified = text-lg (18/28). sm/lg/xl/2xl inferred from the type scale.
|
|
24
|
+
const quoteSizeClasses: Record<ContentQuoteSize, string> = {
|
|
25
|
+
sm: "text-md",
|
|
26
|
+
md: "text-lg",
|
|
27
|
+
lg: "text-xl",
|
|
28
|
+
xl: "text-display-xs",
|
|
29
|
+
"2xl": "text-display-sm",
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/** Author block (avatar + name + role). */
|
|
33
|
+
function Author({
|
|
34
|
+
avatar,
|
|
35
|
+
authorName,
|
|
36
|
+
authorRole,
|
|
37
|
+
center,
|
|
38
|
+
}: {
|
|
39
|
+
avatar?: ReactNode;
|
|
40
|
+
authorName?: ReactNode;
|
|
41
|
+
authorRole?: ReactNode;
|
|
42
|
+
center?: boolean;
|
|
43
|
+
}) {
|
|
44
|
+
if (!avatar && !authorName && !authorRole) return null;
|
|
45
|
+
return (
|
|
46
|
+
<div
|
|
47
|
+
className={clsx(
|
|
48
|
+
"flex gap-md",
|
|
49
|
+
center ? "flex-col items-center" : "items-center",
|
|
50
|
+
)}
|
|
51
|
+
>
|
|
52
|
+
{avatar ? <div className="shrink-0">{avatar}</div> : null}
|
|
53
|
+
<div className={clsx("flex flex-col", center && "items-center text-center")}>
|
|
54
|
+
{authorName ? (
|
|
55
|
+
<span className="text-sm font-semibold text-text-primary">
|
|
56
|
+
{authorName}
|
|
57
|
+
</span>
|
|
58
|
+
) : null}
|
|
59
|
+
{authorRole ? (
|
|
60
|
+
<span className="text-sm font-normal text-text-tertiary">
|
|
61
|
+
{authorRole}
|
|
62
|
+
</span>
|
|
63
|
+
) : null}
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Long-form pull quote with optional author attribution. */
|
|
70
|
+
export function ContentQuote({
|
|
71
|
+
quote,
|
|
72
|
+
align = "left",
|
|
73
|
+
size = "md",
|
|
74
|
+
avatar,
|
|
75
|
+
authorName,
|
|
76
|
+
authorRole,
|
|
77
|
+
className,
|
|
78
|
+
}: ContentQuoteProps) {
|
|
79
|
+
const quoteText = (
|
|
80
|
+
<p
|
|
81
|
+
className={clsx(
|
|
82
|
+
"font-body font-medium italic text-text-primary",
|
|
83
|
+
quoteSizeClasses[size],
|
|
84
|
+
align === "center" && "text-center",
|
|
85
|
+
)}
|
|
86
|
+
>
|
|
87
|
+
{quote}
|
|
88
|
+
</p>
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
if (align === "center") {
|
|
92
|
+
return (
|
|
93
|
+
<div className={clsx("flex flex-col items-center gap-2xl font-body", className)}>
|
|
94
|
+
{quoteText}
|
|
95
|
+
<Author
|
|
96
|
+
avatar={avatar}
|
|
97
|
+
authorName={authorName}
|
|
98
|
+
authorRole={authorRole}
|
|
99
|
+
center
|
|
100
|
+
/>
|
|
101
|
+
</div>
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return (
|
|
106
|
+
<div className={clsx("flex items-stretch gap-2xl font-body", className)}>
|
|
107
|
+
<span className="w-0.5 shrink-0 self-stretch bg-border-secondary" aria-hidden />
|
|
108
|
+
<div className="flex min-w-0 flex-1 flex-col gap-3xl py-md">
|
|
109
|
+
{quoteText}
|
|
110
|
+
<Author avatar={avatar} authorName={authorName} authorRole={authorRole} />
|
|
111
|
+
</div>
|
|
112
|
+
</div>
|
|
113
|
+
);
|
|
114
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import clsx from "clsx";
|
|
2
|
+
|
|
3
|
+
export type ContentRuleSize = "sm" | "md" | "lg" | "xl" | "2xl";
|
|
4
|
+
|
|
5
|
+
export interface ContentRuleProps {
|
|
6
|
+
/** Controls the vertical breathing room around the rule. */
|
|
7
|
+
size?: ContentRuleSize;
|
|
8
|
+
className?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// Vertical padding ramp. md verified (32px each side); others inferred.
|
|
12
|
+
const sizePadding: Record<ContentRuleSize, string> = {
|
|
13
|
+
sm: "py-3xl", // 24px
|
|
14
|
+
md: "py-4xl", // 32px
|
|
15
|
+
lg: "py-5xl", // 40px
|
|
16
|
+
xl: "py-6xl", // 48px
|
|
17
|
+
"2xl": "py-7xl", // 64px
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* A plain full-width horizontal rule for separating blocks of long-form
|
|
22
|
+
* content. Distinct from `ContentDivider`, which carries a centred
|
|
23
|
+
* label/button. `size` sets the surrounding vertical spacing.
|
|
24
|
+
*/
|
|
25
|
+
export function ContentRule({ size = "md", className }: ContentRuleProps) {
|
|
26
|
+
return (
|
|
27
|
+
<div className={clsx("w-full", sizePadding[size], className)}>
|
|
28
|
+
<hr className="border-0 border-t border-border-secondary" />
|
|
29
|
+
</div>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { useEffect, useRef } from "react";
|
|
2
|
+
import type { ReactNode } from "react";
|
|
3
|
+
import clsx from "clsx";
|
|
4
|
+
|
|
5
|
+
export interface ContextMenuProps {
|
|
6
|
+
/** Whether the menu is visible. */
|
|
7
|
+
open: boolean;
|
|
8
|
+
/** Viewport x of the right-click (from useContextMenu). */
|
|
9
|
+
x: number;
|
|
10
|
+
/** Viewport y of the right-click (from useContextMenu). */
|
|
11
|
+
y: number;
|
|
12
|
+
/** Called on outside-click or Escape. */
|
|
13
|
+
onClose: () => void;
|
|
14
|
+
/** Menu rows — typically DropdownMenuListItem elements. */
|
|
15
|
+
children: ReactNode;
|
|
16
|
+
className?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* A thin positioned panel for right-click menus. Reuses the dropdown panel
|
|
21
|
+
* tokens (shadow-lg, border-secondary-alt, radius-md) and renders whatever
|
|
22
|
+
* rows you pass — compose DropdownMenuListItem children; it does NOT
|
|
23
|
+
* reimplement list items. Pair with `useContextMenu`.
|
|
24
|
+
*/
|
|
25
|
+
export function ContextMenu({
|
|
26
|
+
open,
|
|
27
|
+
x,
|
|
28
|
+
y,
|
|
29
|
+
onClose,
|
|
30
|
+
children,
|
|
31
|
+
className,
|
|
32
|
+
}: ContextMenuProps) {
|
|
33
|
+
const ref = useRef<HTMLDivElement>(null);
|
|
34
|
+
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
if (!open) return;
|
|
37
|
+
const onPointerDown = (e: PointerEvent) => {
|
|
38
|
+
if (ref.current && !ref.current.contains(e.target as Node)) onClose();
|
|
39
|
+
};
|
|
40
|
+
const onKeyDown = (e: KeyboardEvent) => {
|
|
41
|
+
if (e.key === "Escape") onClose();
|
|
42
|
+
};
|
|
43
|
+
document.addEventListener("pointerdown", onPointerDown);
|
|
44
|
+
document.addEventListener("keydown", onKeyDown);
|
|
45
|
+
return () => {
|
|
46
|
+
document.removeEventListener("pointerdown", onPointerDown);
|
|
47
|
+
document.removeEventListener("keydown", onKeyDown);
|
|
48
|
+
};
|
|
49
|
+
}, [open, onClose]);
|
|
50
|
+
|
|
51
|
+
if (!open) return null;
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<div
|
|
55
|
+
ref={ref}
|
|
56
|
+
role="menu"
|
|
57
|
+
style={{ position: "fixed", top: y, left: x }}
|
|
58
|
+
className={clsx(
|
|
59
|
+
"z-50 flex min-w-[216px] flex-col py-xs",
|
|
60
|
+
"rounded-md border border-border-secondary-alt bg-bg-primary font-body shadow-lg",
|
|
61
|
+
className,
|
|
62
|
+
)}
|
|
63
|
+
>
|
|
64
|
+
{children}
|
|
65
|
+
</div>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { useCallback, useState } from "react";
|
|
2
|
+
|
|
3
|
+
export interface ContextMenuState {
|
|
4
|
+
open: boolean;
|
|
5
|
+
x: number;
|
|
6
|
+
y: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface UseContextMenuReturn extends ContextMenuState {
|
|
10
|
+
/** Attach to the element that should open the menu on right-click. */
|
|
11
|
+
onContextMenu: (e: { preventDefault: () => void; clientX: number; clientY: number }) => void;
|
|
12
|
+
/** Close the menu. */
|
|
13
|
+
close: () => void;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Captures right-click position and manages open state for a ContextMenu.
|
|
18
|
+
*
|
|
19
|
+
* ```tsx
|
|
20
|
+
* const menu = useContextMenu();
|
|
21
|
+
* <div onContextMenu={menu.onContextMenu}>…</div>
|
|
22
|
+
* <ContextMenu open={menu.open} x={menu.x} y={menu.y} onClose={menu.close}>
|
|
23
|
+
* <DropdownMenuListItem>Cut</DropdownMenuListItem>
|
|
24
|
+
* </ContextMenu>
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export function useContextMenu(): UseContextMenuReturn {
|
|
28
|
+
const [state, setState] = useState<ContextMenuState>({ open: false, x: 0, y: 0 });
|
|
29
|
+
|
|
30
|
+
const onContextMenu = useCallback(
|
|
31
|
+
(e: { preventDefault: () => void; clientX: number; clientY: number }) => {
|
|
32
|
+
e.preventDefault();
|
|
33
|
+
setState({ open: true, x: e.clientX, y: e.clientY });
|
|
34
|
+
},
|
|
35
|
+
[],
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
const close = useCallback(() => setState((s) => ({ ...s, open: false })), []);
|
|
39
|
+
|
|
40
|
+
return { ...state, onContextMenu, close };
|
|
41
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { ButtonHTMLAttributes, ReactNode } from "react";
|
|
2
|
+
import clsx from "clsx";
|
|
3
|
+
|
|
4
|
+
export type DatePickerCellType = "default" | "active" | "selected" | "today";
|
|
5
|
+
|
|
6
|
+
export interface DatePickerCellProps
|
|
7
|
+
extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, "type"> {
|
|
8
|
+
/** Day number (or any short label). */
|
|
9
|
+
date: ReactNode;
|
|
10
|
+
/** Visual role of the cell within the calendar grid. */
|
|
11
|
+
cellType?: DatePickerCellType;
|
|
12
|
+
/** Shows the small "has events" dot under the date. */
|
|
13
|
+
hasDot?: boolean;
|
|
14
|
+
/** Range-fill connector on the left edge (cell is mid/end of a range). */
|
|
15
|
+
leftConnector?: boolean;
|
|
16
|
+
/** Range-fill connector on the right edge (cell is start/mid of a range). */
|
|
17
|
+
rightConnector?: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const cellClasses: Record<DatePickerCellType, string> = {
|
|
21
|
+
default: "text-text-secondary hover:bg-bg-primary-hover hover:text-text-secondary-hover",
|
|
22
|
+
active: "bg-bg-secondary font-medium text-text-secondary hover:bg-bg-secondary-hover",
|
|
23
|
+
today: "bg-bg-secondary font-medium text-text-secondary hover:bg-bg-secondary-hover",
|
|
24
|
+
selected: "bg-bg-brand-solid font-medium text-text-white hover:bg-bg-brand-solid-hover",
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* A single day cell in the date-picker calendar grid. Distinct from the
|
|
29
|
+
* Calendar page's own `CalendarCell` — this one is round, supports range
|
|
30
|
+
* connectors, and a selected (brand-filled) state.
|
|
31
|
+
*/
|
|
32
|
+
export function DatePickerCell({
|
|
33
|
+
date,
|
|
34
|
+
cellType = "default",
|
|
35
|
+
hasDot = false,
|
|
36
|
+
leftConnector = false,
|
|
37
|
+
rightConnector = false,
|
|
38
|
+
disabled = false,
|
|
39
|
+
className,
|
|
40
|
+
...rest
|
|
41
|
+
}: DatePickerCellProps) {
|
|
42
|
+
const isRange = cellType !== "default";
|
|
43
|
+
return (
|
|
44
|
+
<div className="relative size-10">
|
|
45
|
+
{isRange && leftConnector ? (
|
|
46
|
+
<span className="absolute left-0 top-0 h-10 w-1/2 bg-bg-secondary" aria-hidden />
|
|
47
|
+
) : null}
|
|
48
|
+
{isRange && rightConnector ? (
|
|
49
|
+
<span className="absolute right-0 top-0 h-10 w-1/2 bg-bg-secondary" aria-hidden />
|
|
50
|
+
) : null}
|
|
51
|
+
<button
|
|
52
|
+
type="button"
|
|
53
|
+
disabled={disabled}
|
|
54
|
+
className={clsx(
|
|
55
|
+
"relative flex size-10 items-center justify-center rounded-full text-sm font-body",
|
|
56
|
+
"outline-none transition-colors",
|
|
57
|
+
"focus-visible:ring-2 focus-visible:ring-border-brand focus-visible:ring-offset-2 focus-visible:ring-offset-bg-primary",
|
|
58
|
+
cellClasses[cellType],
|
|
59
|
+
disabled && "pointer-events-none opacity-50",
|
|
60
|
+
className,
|
|
61
|
+
)}
|
|
62
|
+
{...rest}
|
|
63
|
+
>
|
|
64
|
+
{date}
|
|
65
|
+
{hasDot ? (
|
|
66
|
+
<span
|
|
67
|
+
className={clsx(
|
|
68
|
+
"absolute bottom-1 left-1/2 size-[5px] -translate-x-1/2 rounded-full",
|
|
69
|
+
cellType === "selected" ? "bg-text-white" : "bg-bg-brand-solid",
|
|
70
|
+
)}
|
|
71
|
+
aria-hidden
|
|
72
|
+
/>
|
|
73
|
+
) : null}
|
|
74
|
+
</button>
|
|
75
|
+
</div>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { ButtonHTMLAttributes, ReactNode } from "react";
|
|
2
|
+
import clsx from "clsx";
|
|
3
|
+
|
|
4
|
+
export interface DatePickerListItemProps
|
|
5
|
+
extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
6
|
+
/** Marks the item as the current selection (filled background). */
|
|
7
|
+
selected?: boolean;
|
|
8
|
+
children: ReactNode;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* A selectable row in a date-picker preset list (e.g. "Today", "Last 7 days").
|
|
13
|
+
* Hover and focus are handled natively; pass `selected` for the active row.
|
|
14
|
+
*/
|
|
15
|
+
export function DatePickerListItem({
|
|
16
|
+
selected = false,
|
|
17
|
+
children,
|
|
18
|
+
className,
|
|
19
|
+
...rest
|
|
20
|
+
}: DatePickerListItemProps) {
|
|
21
|
+
return (
|
|
22
|
+
<button
|
|
23
|
+
type="button"
|
|
24
|
+
aria-pressed={selected}
|
|
25
|
+
className={clsx(
|
|
26
|
+
"flex w-[160px] items-center overflow-hidden rounded-sm px-lg py-md text-left font-body text-sm font-medium",
|
|
27
|
+
"outline-none transition-colors",
|
|
28
|
+
"focus-visible:ring-2 focus-visible:ring-border-brand focus-visible:ring-offset-2 focus-visible:ring-offset-bg-primary",
|
|
29
|
+
selected
|
|
30
|
+
? "bg-bg-secondary text-text-secondary-hover hover:bg-bg-secondary-hover"
|
|
31
|
+
: "text-text-secondary hover:bg-bg-primary-hover hover:text-text-secondary-hover",
|
|
32
|
+
className,
|
|
33
|
+
)}
|
|
34
|
+
{...rest}
|
|
35
|
+
>
|
|
36
|
+
{children}
|
|
37
|
+
</button>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import clsx from "clsx";
|
|
3
|
+
|
|
4
|
+
export interface DatePickerMenuProps {
|
|
5
|
+
/** Current month label, e.g. "January 2027". */
|
|
6
|
+
monthLabel: ReactNode;
|
|
7
|
+
/** Previous-month handler. */
|
|
8
|
+
onPrevMonth?: () => void;
|
|
9
|
+
/** Next-month handler. */
|
|
10
|
+
onNextMonth?: () => void;
|
|
11
|
+
/** Weekday column headers. Defaults to Mo–Su. */
|
|
12
|
+
weekdays?: string[];
|
|
13
|
+
/**
|
|
14
|
+
* Optional row above the grid (e.g. a date input + "Today" button).
|
|
15
|
+
* Compose your own controls here.
|
|
16
|
+
*/
|
|
17
|
+
toolbar?: ReactNode;
|
|
18
|
+
/** The day grid — typically 35–42 DatePickerCell elements. */
|
|
19
|
+
children: ReactNode;
|
|
20
|
+
/** Cancel handler (renders the footer when set with onApply). */
|
|
21
|
+
onCancel?: () => void;
|
|
22
|
+
/** Apply handler (renders the footer when set with onCancel). */
|
|
23
|
+
onApply?: () => void;
|
|
24
|
+
cancelLabel?: ReactNode;
|
|
25
|
+
applyLabel?: ReactNode;
|
|
26
|
+
className?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const defaultWeekdays = ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"];
|
|
30
|
+
|
|
31
|
+
function Chevron({ left }: { left?: boolean }) {
|
|
32
|
+
return (
|
|
33
|
+
<svg viewBox="0 0 20 20" fill="none" className="size-5" aria-hidden>
|
|
34
|
+
<path
|
|
35
|
+
d={left ? "M12.5 15 7.5 10l5-5" : "M7.5 5l5 5-5 5"}
|
|
36
|
+
stroke="currentColor"
|
|
37
|
+
strokeWidth="1.667"
|
|
38
|
+
strokeLinecap="round"
|
|
39
|
+
strokeLinejoin="round"
|
|
40
|
+
/>
|
|
41
|
+
</svg>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function NavButton({
|
|
46
|
+
left,
|
|
47
|
+
onClick,
|
|
48
|
+
}: {
|
|
49
|
+
left?: boolean;
|
|
50
|
+
onClick?: () => void;
|
|
51
|
+
}) {
|
|
52
|
+
return (
|
|
53
|
+
<button
|
|
54
|
+
type="button"
|
|
55
|
+
onClick={onClick}
|
|
56
|
+
aria-label={left ? "Previous month" : "Next month"}
|
|
57
|
+
className="flex size-8 items-center justify-center rounded-sm text-fg-quaternary outline-none transition-colors hover:bg-bg-primary-hover hover:text-fg-quaternary-hover focus-visible:ring-2 focus-visible:ring-border-brand"
|
|
58
|
+
>
|
|
59
|
+
<Chevron left={left} />
|
|
60
|
+
</button>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* The calendar panel of a date picker — month navigation, weekday header,
|
|
66
|
+
* a day grid (compose DatePickerCell children), and optional toolbar/footer.
|
|
67
|
+
* Date math is the consumer's responsibility; this renders the chrome.
|
|
68
|
+
*/
|
|
69
|
+
export function DatePickerMenu({
|
|
70
|
+
monthLabel,
|
|
71
|
+
onPrevMonth,
|
|
72
|
+
onNextMonth,
|
|
73
|
+
weekdays = defaultWeekdays,
|
|
74
|
+
toolbar,
|
|
75
|
+
children,
|
|
76
|
+
onCancel,
|
|
77
|
+
onApply,
|
|
78
|
+
cancelLabel = "Cancel",
|
|
79
|
+
applyLabel = "Apply",
|
|
80
|
+
className,
|
|
81
|
+
}: DatePickerMenuProps) {
|
|
82
|
+
const showFooter = Boolean(onCancel || onApply);
|
|
83
|
+
return (
|
|
84
|
+
<div
|
|
85
|
+
className={clsx(
|
|
86
|
+
"w-[328px] overflow-hidden rounded-2xl border border-border-secondary-alt bg-bg-primary font-body shadow-xl",
|
|
87
|
+
className,
|
|
88
|
+
)}
|
|
89
|
+
>
|
|
90
|
+
<div className="flex flex-col gap-xl px-3xl py-2xl">
|
|
91
|
+
<div className="flex items-center justify-between">
|
|
92
|
+
<NavButton left onClick={onPrevMonth} />
|
|
93
|
+
<span className="text-sm font-semibold text-text-secondary">
|
|
94
|
+
{monthLabel}
|
|
95
|
+
</span>
|
|
96
|
+
<NavButton onClick={onNextMonth} />
|
|
97
|
+
</div>
|
|
98
|
+
{toolbar}
|
|
99
|
+
<div className="flex flex-wrap gap-y-xs">
|
|
100
|
+
{weekdays.map((wd) => (
|
|
101
|
+
<span
|
|
102
|
+
key={wd}
|
|
103
|
+
className="flex size-10 items-center justify-center text-sm font-medium text-text-secondary"
|
|
104
|
+
>
|
|
105
|
+
{wd}
|
|
106
|
+
</span>
|
|
107
|
+
))}
|
|
108
|
+
{children}
|
|
109
|
+
</div>
|
|
110
|
+
</div>
|
|
111
|
+
{showFooter ? (
|
|
112
|
+
<div className="flex gap-lg border-t border-border-secondary p-xl">
|
|
113
|
+
<button
|
|
114
|
+
type="button"
|
|
115
|
+
onClick={onCancel}
|
|
116
|
+
className="flex-1 rounded-md border border-border-primary bg-bg-primary px-lg py-md text-sm font-semibold text-text-secondary shadow-xs"
|
|
117
|
+
>
|
|
118
|
+
{cancelLabel}
|
|
119
|
+
</button>
|
|
120
|
+
<button
|
|
121
|
+
type="button"
|
|
122
|
+
onClick={onApply}
|
|
123
|
+
className="flex-1 rounded-md bg-bg-brand-solid px-lg py-md text-sm font-semibold text-text-white shadow-xs"
|
|
124
|
+
>
|
|
125
|
+
{applyLabel}
|
|
126
|
+
</button>
|
|
127
|
+
</div>
|
|
128
|
+
) : null}
|
|
129
|
+
</div>
|
|
130
|
+
);
|
|
131
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { ButtonHTMLAttributes, ReactNode } from "react";
|
|
2
|
+
import clsx from "clsx";
|
|
3
|
+
|
|
4
|
+
export interface DropdownAccountListItemProps
|
|
5
|
+
extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, "name"> {
|
|
6
|
+
/** Square avatar (compose an avatar/image). */
|
|
7
|
+
avatar?: ReactNode;
|
|
8
|
+
/** Account name. */
|
|
9
|
+
name: ReactNode;
|
|
10
|
+
/** Account email / supporting line. */
|
|
11
|
+
email?: ReactNode;
|
|
12
|
+
/** Whether this account is the active selection (filled radio). */
|
|
13
|
+
selected?: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* An account-switcher row for a dropdown menu — square avatar, name + email,
|
|
18
|
+
* and a trailing radio indicator. Hover handled natively.
|
|
19
|
+
*/
|
|
20
|
+
export function DropdownAccountListItem({
|
|
21
|
+
avatar,
|
|
22
|
+
name,
|
|
23
|
+
email,
|
|
24
|
+
selected = false,
|
|
25
|
+
className,
|
|
26
|
+
...rest
|
|
27
|
+
}: DropdownAccountListItemProps) {
|
|
28
|
+
return (
|
|
29
|
+
<button
|
|
30
|
+
type="button"
|
|
31
|
+
role="menuitemradio"
|
|
32
|
+
aria-checked={selected}
|
|
33
|
+
className={clsx(
|
|
34
|
+
"flex w-full items-start gap-sm rounded-md p-md text-left font-body",
|
|
35
|
+
"outline-none transition-colors hover:bg-bg-primary-hover",
|
|
36
|
+
"focus-visible:ring-2 focus-visible:ring-border-brand",
|
|
37
|
+
className,
|
|
38
|
+
)}
|
|
39
|
+
{...rest}
|
|
40
|
+
>
|
|
41
|
+
{avatar ? (
|
|
42
|
+
<span className="flex size-9 shrink-0 items-center justify-center overflow-hidden rounded-lg border-[0.5px] border-border-secondary bg-bg-primary p-[2px] shadow-md">
|
|
43
|
+
<span className="size-8 overflow-hidden rounded-md">{avatar}</span>
|
|
44
|
+
</span>
|
|
45
|
+
) : null}
|
|
46
|
+
<span className="flex min-w-0 flex-1 flex-col">
|
|
47
|
+
<span className="truncate text-sm font-semibold text-text-primary">
|
|
48
|
+
{name}
|
|
49
|
+
</span>
|
|
50
|
+
{email ? (
|
|
51
|
+
<span className="truncate text-sm font-normal text-text-tertiary">
|
|
52
|
+
{email}
|
|
53
|
+
</span>
|
|
54
|
+
) : null}
|
|
55
|
+
</span>
|
|
56
|
+
<span
|
|
57
|
+
className={clsx(
|
|
58
|
+
"mt-px flex size-4 shrink-0 items-center justify-center rounded-full border",
|
|
59
|
+
selected ? "border-border-brand" : "border-border-primary",
|
|
60
|
+
)}
|
|
61
|
+
aria-hidden
|
|
62
|
+
>
|
|
63
|
+
{selected ? (
|
|
64
|
+
<span className="size-1.5 rounded-full bg-fg-brand-primary" />
|
|
65
|
+
) : null}
|
|
66
|
+
</span>
|
|
67
|
+
</button>
|
|
68
|
+
);
|
|
69
|
+
}
|