The Coline design system: accessible React components for Coline Apps, themed live by the host workspace, and for Coline Sites in light and dark. Tailwind v4 stylesheet included. MIT.
| 5 | import { IconX } from "@tabler/icons-react"; |
| 6 | import { cn } from "../lib/utils"; |
| 7 | import { Button } from "./button"; |
| 8 | import { modalSurfaceClasses } from "./modal-surface"; |
| 9 |
| 10 | /** |
| 11 | * A panel that slides in from an edge: record details, filters, settings. |
| 12 | * Built on the dialog primitive, so focus is trapped and Escape closes it. |
| 13 | */ |
| 14 | export function Sheet(props: React.ComponentProps<typeof SheetPrimitive.Root>) { |
| 15 | return <SheetPrimitive.Root data-slot="sheet" {...props} />; |
| 16 | } |
| 17 |
| 18 | export function SheetTrigger(props: React.ComponentProps<typeof SheetPrimitive.Trigger>) { |
| 19 | return <SheetPrimitive.Trigger data-slot="sheet-trigger" {...props} />; |
| 20 | } |
| 21 |
| 22 | export function SheetClose(props: React.ComponentProps<typeof SheetPrimitive.Close>) { |
| 23 | return <SheetPrimitive.Close data-slot="sheet-close" {...props} />; |
| 24 | } |
| 25 |
| 26 | export function SheetContent({ |
| 27 | className, |
| 28 | children, |
| 29 | side = "right", |
| 30 | ...props |
| 31 | }: React.ComponentProps<typeof SheetPrimitive.Content> & { side?: "right" | "left" | "bottom" }) { |
| 32 | return ( |
| 33 | <SheetPrimitive.Portal> |
| 34 | <SheetPrimitive.Overlay className="coline-dialog-overlay fixed inset-0 z-50 bg-black/25 supports-backdrop-filter:backdrop-blur-[2px]" /> |
| 35 | <SheetPrimitive.Content |
| 36 | data-slot="sheet-content" |
| 37 | data-side={side} |
| 38 | className={cn( |
| 39 | "coline-sheet fixed z-50 flex flex-col gap-4 p-5 text-sm outline-none", |
| 40 | modalSurfaceClasses, |
| 41 | side === "right" && "inset-y-2 right-2 w-[min(28rem,calc(100vw-1rem))]", |
| 42 | side === "left" && "inset-y-2 left-2 w-[min(28rem,calc(100vw-1rem))]", |
| 43 | side === "bottom" && "inset-x-2 bottom-2 max-h-[85dvh]", |
| 44 | className, |
| 45 | )} |
| 46 | {...props} |
| 47 | > |
| 48 | {children} |
| 49 | <SheetPrimitive.Close asChild> |
| 50 | <Button variant="ghost" size="icon-sm" className="absolute top-3.5 right-3.5"> |
| 51 | <IconX /> |
| 52 | <span className="sr-only">Close</span> |
| 53 | </Button> |
| 54 | </SheetPrimitive.Close> |
| 55 | </SheetPrimitive.Content> |
| 56 | </SheetPrimitive.Portal> |
| 57 | ); |
| 58 | } |
| 59 |
| 60 | export function SheetHeader({ className, ...props }: React.ComponentProps<"div">) { |
| 61 | return <div data-slot="sheet-header" className={cn("flex flex-col gap-1 pr-8", className)} {...props} />; |
| 62 | } |
| 63 |
| 64 | export function SheetTitle({ className, ...props }: React.ComponentProps<typeof SheetPrimitive.Title>) { |
| 65 | return ( |
| 66 | <SheetPrimitive.Title |
| 67 | data-slot="sheet-title" |
| 68 | className={cn("text-base font-semibold tracking-tight text-foreground", className)} |
| 69 | {...props} |
| 70 | /> |
| 71 | ); |
| 72 | } |
| 73 |
| 74 | export function SheetDescription({ |
| 75 | className, |
| 76 | ...props |
| 77 | }: React.ComponentProps<typeof SheetPrimitive.Description>) { |
| 78 | return ( |
| 79 | <SheetPrimitive.Description |
| 80 | data-slot="sheet-description" |
| 81 | className={cn("text-sm text-muted-foreground", className)} |
| 82 | {...props} |
| 83 | /> |
| 84 | ); |
| 85 | } |
| 86 |
| 87 | export function SheetBody({ className, ...props }: React.ComponentProps<"div">) { |
| 88 | return <div data-slot="sheet-body" className={cn("-mx-5 min-h-0 flex-1 overflow-y-auto px-5", className)} {...props} />; |
| 89 | } |
| 90 |
| 91 | export function SheetFooter({ className, ...props }: React.ComponentProps<"div">) { |
| 92 | return ( |
| 93 | <div |
| 94 | data-slot="sheet-footer" |
| 95 | className={cn("-mx-5 -mb-5 mt-auto flex items-center justify-end gap-2 border-t border-border/60 px-5 py-3", className)} |
| 96 | {...props} |
| 97 | /> |
| 98 | ); |
| 99 | } |
| 100 |