ruler-components 0.1.1 → 0.1.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.
Files changed (118) hide show
  1. package/dist/index.d.mts +51 -0
  2. package/dist/index.d.ts +51 -0
  3. package/dist/index.js +534 -0
  4. package/dist/index.js.map +1 -0
  5. package/dist/index.mjs +498 -0
  6. package/dist/index.mjs.map +1 -0
  7. package/package.json +21 -2
  8. package/AGENTS.md +0 -9
  9. package/CLAUDE.md +0 -1
  10. package/app/docs/[slug]/page.tsx +0 -147
  11. package/app/docs/layout.tsx +0 -26
  12. package/app/docs/page.tsx +0 -148
  13. package/app/favicon.ico +0 -0
  14. package/app/globals.css +0 -168
  15. package/app/icon.png +0 -0
  16. package/app/layout.tsx +0 -90
  17. package/app/opengraph-image.tsx +0 -10
  18. package/app/page.tsx +0 -34
  19. package/app/twitter-image.tsx +0 -10
  20. package/components/docs/accordion-doc-page.tsx +0 -352
  21. package/components/docs/alert-doc-page.tsx +0 -293
  22. package/components/docs/allinput-doc-page.tsx +0 -183
  23. package/components/docs/avatar-doc-page.tsx +0 -424
  24. package/components/docs/badge-doc-page.tsx +0 -363
  25. package/components/docs/button-doc-page.tsx +0 -406
  26. package/components/docs/card-doc-page.tsx +0 -364
  27. package/components/docs/checkbox-doc-page.tsx +0 -277
  28. package/components/docs/colors-doc-page.tsx +0 -430
  29. package/components/docs/dialog-doc-page.tsx +0 -364
  30. package/components/docs/docs-footer.tsx +0 -11
  31. package/components/docs/docs-mobile-menu-bar.tsx +0 -22
  32. package/components/docs/docs-mobile-nav-context.tsx +0 -27
  33. package/components/docs/docs-page.tsx +0 -263
  34. package/components/docs/docs-search.tsx +0 -303
  35. package/components/docs/docs-sidebar.tsx +0 -165
  36. package/components/docs/docs-topbar.tsx +0 -32
  37. package/components/docs/dropdown-doc-page.tsx +0 -522
  38. package/components/docs/getting-started-doc-page.tsx +0 -205
  39. package/components/docs/label-doc-page.tsx +0 -173
  40. package/components/docs/loaders-doc-page.tsx +0 -244
  41. package/components/docs/menu-doc-page.tsx +0 -373
  42. package/components/docs/modal-doc-page.tsx +0 -436
  43. package/components/docs/next-steps.tsx +0 -53
  44. package/components/docs/pagination-doc-page.tsx +0 -316
  45. package/components/docs/progress-doc-page.tsx +0 -387
  46. package/components/docs/radio-doc-page.tsx +0 -270
  47. package/components/docs/skeleton-doc-page.tsx +0 -230
  48. package/components/docs/stepper-doc-page.tsx +0 -309
  49. package/components/docs/switch-doc-page.tsx +0 -264
  50. package/components/docs/table-doc-page.tsx +0 -689
  51. package/components/docs/tabs-doc-page.tsx +0 -359
  52. package/components/docs/theming-doc-page.tsx +0 -243
  53. package/components/docs/tooltip-doc-page.tsx +0 -357
  54. package/components/docs/usage-section.tsx +0 -21
  55. package/components/icons/discord.tsx +0 -9
  56. package/components/icons/github.tsx +0 -9
  57. package/components/icons/x.tsx +0 -9
  58. package/components/installation-section.tsx +0 -34
  59. package/components/landing/feature-highlights.tsx +0 -105
  60. package/components/landing/footer-strip.tsx +0 -45
  61. package/components/landing/hero-card.tsx +0 -14
  62. package/components/landing/hero-spotlight.tsx +0 -126
  63. package/components/landing/hero.tsx +0 -64
  64. package/components/landing/navbar.tsx +0 -44
  65. package/components/landing/quick-start-section.tsx +0 -59
  66. package/components/landing/site-footer.tsx +0 -55
  67. package/components/theme-provider.tsx +0 -60
  68. package/components/ui/accordion.tsx +0 -174
  69. package/components/ui/alert.tsx +0 -103
  70. package/components/ui/allinput.tsx +0 -210
  71. package/components/ui/avatar.tsx +0 -179
  72. package/components/ui/badge.tsx +0 -52
  73. package/components/ui/button.tsx +0 -75
  74. package/components/ui/card.tsx +0 -105
  75. package/components/ui/checkbox.tsx +0 -130
  76. package/components/ui/code-block.tsx +0 -60
  77. package/components/ui/data-table.tsx +0 -235
  78. package/components/ui/dialog.tsx +0 -173
  79. package/components/ui/dropdown.tsx +0 -78
  80. package/components/ui/feature-card.tsx +0 -38
  81. package/components/ui/input.tsx +0 -44
  82. package/components/ui/label.tsx +0 -110
  83. package/components/ui/loaders.tsx +0 -139
  84. package/components/ui/menu.tsx +0 -118
  85. package/components/ui/modal.tsx +0 -399
  86. package/components/ui/package-manager-tabs.tsx +0 -40
  87. package/components/ui/pagination.tsx +0 -200
  88. package/components/ui/progress.tsx +0 -134
  89. package/components/ui/radio.tsx +0 -262
  90. package/components/ui/skeleton.tsx +0 -102
  91. package/components/ui/steppers.tsx +0 -309
  92. package/components/ui/switch.tsx +0 -153
  93. package/components/ui/table.tsx +0 -58
  94. package/components/ui/tabs.tsx +0 -166
  95. package/components/ui/theme-toggle.tsx +0 -60
  96. package/components/ui/tooltip.tsx +0 -238
  97. package/data/color-palette.ts +0 -35
  98. package/data/docs-nav.ts +0 -84
  99. package/eslint.config.mjs +0 -18
  100. package/index.ts +0 -8
  101. package/lib/cn.ts +0 -4
  102. package/lib/color.ts +0 -85
  103. package/lib/og-image.tsx +0 -138
  104. package/lib/theme.ts +0 -33
  105. package/next.config.ts +0 -7
  106. package/postcss.config.mjs +0 -7
  107. package/public/dark.png +0 -0
  108. package/public/dark1.png +0 -0
  109. package/public/dark2.png +0 -0
  110. package/public/file.svg +0 -1
  111. package/public/globe.svg +0 -1
  112. package/public/light.png +0 -0
  113. package/public/light1.png +0 -0
  114. package/public/next.svg +0 -1
  115. package/public/vercel.svg +0 -1
  116. package/public/video/hero-demo.mp4 +0 -0
  117. package/public/window.svg +0 -1
  118. package/tsconfig.json +0 -34
@@ -1,309 +0,0 @@
1
- "use client";
2
-
3
- import React from "react";
4
- import { motion } from "framer-motion";
5
- import { Check, ChevronRight, HelpCircle } from "lucide-react";
6
- import { cn } from "@/lib/cn";
7
-
8
- export interface Step {
9
- title: string;
10
- description?: string;
11
- icon?: React.ElementType;
12
- }
13
-
14
- interface StepperProps {
15
- steps: Step[];
16
- currentStep: number;
17
- onStepClick?: (step: number) => void;
18
- className?: string;
19
- }
20
-
21
- type StepTone = "done" | "current" | "upcoming";
22
-
23
- function getTone(i: number, currentStep: number): StepTone {
24
- if (i < currentStep) return "done";
25
- if (i === currentStep) return "current";
26
- return "upcoming";
27
- }
28
-
29
- const NODE_TONE: Record<StepTone, string> = {
30
- done: "border-zinc-900 bg-zinc-900 text-white dark:bg-white dark:text-zinc-900 dark:border-white",
31
- current:
32
- "border-zinc-900 bg-zinc-900 text-white dark:border-white dark:bg-white dark:text-zinc-900",
33
- upcoming:
34
- "border-zinc-200 bg-white text-zinc-400 dark:border-zinc-800 dark:bg-zinc-950 dark:text-zinc-600",
35
- };
36
-
37
- const TEXT_TONE: Record<StepTone, string> = {
38
- done: "text-zinc-900 dark:text-zinc-100",
39
- current: "text-zinc-900 dark:text-white",
40
- upcoming: "text-zinc-400 dark:text-zinc-600",
41
- };
42
-
43
- export function HorizontalStepper({ steps, currentStep, className }: StepperProps) {
44
- return (
45
- <nav aria-label="Progress" className={cn("flex w-full items-start pb-8", className)}>
46
- {steps.map((step, i) => {
47
- const tone = getTone(i, currentStep);
48
- return (
49
- <React.Fragment key={i}>
50
- <div className="relative flex flex-col items-center">
51
- <div
52
- aria-current={tone === "current" ? "step" : undefined}
53
- className={cn(
54
- "z-10 flex h-10 w-10 items-center justify-center rounded-full border-2 text-sm font-bold shadow-sm transition-all duration-300",
55
- NODE_TONE[tone],
56
- )}
57
- >
58
- {tone === "done" ? <Check size={18} strokeWidth={3} /> : <span>{i + 1}</span>}
59
- </div>
60
- <div className="absolute top-12 w-24 text-center">
61
- <span className={cn("text-[10px] font-black uppercase tracking-tighter transition-colors duration-300", TEXT_TONE[tone])}>
62
- {step.title}
63
- </span>
64
- </div>
65
- </div>
66
- {i < steps.length - 1 && (
67
- <div className="relative mx-2 mt-5 h-0.5 flex-1 shrink-0 rounded-full bg-zinc-100 dark:bg-zinc-800">
68
- <motion.div
69
- initial={false}
70
- animate={{ width: i < currentStep ? "100%" : "0%" }}
71
- className="absolute h-full rounded-full bg-zinc-900 dark:bg-white"
72
- transition={{ duration: 0.5, ease: "easeInOut" }}
73
- />
74
- </div>
75
- )}
76
- </React.Fragment>
77
- );
78
- })}
79
- </nav>
80
- );
81
- }
82
-
83
- export function VerticalStepper({ steps, currentStep, className }: StepperProps) {
84
- return (
85
- <nav aria-label="Progress" className={cn("space-y-0", className)}>
86
- {steps.map((step, i) => {
87
- const tone = getTone(i, currentStep);
88
- return (
89
- <div key={i} className="relative pl-10 pb-10 last:pb-0">
90
- {i < steps.length - 1 && (
91
- <div className="absolute left-[15px] top-8 bottom-0 w-0.5 rounded-full bg-zinc-100 dark:bg-zinc-800">
92
- <motion.div
93
- initial={false}
94
- animate={{ height: i < currentStep ? "100%" : "0%" }}
95
- className="w-full rounded-full bg-zinc-900 dark:bg-white"
96
- transition={{ duration: 0.4 }}
97
- />
98
- </div>
99
- )}
100
- <motion.div
101
- aria-current={tone === "current" ? "step" : undefined}
102
- animate={{ scale: tone === "current" ? 1.15 : 1 }}
103
- className={cn(
104
- "absolute left-0 top-0 z-10 flex h-8 w-8 items-center justify-center rounded-full border-2 transition-all duration-300",
105
- tone === "done" || tone === "current"
106
- ? "border-zinc-900 bg-zinc-900 dark:border-white dark:bg-white"
107
- : "border-zinc-200 bg-white dark:border-zinc-800 dark:bg-zinc-950",
108
- )}
109
- >
110
- {tone === "done" ? (
111
- <Check size={14} strokeWidth={3} className="text-white dark:text-zinc-900" />
112
- ) : (
113
- <div className={cn("h-1.5 w-1.5 rounded-full", tone === "current" ? "bg-white dark:bg-zinc-900" : "bg-zinc-300 dark:bg-zinc-700")} />
114
- )}
115
- </motion.div>
116
- <div className="flex flex-col">
117
- <h6 className={cn("text-sm font-bold tracking-tight transition-colors duration-300", TEXT_TONE[tone])}>
118
- {step.title}
119
- </h6>
120
- <p className="mt-1 text-xs leading-tight text-zinc-500 dark:text-zinc-400">{step.description}</p>
121
- </div>
122
- </div>
123
- );
124
- })}
125
- </nav>
126
- );
127
- }
128
-
129
- export function ProgressStepper({ steps, currentStep }: StepperProps) {
130
- const progress = ((currentStep + 1) / steps.length) * 100;
131
- return (
132
- <div className="w-full space-y-4">
133
- <div className="flex items-end justify-between">
134
- <div className="flex items-baseline gap-2">
135
- <span className="text-lg font-black tracking-tighter text-zinc-900 dark:text-white">
136
- {Math.round(progress)}%
137
- </span>
138
- </div>
139
- <div className="flex gap-1">
140
- {steps.map((_, i) => (
141
- <motion.div
142
- key={i}
143
- animate={{ opacity: i <= currentStep ? 1 : 0.2 }}
144
- className="h-1 w-4 rounded-full bg-zinc-900 dark:bg-white"
145
- />
146
- ))}
147
- </div>
148
- </div>
149
- <div className="h-3 w-full overflow-hidden rounded-full bg-zinc-100 p-1 dark:bg-zinc-800">
150
- <motion.div
151
- initial={false}
152
- animate={{ width: `${progress}%` }}
153
- className="h-full rounded-full bg-zinc-900 dark:bg-white"
154
- transition={{ type: "spring", bounce: 0, duration: 0.8 }}
155
- />
156
- </div>
157
- </div>
158
- );
159
- }
160
- export function DotStepper({ steps, currentStep }: StepperProps) {
161
- return (
162
- <div className="flex items-center gap-3 rounded-full border border-zinc-200 bg-white p-4 dark:border-zinc-800 dark:bg-zinc-950">
163
- {steps.map((_, i) => {
164
- const tone = getTone(i, currentStep);
165
- return (
166
- <motion.div
167
- key={i}
168
- animate={{ width: tone === "current" ? 40 : 12 }}
169
- className={cn(
170
- "h-3 rounded-full transition-colors duration-300",
171
- tone === "done" || tone === "current" ? "bg-zinc-900 dark:bg-white" : "bg-zinc-200 dark:bg-zinc-800",
172
- )}
173
- transition={{ type: "spring", stiffness: 300, damping: 20 }}
174
- />
175
- );
176
- })}
177
- </div>
178
- );
179
- }
180
-
181
- export function SegmentedStepper({ steps, currentStep }: StepperProps) {
182
- return (
183
- <div className="flex w-full gap-1">
184
- {steps.map((_, i) => (
185
- <div key={i} className="h-2 flex-1 overflow-hidden rounded-sm bg-zinc-100 dark:bg-zinc-800">
186
- <motion.div
187
- initial={false}
188
- animate={{ x: i <= currentStep ? "0%" : "-100%" }}
189
- className="h-full bg-zinc-900 dark:bg-white"
190
- transition={{ duration: 0.6 }}
191
- />
192
- </div>
193
- ))}
194
- </div>
195
- );
196
- }
197
-
198
- export function BreadcrumbStepper({ steps, currentStep }: StepperProps) {
199
- return (
200
- <nav aria-label="Progress" className="w-full">
201
- <div className="flex w-full items-center gap-2">
202
- {steps.map((step, i) => {
203
- const tone = getTone(i, currentStep);
204
-
205
- return (
206
- <div
207
- key={i}
208
- className="flex min-w-0 flex-1 items-center gap-2"
209
- >
210
- <motion.div
211
- animate={{
212
- opacity: tone === "upcoming" ? 0.45 : 1,
213
- scale: tone === "current" ? 1 : 0.98,
214
- }}
215
- transition={{ duration: 0.25 }}
216
- className={cn(
217
- "flex min-w-0 flex-1 items-center justify-center rounded-full px-2 py-2 text-[9px] font-bold uppercase tracking-wide transition-all duration-300",
218
- tone === "current" &&
219
- "bg-zinc-900 text-white shadow-sm dark:bg-white dark:text-zinc-900",
220
- tone === "done" &&
221
- "bg-zinc-100 text-zinc-900 dark:bg-zinc-800 dark:text-white",
222
- tone === "upcoming" &&
223
- "text-zinc-400 dark:text-zinc-600",
224
- )}
225
- >
226
- <span className="whitespace-nowrap">{step.title}</span>
227
- </motion.div>
228
-
229
- {i < steps.length - 1 && (
230
- <span
231
- aria-hidden="true"
232
- className="h-px w-4 shrink-0 bg-zinc-200 dark:bg-zinc-800"
233
- />
234
- )}
235
- </div>
236
- );
237
- })}
238
- </div>
239
- </nav>
240
- );
241
- }
242
-
243
- export function TimelineStepper({ steps, currentStep }: StepperProps) {
244
- return (
245
- <nav aria-label="Progress" className="space-y-12">
246
- {steps.map((step, i) => {
247
- const tone = getTone(i, currentStep);
248
- return (
249
- <div key={i} className="relative flex gap-8">
250
- {i < steps.length - 1 && (
251
- <div className={cn("absolute left-5 top-10 -bottom-12 w-0.5 transition-colors duration-1000", i < currentStep ? "bg-zinc-900 dark:bg-white" : "bg-zinc-100 dark:bg-zinc-800")} />
252
- )}
253
- <motion.div
254
- animate={{ scale: tone === "current" ? 1.2 : 1 }}
255
- className={cn(
256
- "z-10 flex h-10 w-10 shrink-0 items-center justify-center rounded-full border-4 border-white shadow-xl ring-2 transition-all duration-300 dark:border-zinc-950",
257
- tone === "done" || tone === "current"
258
- ? "bg-zinc-900 ring-zinc-900 dark:bg-white dark:ring-white"
259
- : "bg-white ring-zinc-200 dark:bg-zinc-950 dark:ring-zinc-800",
260
- )}
261
- >
262
- {tone === "done" ? (
263
- <Check size={16} strokeWidth={3} className="text-white dark:text-zinc-900" />
264
- ) : (
265
- <span className={cn("text-xs font-bold", tone === "current" ? "text-white dark:text-zinc-900" : "text-zinc-400")}>{i + 1}</span>
266
- )}
267
- </motion.div>
268
- <div className={cn("transition-all duration-500", tone === "upcoming" ? "translate-x-4 opacity-20" : "translate-x-0 opacity-100")}>
269
- <h3 className="text-sm font-black uppercase tracking-tighter text-zinc-900 dark:text-white">{step.title}</h3>
270
- <p className="mt-1 text-xs text-zinc-500 dark:text-zinc-400">{step.description}</p>
271
- </div>
272
- </div>
273
- );
274
- })}
275
- </nav>
276
- );
277
- }
278
-
279
- export function InteractiveStepper({ steps, currentStep, onStepClick }: StepperProps) {
280
- return (
281
- <div className="flex flex-wrap gap-4">
282
- {steps.map((step, i) => {
283
- const tone = getTone(i, currentStep);
284
- return (
285
- <button
286
- key={i}
287
- onClick={() => onStepClick?.(i)}
288
- className={cn(
289
- "group relative flex min-w-44 flex-1 flex-col gap-2 rounded-2xl border-2 p-6 text-left transition-all duration-300",
290
- tone === "current"
291
- ? "border-zinc-900 bg-zinc-900 text-white dark:border-white dark:bg-white dark:text-zinc-900"
292
- : "border-zinc-100 hover:border-zinc-900 dark:border-zinc-800 dark:hover:border-white",
293
- )}
294
- >
295
- <span className={cn("text-[10px] font-bold uppercase tracking-widest", tone === "current" ? "text-zinc-400" : "text-zinc-400 group-hover:text-zinc-900 dark:group-hover:text-zinc-200")}>
296
- Phase 0{i + 1}
297
- </span>
298
- <span className="text-sm font-black">{step.title}</span>
299
- {tone === "done" && (
300
- <motion.div layoutId="inter-check" className="absolute -right-2 -top-2 rounded-full bg-zinc-900 p-1 text-white shadow-lg dark:bg-white dark:text-zinc-900">
301
- <Check size={14} strokeWidth={3} />
302
- </motion.div>
303
- )}
304
- </button>
305
- );
306
- })}
307
- </div>
308
- );
309
- }
@@ -1,153 +0,0 @@
1
- "use client";
2
-
3
- import * as React from "react";
4
- import { cva, type VariantProps } from "class-variance-authority";
5
- import { cn } from "@/lib/cn";
6
-
7
- const switchTrackVariants = cva(
8
- [
9
- "relative inline-flex shrink-0 cursor-pointer items-center rounded-full border border-transparent transition-colors duration-200",
10
- "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-zinc-900/20 focus-visible:ring-offset-2 focus-visible:ring-offset-white",
11
- "dark:focus-visible:ring-zinc-100/20 dark:focus-visible:ring-offset-zinc-950",
12
- "disabled:cursor-not-allowed disabled:opacity-50",
13
- "data-[state=checked]:bg-zinc-900 data-[state=unchecked]:bg-zinc-200",
14
- "dark:data-[state=checked]:bg-zinc-100 dark:data-[state=unchecked]:bg-zinc-700",
15
- "hover:data-[state=unchecked]:bg-zinc-300 dark:hover:data-[state=unchecked]:bg-zinc-600",
16
- "hover:data-[state=checked]:bg-zinc-800 dark:hover:data-[state=checked]:bg-zinc-200",
17
- ],
18
- {
19
- variants: {
20
- size: {
21
- sm: "h-5 w-9",
22
- md: "h-6 w-11",
23
- lg: "h-7 w-[3.25rem]",
24
- },
25
- },
26
- defaultVariants: { size: "md" },
27
- },
28
- );
29
-
30
- const switchThumbVariants = cva(
31
- "pointer-events-none absolute left-0.5 top-1/2 block -translate-y-1/2 rounded-full bg-white shadow-sm transition-transform duration-200 dark:bg-zinc-950",
32
- {
33
- variants: {
34
- size: {
35
- sm: "h-3.5 w-3.5 data-[state=checked]:translate-x-4",
36
- md: "h-4 w-4 data-[state=checked]:translate-x-5",
37
- lg: "h-5 w-5 data-[state=checked]:translate-x-6",
38
- },
39
- },
40
- defaultVariants: { size: "md" },
41
- },
42
- );
43
-
44
- export interface SwitchProps
45
- extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "onChange" | "size">,
46
- VariantProps<typeof switchTrackVariants> {
47
- checked?: boolean;
48
- defaultChecked?: boolean;
49
- onCheckedChange?: (checked: boolean) => void;
50
- label?: string;
51
- description?: string;
52
- }
53
-
54
- const Switch = React.forwardRef<HTMLButtonElement, SwitchProps>(
55
- (
56
- {
57
- className,
58
- size,
59
- checked,
60
- defaultChecked = false,
61
- onCheckedChange,
62
- disabled,
63
- label,
64
- description,
65
- id,
66
- onClick,
67
- ...props
68
- },
69
- ref,
70
- ) => {
71
- const generatedId = React.useId();
72
- const switchId = id ?? generatedId;
73
- const isControlled = checked !== undefined;
74
- const [uncontrolledChecked, setUncontrolledChecked] = React.useState(defaultChecked);
75
- const isChecked = isControlled ? checked : uncontrolledChecked;
76
- const state = isChecked ? "checked" : "unchecked";
77
-
78
- const toggle = () => {
79
- if (disabled) return;
80
- const next = !isChecked;
81
- if (!isControlled) setUncontrolledChecked(next);
82
- onCheckedChange?.(next);
83
- };
84
-
85
- const switchControl = (
86
- <button
87
- ref={ref}
88
- id={switchId}
89
- type="button"
90
- role="switch"
91
- aria-checked={isChecked}
92
- aria-labelledby={label ? `${switchId}-label` : undefined}
93
- aria-describedby={description ? `${switchId}-description` : undefined}
94
- disabled={disabled}
95
- data-state={state}
96
- className={cn(switchTrackVariants({ size }), className)}
97
- onClick={(event) => {
98
- toggle();
99
- onClick?.(event);
100
- }}
101
- onKeyDown={(event) => {
102
- if (event.key === " ") {
103
- event.preventDefault();
104
- toggle();
105
- }
106
- }}
107
- {...props}
108
- >
109
- <span data-state={state} className={switchThumbVariants({ size })} />
110
- </button>
111
- );
112
-
113
- if (!label && !description) {
114
- return switchControl;
115
- }
116
-
117
- return (
118
- <div
119
- className={cn(
120
- "flex items-start justify-between gap-4",
121
- disabled && "opacity-60",
122
- )}
123
- >
124
- <div className="flex min-w-0 flex-col gap-0.5">
125
- {label && (
126
- <label
127
- id={`${switchId}-label`}
128
- htmlFor={switchId}
129
- className={cn(
130
- "text-sm font-medium leading-snug text-zinc-800 dark:text-zinc-100",
131
- !disabled && "cursor-pointer",
132
- )}
133
- >
134
- {label}
135
- </label>
136
- )}
137
- {description && (
138
- <p
139
- id={`${switchId}-description`}
140
- className="text-xs leading-relaxed text-zinc-500 dark:text-zinc-400"
141
- >
142
- {description}
143
- </p>
144
- )}
145
- </div>
146
- {switchControl}
147
- </div>
148
- );
149
- },
150
- );
151
- Switch.displayName = "Switch";
152
-
153
- export { Switch, switchTrackVariants, switchThumbVariants };
@@ -1,58 +0,0 @@
1
- "use client";
2
-
3
- import React, { useState } from "react";
4
- import { ChevronDown, ChevronUp } from "lucide-react";
5
-
6
- export function Table({ children, className = "", ...props }: React.HTMLAttributes<HTMLTableElement>) {
7
- return (
8
- <div className="w-full overflow-auto">
9
- <table className={`w-full caption-bottom text-sm ${className}`} {...props}>{children}</table>
10
- </div>
11
- );
12
- }
13
-
14
- export function TableHeader({ children, className = "", ...props }: React.HTMLAttributes<HTMLTableSectionElement>) {
15
- return <thead className={`border-b border-zinc-200 bg-zinc-50/50 dark:border-zinc-800 dark:bg-zinc-900/50 ${className}`} {...props}>{children}</thead>;
16
- }
17
-
18
- export function TableBody({ children, className = "", ...props }: React.HTMLAttributes<HTMLTableSectionElement>) {
19
- return <tbody className={`divide-y divide-zinc-200 dark:divide-zinc-800 ${className}`} {...props}>{children}</tbody>;
20
- }
21
-
22
- export function TableRow({ children, className = "", ...props }: React.HTMLAttributes<HTMLTableRowElement>) {
23
- return <tr className={`transition-colors hover:bg-zinc-50/50 dark:hover:bg-zinc-900/50 ${className}`} {...props}>{children}</tr>;
24
- }
25
-
26
- export function TableHead({ children, className = "", ...props }: React.ThHTMLAttributes<HTMLTableHeaderCellElement>) {
27
- return (
28
- <th className={`h-12 px-4 text-left align-middle font-semibold text-zinc-500 dark:text-zinc-400 uppercase text-[11px] tracking-wider ${className}`} {...props}>
29
- {children}
30
- </th>
31
- );
32
- }
33
-
34
- export function TableCell({ children, className = "", ...props }: React.TdHTMLAttributes<HTMLTableCellElement>) {
35
- return <td className={`p-4 align-middle text-zinc-700 dark:text-zinc-300 ${className}`} {...props}>{children}</td>;
36
- }
37
-
38
- export function ExpandableRow({ title, details, children, colSpan = 10 }: { title: React.ReactNode; details: React.ReactNode; children?: React.ReactNode; colSpan?: number }) {
39
- const [isExpanded, setIsExpanded] = useState(false);
40
-
41
- return (
42
- <>
43
- <TableRow className="cursor-pointer" onClick={() => setIsExpanded(!isExpanded)}>
44
- <TableCell className="w-10 text-zinc-400">
45
- {isExpanded ? <ChevronUp size={16} /> : <ChevronDown size={16} />}
46
- </TableCell>
47
- {children}
48
- </TableRow>
49
- {isExpanded && (
50
- <tr className="bg-zinc-50/30 dark:bg-zinc-900/30">
51
- <TableCell colSpan={colSpan} className="p-6 border-l-2 border-zinc-900 dark:border-white">
52
- {details}
53
- </TableCell>
54
- </tr>
55
- )}
56
- </>
57
- );
58
- }
@@ -1,166 +0,0 @@
1
- "use client";
2
-
3
- import React, { createContext, useContext, useState } from "react";
4
- import { cn } from "@/lib/cn";
5
-
6
- type TabsContextType = {
7
- activeTab: string;
8
- setActiveTab: (value: string) => void;
9
- variant: string;
10
- uid: string;
11
- };
12
-
13
- const TabsContext = createContext<TabsContextType | null>(null);
14
-
15
- function useTabs() {
16
- const context = useContext(TabsContext);
17
- if (!context) return { activeTab: "", setActiveTab: () => {}, variant: "", uid: "" };
18
- return context;
19
- }
20
-
21
- export function Tabs({
22
- children,
23
- defaultValue,
24
- variant = "underline",
25
- className
26
- }: {
27
- children: React.ReactNode;
28
- defaultValue: string;
29
- variant?: "underline" | "pill" | "segmented" | "bordered" | "filled" | "icon" | "vertical" | "floating" | "scrollable" | "card";
30
- className?: string;
31
- }) {
32
- const [activeTab, setActiveTab] = useState(defaultValue);
33
- const uid = React.useId();
34
- return (
35
- <TabsContext.Provider value={{ activeTab, setActiveTab, variant, uid }}>
36
- <div className={cn(variant === "vertical" ? "flex gap-8" : "flex flex-col gap-4", className)}>
37
- {children}
38
- </div>
39
- </TabsContext.Provider>
40
- );
41
- }
42
-
43
- export function TabsList({ children, className }: { children: React.ReactNode; className?: string }) {
44
- const { variant } = useTabs();
45
-
46
- const variantStyles: Record<string, string> = {
47
- underline: "flex min-w-0 gap-4 overflow-x-auto border-b border-zinc-200 dark:border-zinc-800 sm:gap-8 [-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
48
- pill: "flex min-w-0 flex-wrap gap-2",
49
- segmented: "inline-flex max-w-full flex-wrap p-1 bg-zinc-100 dark:bg-zinc-900 rounded-xl gap-1",
50
- bordered: "flex min-w-0 flex-wrap gap-3",
51
- filled: "flex min-w-0 flex-wrap gap-1 bg-zinc-50 dark:bg-zinc-900/50 p-1.5 rounded-lg",
52
- icon: "flex min-w-0 flex-wrap gap-6 sm:gap-10",
53
- vertical: "flex flex-col border-l border-zinc-100 dark:border-zinc-800",
54
- floating: "inline-flex max-w-full flex-wrap bg-white dark:bg-zinc-950 shadow-xl border border-zinc-100 dark:border-zinc-800 p-2 rounded-2xl gap-2",
55
- scrollable: "flex min-w-0 gap-6 overflow-x-auto pb-2 docs-scrollbar whitespace-nowrap",
56
- card: "flex min-w-0 gap-1 overflow-x-auto border-b border-zinc-100 dark:border-zinc-800 [-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
57
- };
58
-
59
- return (
60
- <div
61
- role="tablist"
62
- aria-orientation={variant === "vertical" ? "vertical" : "horizontal"}
63
- className={cn(variantStyles[variant], className)}
64
- >
65
- {children}
66
- </div>
67
- );
68
- }
69
-
70
- export function TabsTrigger({
71
- value,
72
- label,
73
- icon: Icon,
74
- badge,
75
- className
76
- }: {
77
- value: string;
78
- label: string;
79
- icon?: any;
80
- badge?: string | number;
81
- className?: string;
82
- }) {
83
- const { activeTab, setActiveTab, variant, uid } = useTabs();
84
- const isActive = activeTab === value;
85
-
86
- const triggerStyles: Record<string, string> = {
87
- underline: cn(
88
- "relative pb-3 text-sm font-medium transition-all",
89
- isActive ? "text-zinc-900 dark:text-white" : "text-zinc-500 hover:text-zinc-900 dark:hover:text-zinc-100"
90
- ),
91
- pill: cn(
92
- "px-4 py-2 rounded-full text-sm font-semibold transition-all",
93
- isActive ? "bg-zinc-900 text-white dark:bg-white dark:text-zinc-900 shadow-lg" : "bg-white dark:bg-zinc-900 text-zinc-600 border border-zinc-100 dark:border-zinc-800 hover:bg-zinc-50"
94
- ),
95
- segmented: cn(
96
- "px-6 py-1.5 rounded-lg text-xs font-bold transition-all",
97
- isActive ? "bg-white dark:bg-zinc-800 text-zinc-900 dark:text-white shadow-sm" : "text-zinc-500 hover:text-zinc-700"
98
- ),
99
- bordered: cn(
100
- "px-4 py-2 rounded-lg border-2 text-sm font-bold transition-all",
101
- isActive ? "border-zinc-900 text-zinc-900 dark:border-white dark:text-white" : "border-transparent text-zinc-400 hover:border-zinc-200"
102
- ),
103
- filled: cn(
104
- "px-4 py-2 rounded-md text-sm font-medium transition-all",
105
- isActive ? "bg-zinc-900 text-white dark:bg-white dark:text-zinc-900" : "text-zinc-500 hover:text-zinc-800 dark:hover:text-zinc-200"
106
- ),
107
- card: cn(
108
- "relative shrink-0 px-4 py-2.5 text-sm font-medium transition-all",
109
- isActive ? "text-zinc-900 dark:text-white" : "text-zinc-500 hover:text-zinc-800 dark:hover:text-zinc-200"
110
- ),
111
- icon: cn(
112
- "flex flex-col items-center gap-1.5 transition-all",
113
- isActive ? "text-zinc-900 dark:text-white" : "text-zinc-400 hover:text-zinc-700"
114
- ),
115
- vertical: cn(
116
- "pl-6 pr-12 py-2 text-sm font-medium border-l-2 -ml-[2px] transition-all",
117
- isActive ? "border-zinc-900 text-zinc-900 dark:border-white dark:text-white bg-zinc-50 dark:bg-zinc-900" : "border-transparent text-zinc-500 hover:text-zinc-800"
118
- ),
119
- floating: cn(
120
- "px-4 py-2 rounded-xl text-sm font-medium transition-all",
121
- isActive ? "bg-zinc-900 text-white dark:bg-white dark:text-zinc-900" : "text-zinc-500 hover:bg-zinc-50"
122
- )
123
- };
124
-
125
- return (
126
- <button
127
- type="button"
128
- role="tab"
129
- id={`${uid}-tab-${value}`}
130
- aria-selected={isActive}
131
- aria-controls={`${uid}-panel-${value}`}
132
- tabIndex={isActive ? 0 : -1}
133
- onClick={() => setActiveTab(value)}
134
- className={cn("shrink-0 outline-none", triggerStyles[variant] || triggerStyles['underline'], className)}
135
- >
136
- <div className="flex items-center gap-2">
137
- {Icon && <Icon size={variant === "icon" ? 20 : 16} />}
138
- <span>{label}</span>
139
- {badge && (
140
- <span className={cn("flex h-4 w-4 items-center justify-center rounded-full text-[10px] text-white", isActive ? "bg-zinc-800 dark:bg-zinc-700" : "bg-red-500")}>
141
- {badge}
142
- </span>
143
- )}
144
- </div>
145
- {variant === "underline" && isActive && (
146
- <div className="absolute bottom-0 left-0 h-[2.5px] w-full bg-zinc-900 dark:bg-white rounded-full animate-in fade-in" />
147
- )}
148
- </button>
149
- );
150
- }
151
-
152
- export function TabsContent({ value, children, className }: { value: string; children: React.ReactNode; className?: string }) {
153
- const { activeTab, uid } = useTabs();
154
- if (activeTab !== value) return null;
155
- return (
156
- <div
157
- role="tabpanel"
158
- id={`${uid}-panel-${value}`}
159
- aria-labelledby={`${uid}-tab-${value}`}
160
- tabIndex={0}
161
- className={cn("outline-none animate-in fade-in slide-in-from-bottom-2 duration-300", className)}
162
- >
163
- {children}
164
- </div>
165
- );
166
- }