ruler-components 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 (114) hide show
  1. package/AGENTS.md +9 -0
  2. package/CLAUDE.md +1 -0
  3. package/LICENSE +21 -0
  4. package/README.md +45 -0
  5. package/app/docs/[slug]/page.tsx +147 -0
  6. package/app/docs/layout.tsx +26 -0
  7. package/app/docs/page.tsx +148 -0
  8. package/app/favicon.ico +0 -0
  9. package/app/globals.css +168 -0
  10. package/app/icon.png +0 -0
  11. package/app/layout.tsx +90 -0
  12. package/app/opengraph-image.tsx +10 -0
  13. package/app/page.tsx +34 -0
  14. package/app/twitter-image.tsx +10 -0
  15. package/components/docs/accordion-doc-page.tsx +352 -0
  16. package/components/docs/alert-doc-page.tsx +293 -0
  17. package/components/docs/allinput-doc-page.tsx +183 -0
  18. package/components/docs/avatar-doc-page.tsx +424 -0
  19. package/components/docs/badge-doc-page.tsx +363 -0
  20. package/components/docs/button-doc-page.tsx +406 -0
  21. package/components/docs/card-doc-page.tsx +364 -0
  22. package/components/docs/checkbox-doc-page.tsx +277 -0
  23. package/components/docs/colors-doc-page.tsx +430 -0
  24. package/components/docs/dialog-doc-page.tsx +364 -0
  25. package/components/docs/docs-footer.tsx +11 -0
  26. package/components/docs/docs-mobile-menu-bar.tsx +22 -0
  27. package/components/docs/docs-mobile-nav-context.tsx +27 -0
  28. package/components/docs/docs-page.tsx +263 -0
  29. package/components/docs/docs-search.tsx +303 -0
  30. package/components/docs/docs-sidebar.tsx +165 -0
  31. package/components/docs/docs-topbar.tsx +32 -0
  32. package/components/docs/dropdown-doc-page.tsx +522 -0
  33. package/components/docs/getting-started-doc-page.tsx +205 -0
  34. package/components/docs/label-doc-page.tsx +173 -0
  35. package/components/docs/loaders-doc-page.tsx +244 -0
  36. package/components/docs/menu-doc-page.tsx +373 -0
  37. package/components/docs/modal-doc-page.tsx +436 -0
  38. package/components/docs/next-steps.tsx +53 -0
  39. package/components/docs/pagination-doc-page.tsx +316 -0
  40. package/components/docs/progress-doc-page.tsx +387 -0
  41. package/components/docs/radio-doc-page.tsx +270 -0
  42. package/components/docs/skeleton-doc-page.tsx +230 -0
  43. package/components/docs/stepper-doc-page.tsx +309 -0
  44. package/components/docs/switch-doc-page.tsx +264 -0
  45. package/components/docs/table-doc-page.tsx +689 -0
  46. package/components/docs/tabs-doc-page.tsx +359 -0
  47. package/components/docs/theming-doc-page.tsx +243 -0
  48. package/components/docs/tooltip-doc-page.tsx +357 -0
  49. package/components/docs/usage-section.tsx +21 -0
  50. package/components/icons/discord.tsx +9 -0
  51. package/components/icons/github.tsx +9 -0
  52. package/components/icons/x.tsx +9 -0
  53. package/components/installation-section.tsx +34 -0
  54. package/components/landing/feature-highlights.tsx +105 -0
  55. package/components/landing/footer-strip.tsx +45 -0
  56. package/components/landing/hero-card.tsx +14 -0
  57. package/components/landing/hero-spotlight.tsx +126 -0
  58. package/components/landing/hero.tsx +64 -0
  59. package/components/landing/navbar.tsx +44 -0
  60. package/components/landing/quick-start-section.tsx +59 -0
  61. package/components/landing/site-footer.tsx +55 -0
  62. package/components/theme-provider.tsx +60 -0
  63. package/components/ui/accordion.tsx +174 -0
  64. package/components/ui/alert.tsx +103 -0
  65. package/components/ui/allinput.tsx +210 -0
  66. package/components/ui/avatar.tsx +179 -0
  67. package/components/ui/badge.tsx +52 -0
  68. package/components/ui/button.tsx +75 -0
  69. package/components/ui/card.tsx +105 -0
  70. package/components/ui/checkbox.tsx +130 -0
  71. package/components/ui/code-block.tsx +60 -0
  72. package/components/ui/data-table.tsx +235 -0
  73. package/components/ui/dialog.tsx +173 -0
  74. package/components/ui/dropdown.tsx +78 -0
  75. package/components/ui/feature-card.tsx +38 -0
  76. package/components/ui/input.tsx +44 -0
  77. package/components/ui/label.tsx +110 -0
  78. package/components/ui/loaders.tsx +139 -0
  79. package/components/ui/menu.tsx +118 -0
  80. package/components/ui/modal.tsx +399 -0
  81. package/components/ui/package-manager-tabs.tsx +40 -0
  82. package/components/ui/pagination.tsx +200 -0
  83. package/components/ui/progress.tsx +134 -0
  84. package/components/ui/radio.tsx +262 -0
  85. package/components/ui/skeleton.tsx +102 -0
  86. package/components/ui/steppers.tsx +309 -0
  87. package/components/ui/switch.tsx +153 -0
  88. package/components/ui/table.tsx +58 -0
  89. package/components/ui/tabs.tsx +166 -0
  90. package/components/ui/theme-toggle.tsx +60 -0
  91. package/components/ui/tooltip.tsx +238 -0
  92. package/data/color-palette.ts +35 -0
  93. package/data/docs-nav.ts +84 -0
  94. package/eslint.config.mjs +18 -0
  95. package/index.ts +8 -0
  96. package/lib/cn.ts +4 -0
  97. package/lib/color.ts +85 -0
  98. package/lib/og-image.tsx +138 -0
  99. package/lib/theme.ts +33 -0
  100. package/next.config.ts +7 -0
  101. package/package.json +30 -0
  102. package/postcss.config.mjs +7 -0
  103. package/public/dark.png +0 -0
  104. package/public/dark1.png +0 -0
  105. package/public/dark2.png +0 -0
  106. package/public/file.svg +1 -0
  107. package/public/globe.svg +1 -0
  108. package/public/light.png +0 -0
  109. package/public/light1.png +0 -0
  110. package/public/next.svg +1 -0
  111. package/public/vercel.svg +1 -0
  112. package/public/video/hero-demo.mp4 +0 -0
  113. package/public/window.svg +1 -0
  114. package/tsconfig.json +34 -0
@@ -0,0 +1,38 @@
1
+ import type { LucideIcon } from "lucide-react";
2
+ import { cn } from "@/lib/cn";
3
+
4
+ export function FeatureCard({
5
+ icon: Icon,
6
+ title,
7
+ description,
8
+ variant = "plain",
9
+ }: {
10
+ icon: LucideIcon;
11
+ title: string;
12
+ description: string;
13
+ variant?: "plain" | "bordered";
14
+ }) {
15
+ return (
16
+ <div
17
+ className={cn(
18
+ variant === "bordered" &&
19
+ "rounded-xl border border-zinc-200 p-5 dark:border-zinc-800",
20
+ )}
21
+ >
22
+ <div
23
+ className={cn(
24
+ "mb-4 flex h-11 w-11 items-center justify-center rounded-2xl border",
25
+ variant === "bordered"
26
+ ? "border-zinc-200 bg-zinc-50 text-zinc-900 dark:border-white/10 dark:bg-white/[0.06] dark:text-zinc-100"
27
+ : "border-zinc-800 bg-zinc-900 text-white dark:border-zinc-200 dark:bg-white dark:text-zinc-900",
28
+ )}
29
+ >
30
+ <Icon className="h-5 w-5" />
31
+ </div>
32
+ <h3 className="text-base font-semibold text-zinc-900 dark:text-zinc-50">
33
+ {title}
34
+ </h3>
35
+ <p className="mt-1 text-sm text-zinc-500 dark:text-zinc-400">{description}</p>
36
+ </div>
37
+ );
38
+ }
@@ -0,0 +1,44 @@
1
+ import * as React from "react";
2
+ import { cva, type VariantProps } from "class-variance-authority";
3
+ import { cn } from "@/lib/cn";
4
+
5
+ const inputVariants = cva(
6
+ "flex w-full rounded-xl border bg-white px-3.5 text-sm text-zinc-900 outline-none transition-all duration-150 placeholder:text-zinc-400 disabled:cursor-not-allowed disabled:opacity-50 dark:bg-zinc-900 dark:text-zinc-100 dark:placeholder:text-zinc-500",
7
+ {
8
+ variants: {
9
+ size: {
10
+ sm: "h-8 text-xs",
11
+ md: "h-10 text-sm",
12
+ lg: "h-11 text-base",
13
+ },
14
+ state: {
15
+ default:
16
+ "border-zinc-200 hover:border-zinc-300 focus:border-zinc-900 focus:ring-2 focus:ring-zinc-900/10 dark:border-zinc-800 dark:hover:border-zinc-700 dark:focus:border-zinc-400 dark:focus:ring-zinc-400/15",
17
+ error:
18
+ "border-red-400 focus:border-red-500 focus:ring-2 focus:ring-red-500/15 dark:border-red-500/60 dark:focus:border-red-400",
19
+ },
20
+ },
21
+ defaultVariants: {
22
+ size: "md",
23
+ state: "default",
24
+ },
25
+ },
26
+ );
27
+
28
+ export interface InputProps
29
+ extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "size">,
30
+ VariantProps<typeof inputVariants> {}
31
+
32
+ const Input = React.forwardRef<HTMLInputElement, InputProps>(
33
+ ({ className, size, state, type = "text", ...props }, ref) => (
34
+ <input
35
+ ref={ref}
36
+ type={type}
37
+ className={cn(inputVariants({ size, state }), className)}
38
+ {...props}
39
+ />
40
+ ),
41
+ );
42
+ Input.displayName = "Input";
43
+
44
+ export { Input, inputVariants };
@@ -0,0 +1,110 @@
1
+ import * as React from "react";
2
+ import { cva, type VariantProps } from "class-variance-authority";
3
+ import { cn } from "@/lib/cn";
4
+
5
+ const labelVariants = cva(
6
+ "inline-flex items-center gap-1 font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
7
+ {
8
+ variants: {
9
+ variant: {
10
+ default: "text-zinc-800 dark:text-zinc-100",
11
+ muted: "text-zinc-500 dark:text-zinc-400",
12
+ },
13
+ size: {
14
+ sm: "text-xs",
15
+ md: "text-sm",
16
+ lg: "text-[15px]",
17
+ },
18
+ },
19
+ defaultVariants: {
20
+ variant: "default",
21
+ size: "md",
22
+ },
23
+ },
24
+ );
25
+
26
+ export interface LabelProps
27
+ extends React.LabelHTMLAttributes<HTMLLabelElement>,
28
+ VariantProps<typeof labelVariants> {
29
+ required?: boolean;
30
+ optional?: boolean;
31
+ hint?: string;
32
+ error?: string;
33
+ }
34
+
35
+ const Label = React.forwardRef<HTMLLabelElement, LabelProps>(
36
+ ({ className, variant, size, required, optional, hint, error, children, ...props }, ref) => {
37
+ return (
38
+ <div className="flex w-full flex-col gap-1.5">
39
+ <div className="flex items-center justify-between gap-2">
40
+ <label
41
+ ref={ref}
42
+ className={cn(labelVariants({ variant, size }), className)}
43
+ {...props}
44
+ >
45
+ {children}
46
+ {required && (
47
+ <span className="text-red-500" aria-label="required">
48
+ *
49
+ </span>
50
+ )}
51
+ </label>
52
+ {optional && (
53
+ <span className="text-xs text-zinc-400 dark:text-zinc-500">Optional</span>
54
+ )}
55
+ </div>
56
+ {hint && !error && (
57
+ <p className="text-xs leading-relaxed text-zinc-500 dark:text-zinc-400">{hint}</p>
58
+ )}
59
+ {error && (
60
+ <p className="text-xs leading-relaxed text-red-500 dark:text-red-400" role="alert">
61
+ {error}
62
+ </p>
63
+ )}
64
+ </div>
65
+ );
66
+ },
67
+ );
68
+ Label.displayName = "Label";
69
+
70
+ export interface FieldProps extends React.HTMLAttributes<HTMLDivElement> {
71
+ label: string;
72
+ htmlFor: string;
73
+ required?: boolean;
74
+ optional?: boolean;
75
+ hint?: string;
76
+ error?: string;
77
+ labelSize?: "sm" | "md" | "lg";
78
+ children: React.ReactNode;
79
+ }
80
+
81
+ function Field({
82
+ className,
83
+ label,
84
+ htmlFor,
85
+ required,
86
+ optional,
87
+ hint,
88
+ error,
89
+ labelSize = "md",
90
+ children,
91
+ ...props
92
+ }: FieldProps) {
93
+ return (
94
+ <div className={cn("flex w-full flex-col gap-2", className)} {...props}>
95
+ <Label
96
+ htmlFor={htmlFor}
97
+ required={required}
98
+ optional={optional}
99
+ hint={hint}
100
+ error={error}
101
+ size={labelSize}
102
+ >
103
+ {label}
104
+ </Label>
105
+ {children}
106
+ </div>
107
+ );
108
+ }
109
+
110
+ export { Label, Field, labelVariants };
@@ -0,0 +1,139 @@
1
+ "use client";
2
+
3
+ import React from "react";
4
+ import { Sparkles } from "lucide-react";
5
+ import { cn } from "@/lib/cn";
6
+
7
+ const LoaderStyles = () => (
8
+ <style dangerouslySetInnerHTML={{ __html: `
9
+ @keyframes shimmer { 100% { transform: translateX(100%); } }
10
+ @keyframes progressLoad { 0% { transform: scaleX(0); } 50% { transform: scaleX(0.5); } 100% { transform: scaleX(1); } }
11
+ @keyframes morphShape { 0%, 100% { border-radius: 8px; transform: rotate(0deg); } 50% { border-radius: 50%; transform: rotate(180deg); } }
12
+ @keyframes blurReveal { 0% { filter: blur(10px); opacity: 0; transform: translateY(10px); } 100% { filter: blur(0); opacity: 1; transform: translateY(0); } }
13
+ @keyframes spinDots { 0%, 100% { opacity: 0.2; } 50% { opacity: 1; } }
14
+ @keyframes squareMove { 0%, 100% { transform: scale(1); opacity: 1; } 50% { transform: scale(0.6); opacity: 0.4; } }
15
+ @keyframes loadBarMove { 0% { left: -100%; } 100% { left: 100%; } }
16
+ `}} />
17
+ );
18
+
19
+ export const SkeletonShimmer = ({ className }: { className?: string }) => (
20
+ <div className={cn("relative overflow-hidden rounded-md bg-zinc-100 dark:bg-zinc-800", className)}>
21
+ <LoaderStyles />
22
+ <div className="absolute inset-0 -translate-x-full bg-gradient-to-r from-transparent via-white/40 to-transparent dark:via-white/5" style={{ animation: 'shimmer 2s infinite' }} />
23
+ </div>
24
+ );
25
+
26
+ // --- Circular Spinners ---
27
+
28
+ export const ProgressRing = () => (
29
+ <svg className="h-10 w-10 animate-spin text-zinc-900 dark:text-white" viewBox="0 0 32 32">
30
+ <circle className="opacity-10" cx="16" cy="16" r="14" stroke="currentColor" strokeWidth="3" fill="none" />
31
+ <path className="opacity-100" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" d="M16 2 A14 14 0 0 1 30 16" />
32
+ </svg>
33
+ );
34
+
35
+ export const SpinnerGradient = () => (
36
+ <div className="h-10 w-10 rounded-full border-4 border-zinc-100 dark:border-zinc-800 border-t-zinc-900 dark:border-t-white animate-spin" />
37
+ );
38
+
39
+ export const SpinnerDots = () => (
40
+ <div className="relative h-10 w-10">
41
+ {[...Array(8)].map((_, i) => (
42
+ <div key={i} className="absolute top-0 left-0 w-full h-full" style={{ transform: `rotate(${i * 45}deg)` }}>
43
+ <div className="h-2 w-2 rounded-full bg-zinc-900 dark:bg-white" style={{ animation: 'spinDots 1s linear infinite', animationDelay: `${i * 0.12}s` }} />
44
+ </div>
45
+ ))}
46
+ </div>
47
+ );
48
+
49
+ export const HalfCircles = () => (
50
+ <div className="relative h-10 w-10 border-4 border-transparent border-t-zinc-900 dark:border-t-white rounded-full animate-spin">
51
+ <div className="absolute inset-0 border-4 border-transparent border-b-zinc-400 rounded-full opacity-50" />
52
+ </div>
53
+ );
54
+
55
+ // --- Geometric & Modern ---
56
+
57
+ export const SquaresLoader = () => (
58
+ <div className="flex gap-1.5">
59
+ <div className="h-4 w-4 bg-zinc-900 dark:bg-white" style={{ animation: 'squareMove 1s ease-in-out infinite' }} />
60
+ <div className="h-4 w-4 bg-zinc-300 dark:bg-zinc-700" style={{ animation: 'squareMove 1s ease-in-out infinite', animationDelay: '0.2s' }} />
61
+ </div>
62
+ );
63
+
64
+ export const LoadBar = () => (
65
+ <div className="relative h-4 w-12 overflow-hidden rounded-full bg-zinc-100 dark:bg-zinc-800">
66
+ <div className="absolute top-0 h-full w-6 rounded-full bg-zinc-900 dark:bg-white" style={{ animation: 'loadBarMove 1.5s linear infinite' }} />
67
+ </div>
68
+ );
69
+
70
+ export const PulseLoader = () => (
71
+ <div className="relative h-10 w-10 flex items-center justify-center">
72
+ <div className="absolute h-full w-full animate-ping rounded-full bg-zinc-900/20 dark:bg-white/20" />
73
+ <div className="h-4 w-4 rounded-full bg-zinc-900 dark:bg-white" />
74
+ </div>
75
+ );
76
+
77
+ // --- Indicators ---
78
+
79
+ export const DotWave = () => (
80
+ <div className="flex gap-1.5">
81
+ {[0, 1, 2].map((i) => (
82
+ <div key={i} className="h-2 w-2 animate-bounce rounded-full bg-zinc-900 dark:bg-white" style={{ animationDelay: `${i * 0.15}s` }} />
83
+ ))}
84
+ </div>
85
+ );
86
+
87
+ export const ThreeDots = () => (
88
+ <div className="flex gap-1">
89
+ {[0, 1, 2].map((i) => (
90
+ <div key={i} className="h-1.5 w-1.5 rounded-full bg-zinc-400 animate-pulse" style={{ animationDelay: `${i * 0.2}s` }} />
91
+ ))}
92
+ </div>
93
+ );
94
+
95
+ export const TypingIndicator = () => (
96
+ <div className="flex items-center gap-1.5 px-4 py-2 bg-zinc-100 dark:bg-zinc-800 rounded-2xl w-fit">
97
+ {[0, 1, 2].map((i) => (
98
+ <div key={i} className="h-1.5 w-1.5 animate-pulse rounded-full bg-zinc-400" style={{ animationDelay: `${i * 0.2}s` }} />
99
+ ))}
100
+ </div>
101
+ );
102
+
103
+ export const ProgressBar = () => (
104
+ <div className="h-1.5 w-full max-w-[200px] overflow-hidden rounded-full bg-zinc-100 dark:bg-zinc-800">
105
+ <LoaderStyles />
106
+ <div className="h-full w-full origin-left bg-zinc-900 dark:bg-white" style={{ animation: 'progressLoad 1.5s ease-in-out infinite' }} />
107
+ </div>
108
+ );
109
+
110
+ // --- Premium ---
111
+
112
+ export const MorphingLoader = () => (
113
+ <div className="h-10 w-10 bg-zinc-900 dark:bg-white" style={{ animation: 'morphShape 3s linear infinite' }} />
114
+ );
115
+
116
+ export const OrbitLoader = () => (
117
+ <div className="relative flex h-12 w-12 items-center justify-center">
118
+ <div className="h-2 w-2 rounded-full bg-zinc-900 dark:bg-white" />
119
+ <div className="absolute h-full w-full animate-spin">
120
+ <div className="h-2 w-2 rounded-full bg-zinc-400" />
121
+ </div>
122
+ </div>
123
+ );
124
+
125
+ export const BlurReveal = ({ children }: { children: React.ReactNode }) => (
126
+ <div style={{ animation: 'blurReveal 1s ease-out forwards' }}>
127
+ {children}
128
+ </div>
129
+ );
130
+
131
+ export const AILoader = ({ text = "Thinking..." }: { text?: string }) => (
132
+ <div className="flex flex-col items-center gap-4">
133
+ <div className="relative flex h-12 w-12 items-center justify-center">
134
+ <div className="absolute h-full w-full animate-spin rounded-full border-2 border-zinc-200 border-t-zinc-900 dark:border-zinc-800 dark:border-t-white" />
135
+ <Sparkles className="h-5 w-5 animate-pulse text-zinc-900 dark:text-white" />
136
+ </div>
137
+ <span className="text-[10px] font-bold uppercase tracking-widest text-zinc-400 animate-pulse">{text}</span>
138
+ </div>
139
+ );
@@ -0,0 +1,118 @@
1
+ import * as React from "react";
2
+ import { Check } from "lucide-react";
3
+ import { cva, type VariantProps } from "class-variance-authority";
4
+ import { cn } from "@/lib/cn";
5
+
6
+ const menuVariants = cva(
7
+ "w-56 rounded-lg p-1 outline-none",
8
+ {
9
+ variants: {
10
+ variant: {
11
+ default:
12
+ "border border-zinc-200 bg-white shadow-xl dark:border-zinc-800 dark:bg-zinc-950",
13
+ ghost: "bg-transparent",
14
+ },
15
+ },
16
+ defaultVariants: { variant: "default" },
17
+ },
18
+ );
19
+
20
+ export interface MenuProps
21
+ extends React.HTMLAttributes<HTMLDivElement>,
22
+ VariantProps<typeof menuVariants> {}
23
+
24
+ const Menu = React.forwardRef<HTMLDivElement, MenuProps>(
25
+ ({ className, variant, ...props }, ref) => (
26
+ <div
27
+ ref={ref}
28
+ role="menu"
29
+ className={cn(menuVariants({ variant }), className)}
30
+ {...props}
31
+ />
32
+ ),
33
+ );
34
+ Menu.displayName = "Menu";
35
+
36
+ const menuItemVariants = cva(
37
+ [
38
+ "flex w-full cursor-pointer items-center gap-2 rounded-md px-3 py-2 text-left text-sm outline-none transition-colors",
39
+ "focus-visible:bg-zinc-100 dark:focus-visible:bg-zinc-900",
40
+ "disabled:pointer-events-none disabled:opacity-40",
41
+ ],
42
+ {
43
+ variants: {
44
+ variant: {
45
+ default:
46
+ "text-zinc-700 hover:bg-zinc-100 dark:text-zinc-300 dark:hover:bg-zinc-900",
47
+ destructive:
48
+ "text-red-600 hover:bg-red-50 dark:text-red-400 dark:hover:bg-red-950/30",
49
+ },
50
+ },
51
+ defaultVariants: { variant: "default" },
52
+ },
53
+ );
54
+
55
+ export interface MenuItemProps
56
+ extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "children">,
57
+ VariantProps<typeof menuItemVariants> {
58
+ label: string;
59
+ icon?: React.ElementType;
60
+ shortcut?: string;
61
+ selected?: boolean;
62
+ }
63
+
64
+ const MenuItem = React.forwardRef<HTMLButtonElement, MenuItemProps>(
65
+ (
66
+ { className, variant, label, icon: Icon, shortcut, selected, disabled, ...props },
67
+ ref,
68
+ ) => (
69
+ <button
70
+ ref={ref}
71
+ type="button"
72
+ role="menuitem"
73
+ disabled={disabled}
74
+ className={cn(menuItemVariants({ variant }), className)}
75
+ {...props}
76
+ >
77
+ {Icon && <Icon className="h-4 w-4 shrink-0 opacity-70" />}
78
+ <span className="flex-1 truncate">{label}</span>
79
+ {shortcut && (
80
+ <span className="shrink-0 text-xs tracking-wide text-zinc-400 dark:text-zinc-500">
81
+ {shortcut}
82
+ </span>
83
+ )}
84
+ {selected && <Check className="h-3.5 w-3.5 shrink-0 text-zinc-900 dark:text-white" />}
85
+ </button>
86
+ ),
87
+ );
88
+ MenuItem.displayName = "MenuItem";
89
+
90
+ const MenuLabel = React.forwardRef<
91
+ HTMLDivElement,
92
+ React.HTMLAttributes<HTMLDivElement>
93
+ >(({ className, ...props }, ref) => (
94
+ <div
95
+ ref={ref}
96
+ className={cn(
97
+ "px-3 py-2 text-[11px] font-bold uppercase tracking-wider text-zinc-400 dark:text-zinc-500",
98
+ className,
99
+ )}
100
+ {...props}
101
+ />
102
+ ));
103
+ MenuLabel.displayName = "MenuLabel";
104
+
105
+ const MenuDivider = React.forwardRef<
106
+ HTMLDivElement,
107
+ React.HTMLAttributes<HTMLDivElement>
108
+ >(({ className, ...props }, ref) => (
109
+ <div
110
+ ref={ref}
111
+ role="separator"
112
+ className={cn("my-1 h-px bg-zinc-100 dark:bg-zinc-800", className)}
113
+ {...props}
114
+ />
115
+ ));
116
+ MenuDivider.displayName = "MenuDivider";
117
+
118
+ export { Menu, MenuItem, MenuLabel, MenuDivider, menuVariants, menuItemVariants };