ruler-components 0.1.1 → 0.1.3

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 +458 -0
  2. package/dist/index.d.ts +458 -0
  3. package/dist/index.js +3618 -0
  4. package/dist/index.js.map +1 -0
  5. package/dist/index.mjs +3467 -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,689 +0,0 @@
1
- "use client";
2
-
3
- import React, { useState } from "react";
4
- import { Table2, User, ArrowUpRight, MoreHorizontal, Check, X, Copy } from "lucide-react";
5
- import { Table, TableHeader, TableBody, TableRow, TableHead, TableCell, ExpandableRow } from "@/components/ui/table";
6
- import { Badge, type BadgeProps } from "@/components/ui/badge";
7
- import { Card } from "@/components/ui/card";
8
- import { DataTable, type DataTableColumn } from "@/components/ui/data-table";
9
- import { Pagination } from "@/components/ui/pagination";
10
- import { Tooltip } from "@/components/ui/tooltip";
11
- import {
12
- DocsAccessibility,
13
- DocsApiSection,
14
- DocsApiTable,
15
- DocsPage,
16
- DocsPageHeader,
17
- DocsSection,
18
- DocsSectionGrid,
19
- DocsSimpleTable,
20
- DocsSubsection,
21
- } from "@/components/docs/docs-page";
22
- import { cn } from "@/lib/cn";
23
-
24
- interface Member {
25
- name: string;
26
- email: string;
27
- role: "Admin" | "Editor" | "Viewer";
28
- status: "Active" | "Invited" | "Suspended";
29
- projects: number;
30
- joined: string;
31
- }
32
-
33
- function statusVariant(status: string): BadgeProps["variant"] {
34
- switch (status) {
35
- case "Active":
36
- case "Paid":
37
- return "success";
38
- case "Invited":
39
- return "info";
40
- case "Pending":
41
- case "Paused":
42
- return "warning";
43
- case "Suspended":
44
- case "Overdue":
45
- case "Error":
46
- return "error";
47
- default:
48
- return "default";
49
- }
50
- }
51
-
52
- interface Invoice {
53
- id: string;
54
- customer: string;
55
- amount: string;
56
- status: "Paid" | "Pending" | "Overdue";
57
- }
58
-
59
- const INVOICES: Invoice[] = [
60
- { id: "INV-1042", customer: "Bright Path Studio", amount: "$1,240.00", status: "Paid" },
61
- { id: "INV-1041", customer: "Northwind Traders", amount: "$860.50", status: "Paid" },
62
- { id: "INV-1040", customer: "Vertex Analytics", amount: "$2,310.00", status: "Pending" },
63
- { id: "INV-1039", customer: "Solstice Media", amount: "$540.00", status: "Paid" },
64
- { id: "INV-1038", customer: "Anchor & Co.", amount: "$3,120.75", status: "Overdue" },
65
- { id: "INV-1037", customer: "Glide Robotics", amount: "$975.20", status: "Paid" },
66
- { id: "INV-1036", customer: "Fable Interiors", amount: "$410.00", status: "Pending" },
67
- { id: "INV-1035", customer: "Harbor Logistics", amount: "$1,890.00", status: "Paid" },
68
- { id: "INV-1034", customer: "Quiet Forge", amount: "$260.00", status: "Overdue" },
69
- { id: "INV-1033", customer: "Lumen Health", amount: "$4,050.00", status: "Paid" },
70
- { id: "INV-1032", customer: "Cedar & Vine", amount: "$725.00", status: "Pending" },
71
- { id: "INV-1031", customer: "Pace Robotics", amount: "$1,150.00", status: "Paid" },
72
- ];
73
-
74
- const INVOICE_PAGE_SIZE = 5;
75
-
76
- const MEMBERS: Member[] = [
77
- { name: "Ava Whitfield", email: "ava@ruler.dev", role: "Admin", status: "Active", projects: 12, joined: "2023-02-14" },
78
- { name: "Noah Bianchi", email: "noah@ruler.dev", role: "Editor", status: "Active", projects: 8, joined: "2023-05-30" },
79
- { name: "Lena Kowalski", email: "lena@ruler.dev", role: "Viewer", status: "Invited", projects: 0, joined: "2024-01-09" },
80
- { name: "Marcus Reed", email: "marcus@ruler.dev", role: "Editor", status: "Active", projects: 15, joined: "2022-11-02" },
81
- { name: "Priya Nandakumar", email: "priya@ruler.dev", role: "Admin", status: "Active", projects: 21, joined: "2022-08-18" },
82
- { name: "Diego Ferreira", email: "diego@ruler.dev", role: "Viewer", status: "Suspended", projects: 3, joined: "2023-09-25" },
83
- { name: "Hana Suzuki", email: "hana@ruler.dev", role: "Editor", status: "Active", projects: 6, joined: "2024-03-11" },
84
- { name: "Owen Fitzgerald", email: "owen@ruler.dev", role: "Viewer", status: "Invited", projects: 1, joined: "2024-04-02" },
85
- { name: "Zara Ahmed", email: "zara@ruler.dev", role: "Admin", status: "Active", projects: 18, joined: "2022-06-07" },
86
- { name: "Leo Moretti", email: "leo@ruler.dev", role: "Editor", status: "Active", projects: 9, joined: "2023-07-19" },
87
- { name: "Ines Duarte", email: "ines@ruler.dev", role: "Viewer", status: "Suspended", projects: 2, joined: "2023-12-01" },
88
- { name: "Felix Bergman", email: "felix@ruler.dev", role: "Editor", status: "Active", projects: 11, joined: "2023-03-27" },
89
- ];
90
-
91
- const MEMBER_COLUMNS: DataTableColumn<Member>[] = [
92
- {
93
- key: "name",
94
- header: "Member",
95
- sortable: true,
96
- render: (row) => (
97
- <div className="flex items-center gap-3">
98
- <div className="flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-zinc-100 text-xs font-bold text-zinc-600 dark:bg-zinc-800 dark:text-zinc-300">
99
- {row.name.split(" ").map((part) => part[0]).join("")}
100
- </div>
101
- <div>
102
- <p className="text-sm font-semibold text-zinc-900 dark:text-white">{row.name}</p>
103
- <p className="text-xs text-zinc-500 dark:text-zinc-400">{row.email}</p>
104
- </div>
105
- </div>
106
- ),
107
- },
108
- { key: "role", header: "Role", filterOptions: ["Admin", "Editor", "Viewer"] },
109
- {
110
- key: "status",
111
- header: "Status",
112
- filterOptions: ["Active", "Invited", "Suspended"],
113
- render: (row) => <Badge variant={statusVariant(row.status)}>{row.status}</Badge>,
114
- },
115
- { key: "projects", header: "Projects", sortable: true, align: "right" },
116
- {
117
- key: "joined",
118
- header: "Joined",
119
- sortable: true,
120
- render: (row) => new Date(row.joined).toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" }),
121
- },
122
- ];
123
-
124
- const DATA_TABLE_API_ROWS = [
125
- { prop: "data", type: "T[]", default: "—", description: "Array of row objects to render" },
126
- { prop: "columns", type: "DataTableColumn<T>[]", default: "—", description: "Column definitions — sorting, filtering, alignment, and custom rendering" },
127
- { prop: "searchPlaceholder", type: "string", default: '"Search..."', description: "Placeholder text for the built-in search input" },
128
- { prop: "pageSize", type: "number", default: "5", description: "Number of rows shown per page" },
129
- { prop: "className", type: "string", default: "—", description: "Additional CSS classes for the outer container" },
130
- ];
131
-
132
- const SUBCOMPONENT_ROWS = [
133
- { name: "Table", description: "Root scroll container and <table> element." },
134
- { name: "TableHeader", description: "Wraps the header row(s) in a <thead>." },
135
- { name: "TableBody", description: "Wraps data rows in a <tbody> with row dividers." },
136
- { name: "TableRow", description: "A single row with a subtle hover state." },
137
- { name: "TableHead", description: "A header cell with uppercase, muted styling." },
138
- { name: "TableCell", description: "A standard data cell." },
139
- { name: "ExpandableRow", description: "A row that toggles open to reveal extra details." },
140
- ];
141
-
142
- const ACCESSIBILITY_ITEMS = [
143
- "Use TableHead cells for column headers so screen readers announce them correctly.",
144
- "Keep an accessible label on icon-only actions like row menus and pagination controls.",
145
- "Don't rely on color alone to convey status — pair badges with text.",
146
- "Expandable rows should reflect their open/closed state to assistive tech.",
147
- "Prefer numeric alignment (text-right) for scannable, comparable metrics.",
148
- ];
149
-
150
- interface Product {
151
- name: string;
152
- category: string;
153
- status: "Active" | "Paused" | "Error";
154
- price: string;
155
- }
156
-
157
- const PRODUCTS: Product[] = [
158
- { name: "Aurora Desk Lamp", category: "Lighting", status: "Active", price: "$48.00" },
159
- { name: "Cedar Notebook", category: "Stationery", status: "Active", price: "$14.00" },
160
- { name: "Mono Backpack", category: "Bags", status: "Paused", price: "$92.00" },
161
- { name: "Slate Water Bottle", category: "Outdoor", status: "Active", price: "$22.00" },
162
- { name: "Nimbus Headphones", category: "Audio", status: "Error", price: "$128.00" },
163
- { name: "Birch Wall Clock", category: "Home", status: "Active", price: "$36.00" },
164
- { name: "Ridge Trail Tent", category: "Outdoor", status: "Paused", price: "$210.00" },
165
- { name: "Quartz Desk Mat", category: "Office", status: "Active", price: "$18.00" },
166
- ];
167
-
168
- const ROW_COUNTS = [3, 5, 8] as const;
169
- type RowCount = (typeof ROW_COUNTS)[number];
170
-
171
- function playgroundCodeFor(rowCount: RowCount, expandable: boolean) {
172
- if (expandable) {
173
- return `<Table>
174
- <TableHeader>
175
- <TableRow>
176
- <TableHead></TableHead>
177
- <TableHead>Product</TableHead>
178
- <TableHead>Category</TableHead>
179
- <TableHead>Status</TableHead>
180
- </TableRow>
181
- </TableHeader>
182
- <TableBody>
183
- <ExpandableRow
184
- title="${PRODUCTS[0].name}"
185
- details={<div>Price: ${PRODUCTS[0].price}</div>}
186
- >
187
- <TableCell className="font-medium">${PRODUCTS[0].name}</TableCell>
188
- <TableCell>${PRODUCTS[0].category}</TableCell>
189
- <TableCell>
190
- <Badge variant="success">${PRODUCTS[0].status}</Badge>
191
- </TableCell>
192
- </ExpandableRow>
193
- {/* ...${rowCount - 1} more rows */}
194
- </TableBody>
195
- </Table>`;
196
- }
197
-
198
- return `<Table>
199
- <TableHeader>
200
- <TableRow>
201
- <TableHead>Product</TableHead>
202
- <TableHead>Category</TableHead>
203
- <TableHead>Status</TableHead>
204
- <TableHead className="text-right">Price</TableHead>
205
- </TableRow>
206
- </TableHeader>
207
- <TableBody>
208
- <TableRow>
209
- <TableCell className="font-medium">${PRODUCTS[0].name}</TableCell>
210
- <TableCell>${PRODUCTS[0].category}</TableCell>
211
- <TableCell>
212
- <Badge variant="success">${PRODUCTS[0].status}</Badge>
213
- </TableCell>
214
- <TableCell className="text-right font-mono">${PRODUCTS[0].price}</TableCell>
215
- </TableRow>
216
- {/* ...${rowCount - 1} more rows */}
217
- </TableBody>
218
- </Table>`;
219
- }
220
-
221
- export function TableDocPage() {
222
- const data = [
223
- { channel: "Direct", sessions: "39,443", bounce: "38.47%", status: "Active" },
224
- { channel: "Organic Search", sessions: "18,199", bounce: "37.06%", status: "Paused" },
225
- { channel: "Social", sessions: "13,268", bounce: "55.54%", status: "Error" },
226
- ];
227
-
228
- const [invoicePage, setInvoicePage] = React.useState(1);
229
- const invoiceTotalPages = Math.ceil(INVOICES.length / INVOICE_PAGE_SIZE);
230
- const pagedInvoices = INVOICES.slice(
231
- (invoicePage - 1) * INVOICE_PAGE_SIZE,
232
- invoicePage * INVOICE_PAGE_SIZE,
233
- );
234
-
235
- const [rowCount, setRowCount] = useState<RowCount>(5);
236
- const [expandable, setExpandable] = useState(false);
237
- const [copied, setCopied] = useState(false);
238
-
239
- const playgroundRows = PRODUCTS.slice(0, rowCount);
240
- const playgroundSnippet = playgroundCodeFor(rowCount, expandable);
241
-
242
- function copyPlaygroundSnippet() {
243
- navigator.clipboard.writeText(playgroundSnippet).catch(() => {});
244
- setCopied(true);
245
- setTimeout(() => setCopied(false), 1200);
246
- }
247
-
248
- return (
249
- <DocsPage>
250
- <DocsPageHeader
251
- icon={Table2}
252
- title="Table"
253
- description="Full-width data tables for every use case — from a fully interactive data grid to lightweight static styles."
254
- />
255
-
256
- <DocsSection
257
- first
258
- title="Playground"
259
- description="Compose a table from the real building blocks and copy the exact markup — live."
260
- >
261
- <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)]">
262
- <div className="relative flex min-h-[18rem] items-start justify-center overflow-hidden bg-zinc-50 p-6 dark:bg-zinc-900/40">
263
- <div
264
- aria-hidden
265
- className="pointer-events-none absolute inset-0 opacity-[0.4] dark:opacity-[0.15]"
266
- style={{
267
- backgroundImage:
268
- "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)",
269
- backgroundSize: "24px 24px",
270
- }}
271
- />
272
- <div
273
- key={`${rowCount}-${expandable}`}
274
- className="relative w-full animate-[pagination-pop_0.2s_ease-out] overflow-hidden rounded-xl border border-zinc-200 bg-white dark:border-zinc-800 dark:bg-zinc-950"
275
- >
276
- <Table>
277
- <TableHeader>
278
- <TableRow>
279
- {expandable && <TableHead></TableHead>}
280
- <TableHead>Product</TableHead>
281
- <TableHead>Category</TableHead>
282
- <TableHead>Status</TableHead>
283
- {!expandable && <TableHead className="text-right">Price</TableHead>}
284
- </TableRow>
285
- </TableHeader>
286
- <TableBody>
287
- {expandable
288
- ? playgroundRows.map((p) => (
289
- <ExpandableRow
290
- key={p.name}
291
- title={p.name}
292
- colSpan={3}
293
- details={
294
- <div className="text-zinc-500 dark:text-zinc-400">Price: {p.price}</div>
295
- }
296
- >
297
- <TableCell className="font-medium">{p.name}</TableCell>
298
- <TableCell>{p.category}</TableCell>
299
- <TableCell>
300
- <Badge variant={statusVariant(p.status)}>{p.status}</Badge>
301
- </TableCell>
302
- </ExpandableRow>
303
- ))
304
- : playgroundRows.map((p) => (
305
- <TableRow key={p.name}>
306
- <TableCell className="font-medium">{p.name}</TableCell>
307
- <TableCell>{p.category}</TableCell>
308
- <TableCell>
309
- <Badge variant={statusVariant(p.status)}>{p.status}</Badge>
310
- </TableCell>
311
- <TableCell className="text-right font-mono">{p.price}</TableCell>
312
- </TableRow>
313
- ))}
314
- </TableBody>
315
- </Table>
316
- </div>
317
- </div>
318
-
319
- <div className="flex flex-col gap-5 border-t border-zinc-200 p-5 dark:border-zinc-800">
320
- <div className="flex flex-wrap items-start justify-between gap-6">
321
- <div>
322
- <p className="mb-2 text-xs font-medium uppercase tracking-wide text-zinc-400 dark:text-zinc-500">
323
- Row count
324
- </p>
325
- <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">
326
- {ROW_COUNTS.map((n) => (
327
- <button
328
- key={n}
329
- type="button"
330
- onClick={() => setRowCount(n)}
331
- className={cn(
332
- "rounded-md px-3 py-1.5 text-xs font-medium uppercase tracking-wide transition-colors",
333
- rowCount === n
334
- ? "bg-white text-zinc-900 shadow-sm dark:bg-zinc-800 dark:text-white"
335
- : "text-zinc-500 hover:text-zinc-800 dark:text-zinc-400 dark:hover:text-zinc-200"
336
- )}
337
- >
338
- {n}
339
- </button>
340
- ))}
341
- </div>
342
- </div>
343
-
344
- <div>
345
- <p className="mb-2 text-xs font-medium uppercase tracking-wide text-zinc-400 dark:text-zinc-500">
346
- Expandable rows
347
- </p>
348
- <button
349
- type="button"
350
- onClick={() => setExpandable((v) => !v)}
351
- className={cn(
352
- "flex h-[2.125rem] items-center gap-2 rounded-lg border px-3 text-xs font-medium transition-colors",
353
- expandable
354
- ? "border-zinc-900 bg-zinc-900 text-white dark:border-white dark:bg-white dark:text-zinc-900"
355
- : "border-zinc-200 text-zinc-500 hover:border-zinc-300 dark:border-zinc-700 dark:text-zinc-400"
356
- )}
357
- >
358
- {expandable ? "On" : "Off"}
359
- </button>
360
- </div>
361
- </div>
362
-
363
- <div className="flex items-center justify-between gap-3 rounded-xl bg-zinc-900 px-4 py-3 dark:bg-black">
364
- <pre className="overflow-x-auto font-mono text-[13px] leading-relaxed text-zinc-100">
365
- <code>{playgroundSnippet}</code>
366
- </pre>
367
- <Tooltip content={copied ? "Copied!" : "Copy code"}>
368
- <button
369
- type="button"
370
- onClick={copyPlaygroundSnippet}
371
- 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"
372
- >
373
- {copied ? <Check className="h-4 w-4 text-emerald-400" /> : <Copy className="h-4 w-4" />}
374
- </button>
375
- </Tooltip>
376
- </div>
377
- </div>
378
- </div>
379
- </DocsSection>
380
-
381
- <DocsSection
382
- title="Data Table"
383
- description="A complete, self-contained table with search, sortable columns, dropdown filters, and pagination."
384
- >
385
- <Card className="p-6">
386
- <DataTable data={MEMBERS} columns={MEMBER_COLUMNS} searchPlaceholder="Search members..." pageSize={5} />
387
- </Card>
388
- </DocsSection>
389
-
390
- <DocsSection title="Basic & Striped" description="Standard rows, or zebra-striping for easier scanning.">
391
- <DocsSectionGrid>
392
- <DocsSubsection title="Basic">
393
- <Table>
394
- <TableHeader>
395
- <TableRow>
396
- <TableHead>Channel</TableHead>
397
- <TableHead>Sessions</TableHead>
398
- <TableHead>Bounce Rate</TableHead>
399
- </TableRow>
400
- </TableHeader>
401
- <TableBody>
402
- {data.map((r) => (
403
- <TableRow key={r.channel}>
404
- <TableCell className="font-medium">{r.channel}</TableCell>
405
- <TableCell>{r.sessions}</TableCell>
406
- <TableCell>{r.bounce}</TableCell>
407
- </TableRow>
408
- ))}
409
- </TableBody>
410
- </Table>
411
- </DocsSubsection>
412
-
413
- <DocsSubsection title="Striped">
414
- <Table className="[&_tr:nth-child(even)]:bg-zinc-50 dark:[&_tr:nth-child(even)]:bg-zinc-900/50">
415
- <TableHeader>
416
- <TableRow>
417
- <TableHead>Channel</TableHead>
418
- <TableHead>Sessions</TableHead>
419
- </TableRow>
420
- </TableHeader>
421
- <TableBody>
422
- {data.map((r) => (
423
- <TableRow key={r.channel}>
424
- <TableCell>{r.channel}</TableCell>
425
- <TableCell>{r.sessions}</TableCell>
426
- </TableRow>
427
- ))}
428
- </TableBody>
429
- </Table>
430
- </DocsSubsection>
431
- </DocsSectionGrid>
432
- </DocsSection>
433
-
434
- <DocsSection title="Bordered & Compact" description="Full cell borders for clarity, or tight padding for dense data.">
435
- <DocsSectionGrid>
436
- <DocsSubsection title="Bordered">
437
- <div className="overflow-hidden rounded-xl border border-zinc-200 dark:border-zinc-800">
438
- <Table className="[&_td]:border-r [&_th]:border-r last:[&_td]:border-r-0 last:[&_th]:border-r-0">
439
- <TableHeader>
440
- <TableRow>
441
- <TableHead>Channel</TableHead>
442
- <TableHead>Sessions</TableHead>
443
- <TableHead>Bounce Rate</TableHead>
444
- </TableRow>
445
- </TableHeader>
446
- <TableBody>
447
- {data.map((r) => (
448
- <TableRow key={r.channel}>
449
- <TableCell className="font-medium">{r.channel}</TableCell>
450
- <TableCell>{r.sessions}</TableCell>
451
- <TableCell>{r.bounce}</TableCell>
452
- </TableRow>
453
- ))}
454
- </TableBody>
455
- </Table>
456
- </div>
457
- </DocsSubsection>
458
-
459
- <DocsSubsection title="Compact">
460
- <Table>
461
- <TableBody>
462
- {data.map((r) => (
463
- <TableRow key={r.channel}>
464
- <TableCell className="py-1.5">{r.channel}</TableCell>
465
- <TableCell className="py-1.5 text-right font-mono">{r.sessions}</TableCell>
466
- </TableRow>
467
- ))}
468
- </TableBody>
469
- </Table>
470
- </DocsSubsection>
471
- </DocsSectionGrid>
472
- </DocsSection>
473
-
474
- <DocsSection title="Paginated Table" description="Slice a larger dataset into pages with simple, reusable pagination controls.">
475
- <div className="overflow-hidden rounded-xl border border-zinc-200 dark:border-zinc-800">
476
- <Table>
477
- <TableHeader>
478
- <TableRow>
479
- <TableHead>Invoice</TableHead>
480
- <TableHead>Customer</TableHead>
481
- <TableHead className="text-right">Amount</TableHead>
482
- <TableHead>Status</TableHead>
483
- </TableRow>
484
- </TableHeader>
485
- <TableBody>
486
- {pagedInvoices.map((invoice) => (
487
- <TableRow key={invoice.id}>
488
- <TableCell className="font-mono text-xs text-zinc-500 dark:text-zinc-400">{invoice.id}</TableCell>
489
- <TableCell className="font-medium">{invoice.customer}</TableCell>
490
- <TableCell className="text-right font-mono">{invoice.amount}</TableCell>
491
- <TableCell>
492
- <Badge variant={statusVariant(invoice.status)}>{invoice.status}</Badge>
493
- </TableCell>
494
- </TableRow>
495
- ))}
496
- </TableBody>
497
- </Table>
498
- </div>
499
-
500
- <div className="flex flex-col-reverse items-center justify-between gap-3 pt-4 sm:flex-row">
501
- <p className="text-xs text-zinc-500 dark:text-zinc-400">
502
- Showing {(invoicePage - 1) * INVOICE_PAGE_SIZE + 1}–
503
- {Math.min(invoicePage * INVOICE_PAGE_SIZE, INVOICES.length)} of {INVOICES.length}
504
- </p>
505
- <Pagination page={invoicePage} totalPages={invoiceTotalPages} onPageChange={setInvoicePage} />
506
- </div>
507
- </DocsSection>
508
-
509
- <DocsSection title="Minimal & Comparison" description="A borderless style for quiet lists, or side-by-side plan comparisons.">
510
- <DocsSectionGrid>
511
- <DocsSubsection title="Minimal">
512
- <Table className="border-none">
513
- <TableBody>
514
- {data.map((r) => (
515
- <TableRow key={r.channel} className="hover:bg-transparent">
516
- <TableCell className="px-0 font-medium">{r.channel}</TableCell>
517
- <TableCell className="px-0 text-right opacity-70">{r.sessions}</TableCell>
518
- </TableRow>
519
- ))}
520
- </TableBody>
521
- </Table>
522
- </DocsSubsection>
523
-
524
- <DocsSubsection title="Comparison">
525
- <Table className="border border-zinc-200 dark:border-zinc-800">
526
- <TableHeader>
527
- <TableRow>
528
- <TableHead className="w-[30%]">Feature</TableHead>
529
- <TableHead className="bg-zinc-50 text-center dark:bg-zinc-900">Personal</TableHead>
530
- <TableHead className="text-center">Team</TableHead>
531
- </TableRow>
532
- </TableHeader>
533
- <TableBody>
534
- <TableRow>
535
- <TableCell>API access</TableCell>
536
- <TableCell className="text-center">
537
- <X size={16} className="mx-auto text-red-500" />
538
- </TableCell>
539
- <TableCell className="text-center">
540
- <Check size={16} className="mx-auto text-emerald-500" />
541
- </TableCell>
542
- </TableRow>
543
- <TableRow>
544
- <TableCell>Custom domain</TableCell>
545
- <TableCell className="text-center">
546
- <Check size={16} className="mx-auto text-emerald-500" />
547
- </TableCell>
548
- <TableCell className="text-center">
549
- <Check size={16} className="mx-auto text-emerald-500" />
550
- </TableCell>
551
- </TableRow>
552
- </TableBody>
553
- </Table>
554
- </DocsSubsection>
555
- </DocsSectionGrid>
556
- </DocsSection>
557
-
558
- <DocsSection title="Expandable Rows" description="Toggle a row open to reveal more detailed information.">
559
- <Table>
560
- <TableHeader>
561
- <TableRow>
562
- <TableHead></TableHead>
563
- <TableHead>Project Name</TableHead>
564
- <TableHead>Status</TableHead>
565
- </TableRow>
566
- </TableHeader>
567
- <TableBody>
568
- <ExpandableRow
569
- title="Project X"
570
- details={
571
- <div className="p-2 text-zinc-500 dark:text-zinc-400">
572
- Project internal ID: #9901. Team: Analytics. Priority: High.
573
- </div>
574
- }
575
- >
576
- <TableCell className="font-semibold">Project X</TableCell>
577
- <TableCell>
578
- <Badge variant="success">Active</Badge>
579
- </TableCell>
580
- </ExpandableRow>
581
- <ExpandableRow
582
- title="Project Y"
583
- details={
584
- <div className="p-2 text-zinc-500 dark:text-zinc-400">
585
- Project internal ID: #9902. Team: Marketing. Priority: Low.
586
- </div>
587
- }
588
- >
589
- <TableCell className="font-semibold">Project Y</TableCell>
590
- <TableCell>
591
- <Badge variant="warning">Pending</Badge>
592
- </TableCell>
593
- </ExpandableRow>
594
- </TableBody>
595
- </Table>
596
- </DocsSection>
597
-
598
- <DocsSection title="Status Table" description="Highlight state changes across rows using badges.">
599
- <Table>
600
- <TableHeader>
601
- <TableRow>
602
- <TableHead>Channel</TableHead>
603
- <TableHead>Status</TableHead>
604
- <TableHead>Sessions</TableHead>
605
- </TableRow>
606
- </TableHeader>
607
- <TableBody>
608
- {data.map((r) => (
609
- <TableRow key={r.channel}>
610
- <TableCell className="font-medium">{r.channel}</TableCell>
611
- <TableCell>
612
- <Badge variant={statusVariant(r.status)}>{r.status}</Badge>
613
- </TableCell>
614
- <TableCell>{r.sessions}</TableCell>
615
- </TableRow>
616
- ))}
617
- </TableBody>
618
- </Table>
619
- </DocsSection>
620
-
621
- <DocsSection title="Card Table" description="A table contained within a rounded, elevated surface.">
622
- <Card className="overflow-hidden">
623
- <Table>
624
- <TableHeader>
625
- <TableRow>
626
- <TableHead>Source</TableHead>
627
- <TableHead className="text-right">Value</TableHead>
628
- </TableRow>
629
- </TableHeader>
630
- <TableBody>
631
- {data.map((r) => (
632
- <TableRow key={r.channel}>
633
- <TableCell>{r.channel}</TableCell>
634
- <TableCell className="text-right">{r.sessions}</TableCell>
635
- </TableRow>
636
- ))}
637
- </TableBody>
638
- </Table>
639
- </Card>
640
- </DocsSection>
641
-
642
- <DocsSection title="Dashboard Table" description="A rich dashboard view with avatars, growth metrics, and row actions.">
643
- <Card className="overflow-hidden">
644
- <Table>
645
- <TableHeader>
646
- <TableRow>
647
- <TableHead>Project Lead</TableHead>
648
- <TableHead>Growth</TableHead>
649
- <TableHead>Status</TableHead>
650
- <TableHead className="text-right">Action</TableHead>
651
- </TableRow>
652
- </TableHeader>
653
- <TableBody>
654
- {data.map((r) => (
655
- <TableRow key={r.channel}>
656
- <TableCell>
657
- <div className="flex items-center gap-3">
658
- <div className="flex h-8 w-8 items-center justify-center rounded-full bg-zinc-100 text-zinc-500 dark:bg-zinc-800">
659
- <User size={14} />
660
- </div>
661
- <span className="text-sm font-semibold text-zinc-900 dark:text-white">{r.channel}</span>
662
- </div>
663
- </TableCell>
664
- <TableCell>
665
- <span className="flex items-center gap-1 text-sm font-bold text-emerald-500">
666
- +14.5% <ArrowUpRight size={14} />
667
- </span>
668
- </TableCell>
669
- <TableCell>
670
- <Badge variant={statusVariant(r.status)}>{r.status}</Badge>
671
- </TableCell>
672
- <TableCell className="text-right">
673
- <MoreHorizontal size={16} className="ml-auto cursor-pointer opacity-50 hover:opacity-100" />
674
- </TableCell>
675
- </TableRow>
676
- ))}
677
- </TableBody>
678
- </Table>
679
- </Card>
680
- </DocsSection>
681
-
682
- <DocsApiSection>
683
- <DocsApiTable title="API Reference — DataTable" rows={DATA_TABLE_API_ROWS} />
684
- <DocsSimpleTable title="Building Blocks" rows={SUBCOMPONENT_ROWS} />
685
- <DocsAccessibility items={ACCESSIBILITY_ITEMS} />
686
- </DocsApiSection>
687
- </DocsPage>
688
- );
689
- }