@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
package/package.json
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@borisj74/bv-ds",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "bv-ds — React component library synced from Figma (Untitled UI v8.0), built on Tailwind CSS",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"files": [
|
|
9
|
+
"dist",
|
|
10
|
+
"!dist/*.map",
|
|
11
|
+
"src",
|
|
12
|
+
"!src/**/*.stories.tsx",
|
|
13
|
+
"tailwind-preset.js",
|
|
14
|
+
"LICENSE"
|
|
15
|
+
],
|
|
16
|
+
"main": "./dist/index.cjs",
|
|
17
|
+
"module": "./dist/index.js",
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"types": "./dist/index.d.ts",
|
|
22
|
+
"import": "./dist/index.js",
|
|
23
|
+
"require": "./dist/index.cjs"
|
|
24
|
+
},
|
|
25
|
+
"./tailwind-preset": "./tailwind-preset.js",
|
|
26
|
+
"./package.json": "./package.json"
|
|
27
|
+
},
|
|
28
|
+
"scripts": {
|
|
29
|
+
"build": "tsup",
|
|
30
|
+
"dev": "tsup --watch",
|
|
31
|
+
"typecheck": "tsc --noEmit",
|
|
32
|
+
"storybook": "storybook dev -p 6006",
|
|
33
|
+
"build-storybook": "storybook build",
|
|
34
|
+
"prepublishOnly": "npm run typecheck && npm run build"
|
|
35
|
+
},
|
|
36
|
+
"peerDependencies": {
|
|
37
|
+
"react": ">=18.0.0",
|
|
38
|
+
"react-dom": ">=18.0.0"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@types/react": "^18.2.0",
|
|
42
|
+
"@types/react-dom": "^18.2.0",
|
|
43
|
+
"react": "^18.2.0",
|
|
44
|
+
"react-dom": "^18.2.0",
|
|
45
|
+
"tailwindcss": "^3.4.0",
|
|
46
|
+
"postcss": "^8.4.0",
|
|
47
|
+
"autoprefixer": "^10.4.0",
|
|
48
|
+
"clsx": "^2.1.0",
|
|
49
|
+
"tsup": "^8.0.0",
|
|
50
|
+
"typescript": "^5.4.0",
|
|
51
|
+
"@storybook/react-vite": "^8.0.0",
|
|
52
|
+
"@storybook/addon-essentials": "^8.0.0",
|
|
53
|
+
"@storybook/addon-a11y": "^8.0.0",
|
|
54
|
+
"storybook": "^8.0.0"
|
|
55
|
+
},
|
|
56
|
+
"publishConfig": {
|
|
57
|
+
"access": "public"
|
|
58
|
+
},
|
|
59
|
+
"repository": {
|
|
60
|
+
"type": "git",
|
|
61
|
+
"url": "git+https://github.com/borisj74/DS_BV.git"
|
|
62
|
+
},
|
|
63
|
+
"bugs": {
|
|
64
|
+
"url": "https://github.com/borisj74/DS_BV/issues"
|
|
65
|
+
},
|
|
66
|
+
"homepage": "https://github.com/borisj74/DS_BV#readme"
|
|
67
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Children, Fragment, isValidElement } from "react";
|
|
2
|
+
import type { ReactNode } from "react";
|
|
3
|
+
import clsx from "clsx";
|
|
4
|
+
|
|
5
|
+
export type ActivityFeedDivider = "line" | "connector" | "divider" | "none";
|
|
6
|
+
|
|
7
|
+
export interface ActivityFeedProps {
|
|
8
|
+
/**
|
|
9
|
+
* How feed items are separated (Figma `Divider` variant):
|
|
10
|
+
* - `connector` — items butt together; each child `FeedItemBase` should set
|
|
11
|
+
* `connector` so the avatar lines join into a continuous thread.
|
|
12
|
+
* - `line` — a hairline rule between items.
|
|
13
|
+
* - `divider` — no rule added by the feed; interleave your own labelled date
|
|
14
|
+
* dividers between children.
|
|
15
|
+
* - `none` — plain spacing only.
|
|
16
|
+
*/
|
|
17
|
+
divider?: ActivityFeedDivider;
|
|
18
|
+
/** Feed rows — typically `FeedItemBase` instances. */
|
|
19
|
+
children: ReactNode;
|
|
20
|
+
className?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function ActivityFeed({
|
|
24
|
+
divider = "none",
|
|
25
|
+
children,
|
|
26
|
+
className,
|
|
27
|
+
}: ActivityFeedProps) {
|
|
28
|
+
const items = Children.toArray(children).filter(isValidElement);
|
|
29
|
+
return (
|
|
30
|
+
<div
|
|
31
|
+
role="feed"
|
|
32
|
+
className={clsx(
|
|
33
|
+
"flex w-full flex-col",
|
|
34
|
+
divider === "connector" ? "gap-none" : "gap-xl",
|
|
35
|
+
className,
|
|
36
|
+
)}
|
|
37
|
+
>
|
|
38
|
+
{items.map((child, index) => (
|
|
39
|
+
<Fragment key={child.key ?? index}>
|
|
40
|
+
{divider === "line" && index > 0 ? (
|
|
41
|
+
<div className="h-px w-full bg-border-secondary" aria-hidden />
|
|
42
|
+
) : null}
|
|
43
|
+
{child}
|
|
44
|
+
</Fragment>
|
|
45
|
+
))}
|
|
46
|
+
</div>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import clsx from "clsx";
|
|
2
|
+
import { ringGeometry } from "../../internal/ringBase";
|
|
3
|
+
|
|
4
|
+
export type ActivityGaugeSize = "xs" | "sm" | "md" | "lg";
|
|
5
|
+
export type ActivityGaugeLegend = "none" | "bottom" | "right";
|
|
6
|
+
|
|
7
|
+
export interface ActivityGaugeSeries {
|
|
8
|
+
/** Series name shown in the legend. */
|
|
9
|
+
label: string;
|
|
10
|
+
/** Fill amount for this ring, 0–100. */
|
|
11
|
+
value: number;
|
|
12
|
+
/** Ring/legend color. Defaults to the brand ramp (see DEFAULT_SERIES_COLORS). */
|
|
13
|
+
color?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface ActivityGaugeProps {
|
|
17
|
+
/** Big centered numeral, e.g. "1,000". Pre-formatted by the caller. */
|
|
18
|
+
value: string;
|
|
19
|
+
/** Small centered label above/below the value, e.g. "Active users". */
|
|
20
|
+
label?: string;
|
|
21
|
+
/** One concentric ring per series, outermost first. */
|
|
22
|
+
series: ActivityGaugeSeries[];
|
|
23
|
+
size?: ActivityGaugeSize;
|
|
24
|
+
legend?: ActivityGaugeLegend;
|
|
25
|
+
className?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Default ring ramp + legend dots use real Tailwind stroke-*/bg-* utilities
|
|
29
|
+
// backed by the utility-* tokens now in tailwind-preset.js. A per-series
|
|
30
|
+
// `color` (hex) still overrides via the stroke attribute / inline style.
|
|
31
|
+
const DEFAULT_STROKE_CLASSES = [
|
|
32
|
+
"stroke-utility-brand-600",
|
|
33
|
+
"stroke-utility-brand-400",
|
|
34
|
+
"stroke-utility-brand-700",
|
|
35
|
+
];
|
|
36
|
+
const DEFAULT_DOT_CLASSES = [
|
|
37
|
+
"bg-utility-brand-600",
|
|
38
|
+
"bg-utility-brand-400",
|
|
39
|
+
"bg-utility-brand-700",
|
|
40
|
+
];
|
|
41
|
+
const TRACK_STROKE_CLASS = "stroke-utility-neutral-100";
|
|
42
|
+
|
|
43
|
+
interface SizeSpec {
|
|
44
|
+
diameter: number;
|
|
45
|
+
stroke: number;
|
|
46
|
+
gap: number;
|
|
47
|
+
valueClass: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const sizeSpecs: Record<ActivityGaugeSize, SizeSpec> = {
|
|
51
|
+
xs: { diameter: 120, stroke: 10, gap: 4, valueClass: "text-display-sm" },
|
|
52
|
+
sm: { diameter: 160, stroke: 12, gap: 5, valueClass: "text-display-sm" },
|
|
53
|
+
md: { diameter: 208, stroke: 14, gap: 6, valueClass: "text-display-md" },
|
|
54
|
+
lg: { diameter: 240, stroke: 16, gap: 6, valueClass: "text-display-md" },
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export function ActivityGauge({
|
|
58
|
+
value,
|
|
59
|
+
label,
|
|
60
|
+
series,
|
|
61
|
+
size = "md",
|
|
62
|
+
legend = "none",
|
|
63
|
+
className,
|
|
64
|
+
}: ActivityGaugeProps) {
|
|
65
|
+
const { diameter, stroke, gap, valueClass } = sizeSpecs[size];
|
|
66
|
+
const center = diameter / 2;
|
|
67
|
+
|
|
68
|
+
const rings = series.map((s, i) => {
|
|
69
|
+
const geo = ringGeometry(diameter, stroke, s.value, { ringIndex: i, ringGap: gap });
|
|
70
|
+
const strokeClass = DEFAULT_STROKE_CLASSES[i % DEFAULT_STROKE_CLASSES.length];
|
|
71
|
+
return { radius: geo.radius, circumference: geo.circumference, filled: geo.filled, strokeClass, custom: s.color };
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const gauge = (
|
|
75
|
+
<div className="relative shrink-0" style={{ width: diameter, height: diameter }}>
|
|
76
|
+
<svg
|
|
77
|
+
width={diameter}
|
|
78
|
+
height={diameter}
|
|
79
|
+
viewBox={`0 0 ${diameter} ${diameter}`}
|
|
80
|
+
role="img"
|
|
81
|
+
aria-label={label ? `${label}: ${value}` : value}
|
|
82
|
+
>
|
|
83
|
+
<g transform={`rotate(-90 ${center} ${center})`}>
|
|
84
|
+
{rings.map((r, i) => (
|
|
85
|
+
<g key={i}>
|
|
86
|
+
<circle
|
|
87
|
+
cx={center}
|
|
88
|
+
cy={center}
|
|
89
|
+
r={r.radius}
|
|
90
|
+
fill="none"
|
|
91
|
+
className={TRACK_STROKE_CLASS}
|
|
92
|
+
strokeWidth={stroke}
|
|
93
|
+
/>
|
|
94
|
+
<circle
|
|
95
|
+
cx={center}
|
|
96
|
+
cy={center}
|
|
97
|
+
r={r.radius}
|
|
98
|
+
fill="none"
|
|
99
|
+
className={clsx(!r.custom && r.strokeClass)}
|
|
100
|
+
stroke={r.custom}
|
|
101
|
+
strokeWidth={stroke}
|
|
102
|
+
strokeLinecap="round"
|
|
103
|
+
strokeDasharray={`${r.filled} ${r.circumference - r.filled}`}
|
|
104
|
+
/>
|
|
105
|
+
</g>
|
|
106
|
+
))}
|
|
107
|
+
</g>
|
|
108
|
+
</svg>
|
|
109
|
+
<div className="absolute inset-0 flex flex-col items-center justify-center text-center">
|
|
110
|
+
{label ? (
|
|
111
|
+
<span className="text-sm font-medium text-text-tertiary">{label}</span>
|
|
112
|
+
) : null}
|
|
113
|
+
<span className={clsx("font-display font-semibold text-text-primary", valueClass)}>
|
|
114
|
+
{value}
|
|
115
|
+
</span>
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
const legendEl =
|
|
121
|
+
legend === "none" ? null : (
|
|
122
|
+
<ul
|
|
123
|
+
className={clsx(
|
|
124
|
+
"flex gap-md",
|
|
125
|
+
legend === "right" ? "flex-col" : "flex-wrap justify-center",
|
|
126
|
+
)}
|
|
127
|
+
>
|
|
128
|
+
{series.map((s, i) => (
|
|
129
|
+
<li key={s.label} className="flex items-center gap-sm">
|
|
130
|
+
<span
|
|
131
|
+
className={clsx(
|
|
132
|
+
"size-2 shrink-0 rounded-full",
|
|
133
|
+
!s.color && DEFAULT_DOT_CLASSES[i % DEFAULT_DOT_CLASSES.length],
|
|
134
|
+
)}
|
|
135
|
+
style={s.color ? { backgroundColor: s.color } : undefined}
|
|
136
|
+
/>
|
|
137
|
+
<span className="text-sm font-medium text-text-tertiary">{s.label}</span>
|
|
138
|
+
</li>
|
|
139
|
+
))}
|
|
140
|
+
</ul>
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
return (
|
|
144
|
+
<div
|
|
145
|
+
className={clsx(
|
|
146
|
+
"flex font-body",
|
|
147
|
+
legend === "right" ? "flex-row items-center gap-xl" : "flex-col items-center gap-xl",
|
|
148
|
+
className,
|
|
149
|
+
)}
|
|
150
|
+
>
|
|
151
|
+
{gauge}
|
|
152
|
+
{legendEl}
|
|
153
|
+
</div>
|
|
154
|
+
);
|
|
155
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import clsx from "clsx";
|
|
3
|
+
|
|
4
|
+
function FilterLines() {
|
|
5
|
+
return (
|
|
6
|
+
<svg viewBox="0 0 20 20" fill="none" className="size-5" aria-hidden>
|
|
7
|
+
<path
|
|
8
|
+
d="M5 10h10M2.5 5h15M7.5 15h5"
|
|
9
|
+
stroke="currentColor"
|
|
10
|
+
strokeWidth="1.667"
|
|
11
|
+
strokeLinecap="round"
|
|
12
|
+
strokeLinejoin="round"
|
|
13
|
+
/>
|
|
14
|
+
</svg>
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface AdvancedFilterBarProps {
|
|
19
|
+
/**
|
|
20
|
+
* `false` → collapsed filter trigger only. `true` → trigger plus the active
|
|
21
|
+
* filter rows and a Clear all action.
|
|
22
|
+
*/
|
|
23
|
+
active?: boolean;
|
|
24
|
+
/** Active filter rows (compose Select/Select/Input + a close button each). */
|
|
25
|
+
rows?: ReactNode;
|
|
26
|
+
/** Trigger click handler. */
|
|
27
|
+
onToggle?: () => void;
|
|
28
|
+
/** Clear-all handler (active state). */
|
|
29
|
+
onClearAll?: () => void;
|
|
30
|
+
clearLabel?: ReactNode;
|
|
31
|
+
className?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* The advanced (query-builder style) filter pattern. Collapsed it shows just a
|
|
36
|
+
* filter trigger; active it shows the filter rows plus Clear all. The rows are
|
|
37
|
+
* a slot — compose `Select` + `Select` + `Input` + a close button per row.
|
|
38
|
+
*/
|
|
39
|
+
export function AdvancedFilterBar({
|
|
40
|
+
active = false,
|
|
41
|
+
rows,
|
|
42
|
+
onToggle,
|
|
43
|
+
onClearAll,
|
|
44
|
+
clearLabel = "Clear all",
|
|
45
|
+
className,
|
|
46
|
+
}: AdvancedFilterBarProps) {
|
|
47
|
+
const trigger = (
|
|
48
|
+
<button
|
|
49
|
+
type="button"
|
|
50
|
+
onClick={onToggle}
|
|
51
|
+
aria-label="Filters"
|
|
52
|
+
aria-expanded={active}
|
|
53
|
+
className="flex size-9 shrink-0 items-center justify-center rounded-md border border-border-primary bg-bg-primary text-text-secondary shadow-xs"
|
|
54
|
+
>
|
|
55
|
+
<FilterLines />
|
|
56
|
+
</button>
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
if (!active) {
|
|
60
|
+
return <div className={clsx("font-body", className)}>{trigger}</div>;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<div className={clsx("flex w-full flex-wrap items-start gap-lg font-body", className)}>
|
|
65
|
+
<div className="flex min-w-[280px] flex-1 flex-wrap items-start gap-lg">
|
|
66
|
+
{trigger}
|
|
67
|
+
{rows}
|
|
68
|
+
</div>
|
|
69
|
+
{onClearAll ? (
|
|
70
|
+
<button
|
|
71
|
+
type="button"
|
|
72
|
+
onClick={onClearAll}
|
|
73
|
+
className="shrink-0 rounded-md border border-border-primary bg-bg-primary px-lg py-md text-sm font-semibold text-text-secondary shadow-xs"
|
|
74
|
+
>
|
|
75
|
+
{clearLabel}
|
|
76
|
+
</button>
|
|
77
|
+
) : null}
|
|
78
|
+
</div>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import clsx from "clsx";
|
|
3
|
+
import { Button } from "../Button";
|
|
4
|
+
|
|
5
|
+
export type AlertColor =
|
|
6
|
+
| "Default"
|
|
7
|
+
| "Brand"
|
|
8
|
+
| "Gray"
|
|
9
|
+
| "Error"
|
|
10
|
+
| "Warning"
|
|
11
|
+
| "Success";
|
|
12
|
+
export type AlertSize = "Floating" | "FullWidth";
|
|
13
|
+
|
|
14
|
+
export interface AlertAction {
|
|
15
|
+
label: string;
|
|
16
|
+
onClick?: () => void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface AlertProps {
|
|
20
|
+
/** Bold heading line. */
|
|
21
|
+
title: string;
|
|
22
|
+
/** Supporting text under the title. */
|
|
23
|
+
description?: ReactNode;
|
|
24
|
+
color?: AlertColor;
|
|
25
|
+
/** Floating = padded card with link actions; FullWidth = banner with buttons. */
|
|
26
|
+
size?: AlertSize;
|
|
27
|
+
/** Glyph rendered inside the leading icon ring. Defaults to an info circle. */
|
|
28
|
+
icon?: ReactNode;
|
|
29
|
+
/** Primary action — "View changes" / "Learn more". */
|
|
30
|
+
primaryAction?: AlertAction;
|
|
31
|
+
/** Secondary action — typically "Dismiss". */
|
|
32
|
+
secondaryAction?: AlertAction;
|
|
33
|
+
/** Renders the top-right close button when provided. */
|
|
34
|
+
onClose?: () => void;
|
|
35
|
+
className?: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Per-color icon foreground (preset tokens).
|
|
39
|
+
const iconColor: Record<AlertColor, string> = {
|
|
40
|
+
Default: "text-fg-tertiary",
|
|
41
|
+
Gray: "text-fg-quaternary",
|
|
42
|
+
Brand: "text-fg-brand-primary",
|
|
43
|
+
Error: "text-fg-error-primary",
|
|
44
|
+
Warning: "text-fg-warning-primary",
|
|
45
|
+
Success: "text-fg-success-primary",
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
function InfoCircle() {
|
|
49
|
+
return (
|
|
50
|
+
<svg viewBox="0 0 20 20" fill="none" className="size-5" aria-hidden>
|
|
51
|
+
<path
|
|
52
|
+
d="M10 13.333V10m0-3.333h.008M17.5 10a7.5 7.5 0 1 1-15 0 7.5 7.5 0 0 1 15 0Z"
|
|
53
|
+
stroke="currentColor"
|
|
54
|
+
strokeWidth="1.667"
|
|
55
|
+
strokeLinecap="round"
|
|
56
|
+
strokeLinejoin="round"
|
|
57
|
+
/>
|
|
58
|
+
</svg>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function CloseIcon() {
|
|
63
|
+
return (
|
|
64
|
+
<svg viewBox="0 0 20 20" fill="none" className="size-5" aria-hidden>
|
|
65
|
+
<path
|
|
66
|
+
d="m15 5-10 10M5 5l10 10"
|
|
67
|
+
stroke="currentColor"
|
|
68
|
+
strokeWidth="1.667"
|
|
69
|
+
strokeLinecap="round"
|
|
70
|
+
strokeLinejoin="round"
|
|
71
|
+
/>
|
|
72
|
+
</svg>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function Alert({
|
|
77
|
+
title,
|
|
78
|
+
description,
|
|
79
|
+
color = "Default",
|
|
80
|
+
size = "Floating",
|
|
81
|
+
icon,
|
|
82
|
+
primaryAction,
|
|
83
|
+
secondaryAction,
|
|
84
|
+
onClose,
|
|
85
|
+
className,
|
|
86
|
+
}: AlertProps) {
|
|
87
|
+
// Featured-icon-style ring (the dedicated Featured icons component is still
|
|
88
|
+
// missing from the Figma file — see figma-map.md — so the outline ring is
|
|
89
|
+
// implemented inline here).
|
|
90
|
+
const iconRing = (
|
|
91
|
+
<span
|
|
92
|
+
className={clsx(
|
|
93
|
+
"flex size-9 shrink-0 items-center justify-center rounded-full border border-border-primary bg-bg-primary shadow-xs",
|
|
94
|
+
iconColor[color],
|
|
95
|
+
)}
|
|
96
|
+
>
|
|
97
|
+
{icon ?? <InfoCircle />}
|
|
98
|
+
</span>
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
const closeButton = onClose ? (
|
|
102
|
+
<button
|
|
103
|
+
type="button"
|
|
104
|
+
onClick={onClose}
|
|
105
|
+
aria-label="Close"
|
|
106
|
+
className="shrink-0 text-fg-quaternary transition-colors hover:text-fg-quaternary-hover"
|
|
107
|
+
>
|
|
108
|
+
<CloseIcon />
|
|
109
|
+
</button>
|
|
110
|
+
) : null;
|
|
111
|
+
|
|
112
|
+
const heading = (
|
|
113
|
+
<p className="text-sm font-semibold text-text-secondary">{title}</p>
|
|
114
|
+
);
|
|
115
|
+
const body = description ? (
|
|
116
|
+
<p className="text-sm font-normal text-text-tertiary">{description}</p>
|
|
117
|
+
) : null;
|
|
118
|
+
|
|
119
|
+
if (size === "FullWidth") {
|
|
120
|
+
return (
|
|
121
|
+
<div
|
|
122
|
+
role="alert"
|
|
123
|
+
className={clsx(
|
|
124
|
+
"w-full border-b border-border-secondary bg-bg-primary-alt font-body",
|
|
125
|
+
className,
|
|
126
|
+
)}
|
|
127
|
+
>
|
|
128
|
+
<div className="mx-auto flex max-w-container-max-width-desktop items-start gap-xl px-container-padding-desktop py-2xl">
|
|
129
|
+
{iconRing}
|
|
130
|
+
<div className="flex min-w-0 flex-1 flex-wrap items-center gap-x-md gap-y-xs pt-xs">
|
|
131
|
+
{heading}
|
|
132
|
+
{body}
|
|
133
|
+
</div>
|
|
134
|
+
<div className="flex shrink-0 items-center gap-lg">
|
|
135
|
+
{secondaryAction ? (
|
|
136
|
+
<Button
|
|
137
|
+
hierarchy="Secondary"
|
|
138
|
+
size="sm"
|
|
139
|
+
onClick={secondaryAction.onClick}
|
|
140
|
+
>
|
|
141
|
+
{secondaryAction.label}
|
|
142
|
+
</Button>
|
|
143
|
+
) : null}
|
|
144
|
+
{primaryAction ? (
|
|
145
|
+
<Button
|
|
146
|
+
hierarchy="Primary"
|
|
147
|
+
size="sm"
|
|
148
|
+
onClick={primaryAction.onClick}
|
|
149
|
+
>
|
|
150
|
+
{primaryAction.label}
|
|
151
|
+
</Button>
|
|
152
|
+
) : null}
|
|
153
|
+
{closeButton}
|
|
154
|
+
</div>
|
|
155
|
+
</div>
|
|
156
|
+
</div>
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Floating
|
|
161
|
+
return (
|
|
162
|
+
<div
|
|
163
|
+
role="alert"
|
|
164
|
+
className={clsx(
|
|
165
|
+
"relative flex w-full items-start gap-xl rounded-xl border border-border-primary bg-bg-primary-alt p-xl font-body shadow-skeuomorphic",
|
|
166
|
+
className,
|
|
167
|
+
)}
|
|
168
|
+
>
|
|
169
|
+
{iconRing}
|
|
170
|
+
<div className="flex min-w-0 flex-1 flex-col gap-lg pr-xl">
|
|
171
|
+
<div className="flex flex-col gap-xs">
|
|
172
|
+
{heading}
|
|
173
|
+
{body}
|
|
174
|
+
</div>
|
|
175
|
+
{primaryAction || secondaryAction ? (
|
|
176
|
+
<div className="flex items-center gap-xl">
|
|
177
|
+
{secondaryAction ? (
|
|
178
|
+
<Button
|
|
179
|
+
hierarchy="Link gray"
|
|
180
|
+
size="sm"
|
|
181
|
+
onClick={secondaryAction.onClick}
|
|
182
|
+
>
|
|
183
|
+
{secondaryAction.label}
|
|
184
|
+
</Button>
|
|
185
|
+
) : null}
|
|
186
|
+
{primaryAction ? (
|
|
187
|
+
<Button
|
|
188
|
+
hierarchy="Link color"
|
|
189
|
+
size="sm"
|
|
190
|
+
onClick={primaryAction.onClick}
|
|
191
|
+
>
|
|
192
|
+
{primaryAction.label}
|
|
193
|
+
</Button>
|
|
194
|
+
) : null}
|
|
195
|
+
</div>
|
|
196
|
+
) : null}
|
|
197
|
+
</div>
|
|
198
|
+
{onClose ? (
|
|
199
|
+
<button
|
|
200
|
+
type="button"
|
|
201
|
+
onClick={onClose}
|
|
202
|
+
aria-label="Close"
|
|
203
|
+
className="absolute right-xl top-xl text-fg-quaternary transition-colors hover:text-fg-quaternary-hover"
|
|
204
|
+
>
|
|
205
|
+
<CloseIcon />
|
|
206
|
+
</button>
|
|
207
|
+
) : null}
|
|
208
|
+
</div>
|
|
209
|
+
);
|
|
210
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import clsx from "clsx";
|
|
2
|
+
|
|
3
|
+
export type AvatarSize = "xs" | "sm" | "md" | "lg" | "xl" | "2xl";
|
|
4
|
+
|
|
5
|
+
export interface AvatarProps {
|
|
6
|
+
size?: AvatarSize;
|
|
7
|
+
/** Image URL. Takes precedence over initials and the placeholder icon. */
|
|
8
|
+
src?: string;
|
|
9
|
+
alt?: string;
|
|
10
|
+
/** Placeholder text (initials, e.g. "OR") shown when no image is supplied. */
|
|
11
|
+
initials?: string;
|
|
12
|
+
/** Render the outer border ring (Figma `Border=True`). */
|
|
13
|
+
hasBorder?: boolean;
|
|
14
|
+
/** Show the online status dot (bottom-right). */
|
|
15
|
+
hasStatus?: boolean;
|
|
16
|
+
className?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const sizeClass: Record<AvatarSize, string> = {
|
|
20
|
+
xs: "size-6", // 24px
|
|
21
|
+
sm: "size-8", // 32px
|
|
22
|
+
md: "size-10", // 40px
|
|
23
|
+
lg: "size-12", // 48px
|
|
24
|
+
xl: "size-14", // 56px
|
|
25
|
+
"2xl": "size-16", // 64px
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// Initials typography per size (text-xs → display-xs scale, Semibold).
|
|
29
|
+
const initialsClass: Record<AvatarSize, string> = {
|
|
30
|
+
xs: "text-xs",
|
|
31
|
+
sm: "text-sm",
|
|
32
|
+
md: "text-md",
|
|
33
|
+
lg: "text-lg",
|
|
34
|
+
xl: "text-xl",
|
|
35
|
+
"2xl": "text-display-xs",
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const statusClass: Record<AvatarSize, string> = {
|
|
39
|
+
xs: "size-1.5", // 6px
|
|
40
|
+
sm: "size-2", // 8px
|
|
41
|
+
md: "size-2.5", // 10px
|
|
42
|
+
lg: "size-3", // 12px
|
|
43
|
+
xl: "size-3.5", // 14px
|
|
44
|
+
"2xl": "size-4", // 16px
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
function UserIcon() {
|
|
48
|
+
return (
|
|
49
|
+
<svg viewBox="0 0 24 24" fill="none" className="h-1/2 w-1/2" aria-hidden>
|
|
50
|
+
<path
|
|
51
|
+
d="M20 21a8 8 0 0 0-16 0M12 11a4 4 0 1 0 0-8 4 4 0 0 0 0 8Z"
|
|
52
|
+
stroke="currentColor"
|
|
53
|
+
strokeWidth="2"
|
|
54
|
+
strokeLinecap="round"
|
|
55
|
+
strokeLinejoin="round"
|
|
56
|
+
/>
|
|
57
|
+
</svg>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function Avatar({
|
|
62
|
+
size = "md",
|
|
63
|
+
src,
|
|
64
|
+
alt = "",
|
|
65
|
+
initials,
|
|
66
|
+
hasBorder = false,
|
|
67
|
+
hasStatus = false,
|
|
68
|
+
className,
|
|
69
|
+
}: AvatarProps) {
|
|
70
|
+
return (
|
|
71
|
+
<span className={clsx("relative inline-flex shrink-0", className)}>
|
|
72
|
+
<span
|
|
73
|
+
className={clsx(
|
|
74
|
+
"flex items-center justify-center overflow-hidden rounded-full bg-bg-tertiary shadow-sm",
|
|
75
|
+
src
|
|
76
|
+
? "border-[0.5px] border-border-secondary-alt"
|
|
77
|
+
: "text-text-quaternary",
|
|
78
|
+
hasBorder &&
|
|
79
|
+
"ring-1 ring-border-secondary ring-offset-2 ring-offset-bg-primary",
|
|
80
|
+
sizeClass[size],
|
|
81
|
+
)}
|
|
82
|
+
>
|
|
83
|
+
{src ? (
|
|
84
|
+
<img src={src} alt={alt} className="size-full object-cover" />
|
|
85
|
+
) : initials ? (
|
|
86
|
+
<span
|
|
87
|
+
className={clsx(
|
|
88
|
+
"font-display font-semibold",
|
|
89
|
+
initialsClass[size],
|
|
90
|
+
)}
|
|
91
|
+
>
|
|
92
|
+
{initials}
|
|
93
|
+
</span>
|
|
94
|
+
) : (
|
|
95
|
+
<span className="flex size-full items-center justify-center text-fg-quaternary">
|
|
96
|
+
<UserIcon />
|
|
97
|
+
</span>
|
|
98
|
+
)}
|
|
99
|
+
</span>
|
|
100
|
+
{hasStatus ? (
|
|
101
|
+
<span
|
|
102
|
+
className={clsx(
|
|
103
|
+
"absolute bottom-0 right-0 rounded-full border-[1.5px] border-bg-primary bg-fg-success-secondary",
|
|
104
|
+
statusClass[size],
|
|
105
|
+
)}
|
|
106
|
+
aria-label="Online"
|
|
107
|
+
/>
|
|
108
|
+
) : null}
|
|
109
|
+
</span>
|
|
110
|
+
);
|
|
111
|
+
}
|