@gunjo/ui 0.0.1-alpha.0 → 0.0.1-alpha.2
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.ja.md +90 -0
- package/README.md +52 -91
- package/package.json +47 -6
- package/src/components/display/Accordion.tsx +185 -0
- package/src/components/display/AccordionGroup.tsx +155 -0
- package/src/components/display/ActionDataTable.tsx +413 -0
- package/src/components/display/ActivityTimelineCard.tsx +483 -0
- package/src/components/display/AnalyticsCard.tsx +167 -0
- package/src/components/display/AssetCard.tsx +242 -0
- package/src/components/display/AssetGrid.tsx +164 -0
- package/src/components/display/Avatar.tsx +127 -0
- package/src/components/display/AvatarGroup.tsx +131 -0
- package/src/components/{atoms → display}/Badge.tsx +3 -3
- package/src/components/display/BarChart.tsx +247 -0
- package/src/components/{molecules → display}/Card.tsx +1 -1
- package/src/components/display/Carousel.tsx +593 -0
- package/src/components/display/ChartLegend.tsx +124 -0
- package/src/components/display/ChatMessage.tsx +382 -0
- package/src/components/display/ChoroplethMap.tsx +613 -0
- package/src/components/display/Code.tsx +42 -0
- package/src/components/display/CodeBlock.tsx +338 -0
- package/src/components/display/ColorSwatch.tsx +71 -0
- package/src/components/display/ConcentricProgressCard.tsx +545 -0
- package/src/components/display/DataTable.tsx +522 -0
- package/src/components/display/DistributionBar.tsx +102 -0
- package/src/components/display/DocNote.tsx +36 -0
- package/src/components/display/DonutChart.tsx +257 -0
- package/src/components/display/EmptyState.tsx +44 -0
- package/src/components/display/FileTree.tsx +180 -0
- package/src/components/display/GaugeChart.tsx +219 -0
- package/src/components/display/HeatmapChart.tsx +266 -0
- package/src/components/display/Icon.tsx +66 -0
- package/src/components/display/ImagePreview.tsx +140 -0
- package/src/components/{atoms → display}/Img.tsx +46 -12
- package/src/components/display/LabeledDonutCard.tsx +475 -0
- package/src/components/display/LineChart.tsx +464 -0
- package/src/components/{molecules → display}/List.tsx +20 -13
- package/src/components/display/MarkdownRenderer.tsx +157 -0
- package/src/components/display/MetadataList.tsx +81 -0
- package/src/components/display/MiniDistributionBarCard.tsx +314 -0
- package/src/components/display/PieChart.tsx +234 -0
- package/src/components/display/QuadrantMatrix.tsx +330 -0
- package/src/components/display/RadarChart.tsx +335 -0
- package/src/components/display/RadialBarChart.tsx +264 -0
- package/src/components/display/RetentionCohortCard.tsx +350 -0
- package/src/components/display/RibbonChart.tsx +618 -0
- package/src/components/display/SearchableAccordion.tsx +270 -0
- package/src/components/display/SegmentTimelineCard.tsx +452 -0
- package/src/components/display/SegmentedGaugeCard.tsx +607 -0
- package/src/components/display/Spacer.tsx +51 -0
- package/src/components/display/SparklineChart.tsx +394 -0
- package/src/components/display/StackedBarChart.tsx +393 -0
- package/src/components/display/Statistic.tsx +70 -0
- package/src/components/{molecules → display}/Table.tsx +22 -7
- package/src/components/display/Tag.tsx +80 -0
- package/src/components/display/TagEditor.tsx +141 -0
- package/src/components/display/Timeline.tsx +121 -0
- package/src/components/{atoms → display}/ToolPill.tsx +42 -18
- package/src/components/display/TreeView.tsx +226 -0
- package/src/components/display/chart-tooltip.tsx +423 -0
- package/src/components/display/chart-utils.ts +71 -0
- package/src/components/display/circular-chart-utils.ts +147 -0
- package/src/components/display/generated/default-variant-keys.ts +90 -0
- package/src/components/display/generated/variant-keys.ts +169 -0
- package/src/components/{atoms → feedback}/Alert.tsx +12 -5
- package/src/components/feedback/Banner.tsx +90 -0
- package/src/components/{molecules → feedback}/NotificationCenter.tsx +64 -31
- package/src/components/feedback/ProgressWidget.tsx +44 -0
- package/src/components/{atoms → feedback}/Spinner.tsx +2 -2
- package/src/components/{molecules → feedback}/StatusBar.tsx +4 -4
- package/src/components/feedback/StatusScreen.tsx +148 -0
- package/src/components/{molecules → feedback}/Stepper.tsx +10 -5
- package/src/components/feedback/Toast.tsx +108 -0
- package/src/components/feedback/ToastProvider.tsx +78 -0
- package/src/components/feedback/generated/default-variant-keys.ts +16 -0
- package/src/components/feedback/generated/variant-keys.ts +21 -0
- package/src/components/generated/component-manifest.ts +1568 -454
- package/src/components/generated/component-style-hints.ts +1958 -718
- package/src/components/{atoms → inputs}/ButtonVariants.ts +13 -3
- package/src/components/inputs/Calendar.tsx +212 -0
- package/src/components/inputs/ChatComposer.tsx +75 -0
- package/src/components/inputs/ChatInput.tsx +528 -0
- package/src/components/{atoms → inputs}/Checkbox.tsx +2 -2
- package/src/components/inputs/Combobox.tsx +175 -0
- package/src/components/inputs/CopyButton.tsx +187 -0
- package/src/components/inputs/DatePicker.tsx +519 -0
- package/src/components/inputs/DateRangePicker.tsx +878 -0
- package/src/components/inputs/EditableField.tsx +182 -0
- package/src/components/{organisms → inputs}/FileUploader.tsx +24 -9
- package/src/components/inputs/FilterButton.tsx +163 -0
- package/src/components/{molecules → inputs}/Form.tsx +20 -3
- package/src/components/{atoms → inputs}/Input.tsx +2 -0
- package/src/components/inputs/InputOTP.tsx +75 -0
- package/src/components/inputs/Mention.tsx +279 -0
- package/src/components/inputs/NumberInput.tsx +109 -0
- package/src/components/inputs/PasswordGroup.tsx +138 -0
- package/src/components/inputs/PasswordInput.tsx +74 -0
- package/src/components/inputs/PasswordRequirementList.tsx +96 -0
- package/src/components/inputs/PasswordStrengthMeter.tsx +93 -0
- package/src/components/inputs/PhoneInput.tsx +99 -0
- package/src/components/inputs/PostalCodeInput.tsx +98 -0
- package/src/components/inputs/RangeSlider.tsx +129 -0
- package/src/components/inputs/SearchInput.tsx +76 -0
- package/src/components/inputs/Select.tsx +39 -0
- package/src/components/{atoms → inputs}/Slider.tsx +18 -5
- package/src/components/{molecules → inputs}/SortButton.tsx +5 -2
- package/src/components/{atoms → inputs}/Switch.tsx +15 -4
- package/src/components/inputs/TagInput.tsx +114 -0
- package/src/components/{atoms → inputs}/Textarea.tsx +1 -0
- package/src/components/inputs/TimePicker.tsx +150 -0
- package/src/components/inputs/Toggle.tsx +48 -0
- package/src/components/{atoms → inputs}/ToggleGroup.tsx +2 -2
- package/src/components/inputs/TooltipButton.tsx +148 -0
- package/src/components/inputs/VoiceInputButton.tsx +317 -0
- package/src/components/inputs/calendar-holidays.ts +56 -0
- package/src/components/inputs/generated/default-variant-keys.ts +32 -0
- package/src/components/{atoms → inputs}/generated/variant-keys.ts +19 -27
- package/src/components/layout/AspectRatio.tsx +12 -0
- package/src/components/layout/AssetInspectorPanel.tsx +416 -0
- package/src/components/layout/Cluster.tsx +56 -0
- package/src/components/layout/CollapsiblePanelToggle.tsx +94 -0
- package/src/components/layout/Container.tsx +43 -0
- package/src/components/layout/DeviceFrame.tsx +227 -0
- package/src/components/layout/Grid.tsx +65 -0
- package/src/components/layout/HStack.tsx +73 -0
- package/src/components/{organisms → layout}/InspectorPanel.tsx +6 -5
- package/src/components/layout/MarqueeFrame.tsx +158 -0
- package/src/components/layout/Resizable.tsx +94 -0
- package/src/components/layout/ScrollArea.tsx +71 -0
- package/src/components/{organisms → layout}/SpatialCanvas.tsx +12 -7
- package/src/components/layout/VStack.tsx +69 -0
- package/src/components/layout/generated/default-variant-keys.ts +16 -0
- package/src/components/layout/generated/variant-keys.ts +21 -0
- package/src/components/{molecules → navigation}/Breadcrumb.tsx +5 -4
- package/src/components/navigation/Command.tsx +266 -0
- package/src/components/navigation/CommandPalette.tsx +83 -0
- package/src/components/navigation/DocumentPager.tsx +171 -0
- package/src/components/navigation/Footer.tsx +88 -0
- package/src/components/navigation/Header.tsx +80 -0
- package/src/components/{molecules → navigation}/Menubar.tsx +45 -12
- package/src/components/navigation/NavigationMenu.tsx +128 -0
- package/src/components/navigation/PageAside.tsx +84 -0
- package/src/components/{molecules → navigation}/Pagination.tsx +60 -7
- package/src/components/{organisms → navigation}/RightRail.tsx +1 -1
- package/src/components/navigation/Sidebar.tsx +223 -0
- package/src/components/navigation/SidebarItem.tsx +160 -0
- package/src/components/{molecules → navigation}/Tabs.tsx +2 -2
- package/src/components/navigation/TextLink.tsx +71 -0
- package/src/components/navigation/generated/default-variant-keys.ts +12 -0
- package/src/components/navigation/generated/variant-keys.ts +13 -0
- package/src/components/overlay/AIChatInput.tsx +5 -0
- package/src/components/overlay/AIChatMessage.tsx +6 -0
- package/src/components/overlay/AlertDialog.tsx +145 -0
- package/src/components/overlay/ChatPanel.tsx +180 -0
- package/src/components/{molecules → overlay}/ContextMenu.tsx +65 -29
- package/src/components/{molecules → overlay}/Dialog.tsx +21 -13
- package/src/components/overlay/Drawer.tsx +131 -0
- package/src/components/{molecules → overlay}/DropdownMenu.tsx +52 -17
- package/src/components/overlay/FloatingPanel.tsx +90 -0
- package/src/components/overlay/HoverCard.tsx +36 -0
- package/src/components/overlay/MediaLightbox.tsx +403 -0
- package/src/components/overlay/MediaPickerDialog.tsx +198 -0
- package/src/components/overlay/Modal.tsx +103 -0
- package/src/components/overlay/OnboardingFlow.tsx +172 -0
- package/src/components/overlay/Popover.tsx +36 -0
- package/src/components/overlay/ShareModal.tsx +324 -0
- package/src/components/{molecules → overlay}/Sheet.tsx +76 -19
- package/src/components/overlay/Tooltip.tsx +130 -0
- package/src/components/overlay/generated/default-variant-keys.ts +14 -0
- package/src/components/overlay/generated/variant-keys.ts +17 -0
- package/src/components/patterns/BlogTemplate.tsx +46 -0
- package/src/components/{templates → patterns}/DashboardTemplate.tsx +2 -2
- package/src/components/patterns/DocsTemplate.tsx +41 -0
- package/src/components/{templates → patterns}/MediaLibraryTemplate.tsx +1 -1
- package/src/components/patterns/OnboardingTemplate.tsx +32 -0
- package/src/components/patterns/PricingTemplate.tsx +106 -0
- package/src/globals.css +173 -22
- package/src/index.ts +177 -76
- package/tailwind-theme-extend.cjs +48 -3
- package/design/atoms-metadata.json +0 -82
- package/design/molecules-metadata.json +0 -130
- package/design/organisms-metadata.json +0 -38
- package/design/templates-metadata.json +0 -38
- package/src/components/atoms/Avatar.tsx +0 -57
- package/src/components/atoms/Select.tsx +0 -28
- package/src/components/atoms/generated/default-variant-keys.ts +0 -36
- package/src/components/molecules/AIChatInput.tsx +0 -140
- package/src/components/molecules/AIChatMessage.tsx +0 -109
- package/src/components/molecules/Accordion.tsx +0 -99
- package/src/components/molecules/Calendar.tsx +0 -60
- package/src/components/molecules/Carousel.tsx +0 -261
- package/src/components/molecules/Command.tsx +0 -152
- package/src/components/molecules/FilterButton.tsx +0 -133
- package/src/components/molecules/HoverCard.tsx +0 -29
- package/src/components/molecules/Modal.tsx +0 -66
- package/src/components/molecules/Popover.tsx +0 -31
- package/src/components/molecules/ProgressWidget.tsx +0 -40
- package/src/components/molecules/Resizable.tsx +0 -47
- package/src/components/molecules/ScrollArea.tsx +0 -48
- package/src/components/molecules/SidebarItem.tsx +0 -134
- package/src/components/molecules/Toast.tsx +0 -57
- package/src/components/molecules/Tooltip.tsx +0 -30
- package/src/components/molecules/generated/default-variant-keys.ts +0 -22
- package/src/components/molecules/generated/variant-keys.ts +0 -33
- package/src/components/organisms/CommandPalette.tsx +0 -58
- package/src/components/organisms/FloatingPanel.tsx +0 -46
- package/src/components/organisms/ShareModal.tsx +0 -182
- package/src/components/organisms/ToastProvider.tsx +0 -49
- /package/src/components/{atoms → display}/Kbd.tsx +0 -0
- /package/src/components/{atoms → display}/Separator.tsx +0 -0
- /package/src/components/{atoms → display}/Skeleton.tsx +0 -0
- /package/src/components/{atoms → feedback}/Progress.tsx +0 -0
- /package/src/components/{atoms → inputs}/Button.tsx +0 -0
- /package/src/components/{atoms → inputs}/Label.tsx +0 -0
- /package/src/components/{atoms → inputs}/RadioGroup.tsx +0 -0
- /package/src/components/{organisms → navigation}/AppRail.tsx +0 -0
- /package/src/components/{templates → patterns}/AuthTemplate.tsx +0 -0
- /package/src/components/{templates → patterns}/BannalyzeTemplate.tsx +0 -0
- /package/src/components/{templates → patterns}/ChatTemplate.tsx +0 -0
- /package/src/components/{templates → patterns}/EditorTemplate.tsx +0 -0
- /package/src/components/{templates → patterns}/KanbanTemplate.tsx +0 -0
- /package/src/components/{templates → patterns}/LandingTemplate.tsx +0 -0
- /package/src/components/{templates → patterns}/SettingsTemplate.tsx +0 -0
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"
|
|
5
|
+
|
|
6
|
+
import { cn } from "../../lib/utils"
|
|
7
|
+
import { buttonVariants } from "../inputs/ButtonVariants"
|
|
8
|
+
|
|
9
|
+
const AlertDialog = AlertDialogPrimitive.Root
|
|
10
|
+
|
|
11
|
+
const AlertDialogTrigger = AlertDialogPrimitive.Trigger
|
|
12
|
+
|
|
13
|
+
const AlertDialogPortal = AlertDialogPrimitive.Portal
|
|
14
|
+
|
|
15
|
+
const AlertDialogOverlay = React.forwardRef<
|
|
16
|
+
React.ElementRef<typeof AlertDialogPrimitive.Overlay>,
|
|
17
|
+
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Overlay>
|
|
18
|
+
>(({ className, ...props }, ref) => (
|
|
19
|
+
<AlertDialogPrimitive.Overlay
|
|
20
|
+
ref={ref}
|
|
21
|
+
className={cn(
|
|
22
|
+
"fixed inset-0 z-50 bg-overlay/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
|
23
|
+
className
|
|
24
|
+
)}
|
|
25
|
+
{...props}
|
|
26
|
+
/>
|
|
27
|
+
))
|
|
28
|
+
AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName
|
|
29
|
+
|
|
30
|
+
const AlertDialogContent = React.forwardRef<
|
|
31
|
+
React.ElementRef<typeof AlertDialogPrimitive.Content>,
|
|
32
|
+
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content> & {
|
|
33
|
+
portalContainer?: HTMLElement | null
|
|
34
|
+
overlayClassName?: string
|
|
35
|
+
}
|
|
36
|
+
>(({ className, portalContainer, overlayClassName, ...props }, ref) => (
|
|
37
|
+
<AlertDialogPortal container={portalContainer ?? undefined}>
|
|
38
|
+
<AlertDialogOverlay className={cn(portalContainer && "absolute", overlayClassName)} />
|
|
39
|
+
<AlertDialogPrimitive.Content
|
|
40
|
+
ref={ref}
|
|
41
|
+
className={cn(
|
|
42
|
+
"fixed left-[50%] top-[50%] z-50 grid w-[calc(100%-2rem)] max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 sm:rounded-lg",
|
|
43
|
+
portalContainer && "absolute",
|
|
44
|
+
className
|
|
45
|
+
)}
|
|
46
|
+
{...props}
|
|
47
|
+
/>
|
|
48
|
+
</AlertDialogPortal>
|
|
49
|
+
))
|
|
50
|
+
AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName
|
|
51
|
+
|
|
52
|
+
const AlertDialogHeader = ({
|
|
53
|
+
className,
|
|
54
|
+
...props
|
|
55
|
+
}: React.HTMLAttributes<HTMLDivElement>) => (
|
|
56
|
+
<div
|
|
57
|
+
className={cn(
|
|
58
|
+
"flex flex-col space-y-2 text-center sm:text-left",
|
|
59
|
+
className
|
|
60
|
+
)}
|
|
61
|
+
{...props}
|
|
62
|
+
/>
|
|
63
|
+
)
|
|
64
|
+
AlertDialogHeader.displayName = "AlertDialogHeader"
|
|
65
|
+
|
|
66
|
+
const AlertDialogFooter = ({
|
|
67
|
+
className,
|
|
68
|
+
...props
|
|
69
|
+
}: React.HTMLAttributes<HTMLDivElement>) => (
|
|
70
|
+
<div
|
|
71
|
+
className={cn(
|
|
72
|
+
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
|
|
73
|
+
className
|
|
74
|
+
)}
|
|
75
|
+
{...props}
|
|
76
|
+
/>
|
|
77
|
+
)
|
|
78
|
+
AlertDialogFooter.displayName = "AlertDialogFooter"
|
|
79
|
+
|
|
80
|
+
const AlertDialogTitle = React.forwardRef<
|
|
81
|
+
React.ElementRef<typeof AlertDialogPrimitive.Title>,
|
|
82
|
+
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Title>
|
|
83
|
+
>(({ className, ...props }, ref) => (
|
|
84
|
+
<AlertDialogPrimitive.Title
|
|
85
|
+
ref={ref}
|
|
86
|
+
className={cn("text-lg font-semibold", className)}
|
|
87
|
+
{...props}
|
|
88
|
+
/>
|
|
89
|
+
))
|
|
90
|
+
AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName
|
|
91
|
+
|
|
92
|
+
const AlertDialogDescription = React.forwardRef<
|
|
93
|
+
React.ElementRef<typeof AlertDialogPrimitive.Description>,
|
|
94
|
+
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Description>
|
|
95
|
+
>(({ className, ...props }, ref) => (
|
|
96
|
+
<AlertDialogPrimitive.Description
|
|
97
|
+
ref={ref}
|
|
98
|
+
className={cn("text-sm text-muted-foreground", className)}
|
|
99
|
+
{...props}
|
|
100
|
+
/>
|
|
101
|
+
))
|
|
102
|
+
AlertDialogDescription.displayName =
|
|
103
|
+
AlertDialogPrimitive.Description.displayName
|
|
104
|
+
|
|
105
|
+
const AlertDialogAction = React.forwardRef<
|
|
106
|
+
React.ElementRef<typeof AlertDialogPrimitive.Action>,
|
|
107
|
+
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Action>
|
|
108
|
+
>(({ className, ...props }, ref) => (
|
|
109
|
+
<AlertDialogPrimitive.Action
|
|
110
|
+
ref={ref}
|
|
111
|
+
className={cn(buttonVariants(), className)}
|
|
112
|
+
{...props}
|
|
113
|
+
/>
|
|
114
|
+
))
|
|
115
|
+
AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName
|
|
116
|
+
|
|
117
|
+
const AlertDialogCancel = React.forwardRef<
|
|
118
|
+
React.ElementRef<typeof AlertDialogPrimitive.Cancel>,
|
|
119
|
+
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Cancel>
|
|
120
|
+
>(({ className, ...props }, ref) => (
|
|
121
|
+
<AlertDialogPrimitive.Cancel
|
|
122
|
+
ref={ref}
|
|
123
|
+
className={cn(
|
|
124
|
+
buttonVariants({ variant: "outline" }),
|
|
125
|
+
"mt-2 sm:mt-0",
|
|
126
|
+
className
|
|
127
|
+
)}
|
|
128
|
+
{...props}
|
|
129
|
+
/>
|
|
130
|
+
))
|
|
131
|
+
AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName
|
|
132
|
+
|
|
133
|
+
export {
|
|
134
|
+
AlertDialog,
|
|
135
|
+
AlertDialogPortal,
|
|
136
|
+
AlertDialogOverlay,
|
|
137
|
+
AlertDialogTrigger,
|
|
138
|
+
AlertDialogContent,
|
|
139
|
+
AlertDialogHeader,
|
|
140
|
+
AlertDialogFooter,
|
|
141
|
+
AlertDialogTitle,
|
|
142
|
+
AlertDialogDescription,
|
|
143
|
+
AlertDialogAction,
|
|
144
|
+
AlertDialogCancel,
|
|
145
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { IconX as X } from "@tabler/icons-react";
|
|
5
|
+
import { cn } from "../../lib/utils";
|
|
6
|
+
import { ChatMessage, type ChatMessageActionKey, type ChatMessageProps } from "../display/ChatMessage";
|
|
7
|
+
import { EmptyState } from "../display/EmptyState";
|
|
8
|
+
import { ChatInput, type ChatInputLabels, type ChatInputProps } from "../inputs/ChatInput";
|
|
9
|
+
import { TooltipButton } from "../inputs/TooltipButton";
|
|
10
|
+
import { chatPanelDefaultVariantKey } from "./generated/default-variant-keys";
|
|
11
|
+
import type { ChatPanelVariantKey } from "./generated/variant-keys";
|
|
12
|
+
|
|
13
|
+
const chatPanelVariantClassNames: Record<ChatPanelVariantKey, string> = {
|
|
14
|
+
compact: "h-[420px]",
|
|
15
|
+
default: "h-[560px]",
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export interface ChatPanelMessage extends Omit<ChatMessageProps, "className"> {
|
|
19
|
+
id: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface ChatPanelLabels {
|
|
23
|
+
close?: string;
|
|
24
|
+
empty?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface ChatPanelProps extends Omit<React.HTMLAttributes<HTMLElement>, "title"> {
|
|
28
|
+
variant?: ChatPanelVariantKey;
|
|
29
|
+
title?: React.ReactNode;
|
|
30
|
+
description?: React.ReactNode;
|
|
31
|
+
messages: ChatPanelMessage[];
|
|
32
|
+
onSend: ChatInputProps["onSend"];
|
|
33
|
+
onStop?: ChatInputProps["onStop"];
|
|
34
|
+
isProcessing?: boolean;
|
|
35
|
+
placeholder?: string;
|
|
36
|
+
enableAttachments?: boolean;
|
|
37
|
+
emptyState?: React.ReactNode;
|
|
38
|
+
welcome?: React.ReactNode;
|
|
39
|
+
onMessageAction?: (
|
|
40
|
+
message: ChatPanelMessage,
|
|
41
|
+
actionKey: ChatMessageActionKey,
|
|
42
|
+
event: React.MouseEvent<HTMLButtonElement>
|
|
43
|
+
) => void;
|
|
44
|
+
onClose?: () => void;
|
|
45
|
+
labels?: ChatPanelLabels;
|
|
46
|
+
inputLabels?: ChatInputLabels;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function ChatPanel({
|
|
50
|
+
variant = chatPanelDefaultVariantKey,
|
|
51
|
+
title = "Assistant",
|
|
52
|
+
description,
|
|
53
|
+
messages,
|
|
54
|
+
onSend,
|
|
55
|
+
onStop,
|
|
56
|
+
isProcessing = false,
|
|
57
|
+
placeholder,
|
|
58
|
+
enableAttachments = true,
|
|
59
|
+
emptyState,
|
|
60
|
+
welcome,
|
|
61
|
+
onMessageAction,
|
|
62
|
+
onClose,
|
|
63
|
+
labels,
|
|
64
|
+
inputLabels,
|
|
65
|
+
className,
|
|
66
|
+
...props
|
|
67
|
+
}: ChatPanelProps) {
|
|
68
|
+
const messageScrollRef = React.useRef<HTMLDivElement | null>(null);
|
|
69
|
+
const resolvedLabels = {
|
|
70
|
+
close: labels?.close ?? "閉じる",
|
|
71
|
+
empty: labels?.empty ?? "まだメッセージはありません。",
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
React.useEffect(() => {
|
|
75
|
+
if (messages.length === 0 && !isProcessing) return;
|
|
76
|
+
|
|
77
|
+
const frame = window.requestAnimationFrame(() => {
|
|
78
|
+
const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
79
|
+
const scrollElement = messageScrollRef.current;
|
|
80
|
+
|
|
81
|
+
if (!scrollElement) return;
|
|
82
|
+
|
|
83
|
+
scrollElement.scrollTo({
|
|
84
|
+
top: scrollElement.scrollHeight,
|
|
85
|
+
behavior: reduceMotion ? "auto" : "smooth",
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
return () => window.cancelAnimationFrame(frame);
|
|
90
|
+
}, [isProcessing, messages]);
|
|
91
|
+
|
|
92
|
+
return (
|
|
93
|
+
<section
|
|
94
|
+
className={cn(
|
|
95
|
+
"flex w-full max-w-2xl flex-col overflow-hidden rounded-lg border bg-background p-0 shadow-xl",
|
|
96
|
+
chatPanelVariantClassNames[variant],
|
|
97
|
+
className
|
|
98
|
+
)}
|
|
99
|
+
{...props}
|
|
100
|
+
>
|
|
101
|
+
<header className="flex items-start gap-3 border-b bg-muted/50 px-4 py-3">
|
|
102
|
+
<div className="min-w-0 flex-1">
|
|
103
|
+
<h3 className="truncate text-sm font-semibold">{title}</h3>
|
|
104
|
+
{description ? (
|
|
105
|
+
<p className="mt-0.5 text-xs leading-relaxed text-muted-foreground">
|
|
106
|
+
{description}
|
|
107
|
+
</p>
|
|
108
|
+
) : null}
|
|
109
|
+
</div>
|
|
110
|
+
{onClose ? (
|
|
111
|
+
<TooltipButton
|
|
112
|
+
type="button"
|
|
113
|
+
tooltip={resolvedLabels.close}
|
|
114
|
+
aria-label={resolvedLabels.close}
|
|
115
|
+
variant="ghost"
|
|
116
|
+
size="icon"
|
|
117
|
+
className="-mr-2 -mt-1 h-8 w-8 shrink-0"
|
|
118
|
+
onClick={onClose}
|
|
119
|
+
>
|
|
120
|
+
<X className="h-4 w-4" />
|
|
121
|
+
</TooltipButton>
|
|
122
|
+
) : null}
|
|
123
|
+
</header>
|
|
124
|
+
<div
|
|
125
|
+
ref={messageScrollRef}
|
|
126
|
+
className={cn(
|
|
127
|
+
"flex-1 bg-muted/30 p-4",
|
|
128
|
+
messages.length > 0 || isProcessing ? "overflow-y-auto" : "overflow-hidden"
|
|
129
|
+
)}
|
|
130
|
+
>
|
|
131
|
+
{messages.length > 0 ? (
|
|
132
|
+
<div className="space-y-3">
|
|
133
|
+
{messages.map((message) => {
|
|
134
|
+
const { onAction, ...messageProps } = message;
|
|
135
|
+
|
|
136
|
+
return (
|
|
137
|
+
<ChatMessage
|
|
138
|
+
key={message.id}
|
|
139
|
+
{...messageProps}
|
|
140
|
+
onAction={(actionKey, event) => {
|
|
141
|
+
onAction?.(actionKey, event);
|
|
142
|
+
if (!event.defaultPrevented) {
|
|
143
|
+
onMessageAction?.(message, actionKey, event);
|
|
144
|
+
}
|
|
145
|
+
}}
|
|
146
|
+
/>
|
|
147
|
+
);
|
|
148
|
+
})}
|
|
149
|
+
{isProcessing ? (
|
|
150
|
+
<ChatMessage role="assistant" content="" isTyping />
|
|
151
|
+
) : null}
|
|
152
|
+
</div>
|
|
153
|
+
) : welcome ? (
|
|
154
|
+
<div className="flex min-h-full items-end justify-center">
|
|
155
|
+
{welcome}
|
|
156
|
+
</div>
|
|
157
|
+
) : emptyState ? (
|
|
158
|
+
<div className="flex min-h-full items-center justify-center">
|
|
159
|
+
{emptyState}
|
|
160
|
+
</div>
|
|
161
|
+
) : (
|
|
162
|
+
<EmptyState
|
|
163
|
+
className="min-h-full border-dashed bg-background/70"
|
|
164
|
+
title={resolvedLabels.empty}
|
|
165
|
+
/>
|
|
166
|
+
)}
|
|
167
|
+
</div>
|
|
168
|
+
<div className="bg-muted/30 p-3 sm:p-4">
|
|
169
|
+
<ChatInput
|
|
170
|
+
onSend={onSend}
|
|
171
|
+
onStop={onStop}
|
|
172
|
+
isProcessing={isProcessing}
|
|
173
|
+
placeholder={placeholder}
|
|
174
|
+
enableAttachments={enableAttachments}
|
|
175
|
+
labels={inputLabels}
|
|
176
|
+
/>
|
|
177
|
+
</div>
|
|
178
|
+
</section>
|
|
179
|
+
);
|
|
180
|
+
}
|
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
import * as React from "react"
|
|
4
4
|
import * as ContextMenuPrimitive from "@radix-ui/react-context-menu"
|
|
5
|
-
import { Check, ChevronRight, Circle } from "
|
|
5
|
+
import { IconCheck as Check, IconChevronRight as ChevronRight, IconCircle as Circle } from "@tabler/icons-react";
|
|
6
6
|
|
|
7
7
|
import { cn } from "../../lib/utils"
|
|
8
|
+
import { Tooltip, TooltipContent, TooltipTrigger } from "./Tooltip"
|
|
8
9
|
|
|
9
10
|
const ContextMenu = ContextMenuPrimitive.Root
|
|
10
11
|
|
|
@@ -27,7 +28,7 @@ const ContextMenuSubTrigger = React.forwardRef<
|
|
|
27
28
|
<ContextMenuPrimitive.SubTrigger
|
|
28
29
|
ref={ref}
|
|
29
30
|
className={cn(
|
|
30
|
-
"flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground",
|
|
31
|
+
"flex w-full cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-left text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground",
|
|
31
32
|
inset && "pl-8",
|
|
32
33
|
className
|
|
33
34
|
)}
|
|
@@ -41,28 +42,34 @@ ContextMenuSubTrigger.displayName = ContextMenuPrimitive.SubTrigger.displayName
|
|
|
41
42
|
|
|
42
43
|
const ContextMenuSubContent = React.forwardRef<
|
|
43
44
|
React.ElementRef<typeof ContextMenuPrimitive.SubContent>,
|
|
44
|
-
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.SubContent>
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
45
|
+
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.SubContent> & {
|
|
46
|
+
portalContainer?: HTMLElement | null
|
|
47
|
+
}
|
|
48
|
+
>(({ className, portalContainer, ...props }, ref) => (
|
|
49
|
+
<ContextMenuPrimitive.Portal container={portalContainer ?? undefined}>
|
|
50
|
+
<ContextMenuPrimitive.SubContent
|
|
51
|
+
ref={ref}
|
|
52
|
+
className={cn(
|
|
53
|
+
"z-50 flex min-w-[8rem] flex-col items-stretch gap-0.5 overflow-hidden rounded-md border bg-popover p-1 text-left text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
|
54
|
+
className
|
|
55
|
+
)}
|
|
56
|
+
{...props}
|
|
57
|
+
/>
|
|
58
|
+
</ContextMenuPrimitive.Portal>
|
|
54
59
|
))
|
|
55
60
|
ContextMenuSubContent.displayName = ContextMenuPrimitive.SubContent.displayName
|
|
56
61
|
|
|
57
62
|
const ContextMenuContent = React.forwardRef<
|
|
58
63
|
React.ElementRef<typeof ContextMenuPrimitive.Content>,
|
|
59
|
-
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Content>
|
|
60
|
-
|
|
61
|
-
|
|
64
|
+
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Content> & {
|
|
65
|
+
portalContainer?: HTMLElement | null
|
|
66
|
+
}
|
|
67
|
+
>(({ className, portalContainer, ...props }, ref) => (
|
|
68
|
+
<ContextMenuPrimitive.Portal container={portalContainer ?? undefined}>
|
|
62
69
|
<ContextMenuPrimitive.Content
|
|
63
70
|
ref={ref}
|
|
64
71
|
className={cn(
|
|
65
|
-
"z-50 flex
|
|
72
|
+
"z-50 flex w-[192px] min-w-[8rem] flex-col items-stretch gap-0.5 overflow-hidden rounded-md border bg-popover p-1 text-left text-popover-foreground shadow-md animate-in fade-in-80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
|
66
73
|
className
|
|
67
74
|
)}
|
|
68
75
|
{...props}
|
|
@@ -75,18 +82,47 @@ const ContextMenuItem = React.forwardRef<
|
|
|
75
82
|
React.ElementRef<typeof ContextMenuPrimitive.Item>,
|
|
76
83
|
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Item> & {
|
|
77
84
|
inset?: boolean
|
|
85
|
+
disabledReason?: React.ReactNode
|
|
86
|
+
disabledReasonLabel?: string
|
|
78
87
|
}
|
|
79
|
-
>(({ className, inset, ...props }, ref) =>
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
)
|
|
88
|
+
>(({ className, inset, disabledReason, disabledReasonLabel, disabled, onSelect, ...props }, ref) => {
|
|
89
|
+
const item = (
|
|
90
|
+
<ContextMenuPrimitive.Item
|
|
91
|
+
ref={ref}
|
|
92
|
+
disabled={disabled}
|
|
93
|
+
onSelect={(event) => {
|
|
94
|
+
if (disabled) {
|
|
95
|
+
event.preventDefault()
|
|
96
|
+
return
|
|
97
|
+
}
|
|
98
|
+
onSelect?.(event)
|
|
99
|
+
}}
|
|
100
|
+
className={cn(
|
|
101
|
+
"relative flex w-full cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-left text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50",
|
|
102
|
+
inset && "pl-8",
|
|
103
|
+
className
|
|
104
|
+
)}
|
|
105
|
+
{...props}
|
|
106
|
+
/>
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
if (!disabled || !disabledReason) return item
|
|
110
|
+
|
|
111
|
+
return (
|
|
112
|
+
<Tooltip>
|
|
113
|
+
<TooltipTrigger asChild>
|
|
114
|
+
<span
|
|
115
|
+
className="block w-full"
|
|
116
|
+
tabIndex={0}
|
|
117
|
+
aria-label={disabledReasonLabel ?? (typeof disabledReason === "string" ? disabledReason : undefined)}
|
|
118
|
+
>
|
|
119
|
+
{item}
|
|
120
|
+
</span>
|
|
121
|
+
</TooltipTrigger>
|
|
122
|
+
<TooltipContent className="max-w-64 text-left">{disabledReason}</TooltipContent>
|
|
123
|
+
</Tooltip>
|
|
124
|
+
)
|
|
125
|
+
})
|
|
90
126
|
ContextMenuItem.displayName = ContextMenuPrimitive.Item.displayName
|
|
91
127
|
|
|
92
128
|
const ContextMenuCheckboxItem = React.forwardRef<
|
|
@@ -96,7 +132,7 @@ const ContextMenuCheckboxItem = React.forwardRef<
|
|
|
96
132
|
<ContextMenuPrimitive.CheckboxItem
|
|
97
133
|
ref={ref}
|
|
98
134
|
className={cn(
|
|
99
|
-
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
135
|
+
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-left text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
100
136
|
className
|
|
101
137
|
)}
|
|
102
138
|
checked={checked}
|
|
@@ -120,7 +156,7 @@ const ContextMenuRadioItem = React.forwardRef<
|
|
|
120
156
|
<ContextMenuPrimitive.RadioItem
|
|
121
157
|
ref={ref}
|
|
122
158
|
className={cn(
|
|
123
|
-
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
159
|
+
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-left text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
124
160
|
className
|
|
125
161
|
)}
|
|
126
162
|
{...props}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import * as React from "react"
|
|
4
4
|
import * as DialogPrimitive from "@radix-ui/react-dialog"
|
|
5
|
-
import { X } from "
|
|
5
|
+
import { IconX as X } from "@tabler/icons-react";
|
|
6
6
|
|
|
7
7
|
import { cn } from "../../lib/utils"
|
|
8
8
|
|
|
@@ -31,23 +31,31 @@ DialogOverlay.displayName = DialogPrimitive.Overlay.displayName
|
|
|
31
31
|
|
|
32
32
|
const DialogContent = React.forwardRef<
|
|
33
33
|
React.ElementRef<typeof DialogPrimitive.Content>,
|
|
34
|
-
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
34
|
+
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> & {
|
|
35
|
+
portalContainer?: HTMLElement | null
|
|
36
|
+
overlayClassName?: string
|
|
37
|
+
showCloseButton?: boolean
|
|
38
|
+
closeLabel?: string
|
|
39
|
+
}
|
|
40
|
+
>(({ className, children, portalContainer, overlayClassName, showCloseButton = true, closeLabel = "Close", ...props }, ref) => (
|
|
41
|
+
<DialogPortal container={portalContainer ?? undefined}>
|
|
42
|
+
<DialogOverlay className={cn(portalContainer && "absolute", overlayClassName)} />
|
|
38
43
|
<DialogPrimitive.Content
|
|
39
44
|
ref={ref}
|
|
40
45
|
className={cn(
|
|
41
|
-
"fixed left-
|
|
46
|
+
"fixed left-1/2 top-1/2 z-50 grid w-[512px] w-full max-w-lg -translate-x-1/2 -translate-y-1/2 gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 sm:rounded-lg",
|
|
47
|
+
portalContainer && "absolute w-[calc(100%-2rem)] max-w-[calc(100%-2rem)]",
|
|
42
48
|
className
|
|
43
49
|
)}
|
|
44
50
|
{...props}
|
|
45
51
|
>
|
|
46
52
|
{children}
|
|
47
|
-
|
|
48
|
-
<
|
|
49
|
-
|
|
50
|
-
|
|
53
|
+
{showCloseButton ? (
|
|
54
|
+
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
|
|
55
|
+
<X className="h-4 w-4" />
|
|
56
|
+
<span className="sr-only">{closeLabel}</span>
|
|
57
|
+
</DialogPrimitive.Close>
|
|
58
|
+
) : null}
|
|
51
59
|
</DialogPrimitive.Content>
|
|
52
60
|
</DialogPortal>
|
|
53
61
|
))
|
|
@@ -59,7 +67,7 @@ const DialogHeader = ({
|
|
|
59
67
|
}: React.HTMLAttributes<HTMLDivElement>) => (
|
|
60
68
|
<div
|
|
61
69
|
className={cn(
|
|
62
|
-
"flex flex-col space-y-1.5
|
|
70
|
+
"flex flex-col space-y-1.5 pr-4 text-left",
|
|
63
71
|
className
|
|
64
72
|
)}
|
|
65
73
|
{...props}
|
|
@@ -73,7 +81,7 @@ const DialogFooter = ({
|
|
|
73
81
|
}: React.HTMLAttributes<HTMLDivElement>) => (
|
|
74
82
|
<div
|
|
75
83
|
className={cn(
|
|
76
|
-
"flex flex-
|
|
84
|
+
"flex flex-row flex-wrap justify-end gap-2 sm:space-x-2",
|
|
77
85
|
className
|
|
78
86
|
)}
|
|
79
87
|
{...props}
|
|
@@ -88,7 +96,7 @@ const DialogTitle = React.forwardRef<
|
|
|
88
96
|
<DialogPrimitive.Title
|
|
89
97
|
ref={ref}
|
|
90
98
|
className={cn(
|
|
91
|
-
"text-lg font-semibold leading-
|
|
99
|
+
"text-lg font-semibold leading-tight tracking-tight",
|
|
92
100
|
className
|
|
93
101
|
)}
|
|
94
102
|
{...props}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { Drawer as DrawerPrimitive } from "vaul"
|
|
5
|
+
|
|
6
|
+
import { cn } from "../../lib/utils"
|
|
7
|
+
|
|
8
|
+
const Drawer = ({
|
|
9
|
+
shouldScaleBackground = true,
|
|
10
|
+
...props
|
|
11
|
+
}: React.ComponentProps<typeof DrawerPrimitive.Root>) => (
|
|
12
|
+
<DrawerPrimitive.Root
|
|
13
|
+
shouldScaleBackground={shouldScaleBackground}
|
|
14
|
+
{...props}
|
|
15
|
+
/>
|
|
16
|
+
)
|
|
17
|
+
Drawer.displayName = "Drawer"
|
|
18
|
+
|
|
19
|
+
const DrawerTrigger = DrawerPrimitive.Trigger
|
|
20
|
+
const DrawerPortal = DrawerPrimitive.Portal
|
|
21
|
+
const DrawerClose = DrawerPrimitive.Close
|
|
22
|
+
|
|
23
|
+
const DrawerOverlay = React.forwardRef<
|
|
24
|
+
React.ElementRef<typeof DrawerPrimitive.Overlay>,
|
|
25
|
+
React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Overlay>
|
|
26
|
+
>(({ className, ...props }, ref) => (
|
|
27
|
+
<DrawerPrimitive.Overlay
|
|
28
|
+
ref={ref}
|
|
29
|
+
className={cn("fixed inset-0 z-50 bg-overlay/80", className)}
|
|
30
|
+
{...props}
|
|
31
|
+
/>
|
|
32
|
+
))
|
|
33
|
+
DrawerOverlay.displayName = DrawerPrimitive.Overlay.displayName
|
|
34
|
+
|
|
35
|
+
type DrawerSide = "bottom" | "right" | "left" | "top"
|
|
36
|
+
|
|
37
|
+
const drawerContentSideClasses: Record<DrawerSide, string> = {
|
|
38
|
+
bottom: "inset-x-0 bottom-0 mt-24 h-auto rounded-t-[10px] border",
|
|
39
|
+
right: "inset-y-0 right-0 h-full w-80 max-w-[calc(100%_-_2rem)] rounded-l-[10px] border-l",
|
|
40
|
+
left: "inset-y-0 left-0 h-full w-80 max-w-[calc(100%_-_2rem)] rounded-r-[10px] border-r",
|
|
41
|
+
top: "inset-x-0 top-0 mb-24 h-auto rounded-b-[10px] border",
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const DrawerContent = React.forwardRef<
|
|
45
|
+
React.ElementRef<typeof DrawerPrimitive.Content>,
|
|
46
|
+
React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Content> & {
|
|
47
|
+
side?: DrawerSide
|
|
48
|
+
portalContainer?: HTMLElement | null
|
|
49
|
+
overlayClassName?: string
|
|
50
|
+
}
|
|
51
|
+
>(({ className, children, side = "bottom", portalContainer, overlayClassName, ...props }, ref) => (
|
|
52
|
+
<DrawerPortal container={portalContainer ?? undefined}>
|
|
53
|
+
<DrawerOverlay className={cn(portalContainer && "absolute", overlayClassName)} />
|
|
54
|
+
<DrawerPrimitive.Content
|
|
55
|
+
ref={ref}
|
|
56
|
+
className={cn(
|
|
57
|
+
"fixed z-50 flex flex-col bg-background",
|
|
58
|
+
portalContainer && "absolute",
|
|
59
|
+
drawerContentSideClasses[side],
|
|
60
|
+
className
|
|
61
|
+
)}
|
|
62
|
+
{...props}
|
|
63
|
+
>
|
|
64
|
+
{side === "bottom" ? <div className="mx-auto mt-4 h-2 w-[100px] rounded-full bg-muted" /> : null}
|
|
65
|
+
{children}
|
|
66
|
+
</DrawerPrimitive.Content>
|
|
67
|
+
</DrawerPortal>
|
|
68
|
+
))
|
|
69
|
+
DrawerContent.displayName = "DrawerContent"
|
|
70
|
+
|
|
71
|
+
const DrawerHeader = ({
|
|
72
|
+
className,
|
|
73
|
+
...props
|
|
74
|
+
}: React.HTMLAttributes<HTMLDivElement>) => (
|
|
75
|
+
<div
|
|
76
|
+
className={cn("grid gap-1.5 p-4 text-left", className)}
|
|
77
|
+
{...props}
|
|
78
|
+
/>
|
|
79
|
+
)
|
|
80
|
+
DrawerHeader.displayName = "DrawerHeader"
|
|
81
|
+
|
|
82
|
+
const DrawerFooter = ({
|
|
83
|
+
className,
|
|
84
|
+
...props
|
|
85
|
+
}: React.HTMLAttributes<HTMLDivElement>) => (
|
|
86
|
+
<div
|
|
87
|
+
className={cn("mt-auto flex flex-row flex-wrap justify-end gap-2 p-4", className)}
|
|
88
|
+
{...props}
|
|
89
|
+
/>
|
|
90
|
+
)
|
|
91
|
+
DrawerFooter.displayName = "DrawerFooter"
|
|
92
|
+
|
|
93
|
+
const DrawerTitle = React.forwardRef<
|
|
94
|
+
React.ElementRef<typeof DrawerPrimitive.Title>,
|
|
95
|
+
React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Title>
|
|
96
|
+
>(({ className, ...props }, ref) => (
|
|
97
|
+
<DrawerPrimitive.Title
|
|
98
|
+
ref={ref}
|
|
99
|
+
className={cn(
|
|
100
|
+
"text-lg font-semibold leading-none tracking-tight",
|
|
101
|
+
className
|
|
102
|
+
)}
|
|
103
|
+
{...props}
|
|
104
|
+
/>
|
|
105
|
+
))
|
|
106
|
+
DrawerTitle.displayName = DrawerPrimitive.Title.displayName
|
|
107
|
+
|
|
108
|
+
const DrawerDescription = React.forwardRef<
|
|
109
|
+
React.ElementRef<typeof DrawerPrimitive.Description>,
|
|
110
|
+
React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Description>
|
|
111
|
+
>(({ className, ...props }, ref) => (
|
|
112
|
+
<DrawerPrimitive.Description
|
|
113
|
+
ref={ref}
|
|
114
|
+
className={cn("text-sm text-muted-foreground", className)}
|
|
115
|
+
{...props}
|
|
116
|
+
/>
|
|
117
|
+
))
|
|
118
|
+
DrawerDescription.displayName = DrawerPrimitive.Description.displayName
|
|
119
|
+
|
|
120
|
+
export {
|
|
121
|
+
Drawer,
|
|
122
|
+
DrawerPortal,
|
|
123
|
+
DrawerOverlay,
|
|
124
|
+
DrawerTrigger,
|
|
125
|
+
DrawerClose,
|
|
126
|
+
DrawerContent,
|
|
127
|
+
DrawerHeader,
|
|
128
|
+
DrawerFooter,
|
|
129
|
+
DrawerTitle,
|
|
130
|
+
DrawerDescription,
|
|
131
|
+
}
|