@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,124 @@
|
|
|
1
|
+
import { type HTMLAttributes, type ReactNode } from "react";
|
|
2
|
+
import clsx from "clsx";
|
|
3
|
+
|
|
4
|
+
export type NavAccountCardVariant = "simple" | "card";
|
|
5
|
+
|
|
6
|
+
export interface NavAccountCardProps
|
|
7
|
+
extends Omit<HTMLAttributes<HTMLDivElement>, "title"> {
|
|
8
|
+
/** `simple` = inline row + sign-out button · `card` = boxed trigger that opens a menu. */
|
|
9
|
+
variant?: NavAccountCardVariant;
|
|
10
|
+
/** card only — whether the dropdown menu is shown. */
|
|
11
|
+
open?: boolean;
|
|
12
|
+
avatar?: ReactNode;
|
|
13
|
+
name?: ReactNode;
|
|
14
|
+
email?: ReactNode;
|
|
15
|
+
/** card only — toggles the dropdown (fires on trigger click). */
|
|
16
|
+
onToggle?: () => void;
|
|
17
|
+
/** simple only — sign-out button handler. */
|
|
18
|
+
onSignOut?: () => void;
|
|
19
|
+
/**
|
|
20
|
+
* card + open — dropdown content. Compose `NavAccountCardMenuItem` rows,
|
|
21
|
+
* dividers and an add-account / sign-out footer here.
|
|
22
|
+
*/
|
|
23
|
+
menu?: ReactNode;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const ChevronSelector = () => (
|
|
27
|
+
<svg viewBox="0 0 16 16" fill="none" className="size-4" aria-hidden>
|
|
28
|
+
<path d="M5 6l3-3 3 3M5 10l3 3 3-3" stroke="currentColor" strokeWidth="1.33" strokeLinecap="round" strokeLinejoin="round" />
|
|
29
|
+
</svg>
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
const LogOut = () => (
|
|
33
|
+
<svg viewBox="0 0 16 16" fill="none" className="size-4" aria-hidden>
|
|
34
|
+
<path
|
|
35
|
+
d="M10.67 11.33 14 8m0 0-3.33-3.33M14 8H6M6 2H4.67c-.93 0-1.4 0-1.76.18-.31.16-.57.42-.73.73C2 3.27 2 3.73 2 4.67v6.66c0 .94 0 1.4.18 1.76.16.31.42.57.73.73.36.18.83.18 1.76.18H6"
|
|
36
|
+
stroke="currentColor"
|
|
37
|
+
strokeWidth="1.33"
|
|
38
|
+
strokeLinecap="round"
|
|
39
|
+
strokeLinejoin="round"
|
|
40
|
+
/>
|
|
41
|
+
</svg>
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
const AvatarLabel = ({ avatar, name, email }: { avatar?: ReactNode; name?: ReactNode; email?: ReactNode }) => (
|
|
45
|
+
<div className="flex min-w-0 flex-1 items-center gap-md">
|
|
46
|
+
{avatar && <span className="shrink-0">{avatar}</span>}
|
|
47
|
+
<div className="flex min-w-0 flex-col text-left">
|
|
48
|
+
<span className="truncate text-sm font-semibold text-text-primary">{name}</span>
|
|
49
|
+
<span className="truncate text-sm text-text-tertiary">{email}</span>
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Account switcher for the bottom of a sidebar. `simple` renders an inline row
|
|
56
|
+
* with a sign-out button; `card` renders a boxed trigger that, when `open`,
|
|
57
|
+
* reveals a dropdown (the `menu` slot — compose `NavAccountCardMenuItem` rows).
|
|
58
|
+
*
|
|
59
|
+
* The active account is marked with `bg-brand-solid` (the radio inside the
|
|
60
|
+
* account menu items). Note: Figma opens the desktop menu as a right-side
|
|
61
|
+
* flyout; this opens it above the trigger (`bottom-full`) — a layout
|
|
62
|
+
* simplification (see figma-map). Width defaults to 280px; override via
|
|
63
|
+
* `className` for mobile (256px).
|
|
64
|
+
*/
|
|
65
|
+
export function NavAccountCard({
|
|
66
|
+
variant = "card",
|
|
67
|
+
open = false,
|
|
68
|
+
avatar,
|
|
69
|
+
name,
|
|
70
|
+
email,
|
|
71
|
+
onToggle,
|
|
72
|
+
onSignOut,
|
|
73
|
+
menu,
|
|
74
|
+
className,
|
|
75
|
+
...rest
|
|
76
|
+
}: NavAccountCardProps) {
|
|
77
|
+
if (variant === "simple") {
|
|
78
|
+
return (
|
|
79
|
+
<div
|
|
80
|
+
className={clsx(
|
|
81
|
+
"relative flex w-[280px] items-start gap-xl border-t border-border-secondary px-md pt-2xl",
|
|
82
|
+
className,
|
|
83
|
+
)}
|
|
84
|
+
{...rest}
|
|
85
|
+
>
|
|
86
|
+
<AvatarLabel avatar={avatar} name={name} email={email} />
|
|
87
|
+
<button
|
|
88
|
+
type="button"
|
|
89
|
+
onClick={onSignOut}
|
|
90
|
+
aria-label="Sign out"
|
|
91
|
+
className="absolute right-0 top-[15px] flex items-center justify-center rounded-sm p-sm text-fg-quaternary transition-colors hover:text-fg-quaternary-hover"
|
|
92
|
+
>
|
|
93
|
+
<LogOut />
|
|
94
|
+
</button>
|
|
95
|
+
</div>
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return (
|
|
100
|
+
<div className={clsx("relative w-[280px]", className)} {...rest}>
|
|
101
|
+
<button
|
|
102
|
+
type="button"
|
|
103
|
+
onClick={onToggle}
|
|
104
|
+
className="relative flex w-full items-start gap-xl rounded-xl border border-border-secondary bg-bg-primary-alt p-lg text-left shadow-xs"
|
|
105
|
+
>
|
|
106
|
+
<AvatarLabel avatar={avatar} name={name} email={email} />
|
|
107
|
+
<span
|
|
108
|
+
className={clsx(
|
|
109
|
+
"absolute right-[7px] top-[7px] flex items-center justify-center rounded-sm p-sm text-fg-quaternary",
|
|
110
|
+
open && "bg-bg-primary-hover",
|
|
111
|
+
)}
|
|
112
|
+
>
|
|
113
|
+
<ChevronSelector />
|
|
114
|
+
</span>
|
|
115
|
+
</button>
|
|
116
|
+
|
|
117
|
+
{open && (
|
|
118
|
+
<div className="absolute bottom-full left-0 z-10 mb-2 w-full overflow-hidden rounded-xl border border-border-secondary-alt bg-bg-secondary-alt shadow-lg">
|
|
119
|
+
{menu}
|
|
120
|
+
</div>
|
|
121
|
+
)}
|
|
122
|
+
</div>
|
|
123
|
+
);
|
|
124
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { type ButtonHTMLAttributes, type ReactNode } from "react";
|
|
2
|
+
import clsx from "clsx";
|
|
3
|
+
|
|
4
|
+
export type NavAccountCardMenuItemType = "menu-item" | "account";
|
|
5
|
+
|
|
6
|
+
export interface NavAccountCardMenuItemProps
|
|
7
|
+
extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, "name" | "type"> {
|
|
8
|
+
/** `menu-item` = icon + label (+ shortcut) · `account` = avatar + name/email + radio. */
|
|
9
|
+
type?: NavAccountCardMenuItemType;
|
|
10
|
+
/** Marks the row as the active account / current item (radio filled, hover bg). */
|
|
11
|
+
current?: boolean;
|
|
12
|
+
/** menu-item: leading icon (20px). */
|
|
13
|
+
icon?: ReactNode;
|
|
14
|
+
/** menu-item: row label. */
|
|
15
|
+
label?: ReactNode;
|
|
16
|
+
/** menu-item: trailing keyboard-shortcut chip. */
|
|
17
|
+
shortcut?: ReactNode;
|
|
18
|
+
/** account: avatar slot (40px). */
|
|
19
|
+
avatar?: ReactNode;
|
|
20
|
+
/** account: display name. */
|
|
21
|
+
name?: ReactNode;
|
|
22
|
+
/** account: secondary line (email). */
|
|
23
|
+
email?: ReactNode;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* A single row inside `NavAccountCard`'s dropdown. Two shapes:
|
|
28
|
+
* - `menu-item` — icon + label + optional shortcut chip (View profile, Settings…).
|
|
29
|
+
* - `account` — avatar + name/email with a radio indicator; `current` fills the
|
|
30
|
+
* radio with `bg-brand-solid` (the active-account marker).
|
|
31
|
+
*/
|
|
32
|
+
export function NavAccountCardMenuItem({
|
|
33
|
+
type = "menu-item",
|
|
34
|
+
current = false,
|
|
35
|
+
icon,
|
|
36
|
+
label,
|
|
37
|
+
shortcut,
|
|
38
|
+
avatar,
|
|
39
|
+
name,
|
|
40
|
+
email,
|
|
41
|
+
className,
|
|
42
|
+
...rest
|
|
43
|
+
}: NavAccountCardMenuItemProps) {
|
|
44
|
+
return (
|
|
45
|
+
<button
|
|
46
|
+
type="button"
|
|
47
|
+
className={clsx(
|
|
48
|
+
"flex w-full items-center px-sm text-left",
|
|
49
|
+
className,
|
|
50
|
+
)}
|
|
51
|
+
{...rest}
|
|
52
|
+
>
|
|
53
|
+
<div
|
|
54
|
+
className={clsx(
|
|
55
|
+
"flex min-w-0 flex-1 items-center gap-lg overflow-hidden rounded-sm p-md transition-colors",
|
|
56
|
+
current ? "bg-bg-primary-hover" : "hover:bg-bg-primary-hover",
|
|
57
|
+
)}
|
|
58
|
+
>
|
|
59
|
+
{type === "menu-item" ? (
|
|
60
|
+
<>
|
|
61
|
+
<div className="flex min-w-0 flex-1 items-center gap-md">
|
|
62
|
+
{icon && (
|
|
63
|
+
<span className="flex size-5 shrink-0 items-center justify-center text-fg-quaternary [&>svg]:size-5">
|
|
64
|
+
{icon}
|
|
65
|
+
</span>
|
|
66
|
+
)}
|
|
67
|
+
<span className="min-w-0 flex-1 text-sm font-semibold text-text-secondary">
|
|
68
|
+
{label}
|
|
69
|
+
</span>
|
|
70
|
+
</div>
|
|
71
|
+
{shortcut && (
|
|
72
|
+
<span className="shrink-0 rounded-xs border border-border-secondary px-xs py-px text-xs font-medium text-text-tertiary">
|
|
73
|
+
{shortcut}
|
|
74
|
+
</span>
|
|
75
|
+
)}
|
|
76
|
+
</>
|
|
77
|
+
) : (
|
|
78
|
+
<>
|
|
79
|
+
<div className="flex min-w-0 flex-1 items-center gap-md">
|
|
80
|
+
{avatar && <span className="shrink-0">{avatar}</span>}
|
|
81
|
+
<span className="flex min-w-0 flex-col">
|
|
82
|
+
<span className="truncate text-sm font-semibold text-text-primary">{name}</span>
|
|
83
|
+
<span className="truncate text-sm text-text-tertiary">{email}</span>
|
|
84
|
+
</span>
|
|
85
|
+
</div>
|
|
86
|
+
<span
|
|
87
|
+
className={clsx(
|
|
88
|
+
"flex size-4 shrink-0 items-center justify-center rounded-full",
|
|
89
|
+
current
|
|
90
|
+
? "bg-bg-brand-solid"
|
|
91
|
+
: "border border-border-primary",
|
|
92
|
+
)}
|
|
93
|
+
>
|
|
94
|
+
{current && <span className="size-1.5 rounded-full bg-fg-white" />}
|
|
95
|
+
</span>
|
|
96
|
+
</>
|
|
97
|
+
)}
|
|
98
|
+
</div>
|
|
99
|
+
</button>
|
|
100
|
+
);
|
|
101
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { type ButtonHTMLAttributes, type ReactNode } from "react";
|
|
2
|
+
import clsx from "clsx";
|
|
3
|
+
|
|
4
|
+
export interface NavButtonProps
|
|
5
|
+
extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, "children"> {
|
|
6
|
+
/** Active route — applies the selected (`bg-secondary`) background. */
|
|
7
|
+
current?: boolean;
|
|
8
|
+
/** Render only the leading icon (square button). */
|
|
9
|
+
iconOnly?: boolean;
|
|
10
|
+
/** Leading icon (20px). */
|
|
11
|
+
icon?: ReactNode;
|
|
12
|
+
/** Label text (hidden when `iconOnly`). */
|
|
13
|
+
children?: ReactNode;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Header-nav button (the horizontal top-bar variant, distinct from the sidebar
|
|
18
|
+
* `NavItemBase`). Pill shape; `current` marks the active route. Focus ring uses
|
|
19
|
+
* the brand focus colour.
|
|
20
|
+
*/
|
|
21
|
+
export function NavButton({
|
|
22
|
+
current = false,
|
|
23
|
+
iconOnly = false,
|
|
24
|
+
icon,
|
|
25
|
+
children,
|
|
26
|
+
className,
|
|
27
|
+
type = "button",
|
|
28
|
+
...rest
|
|
29
|
+
}: NavButtonProps) {
|
|
30
|
+
return (
|
|
31
|
+
<button
|
|
32
|
+
type={type}
|
|
33
|
+
className={clsx(
|
|
34
|
+
"inline-flex items-center justify-center gap-xs overflow-hidden rounded-sm text-sm font-semibold text-text-secondary outline-none transition-colors",
|
|
35
|
+
"focus-visible:ring-2 focus-visible:ring-utility-brand-500 focus-visible:ring-offset-2",
|
|
36
|
+
iconOnly ? "p-md" : "px-md py-sm",
|
|
37
|
+
current ? "bg-bg-secondary hover:bg-bg-secondary-hover" : "hover:bg-bg-primary-hover",
|
|
38
|
+
className,
|
|
39
|
+
)}
|
|
40
|
+
{...rest}
|
|
41
|
+
>
|
|
42
|
+
{icon && (
|
|
43
|
+
<span className="flex size-5 shrink-0 items-center justify-center [&>svg]:size-5">
|
|
44
|
+
{icon}
|
|
45
|
+
</span>
|
|
46
|
+
)}
|
|
47
|
+
{!iconOnly && <span className="px-xxs">{children}</span>}
|
|
48
|
+
</button>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { type HTMLAttributes, type ReactNode } from "react";
|
|
2
|
+
import clsx from "clsx";
|
|
3
|
+
|
|
4
|
+
export interface NavFeaturedCardProps
|
|
5
|
+
extends Omit<HTMLAttributes<HTMLDivElement>, "title"> {
|
|
6
|
+
/** Featured-icon / media slot rendered at the top. */
|
|
7
|
+
icon?: ReactNode;
|
|
8
|
+
title?: ReactNode;
|
|
9
|
+
subtitle?: ReactNode;
|
|
10
|
+
/** Body content — progress bar, list, QR code, input, etc. */
|
|
11
|
+
children?: ReactNode;
|
|
12
|
+
/** CTA row — compose Button(s) / link buttons. */
|
|
13
|
+
action?: ReactNode;
|
|
14
|
+
/** Bordered surface (`bg-primary` + border) vs subtle (`bg-secondary`). */
|
|
15
|
+
bordered?: boolean;
|
|
16
|
+
/** Renders an x-close button top-right. */
|
|
17
|
+
onClose?: () => void;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const XClose = () => (
|
|
21
|
+
<svg viewBox="0 0 20 20" fill="none" className="size-5" aria-hidden>
|
|
22
|
+
<path d="M15 5 5 15M5 5l10 10" stroke="currentColor" strokeWidth="1.67" strokeLinecap="round" strokeLinejoin="round" />
|
|
23
|
+
</svg>
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Featured card for the bottom of a sidebar — a generic shell, not 13 variants.
|
|
28
|
+
* The Figma `Type` set (Progress bar, Upgrade CTA, Message, Current projects,
|
|
29
|
+
* QR code, …) is expressed by filling the slots: `icon` / `title` / `subtitle`
|
|
30
|
+
* for the header, `children` for the body (progress bar, list, QR, input…), and
|
|
31
|
+
* `action` for the CTA row. See the stories for the 12 type recipes.
|
|
32
|
+
*/
|
|
33
|
+
export function NavFeaturedCard({
|
|
34
|
+
icon,
|
|
35
|
+
title,
|
|
36
|
+
subtitle,
|
|
37
|
+
children,
|
|
38
|
+
action,
|
|
39
|
+
bordered = false,
|
|
40
|
+
onClose,
|
|
41
|
+
className,
|
|
42
|
+
...rest
|
|
43
|
+
}: NavFeaturedCardProps) {
|
|
44
|
+
const hasHeader = icon || title || subtitle;
|
|
45
|
+
return (
|
|
46
|
+
<div
|
|
47
|
+
className={clsx(
|
|
48
|
+
"relative flex w-[248px] flex-col gap-xl rounded-xl p-xl",
|
|
49
|
+
bordered ? "border border-border-secondary bg-bg-primary" : "bg-bg-secondary",
|
|
50
|
+
className,
|
|
51
|
+
)}
|
|
52
|
+
{...rest}
|
|
53
|
+
>
|
|
54
|
+
{onClose && (
|
|
55
|
+
<button
|
|
56
|
+
type="button"
|
|
57
|
+
onClick={onClose}
|
|
58
|
+
aria-label="Dismiss"
|
|
59
|
+
className="absolute right-1 top-1 flex size-9 items-center justify-center rounded-md text-fg-quaternary transition-colors hover:text-fg-quaternary-hover"
|
|
60
|
+
>
|
|
61
|
+
<XClose />
|
|
62
|
+
</button>
|
|
63
|
+
)}
|
|
64
|
+
|
|
65
|
+
{hasHeader && (
|
|
66
|
+
<div className="flex flex-col gap-xs">
|
|
67
|
+
{icon && <div className="shrink-0">{icon}</div>}
|
|
68
|
+
{(title || subtitle) && (
|
|
69
|
+
<div className="flex flex-col gap-xs pr-3xl">
|
|
70
|
+
{title && <p className="text-sm font-semibold text-text-primary">{title}</p>}
|
|
71
|
+
{subtitle && <p className="text-sm text-text-tertiary">{subtitle}</p>}
|
|
72
|
+
</div>
|
|
73
|
+
)}
|
|
74
|
+
</div>
|
|
75
|
+
)}
|
|
76
|
+
|
|
77
|
+
{children}
|
|
78
|
+
|
|
79
|
+
{action && <div className="flex items-center gap-lg">{action}</div>}
|
|
80
|
+
</div>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { type ButtonHTMLAttributes, type ReactNode } from "react";
|
|
2
|
+
import clsx from "clsx";
|
|
3
|
+
|
|
4
|
+
export interface NavItemBaseProps
|
|
5
|
+
extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, "children"> {
|
|
6
|
+
/** Active route — selected (`bg-secondary`) background. */
|
|
7
|
+
current?: boolean;
|
|
8
|
+
/** Leading icon (20px). */
|
|
9
|
+
icon?: ReactNode;
|
|
10
|
+
label?: ReactNode;
|
|
11
|
+
/** Leading status dot (8px, success green). */
|
|
12
|
+
dot?: boolean;
|
|
13
|
+
/** Trailing count badge — pass a number/string. */
|
|
14
|
+
badge?: ReactNode;
|
|
15
|
+
/** Trailing chevron (for expandable groups). */
|
|
16
|
+
trailingChevron?: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const ChevronDown = () => (
|
|
20
|
+
<svg viewBox="0 0 16 16" fill="none" className="size-4" aria-hidden>
|
|
21
|
+
<path d="M4 6l4 4 4-4" stroke="currentColor" strokeWidth="1.33" strokeLinecap="round" strokeLinejoin="round" />
|
|
22
|
+
</svg>
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Base sidebar nav link (272px). `current` selects it; optional leading `dot`,
|
|
27
|
+
* leading `icon`, trailing count `badge`, and `trailingChevron`. Brand focus ring.
|
|
28
|
+
*/
|
|
29
|
+
export function NavItemBase({
|
|
30
|
+
current = false,
|
|
31
|
+
icon,
|
|
32
|
+
label,
|
|
33
|
+
dot = false,
|
|
34
|
+
badge,
|
|
35
|
+
trailingChevron = false,
|
|
36
|
+
className,
|
|
37
|
+
type = "button",
|
|
38
|
+
...rest
|
|
39
|
+
}: NavItemBaseProps) {
|
|
40
|
+
return (
|
|
41
|
+
<button
|
|
42
|
+
type={type}
|
|
43
|
+
className={clsx(
|
|
44
|
+
"flex w-[272px] items-center rounded-sm outline-none focus-visible:ring-2 focus-visible:ring-utility-brand-500 focus-visible:ring-offset-2",
|
|
45
|
+
className,
|
|
46
|
+
)}
|
|
47
|
+
{...rest}
|
|
48
|
+
>
|
|
49
|
+
<div
|
|
50
|
+
className={clsx(
|
|
51
|
+
"flex max-h-9 flex-1 items-center gap-lg rounded-sm p-md transition-colors",
|
|
52
|
+
current ? "bg-bg-secondary" : "hover:bg-bg-primary-hover",
|
|
53
|
+
)}
|
|
54
|
+
>
|
|
55
|
+
<div className="flex min-w-0 flex-1 items-center gap-md">
|
|
56
|
+
{dot && <span className="size-2 shrink-0 rounded-full bg-fg-success-secondary" />}
|
|
57
|
+
{icon && (
|
|
58
|
+
<span className="flex size-5 shrink-0 items-center justify-center text-fg-quaternary [&>svg]:size-5">
|
|
59
|
+
{icon}
|
|
60
|
+
</span>
|
|
61
|
+
)}
|
|
62
|
+
<span className="min-w-0 flex-1 truncate text-left text-sm font-semibold text-text-secondary">
|
|
63
|
+
{label}
|
|
64
|
+
</span>
|
|
65
|
+
</div>
|
|
66
|
+
{badge != null && (
|
|
67
|
+
<span className="shrink-0 rounded-full border border-utility-neutral-200 bg-utility-neutral-50 px-md py-xxs text-xs font-medium text-utility-neutral-700">
|
|
68
|
+
{badge}
|
|
69
|
+
</span>
|
|
70
|
+
)}
|
|
71
|
+
{trailingChevron && (
|
|
72
|
+
<span className="shrink-0 text-fg-quaternary">
|
|
73
|
+
<ChevronDown />
|
|
74
|
+
</span>
|
|
75
|
+
)}
|
|
76
|
+
</div>
|
|
77
|
+
</button>
|
|
78
|
+
);
|
|
79
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { type ButtonHTMLAttributes, type ReactNode } from "react";
|
|
2
|
+
import clsx from "clsx";
|
|
3
|
+
|
|
4
|
+
export interface NavItemDropdownBaseProps
|
|
5
|
+
extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, "children"> {
|
|
6
|
+
/** Active group — selected (`bg-secondary`) background on the trigger. */
|
|
7
|
+
current?: boolean;
|
|
8
|
+
/** Expanded state — reveals the submenu. */
|
|
9
|
+
open?: boolean;
|
|
10
|
+
/** Trigger icon (20px). */
|
|
11
|
+
icon?: ReactNode;
|
|
12
|
+
label?: ReactNode;
|
|
13
|
+
/** Toggles open/closed. */
|
|
14
|
+
onToggle?: () => void;
|
|
15
|
+
/** Submenu rows (rendered when `open`) — compose indented `NavItemBase`s. */
|
|
16
|
+
children?: ReactNode;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const Chevron = ({ up }: { up?: boolean }) => (
|
|
20
|
+
<svg viewBox="0 0 16 16" fill="none" className="size-4" aria-hidden>
|
|
21
|
+
<path d={up ? "M4 10l4-4 4 4" : "M4 6l4 4 4-4"} stroke="currentColor" strokeWidth="1.33" strokeLinecap="round" strokeLinejoin="round" />
|
|
22
|
+
</svg>
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Expandable sidebar nav group (272px). The trigger mirrors `NavItemBase`; when
|
|
27
|
+
* `open`, `children` (indented submenu rows) render below with a chevron flip.
|
|
28
|
+
*/
|
|
29
|
+
export function NavItemDropdownBase({
|
|
30
|
+
current = false,
|
|
31
|
+
open = false,
|
|
32
|
+
icon,
|
|
33
|
+
label,
|
|
34
|
+
onToggle,
|
|
35
|
+
children,
|
|
36
|
+
className,
|
|
37
|
+
type = "button",
|
|
38
|
+
...rest
|
|
39
|
+
}: NavItemDropdownBaseProps) {
|
|
40
|
+
return (
|
|
41
|
+
<div className={clsx("flex w-[272px] flex-col", className)}>
|
|
42
|
+
<button
|
|
43
|
+
type={type}
|
|
44
|
+
onClick={onToggle}
|
|
45
|
+
className="flex items-center rounded-sm outline-none focus-visible:ring-2 focus-visible:ring-utility-brand-500 focus-visible:ring-offset-2"
|
|
46
|
+
{...rest}
|
|
47
|
+
>
|
|
48
|
+
<div
|
|
49
|
+
className={clsx(
|
|
50
|
+
"flex max-h-9 flex-1 items-center gap-lg rounded-sm p-md transition-colors",
|
|
51
|
+
current ? "bg-bg-secondary" : "hover:bg-bg-primary-hover",
|
|
52
|
+
)}
|
|
53
|
+
>
|
|
54
|
+
<div className="flex min-w-0 flex-1 items-center gap-md">
|
|
55
|
+
{icon && (
|
|
56
|
+
<span className="flex size-5 shrink-0 items-center justify-center text-fg-quaternary [&>svg]:size-5">
|
|
57
|
+
{icon}
|
|
58
|
+
</span>
|
|
59
|
+
)}
|
|
60
|
+
<span className="min-w-0 flex-1 truncate text-left text-sm font-semibold text-text-secondary">
|
|
61
|
+
{label}
|
|
62
|
+
</span>
|
|
63
|
+
</div>
|
|
64
|
+
<span className="shrink-0 text-fg-quaternary">
|
|
65
|
+
<Chevron up={open} />
|
|
66
|
+
</span>
|
|
67
|
+
</div>
|
|
68
|
+
</button>
|
|
69
|
+
{open && (
|
|
70
|
+
<div className="flex flex-col pb-xs">{children}</div>
|
|
71
|
+
)}
|
|
72
|
+
</div>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { type ButtonHTMLAttributes } from "react";
|
|
2
|
+
import clsx from "clsx";
|
|
3
|
+
|
|
4
|
+
export interface NavMenuButtonProps
|
|
5
|
+
extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
6
|
+
/** Open state — swaps the hamburger for an x-close icon. */
|
|
7
|
+
opened?: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const Menu = () => (
|
|
11
|
+
<svg viewBox="0 0 20 20" fill="none" className="size-5" aria-hidden>
|
|
12
|
+
<path d="M2.5 10h15M2.5 5h15M2.5 15h15" stroke="currentColor" strokeWidth="1.67" strokeLinecap="round" strokeLinejoin="round" />
|
|
13
|
+
</svg>
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
const XClose = () => (
|
|
17
|
+
<svg viewBox="0 0 20 20" fill="none" className="size-5" aria-hidden>
|
|
18
|
+
<path d="M15 5 5 15M5 5l10 10" stroke="currentColor" strokeWidth="1.67" strokeLinecap="round" strokeLinejoin="round" />
|
|
19
|
+
</svg>
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Hamburger / collapse toggle for the mobile header & sidebar. Shows the
|
|
24
|
+
* hamburger when closed, an x-close (dimmed) when `opened`. Brand focus ring.
|
|
25
|
+
*/
|
|
26
|
+
export function NavMenuButton({
|
|
27
|
+
opened = false,
|
|
28
|
+
className,
|
|
29
|
+
type = "button",
|
|
30
|
+
...rest
|
|
31
|
+
}: NavMenuButtonProps) {
|
|
32
|
+
return (
|
|
33
|
+
<button
|
|
34
|
+
type={type}
|
|
35
|
+
aria-label={opened ? "Close menu" : "Open menu"}
|
|
36
|
+
aria-expanded={opened}
|
|
37
|
+
className={clsx(
|
|
38
|
+
"flex items-center justify-center rounded-md p-md text-fg-secondary outline-none transition-colors",
|
|
39
|
+
"hover:bg-bg-primary-hover focus-visible:ring-2 focus-visible:ring-utility-brand-500 focus-visible:ring-offset-2",
|
|
40
|
+
className,
|
|
41
|
+
)}
|
|
42
|
+
{...rest}
|
|
43
|
+
>
|
|
44
|
+
{opened ? <span className="opacity-70"><XClose /></span> : <Menu />}
|
|
45
|
+
</button>
|
|
46
|
+
);
|
|
47
|
+
}
|