ai-design-system 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/README.md +307 -0
- package/components/ai-elements/actions.tsx +65 -0
- package/components/ai-elements/artifact.tsx +147 -0
- package/components/ai-elements/branch.tsx +212 -0
- package/components/ai-elements/canvas.tsx +24 -0
- package/components/ai-elements/chain-of-thought.tsx +228 -0
- package/components/ai-elements/code-block.tsx +179 -0
- package/components/ai-elements/confirmation.tsx +169 -0
- package/components/ai-elements/connection.tsx +28 -0
- package/components/ai-elements/context.tsx +408 -0
- package/components/ai-elements/controls.tsx +18 -0
- package/components/ai-elements/conversation.tsx +97 -0
- package/components/ai-elements/edge.tsx +140 -0
- package/components/ai-elements/image.tsx +24 -0
- package/components/ai-elements/inline-citation.tsx +287 -0
- package/components/ai-elements/loader.tsx +96 -0
- package/components/ai-elements/message.tsx +80 -0
- package/components/ai-elements/node.tsx +71 -0
- package/components/ai-elements/open-in-chat.tsx +363 -0
- package/components/ai-elements/panel.tsx +15 -0
- package/components/ai-elements/plan.tsx +142 -0
- package/components/ai-elements/prompt-input.tsx +1352 -0
- package/components/ai-elements/queue.tsx +274 -0
- package/components/ai-elements/reasoning.tsx +178 -0
- package/components/ai-elements/response.tsx +22 -0
- package/components/ai-elements/shimmer.tsx +64 -0
- package/components/ai-elements/sources.tsx +77 -0
- package/components/ai-elements/suggestion.tsx +56 -0
- package/components/ai-elements/task.tsx +87 -0
- package/components/ai-elements/tool.tsx +179 -0
- package/components/ai-elements/toolbar.tsx +16 -0
- package/components/ai-elements/web-preview.tsx +263 -0
- package/components/blocks/AIConversation/AIConversation.stories.tsx +164 -0
- package/components/blocks/AIConversation/AIConversation.tsx +186 -0
- package/components/blocks/AIConversation/index.ts +8 -0
- package/components/blocks/AppSidebar/AppSidebar.stories.tsx +63 -0
- package/components/blocks/AppSidebar/AppSidebar.tsx +87 -0
- package/components/blocks/AppSidebar/index.ts +2 -0
- package/components/blocks/DocumentEditorWithComments/DocumentEditorWithComments.stories.tsx +341 -0
- package/components/blocks/DocumentEditorWithComments/DocumentEditorWithComments.tsx +255 -0
- package/components/blocks/DocumentEditorWithComments/index.ts +9 -0
- package/components/blocks/FileChangeQueue/FileChangeQueue.stories.tsx +207 -0
- package/components/blocks/FileChangeQueue/FileChangeQueue.tsx +143 -0
- package/components/blocks/FileChangeQueue/index.ts +7 -0
- package/components/blocks/LayoutProvider/LayoutProvider.tsx +34 -0
- package/components/blocks/LayoutProvider/index.ts +1 -0
- package/components/blocks/index.ts +2 -0
- package/components/composites/AgentIndicator/AgentIndicator.stories.tsx +154 -0
- package/components/composites/AgentIndicator/AgentIndicator.tsx +102 -0
- package/components/composites/AgentIndicator/index.ts +8 -0
- package/components/composites/AppHeader/AppHeader.stories.tsx +46 -0
- package/components/composites/AppHeader/AppHeader.tsx +24 -0
- package/components/composites/AppHeader/index.ts +2 -0
- package/components/composites/CommentBox/CommentBox.stories.tsx +192 -0
- package/components/composites/CommentBox/CommentBox.tsx +364 -0
- package/components/composites/CommentBox/index.ts +8 -0
- package/components/composites/Confirmation/Confirmation.stories.tsx +151 -0
- package/components/composites/Confirmation/Confirmation.tsx +93 -0
- package/components/composites/Confirmation/index.ts +7 -0
- package/components/composites/DataTable/DataTable.stories.tsx +35 -0
- package/components/composites/DataTable/DataTable.tsx +95 -0
- package/components/composites/DataTable/index.ts +2 -0
- package/components/composites/DocumentEditor/DocumentEditor.css +106 -0
- package/components/composites/DocumentEditor/DocumentEditor.stories.tsx +927 -0
- package/components/composites/DocumentEditor/DocumentEditor.tsx +279 -0
- package/components/composites/DocumentEditor/index.ts +8 -0
- package/components/composites/FileQueue/FileQueue.stories.tsx +175 -0
- package/components/composites/FileQueue/FileQueue.tsx +161 -0
- package/components/composites/FileQueue/FileStatusBadge.tsx +74 -0
- package/components/composites/FileQueue/index.ts +24 -0
- package/components/composites/InteractiveChart/InteractiveChart.stories.tsx +49 -0
- package/components/composites/InteractiveChart/InteractiveChart.tsx +69 -0
- package/components/composites/InteractiveChart/index.ts +2 -0
- package/components/composites/ModeToggle/ModeToggle.stories.tsx +212 -0
- package/components/composites/ModeToggle/ModeToggle.tsx +100 -0
- package/components/composites/ModeToggle/index.ts +7 -0
- package/components/composites/NavUser/NavUser.stories.tsx +50 -0
- package/components/composites/NavUser/NavUser.tsx +60 -0
- package/components/composites/NavUser/index.ts +2 -0
- package/components/composites/NavigationList/NavigationList.stories.tsx +46 -0
- package/components/composites/NavigationList/NavigationList.tsx +46 -0
- package/components/composites/NavigationList/index.ts +2 -0
- package/components/composites/OrchestratorMessage/OrchestratorMessage.stories.tsx +188 -0
- package/components/composites/OrchestratorMessage/OrchestratorMessage.tsx +72 -0
- package/components/composites/OrchestratorMessage/index.ts +8 -0
- package/components/composites/PageContainer/PageContainer.stories.tsx +41 -0
- package/components/composites/PageContainer/PageContainer.tsx +24 -0
- package/components/composites/PageContainer/index.ts +2 -0
- package/components/composites/PromptInput/PromptInput.stories.tsx +200 -0
- package/components/composites/PromptInput/PromptInput.tsx +129 -0
- package/components/composites/PromptInput/index.ts +8 -0
- package/components/composites/SpecialistMessage/SpecialistMessage.stories.tsx +286 -0
- package/components/composites/SpecialistMessage/SpecialistMessage.tsx +107 -0
- package/components/composites/SpecialistMessage/index.ts +8 -0
- package/components/composites/StatsCard/StatsCard.stories.tsx +76 -0
- package/components/composites/StatsCard/StatsCard.tsx +81 -0
- package/components/composites/StatsCard/index.ts +2 -0
- package/components/composites/TablePagination/TablePagination.stories.tsx +38 -0
- package/components/composites/TablePagination/TablePagination.tsx +119 -0
- package/components/composites/TablePagination/index.ts +2 -0
- package/components/composites/TableToolbar/TableToolbar.stories.tsx +60 -0
- package/components/composites/TableToolbar/TableToolbar.tsx +66 -0
- package/components/composites/TableToolbar/index.ts +2 -0
- package/components/composites/ThemeSelector/ThemeSelector.stories.tsx +48 -0
- package/components/composites/ThemeSelector/ThemeSelector.tsx +79 -0
- package/components/composites/ThemeSelector/index.ts +2 -0
- package/components/composites/ToolCallDisplay/ToolCallDisplay.stories.tsx +49 -0
- package/components/composites/ToolCallDisplay/ToolCallDisplay.tsx +108 -0
- package/components/composites/ToolCallDisplay/index.ts +8 -0
- package/components/composites/UserMessage/UserMessage.stories.tsx +59 -0
- package/components/composites/UserMessage/UserMessage.tsx +52 -0
- package/components/composites/UserMessage/index.ts +8 -0
- package/components/composites/index.ts +90 -0
- package/components/features/AIDocEditor/AIDocEditor.behaviors.stories.tsx +451 -0
- package/components/features/AIDocEditor/AIDocEditor.mocks.ts +96 -0
- package/components/features/AIDocEditor/AIDocEditor.stories.tsx +140 -0
- package/components/features/AIDocEditor/AIDocEditor.tsx +130 -0
- package/components/features/AIDocEditor/index.ts +8 -0
- package/components/features/AIDocEditor/useAIDocEditor.d.ts +97 -0
- package/components/features/AIDocEditor/useAIDocEditor.mock.ts +83 -0
- package/components/features/PageLayout/PageLayout.behaviors.stories.tsx +119 -0
- package/components/features/PageLayout/PageLayout.mocks.ts +27 -0
- package/components/features/PageLayout/PageLayout.stories.tsx +142 -0
- package/components/features/PageLayout/PageLayout.tsx +94 -0
- package/components/features/PageLayout/index.ts +4 -0
- package/components/features/PageLayout/usePageLayout.d.ts +24 -0
- package/components/features/PageLayout/usePageLayout.mock.ts +19 -0
- package/components/features/RefinementPanel/README.md +189 -0
- package/components/features/RefinementPanel/RefinementPanel.behaviors.stories.tsx +475 -0
- package/components/features/RefinementPanel/RefinementPanel.mocks.ts +131 -0
- package/components/features/RefinementPanel/RefinementPanel.stories.tsx +141 -0
- package/components/features/RefinementPanel/RefinementPanel.tsx +160 -0
- package/components/features/RefinementPanel/index.ts +25 -0
- package/components/features/RefinementPanel/useRefinementPanel.d.ts +74 -0
- package/components/features/RefinementPanel/useRefinementPanel.mock.ts +121 -0
- package/components/features/SpecNavigator/SpecNavigator.behaviors.stories.tsx +379 -0
- package/components/features/SpecNavigator/SpecNavigator.mocks.ts +131 -0
- package/components/features/SpecNavigator/SpecNavigator.stories.tsx +122 -0
- package/components/features/SpecNavigator/SpecNavigator.tsx +43 -0
- package/components/features/SpecNavigator/index.ts +2 -0
- package/components/features/SpecNavigator/useSpecNavigator.d.ts +122 -0
- package/components/features/SpecNavigator/useSpecNavigator.mock.ts +93 -0
- package/components/features/index.ts +18 -0
- package/components/index.ts +14 -0
- package/components/primitives/Accordion/Accordion.stories.tsx +87 -0
- package/components/primitives/Accordion/Accordion.tsx +66 -0
- package/components/primitives/Accordion/index.ts +13 -0
- package/components/primitives/Alert/Alert.stories.tsx +422 -0
- package/components/primitives/Alert/Alert.tsx +61 -0
- package/components/primitives/Alert/index.ts +8 -0
- package/components/primitives/AlertDialog/AlertDialog.stories.tsx +367 -0
- package/components/primitives/AlertDialog/AlertDialog.tsx +182 -0
- package/components/primitives/AlertDialog/index.ts +25 -0
- package/components/primitives/Avatar/Avatar.stories.tsx +321 -0
- package/components/primitives/Avatar/Avatar.tsx +63 -0
- package/components/primitives/Avatar/index.ts +8 -0
- package/components/primitives/Badge/Badge.stories.tsx +74 -0
- package/components/primitives/Badge/Badge.tsx +49 -0
- package/components/primitives/Badge/index.ts +2 -0
- package/components/primitives/Button/Button.stories.tsx +445 -0
- package/components/primitives/Button/Button.tsx +89 -0
- package/components/primitives/Button/index.ts +7 -0
- package/components/primitives/Card/Card.stories.tsx +831 -0
- package/components/primitives/Card/Card.tsx +242 -0
- package/components/primitives/Card/index.ts +30 -0
- package/components/primitives/Carousel/Carousel.stories.tsx +32 -0
- package/components/primitives/Carousel/Carousel.tsx +63 -0
- package/components/primitives/Carousel/index.ts +13 -0
- package/components/primitives/Chart/Chart.stories.tsx +346 -0
- package/components/primitives/Chart/Chart.tsx +117 -0
- package/components/primitives/Chart/index.ts +20 -0
- package/components/primitives/Checkbox/Checkbox.stories.tsx +87 -0
- package/components/primitives/Checkbox/Checkbox.tsx +38 -0
- package/components/primitives/Checkbox/index.ts +2 -0
- package/components/primitives/Collapsible/Collapsible.stories.tsx +38 -0
- package/components/primitives/Collapsible/Collapsible.tsx +39 -0
- package/components/primitives/Collapsible/index.ts +8 -0
- package/components/primitives/Command/Command.stories.tsx +150 -0
- package/components/primitives/Command/Command.tsx +147 -0
- package/components/primitives/Command/index.ts +20 -0
- package/components/primitives/Dialog/Dialog.stories.tsx +390 -0
- package/components/primitives/Dialog/Dialog.tsx +140 -0
- package/components/primitives/Dialog/index.ts +22 -0
- package/components/primitives/Drawer/Drawer.stories.tsx +327 -0
- package/components/primitives/Drawer/Drawer.tsx +208 -0
- package/components/primitives/Drawer/index.ts +27 -0
- package/components/primitives/DropdownMenu/DropdownMenu.stories.tsx +150 -0
- package/components/primitives/DropdownMenu/DropdownMenu.tsx +73 -0
- package/components/primitives/DropdownMenu/index.ts +1 -0
- package/components/primitives/HoverCard/HoverCard.stories.tsx +26 -0
- package/components/primitives/HoverCard/HoverCard.tsx +39 -0
- package/components/primitives/HoverCard/index.ts +8 -0
- package/components/primitives/Icon/Icon.stories.tsx +281 -0
- package/components/primitives/Icon/Icon.tsx +87 -0
- package/components/primitives/Icon/index.ts +8 -0
- package/components/primitives/Input/Input.stories.tsx +370 -0
- package/components/primitives/Input/Input.tsx +88 -0
- package/components/primitives/Input/index.ts +7 -0
- package/components/primitives/InputGroup/InputGroup.stories.tsx +40 -0
- package/components/primitives/InputGroup/InputGroup.tsx +72 -0
- package/components/primitives/InputGroup/index.ts +14 -0
- package/components/primitives/Label/Label.stories.tsx +227 -0
- package/components/primitives/Label/Label.tsx +53 -0
- package/components/primitives/Label/index.ts +7 -0
- package/components/primitives/Popover/Popover.stories.tsx +42 -0
- package/components/primitives/Popover/Popover.tsx +107 -0
- package/components/primitives/Popover/index.ts +2 -0
- package/components/primitives/Progress/Progress.stories.tsx +340 -0
- package/components/primitives/Progress/Progress.tsx +31 -0
- package/components/primitives/Progress/index.ts +1 -0
- package/components/primitives/ScrollArea/ScrollArea.stories.tsx +26 -0
- package/components/primitives/ScrollArea/ScrollArea.tsx +28 -0
- package/components/primitives/ScrollArea/index.ts +6 -0
- package/components/primitives/Select/Select.stories.tsx +288 -0
- package/components/primitives/Select/Select.tsx +162 -0
- package/components/primitives/Select/index.ts +22 -0
- package/components/primitives/Separator/Separator.stories.tsx +264 -0
- package/components/primitives/Separator/Separator.tsx +48 -0
- package/components/primitives/Separator/index.ts +7 -0
- package/components/primitives/Sidebar/Sidebar.stories.tsx +358 -0
- package/components/primitives/Sidebar/Sidebar.tsx +317 -0
- package/components/primitives/Sidebar/index.ts +41 -0
- package/components/primitives/Table/Table.stories.tsx +389 -0
- package/components/primitives/Table/Table.tsx +191 -0
- package/components/primitives/Table/index.ts +26 -0
- package/components/primitives/Tabs/Tabs.stories.tsx +129 -0
- package/components/primitives/Tabs/Tabs.tsx +70 -0
- package/components/primitives/Tabs/index.ts +13 -0
- package/components/primitives/Textarea/Textarea.stories.tsx +358 -0
- package/components/primitives/Textarea/Textarea.tsx +91 -0
- package/components/primitives/Textarea/index.ts +7 -0
- package/components/primitives/ToggleGroup/ToggleGroup.stories.tsx +87 -0
- package/components/primitives/ToggleGroup/ToggleGroup.tsx +52 -0
- package/components/primitives/ToggleGroup/index.ts +6 -0
- package/components/primitives/Tooltip/Tooltip.stories.tsx +336 -0
- package/components/primitives/Tooltip/Tooltip.tsx +78 -0
- package/components/primitives/Tooltip/index.ts +10 -0
- package/components/primitives/index.ts +34 -0
- package/components/ui/accordion.tsx +66 -0
- package/components/ui/alert-dialog.tsx +157 -0
- package/components/ui/alert.tsx +66 -0
- package/components/ui/avatar.tsx +53 -0
- package/components/ui/badge.tsx +46 -0
- package/components/ui/button.tsx +60 -0
- package/components/ui/card.tsx +117 -0
- package/components/ui/carousel.tsx +241 -0
- package/components/ui/chart.tsx +334 -0
- package/components/ui/checkbox.tsx +32 -0
- package/components/ui/collapsible.tsx +33 -0
- package/components/ui/command.tsx +184 -0
- package/components/ui/dialog.tsx +143 -0
- package/components/ui/drawer.tsx +118 -0
- package/components/ui/dropdown-menu.tsx +257 -0
- package/components/ui/hover-card.tsx +44 -0
- package/components/ui/input-group.tsx +170 -0
- package/components/ui/input.tsx +48 -0
- package/components/ui/label.tsx +26 -0
- package/components/ui/popover.tsx +33 -0
- package/components/ui/progress.tsx +31 -0
- package/components/ui/scroll-area.tsx +58 -0
- package/components/ui/select.tsx +187 -0
- package/components/ui/separator.tsx +31 -0
- package/components/ui/sidebar.tsx +577 -0
- package/components/ui/table.tsx +120 -0
- package/components/ui/tabs.tsx +66 -0
- package/components/ui/textarea.tsx +46 -0
- package/components/ui/toggle-group.tsx +83 -0
- package/components/ui/toggle.tsx +47 -0
- package/components/ui/tooltip.tsx +61 -0
- package/dist/index.cjs +7389 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.css +75 -0
- package/dist/index.css.map +1 -0
- package/dist/index.js +7160 -0
- package/dist/index.js.map +1 -0
- package/hooks/useAIDocReviewer.d.ts +0 -0
- package/lib/utils.ts +6 -0
- package/package.json +140 -0
- package/tokens/color/base.json +14 -0
- package/tokens/color/dark.json +40 -0
- package/tokens/color/green.json +21 -0
- package/tokens/color/light.json +52 -0
- package/tokens/color/neutral.json +20 -0
- package/tokens/color/violet.json +21 -0
- package/tokens/spacing.json +22 -0
- package/utils/ai-editor/format-date.ts +41 -0
- package/utils/ai-editor/index.ts +22 -0
- package/utils/ai-editor/type-guards.ts +72 -0
- package/utils/ai-editor/validation.ts +130 -0
- package/utils/editor-annotations.ts +122 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Drawer Primitive
|
|
3
|
+
*
|
|
4
|
+
* Central export point for the Drawer primitive component and its related types.
|
|
5
|
+
*/
|
|
6
|
+
export {
|
|
7
|
+
Drawer,
|
|
8
|
+
DrawerPortal,
|
|
9
|
+
DrawerOverlay,
|
|
10
|
+
DrawerTrigger,
|
|
11
|
+
DrawerClose,
|
|
12
|
+
DrawerContent,
|
|
13
|
+
DrawerHeader,
|
|
14
|
+
DrawerFooter,
|
|
15
|
+
DrawerTitle,
|
|
16
|
+
DrawerDescription,
|
|
17
|
+
} from './Drawer'
|
|
18
|
+
|
|
19
|
+
export type {
|
|
20
|
+
DrawerProps,
|
|
21
|
+
DrawerOverlayProps,
|
|
22
|
+
DrawerContentProps,
|
|
23
|
+
DrawerHeaderProps,
|
|
24
|
+
DrawerFooterProps,
|
|
25
|
+
DrawerTitleProps,
|
|
26
|
+
DrawerDescriptionProps,
|
|
27
|
+
} from './Drawer'
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react"
|
|
2
|
+
import {
|
|
3
|
+
DropdownMenu,
|
|
4
|
+
DropdownMenuCheckboxItem,
|
|
5
|
+
DropdownMenuContent,
|
|
6
|
+
DropdownMenuGroup,
|
|
7
|
+
DropdownMenuItem,
|
|
8
|
+
DropdownMenuLabel,
|
|
9
|
+
DropdownMenuRadioGroup,
|
|
10
|
+
DropdownMenuRadioItem,
|
|
11
|
+
DropdownMenuSeparator,
|
|
12
|
+
DropdownMenuShortcut,
|
|
13
|
+
DropdownMenuSub,
|
|
14
|
+
DropdownMenuSubContent,
|
|
15
|
+
DropdownMenuSubTrigger,
|
|
16
|
+
DropdownMenuTrigger,
|
|
17
|
+
} from "./DropdownMenu"
|
|
18
|
+
import { Button } from "../Button"
|
|
19
|
+
import * as React from "react"
|
|
20
|
+
|
|
21
|
+
const meta = {
|
|
22
|
+
title: "Primitives/DropdownMenu",
|
|
23
|
+
component: DropdownMenu,
|
|
24
|
+
parameters: {
|
|
25
|
+
layout: "centered",
|
|
26
|
+
},
|
|
27
|
+
tags: ["autodocs"],
|
|
28
|
+
} satisfies Meta<typeof DropdownMenu>
|
|
29
|
+
|
|
30
|
+
export default meta
|
|
31
|
+
type Story = StoryObj<typeof meta>
|
|
32
|
+
|
|
33
|
+
export const Default: Story = {
|
|
34
|
+
render: () => (
|
|
35
|
+
<DropdownMenu>
|
|
36
|
+
<DropdownMenuTrigger asChild>
|
|
37
|
+
<Button variant="outline">Open Menu</Button>
|
|
38
|
+
</DropdownMenuTrigger>
|
|
39
|
+
<DropdownMenuContent>
|
|
40
|
+
<DropdownMenuLabel>My Account</DropdownMenuLabel>
|
|
41
|
+
<DropdownMenuSeparator />
|
|
42
|
+
<DropdownMenuItem>Profile</DropdownMenuItem>
|
|
43
|
+
<DropdownMenuItem>Settings</DropdownMenuItem>
|
|
44
|
+
<DropdownMenuSeparator />
|
|
45
|
+
<DropdownMenuItem>Logout</DropdownMenuItem>
|
|
46
|
+
</DropdownMenuContent>
|
|
47
|
+
</DropdownMenu>
|
|
48
|
+
),
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export const WithShortcuts: Story = {
|
|
52
|
+
render: () => (
|
|
53
|
+
<DropdownMenu>
|
|
54
|
+
<DropdownMenuTrigger asChild>
|
|
55
|
+
<Button variant="outline">Actions</Button>
|
|
56
|
+
</DropdownMenuTrigger>
|
|
57
|
+
<DropdownMenuContent className="w-56">
|
|
58
|
+
<DropdownMenuLabel>Actions</DropdownMenuLabel>
|
|
59
|
+
<DropdownMenuSeparator />
|
|
60
|
+
<DropdownMenuItem>
|
|
61
|
+
New File
|
|
62
|
+
<DropdownMenuShortcut>⌘N</DropdownMenuShortcut>
|
|
63
|
+
</DropdownMenuItem>
|
|
64
|
+
<DropdownMenuItem>
|
|
65
|
+
Save
|
|
66
|
+
<DropdownMenuShortcut>⌘S</DropdownMenuShortcut>
|
|
67
|
+
</DropdownMenuItem>
|
|
68
|
+
<DropdownMenuItem>
|
|
69
|
+
Print
|
|
70
|
+
<DropdownMenuShortcut>⌘P</DropdownMenuShortcut>
|
|
71
|
+
</DropdownMenuItem>
|
|
72
|
+
</DropdownMenuContent>
|
|
73
|
+
</DropdownMenu>
|
|
74
|
+
),
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export const WithCheckboxes: Story = {
|
|
78
|
+
render: () => {
|
|
79
|
+
const [showPanel, setShowPanel] = React.useState(true)
|
|
80
|
+
const [showToolbar, setShowToolbar] = React.useState(false)
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
<DropdownMenu>
|
|
84
|
+
<DropdownMenuTrigger asChild>
|
|
85
|
+
<Button variant="outline">View</Button>
|
|
86
|
+
</DropdownMenuTrigger>
|
|
87
|
+
<DropdownMenuContent className="w-56">
|
|
88
|
+
<DropdownMenuLabel>Panels</DropdownMenuLabel>
|
|
89
|
+
<DropdownMenuSeparator />
|
|
90
|
+
<DropdownMenuCheckboxItem
|
|
91
|
+
checked={showPanel}
|
|
92
|
+
onCheckedChange={setShowPanel}
|
|
93
|
+
>
|
|
94
|
+
Side Panel
|
|
95
|
+
</DropdownMenuCheckboxItem>
|
|
96
|
+
<DropdownMenuCheckboxItem
|
|
97
|
+
checked={showToolbar}
|
|
98
|
+
onCheckedChange={setShowToolbar}
|
|
99
|
+
>
|
|
100
|
+
Toolbar
|
|
101
|
+
</DropdownMenuCheckboxItem>
|
|
102
|
+
</DropdownMenuContent>
|
|
103
|
+
</DropdownMenu>
|
|
104
|
+
)
|
|
105
|
+
},
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export const WithRadioGroup: Story = {
|
|
109
|
+
render: () => {
|
|
110
|
+
const [position, setPosition] = React.useState("bottom")
|
|
111
|
+
|
|
112
|
+
return (
|
|
113
|
+
<DropdownMenu>
|
|
114
|
+
<DropdownMenuTrigger asChild>
|
|
115
|
+
<Button variant="outline">Position</Button>
|
|
116
|
+
</DropdownMenuTrigger>
|
|
117
|
+
<DropdownMenuContent className="w-56">
|
|
118
|
+
<DropdownMenuLabel>Panel Position</DropdownMenuLabel>
|
|
119
|
+
<DropdownMenuSeparator />
|
|
120
|
+
<DropdownMenuRadioGroup value={position} onValueChange={setPosition}>
|
|
121
|
+
<DropdownMenuRadioItem value="top">Top</DropdownMenuRadioItem>
|
|
122
|
+
<DropdownMenuRadioItem value="bottom">Bottom</DropdownMenuRadioItem>
|
|
123
|
+
<DropdownMenuRadioItem value="right">Right</DropdownMenuRadioItem>
|
|
124
|
+
</DropdownMenuRadioGroup>
|
|
125
|
+
</DropdownMenuContent>
|
|
126
|
+
</DropdownMenu>
|
|
127
|
+
)
|
|
128
|
+
},
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export const DarkMode: Story = {
|
|
132
|
+
parameters: {
|
|
133
|
+
backgrounds: { default: "dark" },
|
|
134
|
+
},
|
|
135
|
+
render: () => (
|
|
136
|
+
<div className="dark">
|
|
137
|
+
<DropdownMenu>
|
|
138
|
+
<DropdownMenuTrigger asChild>
|
|
139
|
+
<Button variant="outline">Open Menu</Button>
|
|
140
|
+
</DropdownMenuTrigger>
|
|
141
|
+
<DropdownMenuContent>
|
|
142
|
+
<DropdownMenuLabel>My Account</DropdownMenuLabel>
|
|
143
|
+
<DropdownMenuSeparator />
|
|
144
|
+
<DropdownMenuItem>Profile</DropdownMenuItem>
|
|
145
|
+
<DropdownMenuItem>Settings</DropdownMenuItem>
|
|
146
|
+
</DropdownMenuContent>
|
|
147
|
+
</DropdownMenu>
|
|
148
|
+
</div>
|
|
149
|
+
),
|
|
150
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import {
|
|
3
|
+
DropdownMenu as ShadcnDropdownMenu,
|
|
4
|
+
DropdownMenuCheckboxItem,
|
|
5
|
+
DropdownMenuContent,
|
|
6
|
+
DropdownMenuGroup,
|
|
7
|
+
DropdownMenuItem,
|
|
8
|
+
DropdownMenuLabel,
|
|
9
|
+
DropdownMenuPortal,
|
|
10
|
+
DropdownMenuRadioGroup,
|
|
11
|
+
DropdownMenuRadioItem,
|
|
12
|
+
DropdownMenuSeparator,
|
|
13
|
+
DropdownMenuShortcut,
|
|
14
|
+
DropdownMenuSub,
|
|
15
|
+
DropdownMenuSubContent,
|
|
16
|
+
DropdownMenuSubTrigger,
|
|
17
|
+
DropdownMenuTrigger,
|
|
18
|
+
} from "../../ui/dropdown-menu"
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* DropdownMenu Primitive
|
|
22
|
+
*
|
|
23
|
+
* Displays a menu with actions or options triggered by a button.
|
|
24
|
+
* Built on Radix UI DropdownMenu primitive with WCAG 2.1 Level AA compliance.
|
|
25
|
+
*
|
|
26
|
+
* @see https://ui.shadcn.com/docs/components/dropdown-menu
|
|
27
|
+
* @see https://www.radix-ui.com/primitives/docs/components/dropdown-menu
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
export type DropdownMenuProps = React.ComponentProps<typeof ShadcnDropdownMenu>
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* DropdownMenu component
|
|
34
|
+
*
|
|
35
|
+
* Displays a menu with actions or options triggered by a button.
|
|
36
|
+
* Built on Radix UI DropdownMenu primitive with full accessibility support.
|
|
37
|
+
*
|
|
38
|
+
* Features:
|
|
39
|
+
* - Keyboard navigation
|
|
40
|
+
* - Checkbox and radio item support
|
|
41
|
+
* - Submenu support
|
|
42
|
+
* - Keyboard shortcuts display
|
|
43
|
+
* - ARIA attributes
|
|
44
|
+
* - Dark mode support
|
|
45
|
+
*/
|
|
46
|
+
export const DropdownMenu = React.forwardRef<
|
|
47
|
+
React.ElementRef<typeof ShadcnDropdownMenu>,
|
|
48
|
+
DropdownMenuProps
|
|
49
|
+
>((props, ref) => {
|
|
50
|
+
return <ShadcnDropdownMenu {...props} />
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
DropdownMenu.displayName = "DropdownMenu"
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Re-export DropdownMenu sub-components for composition
|
|
57
|
+
*/
|
|
58
|
+
export {
|
|
59
|
+
DropdownMenuCheckboxItem,
|
|
60
|
+
DropdownMenuContent,
|
|
61
|
+
DropdownMenuGroup,
|
|
62
|
+
DropdownMenuItem,
|
|
63
|
+
DropdownMenuLabel,
|
|
64
|
+
DropdownMenuPortal,
|
|
65
|
+
DropdownMenuRadioGroup,
|
|
66
|
+
DropdownMenuRadioItem,
|
|
67
|
+
DropdownMenuSeparator,
|
|
68
|
+
DropdownMenuShortcut,
|
|
69
|
+
DropdownMenuSub,
|
|
70
|
+
DropdownMenuSubContent,
|
|
71
|
+
DropdownMenuSubTrigger,
|
|
72
|
+
DropdownMenuTrigger,
|
|
73
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./DropdownMenu"
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import { HoverCard, HoverCardContent, HoverCardTrigger } from "./HoverCard";
|
|
3
|
+
|
|
4
|
+
const meta: Meta<typeof HoverCard> = {
|
|
5
|
+
title: "Primitives/HoverCard",
|
|
6
|
+
component: HoverCard,
|
|
7
|
+
parameters: { layout: "centered" },
|
|
8
|
+
tags: ["autodocs"],
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export default meta;
|
|
12
|
+
type Story = StoryObj<typeof HoverCard>;
|
|
13
|
+
|
|
14
|
+
export const Default: Story = {
|
|
15
|
+
render: () => (
|
|
16
|
+
<HoverCard>
|
|
17
|
+
<HoverCardTrigger>Hover me</HoverCardTrigger>
|
|
18
|
+
<HoverCardContent>
|
|
19
|
+
<div className="space-y-2">
|
|
20
|
+
<h4 className="text-sm font-semibold">@username</h4>
|
|
21
|
+
<p className="text-sm">Additional information appears here.</p>
|
|
22
|
+
</div>
|
|
23
|
+
</HoverCardContent>
|
|
24
|
+
</HoverCard>
|
|
25
|
+
),
|
|
26
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import {
|
|
5
|
+
HoverCard as ShadcnHoverCard,
|
|
6
|
+
HoverCardContent as ShadcnHoverCardContent,
|
|
7
|
+
HoverCardTrigger as ShadcnHoverCardTrigger,
|
|
8
|
+
} from "@/components/ui/hover-card";
|
|
9
|
+
|
|
10
|
+
export type HoverCardProps = React.ComponentProps<typeof ShadcnHoverCard>;
|
|
11
|
+
export type HoverCardTriggerProps = React.ComponentProps<typeof ShadcnHoverCardTrigger>;
|
|
12
|
+
export type HoverCardContentProps = React.ComponentProps<typeof ShadcnHoverCardContent>;
|
|
13
|
+
|
|
14
|
+
export const HoverCard = React.memo<HoverCardProps>(
|
|
15
|
+
React.forwardRef<React.ElementRef<typeof ShadcnHoverCard>, HoverCardProps>(
|
|
16
|
+
(props, ref) => {
|
|
17
|
+
return <ShadcnHoverCard {...props} />;
|
|
18
|
+
}
|
|
19
|
+
)
|
|
20
|
+
);
|
|
21
|
+
HoverCard.displayName = "HoverCard";
|
|
22
|
+
|
|
23
|
+
export const HoverCardTrigger = React.memo<HoverCardTriggerProps>(
|
|
24
|
+
React.forwardRef<React.ElementRef<typeof ShadcnHoverCardTrigger>, HoverCardTriggerProps>(
|
|
25
|
+
(props, ref) => {
|
|
26
|
+
return <ShadcnHoverCardTrigger ref={ref} {...props} />;
|
|
27
|
+
}
|
|
28
|
+
)
|
|
29
|
+
);
|
|
30
|
+
HoverCardTrigger.displayName = "HoverCardTrigger";
|
|
31
|
+
|
|
32
|
+
export const HoverCardContent = React.memo<HoverCardContentProps>(
|
|
33
|
+
React.forwardRef<React.ElementRef<typeof ShadcnHoverCardContent>, HoverCardContentProps>(
|
|
34
|
+
(props, ref) => {
|
|
35
|
+
return <ShadcnHoverCardContent ref={ref} {...props} />;
|
|
36
|
+
}
|
|
37
|
+
)
|
|
38
|
+
);
|
|
39
|
+
HoverCardContent.displayName = "HoverCardContent";
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import { Icon } from "./Icon";
|
|
3
|
+
import { iconRegistry } from "@/registry/icons";
|
|
4
|
+
import { Button } from "../Button";
|
|
5
|
+
|
|
6
|
+
const meta: Meta<typeof Icon> = {
|
|
7
|
+
title: "Primitives/Icon",
|
|
8
|
+
component: Icon,
|
|
9
|
+
parameters: {
|
|
10
|
+
layout: "padded",
|
|
11
|
+
},
|
|
12
|
+
argTypes: {
|
|
13
|
+
name: {
|
|
14
|
+
control: "select",
|
|
15
|
+
options: iconRegistry.getNames(),
|
|
16
|
+
description: "Name of the icon from the registry",
|
|
17
|
+
},
|
|
18
|
+
size: {
|
|
19
|
+
control: "select",
|
|
20
|
+
options: ["xs", "sm", "default", "lg", "xl"],
|
|
21
|
+
description: "Size variant of the icon",
|
|
22
|
+
},
|
|
23
|
+
"aria-label": {
|
|
24
|
+
control: "text",
|
|
25
|
+
description: "Accessibility label for screen readers",
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
} satisfies Meta<typeof Icon>;
|
|
29
|
+
|
|
30
|
+
export default meta;
|
|
31
|
+
type Story = StoryObj<typeof meta>;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Default icon display
|
|
35
|
+
*/
|
|
36
|
+
export const Default: Story = {
|
|
37
|
+
args: {
|
|
38
|
+
name: "check",
|
|
39
|
+
size: "default",
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Icon gallery showing all available icons
|
|
45
|
+
*/
|
|
46
|
+
export const AllIcons: Story = {
|
|
47
|
+
render: () => {
|
|
48
|
+
const iconNames = iconRegistry.getNames();
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<div className="space-y-6">
|
|
52
|
+
<div>
|
|
53
|
+
<h3 className="mb-4 font-semibold text-lg">
|
|
54
|
+
All Available Icons ({iconNames.length})
|
|
55
|
+
</h3>
|
|
56
|
+
<div className="grid grid-cols-8 gap-4 md:grid-cols-12">
|
|
57
|
+
{iconNames.map((name) => (
|
|
58
|
+
<div
|
|
59
|
+
key={name}
|
|
60
|
+
className="flex flex-col items-center gap-2 rounded-md border border-border p-3 hover:bg-accent"
|
|
61
|
+
>
|
|
62
|
+
<Icon name={name} size="default" aria-label={name} />
|
|
63
|
+
<span className="text-center text-muted-foreground text-xs">
|
|
64
|
+
{name}
|
|
65
|
+
</span>
|
|
66
|
+
</div>
|
|
67
|
+
))}
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
);
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Size variants comparison
|
|
77
|
+
*/
|
|
78
|
+
export const AllSizes: Story = {
|
|
79
|
+
render: () => (
|
|
80
|
+
<div className="space-y-6">
|
|
81
|
+
<div className="flex items-center gap-8">
|
|
82
|
+
<div className="flex flex-col items-center gap-2">
|
|
83
|
+
<Icon name="check" size="xs" />
|
|
84
|
+
<span className="text-muted-foreground text-xs">xs (12px)</span>
|
|
85
|
+
</div>
|
|
86
|
+
<div className="flex flex-col items-center gap-2">
|
|
87
|
+
<Icon name="check" size="sm" />
|
|
88
|
+
<span className="text-muted-foreground text-xs">sm (16px)</span>
|
|
89
|
+
</div>
|
|
90
|
+
<div className="flex flex-col items-center gap-2">
|
|
91
|
+
<Icon name="check" size="default" />
|
|
92
|
+
<span className="text-muted-foreground text-xs">default (20px)</span>
|
|
93
|
+
</div>
|
|
94
|
+
<div className="flex flex-col items-center gap-2">
|
|
95
|
+
<Icon name="check" size="lg" />
|
|
96
|
+
<span className="text-muted-foreground text-xs">lg (24px)</span>
|
|
97
|
+
</div>
|
|
98
|
+
<div className="flex flex-col items-center gap-2">
|
|
99
|
+
<Icon name="check" size="xl" />
|
|
100
|
+
<span className="text-muted-foreground text-xs">xl (32px)</span>
|
|
101
|
+
</div>
|
|
102
|
+
</div>
|
|
103
|
+
</div>
|
|
104
|
+
),
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Icons with different colors (inherits from currentColor)
|
|
109
|
+
*/
|
|
110
|
+
export const WithColor: Story = {
|
|
111
|
+
render: () => (
|
|
112
|
+
<div className="flex items-center gap-6">
|
|
113
|
+
<div className="flex flex-col items-center gap-2">
|
|
114
|
+
<Icon name="check-circle" size="lg" className="text-green-600" />
|
|
115
|
+
<span className="text-muted-foreground text-xs">Success</span>
|
|
116
|
+
</div>
|
|
117
|
+
<div className="flex flex-col items-center gap-2">
|
|
118
|
+
<Icon name="alert-circle" size="lg" className="text-yellow-600" />
|
|
119
|
+
<span className="text-muted-foreground text-xs">Warning</span>
|
|
120
|
+
</div>
|
|
121
|
+
<div className="flex flex-col items-center gap-2">
|
|
122
|
+
<Icon name="info" size="lg" className="text-blue-600" />
|
|
123
|
+
<span className="text-muted-foreground text-xs">Info</span>
|
|
124
|
+
</div>
|
|
125
|
+
<div className="flex flex-col items-center gap-2">
|
|
126
|
+
<Icon name="x" size="lg" className="text-red-600" />
|
|
127
|
+
<span className="text-muted-foreground text-xs">Error</span>
|
|
128
|
+
</div>
|
|
129
|
+
<div className="flex flex-col items-center gap-2">
|
|
130
|
+
<Icon name="settings" size="lg" className="text-purple-600" />
|
|
131
|
+
<span className="text-muted-foreground text-xs">Custom</span>
|
|
132
|
+
</div>
|
|
133
|
+
</div>
|
|
134
|
+
),
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Accessible icons with aria-label
|
|
139
|
+
*/
|
|
140
|
+
export const Accessible: Story = {
|
|
141
|
+
render: () => (
|
|
142
|
+
<div className="space-y-4">
|
|
143
|
+
<div className="rounded-md border border-border bg-muted p-4">
|
|
144
|
+
<p className="mb-4 text-sm">
|
|
145
|
+
Icons with aria-label are announced by screen readers:
|
|
146
|
+
</p>
|
|
147
|
+
<div className="flex items-center gap-4">
|
|
148
|
+
<Icon name="check" size="default" aria-label="Success" />
|
|
149
|
+
<Icon name="x" size="default" aria-label="Error" />
|
|
150
|
+
<Icon name="info" size="default" aria-label="Information" />
|
|
151
|
+
<Icon name="warning" size="default" aria-label="Warning" />
|
|
152
|
+
</div>
|
|
153
|
+
</div>
|
|
154
|
+
<div className="rounded-md border border-border bg-muted p-4">
|
|
155
|
+
<p className="mb-4 text-sm">
|
|
156
|
+
Decorative icons without aria-label are hidden from screen readers:
|
|
157
|
+
</p>
|
|
158
|
+
<div className="flex items-center gap-4">
|
|
159
|
+
<Icon name="chevron-right" size="sm" />
|
|
160
|
+
<Icon name="menu" size="sm" />
|
|
161
|
+
<Icon name="more-horizontal" size="sm" />
|
|
162
|
+
</div>
|
|
163
|
+
</div>
|
|
164
|
+
</div>
|
|
165
|
+
),
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Error handling for missing icons
|
|
170
|
+
*/
|
|
171
|
+
export const NotFound: Story = {
|
|
172
|
+
render: () => (
|
|
173
|
+
<div className="space-y-4">
|
|
174
|
+
<div className="rounded-md border border-border bg-muted p-4">
|
|
175
|
+
<p className="mb-4 text-sm">
|
|
176
|
+
Missing icons are handled gracefully (returns null and logs warning in
|
|
177
|
+
dev):
|
|
178
|
+
</p>
|
|
179
|
+
<div className="flex items-center gap-4">
|
|
180
|
+
<Icon name="check" size="default" />
|
|
181
|
+
<Icon name="invalid-icon-name" size="default" />
|
|
182
|
+
<Icon name="x" size="default" />
|
|
183
|
+
</div>
|
|
184
|
+
<p className="mt-4 text-muted-foreground text-xs">
|
|
185
|
+
Open browser console to see the warning for "invalid-icon-name"
|
|
186
|
+
</p>
|
|
187
|
+
</div>
|
|
188
|
+
</div>
|
|
189
|
+
),
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Icons in buttons
|
|
194
|
+
*/
|
|
195
|
+
export const InButtons: Story = {
|
|
196
|
+
render: () => (
|
|
197
|
+
<div className="flex flex-wrap items-center gap-4">
|
|
198
|
+
<Button>
|
|
199
|
+
<Icon name="plus" size="sm" className="mr-2" />
|
|
200
|
+
Add Item
|
|
201
|
+
</Button>
|
|
202
|
+
<Button variant="secondary">
|
|
203
|
+
<Icon name="download" size="sm" className="mr-2" />
|
|
204
|
+
Download
|
|
205
|
+
</Button>
|
|
206
|
+
<Button variant="outline">
|
|
207
|
+
<Icon name="settings" size="sm" className="mr-2" />
|
|
208
|
+
Settings
|
|
209
|
+
</Button>
|
|
210
|
+
<Button variant="ghost">
|
|
211
|
+
<Icon name="refresh-cw" size="sm" className="mr-2" />
|
|
212
|
+
Refresh
|
|
213
|
+
</Button>
|
|
214
|
+
<Button variant="destructive">
|
|
215
|
+
<Icon name="x" size="sm" className="mr-2" />
|
|
216
|
+
Delete
|
|
217
|
+
</Button>
|
|
218
|
+
<Button size="icon" variant="outline">
|
|
219
|
+
<Icon name="search" size="sm" aria-label="Search" />
|
|
220
|
+
</Button>
|
|
221
|
+
<Button size="icon" variant="ghost">
|
|
222
|
+
<Icon name="more-vertical" size="sm" aria-label="More options" />
|
|
223
|
+
</Button>
|
|
224
|
+
</div>
|
|
225
|
+
),
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Loading spinner animation
|
|
230
|
+
*/
|
|
231
|
+
export const LoadingSpinner: Story = {
|
|
232
|
+
render: () => (
|
|
233
|
+
<div className="flex items-center gap-6">
|
|
234
|
+
<Icon
|
|
235
|
+
name="loader-2"
|
|
236
|
+
size="default"
|
|
237
|
+
className="animate-spin"
|
|
238
|
+
aria-label="Loading"
|
|
239
|
+
/>
|
|
240
|
+
<Icon
|
|
241
|
+
name="loader-2"
|
|
242
|
+
size="lg"
|
|
243
|
+
className="animate-spin text-primary"
|
|
244
|
+
aria-label="Loading"
|
|
245
|
+
/>
|
|
246
|
+
<Icon
|
|
247
|
+
name="loader-2"
|
|
248
|
+
size="xl"
|
|
249
|
+
className="animate-spin text-muted-foreground"
|
|
250
|
+
aria-label="Loading"
|
|
251
|
+
/>
|
|
252
|
+
</div>
|
|
253
|
+
),
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Navigation icons
|
|
258
|
+
*/
|
|
259
|
+
export const Navigation: Story = {
|
|
260
|
+
render: () => (
|
|
261
|
+
<div className="space-y-6">
|
|
262
|
+
<div>
|
|
263
|
+
<h4 className="mb-3 font-semibold text-sm">Chevrons</h4>
|
|
264
|
+
<div className="flex items-center gap-4">
|
|
265
|
+
<Icon name="chevron-up" size="default" />
|
|
266
|
+
<Icon name="chevron-down" size="default" />
|
|
267
|
+
<Icon name="chevron-left" size="default" />
|
|
268
|
+
<Icon name="chevron-right" size="default" />
|
|
269
|
+
<Icon name="chevrons-up-down" size="default" />
|
|
270
|
+
</div>
|
|
271
|
+
</div>
|
|
272
|
+
<div>
|
|
273
|
+
<h4 className="mb-3 font-semibold text-sm">Arrows</h4>
|
|
274
|
+
<div className="flex items-center gap-4">
|
|
275
|
+
<Icon name="arrow-left" size="default" />
|
|
276
|
+
<Icon name="arrow-right" size="default" />
|
|
277
|
+
</div>
|
|
278
|
+
</div>
|
|
279
|
+
</div>
|
|
280
|
+
),
|
|
281
|
+
};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { iconRegistry, type IconName } from "@/registry/icons";
|
|
5
|
+
import { cn } from "@/lib/utils";
|
|
6
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Icon Primitive
|
|
10
|
+
*
|
|
11
|
+
* A reusable icon component that uses the centralized icon registry.
|
|
12
|
+
* All icons MUST use this component or the iconRegistry directly.
|
|
13
|
+
*
|
|
14
|
+
* Features:
|
|
15
|
+
* - Size variants (xs, sm, default, lg, xl)
|
|
16
|
+
* - Accessibility support with aria-label
|
|
17
|
+
* - Error handling for missing icons
|
|
18
|
+
* - Color inherits from currentColor
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
const iconVariants = cva("inline-block flex-shrink-0", {
|
|
22
|
+
variants: {
|
|
23
|
+
size: {
|
|
24
|
+
xs: "h-3 w-3",
|
|
25
|
+
sm: "h-4 w-4",
|
|
26
|
+
default: "h-5 w-5",
|
|
27
|
+
lg: "h-6 w-6",
|
|
28
|
+
xl: "h-8 w-8",
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
defaultVariants: {
|
|
32
|
+
size: "default",
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
export interface IconProps
|
|
37
|
+
extends Omit<React.SVGProps<SVGSVGElement>, "viewBox" | "children">,
|
|
38
|
+
VariantProps<typeof iconVariants> {
|
|
39
|
+
/**
|
|
40
|
+
* Name of the icon from the icon registry
|
|
41
|
+
* Type-safe: only accepts valid icon names
|
|
42
|
+
*/
|
|
43
|
+
name: IconName | string;
|
|
44
|
+
/**
|
|
45
|
+
* Optional aria-label for accessibility
|
|
46
|
+
* If provided, aria-hidden will be false
|
|
47
|
+
*/
|
|
48
|
+
"aria-label"?: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Icon component - renders icons from the icon registry
|
|
53
|
+
*/
|
|
54
|
+
export const Icon = React.memo<IconProps>(
|
|
55
|
+
({ name, size, className, "aria-label": ariaLabel, ...props }) => {
|
|
56
|
+
const icon = iconRegistry.get(name);
|
|
57
|
+
|
|
58
|
+
if (!icon) {
|
|
59
|
+
if (process.env.NODE_ENV === "development") {
|
|
60
|
+
console.warn(
|
|
61
|
+
`Icon "${name}" not found in registry. Available icons:`,
|
|
62
|
+
iconRegistry.getNames().join(", ")
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<svg
|
|
70
|
+
viewBox={icon.viewBox}
|
|
71
|
+
className={cn(iconVariants({ size }), className)}
|
|
72
|
+
aria-hidden={!ariaLabel}
|
|
73
|
+
aria-label={ariaLabel}
|
|
74
|
+
fill="none"
|
|
75
|
+
stroke="currentColor"
|
|
76
|
+
strokeWidth="2"
|
|
77
|
+
strokeLinecap="round"
|
|
78
|
+
strokeLinejoin="round"
|
|
79
|
+
{...props}
|
|
80
|
+
>
|
|
81
|
+
<path d={icon.path} />
|
|
82
|
+
</svg>
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
Icon.displayName = "Icon";
|