@redbamboo/ui 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.
Files changed (73) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +78 -0
  3. package/dist/components/app-header.d.ts +14 -0
  4. package/dist/components/app-header.d.ts.map +1 -0
  5. package/dist/components/badge.d.ts +8 -0
  6. package/dist/components/badge.d.ts.map +1 -0
  7. package/dist/components/button.d.ts +9 -0
  8. package/dist/components/button.d.ts.map +1 -0
  9. package/dist/components/card.d.ts +12 -0
  10. package/dist/components/card.d.ts.map +1 -0
  11. package/dist/components/collapsible.d.ts +6 -0
  12. package/dist/components/collapsible.d.ts.map +1 -0
  13. package/dist/components/dialog.d.ts +18 -0
  14. package/dist/components/dialog.d.ts.map +1 -0
  15. package/dist/components/dropdown-menu.d.ts +25 -0
  16. package/dist/components/dropdown-menu.d.ts.map +1 -0
  17. package/dist/components/input.d.ts +4 -0
  18. package/dist/components/input.d.ts.map +1 -0
  19. package/dist/components/json-highlight.d.ts +4 -0
  20. package/dist/components/json-highlight.d.ts.map +1 -0
  21. package/dist/components/label.d.ts +4 -0
  22. package/dist/components/label.d.ts.map +1 -0
  23. package/dist/components/modal.d.ts +33 -0
  24. package/dist/components/modal.d.ts.map +1 -0
  25. package/dist/components/popover.d.ts +10 -0
  26. package/dist/components/popover.d.ts.map +1 -0
  27. package/dist/components/scroll-area.d.ts +5 -0
  28. package/dist/components/scroll-area.d.ts.map +1 -0
  29. package/dist/components/select.d.ts +13 -0
  30. package/dist/components/select.d.ts.map +1 -0
  31. package/dist/components/separator.d.ts +4 -0
  32. package/dist/components/separator.d.ts.map +1 -0
  33. package/dist/components/slider.d.ts +4 -0
  34. package/dist/components/slider.d.ts.map +1 -0
  35. package/dist/components/switch.d.ts +6 -0
  36. package/dist/components/switch.d.ts.map +1 -0
  37. package/dist/components/table.d.ts +11 -0
  38. package/dist/components/table.d.ts.map +1 -0
  39. package/dist/components/tabs.d.ts +11 -0
  40. package/dist/components/tabs.d.ts.map +1 -0
  41. package/dist/components/tooltip.d.ts +7 -0
  42. package/dist/components/tooltip.d.ts.map +1 -0
  43. package/dist/index.css +2 -0
  44. package/dist/index.d.ts +24 -0
  45. package/dist/index.d.ts.map +1 -0
  46. package/dist/index.js +2648 -0
  47. package/dist/utils.d.ts +3 -0
  48. package/dist/utils.d.ts.map +1 -0
  49. package/package.json +49 -0
  50. package/src/components/app-header.css +40 -0
  51. package/src/components/app-header.tsx +57 -0
  52. package/src/components/badge.tsx +52 -0
  53. package/src/components/button.tsx +58 -0
  54. package/src/components/card.tsx +103 -0
  55. package/src/components/collapsible.tsx +21 -0
  56. package/src/components/dialog.tsx +155 -0
  57. package/src/components/dropdown-menu.tsx +240 -0
  58. package/src/components/input.tsx +20 -0
  59. package/src/components/json-highlight.tsx +84 -0
  60. package/src/components/label.tsx +21 -0
  61. package/src/components/modal.tsx +114 -0
  62. package/src/components/popover.tsx +90 -0
  63. package/src/components/scroll-area.tsx +52 -0
  64. package/src/components/select.tsx +145 -0
  65. package/src/components/separator.tsx +23 -0
  66. package/src/components/slider.tsx +41 -0
  67. package/src/components/switch.tsx +30 -0
  68. package/src/components/table.tsx +114 -0
  69. package/src/components/tabs.tsx +80 -0
  70. package/src/components/tooltip.tsx +64 -0
  71. package/src/index.ts +141 -0
  72. package/src/tokens.css +397 -0
  73. package/src/utils.ts +6 -0
@@ -0,0 +1,3 @@
1
+ import { ClassValue } from 'clsx';
2
+ export declare function cn(...inputs: ClassValue[]): string;
3
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,UAAU,EAAE,MAAM,MAAM,CAAA;AAG5C,wBAAgB,EAAE,CAAC,GAAG,MAAM,EAAE,UAAU,EAAE,UAEzC"}
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@redbamboo/ui",
3
+ "version": "0.1.0",
4
+ "description": "RedBamboo design system — tokens, Tailwind theme, and components",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "sideEffects": [
8
+ "*.css"
9
+ ],
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "default": "./src/index.ts"
14
+ },
15
+ "./dialog": {
16
+ "default": "./src/components/dialog.tsx"
17
+ },
18
+ "./tokens.css": "./src/tokens.css"
19
+ },
20
+ "files": [
21
+ "dist",
22
+ "src"
23
+ ],
24
+ "peerDependencies": {
25
+ "react": "^19.0.0",
26
+ "react-dom": "^19.0.0",
27
+ "tailwindcss": "^4.0.0",
28
+ "@base-ui/react": "^1.4.0"
29
+ },
30
+ "dependencies": {
31
+ "class-variance-authority": "^0.7.1",
32
+ "clsx": "^2.1.1",
33
+ "tailwind-merge": "^3.5.0"
34
+ },
35
+ "devDependencies": {
36
+ "@vitejs/plugin-react": "^6.0.1",
37
+ "typescript": "~5.9.3",
38
+ "vite": "^8.0.1",
39
+ "vite-plugin-dts": "^4.5.4"
40
+ },
41
+ "scripts": {
42
+ "build": "vite build",
43
+ "dev": "vite build --watch",
44
+ "typecheck": "tsc --noEmit",
45
+ "lint": "echo ok",
46
+ "test": "echo ok",
47
+ "clean": "rimraf dist"
48
+ }
49
+ }
@@ -0,0 +1,40 @@
1
+ /* ── AppHeader brand ────────────────────────────────────────────────── */
2
+
3
+ .app-header-brand__bg {
4
+ background: currentColor;
5
+ opacity: 0.2;
6
+ }
7
+
8
+ .app-header-brand:hover .app-header-brand__bg {
9
+ animation: brand-bg-wobble 0.5s ease-in-out;
10
+ }
11
+
12
+ .app-header-brand:hover .app-header-brand__i {
13
+ animation: brand-icon-bounce 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
14
+ }
15
+
16
+ .app-header-brand:hover .app-header-brand__letter {
17
+ animation: brand-letter-hop 0.3s cubic-bezier(0.34, 1.56, 0.64, 1) both;
18
+ animation-delay: calc(var(--letter-i) * 0.03s);
19
+ }
20
+
21
+ @keyframes brand-bg-wobble {
22
+ 0% { transform: scale(1) rotate(0deg); }
23
+ 25% { transform: scale(1.15) rotate(-3deg); }
24
+ 50% { transform: scale(0.92) rotate(2deg); }
25
+ 75% { transform: scale(1.05) rotate(-1deg); }
26
+ 100% { transform: scale(1) rotate(0deg); }
27
+ }
28
+
29
+ @keyframes brand-icon-bounce {
30
+ 0% { transform: scale(1) rotate(0deg); }
31
+ 30% { transform: scale(1.3) rotate(-10deg); }
32
+ 60% { transform: scale(0.9) rotate(5deg); }
33
+ 100% { transform: scale(1) rotate(0deg); }
34
+ }
35
+
36
+ @keyframes brand-letter-hop {
37
+ 0% { transform: translateY(0); }
38
+ 50% { transform: translateY(-3px); }
39
+ 100% { transform: translateY(0); }
40
+ }
@@ -0,0 +1,57 @@
1
+ import "./app-header.css"
2
+ import { cn } from "../utils"
3
+
4
+ export interface AppHeaderBrandProps {
5
+ icon: string
6
+ nameParts: [string, string]
7
+ accentClass?: string
8
+ }
9
+
10
+ export interface AppHeaderProps {
11
+ brand: AppHeaderBrandProps
12
+ children?: React.ReactNode
13
+ className?: string
14
+ }
15
+
16
+ function AppHeaderBrand({ icon, nameParts, accentClass = "text-primary" }: AppHeaderBrandProps) {
17
+ const allLetters = (nameParts[0] + nameParts[1]).split("")
18
+ const mutedCount = nameParts[0].length
19
+
20
+ return (
21
+ <div className={cn("app-header-brand flex items-center gap-2 cursor-default select-none", accentClass)}>
22
+ <div className="app-header-brand__icon relative w-6 h-6 rounded flex items-center justify-center">
23
+ <div className="app-header-brand__bg absolute inset-0 rounded" />
24
+ <i className={cn(icon, "app-header-brand__i relative z-10 text-xs")} />
25
+ </div>
26
+ <span className="app-header-brand__text text-sm font-semibold">
27
+ {allLetters.map((char, i) => (
28
+ <span
29
+ key={i}
30
+ className={cn(
31
+ "app-header-brand__letter inline-block",
32
+ i < mutedCount ? "text-muted-foreground" : "text-current",
33
+ )}
34
+ style={{ "--letter-i": i } as React.CSSProperties}
35
+ >
36
+ {char}
37
+ </span>
38
+ ))}
39
+ </span>
40
+ </div>
41
+ )
42
+ }
43
+
44
+ function AppHeader({ brand, children, className }: AppHeaderProps) {
45
+ return (
46
+ <header className={cn(
47
+ "shrink-0 flex items-center gap-3 px-4 py-2 border-b border-border/60",
48
+ className,
49
+ )}>
50
+ <AppHeaderBrand {...brand} />
51
+ <span className="flex-1" />
52
+ {children}
53
+ </header>
54
+ )
55
+ }
56
+
57
+ export { AppHeader, AppHeaderBrand }
@@ -0,0 +1,52 @@
1
+ import { mergeProps } from "@base-ui/react/merge-props"
2
+ import { useRender } from "@base-ui/react/use-render"
3
+ import { cva, type VariantProps } from "class-variance-authority"
4
+
5
+ import { cn } from "../utils"
6
+
7
+ const badgeVariants = cva(
8
+ "group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!",
9
+ {
10
+ variants: {
11
+ variant: {
12
+ default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
13
+ secondary:
14
+ "bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80",
15
+ destructive:
16
+ "bg-destructive/10 text-destructive focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:focus-visible:ring-destructive/40 [a]:hover:bg-destructive/20",
17
+ outline:
18
+ "border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground",
19
+ ghost:
20
+ "hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50",
21
+ link: "text-primary underline-offset-4 hover:underline",
22
+ },
23
+ },
24
+ defaultVariants: {
25
+ variant: "default",
26
+ },
27
+ }
28
+ )
29
+
30
+ function Badge({
31
+ className,
32
+ variant = "default",
33
+ render,
34
+ ...props
35
+ }: useRender.ComponentProps<"span"> & VariantProps<typeof badgeVariants>) {
36
+ return useRender({
37
+ defaultTagName: "span",
38
+ props: mergeProps<"span">(
39
+ {
40
+ className: cn(badgeVariants({ variant }), className),
41
+ },
42
+ props
43
+ ),
44
+ render,
45
+ state: {
46
+ slot: "badge",
47
+ variant,
48
+ },
49
+ })
50
+ }
51
+
52
+ export { Badge, badgeVariants }
@@ -0,0 +1,58 @@
1
+ import { Button as ButtonPrimitive } from "@base-ui/react/button"
2
+ import { cva, type VariantProps } from "class-variance-authority"
3
+
4
+ import { cn } from "../utils"
5
+
6
+ const buttonVariants = cva(
7
+ "group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
8
+ {
9
+ variants: {
10
+ variant: {
11
+ default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
12
+ outline:
13
+ "border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
14
+ secondary:
15
+ "bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
16
+ ghost:
17
+ "hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50",
18
+ destructive:
19
+ "bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40",
20
+ link: "text-primary underline-offset-4 hover:underline",
21
+ },
22
+ size: {
23
+ default:
24
+ "h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
25
+ xs: "h-6 gap-1 rounded-[min(var(--radius-md),10px)] px-2 text-xs in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3",
26
+ sm: "h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5",
27
+ lg: "h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
28
+ icon: "size-8",
29
+ "icon-xs":
30
+ "size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3",
31
+ "icon-sm":
32
+ "size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg",
33
+ "icon-lg": "size-9",
34
+ },
35
+ },
36
+ defaultVariants: {
37
+ variant: "default",
38
+ size: "default",
39
+ },
40
+ }
41
+ )
42
+
43
+ function Button({
44
+ className,
45
+ variant = "default",
46
+ size = "default",
47
+ ...props
48
+ }: ButtonPrimitive.Props & VariantProps<typeof buttonVariants>) {
49
+ return (
50
+ <ButtonPrimitive
51
+ data-slot="button"
52
+ className={cn(buttonVariants({ variant, size, className }))}
53
+ {...props}
54
+ />
55
+ )
56
+ }
57
+
58
+ export { Button, buttonVariants }
@@ -0,0 +1,103 @@
1
+ import * as React from "react"
2
+
3
+ import { cn } from "../utils"
4
+
5
+ function Card({
6
+ className,
7
+ size = "default",
8
+ ...props
9
+ }: React.ComponentProps<"div"> & { size?: "default" | "sm" }) {
10
+ return (
11
+ <div
12
+ data-slot="card"
13
+ data-size={size}
14
+ className={cn(
15
+ "group/card flex flex-col gap-4 overflow-hidden rounded-xl bg-card py-4 text-sm text-card-foreground ring-1 ring-foreground/10 has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl",
16
+ className
17
+ )}
18
+ {...props}
19
+ />
20
+ )
21
+ }
22
+
23
+ function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
24
+ return (
25
+ <div
26
+ data-slot="card-header"
27
+ className={cn(
28
+ "group/card-header @container/card-header grid auto-rows-min items-start gap-1 rounded-t-xl px-4 group-data-[size=sm]/card:px-3 has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] [.border-b]:pb-4 group-data-[size=sm]/card:[.border-b]:pb-3",
29
+ className
30
+ )}
31
+ {...props}
32
+ />
33
+ )
34
+ }
35
+
36
+ function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
37
+ return (
38
+ <div
39
+ data-slot="card-title"
40
+ className={cn(
41
+ "font-heading text-base leading-snug font-medium group-data-[size=sm]/card:text-sm",
42
+ className
43
+ )}
44
+ {...props}
45
+ />
46
+ )
47
+ }
48
+
49
+ function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
50
+ return (
51
+ <div
52
+ data-slot="card-description"
53
+ className={cn("text-sm text-muted-foreground", className)}
54
+ {...props}
55
+ />
56
+ )
57
+ }
58
+
59
+ function CardAction({ className, ...props }: React.ComponentProps<"div">) {
60
+ return (
61
+ <div
62
+ data-slot="card-action"
63
+ className={cn(
64
+ "col-start-2 row-span-2 row-start-1 self-start justify-self-end",
65
+ className
66
+ )}
67
+ {...props}
68
+ />
69
+ )
70
+ }
71
+
72
+ function CardContent({ className, ...props }: React.ComponentProps<"div">) {
73
+ return (
74
+ <div
75
+ data-slot="card-content"
76
+ className={cn("px-4 group-data-[size=sm]/card:px-3", className)}
77
+ {...props}
78
+ />
79
+ )
80
+ }
81
+
82
+ function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
83
+ return (
84
+ <div
85
+ data-slot="card-footer"
86
+ className={cn(
87
+ "flex items-center rounded-b-xl border-t bg-muted/50 p-4 group-data-[size=sm]/card:p-3",
88
+ className
89
+ )}
90
+ {...props}
91
+ />
92
+ )
93
+ }
94
+
95
+ export {
96
+ Card,
97
+ CardHeader,
98
+ CardFooter,
99
+ CardTitle,
100
+ CardAction,
101
+ CardDescription,
102
+ CardContent,
103
+ }
@@ -0,0 +1,21 @@
1
+ import { Collapsible as CollapsiblePrimitive } from "@base-ui/react/collapsible"
2
+
3
+ function Collapsible({
4
+ ...props
5
+ }: CollapsiblePrimitive.Root.Props) {
6
+ return <CollapsiblePrimitive.Root data-slot="collapsible" {...props} />
7
+ }
8
+
9
+ function CollapsibleTrigger({
10
+ ...props
11
+ }: CollapsiblePrimitive.Trigger.Props) {
12
+ return <CollapsiblePrimitive.Trigger data-slot="collapsible-trigger" {...props} />
13
+ }
14
+
15
+ function CollapsibleContent({
16
+ ...props
17
+ }: CollapsiblePrimitive.Panel.Props) {
18
+ return <CollapsiblePrimitive.Panel data-slot="collapsible-content" {...props} />
19
+ }
20
+
21
+ export { Collapsible, CollapsibleTrigger, CollapsibleContent }
@@ -0,0 +1,155 @@
1
+ import * as React from "react"
2
+ import { Dialog as DialogPrimitive } from "@base-ui/react/dialog"
3
+ import { cn } from "../utils"
4
+ import { Button } from "./button"
5
+
6
+ function Dialog({ ...props }: DialogPrimitive.Root.Props) {
7
+ return <DialogPrimitive.Root data-slot="dialog" {...props} />
8
+ }
9
+
10
+ function DialogTrigger({ ...props }: DialogPrimitive.Trigger.Props) {
11
+ return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />
12
+ }
13
+
14
+ function DialogPortal({ ...props }: DialogPrimitive.Portal.Props) {
15
+ return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />
16
+ }
17
+
18
+ function DialogClose({ ...props }: DialogPrimitive.Close.Props) {
19
+ return <DialogPrimitive.Close data-slot="dialog-close" {...props} />
20
+ }
21
+
22
+ function DialogOverlay({
23
+ className,
24
+ ...props
25
+ }: DialogPrimitive.Backdrop.Props) {
26
+ return (
27
+ <DialogPrimitive.Backdrop
28
+ data-slot="dialog-overlay"
29
+ className={cn(
30
+ "fixed inset-0 isolate z-50 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0",
31
+ className
32
+ )}
33
+ {...props}
34
+ />
35
+ )
36
+ }
37
+
38
+ function DialogContent({
39
+ className,
40
+ children,
41
+ showCloseButton = true,
42
+ ...props
43
+ }: DialogPrimitive.Popup.Props & {
44
+ showCloseButton?: boolean
45
+ }) {
46
+ return (
47
+ <DialogPortal>
48
+ <DialogOverlay />
49
+ <DialogPrimitive.Popup
50
+ data-slot="dialog-content"
51
+ className={cn(
52
+ "fixed top-1/2 left-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl bg-popover p-5 text-sm text-popover-foreground ring-1 ring-foreground/10 duration-100 outline-none sm:max-w-md data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
53
+ className
54
+ )}
55
+ {...props}
56
+ >
57
+ {children}
58
+ {showCloseButton && (
59
+ <DialogPrimitive.Close
60
+ data-slot="dialog-close"
61
+ render={
62
+ <Button
63
+ variant="ghost"
64
+ className="absolute top-2 right-2"
65
+ size="icon-sm"
66
+ />
67
+ }
68
+ >
69
+ <i className="fa-solid fa-xmark" />
70
+ <span className="sr-only">Close</span>
71
+ </DialogPrimitive.Close>
72
+ )}
73
+ </DialogPrimitive.Popup>
74
+ </DialogPortal>
75
+ )
76
+ }
77
+
78
+ function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
79
+ return (
80
+ <div
81
+ data-slot="dialog-header"
82
+ className={cn("flex flex-col gap-2", className)}
83
+ {...props}
84
+ />
85
+ )
86
+ }
87
+
88
+ function DialogFooter({
89
+ className,
90
+ showCloseButton = false,
91
+ children,
92
+ ...props
93
+ }: React.ComponentProps<"div"> & {
94
+ showCloseButton?: boolean
95
+ }) {
96
+ return (
97
+ <div
98
+ data-slot="dialog-footer"
99
+ className={cn(
100
+ "-mx-4 -mb-4 flex flex-col-reverse gap-2 rounded-b-xl border-t bg-muted/50 p-4 sm:flex-row sm:justify-end",
101
+ className
102
+ )}
103
+ {...props}
104
+ >
105
+ {children}
106
+ {showCloseButton && (
107
+ <DialogPrimitive.Close render={<Button variant="outline" />}>
108
+ Close
109
+ </DialogPrimitive.Close>
110
+ )}
111
+ </div>
112
+ )
113
+ }
114
+
115
+ function DialogTitle({ className, ...props }: DialogPrimitive.Title.Props) {
116
+ return (
117
+ <DialogPrimitive.Title
118
+ data-slot="dialog-title"
119
+ className={cn(
120
+ "font-heading text-base leading-none font-medium",
121
+ className
122
+ )}
123
+ {...props}
124
+ />
125
+ )
126
+ }
127
+
128
+ function DialogDescription({
129
+ className,
130
+ ...props
131
+ }: DialogPrimitive.Description.Props) {
132
+ return (
133
+ <DialogPrimitive.Description
134
+ data-slot="dialog-description"
135
+ className={cn(
136
+ "text-sm text-muted-foreground *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground",
137
+ className
138
+ )}
139
+ {...props}
140
+ />
141
+ )
142
+ }
143
+
144
+ export {
145
+ Dialog,
146
+ DialogClose,
147
+ DialogContent,
148
+ DialogDescription,
149
+ DialogFooter,
150
+ DialogHeader,
151
+ DialogOverlay,
152
+ DialogPortal,
153
+ DialogTitle,
154
+ DialogTrigger,
155
+ }