ruler-components 0.1.0 → 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.
- package/dist/index.d.mts +51 -0
- package/dist/index.d.ts +51 -0
- package/dist/index.js +534 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +498 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +21 -2
- package/AGENTS.md +0 -9
- package/CLAUDE.md +0 -1
- package/app/docs/[slug]/page.tsx +0 -147
- package/app/docs/layout.tsx +0 -26
- package/app/docs/page.tsx +0 -148
- package/app/favicon.ico +0 -0
- package/app/globals.css +0 -168
- package/app/icon.png +0 -0
- package/app/layout.tsx +0 -90
- package/app/opengraph-image.tsx +0 -10
- package/app/page.tsx +0 -34
- package/app/twitter-image.tsx +0 -10
- package/components/docs/accordion-doc-page.tsx +0 -352
- package/components/docs/alert-doc-page.tsx +0 -293
- package/components/docs/allinput-doc-page.tsx +0 -183
- package/components/docs/avatar-doc-page.tsx +0 -424
- package/components/docs/badge-doc-page.tsx +0 -363
- package/components/docs/button-doc-page.tsx +0 -406
- package/components/docs/card-doc-page.tsx +0 -364
- package/components/docs/checkbox-doc-page.tsx +0 -277
- package/components/docs/colors-doc-page.tsx +0 -430
- package/components/docs/dialog-doc-page.tsx +0 -364
- package/components/docs/docs-footer.tsx +0 -11
- package/components/docs/docs-mobile-menu-bar.tsx +0 -22
- package/components/docs/docs-mobile-nav-context.tsx +0 -27
- package/components/docs/docs-page.tsx +0 -263
- package/components/docs/docs-search.tsx +0 -303
- package/components/docs/docs-sidebar.tsx +0 -165
- package/components/docs/docs-topbar.tsx +0 -32
- package/components/docs/dropdown-doc-page.tsx +0 -522
- package/components/docs/getting-started-doc-page.tsx +0 -205
- package/components/docs/label-doc-page.tsx +0 -173
- package/components/docs/loaders-doc-page.tsx +0 -244
- package/components/docs/menu-doc-page.tsx +0 -373
- package/components/docs/modal-doc-page.tsx +0 -436
- package/components/docs/next-steps.tsx +0 -53
- package/components/docs/pagination-doc-page.tsx +0 -316
- package/components/docs/progress-doc-page.tsx +0 -387
- package/components/docs/radio-doc-page.tsx +0 -270
- package/components/docs/skeleton-doc-page.tsx +0 -230
- package/components/docs/stepper-doc-page.tsx +0 -309
- package/components/docs/switch-doc-page.tsx +0 -264
- package/components/docs/table-doc-page.tsx +0 -689
- package/components/docs/tabs-doc-page.tsx +0 -359
- package/components/docs/theming-doc-page.tsx +0 -243
- package/components/docs/tooltip-doc-page.tsx +0 -357
- package/components/docs/usage-section.tsx +0 -21
- package/components/icons/discord.tsx +0 -9
- package/components/icons/github.tsx +0 -9
- package/components/icons/x.tsx +0 -9
- package/components/installation-section.tsx +0 -34
- package/components/landing/feature-highlights.tsx +0 -105
- package/components/landing/footer-strip.tsx +0 -45
- package/components/landing/hero-card.tsx +0 -14
- package/components/landing/hero-spotlight.tsx +0 -126
- package/components/landing/hero.tsx +0 -64
- package/components/landing/navbar.tsx +0 -44
- package/components/landing/quick-start-section.tsx +0 -59
- package/components/landing/site-footer.tsx +0 -55
- package/components/theme-provider.tsx +0 -60
- package/components/ui/accordion.tsx +0 -174
- package/components/ui/alert.tsx +0 -103
- package/components/ui/allinput.tsx +0 -210
- package/components/ui/avatar.tsx +0 -179
- package/components/ui/badge.tsx +0 -52
- package/components/ui/button.tsx +0 -75
- package/components/ui/card.tsx +0 -105
- package/components/ui/checkbox.tsx +0 -130
- package/components/ui/code-block.tsx +0 -60
- package/components/ui/data-table.tsx +0 -235
- package/components/ui/dialog.tsx +0 -173
- package/components/ui/dropdown.tsx +0 -78
- package/components/ui/feature-card.tsx +0 -38
- package/components/ui/input.tsx +0 -44
- package/components/ui/label.tsx +0 -110
- package/components/ui/loaders.tsx +0 -139
- package/components/ui/menu.tsx +0 -118
- package/components/ui/modal.tsx +0 -399
- package/components/ui/package-manager-tabs.tsx +0 -40
- package/components/ui/pagination.tsx +0 -200
- package/components/ui/progress.tsx +0 -134
- package/components/ui/radio.tsx +0 -262
- package/components/ui/skeleton.tsx +0 -102
- package/components/ui/steppers.tsx +0 -309
- package/components/ui/switch.tsx +0 -153
- package/components/ui/table.tsx +0 -58
- package/components/ui/tabs.tsx +0 -166
- package/components/ui/theme-toggle.tsx +0 -60
- package/components/ui/tooltip.tsx +0 -238
- package/data/color-palette.ts +0 -35
- package/data/docs-nav.ts +0 -84
- package/eslint.config.mjs +0 -18
- package/index.ts +0 -8
- package/lib/cn.ts +0 -4
- package/lib/color.ts +0 -85
- package/lib/og-image.tsx +0 -138
- package/lib/theme.ts +0 -33
- package/next.config.ts +0 -7
- package/postcss.config.mjs +0 -7
- package/public/dark.png +0 -0
- package/public/dark1.png +0 -0
- package/public/dark2.png +0 -0
- package/public/file.svg +0 -1
- package/public/globe.svg +0 -1
- package/public/light.png +0 -0
- package/public/light1.png +0 -0
- package/public/next.svg +0 -1
- package/public/vercel.svg +0 -1
- package/public/video/hero-demo.mp4 +0 -0
- package/public/window.svg +0 -1
- package/tsconfig.json +0 -34
|
@@ -1,387 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import React, { useState } from "react";
|
|
4
|
-
import { Check, Copy, GaugeCircle, HardDrive, Sparkles, Upload } from "lucide-react";
|
|
5
|
-
import { Progress, type ProgressProps } from "@/components/ui/progress";
|
|
6
|
-
import { Tooltip } from "@/components/ui/tooltip";
|
|
7
|
-
import { Badge } from "@/components/ui/badge";
|
|
8
|
-
import {
|
|
9
|
-
Card,
|
|
10
|
-
CardContent,
|
|
11
|
-
CardDescription,
|
|
12
|
-
CardHeader,
|
|
13
|
-
CardTitle,
|
|
14
|
-
} from "@/components/ui/card";
|
|
15
|
-
import {
|
|
16
|
-
DocsAccessibility,
|
|
17
|
-
DocsApiSection,
|
|
18
|
-
DocsApiTable,
|
|
19
|
-
DocsPage,
|
|
20
|
-
DocsPageHeader,
|
|
21
|
-
DocsSection,
|
|
22
|
-
DocsSectionGrid,
|
|
23
|
-
DocsSubsection,
|
|
24
|
-
} from "@/components/docs/docs-page";
|
|
25
|
-
import { cn } from "@/lib/cn";
|
|
26
|
-
|
|
27
|
-
const API_ROWS = [
|
|
28
|
-
{ prop: "value", type: "number", default: "0", description: "Current progress value" },
|
|
29
|
-
{ prop: "max", type: "number", default: "100", description: "Maximum progress value" },
|
|
30
|
-
{ prop: "variant", type: '"default" | "success" | "warning" | "error"', default: '"default"', description: "Visual variant of the indicator" },
|
|
31
|
-
{ prop: "size", type: '"sm" | "md" | "lg"', default: '"md"', description: "Height of the progress track" },
|
|
32
|
-
{ prop: "label", type: "string", default: "—", description: "Optional label above the bar" },
|
|
33
|
-
{ prop: "showValue", type: "boolean", default: "false", description: "Shows percentage next to the label" },
|
|
34
|
-
{ prop: "indeterminate", type: "boolean", default: "false", description: "Animated loading state" },
|
|
35
|
-
{ prop: "disabled", type: "boolean", default: "false", description: "Reduces opacity and marks as inactive" },
|
|
36
|
-
];
|
|
37
|
-
|
|
38
|
-
const ACCESSIBILITY_ITEMS = [
|
|
39
|
-
"Uses role=\"progressbar\" with aria-valuenow, aria-valuemin, and aria-valuemax.",
|
|
40
|
-
"Provide a label when context isn't clear from surrounding content.",
|
|
41
|
-
"Use indeterminate mode only when completion is unknown.",
|
|
42
|
-
"Pair color with labels or percentage text.",
|
|
43
|
-
];
|
|
44
|
-
|
|
45
|
-
type Variant = NonNullable<ProgressProps["variant"]>;
|
|
46
|
-
type Size = NonNullable<ProgressProps["size"]>;
|
|
47
|
-
|
|
48
|
-
const VARIANTS: { value: Variant; label: string; swatch: string }[] = [
|
|
49
|
-
{ value: "default", label: "Default", swatch: "bg-zinc-900 dark:bg-zinc-100" },
|
|
50
|
-
{ value: "success", label: "Success", swatch: "bg-[#28A745]" },
|
|
51
|
-
{ value: "warning", label: "Warning", swatch: "bg-[#E58E00]" },
|
|
52
|
-
{ value: "error", label: "Error", swatch: "bg-[#D93025]" },
|
|
53
|
-
];
|
|
54
|
-
|
|
55
|
-
const SIZES: Size[] = ["sm", "md", "lg"];
|
|
56
|
-
|
|
57
|
-
function codeFor(variant: Variant, size: Size, value: number, showValue: boolean, indeterminate: boolean) {
|
|
58
|
-
const props = [
|
|
59
|
-
variant !== "default" && `variant="${variant}"`,
|
|
60
|
-
size !== "md" && `size="${size}"`,
|
|
61
|
-
!indeterminate && `value={${value}}`,
|
|
62
|
-
indeterminate && "indeterminate",
|
|
63
|
-
showValue && !indeterminate && "showValue",
|
|
64
|
-
`label="${indeterminate ? "Syncing data..." : "Uploading"}"`,
|
|
65
|
-
]
|
|
66
|
-
.filter(Boolean)
|
|
67
|
-
.join("\n ");
|
|
68
|
-
return `<Progress\n ${props}\n/>`;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export function ProgressDocPage() {
|
|
72
|
-
const [uploadProgress, setUploadProgress] = React.useState(42);
|
|
73
|
-
|
|
74
|
-
React.useEffect(() => {
|
|
75
|
-
const interval = setInterval(() => {
|
|
76
|
-
setUploadProgress((prev) => (prev >= 100 ? 42 : prev + 6));
|
|
77
|
-
}, 500);
|
|
78
|
-
return () => clearInterval(interval);
|
|
79
|
-
}, []);
|
|
80
|
-
|
|
81
|
-
const [variant, setVariant] = useState<Variant>("default");
|
|
82
|
-
const [size, setSize] = useState<Size>("md");
|
|
83
|
-
const [value, setValue] = useState(65);
|
|
84
|
-
const [showValue, setShowValue] = useState(true);
|
|
85
|
-
const [indeterminate, setIndeterminate] = useState(false);
|
|
86
|
-
const [copied, setCopied] = useState(false);
|
|
87
|
-
|
|
88
|
-
const snippet = codeFor(variant, size, value, showValue, indeterminate);
|
|
89
|
-
|
|
90
|
-
function copySnippet() {
|
|
91
|
-
navigator.clipboard.writeText(snippet).catch(() => {});
|
|
92
|
-
setCopied(true);
|
|
93
|
-
setTimeout(() => setCopied(false), 1200);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
return (
|
|
97
|
-
<DocsPage>
|
|
98
|
-
<DocsPageHeader
|
|
99
|
-
icon={GaugeCircle}
|
|
100
|
-
title="Progress"
|
|
101
|
-
description="Displays completion status for tasks, uploads, and loading states."
|
|
102
|
-
/>
|
|
103
|
-
|
|
104
|
-
<DocsSection
|
|
105
|
-
first
|
|
106
|
-
title="Playground"
|
|
107
|
-
description="Build a progress bar and copy the exact markup — every combination, live."
|
|
108
|
-
>
|
|
109
|
-
<div className="overflow-hidden rounded-2xl border border-zinc-200 shadow-[0_2px_8px_rgba(0,0,0,0.04)] dark:border-zinc-800 dark:shadow-[0_2px_8px_rgba(0,0,0,0.2)]">
|
|
110
|
-
<div className="relative flex min-h-[11rem] items-center justify-center overflow-hidden bg-zinc-50 p-8 dark:bg-zinc-900/40">
|
|
111
|
-
<div
|
|
112
|
-
aria-hidden
|
|
113
|
-
className="pointer-events-none absolute inset-0 opacity-[0.4] dark:opacity-[0.15]"
|
|
114
|
-
style={{
|
|
115
|
-
backgroundImage:
|
|
116
|
-
"linear-gradient(to right, rgb(161 161 170 / 0.35) 1px, transparent 1px), linear-gradient(to bottom, rgb(161 161 170 / 0.35) 1px, transparent 1px)",
|
|
117
|
-
backgroundSize: "24px 24px",
|
|
118
|
-
}}
|
|
119
|
-
/>
|
|
120
|
-
<div className="relative w-full max-w-sm" key={`${variant}-${size}-${indeterminate}`}>
|
|
121
|
-
<Progress
|
|
122
|
-
variant={variant}
|
|
123
|
-
size={size}
|
|
124
|
-
value={value}
|
|
125
|
-
showValue={showValue}
|
|
126
|
-
indeterminate={indeterminate}
|
|
127
|
-
label={indeterminate ? "Syncing data..." : "Uploading"}
|
|
128
|
-
className="animate-[pagination-pop_0.25s_ease-out]"
|
|
129
|
-
/>
|
|
130
|
-
</div>
|
|
131
|
-
</div>
|
|
132
|
-
|
|
133
|
-
<div className="flex flex-col gap-5 border-t border-zinc-200 p-5 dark:border-zinc-800">
|
|
134
|
-
<div className="flex flex-wrap items-start justify-between gap-6">
|
|
135
|
-
<div>
|
|
136
|
-
<p className="mb-2 text-xs font-medium uppercase tracking-wide text-zinc-400 dark:text-zinc-500">
|
|
137
|
-
Variant
|
|
138
|
-
</p>
|
|
139
|
-
<div className="flex flex-wrap gap-1.5">
|
|
140
|
-
{VARIANTS.map((v) => (
|
|
141
|
-
<Tooltip key={v.value} content={v.label}>
|
|
142
|
-
<button
|
|
143
|
-
type="button"
|
|
144
|
-
onClick={() => setVariant(v.value)}
|
|
145
|
-
aria-label={v.label}
|
|
146
|
-
className={cn(
|
|
147
|
-
"flex h-8 w-8 items-center justify-center rounded-lg border transition-all",
|
|
148
|
-
variant === v.value
|
|
149
|
-
? "border-zinc-900 ring-2 ring-zinc-900 ring-offset-2 ring-offset-zinc-50 dark:border-white dark:ring-white dark:ring-offset-zinc-900"
|
|
150
|
-
: "border-zinc-200 hover:border-zinc-300 dark:border-zinc-700 dark:hover:border-zinc-600"
|
|
151
|
-
)}
|
|
152
|
-
>
|
|
153
|
-
<span className={cn("h-3.5 w-3.5 rounded-full", v.swatch)} />
|
|
154
|
-
</button>
|
|
155
|
-
</Tooltip>
|
|
156
|
-
))}
|
|
157
|
-
</div>
|
|
158
|
-
</div>
|
|
159
|
-
|
|
160
|
-
<div>
|
|
161
|
-
<p className="mb-2 text-xs font-medium uppercase tracking-wide text-zinc-400 dark:text-zinc-500">
|
|
162
|
-
Size
|
|
163
|
-
</p>
|
|
164
|
-
<div className="flex flex-wrap items-center gap-1 rounded-lg border border-zinc-200 bg-zinc-50 p-1 dark:border-zinc-800 dark:bg-zinc-900/60">
|
|
165
|
-
{SIZES.map((s) => (
|
|
166
|
-
<button
|
|
167
|
-
key={s}
|
|
168
|
-
type="button"
|
|
169
|
-
onClick={() => setSize(s)}
|
|
170
|
-
className={cn(
|
|
171
|
-
"rounded-md px-3 py-1.5 text-xs font-medium uppercase tracking-wide transition-colors",
|
|
172
|
-
size === s
|
|
173
|
-
? "bg-white text-zinc-900 shadow-sm dark:bg-zinc-800 dark:text-white"
|
|
174
|
-
: "text-zinc-500 hover:text-zinc-800 dark:text-zinc-400 dark:hover:text-zinc-200"
|
|
175
|
-
)}
|
|
176
|
-
>
|
|
177
|
-
{s}
|
|
178
|
-
</button>
|
|
179
|
-
))}
|
|
180
|
-
</div>
|
|
181
|
-
</div>
|
|
182
|
-
|
|
183
|
-
<div>
|
|
184
|
-
<p className="mb-2 text-xs font-medium uppercase tracking-wide text-zinc-400 dark:text-zinc-500">
|
|
185
|
-
Value
|
|
186
|
-
</p>
|
|
187
|
-
<input
|
|
188
|
-
type="range"
|
|
189
|
-
min={0}
|
|
190
|
-
max={100}
|
|
191
|
-
value={value}
|
|
192
|
-
disabled={indeterminate}
|
|
193
|
-
onChange={(e) => setValue(Number(e.target.value))}
|
|
194
|
-
className="h-[2.125rem] w-32 accent-zinc-900 disabled:opacity-40 dark:accent-zinc-100"
|
|
195
|
-
/>
|
|
196
|
-
</div>
|
|
197
|
-
|
|
198
|
-
<div className="flex gap-2">
|
|
199
|
-
<button
|
|
200
|
-
type="button"
|
|
201
|
-
onClick={() => setShowValue((v) => !v)}
|
|
202
|
-
className={cn(
|
|
203
|
-
"flex h-[2.125rem] items-center gap-2 rounded-lg border px-3 text-xs font-medium transition-colors",
|
|
204
|
-
showValue
|
|
205
|
-
? "border-zinc-900 bg-zinc-900 text-white dark:border-white dark:bg-white dark:text-zinc-900"
|
|
206
|
-
: "border-zinc-200 text-zinc-500 hover:border-zinc-300 dark:border-zinc-700 dark:text-zinc-400"
|
|
207
|
-
)}
|
|
208
|
-
>
|
|
209
|
-
{showValue ? "Value on" : "Value off"}
|
|
210
|
-
</button>
|
|
211
|
-
<button
|
|
212
|
-
type="button"
|
|
213
|
-
onClick={() => setIndeterminate((v) => !v)}
|
|
214
|
-
className={cn(
|
|
215
|
-
"flex h-[2.125rem] items-center gap-2 rounded-lg border px-3 text-xs font-medium transition-colors",
|
|
216
|
-
indeterminate
|
|
217
|
-
? "border-zinc-900 bg-zinc-900 text-white dark:border-white dark:bg-white dark:text-zinc-900"
|
|
218
|
-
: "border-zinc-200 text-zinc-500 hover:border-zinc-300 dark:border-zinc-700 dark:text-zinc-400"
|
|
219
|
-
)}
|
|
220
|
-
>
|
|
221
|
-
{indeterminate ? "Indeterminate" : "Determinate"}
|
|
222
|
-
</button>
|
|
223
|
-
</div>
|
|
224
|
-
</div>
|
|
225
|
-
|
|
226
|
-
<div className="flex items-center justify-between gap-3 rounded-xl bg-zinc-900 px-4 py-3 dark:bg-black">
|
|
227
|
-
<pre className="overflow-x-auto font-mono text-[13px] leading-relaxed text-zinc-100">
|
|
228
|
-
<code>{snippet}</code>
|
|
229
|
-
</pre>
|
|
230
|
-
<Tooltip content={copied ? "Copied!" : "Copy code"}>
|
|
231
|
-
<button
|
|
232
|
-
type="button"
|
|
233
|
-
onClick={copySnippet}
|
|
234
|
-
className="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg text-zinc-400 transition-colors hover:bg-white/10 hover:text-white"
|
|
235
|
-
>
|
|
236
|
-
{copied ? <Check className="h-4 w-4 text-emerald-400" /> : <Copy className="h-4 w-4" />}
|
|
237
|
-
</button>
|
|
238
|
-
</Tooltip>
|
|
239
|
-
</div>
|
|
240
|
-
</div>
|
|
241
|
-
</div>
|
|
242
|
-
</DocsSection>
|
|
243
|
-
|
|
244
|
-
<DocsSection>
|
|
245
|
-
<DocsSectionGrid>
|
|
246
|
-
<DocsSubsection
|
|
247
|
-
title="Default"
|
|
248
|
-
description="Clean track with smooth fill animation and inline percentage."
|
|
249
|
-
>
|
|
250
|
-
<Card className="h-full">
|
|
251
|
-
<CardHeader>
|
|
252
|
-
<div className="flex items-center justify-between gap-3">
|
|
253
|
-
<div>
|
|
254
|
-
<CardTitle>Profile completion</CardTitle>
|
|
255
|
-
<CardDescription>Finish setting up your account</CardDescription>
|
|
256
|
-
</div>
|
|
257
|
-
<Badge variant="info">In progress</Badge>
|
|
258
|
-
</div>
|
|
259
|
-
</CardHeader>
|
|
260
|
-
<CardContent>
|
|
261
|
-
<Progress value={68} label="Overall progress" showValue />
|
|
262
|
-
</CardContent>
|
|
263
|
-
</Card>
|
|
264
|
-
</DocsSubsection>
|
|
265
|
-
|
|
266
|
-
<DocsSubsection
|
|
267
|
-
title="Basic Usage"
|
|
268
|
-
description="Common states you'll reach for out of the box."
|
|
269
|
-
>
|
|
270
|
-
<div className="grid gap-5">
|
|
271
|
-
<Progress value={65} />
|
|
272
|
-
<Progress value={80} variant="success" showValue />
|
|
273
|
-
<Progress value={45} label="Uploading..." showValue />
|
|
274
|
-
<Progress indeterminate label="Syncing data..." />
|
|
275
|
-
</div>
|
|
276
|
-
</DocsSubsection>
|
|
277
|
-
</DocsSectionGrid>
|
|
278
|
-
</DocsSection>
|
|
279
|
-
|
|
280
|
-
<DocsSection>
|
|
281
|
-
<DocsSectionGrid>
|
|
282
|
-
<DocsSubsection
|
|
283
|
-
title="Variants"
|
|
284
|
-
description="Color-coded states for different statuses."
|
|
285
|
-
>
|
|
286
|
-
<div className="grid gap-4">
|
|
287
|
-
<Progress value={60} variant="default" label="Default" showValue />
|
|
288
|
-
<Progress value={85} variant="success" label="Completed" showValue />
|
|
289
|
-
<Progress value={55} variant="warning" label="Storage almost full" showValue />
|
|
290
|
-
<Progress value={22} variant="error" label="Upload failed" showValue />
|
|
291
|
-
</div>
|
|
292
|
-
</DocsSubsection>
|
|
293
|
-
|
|
294
|
-
<DocsSubsection
|
|
295
|
-
title="Onboarding Checklist"
|
|
296
|
-
description="A real-world composition of labeled, color-coded steps."
|
|
297
|
-
>
|
|
298
|
-
<Card className="h-full">
|
|
299
|
-
<CardHeader>
|
|
300
|
-
<div className="flex items-center gap-3">
|
|
301
|
-
<div className="flex h-10 w-10 items-center justify-center rounded-xl bg-[#28A745]/10 text-[#28A745] dark:bg-[#28A745]/20 dark:text-[#5CC975]">
|
|
302
|
-
<Sparkles className="h-5 w-5" />
|
|
303
|
-
</div>
|
|
304
|
-
<div>
|
|
305
|
-
<CardTitle>Onboarding</CardTitle>
|
|
306
|
-
<CardDescription>Complete setup to unlock features</CardDescription>
|
|
307
|
-
</div>
|
|
308
|
-
</div>
|
|
309
|
-
</CardHeader>
|
|
310
|
-
<CardContent className="space-y-4">
|
|
311
|
-
<Progress value={100} variant="success" label="Create account" showValue />
|
|
312
|
-
<Progress value={100} variant="success" label="Verify email" showValue />
|
|
313
|
-
<Progress value={40} variant="default" label="Set up workspace" showValue />
|
|
314
|
-
<Progress value={0} variant="default" label="Invite team" showValue />
|
|
315
|
-
</CardContent>
|
|
316
|
-
</Card>
|
|
317
|
-
</DocsSubsection>
|
|
318
|
-
</DocsSectionGrid>
|
|
319
|
-
</DocsSection>
|
|
320
|
-
|
|
321
|
-
<DocsSection>
|
|
322
|
-
<DocsSectionGrid>
|
|
323
|
-
<DocsSubsection title="Sizes">
|
|
324
|
-
<div className="grid gap-4">
|
|
325
|
-
<Progress value={70} size="sm" label="Small" showValue />
|
|
326
|
-
<Progress value={70} size="md" label="Medium" showValue />
|
|
327
|
-
<Progress value={70} size="lg" label="Large" showValue />
|
|
328
|
-
</div>
|
|
329
|
-
</DocsSubsection>
|
|
330
|
-
<DocsSubsection title="Disabled">
|
|
331
|
-
<Card className="flex h-full flex-col justify-center">
|
|
332
|
-
<CardContent className="pt-6">
|
|
333
|
-
<Progress value={50} label="Paused upload" showValue disabled />
|
|
334
|
-
</CardContent>
|
|
335
|
-
</Card>
|
|
336
|
-
</DocsSubsection>
|
|
337
|
-
</DocsSectionGrid>
|
|
338
|
-
</DocsSection>
|
|
339
|
-
|
|
340
|
-
<DocsSection>
|
|
341
|
-
<DocsSectionGrid>
|
|
342
|
-
<Card>
|
|
343
|
-
<CardHeader>
|
|
344
|
-
<div className="flex items-center gap-3">
|
|
345
|
-
<div className="flex h-10 w-10 items-center justify-center rounded-xl bg-zinc-100 dark:bg-zinc-800">
|
|
346
|
-
<Upload className="h-5 w-5 text-zinc-600 dark:text-zinc-300" />
|
|
347
|
-
</div>
|
|
348
|
-
<div className="min-w-0 flex-1">
|
|
349
|
-
<CardTitle>File Upload</CardTitle>
|
|
350
|
-
<CardDescription className="truncate">design-system-v2.fig · 24.8 MB</CardDescription>
|
|
351
|
-
</div>
|
|
352
|
-
</div>
|
|
353
|
-
</CardHeader>
|
|
354
|
-
<CardContent>
|
|
355
|
-
<Progress value={uploadProgress} variant="default" label="Uploading" showValue />
|
|
356
|
-
</CardContent>
|
|
357
|
-
</Card>
|
|
358
|
-
|
|
359
|
-
<Card>
|
|
360
|
-
<CardHeader>
|
|
361
|
-
<div className="flex items-start justify-between gap-3">
|
|
362
|
-
<div className="flex items-center gap-3">
|
|
363
|
-
<div className="flex h-10 w-10 items-center justify-center rounded-xl bg-[#E58E00]/10 text-[#E58E00] dark:bg-[#E58E00]/20 dark:text-[#F5B84A]">
|
|
364
|
-
<HardDrive className="h-5 w-5" />
|
|
365
|
-
</div>
|
|
366
|
-
<div>
|
|
367
|
-
<CardTitle>Cloud Storage</CardTitle>
|
|
368
|
-
<CardDescription>7.2 GB of 10 GB used</CardDescription>
|
|
369
|
-
</div>
|
|
370
|
-
</div>
|
|
371
|
-
<Badge variant="warning">72%</Badge>
|
|
372
|
-
</div>
|
|
373
|
-
</CardHeader>
|
|
374
|
-
<CardContent>
|
|
375
|
-
<Progress value={72} variant="warning" size="lg" />
|
|
376
|
-
</CardContent>
|
|
377
|
-
</Card>
|
|
378
|
-
</DocsSectionGrid>
|
|
379
|
-
</DocsSection>
|
|
380
|
-
|
|
381
|
-
<DocsApiSection>
|
|
382
|
-
<DocsApiTable title="API Reference" rows={API_ROWS} />
|
|
383
|
-
<DocsAccessibility items={ACCESSIBILITY_ITEMS} />
|
|
384
|
-
</DocsApiSection>
|
|
385
|
-
</DocsPage>
|
|
386
|
-
);
|
|
387
|
-
}
|
|
@@ -1,270 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import React, { useState } from "react";
|
|
4
|
-
import { Check, CircleDot, Copy } from "lucide-react";
|
|
5
|
-
import { Radio, RadioGroup } from "@/components/ui/radio";
|
|
6
|
-
import { Tooltip } from "@/components/ui/tooltip";
|
|
7
|
-
import {
|
|
8
|
-
DocsAccessibility,
|
|
9
|
-
DocsApiSection,
|
|
10
|
-
DocsApiTable,
|
|
11
|
-
DocsPage,
|
|
12
|
-
DocsPageHeader,
|
|
13
|
-
DocsSection,
|
|
14
|
-
DocsSectionGrid,
|
|
15
|
-
DocsSubsection,
|
|
16
|
-
} from "@/components/docs/docs-page";
|
|
17
|
-
import { cn } from "@/lib/cn";
|
|
18
|
-
|
|
19
|
-
const API_ROWS = [
|
|
20
|
-
{ prop: "value", type: "string", default: "—", description: "The value of the radio option" },
|
|
21
|
-
{ prop: "label", type: "string", default: "—", description: "Label text displayed beside the radio" },
|
|
22
|
-
{ prop: "description", type: "string", default: "—", description: "Helper text below the label" },
|
|
23
|
-
{ prop: "variant", type: '"inline" | "card"', default: '"inline"', description: "Layout style — card auto-applies when description is set" },
|
|
24
|
-
{ prop: "disabled", type: "boolean", default: "false", description: "Disables the radio option" },
|
|
25
|
-
{ prop: "size", type: '"sm" | "md" | "lg"', default: '"md"', description: "The size of the radio button" },
|
|
26
|
-
{ prop: "className", type: "string", default: "—", description: "Additional CSS classes" },
|
|
27
|
-
];
|
|
28
|
-
|
|
29
|
-
const GROUP_API_ROWS = [
|
|
30
|
-
{ prop: "name", type: "string", default: "—", description: "Name attribute shared by all radios in the group" },
|
|
31
|
-
{ prop: "value", type: "string", default: "—", description: "Controlled selected value" },
|
|
32
|
-
{ prop: "defaultValue", type: "string", default: "—", description: "Uncontrolled initial selected value" },
|
|
33
|
-
{ prop: "onValueChange", type: "(value: string) => void", default: "—", description: "Callback when selection changes" },
|
|
34
|
-
{ prop: "variant", type: '"inline" | "card"', default: '"inline"', description: "Layout style for all options in the group" },
|
|
35
|
-
{ prop: "appearance", type: '"default" | "segmented"', default: '"default"', description: "Visual style — segmented for pill-style toggles" },
|
|
36
|
-
{ prop: "orientation", type: '"vertical" | "horizontal"', default: '"vertical"', description: "Layout direction of the group" },
|
|
37
|
-
{ prop: "disabled", type: "boolean", default: "false", description: "Disables all radios in the group" },
|
|
38
|
-
{ prop: "size", type: '"sm" | "md" | "lg"', default: '"md"', description: "Size applied to all radios in the group" },
|
|
39
|
-
];
|
|
40
|
-
|
|
41
|
-
const ACCESSIBILITY_ITEMS = [
|
|
42
|
-
"Group related radio buttons with a shared name attribute.",
|
|
43
|
-
"Only one option can be selected at a time within a group.",
|
|
44
|
-
"Provide a visible label for each radio option.",
|
|
45
|
-
"Use arrow keys to navigate between options in a group.",
|
|
46
|
-
"Ensure focus states are clearly visible.",
|
|
47
|
-
];
|
|
48
|
-
|
|
49
|
-
type Size = "sm" | "md" | "lg";
|
|
50
|
-
type Variant = "inline" | "card";
|
|
51
|
-
|
|
52
|
-
const SIZES: Size[] = ["sm", "md", "lg"];
|
|
53
|
-
|
|
54
|
-
const VARIANTS: { value: Variant; label: string }[] = [
|
|
55
|
-
{ value: "inline", label: "Inline" },
|
|
56
|
-
{ value: "card", label: "Card" },
|
|
57
|
-
];
|
|
58
|
-
|
|
59
|
-
function codeFor(variant: Variant, size: Size, disabled: boolean) {
|
|
60
|
-
const groupProps = [size !== "md" && `size="${size}"`, variant !== "inline" && `variant="${variant}"`]
|
|
61
|
-
.filter(Boolean)
|
|
62
|
-
.join(" ");
|
|
63
|
-
const open = groupProps ? `<RadioGroup name="demo" ${groupProps} defaultValue="option-1">` : `<RadioGroup name="demo" defaultValue="option-1">`;
|
|
64
|
-
const disabledAttr = disabled ? " disabled" : "";
|
|
65
|
-
const descriptionLine = variant === "card" ? '\n description="A helper line of detail."' : "";
|
|
66
|
-
return `${open}\n <Radio\n value="option-1"\n label="Option 1"${descriptionLine}${disabledAttr}\n />\n <Radio value="option-2" label="Option 2" />\n</RadioGroup>`;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export function RadioDocPage() {
|
|
70
|
-
const [plan, setPlan] = React.useState("pro");
|
|
71
|
-
|
|
72
|
-
const [variant, setVariant] = useState<Variant>("inline");
|
|
73
|
-
const [size, setSize] = useState<Size>("md");
|
|
74
|
-
const [disabled, setDisabled] = useState(false);
|
|
75
|
-
const [copied, setCopied] = useState(false);
|
|
76
|
-
|
|
77
|
-
const snippet = codeFor(variant, size, disabled);
|
|
78
|
-
|
|
79
|
-
function copySnippet() {
|
|
80
|
-
navigator.clipboard.writeText(snippet).catch(() => {});
|
|
81
|
-
setCopied(true);
|
|
82
|
-
setTimeout(() => setCopied(false), 1200);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
return (
|
|
86
|
-
<DocsPage>
|
|
87
|
-
<DocsPageHeader
|
|
88
|
-
icon={CircleDot}
|
|
89
|
-
title="Radio"
|
|
90
|
-
description="Allows users to select a single option from a mutually exclusive set of choices."
|
|
91
|
-
/>
|
|
92
|
-
|
|
93
|
-
<DocsSection
|
|
94
|
-
first
|
|
95
|
-
title="Playground"
|
|
96
|
-
description="Build a radio group and copy the exact markup — every combination, live."
|
|
97
|
-
>
|
|
98
|
-
<div className="overflow-hidden rounded-2xl border border-zinc-200 shadow-[0_2px_8px_rgba(0,0,0,0.04)] dark:border-zinc-800 dark:shadow-[0_2px_8px_rgba(0,0,0,0.2)]">
|
|
99
|
-
<div className="relative flex min-h-[11rem] items-center justify-center overflow-hidden bg-zinc-50 p-8 dark:bg-zinc-900/40">
|
|
100
|
-
<div
|
|
101
|
-
aria-hidden
|
|
102
|
-
className="pointer-events-none absolute inset-0 opacity-[0.4] dark:opacity-[0.15]"
|
|
103
|
-
style={{
|
|
104
|
-
backgroundImage:
|
|
105
|
-
"linear-gradient(to right, rgb(161 161 170 / 0.35) 1px, transparent 1px), linear-gradient(to bottom, rgb(161 161 170 / 0.35) 1px, transparent 1px)",
|
|
106
|
-
backgroundSize: "24px 24px",
|
|
107
|
-
}}
|
|
108
|
-
/>
|
|
109
|
-
<div className="relative w-full max-w-xs" key={`${variant}-${size}-${disabled}`}>
|
|
110
|
-
<RadioGroup
|
|
111
|
-
name="playground-demo"
|
|
112
|
-
variant={variant}
|
|
113
|
-
size={size}
|
|
114
|
-
disabled={disabled}
|
|
115
|
-
defaultValue="option-1"
|
|
116
|
-
className="animate-[pagination-pop_0.25s_ease-out]"
|
|
117
|
-
>
|
|
118
|
-
<Radio
|
|
119
|
-
value="option-1"
|
|
120
|
-
label="Option 1"
|
|
121
|
-
description={variant === "card" ? "A helper line of detail." : undefined}
|
|
122
|
-
className="items-center"
|
|
123
|
-
/>
|
|
124
|
-
<Radio value="option-2" label="Option 2" className="items-center" />
|
|
125
|
-
</RadioGroup>
|
|
126
|
-
</div>
|
|
127
|
-
</div>
|
|
128
|
-
|
|
129
|
-
<div className="flex flex-col gap-5 border-t border-zinc-200 p-5 dark:border-zinc-800">
|
|
130
|
-
<div className="flex flex-wrap items-start justify-between gap-6">
|
|
131
|
-
<div>
|
|
132
|
-
<p className="mb-2 text-xs font-medium uppercase tracking-wide text-zinc-400 dark:text-zinc-500">
|
|
133
|
-
Variant
|
|
134
|
-
</p>
|
|
135
|
-
<div className="flex flex-wrap items-center gap-1 rounded-lg border border-zinc-200 bg-zinc-50 p-1 dark:border-zinc-800 dark:bg-zinc-900/60">
|
|
136
|
-
{VARIANTS.map((v) => (
|
|
137
|
-
<button
|
|
138
|
-
key={v.value}
|
|
139
|
-
type="button"
|
|
140
|
-
onClick={() => setVariant(v.value)}
|
|
141
|
-
className={cn(
|
|
142
|
-
"rounded-md px-3 py-1.5 text-xs font-medium transition-colors",
|
|
143
|
-
variant === v.value
|
|
144
|
-
? "bg-white text-zinc-900 shadow-sm dark:bg-zinc-800 dark:text-white"
|
|
145
|
-
: "text-zinc-500 hover:text-zinc-800 dark:text-zinc-400 dark:hover:text-zinc-200"
|
|
146
|
-
)}
|
|
147
|
-
>
|
|
148
|
-
{v.label}
|
|
149
|
-
</button>
|
|
150
|
-
))}
|
|
151
|
-
</div>
|
|
152
|
-
</div>
|
|
153
|
-
|
|
154
|
-
<div>
|
|
155
|
-
<p className="mb-2 text-xs font-medium uppercase tracking-wide text-zinc-400 dark:text-zinc-500">
|
|
156
|
-
Size
|
|
157
|
-
</p>
|
|
158
|
-
<div className="flex flex-wrap items-center gap-1 rounded-lg border border-zinc-200 bg-zinc-50 p-1 dark:border-zinc-800 dark:bg-zinc-900/60">
|
|
159
|
-
{SIZES.map((s) => (
|
|
160
|
-
<button
|
|
161
|
-
key={s}
|
|
162
|
-
type="button"
|
|
163
|
-
onClick={() => setSize(s)}
|
|
164
|
-
className={cn(
|
|
165
|
-
"rounded-md px-3 py-1.5 text-xs font-medium uppercase tracking-wide transition-colors",
|
|
166
|
-
size === s
|
|
167
|
-
? "bg-white text-zinc-900 shadow-sm dark:bg-zinc-800 dark:text-white"
|
|
168
|
-
: "text-zinc-500 hover:text-zinc-800 dark:text-zinc-400 dark:hover:text-zinc-200"
|
|
169
|
-
)}
|
|
170
|
-
>
|
|
171
|
-
{s}
|
|
172
|
-
</button>
|
|
173
|
-
))}
|
|
174
|
-
</div>
|
|
175
|
-
</div>
|
|
176
|
-
|
|
177
|
-
<div>
|
|
178
|
-
<p className="mb-2 text-xs font-medium uppercase tracking-wide text-zinc-400 dark:text-zinc-500">
|
|
179
|
-
State
|
|
180
|
-
</p>
|
|
181
|
-
<button
|
|
182
|
-
type="button"
|
|
183
|
-
onClick={() => setDisabled((v) => !v)}
|
|
184
|
-
className={cn(
|
|
185
|
-
"flex h-[2.125rem] items-center gap-2 rounded-lg border px-3 text-xs font-medium transition-colors",
|
|
186
|
-
disabled
|
|
187
|
-
? "border-zinc-900 bg-zinc-900 text-white dark:border-white dark:bg-white dark:text-zinc-900"
|
|
188
|
-
: "border-zinc-200 text-zinc-500 hover:border-zinc-300 dark:border-zinc-700 dark:text-zinc-400"
|
|
189
|
-
)}
|
|
190
|
-
>
|
|
191
|
-
<CircleDot className="h-3.5 w-3.5" />
|
|
192
|
-
{disabled ? "Disabled" : "Enabled"}
|
|
193
|
-
</button>
|
|
194
|
-
</div>
|
|
195
|
-
</div>
|
|
196
|
-
|
|
197
|
-
<div className="flex items-center justify-between gap-3 rounded-xl bg-zinc-900 px-4 py-3 dark:bg-black">
|
|
198
|
-
<pre className="overflow-x-auto font-mono text-[13px] leading-relaxed text-zinc-100">
|
|
199
|
-
<code>{snippet}</code>
|
|
200
|
-
</pre>
|
|
201
|
-
<Tooltip content={copied ? "Copied!" : "Copy code"}>
|
|
202
|
-
<button
|
|
203
|
-
type="button"
|
|
204
|
-
onClick={copySnippet}
|
|
205
|
-
className="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg text-zinc-400 transition-colors hover:bg-white/10 hover:text-white"
|
|
206
|
-
>
|
|
207
|
-
{copied ? <Check className="h-4 w-4 text-emerald-400" /> : <Copy className="h-4 w-4" />}
|
|
208
|
-
</button>
|
|
209
|
-
</Tooltip>
|
|
210
|
-
</div>
|
|
211
|
-
</div>
|
|
212
|
-
</div>
|
|
213
|
-
</DocsSection>
|
|
214
|
-
|
|
215
|
-
<DocsSection>
|
|
216
|
-
<DocsSectionGrid>
|
|
217
|
-
<DocsSubsection title="Sizes">
|
|
218
|
-
<RadioGroup name="size-demo" defaultValue="md">
|
|
219
|
-
<Radio value="sm" label="Small" size="sm" />
|
|
220
|
-
<Radio value="md" label="Medium" size="md" />
|
|
221
|
-
<Radio value="lg" label="Large" size="lg" />
|
|
222
|
-
</RadioGroup>
|
|
223
|
-
</DocsSubsection>
|
|
224
|
-
<DocsSubsection title="Disabled">
|
|
225
|
-
<RadioGroup name="disabled-demo" defaultValue="enabled">
|
|
226
|
-
<Radio value="enabled" label="Enabled option" />
|
|
227
|
-
<Radio value="disabled" label="Disabled option" disabled />
|
|
228
|
-
<Radio value="another" label="Another option" />
|
|
229
|
-
</RadioGroup>
|
|
230
|
-
</DocsSubsection>
|
|
231
|
-
</DocsSectionGrid>
|
|
232
|
-
</DocsSection>
|
|
233
|
-
|
|
234
|
-
<DocsSection
|
|
235
|
-
title="Plan Selection"
|
|
236
|
-
description="Card-style radios are ideal for pricing tiers and feature comparisons."
|
|
237
|
-
>
|
|
238
|
-
<div className="flex max-w-md flex-col gap-2.5">
|
|
239
|
-
<RadioGroup name="plan-demo" variant="card" value={plan} onValueChange={setPlan}>
|
|
240
|
-
<Radio value="free" label="Free" description="Basic features for personal use." />
|
|
241
|
-
<Radio value="pro" label="Pro" description="Advanced features for professionals." />
|
|
242
|
-
<Radio value="enterprise" label="Enterprise" description="Custom solutions for large teams." />
|
|
243
|
-
</RadioGroup>
|
|
244
|
-
</div>
|
|
245
|
-
</DocsSection>
|
|
246
|
-
|
|
247
|
-
<DocsSection
|
|
248
|
-
title="Segmented Control"
|
|
249
|
-
description="Compact pill-style selector for switching between a few related options."
|
|
250
|
-
>
|
|
251
|
-
<RadioGroup
|
|
252
|
-
name="layout-demo"
|
|
253
|
-
defaultValue="daily"
|
|
254
|
-
orientation="horizontal"
|
|
255
|
-
appearance="segmented"
|
|
256
|
-
>
|
|
257
|
-
<Radio value="daily" label="Daily" />
|
|
258
|
-
<Radio value="weekly" label="Weekly" />
|
|
259
|
-
<Radio value="monthly" label="Monthly" />
|
|
260
|
-
</RadioGroup>
|
|
261
|
-
</DocsSection>
|
|
262
|
-
|
|
263
|
-
<DocsApiSection>
|
|
264
|
-
<DocsApiTable title="API Reference — Radio" rows={API_ROWS} />
|
|
265
|
-
<DocsApiTable title="API Reference — RadioGroup" rows={GROUP_API_ROWS} />
|
|
266
|
-
<DocsAccessibility items={ACCESSIBILITY_ITEMS} />
|
|
267
|
-
</DocsApiSection>
|
|
268
|
-
</DocsPage>
|
|
269
|
-
);
|
|
270
|
-
}
|