mirror of
https://github.com/shadcn-ui/ui.git
synced 2026-06-14 03:11:33 +00:00
Compare commits
27 Commits
shadcn-ui@
...
shadcn-ui@
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
df9369762a | ||
|
|
87ad14cb2a | ||
|
|
5a2ce61e2e | ||
|
|
4a7c07e754 | ||
|
|
8eb3e1e160 | ||
|
|
5bc68894b8 | ||
|
|
21890afa80 | ||
|
|
84b7200178 | ||
|
|
f8272baf07 | ||
|
|
9a6b934421 | ||
|
|
b3247d90a6 | ||
|
|
0c31f60bb0 | ||
|
|
be64c55901 | ||
|
|
b19199a35d | ||
|
|
6e67107170 | ||
|
|
71c631891f | ||
|
|
888a5ad6f6 | ||
|
|
d99b992e27 | ||
|
|
7dc67be1ad | ||
|
|
d528e9a45e | ||
|
|
2a61b54096 | ||
|
|
5b004f7565 | ||
|
|
5e915756d5 | ||
|
|
bfc66148ea | ||
|
|
2fa78133c8 | ||
|
|
3a60500d54 | ||
|
|
a5b313ab13 |
@@ -11,6 +11,7 @@ import { siteConfig } from "@/config/site"
|
||||
import { getTableOfContents } from "@/lib/toc"
|
||||
import { absoluteUrl, cn } from "@/lib/utils"
|
||||
import { badgeVariants } from "@/components/ui/badge"
|
||||
import { ScrollArea } from "@/components/ui/scroll-area"
|
||||
import { Separator } from "@/components/ui/separator"
|
||||
import { Icons } from "@/components/icons"
|
||||
import { Mdx } from "@/components/mdx-components"
|
||||
@@ -138,8 +139,10 @@ export default async function DocPage({ params }: DocPageProps) {
|
||||
<DocsPager doc={doc} />
|
||||
</div>
|
||||
<div className="hidden text-sm xl:block">
|
||||
<div className="sticky top-16 -mt-10 max-h-[calc(var(--vh)-4rem)] overflow-y-auto pt-6">
|
||||
<DashboardTableOfContents toc={toc} />
|
||||
<div className="sticky top-16 -mt-10 h-[calc(100vh-3.5rem)] overflow-hidden pt-6">
|
||||
<ScrollArea className="pb-10">
|
||||
<DashboardTableOfContents toc={toc} />
|
||||
</ScrollArea>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Card, CardContent } from "@/components/ui/card"
|
||||
import { Label } from "@/components/ui/label"
|
||||
import { CalendarDateRangePicker } from "@/components/examples/calendar/date-range-picker"
|
||||
import { DatePickerWithRange } from "@/components/examples/date-picker/with-range"
|
||||
|
||||
export function DemoDatePicker() {
|
||||
return (
|
||||
@@ -10,7 +10,7 @@ export function DemoDatePicker() {
|
||||
<Label htmlFor="date" className="shrink-0">
|
||||
Pick a date
|
||||
</Label>
|
||||
<CalendarDateRangePicker className="[&>button]:w-[260px]" />
|
||||
<DatePickerWithRange className="[&>button]:w-[260px]" />
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { Metadata } from "next"
|
||||
import Link from "next/link"
|
||||
import { ChevronRight } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { buttonVariants } from "@/components/ui/button"
|
||||
import { Separator } from "@/components/ui/separator"
|
||||
import { ExamplesNav } from "@/components/examples-nav"
|
||||
import {
|
||||
PageHeader,
|
||||
@@ -24,6 +26,14 @@ export default function ExamplesLayout({ children }: ExamplesLayoutProps) {
|
||||
<>
|
||||
<div className="container relative pb-10">
|
||||
<PageHeader className="page-header">
|
||||
<Link
|
||||
href="/docs/components/data-table"
|
||||
className="inline-flex items-center rounded-lg bg-muted px-3 py-1 text-sm font-medium"
|
||||
>
|
||||
🎉 <Separator className="mx-2 h-4" orientation="vertical" />{" "}
|
||||
Introducing Table and Data Table{" "}
|
||||
<ChevronRight className="ml-1 h-4 w-4" />
|
||||
</Link>
|
||||
<PageHeaderHeading className="hidden md:block">
|
||||
Check out some examples.
|
||||
</PageHeaderHeading>
|
||||
@@ -32,31 +42,24 @@ export default function ExamplesLayout({ children }: ExamplesLayoutProps) {
|
||||
Dashboard, cards, authentication. Some examples built using the
|
||||
components. Use this as a guide to build your own.
|
||||
</PageHeaderDescription>
|
||||
<section className="flex w-full items-center space-x-4 pb-8 pt-4 md:pb-10">
|
||||
<Link
|
||||
href="/docs"
|
||||
className={cn(buttonVariants(), "rounded-[6px]")}
|
||||
>
|
||||
Get Started
|
||||
</Link>
|
||||
<Link
|
||||
href="/components"
|
||||
className={cn(
|
||||
buttonVariants({ variant: "outline" }),
|
||||
"rounded-[6px]"
|
||||
)}
|
||||
>
|
||||
Components
|
||||
</Link>
|
||||
</section>
|
||||
</PageHeader>
|
||||
<section className="pb-6 md:pb-10">
|
||||
<div className="flex w-full items-center justify-between">
|
||||
<div className="flex space-x-4">
|
||||
<Link
|
||||
href="/docs"
|
||||
className={cn(
|
||||
buttonVariants({ size: "lg" }),
|
||||
"rounded-[0.5rem]"
|
||||
)}
|
||||
>
|
||||
Get Started
|
||||
</Link>
|
||||
<Link
|
||||
href="/components"
|
||||
className={cn(
|
||||
buttonVariants({ variant: "outline", size: "lg" }),
|
||||
"rounded-[0.5rem] pl-6"
|
||||
)}
|
||||
>
|
||||
Components
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<ExamplesNav />
|
||||
<div className="overflow-hidden rounded-[0.5rem] border bg-background shadow-xl">
|
||||
|
||||
120
apps/www/app/examples/tasks/components/columns.tsx
Normal file
120
apps/www/app/examples/tasks/components/columns.tsx
Normal file
@@ -0,0 +1,120 @@
|
||||
"use client"
|
||||
|
||||
import { ColumnDef } from "@tanstack/react-table"
|
||||
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
import { Checkbox } from "@/components/ui/checkbox"
|
||||
|
||||
import { labels, priorities, statuses } from "../data/data"
|
||||
import { Task } from "../data/schema"
|
||||
import { DataTableColumnHeader } from "./data-table-column-header"
|
||||
import { DataTableRowActions } from "./data-table-row-actions"
|
||||
|
||||
export const columns: ColumnDef<Task>[] = [
|
||||
{
|
||||
id: "select",
|
||||
header: ({ table }) => (
|
||||
<Checkbox
|
||||
checked={table.getIsAllPageRowsSelected()}
|
||||
onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
|
||||
aria-label="Select all"
|
||||
className="translate-y-[2px]"
|
||||
/>
|
||||
),
|
||||
cell: ({ row }) => (
|
||||
<Checkbox
|
||||
checked={row.getIsSelected()}
|
||||
onCheckedChange={(value) => row.toggleSelected(!!value)}
|
||||
aria-label="Select row"
|
||||
className="translate-y-[2px]"
|
||||
/>
|
||||
),
|
||||
enableSorting: false,
|
||||
enableHiding: false,
|
||||
},
|
||||
{
|
||||
accessorKey: "id",
|
||||
header: ({ column }) => (
|
||||
<DataTableColumnHeader column={column} title="Task" />
|
||||
),
|
||||
cell: ({ row }) => <div className="w-[80px]">{row.getValue("id")}</div>,
|
||||
enableSorting: false,
|
||||
enableHiding: false,
|
||||
},
|
||||
{
|
||||
accessorKey: "title",
|
||||
header: ({ column }) => (
|
||||
<DataTableColumnHeader column={column} title="Title" />
|
||||
),
|
||||
cell: ({ row }) => {
|
||||
const label = labels.find((label) => label.value === row.original.label)
|
||||
|
||||
return (
|
||||
<div className="flex space-x-2">
|
||||
{label && <Badge variant="outline">{label.label}</Badge>}
|
||||
<span className="max-w-[500px] truncate font-medium">
|
||||
{row.getValue("title")}
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "status",
|
||||
header: ({ column }) => (
|
||||
<DataTableColumnHeader column={column} title="Status" />
|
||||
),
|
||||
cell: ({ row }) => {
|
||||
const status = statuses.find(
|
||||
(status) => status.value === row.getValue("status")
|
||||
)
|
||||
|
||||
if (!status) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex w-[100px] items-center">
|
||||
{status.icon && (
|
||||
<status.icon className="mr-2 h-4 w-4 text-muted-foreground" />
|
||||
)}
|
||||
<span>{status.label}</span>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
filterFn: (row, id, value) => {
|
||||
return value.includes(row.getValue(id))
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "priority",
|
||||
header: ({ column }) => (
|
||||
<DataTableColumnHeader column={column} title="Priority" />
|
||||
),
|
||||
cell: ({ row }) => {
|
||||
const priority = priorities.find(
|
||||
(priority) => priority.value === row.getValue("priority")
|
||||
)
|
||||
|
||||
if (!priority) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex items-center">
|
||||
{priority.icon && (
|
||||
<priority.icon className="mr-2 h-4 w-4 text-muted-foreground" />
|
||||
)}
|
||||
<span>{priority.label}</span>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
filterFn: (row, id, value) => {
|
||||
return value.includes(row.getValue(id))
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "actions",
|
||||
cell: ({ row }) => <DataTableRowActions row={row} />,
|
||||
},
|
||||
]
|
||||
@@ -0,0 +1,66 @@
|
||||
import { Column } from "@tanstack/react-table"
|
||||
import { ChevronsUpDown, EyeOff, SortAsc, SortDesc } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu"
|
||||
|
||||
interface DataTableColumnHeaderProps<TData, TValue>
|
||||
extends React.HTMLAttributes<HTMLDivElement> {
|
||||
column: Column<TData, TValue>
|
||||
title: string
|
||||
}
|
||||
|
||||
export function DataTableColumnHeader<TData, TValue>({
|
||||
column,
|
||||
title,
|
||||
className,
|
||||
}: DataTableColumnHeaderProps<TData, TValue>) {
|
||||
if (!column.getCanSort()) {
|
||||
return <div className={cn(className)}>{title}</div>
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={cn("flex items-center space-x-2", className)}>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="-ml-3 h-8 data-[state=open]:bg-accent"
|
||||
>
|
||||
<span>{title}</span>
|
||||
{column.getIsSorted() === "desc" ? (
|
||||
<SortDesc className="ml-2 h-4 w-4" />
|
||||
) : column.getIsSorted() === "asc" ? (
|
||||
<SortAsc className="ml-2 h-4 w-4" />
|
||||
) : (
|
||||
<ChevronsUpDown className="ml-2 h-4 w-4" />
|
||||
)}
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="start">
|
||||
<DropdownMenuItem onClick={() => column.toggleSorting(false)}>
|
||||
<SortAsc className="mr-2 h-3.5 w-3.5 text-muted-foreground/70" />
|
||||
Asc
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => column.toggleSorting(true)}>
|
||||
<SortDesc className="mr-2 h-3.5 w-3.5 text-muted-foreground/70" />
|
||||
Desc
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem onClick={() => column.toggleVisibility(false)}>
|
||||
<EyeOff className="mr-2 h-3.5 w-3.5 text-muted-foreground/70" />
|
||||
Hide
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
import * as React from "react"
|
||||
import { Column } from "@tanstack/react-table"
|
||||
import { Check, LucideIcon, PlusCircle } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import {
|
||||
Command,
|
||||
CommandEmpty,
|
||||
CommandGroup,
|
||||
CommandInput,
|
||||
CommandItem,
|
||||
CommandList,
|
||||
CommandSeparator,
|
||||
} from "@/components/ui/command"
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/components/ui/popover"
|
||||
import { Separator } from "@/components/ui/separator"
|
||||
|
||||
interface DataTableFacetedFilter<TData, TValue> {
|
||||
column?: Column<TData, TValue>
|
||||
title?: string
|
||||
options: {
|
||||
label: string
|
||||
value: string
|
||||
icon?: LucideIcon
|
||||
}[]
|
||||
}
|
||||
|
||||
export function DataTableFacetedFilter<TData, TValue>({
|
||||
column,
|
||||
title,
|
||||
options,
|
||||
}: DataTableFacetedFilter<TData, TValue>) {
|
||||
const facets = column?.getFacetedUniqueValues()
|
||||
const selectedValues = new Set(column?.getFilterValue() as string[])
|
||||
|
||||
return (
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button variant="outline" size="sm" className="h-8 border-dashed">
|
||||
<PlusCircle className="mr-2 h-4 w-4" />
|
||||
{title}
|
||||
{selectedValues?.size > 0 && (
|
||||
<>
|
||||
<Separator orientation="vertical" className="mx-2 h-4" />
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className="rounded-sm px-1 font-normal lg:hidden"
|
||||
>
|
||||
{selectedValues.size}
|
||||
</Badge>
|
||||
<div className="hidden space-x-1 lg:flex">
|
||||
{selectedValues.size > 2 ? (
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className="rounded-sm px-1 font-normal"
|
||||
>
|
||||
{selectedValues.size} selected
|
||||
</Badge>
|
||||
) : (
|
||||
options
|
||||
.filter((option) => selectedValues.has(option.value))
|
||||
.map((option) => (
|
||||
<Badge
|
||||
variant="secondary"
|
||||
key={option.value}
|
||||
className="rounded-sm px-1 font-normal"
|
||||
>
|
||||
{option.label}
|
||||
</Badge>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-[200px] p-0" align="start">
|
||||
<Command>
|
||||
<CommandInput placeholder={title} />
|
||||
<CommandList>
|
||||
<CommandEmpty>No results found.</CommandEmpty>
|
||||
<CommandGroup>
|
||||
{options.map((option) => {
|
||||
const isSelected = selectedValues.has(option.value)
|
||||
return (
|
||||
<CommandItem
|
||||
key={option.value}
|
||||
onSelect={() => {
|
||||
if (isSelected) {
|
||||
selectedValues.delete(option.value)
|
||||
} else {
|
||||
selectedValues.add(option.value)
|
||||
}
|
||||
const filterValues = Array.from(selectedValues)
|
||||
column?.setFilterValue(
|
||||
filterValues.length ? filterValues : undefined
|
||||
)
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
"mr-2 flex h-4 w-4 items-center justify-center rounded-sm border border-primary",
|
||||
isSelected
|
||||
? "bg-primary text-primary-foreground"
|
||||
: "opacity-50 [&_svg]:invisible"
|
||||
)}
|
||||
>
|
||||
<Check className={cn("h-4 w-4")} />
|
||||
</div>
|
||||
{option.icon && (
|
||||
<option.icon className="mr-2 h-4 w-4 text-muted-foreground" />
|
||||
)}
|
||||
<span>{option.label}</span>
|
||||
{facets?.get(option.value) && (
|
||||
<span className="ml-auto flex h-4 w-4 items-center justify-center font-mono text-xs">
|
||||
{facets.get(option.value)}
|
||||
</span>
|
||||
)}
|
||||
</CommandItem>
|
||||
)
|
||||
})}
|
||||
</CommandGroup>
|
||||
{selectedValues.size > 0 && (
|
||||
<>
|
||||
<CommandSeparator />
|
||||
<CommandGroup>
|
||||
<CommandItem
|
||||
onSelect={() => column?.setFilterValue(undefined)}
|
||||
className="justify-center text-center"
|
||||
>
|
||||
Clear filters
|
||||
</CommandItem>
|
||||
</CommandGroup>
|
||||
</>
|
||||
)}
|
||||
</CommandList>
|
||||
</Command>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
import { Table } from "@tanstack/react-table"
|
||||
import {
|
||||
ChevronLeft,
|
||||
ChevronRight,
|
||||
ChevronsLeft,
|
||||
ChevronsRight,
|
||||
} from "lucide-react"
|
||||
|
||||
import { Button } from "@/components/ui/button"
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select"
|
||||
|
||||
interface DataTablePaginationProps<TData> {
|
||||
table: Table<TData>
|
||||
}
|
||||
|
||||
export function DataTablePagination<TData>({
|
||||
table,
|
||||
}: DataTablePaginationProps<TData>) {
|
||||
return (
|
||||
<div className="flex items-center justify-between px-2">
|
||||
<div className="flex-1 text-sm text-muted-foreground">
|
||||
{table.getFilteredSelectedRowModel().rows.length} of{" "}
|
||||
{table.getFilteredRowModel().rows.length} row(s) selected.
|
||||
</div>
|
||||
<div className="flex items-center space-x-6 lg:space-x-8">
|
||||
<div className="flex items-center space-x-2">
|
||||
<p className="text-sm font-medium">Rows per page</p>
|
||||
<Select
|
||||
value={`${table.getState().pagination.pageSize}`}
|
||||
onValueChange={(value) => {
|
||||
table.setPageSize(Number(value))
|
||||
}}
|
||||
>
|
||||
<SelectTrigger className="h-8 w-[70px]">
|
||||
<SelectValue placeholder={table.getState().pagination.pageSize} />
|
||||
</SelectTrigger>
|
||||
<SelectContent side="top">
|
||||
{[10, 20, 30, 40, 50].map((pageSize) => (
|
||||
<SelectItem key={pageSize} value={`${pageSize}`}>
|
||||
{pageSize}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className="flex w-[100px] items-center justify-center text-sm font-medium">
|
||||
Page {table.getState().pagination.pageIndex + 1} of{" "}
|
||||
{table.getPageCount()}
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
className="hidden h-8 w-8 p-0 lg:flex"
|
||||
onClick={() => table.setPageIndex(0)}
|
||||
disabled={!table.getCanPreviousPage()}
|
||||
>
|
||||
<span className="sr-only">Go to first page</span>
|
||||
<ChevronsLeft className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="h-8 w-8 p-0"
|
||||
onClick={() => table.previousPage()}
|
||||
disabled={!table.getCanPreviousPage()}
|
||||
>
|
||||
<span className="sr-only">Go to previous page</span>
|
||||
<ChevronLeft className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="h-8 w-8 p-0"
|
||||
onClick={() => table.nextPage()}
|
||||
disabled={!table.getCanNextPage()}
|
||||
>
|
||||
<span className="sr-only">Go to next page</span>
|
||||
<ChevronRight className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="hidden h-8 w-8 p-0 lg:flex"
|
||||
onClick={() => table.setPageIndex(table.getPageCount() - 1)}
|
||||
disabled={!table.getCanNextPage()}
|
||||
>
|
||||
<span className="sr-only">Go to last page</span>
|
||||
<ChevronsRight className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
"use client"
|
||||
|
||||
import { Row } from "@tanstack/react-table"
|
||||
import { Copy, MoreHorizontal, Pen, Star, Tags, Trash } from "lucide-react"
|
||||
|
||||
import { Button } from "@/components/ui/button"
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuRadioGroup,
|
||||
DropdownMenuRadioItem,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuShortcut,
|
||||
DropdownMenuSub,
|
||||
DropdownMenuSubContent,
|
||||
DropdownMenuSubTrigger,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu"
|
||||
|
||||
import { labels } from "../data/data"
|
||||
import { taskSchema } from "../data/schema"
|
||||
|
||||
interface DataTableRowActionsProps<TData> {
|
||||
row: Row<TData>
|
||||
}
|
||||
|
||||
export function DataTableRowActions<TData>({
|
||||
row,
|
||||
}: DataTableRowActionsProps<TData>) {
|
||||
const task = taskSchema.parse(row.original)
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="flex h-8 w-8 p-0 data-[state=open]:bg-muted"
|
||||
>
|
||||
<MoreHorizontal className="h-4 w-4" />
|
||||
<span className="sr-only">Open menu</span>
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="w-[160px]">
|
||||
<DropdownMenuItem>
|
||||
<Pen className="mr-2 h-3.5 w-3.5 text-muted-foreground/70" />
|
||||
Edit
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<Copy className="mr-2 h-3.5 w-3.5 text-muted-foreground/70" />
|
||||
Make a copy
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<Star className="mr-2 h-3.5 w-3.5 text-muted-foreground/70" />
|
||||
Favorite
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuSub>
|
||||
<DropdownMenuSubTrigger>
|
||||
<Tags className="mr-2 h-3.5 w-3.5 text-muted-foreground/70" />
|
||||
Labels
|
||||
</DropdownMenuSubTrigger>
|
||||
<DropdownMenuSubContent>
|
||||
<DropdownMenuRadioGroup value={task.label}>
|
||||
{labels.map((label) => (
|
||||
<DropdownMenuRadioItem key={label.value} value={label.value}>
|
||||
{label.label}
|
||||
</DropdownMenuRadioItem>
|
||||
))}
|
||||
</DropdownMenuRadioGroup>
|
||||
</DropdownMenuSubContent>
|
||||
</DropdownMenuSub>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem>
|
||||
<Trash className="mr-2 h-3.5 w-3.5 text-muted-foreground/70" />
|
||||
Delete
|
||||
<DropdownMenuShortcut>⌘⌫</DropdownMenuShortcut>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
"use client"
|
||||
|
||||
import { Table } from "@tanstack/react-table"
|
||||
import { X } from "lucide-react"
|
||||
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { DataTableViewOptions } from "@/app/examples/tasks/components/data-table-view-options"
|
||||
|
||||
import { priorities, statuses } from "../data/data"
|
||||
import { DataTableFacetedFilter } from "./data-table-faceted-filter"
|
||||
|
||||
interface DataTableToolbarProps<TData> {
|
||||
table: Table<TData>
|
||||
}
|
||||
|
||||
export function DataTableToolbar<TData>({
|
||||
table,
|
||||
}: DataTableToolbarProps<TData>) {
|
||||
const isFiltered =
|
||||
table.getPreFilteredRowModel().rows.length >
|
||||
table.getFilteredRowModel().rows.length
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex flex-1 items-center space-x-2">
|
||||
<Input
|
||||
placeholder="Filter tasks..."
|
||||
value={(table.getColumn("title")?.getFilterValue() as string) ?? ""}
|
||||
onChange={(event) =>
|
||||
table.getColumn("title")?.setFilterValue(event.target.value)
|
||||
}
|
||||
className="h-8 w-[150px] lg:w-[250px]"
|
||||
/>
|
||||
{table.getColumn("status") && (
|
||||
<DataTableFacetedFilter
|
||||
column={table.getColumn("status")}
|
||||
title="Status"
|
||||
options={statuses}
|
||||
/>
|
||||
)}
|
||||
{table.getColumn("priority") && (
|
||||
<DataTableFacetedFilter
|
||||
column={table.getColumn("priority")}
|
||||
title="Priority"
|
||||
options={priorities}
|
||||
/>
|
||||
)}
|
||||
{isFiltered && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => table.resetColumnFilters()}
|
||||
className="h-8 px-2 lg:px-3"
|
||||
>
|
||||
Reset
|
||||
<X className="ml-2 h-4 w-4" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<DataTableViewOptions table={table} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
"use client"
|
||||
|
||||
import { DropdownMenuTrigger } from "@radix-ui/react-dropdown-menu"
|
||||
import { Table } from "@tanstack/react-table"
|
||||
import { SlidersHorizontal } from "lucide-react"
|
||||
|
||||
import { Button } from "@/components/ui/button"
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuCheckboxItem,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
} from "@/components/ui/dropdown-menu"
|
||||
|
||||
interface DataTableViewOptionsProps<TData> {
|
||||
table: Table<TData>
|
||||
}
|
||||
|
||||
export function DataTableViewOptions<TData>({
|
||||
table,
|
||||
}: DataTableViewOptionsProps<TData>) {
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="ml-auto hidden h-8 lg:flex"
|
||||
>
|
||||
<SlidersHorizontal className="mr-2 h-4 w-4" />
|
||||
View
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="w-[150px]">
|
||||
<DropdownMenuLabel>Toggle columns</DropdownMenuLabel>
|
||||
<DropdownMenuSeparator />
|
||||
{table
|
||||
.getAllColumns()
|
||||
.filter(
|
||||
(column) =>
|
||||
typeof column.accessorFn !== "undefined" && column.getCanHide()
|
||||
)
|
||||
.map((column) => {
|
||||
return (
|
||||
<DropdownMenuCheckboxItem
|
||||
key={column.id}
|
||||
className="capitalize"
|
||||
checked={column.getIsVisible()}
|
||||
onCheckedChange={(value) => column.toggleVisibility(!!value)}
|
||||
>
|
||||
{column.id}
|
||||
</DropdownMenuCheckboxItem>
|
||||
)
|
||||
})}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)
|
||||
}
|
||||
126
apps/www/app/examples/tasks/components/data-table.tsx
Normal file
126
apps/www/app/examples/tasks/components/data-table.tsx
Normal file
@@ -0,0 +1,126 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import {
|
||||
ColumnDef,
|
||||
ColumnFiltersState,
|
||||
SortingState,
|
||||
VisibilityState,
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
getFacetedRowModel,
|
||||
getFacetedUniqueValues,
|
||||
getFilteredRowModel,
|
||||
getPaginationRowModel,
|
||||
getSortedRowModel,
|
||||
useReactTable,
|
||||
} from "@tanstack/react-table"
|
||||
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/components/ui/table"
|
||||
|
||||
import { DataTablePagination } from "../components/data-table-pagination"
|
||||
import { DataTableToolbar } from "../components/data-table-toolbar"
|
||||
|
||||
interface DataTableProps<TData, TValue> {
|
||||
columns: ColumnDef<TData, TValue>[]
|
||||
data: TData[]
|
||||
}
|
||||
|
||||
export function DataTable<TData, TValue>({
|
||||
columns,
|
||||
data,
|
||||
}: DataTableProps<TData, TValue>) {
|
||||
const [rowSelection, setRowSelection] = React.useState({})
|
||||
const [columnVisibility, setColumnVisibility] =
|
||||
React.useState<VisibilityState>({})
|
||||
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
||||
[]
|
||||
)
|
||||
const [sorting, setSorting] = React.useState<SortingState>([])
|
||||
|
||||
const table = useReactTable({
|
||||
data,
|
||||
columns,
|
||||
state: {
|
||||
sorting,
|
||||
columnVisibility,
|
||||
rowSelection,
|
||||
columnFilters,
|
||||
},
|
||||
enableRowSelection: true,
|
||||
onRowSelectionChange: setRowSelection,
|
||||
onSortingChange: setSorting,
|
||||
onColumnFiltersChange: setColumnFilters,
|
||||
onColumnVisibilityChange: setColumnVisibility,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getFilteredRowModel: getFilteredRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
getFacetedRowModel: getFacetedRowModel(),
|
||||
getFacetedUniqueValues: getFacetedUniqueValues(),
|
||||
})
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<DataTableToolbar table={table} />
|
||||
<div className="rounded-md border">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<TableRow key={headerGroup.id}>
|
||||
{headerGroup.headers.map((header) => {
|
||||
return (
|
||||
<TableHead key={header.id}>
|
||||
{header.isPlaceholder
|
||||
? null
|
||||
: flexRender(
|
||||
header.column.columnDef.header,
|
||||
header.getContext()
|
||||
)}
|
||||
</TableHead>
|
||||
)
|
||||
})}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{table.getRowModel().rows?.length ? (
|
||||
table.getRowModel().rows.map((row) => (
|
||||
<TableRow
|
||||
key={row.id}
|
||||
data-state={row.getIsSelected() && "selected"}
|
||||
>
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
<TableCell key={cell.id}>
|
||||
{flexRender(
|
||||
cell.column.columnDef.cell,
|
||||
cell.getContext()
|
||||
)}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
))
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell
|
||||
colSpan={columns.length}
|
||||
className="h-24 text-center"
|
||||
>
|
||||
No results.
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
<DataTablePagination table={table} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
67
apps/www/app/examples/tasks/components/user-nav.tsx
Normal file
67
apps/www/app/examples/tasks/components/user-nav.tsx
Normal file
@@ -0,0 +1,67 @@
|
||||
import { CreditCard, LogOut, PlusCircle, Settings, User } from "lucide-react"
|
||||
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuGroup,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuShortcut,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu"
|
||||
|
||||
export function UserNav() {
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="ghost" className="relative h-8 w-8 rounded-full">
|
||||
<Avatar className="h-9 w-9">
|
||||
<AvatarImage src="/avatars/03.png" alt="@shadcn" />
|
||||
<AvatarFallback>SC</AvatarFallback>
|
||||
</Avatar>
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="w-56" align="end" forceMount>
|
||||
<DropdownMenuLabel className="font-normal">
|
||||
<div className="flex flex-col space-y-1">
|
||||
<p className="text-sm font-medium leading-none">shadcn</p>
|
||||
<p className="text-xs leading-none text-muted-foreground">
|
||||
m@example.com
|
||||
</p>
|
||||
</div>
|
||||
</DropdownMenuLabel>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuGroup>
|
||||
<DropdownMenuItem>
|
||||
<User className="mr-2 h-4 w-4" />
|
||||
<span>Profile</span>
|
||||
<DropdownMenuShortcut>⇧⌘P</DropdownMenuShortcut>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<CreditCard className="mr-2 h-4 w-4" />
|
||||
<span>Billing</span>
|
||||
<DropdownMenuShortcut>⌘B</DropdownMenuShortcut>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<Settings className="mr-2 h-4 w-4" />
|
||||
<span>Settings</span>
|
||||
<DropdownMenuShortcut>⌘S</DropdownMenuShortcut>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<PlusCircle className="mr-2 h-4 w-4" />
|
||||
<span>New Team</span>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuGroup>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem>
|
||||
<LogOut className="mr-2 h-4 w-4" />
|
||||
<span>Log out</span>
|
||||
<DropdownMenuShortcut>⇧⌘Q</DropdownMenuShortcut>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)
|
||||
}
|
||||
71
apps/www/app/examples/tasks/data/data.tsx
Normal file
71
apps/www/app/examples/tasks/data/data.tsx
Normal file
@@ -0,0 +1,71 @@
|
||||
import {
|
||||
ArrowDownToLine,
|
||||
ArrowRightToLine,
|
||||
ArrowUpCircle,
|
||||
ArrowUpToLine,
|
||||
CheckCircle2,
|
||||
Circle,
|
||||
HelpCircle,
|
||||
XCircle,
|
||||
} from "lucide-react"
|
||||
|
||||
export const labels = [
|
||||
{
|
||||
value: "bug",
|
||||
label: "Bug",
|
||||
},
|
||||
{
|
||||
value: "feature",
|
||||
label: "Feature",
|
||||
},
|
||||
{
|
||||
value: "documentation",
|
||||
label: "Documentation",
|
||||
},
|
||||
]
|
||||
|
||||
export const statuses = [
|
||||
{
|
||||
value: "backlog",
|
||||
label: "Backlog",
|
||||
icon: HelpCircle,
|
||||
},
|
||||
{
|
||||
value: "todo",
|
||||
label: "Todo",
|
||||
icon: Circle,
|
||||
},
|
||||
{
|
||||
value: "in progress",
|
||||
label: "In Progress",
|
||||
icon: ArrowUpCircle,
|
||||
},
|
||||
{
|
||||
value: "done",
|
||||
label: "Done",
|
||||
icon: CheckCircle2,
|
||||
},
|
||||
{
|
||||
value: "canceled",
|
||||
label: "Canceled",
|
||||
icon: XCircle,
|
||||
},
|
||||
]
|
||||
|
||||
export const priorities = [
|
||||
{
|
||||
label: "Low",
|
||||
value: "low",
|
||||
icon: ArrowDownToLine,
|
||||
},
|
||||
{
|
||||
label: "Medium",
|
||||
value: "medium",
|
||||
icon: ArrowRightToLine,
|
||||
},
|
||||
{
|
||||
label: "High",
|
||||
value: "high",
|
||||
icon: ArrowUpToLine,
|
||||
},
|
||||
]
|
||||
13
apps/www/app/examples/tasks/data/schema.ts
Normal file
13
apps/www/app/examples/tasks/data/schema.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { z } from "zod"
|
||||
|
||||
// We're keeping a simple non-relational schema here.
|
||||
// IRL, you will have a schema for your data models.
|
||||
export const taskSchema = z.object({
|
||||
id: z.string(),
|
||||
title: z.string(),
|
||||
status: z.string(),
|
||||
label: z.string(),
|
||||
priority: z.string(),
|
||||
})
|
||||
|
||||
export type Task = z.infer<typeof taskSchema>
|
||||
20
apps/www/app/examples/tasks/data/seed.ts
Executable file
20
apps/www/app/examples/tasks/data/seed.ts
Executable file
@@ -0,0 +1,20 @@
|
||||
import fs from "fs"
|
||||
import path from "path"
|
||||
import { faker } from "@faker-js/faker"
|
||||
|
||||
import { labels, priorities, statuses } from "./data"
|
||||
|
||||
const tasks = Array.from({ length: 100 }, () => ({
|
||||
id: `TASK-${faker.datatype.number({ min: 1000, max: 9999 })}`,
|
||||
title: faker.hacker.phrase().replace(/^./, (letter) => letter.toUpperCase()),
|
||||
status: faker.helpers.arrayElement(statuses).value,
|
||||
label: faker.helpers.arrayElement(labels).value,
|
||||
priority: faker.helpers.arrayElement(priorities).value,
|
||||
}))
|
||||
|
||||
fs.writeFileSync(
|
||||
path.join(__dirname, "tasks.json"),
|
||||
JSON.stringify(tasks, null, 2)
|
||||
)
|
||||
|
||||
console.log("✅ Tasks data generated.")
|
||||
702
apps/www/app/examples/tasks/data/tasks.json
Normal file
702
apps/www/app/examples/tasks/data/tasks.json
Normal file
@@ -0,0 +1,702 @@
|
||||
[
|
||||
{
|
||||
"id": "TASK-8782",
|
||||
"title": "You can't compress the program without quantifying the open-source SSD pixel!",
|
||||
"status": "in progress",
|
||||
"label": "documentation",
|
||||
"priority": "medium"
|
||||
},
|
||||
{
|
||||
"id": "TASK-7878",
|
||||
"title": "Try to calculate the EXE feed, maybe it will index the multi-byte pixel!",
|
||||
"status": "backlog",
|
||||
"label": "documentation",
|
||||
"priority": "medium"
|
||||
},
|
||||
{
|
||||
"id": "TASK-7839",
|
||||
"title": "We need to bypass the neural TCP card!",
|
||||
"status": "todo",
|
||||
"label": "bug",
|
||||
"priority": "high"
|
||||
},
|
||||
{
|
||||
"id": "TASK-5562",
|
||||
"title": "The SAS interface is down, bypass the open-source pixel so we can back up the PNG bandwidth!",
|
||||
"status": "backlog",
|
||||
"label": "feature",
|
||||
"priority": "medium"
|
||||
},
|
||||
{
|
||||
"id": "TASK-8686",
|
||||
"title": "I'll parse the wireless SSL protocol, that should driver the API panel!",
|
||||
"status": "canceled",
|
||||
"label": "feature",
|
||||
"priority": "medium"
|
||||
},
|
||||
{
|
||||
"id": "TASK-1280",
|
||||
"title": "Use the digital TLS panel, then you can transmit the haptic system!",
|
||||
"status": "done",
|
||||
"label": "bug",
|
||||
"priority": "high"
|
||||
},
|
||||
{
|
||||
"id": "TASK-7262",
|
||||
"title": "The UTF8 application is down, parse the neural bandwidth so we can back up the PNG firewall!",
|
||||
"status": "done",
|
||||
"label": "feature",
|
||||
"priority": "high"
|
||||
},
|
||||
{
|
||||
"id": "TASK-1138",
|
||||
"title": "Generating the driver won't do anything, we need to quantify the 1080p SMTP bandwidth!",
|
||||
"status": "in progress",
|
||||
"label": "feature",
|
||||
"priority": "medium"
|
||||
},
|
||||
{
|
||||
"id": "TASK-7184",
|
||||
"title": "We need to program the back-end THX pixel!",
|
||||
"status": "todo",
|
||||
"label": "feature",
|
||||
"priority": "low"
|
||||
},
|
||||
{
|
||||
"id": "TASK-5160",
|
||||
"title": "Calculating the bus won't do anything, we need to navigate the back-end JSON protocol!",
|
||||
"status": "in progress",
|
||||
"label": "documentation",
|
||||
"priority": "high"
|
||||
},
|
||||
{
|
||||
"id": "TASK-5618",
|
||||
"title": "Generating the driver won't do anything, we need to index the online SSL application!",
|
||||
"status": "done",
|
||||
"label": "documentation",
|
||||
"priority": "medium"
|
||||
},
|
||||
{
|
||||
"id": "TASK-6699",
|
||||
"title": "I'll transmit the wireless JBOD capacitor, that should hard drive the SSD feed!",
|
||||
"status": "backlog",
|
||||
"label": "documentation",
|
||||
"priority": "medium"
|
||||
},
|
||||
{
|
||||
"id": "TASK-2858",
|
||||
"title": "We need to override the online UDP bus!",
|
||||
"status": "backlog",
|
||||
"label": "bug",
|
||||
"priority": "medium"
|
||||
},
|
||||
{
|
||||
"id": "TASK-9864",
|
||||
"title": "I'll reboot the 1080p FTP panel, that should matrix the HEX hard drive!",
|
||||
"status": "done",
|
||||
"label": "bug",
|
||||
"priority": "high"
|
||||
},
|
||||
{
|
||||
"id": "TASK-8404",
|
||||
"title": "We need to generate the virtual HEX alarm!",
|
||||
"status": "in progress",
|
||||
"label": "bug",
|
||||
"priority": "low"
|
||||
},
|
||||
{
|
||||
"id": "TASK-5365",
|
||||
"title": "Backing up the pixel won't do anything, we need to transmit the primary IB array!",
|
||||
"status": "in progress",
|
||||
"label": "documentation",
|
||||
"priority": "low"
|
||||
},
|
||||
{
|
||||
"id": "TASK-1780",
|
||||
"title": "The CSS feed is down, index the bluetooth transmitter so we can compress the CLI protocol!",
|
||||
"status": "todo",
|
||||
"label": "documentation",
|
||||
"priority": "high"
|
||||
},
|
||||
{
|
||||
"id": "TASK-6938",
|
||||
"title": "Use the redundant SCSI application, then you can hack the optical alarm!",
|
||||
"status": "todo",
|
||||
"label": "documentation",
|
||||
"priority": "high"
|
||||
},
|
||||
{
|
||||
"id": "TASK-9885",
|
||||
"title": "We need to compress the auxiliary VGA driver!",
|
||||
"status": "backlog",
|
||||
"label": "bug",
|
||||
"priority": "high"
|
||||
},
|
||||
{
|
||||
"id": "TASK-3216",
|
||||
"title": "Transmitting the transmitter won't do anything, we need to compress the virtual HDD sensor!",
|
||||
"status": "backlog",
|
||||
"label": "documentation",
|
||||
"priority": "medium"
|
||||
},
|
||||
{
|
||||
"id": "TASK-9285",
|
||||
"title": "The IP monitor is down, copy the haptic alarm so we can generate the HTTP transmitter!",
|
||||
"status": "todo",
|
||||
"label": "bug",
|
||||
"priority": "high"
|
||||
},
|
||||
{
|
||||
"id": "TASK-1024",
|
||||
"title": "Overriding the microchip won't do anything, we need to transmit the digital OCR transmitter!",
|
||||
"status": "in progress",
|
||||
"label": "documentation",
|
||||
"priority": "low"
|
||||
},
|
||||
{
|
||||
"id": "TASK-7068",
|
||||
"title": "You can't generate the capacitor without indexing the wireless HEX pixel!",
|
||||
"status": "canceled",
|
||||
"label": "bug",
|
||||
"priority": "low"
|
||||
},
|
||||
{
|
||||
"id": "TASK-6502",
|
||||
"title": "Navigating the microchip won't do anything, we need to bypass the back-end SQL bus!",
|
||||
"status": "todo",
|
||||
"label": "bug",
|
||||
"priority": "high"
|
||||
},
|
||||
{
|
||||
"id": "TASK-5326",
|
||||
"title": "We need to hack the redundant UTF8 transmitter!",
|
||||
"status": "todo",
|
||||
"label": "bug",
|
||||
"priority": "low"
|
||||
},
|
||||
{
|
||||
"id": "TASK-6274",
|
||||
"title": "Use the virtual PCI circuit, then you can parse the bluetooth alarm!",
|
||||
"status": "canceled",
|
||||
"label": "documentation",
|
||||
"priority": "low"
|
||||
},
|
||||
{
|
||||
"id": "TASK-1571",
|
||||
"title": "I'll input the neural DRAM circuit, that should protocol the SMTP interface!",
|
||||
"status": "in progress",
|
||||
"label": "feature",
|
||||
"priority": "medium"
|
||||
},
|
||||
{
|
||||
"id": "TASK-9518",
|
||||
"title": "Compressing the interface won't do anything, we need to compress the online SDD matrix!",
|
||||
"status": "canceled",
|
||||
"label": "documentation",
|
||||
"priority": "medium"
|
||||
},
|
||||
{
|
||||
"id": "TASK-5581",
|
||||
"title": "I'll synthesize the digital COM pixel, that should transmitter the UTF8 protocol!",
|
||||
"status": "backlog",
|
||||
"label": "documentation",
|
||||
"priority": "high"
|
||||
},
|
||||
{
|
||||
"id": "TASK-2197",
|
||||
"title": "Parsing the feed won't do anything, we need to copy the bluetooth DRAM bus!",
|
||||
"status": "todo",
|
||||
"label": "documentation",
|
||||
"priority": "low"
|
||||
},
|
||||
{
|
||||
"id": "TASK-8484",
|
||||
"title": "We need to parse the solid state UDP firewall!",
|
||||
"status": "in progress",
|
||||
"label": "bug",
|
||||
"priority": "low"
|
||||
},
|
||||
{
|
||||
"id": "TASK-9892",
|
||||
"title": "If we back up the application, we can get to the UDP application through the multi-byte THX capacitor!",
|
||||
"status": "done",
|
||||
"label": "documentation",
|
||||
"priority": "high"
|
||||
},
|
||||
{
|
||||
"id": "TASK-9616",
|
||||
"title": "We need to synthesize the cross-platform ASCII pixel!",
|
||||
"status": "in progress",
|
||||
"label": "feature",
|
||||
"priority": "medium"
|
||||
},
|
||||
{
|
||||
"id": "TASK-9744",
|
||||
"title": "Use the back-end IP card, then you can input the solid state hard drive!",
|
||||
"status": "done",
|
||||
"label": "documentation",
|
||||
"priority": "low"
|
||||
},
|
||||
{
|
||||
"id": "TASK-1376",
|
||||
"title": "Generating the alarm won't do anything, we need to generate the mobile IP capacitor!",
|
||||
"status": "backlog",
|
||||
"label": "documentation",
|
||||
"priority": "low"
|
||||
},
|
||||
{
|
||||
"id": "TASK-7382",
|
||||
"title": "If we back up the firewall, we can get to the RAM alarm through the primary UTF8 pixel!",
|
||||
"status": "todo",
|
||||
"label": "feature",
|
||||
"priority": "low"
|
||||
},
|
||||
{
|
||||
"id": "TASK-2290",
|
||||
"title": "I'll compress the virtual JSON panel, that should application the UTF8 bus!",
|
||||
"status": "canceled",
|
||||
"label": "documentation",
|
||||
"priority": "high"
|
||||
},
|
||||
{
|
||||
"id": "TASK-1533",
|
||||
"title": "You can't input the firewall without overriding the wireless TCP firewall!",
|
||||
"status": "done",
|
||||
"label": "bug",
|
||||
"priority": "high"
|
||||
},
|
||||
{
|
||||
"id": "TASK-4920",
|
||||
"title": "Bypassing the hard drive won't do anything, we need to input the bluetooth JSON program!",
|
||||
"status": "in progress",
|
||||
"label": "bug",
|
||||
"priority": "high"
|
||||
},
|
||||
{
|
||||
"id": "TASK-5168",
|
||||
"title": "If we synthesize the bus, we can get to the IP panel through the virtual TLS array!",
|
||||
"status": "in progress",
|
||||
"label": "feature",
|
||||
"priority": "low"
|
||||
},
|
||||
{
|
||||
"id": "TASK-7103",
|
||||
"title": "We need to parse the multi-byte EXE bandwidth!",
|
||||
"status": "canceled",
|
||||
"label": "feature",
|
||||
"priority": "low"
|
||||
},
|
||||
{
|
||||
"id": "TASK-4314",
|
||||
"title": "If we compress the program, we can get to the XML alarm through the multi-byte COM matrix!",
|
||||
"status": "in progress",
|
||||
"label": "bug",
|
||||
"priority": "high"
|
||||
},
|
||||
{
|
||||
"id": "TASK-3415",
|
||||
"title": "Use the cross-platform XML application, then you can quantify the solid state feed!",
|
||||
"status": "todo",
|
||||
"label": "feature",
|
||||
"priority": "high"
|
||||
},
|
||||
{
|
||||
"id": "TASK-8339",
|
||||
"title": "Try to calculate the DNS interface, maybe it will input the bluetooth capacitor!",
|
||||
"status": "in progress",
|
||||
"label": "feature",
|
||||
"priority": "low"
|
||||
},
|
||||
{
|
||||
"id": "TASK-6995",
|
||||
"title": "Try to hack the XSS bandwidth, maybe it will override the bluetooth matrix!",
|
||||
"status": "todo",
|
||||
"label": "feature",
|
||||
"priority": "high"
|
||||
},
|
||||
{
|
||||
"id": "TASK-8053",
|
||||
"title": "If we connect the program, we can get to the UTF8 matrix through the digital UDP protocol!",
|
||||
"status": "todo",
|
||||
"label": "feature",
|
||||
"priority": "medium"
|
||||
},
|
||||
{
|
||||
"id": "TASK-4336",
|
||||
"title": "If we synthesize the microchip, we can get to the SAS sensor through the optical UDP program!",
|
||||
"status": "todo",
|
||||
"label": "documentation",
|
||||
"priority": "low"
|
||||
},
|
||||
{
|
||||
"id": "TASK-8790",
|
||||
"title": "I'll back up the optical COM alarm, that should alarm the RSS capacitor!",
|
||||
"status": "done",
|
||||
"label": "bug",
|
||||
"priority": "medium"
|
||||
},
|
||||
{
|
||||
"id": "TASK-8980",
|
||||
"title": "Try to navigate the SQL transmitter, maybe it will back up the virtual firewall!",
|
||||
"status": "canceled",
|
||||
"label": "bug",
|
||||
"priority": "low"
|
||||
},
|
||||
{
|
||||
"id": "TASK-7342",
|
||||
"title": "Use the neural CLI card, then you can parse the online port!",
|
||||
"status": "backlog",
|
||||
"label": "documentation",
|
||||
"priority": "low"
|
||||
},
|
||||
{
|
||||
"id": "TASK-5608",
|
||||
"title": "I'll hack the haptic SSL program, that should bus the UDP transmitter!",
|
||||
"status": "canceled",
|
||||
"label": "documentation",
|
||||
"priority": "low"
|
||||
},
|
||||
{
|
||||
"id": "TASK-1606",
|
||||
"title": "I'll generate the bluetooth PNG firewall, that should pixel the SSL driver!",
|
||||
"status": "done",
|
||||
"label": "feature",
|
||||
"priority": "medium"
|
||||
},
|
||||
{
|
||||
"id": "TASK-7872",
|
||||
"title": "Transmitting the circuit won't do anything, we need to reboot the 1080p RSS monitor!",
|
||||
"status": "canceled",
|
||||
"label": "feature",
|
||||
"priority": "medium"
|
||||
},
|
||||
{
|
||||
"id": "TASK-4167",
|
||||
"title": "Use the cross-platform SMS circuit, then you can synthesize the optical feed!",
|
||||
"status": "canceled",
|
||||
"label": "bug",
|
||||
"priority": "medium"
|
||||
},
|
||||
{
|
||||
"id": "TASK-9581",
|
||||
"title": "You can't index the port without hacking the cross-platform XSS monitor!",
|
||||
"status": "backlog",
|
||||
"label": "documentation",
|
||||
"priority": "low"
|
||||
},
|
||||
{
|
||||
"id": "TASK-8806",
|
||||
"title": "We need to bypass the back-end SSL panel!",
|
||||
"status": "done",
|
||||
"label": "bug",
|
||||
"priority": "medium"
|
||||
},
|
||||
{
|
||||
"id": "TASK-6542",
|
||||
"title": "Try to quantify the RSS firewall, maybe it will quantify the open-source system!",
|
||||
"status": "done",
|
||||
"label": "feature",
|
||||
"priority": "low"
|
||||
},
|
||||
{
|
||||
"id": "TASK-6806",
|
||||
"title": "The VGA protocol is down, reboot the back-end matrix so we can parse the CSS panel!",
|
||||
"status": "canceled",
|
||||
"label": "documentation",
|
||||
"priority": "low"
|
||||
},
|
||||
{
|
||||
"id": "TASK-9549",
|
||||
"title": "You can't bypass the bus without connecting the neural JBOD bus!",
|
||||
"status": "todo",
|
||||
"label": "feature",
|
||||
"priority": "high"
|
||||
},
|
||||
{
|
||||
"id": "TASK-1075",
|
||||
"title": "Backing up the driver won't do anything, we need to parse the redundant RAM pixel!",
|
||||
"status": "done",
|
||||
"label": "feature",
|
||||
"priority": "medium"
|
||||
},
|
||||
{
|
||||
"id": "TASK-1427",
|
||||
"title": "Use the auxiliary PCI circuit, then you can calculate the cross-platform interface!",
|
||||
"status": "done",
|
||||
"label": "documentation",
|
||||
"priority": "high"
|
||||
},
|
||||
{
|
||||
"id": "TASK-1907",
|
||||
"title": "Hacking the circuit won't do anything, we need to back up the online DRAM system!",
|
||||
"status": "todo",
|
||||
"label": "documentation",
|
||||
"priority": "high"
|
||||
},
|
||||
{
|
||||
"id": "TASK-4309",
|
||||
"title": "If we generate the system, we can get to the TCP sensor through the optical GB pixel!",
|
||||
"status": "backlog",
|
||||
"label": "bug",
|
||||
"priority": "medium"
|
||||
},
|
||||
{
|
||||
"id": "TASK-3973",
|
||||
"title": "I'll parse the back-end ADP array, that should bandwidth the RSS bandwidth!",
|
||||
"status": "todo",
|
||||
"label": "feature",
|
||||
"priority": "medium"
|
||||
},
|
||||
{
|
||||
"id": "TASK-7962",
|
||||
"title": "Use the wireless RAM program, then you can hack the cross-platform feed!",
|
||||
"status": "canceled",
|
||||
"label": "bug",
|
||||
"priority": "low"
|
||||
},
|
||||
{
|
||||
"id": "TASK-3360",
|
||||
"title": "You can't quantify the program without synthesizing the neural OCR interface!",
|
||||
"status": "done",
|
||||
"label": "feature",
|
||||
"priority": "medium"
|
||||
},
|
||||
{
|
||||
"id": "TASK-9887",
|
||||
"title": "Use the auxiliary ASCII sensor, then you can connect the solid state port!",
|
||||
"status": "backlog",
|
||||
"label": "bug",
|
||||
"priority": "medium"
|
||||
},
|
||||
{
|
||||
"id": "TASK-3649",
|
||||
"title": "I'll input the virtual USB system, that should circuit the DNS monitor!",
|
||||
"status": "in progress",
|
||||
"label": "feature",
|
||||
"priority": "medium"
|
||||
},
|
||||
{
|
||||
"id": "TASK-3586",
|
||||
"title": "If we quantify the circuit, we can get to the CLI feed through the mobile SMS hard drive!",
|
||||
"status": "in progress",
|
||||
"label": "bug",
|
||||
"priority": "low"
|
||||
},
|
||||
{
|
||||
"id": "TASK-5150",
|
||||
"title": "I'll hack the wireless XSS port, that should transmitter the IP interface!",
|
||||
"status": "canceled",
|
||||
"label": "feature",
|
||||
"priority": "medium"
|
||||
},
|
||||
{
|
||||
"id": "TASK-3652",
|
||||
"title": "The SQL interface is down, override the optical bus so we can program the ASCII interface!",
|
||||
"status": "backlog",
|
||||
"label": "feature",
|
||||
"priority": "low"
|
||||
},
|
||||
{
|
||||
"id": "TASK-6884",
|
||||
"title": "Use the digital PCI circuit, then you can synthesize the multi-byte microchip!",
|
||||
"status": "canceled",
|
||||
"label": "feature",
|
||||
"priority": "high"
|
||||
},
|
||||
{
|
||||
"id": "TASK-1591",
|
||||
"title": "We need to connect the mobile XSS driver!",
|
||||
"status": "in progress",
|
||||
"label": "feature",
|
||||
"priority": "high"
|
||||
},
|
||||
{
|
||||
"id": "TASK-3802",
|
||||
"title": "Try to override the ASCII protocol, maybe it will parse the virtual matrix!",
|
||||
"status": "in progress",
|
||||
"label": "feature",
|
||||
"priority": "low"
|
||||
},
|
||||
{
|
||||
"id": "TASK-7253",
|
||||
"title": "Programming the capacitor won't do anything, we need to bypass the neural IB hard drive!",
|
||||
"status": "backlog",
|
||||
"label": "bug",
|
||||
"priority": "high"
|
||||
},
|
||||
{
|
||||
"id": "TASK-9739",
|
||||
"title": "We need to hack the multi-byte HDD bus!",
|
||||
"status": "done",
|
||||
"label": "documentation",
|
||||
"priority": "medium"
|
||||
},
|
||||
{
|
||||
"id": "TASK-4424",
|
||||
"title": "Try to hack the HEX alarm, maybe it will connect the optical pixel!",
|
||||
"status": "in progress",
|
||||
"label": "documentation",
|
||||
"priority": "medium"
|
||||
},
|
||||
{
|
||||
"id": "TASK-3922",
|
||||
"title": "You can't back up the capacitor without generating the wireless PCI program!",
|
||||
"status": "backlog",
|
||||
"label": "bug",
|
||||
"priority": "low"
|
||||
},
|
||||
{
|
||||
"id": "TASK-4921",
|
||||
"title": "I'll index the open-source IP feed, that should system the GB application!",
|
||||
"status": "canceled",
|
||||
"label": "bug",
|
||||
"priority": "low"
|
||||
},
|
||||
{
|
||||
"id": "TASK-5814",
|
||||
"title": "We need to calculate the 1080p AGP feed!",
|
||||
"status": "backlog",
|
||||
"label": "bug",
|
||||
"priority": "high"
|
||||
},
|
||||
{
|
||||
"id": "TASK-2645",
|
||||
"title": "Synthesizing the system won't do anything, we need to navigate the multi-byte HDD firewall!",
|
||||
"status": "todo",
|
||||
"label": "documentation",
|
||||
"priority": "medium"
|
||||
},
|
||||
{
|
||||
"id": "TASK-4535",
|
||||
"title": "Try to copy the JSON circuit, maybe it will connect the wireless feed!",
|
||||
"status": "in progress",
|
||||
"label": "feature",
|
||||
"priority": "low"
|
||||
},
|
||||
{
|
||||
"id": "TASK-4463",
|
||||
"title": "We need to copy the solid state AGP monitor!",
|
||||
"status": "done",
|
||||
"label": "documentation",
|
||||
"priority": "low"
|
||||
},
|
||||
{
|
||||
"id": "TASK-9745",
|
||||
"title": "If we connect the protocol, we can get to the GB system through the bluetooth PCI microchip!",
|
||||
"status": "canceled",
|
||||
"label": "feature",
|
||||
"priority": "high"
|
||||
},
|
||||
{
|
||||
"id": "TASK-2080",
|
||||
"title": "If we input the bus, we can get to the RAM matrix through the auxiliary RAM card!",
|
||||
"status": "todo",
|
||||
"label": "bug",
|
||||
"priority": "medium"
|
||||
},
|
||||
{
|
||||
"id": "TASK-3838",
|
||||
"title": "I'll bypass the online TCP application, that should panel the AGP system!",
|
||||
"status": "backlog",
|
||||
"label": "bug",
|
||||
"priority": "high"
|
||||
},
|
||||
{
|
||||
"id": "TASK-1340",
|
||||
"title": "We need to navigate the virtual PNG circuit!",
|
||||
"status": "todo",
|
||||
"label": "bug",
|
||||
"priority": "medium"
|
||||
},
|
||||
{
|
||||
"id": "TASK-6665",
|
||||
"title": "If we parse the monitor, we can get to the SSD hard drive through the cross-platform AGP alarm!",
|
||||
"status": "canceled",
|
||||
"label": "feature",
|
||||
"priority": "low"
|
||||
},
|
||||
{
|
||||
"id": "TASK-7585",
|
||||
"title": "If we calculate the hard drive, we can get to the SSL program through the multi-byte CSS microchip!",
|
||||
"status": "backlog",
|
||||
"label": "feature",
|
||||
"priority": "low"
|
||||
},
|
||||
{
|
||||
"id": "TASK-6319",
|
||||
"title": "We need to copy the multi-byte SCSI program!",
|
||||
"status": "backlog",
|
||||
"label": "bug",
|
||||
"priority": "high"
|
||||
},
|
||||
{
|
||||
"id": "TASK-4369",
|
||||
"title": "Try to input the SCSI bus, maybe it will generate the 1080p pixel!",
|
||||
"status": "backlog",
|
||||
"label": "bug",
|
||||
"priority": "high"
|
||||
},
|
||||
{
|
||||
"id": "TASK-9035",
|
||||
"title": "We need to override the solid state PNG array!",
|
||||
"status": "canceled",
|
||||
"label": "documentation",
|
||||
"priority": "low"
|
||||
},
|
||||
{
|
||||
"id": "TASK-3970",
|
||||
"title": "You can't index the transmitter without quantifying the haptic ASCII card!",
|
||||
"status": "todo",
|
||||
"label": "documentation",
|
||||
"priority": "medium"
|
||||
},
|
||||
{
|
||||
"id": "TASK-4473",
|
||||
"title": "You can't bypass the protocol without overriding the neural RSS program!",
|
||||
"status": "todo",
|
||||
"label": "documentation",
|
||||
"priority": "low"
|
||||
},
|
||||
{
|
||||
"id": "TASK-4136",
|
||||
"title": "You can't hack the hard drive without hacking the primary JSON program!",
|
||||
"status": "canceled",
|
||||
"label": "bug",
|
||||
"priority": "medium"
|
||||
},
|
||||
{
|
||||
"id": "TASK-3939",
|
||||
"title": "Use the back-end SQL firewall, then you can connect the neural hard drive!",
|
||||
"status": "done",
|
||||
"label": "feature",
|
||||
"priority": "low"
|
||||
},
|
||||
{
|
||||
"id": "TASK-2007",
|
||||
"title": "I'll input the back-end USB protocol, that should bandwidth the PCI system!",
|
||||
"status": "backlog",
|
||||
"label": "bug",
|
||||
"priority": "high"
|
||||
},
|
||||
{
|
||||
"id": "TASK-7516",
|
||||
"title": "Use the primary SQL program, then you can generate the auxiliary transmitter!",
|
||||
"status": "done",
|
||||
"label": "documentation",
|
||||
"priority": "medium"
|
||||
},
|
||||
{
|
||||
"id": "TASK-6906",
|
||||
"title": "Try to back up the DRAM system, maybe it will reboot the online transmitter!",
|
||||
"status": "done",
|
||||
"label": "feature",
|
||||
"priority": "high"
|
||||
},
|
||||
{
|
||||
"id": "TASK-5207",
|
||||
"title": "The SMS interface is down, copy the bluetooth bus so we can quantify the VGA card!",
|
||||
"status": "in progress",
|
||||
"label": "bug",
|
||||
"priority": "low"
|
||||
}
|
||||
]
|
||||
65
apps/www/app/examples/tasks/page.tsx
Normal file
65
apps/www/app/examples/tasks/page.tsx
Normal file
@@ -0,0 +1,65 @@
|
||||
import { promises as fs } from "fs"
|
||||
import path from "path"
|
||||
import { Metadata } from "next"
|
||||
import Image from "next/image"
|
||||
import { z } from "zod"
|
||||
|
||||
import { columns } from "./components/columns"
|
||||
import { DataTable } from "./components/data-table"
|
||||
import { UserNav } from "./components/user-nav"
|
||||
import { taskSchema } from "./data/schema"
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Tasks",
|
||||
description: "A task and issue tracker build using Tanstack Table.",
|
||||
}
|
||||
|
||||
// Simulate a database read for tasks.
|
||||
async function getTasks() {
|
||||
const data = await fs.readFile(
|
||||
path.join(process.cwd(), "app/examples/tasks/data/tasks.json")
|
||||
)
|
||||
|
||||
const tasks = JSON.parse(data.toString())
|
||||
|
||||
return z.array(taskSchema).parse(tasks)
|
||||
}
|
||||
|
||||
export default async function TaskPage() {
|
||||
const tasks = await getTasks()
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="md:hidden">
|
||||
<Image
|
||||
src="/examples/tasks-light.png"
|
||||
width={1280}
|
||||
height={998}
|
||||
alt="Playground"
|
||||
className="block dark:hidden"
|
||||
/>
|
||||
<Image
|
||||
src="/examples/tasks-dark.png"
|
||||
width={1280}
|
||||
height={998}
|
||||
alt="Playground"
|
||||
className="hidden dark:block"
|
||||
/>
|
||||
</div>
|
||||
<div className="hidden h-full flex-1 flex-col space-y-8 p-8 md:flex">
|
||||
<div className="flex items-center justify-between space-y-2">
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold tracking-tight">Welcome back!</h2>
|
||||
<p className="text-muted-foreground">
|
||||
Here's a list of your tasks for this month!
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<UserNav />
|
||||
</div>
|
||||
</div>
|
||||
<DataTable data={tasks} columns={columns} />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
import { Metadata } from "next"
|
||||
import Link from "next/link"
|
||||
import { Heart } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { AspectRatio } from "@/components/ui/aspect-ratio"
|
||||
import { buttonVariants } from "@/components/ui/button"
|
||||
import {
|
||||
PageHeader,
|
||||
PageHeaderDescription,
|
||||
PageHeaderHeading,
|
||||
} from "@/components/page-header"
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Figma",
|
||||
description:
|
||||
"Every component recreated in Figma. With customizable props, typography and icons.",
|
||||
}
|
||||
|
||||
export default function FigmaPage() {
|
||||
return (
|
||||
<div className="container pb-10">
|
||||
<PageHeader>
|
||||
<PageHeaderHeading>Grab the free Figma UI Kit.</PageHeaderHeading>
|
||||
<PageHeaderDescription>
|
||||
Every component recreated in Figma. With customizable props,
|
||||
typography and icons. Open sourced by{" "}
|
||||
<Link
|
||||
href="https://twitter.com/skirano"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="font-medium underline underline-offset-4"
|
||||
>
|
||||
Pietro Schirano
|
||||
</Link>
|
||||
.
|
||||
</PageHeaderDescription>
|
||||
</PageHeader>
|
||||
<section className="mb-4 grid items-center gap-6">
|
||||
<div className="flex flex-col space-y-4 sm:flex-row sm:space-x-4 sm:space-y-0 md:flex-row">
|
||||
<Link
|
||||
href="https://www.figma.com/community/file/1203061493325953101"
|
||||
className={buttonVariants({ size: "lg" })}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
Get a copy
|
||||
</Link>
|
||||
<Link
|
||||
href="https://twitter.com/skirano"
|
||||
className={cn(
|
||||
buttonVariants({ size: "lg", variant: "outline" }),
|
||||
"px-5"
|
||||
)}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<Heart className="mr-2 h-4 w-4 fill-current" />
|
||||
Follow Pietro
|
||||
</Link>
|
||||
</div>
|
||||
</section>
|
||||
<AspectRatio ratio={16 / 9} className="w-full">
|
||||
<iframe
|
||||
src="https://embed.figma.com/file/1203061493325953101/hf_embed?community_viewer=true&embed_host=shadcn&hub_file_id=1203061493325953101&kind=&viewer=1"
|
||||
className="h-full w-full overflow-hidden rounded-lg border bg-muted"
|
||||
/>
|
||||
</AspectRatio>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
import Image from "next/image"
|
||||
import Link from "next/link"
|
||||
import { ChevronRight } from "lucide-react"
|
||||
|
||||
import { siteConfig } from "@/config/site"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { buttonVariants } from "@/components/ui/button"
|
||||
import { Separator } from "@/components/ui/separator"
|
||||
import { ExamplesNav } from "@/components/examples-nav"
|
||||
import { Icons } from "@/components/icons"
|
||||
import {
|
||||
@@ -20,33 +22,34 @@ export default function IndexPage() {
|
||||
<div className="container relative pb-10">
|
||||
<StyleSwitcher />
|
||||
<PageHeader>
|
||||
<Link
|
||||
href="/docs/components/data-table"
|
||||
className="inline-flex items-center rounded-lg bg-muted px-3 py-1 text-sm font-medium"
|
||||
>
|
||||
🎉 <Separator className="mx-2 h-4" orientation="vertical" />{" "}
|
||||
Introducing Table and Data Table{" "}
|
||||
<ChevronRight className="ml-1 h-4 w-4" />
|
||||
</Link>
|
||||
<PageHeaderHeading>Build your component library.</PageHeaderHeading>
|
||||
<PageHeaderDescription>
|
||||
Beautifully designed components that you can copy and paste into your
|
||||
apps. Accessible. Customizable. Open Source.
|
||||
</PageHeaderDescription>
|
||||
</PageHeader>
|
||||
<section className="pb-8 md:pb-10">
|
||||
<div className="flex w-full items-center justify-between">
|
||||
<div className="flex space-x-4">
|
||||
<Link href="/docs" className={cn(buttonVariants({ size: "lg" }))}>
|
||||
Get Started
|
||||
</Link>
|
||||
<Link
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
href={siteConfig.links.github}
|
||||
className={cn(
|
||||
buttonVariants({ variant: "outline", size: "lg" }),
|
||||
"pl-6"
|
||||
)}
|
||||
>
|
||||
<Icons.gitHub className="mr-2 h-4 w-4" />
|
||||
GitHub
|
||||
</Link>
|
||||
</div>
|
||||
<div className="flex w-full items-center space-x-4 pb-8 pt-4 md:pb-10">
|
||||
<Link href="/docs" className={cn(buttonVariants())}>
|
||||
Get Started
|
||||
</Link>
|
||||
<Link
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
href={siteConfig.links.github}
|
||||
className={cn(buttonVariants({ variant: "outline" }))}
|
||||
>
|
||||
<Icons.gitHub className="mr-2 h-4 w-4" />
|
||||
GitHub
|
||||
</Link>
|
||||
</div>
|
||||
</section>
|
||||
</PageHeader>
|
||||
<ExamplesNav className="[&>a:first-child]:text-primary" />
|
||||
<section className="space-y-8 overflow-hidden rounded-lg border-2 border-primary dark:border-muted md:hidden">
|
||||
<Image
|
||||
|
||||
@@ -18,12 +18,12 @@ import { ButtonLoading } from "@/components/examples/button/loading"
|
||||
import { ButtonOutline } from "@/components/examples/button/outline"
|
||||
import { ButtonSecondary } from "@/components/examples/button/secondary"
|
||||
import { ButtonWithIcon } from "@/components/examples/button/with-icon"
|
||||
import { CalendarDatePicker } from "@/components/examples/calendar/date-picker"
|
||||
import { CardDemo } from "@/components/examples/card/demo"
|
||||
import { CheckboxDemo } from "@/components/examples/checkbox/demo"
|
||||
import { CollapsibleDemo } from "@/components/examples/collapsible/demo"
|
||||
import { CommandDemo } from "@/components/examples/command/demo"
|
||||
import { ContextMenuDemo } from "@/components/examples/context-menu/demo"
|
||||
import { DatePickerDemo } from "@/components/examples/date-picker/demo"
|
||||
import { DialogDemo } from "@/components/examples/dialog/demo"
|
||||
import { DropdownMenuDemo } from "@/components/examples/dropdown-menu/demo"
|
||||
import { HoverCardDemo } from "@/components/examples/hover-card/demo"
|
||||
@@ -109,7 +109,7 @@ export default function KitchenSinkPage() {
|
||||
</div>
|
||||
</ComponentWrapper>
|
||||
<ComponentWrapper>
|
||||
<CalendarDatePicker />
|
||||
<DatePickerDemo />
|
||||
</ComponentWrapper>
|
||||
<ComponentWrapper>
|
||||
<AccordionDemo />
|
||||
|
||||
@@ -33,7 +33,7 @@ export function ComponentExample({
|
||||
const [, Button] = React.Children.toArray(
|
||||
Code.props.children
|
||||
) as React.ReactElement[]
|
||||
return Button?.props?.value || null
|
||||
return Button?.props?.value || Button?.props?.__rawString__ || null
|
||||
}
|
||||
}, [Code])
|
||||
|
||||
@@ -42,7 +42,7 @@ export function ComponentExample({
|
||||
className={cn("group relative my-4 flex flex-col space-y-2", className)}
|
||||
{...props}
|
||||
>
|
||||
<Tabs defaultValue="preview" className="mr-auto w-full">
|
||||
<Tabs defaultValue="preview" className="relative mr-auto w-full">
|
||||
<div className="flex items-center justify-between pb-3">
|
||||
<TabsList className="w-full justify-start rounded-none border-b bg-transparent p-0">
|
||||
<TabsTrigger
|
||||
@@ -62,9 +62,15 @@ export function ComponentExample({
|
||||
<CopyWithClassNames
|
||||
value={codeString}
|
||||
classNames={extractedClassNames}
|
||||
className="absolute right-4 top-20"
|
||||
/>
|
||||
) : (
|
||||
codeString && <CopyButton value={codeString} />
|
||||
codeString && (
|
||||
<CopyButton
|
||||
value={codeString}
|
||||
className="absolute right-4 top-20"
|
||||
/>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
<TabsContent value="preview" className="rounded-md border">
|
||||
|
||||
@@ -12,7 +12,7 @@ interface ComponentSourceProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
export function ComponentSource({ children, className }: ComponentSourceProps) {
|
||||
return (
|
||||
<CodeBlockWrapper
|
||||
expandButtonTitle="View Primitive"
|
||||
expandButtonTitle="Expand"
|
||||
className={cn("my-6 overflow-hidden rounded-md", className)}
|
||||
>
|
||||
{children}
|
||||
|
||||
@@ -4,6 +4,7 @@ import * as React from "react"
|
||||
import { DropdownMenuTriggerProps } from "@radix-ui/react-dropdown-menu"
|
||||
import { NpmCommands } from "types/unist"
|
||||
|
||||
import { Event, trackEvent } from "@/lib/events"
|
||||
import { cn } from "@/lib/utils"
|
||||
import {
|
||||
DropdownMenu,
|
||||
@@ -16,19 +17,21 @@ import { Icons } from "@/components/icons"
|
||||
interface CopyButtonProps extends React.HTMLAttributes<HTMLButtonElement> {
|
||||
value: string
|
||||
src?: string
|
||||
event?: Event["name"]
|
||||
}
|
||||
|
||||
async function copyToClipboardWithMeta(
|
||||
value: string,
|
||||
meta?: Record<string, unknown>
|
||||
) {
|
||||
async function copyToClipboardWithMeta(value: string, event?: Event) {
|
||||
navigator.clipboard.writeText(value)
|
||||
if (event) {
|
||||
trackEvent(event)
|
||||
}
|
||||
}
|
||||
|
||||
export function CopyButton({
|
||||
value,
|
||||
className,
|
||||
src,
|
||||
event,
|
||||
...props
|
||||
}: CopyButtonProps) {
|
||||
const [hasCopied, setHasCopied] = React.useState(false)
|
||||
@@ -46,9 +49,17 @@ export function CopyButton({
|
||||
className
|
||||
)}
|
||||
onClick={() => {
|
||||
copyToClipboardWithMeta(value, {
|
||||
component: src,
|
||||
})
|
||||
copyToClipboardWithMeta(
|
||||
value,
|
||||
event
|
||||
? {
|
||||
name: event,
|
||||
properties: {
|
||||
code: value,
|
||||
},
|
||||
}
|
||||
: undefined
|
||||
)
|
||||
setHasCopied(true)
|
||||
}}
|
||||
{...props}
|
||||
@@ -104,7 +115,7 @@ export function CopyWithClassNames({
|
||||
)}
|
||||
<span className="sr-only">Copy</span>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem onClick={() => copyToClipboard(value)}>
|
||||
<Icons.react className="mr-2 h-4 w-4" />
|
||||
<span>Component</span>
|
||||
@@ -135,10 +146,19 @@ export function CopyNpmCommandButton({
|
||||
}, 2000)
|
||||
}, [hasCopied])
|
||||
|
||||
const copyCommand = React.useCallback((value: string) => {
|
||||
copyToClipboardWithMeta(value)
|
||||
setHasCopied(true)
|
||||
}, [])
|
||||
const copyCommand = React.useCallback(
|
||||
(value: string, pm: "npm" | "pnpm" | "yarn") => {
|
||||
copyToClipboardWithMeta(value, {
|
||||
name: "copy_npm_command",
|
||||
properties: {
|
||||
command: value,
|
||||
pm,
|
||||
},
|
||||
})
|
||||
setHasCopied(true)
|
||||
},
|
||||
[]
|
||||
)
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
@@ -156,16 +176,22 @@ export function CopyNpmCommandButton({
|
||||
)}
|
||||
<span className="sr-only">Copy</span>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuItem onClick={() => copyCommand(commands.__npmCommand__)}>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem
|
||||
onClick={() => copyCommand(commands.__npmCommand__, "npm")}
|
||||
>
|
||||
<Icons.npm className="mr-2 h-4 w-4" />
|
||||
<span>npm</span>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => copyCommand(commands.__yarnCommand__)}>
|
||||
<DropdownMenuItem
|
||||
onClick={() => copyCommand(commands.__yarnCommand__, "yarn")}
|
||||
>
|
||||
<Icons.yarn className="mr-2 h-4 w-4" />
|
||||
<span>yarn</span>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => copyCommand(commands.__pnpmCommand__)}>
|
||||
<DropdownMenuItem
|
||||
onClick={() => copyCommand(commands.__pnpmCommand__, "pnpm")}
|
||||
>
|
||||
<Icons.pnpm className="mr-2 h-4 w-4" />
|
||||
<span>pnpm</span>
|
||||
</DropdownMenuItem>
|
||||
|
||||
@@ -15,6 +15,11 @@ const examples = [
|
||||
name: "Cards",
|
||||
href: "/examples/cards",
|
||||
},
|
||||
{
|
||||
name: "Tasks",
|
||||
href: "/examples/tasks",
|
||||
label: "New",
|
||||
},
|
||||
{
|
||||
name: "Playground",
|
||||
href: "/examples/playground",
|
||||
@@ -42,13 +47,18 @@ export function ExamplesNav({ className, ...props }: ExamplesNavProps) {
|
||||
href={example.href}
|
||||
key={example.href}
|
||||
className={cn(
|
||||
"flex px-4 font-medium",
|
||||
"flex items-center px-4",
|
||||
pathname === example.href
|
||||
? "text-primary"
|
||||
: "text-muted-foreground"
|
||||
? "font-bold text-primary"
|
||||
: "font-medium text-muted-foreground"
|
||||
)}
|
||||
>
|
||||
{example.name}
|
||||
{example.name}{" "}
|
||||
{example.label && (
|
||||
<span className="ml-2 rounded-md bg-[#adfa1d] px-1.5 py-0.5 text-xs font-medium leading-none text-[#000000] no-underline group-hover:no-underline">
|
||||
{example.label}
|
||||
</span>
|
||||
)}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
|
||||
11
apps/www/components/examples/button/as-child.tsx
Normal file
11
apps/www/components/examples/button/as-child.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import Link from "next/link"
|
||||
|
||||
import { Button } from "@/components/ui/button"
|
||||
|
||||
export function ButtonAsChild() {
|
||||
return (
|
||||
<Button asChild>
|
||||
<Link href="/login">Login</Link>
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
@@ -41,7 +41,7 @@ const frameworks = [
|
||||
},
|
||||
]
|
||||
|
||||
export function CommandCombobox() {
|
||||
export function ComboboxDemo() {
|
||||
const [open, setOpen] = React.useState(false)
|
||||
const [value, setValue] = React.useState("")
|
||||
|
||||
@@ -36,7 +36,7 @@ const labels = [
|
||||
"maintenance",
|
||||
]
|
||||
|
||||
export function CommandDropdownMenu() {
|
||||
export function ComboboxDropdownMenu() {
|
||||
const [label, setLabel] = React.useState("feature")
|
||||
const [open, setOpen] = React.useState(false)
|
||||
|
||||
@@ -60,7 +60,7 @@ const statuses: Status[] = [
|
||||
},
|
||||
]
|
||||
|
||||
export function CommandPopover() {
|
||||
export function ComboboxPopover() {
|
||||
const [open, setOpen] = React.useState(false)
|
||||
const [selectedStatus, setSelectedStatus] = React.useState<Status | null>(
|
||||
null
|
||||
110
apps/www/components/examples/data-table/columns.tsx
Normal file
110
apps/www/components/examples/data-table/columns.tsx
Normal file
@@ -0,0 +1,110 @@
|
||||
"use client"
|
||||
|
||||
import { ColumnDef } from "@tanstack/react-table"
|
||||
import { ArrowUpDown, MoreHorizontal } from "lucide-react"
|
||||
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Checkbox } from "@/components/ui/checkbox"
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu"
|
||||
|
||||
export type Payment = {
|
||||
id: string
|
||||
amount: number
|
||||
status: "pending" | "processing" | "success" | "failed"
|
||||
email: string
|
||||
}
|
||||
|
||||
export const columns: ColumnDef<Payment>[] = [
|
||||
{
|
||||
id: "select",
|
||||
header: ({ table }) => (
|
||||
<Checkbox
|
||||
checked={table.getIsAllPageRowsSelected()}
|
||||
onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
|
||||
aria-label="Select all"
|
||||
/>
|
||||
),
|
||||
cell: ({ row }) => (
|
||||
<Checkbox
|
||||
checked={row.getIsSelected()}
|
||||
onCheckedChange={(value) => row.toggleSelected(!!value)}
|
||||
aria-label="Select row"
|
||||
/>
|
||||
),
|
||||
enableSorting: false,
|
||||
enableHiding: false,
|
||||
},
|
||||
{
|
||||
accessorKey: "status",
|
||||
header: "Status",
|
||||
cell: ({ row }) => (
|
||||
<div className="capitalize">{row.getValue("status")}</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: "email",
|
||||
header: ({ column }) => {
|
||||
return (
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
|
||||
>
|
||||
Email
|
||||
<ArrowUpDown className="ml-2 h-4 w-4" />
|
||||
</Button>
|
||||
)
|
||||
},
|
||||
cell: ({ row }) => <div className="lowercase">{row.getValue("email")}</div>,
|
||||
},
|
||||
{
|
||||
accessorKey: "amount",
|
||||
header: () => <div className="text-right">Amount</div>,
|
||||
cell: ({ row }) => {
|
||||
const amount = parseFloat(row.getValue("amount"))
|
||||
|
||||
// Format the amount as a dollar amount
|
||||
const formatted = new Intl.NumberFormat("en-US", {
|
||||
style: "currency",
|
||||
currency: "USD",
|
||||
}).format(amount)
|
||||
|
||||
return <div className="text-right font-medium">{formatted}</div>
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "actions",
|
||||
enableHiding: false,
|
||||
cell: ({ row }) => {
|
||||
const payment = row.original
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="ghost" className="h-8 w-8 p-0">
|
||||
<span className="sr-only">Open menu</span>
|
||||
<MoreHorizontal className="h-4 w-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuLabel>Actions</DropdownMenuLabel>
|
||||
<DropdownMenuItem
|
||||
onClick={() => navigator.clipboard.writeText(payment.id)}
|
||||
>
|
||||
Copy payment ID
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem>View customer</DropdownMenuItem>
|
||||
<DropdownMenuItem>View payment details</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)
|
||||
},
|
||||
},
|
||||
]
|
||||
185
apps/www/components/examples/data-table/data-table.tsx
Normal file
185
apps/www/components/examples/data-table/data-table.tsx
Normal file
@@ -0,0 +1,185 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import {
|
||||
ColumnDef,
|
||||
ColumnFiltersState,
|
||||
SortingState,
|
||||
VisibilityState,
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
getFilteredRowModel,
|
||||
getPaginationRowModel,
|
||||
getSortedRowModel,
|
||||
useReactTable,
|
||||
} from "@tanstack/react-table"
|
||||
import { ChevronDown } from "lucide-react"
|
||||
|
||||
import { Button } from "@/components/ui/button"
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuCheckboxItem,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/components/ui/table"
|
||||
|
||||
interface DataTableProps<TData, TValue> {
|
||||
columns: ColumnDef<TData, TValue>[]
|
||||
data: TData[]
|
||||
}
|
||||
|
||||
export function DataTable<TData, TValue>({
|
||||
columns,
|
||||
data,
|
||||
}: DataTableProps<TData, TValue>) {
|
||||
const [sorting, setSorting] = React.useState<SortingState>([])
|
||||
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
||||
[]
|
||||
)
|
||||
const [columnVisibility, setColumnVisibility] =
|
||||
React.useState<VisibilityState>({})
|
||||
const [rowSelection, setRowSelection] = React.useState({})
|
||||
|
||||
const table = useReactTable({
|
||||
data,
|
||||
columns,
|
||||
onSortingChange: setSorting,
|
||||
onColumnFiltersChange: setColumnFilters,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
getFilteredRowModel: getFilteredRowModel(),
|
||||
onColumnVisibilityChange: setColumnVisibility,
|
||||
onRowSelectionChange: setRowSelection,
|
||||
state: {
|
||||
sorting,
|
||||
columnFilters,
|
||||
columnVisibility,
|
||||
rowSelection,
|
||||
},
|
||||
})
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
<div className="flex items-center py-4">
|
||||
<Input
|
||||
placeholder="Filter emails..."
|
||||
value={(table.getColumn("email")?.getFilterValue() as string) ?? ""}
|
||||
onChange={(event) =>
|
||||
table.getColumn("email")?.setFilterValue(event.target.value)
|
||||
}
|
||||
className="max-w-sm"
|
||||
/>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="outline" className="ml-auto">
|
||||
Columns <ChevronDown className="ml-2 h-4 w-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
{table
|
||||
.getAllColumns()
|
||||
.filter((column) => column.getCanHide())
|
||||
.map((column) => {
|
||||
return (
|
||||
<DropdownMenuCheckboxItem
|
||||
key={column.id}
|
||||
className="capitalize"
|
||||
checked={column.getIsVisible()}
|
||||
onCheckedChange={(value) =>
|
||||
column.toggleVisibility(!!value)
|
||||
}
|
||||
>
|
||||
{column.id}
|
||||
</DropdownMenuCheckboxItem>
|
||||
)
|
||||
})}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
<div className="rounded-md border">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<TableRow key={headerGroup.id}>
|
||||
{headerGroup.headers.map((header) => {
|
||||
return (
|
||||
<TableHead key={header.id}>
|
||||
{header.isPlaceholder
|
||||
? null
|
||||
: flexRender(
|
||||
header.column.columnDef.header,
|
||||
header.getContext()
|
||||
)}
|
||||
</TableHead>
|
||||
)
|
||||
})}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{table.getRowModel().rows?.length ? (
|
||||
table.getRowModel().rows.map((row) => (
|
||||
<TableRow
|
||||
key={row.id}
|
||||
data-state={row.getIsSelected() && "selected"}
|
||||
>
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
<TableCell key={cell.id}>
|
||||
{flexRender(
|
||||
cell.column.columnDef.cell,
|
||||
cell.getContext()
|
||||
)}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
))
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell
|
||||
colSpan={columns.length}
|
||||
className="h-24 text-center"
|
||||
>
|
||||
No results.
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
<div className="flex items-center justify-end space-x-2 py-4">
|
||||
<div className="flex-1 text-sm text-muted-foreground">
|
||||
{table.getFilteredSelectedRowModel().rows.length} of{" "}
|
||||
{table.getFilteredRowModel().rows.length} row(s) selected.
|
||||
</div>
|
||||
<div className="space-x-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => table.previousPage()}
|
||||
disabled={!table.getCanPreviousPage()}
|
||||
>
|
||||
Previous
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => table.nextPage()}
|
||||
disabled={!table.getCanNextPage()}
|
||||
>
|
||||
Next
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
41
apps/www/components/examples/data-table/demo.tsx
Normal file
41
apps/www/components/examples/data-table/demo.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import { faker } from "@faker-js/faker"
|
||||
|
||||
import { Payment, columns } from "@/components/examples/data-table/columns"
|
||||
import { DataTable } from "@/components/examples/data-table/data-table"
|
||||
|
||||
const data: Payment[] = [
|
||||
{
|
||||
id: "m5gr84i9",
|
||||
amount: 316,
|
||||
status: "success",
|
||||
email: "ken99@yahoo.com",
|
||||
},
|
||||
{
|
||||
id: "3u1reuv4",
|
||||
amount: 242,
|
||||
status: "success",
|
||||
email: "Abe45@gmail.com",
|
||||
},
|
||||
{
|
||||
id: "derv1ws0",
|
||||
amount: 837,
|
||||
status: "processing",
|
||||
email: "Monserrat44@gmail.com",
|
||||
},
|
||||
{
|
||||
id: "5kma53ae",
|
||||
amount: 874,
|
||||
status: "success",
|
||||
email: "Silas22@gmail.com",
|
||||
},
|
||||
{
|
||||
id: "bhqecj4p",
|
||||
amount: 721,
|
||||
status: "failed",
|
||||
email: "carmella@hotmail.com",
|
||||
},
|
||||
]
|
||||
|
||||
export function DataTableDemo() {
|
||||
return <DataTable columns={columns} data={data} />
|
||||
}
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
PopoverTrigger,
|
||||
} from "@/components/ui/popover"
|
||||
|
||||
export function CalendarDatePicker() {
|
||||
export function DatePickerDemo() {
|
||||
const [date, setDate] = React.useState<Date>()
|
||||
|
||||
return (
|
||||
@@ -20,7 +20,7 @@ import {
|
||||
SelectValue,
|
||||
} from "@/components/ui/select"
|
||||
|
||||
export function CalendarDatePickerWithPresets() {
|
||||
export function DatePickerWithPresets() {
|
||||
const [date, setDate] = React.useState<Date>()
|
||||
|
||||
return (
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
PopoverTrigger,
|
||||
} from "@/components/ui/popover"
|
||||
|
||||
export function CalendarDateRangePicker({
|
||||
export function DatePickerWithRange({
|
||||
className,
|
||||
}: React.HTMLAttributes<HTMLDivElement>) {
|
||||
const [date, setDate] = React.useState<DateRange | undefined>({
|
||||
@@ -8,6 +8,7 @@ import { BadgeDemo } from "@/components/examples/badge/demo"
|
||||
import { BadgeDestructive } from "@/components/examples/badge/destructive"
|
||||
import { BadgeOutline } from "@/components/examples/badge/outline"
|
||||
import { BadgeSecondary } from "@/components/examples/badge/secondary"
|
||||
import { ButtonAsChild } from "@/components/examples/button/as-child"
|
||||
import { ButtonDemo } from "@/components/examples/button/demo"
|
||||
import { ButtonDestructive } from "@/components/examples/button/destructive"
|
||||
import { ButtonGhost } from "@/components/examples/button/ghost"
|
||||
@@ -16,22 +17,23 @@ import { ButtonLoading } from "@/components/examples/button/loading"
|
||||
import { ButtonOutline } from "@/components/examples/button/outline"
|
||||
import { ButtonSecondary } from "@/components/examples/button/secondary"
|
||||
import { ButtonWithIcon } from "@/components/examples/button/with-icon"
|
||||
import { CalendarDatePicker } from "@/components/examples/calendar/date-picker"
|
||||
import { CalendarDateRangePicker } from "@/components/examples/calendar/date-range-picker"
|
||||
import { CalendarDemo } from "@/components/examples/calendar/demo"
|
||||
import { CalendarDatePickerWithPresets } from "@/components/examples/calendar/with-presets"
|
||||
import { CardDemo } from "@/components/examples/card/demo"
|
||||
import { CardWithForm } from "@/components/examples/card/with-form"
|
||||
import { CheckboxDemo } from "@/components/examples/checkbox/demo"
|
||||
import { CheckboxDisabled } from "@/components/examples/checkbox/disabled"
|
||||
import { CheckboxWithText } from "@/components/examples/checkbox/with-text"
|
||||
import { CollapsibleDemo } from "@/components/examples/collapsible/demo"
|
||||
import { CommandCombobox } from "@/components/examples/command/combobox"
|
||||
import { ComboboxDemo } from "@/components/examples/combobox/demo"
|
||||
import { ComboboxDropdownMenu } from "@/components/examples/combobox/dropdown-menu"
|
||||
import { ComboboxPopover } from "@/components/examples/combobox/popover"
|
||||
import { CommandDemo } from "@/components/examples/command/demo"
|
||||
import { CommandDialogDemo } from "@/components/examples/command/dialog"
|
||||
import { CommandDropdownMenu } from "@/components/examples/command/dropdown-menu"
|
||||
import { CommandPopover } from "@/components/examples/command/popover"
|
||||
import { ContextMenuDemo } from "@/components/examples/context-menu/demo"
|
||||
import { DataTableDemo } from "@/components/examples/data-table/demo"
|
||||
import { DatePickerDemo } from "@/components/examples/date-picker/demo"
|
||||
import { DatePickerWithPresets } from "@/components/examples/date-picker/with-presets"
|
||||
import { DatePickerWithRange } from "@/components/examples/date-picker/with-range"
|
||||
import { DialogDemo } from "@/components/examples/dialog/demo"
|
||||
import { DropdownMenuCheckboxes } from "@/components/examples/dropdown-menu/checkboxes"
|
||||
import { DropdownMenuDemo } from "@/components/examples/dropdown-menu/demo"
|
||||
@@ -58,6 +60,7 @@ import { SheetSize } from "@/components/examples/sheet/size"
|
||||
import { SkeletonDemo } from "@/components/examples/skeleton/demo"
|
||||
import { SliderDemo } from "@/components/examples/slider/demo"
|
||||
import { SwitchDemo } from "@/components/examples/switch/demo"
|
||||
import { TableDemo } from "@/components/examples/table/demo"
|
||||
import { TabsDemo } from "@/components/examples/tabs/demo"
|
||||
import { TextareaDemo } from "@/components/examples/textarea/demo"
|
||||
import { TextareaDisabled } from "@/components/examples/textarea/disabled"
|
||||
@@ -110,10 +113,12 @@ export const examples = {
|
||||
ButtonOutline,
|
||||
ButtonSecondary,
|
||||
ButtonWithIcon,
|
||||
ButtonAsChild,
|
||||
CalendarDemo,
|
||||
CalendarDatePicker,
|
||||
CalendarDateRangePicker,
|
||||
CalendarDatePickerWithPresets,
|
||||
DataTableDemo,
|
||||
DatePickerDemo,
|
||||
DatePickerWithRange,
|
||||
DatePickerWithPresets,
|
||||
CardDemo,
|
||||
CardWithForm,
|
||||
CheckboxDemo,
|
||||
@@ -122,9 +127,9 @@ export const examples = {
|
||||
CollapsibleDemo,
|
||||
CommandDemo,
|
||||
CommandDialogDemo,
|
||||
CommandCombobox,
|
||||
CommandPopover,
|
||||
CommandDropdownMenu,
|
||||
ComboboxDemo,
|
||||
ComboboxPopover,
|
||||
ComboboxDropdownMenu,
|
||||
ContextMenuDemo,
|
||||
DialogDemo,
|
||||
DropdownMenuCheckboxes,
|
||||
@@ -152,6 +157,7 @@ export const examples = {
|
||||
SkeletonDemo,
|
||||
SliderDemo,
|
||||
SwitchDemo,
|
||||
TableDemo,
|
||||
TabsDemo,
|
||||
TextareaDemo,
|
||||
TextareaDisabled,
|
||||
|
||||
80
apps/www/components/examples/table/demo.tsx
Normal file
80
apps/www/components/examples/table/demo.tsx
Normal file
@@ -0,0 +1,80 @@
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCaption,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/components/ui/table"
|
||||
|
||||
const invoices = [
|
||||
{
|
||||
invoice: "INV001",
|
||||
paymentStatus: "Paid",
|
||||
totalAmount: "$250.00",
|
||||
paymentMethod: "Credit Card",
|
||||
},
|
||||
{
|
||||
invoice: "INV002",
|
||||
paymentStatus: "Pending",
|
||||
totalAmount: "$150.00",
|
||||
paymentMethod: "PayPal",
|
||||
},
|
||||
{
|
||||
invoice: "INV003",
|
||||
paymentStatus: "Unpaid",
|
||||
totalAmount: "$350.00",
|
||||
paymentMethod: "Bank Transfer",
|
||||
},
|
||||
{
|
||||
invoice: "INV004",
|
||||
paymentStatus: "Paid",
|
||||
totalAmount: "$450.00",
|
||||
paymentMethod: "Credit Card",
|
||||
},
|
||||
{
|
||||
invoice: "INV005",
|
||||
paymentStatus: "Paid",
|
||||
totalAmount: "$550.00",
|
||||
paymentMethod: "PayPal",
|
||||
},
|
||||
{
|
||||
invoice: "INV006",
|
||||
paymentStatus: "Pending",
|
||||
totalAmount: "$200.00",
|
||||
paymentMethod: "Bank Transfer",
|
||||
},
|
||||
{
|
||||
invoice: "INV007",
|
||||
paymentStatus: "Unpaid",
|
||||
totalAmount: "$300.00",
|
||||
paymentMethod: "Credit Card",
|
||||
},
|
||||
]
|
||||
|
||||
export function TableDemo() {
|
||||
return (
|
||||
<Table>
|
||||
<TableCaption>A list of your recent invoices.</TableCaption>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead className="w-[100px]">Invoice</TableHead>
|
||||
<TableHead>Status</TableHead>
|
||||
<TableHead>Method</TableHead>
|
||||
<TableHead className="text-right">Amount</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{invoices.map((invoice) => (
|
||||
<TableRow key={invoice.invoice}>
|
||||
<TableCell className="font-medium">{invoice.invoice}</TableCell>
|
||||
<TableCell>{invoice.paymentStatus}</TableCell>
|
||||
<TableCell>{invoice.paymentMethod}</TableCell>
|
||||
<TableCell className="text-right">{invoice.totalAmount}</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
)
|
||||
}
|
||||
@@ -95,27 +95,42 @@ export const Icons = {
|
||||
),
|
||||
npm: (props: LucideProps) => (
|
||||
<svg viewBox="0 0 24 24" {...props}>
|
||||
<path d="M1.763 0C.786 0 0 .786 0 1.763v20.474C0 23.214.786 24 1.763 24h20.474c.977 0 1.763-.786 1.763-1.763V1.763C24 .786 23.214 0 22.237 0zM5.13 5.323l13.837.019-.009 13.836h-3.464l.01-10.382h-3.456L12.04 19.17H5.113z" />
|
||||
<path
|
||||
d="M1.763 0C.786 0 0 .786 0 1.763v20.474C0 23.214.786 24 1.763 24h20.474c.977 0 1.763-.786 1.763-1.763V1.763C24 .786 23.214 0 22.237 0zM5.13 5.323l13.837.019-.009 13.836h-3.464l.01-10.382h-3.456L12.04 19.17H5.113z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
),
|
||||
yarn: (props: LucideProps) => (
|
||||
<svg viewBox="0 0 24 24" {...props}>
|
||||
<path d="M12 0C5.375 0 0 5.375 0 12s5.375 12 12 12 12-5.375 12-12S18.625 0 12 0zm.768 4.105c.183 0 .363.053.525.157.125.083.287.185.755 1.154.31-.088.468-.042.551-.019.204.056.366.19.463.375.477.917.542 2.553.334 3.605-.241 1.232-.755 2.029-1.131 2.576.324.329.778.899 1.117 1.825.278.774.31 1.478.273 2.015a5.51 5.51 0 0 0 .602-.329c.593-.366 1.487-.917 2.553-.931.714-.009 1.269.445 1.353 1.103a1.23 1.23 0 0 1-.945 1.362c-.649.158-.95.278-1.821.843-1.232.797-2.539 1.242-3.012 1.39a1.686 1.686 0 0 1-.704.343c-.737.181-3.266.315-3.466.315h-.046c-.783 0-1.214-.241-1.45-.491-.658.329-1.51.19-2.122-.134a1.078 1.078 0 0 1-.58-1.153 1.243 1.243 0 0 1-.153-.195c-.162-.25-.528-.936-.454-1.946.056-.723.556-1.367.88-1.71a5.522 5.522 0 0 1 .408-2.256c.306-.727.885-1.348 1.32-1.737-.32-.537-.644-1.367-.329-2.21.227-.602.412-.936.82-1.08h-.005c.199-.074.389-.153.486-.259a3.418 3.418 0 0 1 2.298-1.103c.037-.093.079-.185.125-.283.31-.658.639-1.029 1.024-1.168a.94.94 0 0 1 .328-.06zm.006.7c-.507.016-1.001 1.519-1.001 1.519s-1.27-.204-2.266.871c-.199.218-.468.334-.746.44-.079.028-.176.023-.417.672-.371.991.625 2.094.625 2.094s-1.186.839-1.626 1.881c-.486 1.144-.338 2.261-.338 2.261s-.843.732-.899 1.487c-.051.663.139 1.2.343 1.515.227.343.51.176.51.176s-.561.653-.037.931c.477.25 1.283.394 1.71-.037.31-.31.371-1.001.486-1.283.028-.065.12.111.209.199.097.093.264.195.264.195s-.755.324-.445 1.066c.102.246.468.403 1.066.398.222-.005 2.664-.139 3.313-.296.375-.088.505-.283.505-.283s1.566-.431 2.998-1.357c.917-.598 1.293-.76 2.034-.936.612-.148.57-1.098-.241-1.084-.839.009-1.575.44-2.196.825-1.163.718-1.742.672-1.742.672l-.018-.032c-.079-.13.371-1.293-.134-2.678-.547-1.515-1.413-1.881-1.344-1.997.297-.5 1.038-1.297 1.334-2.78.176-.899.13-2.377-.269-3.151-.074-.144-.732.241-.732.241s-.616-1.371-.788-1.483a.271.271 0 0 0-.157-.046z" />
|
||||
<path
|
||||
d="M12 0C5.375 0 0 5.375 0 12s5.375 12 12 12 12-5.375 12-12S18.625 0 12 0zm.768 4.105c.183 0 .363.053.525.157.125.083.287.185.755 1.154.31-.088.468-.042.551-.019.204.056.366.19.463.375.477.917.542 2.553.334 3.605-.241 1.232-.755 2.029-1.131 2.576.324.329.778.899 1.117 1.825.278.774.31 1.478.273 2.015a5.51 5.51 0 0 0 .602-.329c.593-.366 1.487-.917 2.553-.931.714-.009 1.269.445 1.353 1.103a1.23 1.23 0 0 1-.945 1.362c-.649.158-.95.278-1.821.843-1.232.797-2.539 1.242-3.012 1.39a1.686 1.686 0 0 1-.704.343c-.737.181-3.266.315-3.466.315h-.046c-.783 0-1.214-.241-1.45-.491-.658.329-1.51.19-2.122-.134a1.078 1.078 0 0 1-.58-1.153 1.243 1.243 0 0 1-.153-.195c-.162-.25-.528-.936-.454-1.946.056-.723.556-1.367.88-1.71a5.522 5.522 0 0 1 .408-2.256c.306-.727.885-1.348 1.32-1.737-.32-.537-.644-1.367-.329-2.21.227-.602.412-.936.82-1.08h-.005c.199-.074.389-.153.486-.259a3.418 3.418 0 0 1 2.298-1.103c.037-.093.079-.185.125-.283.31-.658.639-1.029 1.024-1.168a.94.94 0 0 1 .328-.06zm.006.7c-.507.016-1.001 1.519-1.001 1.519s-1.27-.204-2.266.871c-.199.218-.468.334-.746.44-.079.028-.176.023-.417.672-.371.991.625 2.094.625 2.094s-1.186.839-1.626 1.881c-.486 1.144-.338 2.261-.338 2.261s-.843.732-.899 1.487c-.051.663.139 1.2.343 1.515.227.343.51.176.51.176s-.561.653-.037.931c.477.25 1.283.394 1.71-.037.31-.31.371-1.001.486-1.283.028-.065.12.111.209.199.097.093.264.195.264.195s-.755.324-.445 1.066c.102.246.468.403 1.066.398.222-.005 2.664-.139 3.313-.296.375-.088.505-.283.505-.283s1.566-.431 2.998-1.357c.917-.598 1.293-.76 2.034-.936.612-.148.57-1.098-.241-1.084-.839.009-1.575.44-2.196.825-1.163.718-1.742.672-1.742.672l-.018-.032c-.079-.13.371-1.293-.134-2.678-.547-1.515-1.413-1.881-1.344-1.997.297-.5 1.038-1.297 1.334-2.78.176-.899.13-2.377-.269-3.151-.074-.144-.732.241-.732.241s-.616-1.371-.788-1.483a.271.271 0 0 0-.157-.046z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
),
|
||||
pnpm: (props: LucideProps) => (
|
||||
<svg viewBox="0 0 24 24" {...props}>
|
||||
<path d="M0 0v7.5h7.5V0zm8.25 0v7.5h7.498V0zm8.25 0v7.5H24V0zM8.25 8.25v7.5h7.498v-7.5zm8.25 0v7.5H24v-7.5zM0 16.5V24h7.5v-7.5zm8.25 0V24h7.498v-7.5zm8.25 0V24H24v-7.5z" />
|
||||
<path
|
||||
d="M0 0v7.5h7.5V0zm8.25 0v7.5h7.498V0zm8.25 0v7.5H24V0zM8.25 8.25v7.5h7.498v-7.5zm8.25 0v7.5H24v-7.5zM0 16.5V24h7.5v-7.5zm8.25 0V24h7.498v-7.5zm8.25 0V24H24v-7.5z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
),
|
||||
react: (props: LucideProps) => (
|
||||
<svg viewBox="0 0 24 24" {...props}>
|
||||
<path d="M14.23 12.004a2.236 2.236 0 0 1-2.235 2.236 2.236 2.236 0 0 1-2.236-2.236 2.236 2.236 0 0 1 2.235-2.236 2.236 2.236 0 0 1 2.236 2.236zm2.648-10.69c-1.346 0-3.107.96-4.888 2.622-1.78-1.653-3.542-2.602-4.887-2.602-.41 0-.783.093-1.106.278-1.375.793-1.683 3.264-.973 6.365C1.98 8.917 0 10.42 0 12.004c0 1.59 1.99 3.097 5.043 4.03-.704 3.113-.39 5.588.988 6.38.32.187.69.275 1.102.275 1.345 0 3.107-.96 4.888-2.624 1.78 1.654 3.542 2.603 4.887 2.603.41 0 .783-.09 1.106-.275 1.374-.792 1.683-3.263.973-6.365C22.02 15.096 24 13.59 24 12.004c0-1.59-1.99-3.097-5.043-4.032.704-3.11.39-5.587-.988-6.38-.318-.184-.688-.277-1.092-.278zm-.005 1.09v.006c.225 0 .406.044.558.127.666.382.955 1.835.73 3.704-.054.46-.142.945-.25 1.44-.96-.236-2.006-.417-3.107-.534-.66-.905-1.345-1.727-2.035-2.447 1.592-1.48 3.087-2.292 4.105-2.295zm-9.77.02c1.012 0 2.514.808 4.11 2.28-.686.72-1.37 1.537-2.02 2.442-1.107.117-2.154.298-3.113.538-.112-.49-.195-.964-.254-1.42-.23-1.868.054-3.32.714-3.707.19-.09.4-.127.563-.132zm4.882 3.05c.455.468.91.992 1.36 1.564-.44-.02-.89-.034-1.345-.034-.46 0-.915.01-1.36.034.44-.572.895-1.096 1.345-1.565zM12 8.1c.74 0 1.477.034 2.202.093.406.582.802 1.203 1.183 1.86.372.64.71 1.29 1.018 1.946-.308.655-.646 1.31-1.013 1.95-.38.66-.773 1.288-1.18 1.87-.728.063-1.466.098-2.21.098-.74 0-1.477-.035-2.202-.093-.406-.582-.802-1.204-1.183-1.86-.372-.64-.71-1.29-1.018-1.946.303-.657.646-1.313 1.013-1.954.38-.66.773-1.286 1.18-1.868.728-.064 1.466-.098 2.21-.098zm-3.635.254c-.24.377-.48.763-.704 1.16-.225.39-.435.782-.635 1.174-.265-.656-.49-1.31-.676-1.947.64-.15 1.315-.283 2.015-.386zm7.26 0c.695.103 1.365.23 2.006.387-.18.632-.405 1.282-.66 1.933-.2-.39-.41-.783-.64-1.174-.225-.392-.465-.774-.705-1.146zm3.063.675c.484.15.944.317 1.375.498 1.732.74 2.852 1.708 2.852 2.476-.005.768-1.125 1.74-2.857 2.475-.42.18-.88.342-1.355.493-.28-.958-.646-1.956-1.1-2.98.45-1.017.81-2.01 1.085-2.964zm-13.395.004c.278.96.645 1.957 1.1 2.98-.45 1.017-.812 2.01-1.086 2.964-.484-.15-.944-.318-1.37-.5-1.732-.737-2.852-1.706-2.852-2.474 0-.768 1.12-1.742 2.852-2.476.42-.18.88-.342 1.356-.494zm11.678 4.28c.265.657.49 1.312.676 1.948-.64.157-1.316.29-2.016.39.24-.375.48-.762.705-1.158.225-.39.435-.788.636-1.18zm-9.945.02c.2.392.41.783.64 1.175.23.39.465.772.705 1.143-.695-.102-1.365-.23-2.006-.386.18-.63.406-1.282.66-1.933zM17.92 16.32c.112.493.2.968.254 1.423.23 1.868-.054 3.32-.714 3.708-.147.09-.338.128-.563.128-1.012 0-2.514-.807-4.11-2.28.686-.72 1.37-1.536 2.02-2.44 1.107-.118 2.154-.3 3.113-.54zm-11.83.01c.96.234 2.006.415 3.107.532.66.905 1.345 1.727 2.035 2.446-1.595 1.483-3.092 2.295-4.11 2.295-.22-.005-.406-.05-.553-.132-.666-.38-.955-1.834-.73-3.703.054-.46.142-.944.25-1.438zm4.56.64c.44.02.89.034 1.345.034.46 0 .915-.01 1.36-.034-.44.572-.895 1.095-1.345 1.565-.455-.47-.91-.993-1.36-1.565z" />
|
||||
<path
|
||||
d="M14.23 12.004a2.236 2.236 0 0 1-2.235 2.236 2.236 2.236 0 0 1-2.236-2.236 2.236 2.236 0 0 1 2.235-2.236 2.236 2.236 0 0 1 2.236 2.236zm2.648-10.69c-1.346 0-3.107.96-4.888 2.622-1.78-1.653-3.542-2.602-4.887-2.602-.41 0-.783.093-1.106.278-1.375.793-1.683 3.264-.973 6.365C1.98 8.917 0 10.42 0 12.004c0 1.59 1.99 3.097 5.043 4.03-.704 3.113-.39 5.588.988 6.38.32.187.69.275 1.102.275 1.345 0 3.107-.96 4.888-2.624 1.78 1.654 3.542 2.603 4.887 2.603.41 0 .783-.09 1.106-.275 1.374-.792 1.683-3.263.973-6.365C22.02 15.096 24 13.59 24 12.004c0-1.59-1.99-3.097-5.043-4.032.704-3.11.39-5.587-.988-6.38-.318-.184-.688-.277-1.092-.278zm-.005 1.09v.006c.225 0 .406.044.558.127.666.382.955 1.835.73 3.704-.054.46-.142.945-.25 1.44-.96-.236-2.006-.417-3.107-.534-.66-.905-1.345-1.727-2.035-2.447 1.592-1.48 3.087-2.292 4.105-2.295zm-9.77.02c1.012 0 2.514.808 4.11 2.28-.686.72-1.37 1.537-2.02 2.442-1.107.117-2.154.298-3.113.538-.112-.49-.195-.964-.254-1.42-.23-1.868.054-3.32.714-3.707.19-.09.4-.127.563-.132zm4.882 3.05c.455.468.91.992 1.36 1.564-.44-.02-.89-.034-1.345-.034-.46 0-.915.01-1.36.034.44-.572.895-1.096 1.345-1.565zM12 8.1c.74 0 1.477.034 2.202.093.406.582.802 1.203 1.183 1.86.372.64.71 1.29 1.018 1.946-.308.655-.646 1.31-1.013 1.95-.38.66-.773 1.288-1.18 1.87-.728.063-1.466.098-2.21.098-.74 0-1.477-.035-2.202-.093-.406-.582-.802-1.204-1.183-1.86-.372-.64-.71-1.29-1.018-1.946.303-.657.646-1.313 1.013-1.954.38-.66.773-1.286 1.18-1.868.728-.064 1.466-.098 2.21-.098zm-3.635.254c-.24.377-.48.763-.704 1.16-.225.39-.435.782-.635 1.174-.265-.656-.49-1.31-.676-1.947.64-.15 1.315-.283 2.015-.386zm7.26 0c.695.103 1.365.23 2.006.387-.18.632-.405 1.282-.66 1.933-.2-.39-.41-.783-.64-1.174-.225-.392-.465-.774-.705-1.146zm3.063.675c.484.15.944.317 1.375.498 1.732.74 2.852 1.708 2.852 2.476-.005.768-1.125 1.74-2.857 2.475-.42.18-.88.342-1.355.493-.28-.958-.646-1.956-1.1-2.98.45-1.017.81-2.01 1.085-2.964zm-13.395.004c.278.96.645 1.957 1.1 2.98-.45 1.017-.812 2.01-1.086 2.964-.484-.15-.944-.318-1.37-.5-1.732-.737-2.852-1.706-2.852-2.474 0-.768 1.12-1.742 2.852-2.476.42-.18.88-.342 1.356-.494zm11.678 4.28c.265.657.49 1.312.676 1.948-.64.157-1.316.29-2.016.39.24-.375.48-.762.705-1.158.225-.39.435-.788.636-1.18zm-9.945.02c.2.392.41.783.64 1.175.23.39.465.772.705 1.143-.695-.102-1.365-.23-2.006-.386.18-.63.406-1.282.66-1.933zM17.92 16.32c.112.493.2.968.254 1.423.23 1.868-.054 3.32-.714 3.708-.147.09-.338.128-.563.128-1.012 0-2.514-.807-4.11-2.28.686-.72 1.37-1.536 2.02-2.44 1.107-.118 2.154-.3 3.113-.54zm-11.83.01c.96.234 2.006.415 3.107.532.66.905 1.345 1.727 2.035 2.446-1.595 1.483-3.092 2.295-4.11 2.295-.22-.005-.406-.05-.553-.132-.666-.38-.955-1.834-.73-3.703.054-.46.142-.944.25-1.438zm4.56.64c.44.02.89.034 1.345.034.46 0 .915-.01 1.36-.034-.44.572-.895 1.095-1.345 1.565-.455-.47-.91-.993-1.36-1.565z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
),
|
||||
tailwind: (props: LucideProps) => (
|
||||
<svg viewBox="0 0 24 24" {...props}>
|
||||
<path d="M12.001,4.8c-3.2,0-5.2,1.6-6,4.8c1.2-1.6,2.6-2.2,4.2-1.8c0.913,0.228,1.565,0.89,2.288,1.624 C13.666,10.618,15.027,12,18.001,12c3.2,0,5.2-1.6,6-4.8c-1.2,1.6-2.6,2.2-4.2,1.8c-0.913-0.228-1.565-0.89-2.288-1.624 C16.337,6.182,14.976,4.8,12.001,4.8z M6.001,12c-3.2,0-5.2,1.6-6,4.8c1.2-1.6,2.6-2.2,4.2-1.8c0.913,0.228,1.565,0.89,2.288,1.624 c1.177,1.194,2.538,2.576,5.512,2.576c3.2,0,5.2-1.6,6-4.8c-1.2,1.6-2.6,2.2-4.2,1.8c-0.913-0.228-1.565-0.89-2.288-1.624 C10.337,13.382,8.976,12,6.001,12z" />
|
||||
<path
|
||||
d="M12.001,4.8c-3.2,0-5.2,1.6-6,4.8c1.2-1.6,2.6-2.2,4.2-1.8c0.913,0.228,1.565,0.89,2.288,1.624 C13.666,10.618,15.027,12,18.001,12c3.2,0,5.2-1.6,6-4.8c-1.2,1.6-2.6,2.2-4.2,1.8c-0.913-0.228-1.565-0.89-2.288-1.624 C16.337,6.182,14.976,4.8,12.001,4.8z M6.001,12c-3.2,0-5.2,1.6-6,4.8c1.2-1.6,2.6-2.2,4.2-1.8c0.913,0.228,1.565,0.89,2.288,1.624 c1.177,1.194,2.538,2.576,5.512,2.576c3.2,0,5.2-1.6,6-4.8c-1.2,1.6-2.6,2.2-4.2,1.8c-0.913-0.228-1.565-0.89-2.288-1.624 C10.337,13.382,8.976,12,6.001,12z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
),
|
||||
google: (props: LucideProps) => (
|
||||
|
||||
@@ -5,6 +5,7 @@ import Image from "next/image"
|
||||
import { useMDXComponent } from "next-contentlayer/hooks"
|
||||
import { NpmCommands } from "types/unist"
|
||||
|
||||
import { Event } from "@/lib/events"
|
||||
import { cn } from "@/lib/utils"
|
||||
import {
|
||||
Accordion,
|
||||
@@ -13,6 +14,7 @@ import {
|
||||
AccordionTrigger,
|
||||
} from "@/components/ui/accordion"
|
||||
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"
|
||||
import { AspectRatio } from "@/components/ui/aspect-ratio"
|
||||
import { Callout } from "@/components/callout"
|
||||
import { CodeBlockWrapper } from "@/components/code-block-wrapper"
|
||||
import { ComponentExample } from "@/components/component-example"
|
||||
@@ -157,17 +159,19 @@ const components = {
|
||||
__yarnCommand__,
|
||||
__withMeta__,
|
||||
__src__,
|
||||
__event__,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLPreElement> & {
|
||||
__rawString__?: string
|
||||
__withMeta__?: boolean
|
||||
__src__?: string
|
||||
__event__?: Event["name"]
|
||||
} & NpmCommands) => {
|
||||
return (
|
||||
<>
|
||||
<pre
|
||||
className={cn(
|
||||
"mb-4 mt-6 overflow-x-auto rounded-lg border py-4 data-[theme=dark]:bg-background data-[theme=light]:bg-white",
|
||||
"mb-4 mt-6 max-h-[650px] overflow-x-auto rounded-lg border py-4 data-[theme=dark]:bg-background data-[theme=light]:bg-white",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -176,6 +180,7 @@ const components = {
|
||||
<CopyButton
|
||||
value={__rawString__}
|
||||
src={__src__}
|
||||
event={__event__}
|
||||
className={cn("absolute right-4 top-4", __withMeta__ && "top-16")}
|
||||
/>
|
||||
)}
|
||||
@@ -205,6 +210,7 @@ const components = {
|
||||
Callout,
|
||||
ComponentExample,
|
||||
ComponentSource,
|
||||
AspectRatio,
|
||||
CodeBlockWrapper: ({ ...props }) => (
|
||||
<CodeBlockWrapper className="rounded-md border" {...props} />
|
||||
),
|
||||
|
||||
@@ -4,16 +4,19 @@ import { cn } from "@/lib/utils"
|
||||
|
||||
function PageHeader({
|
||||
className,
|
||||
children,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLDivElement>) {
|
||||
return (
|
||||
<section
|
||||
className={cn(
|
||||
"flex max-w-[980px] flex-col items-start gap-2 py-6",
|
||||
"flex max-w-[980px] flex-col items-start gap-2 px-4 pt-8 md:pt-12",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
>
|
||||
{children}
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ export function DocsSidebarNav({ items }: DocsSidebarNavProps) {
|
||||
return items.length ? (
|
||||
<div className="w-full">
|
||||
{items.map((item, index) => (
|
||||
<div key={index} className={cn("pb-6")}>
|
||||
<div key={index} className={cn("pb-4")}>
|
||||
<h4 className="mb-1 rounded-md px-2 py-1 text-sm font-semibold">
|
||||
{item.title}
|
||||
</h4>
|
||||
@@ -46,19 +46,18 @@ export function DocsSidebarNavItems({
|
||||
key={index}
|
||||
href={item.href}
|
||||
className={cn(
|
||||
"group flex w-full items-center rounded-md border border-transparent px-2 py-1.5 hover:underline",
|
||||
"group flex w-full items-center rounded-md border border-transparent px-2 py-1 hover:underline",
|
||||
item.disabled && "cursor-not-allowed opacity-60",
|
||||
{
|
||||
"font-medium bg-accent border-border text-accent-foreground":
|
||||
pathname === item.href,
|
||||
}
|
||||
pathname === item.href
|
||||
? "font-medium text-foreground"
|
||||
: "text-muted-foreground"
|
||||
)}
|
||||
target={item.external ? "_blank" : ""}
|
||||
rel={item.external ? "noreferrer" : ""}
|
||||
>
|
||||
{item.title}
|
||||
{item.label && (
|
||||
<span className="ml-2 rounded-md bg-teal-100 px-1.5 py-0.5 text-xs no-underline group-hover:no-underline dark:bg-teal-600">
|
||||
<span className="ml-2 rounded-md bg-[#adfa1d] px-1.5 py-0.5 text-xs leading-none text-[#000000] no-underline group-hover:no-underline">
|
||||
{item.label}
|
||||
</span>
|
||||
)}
|
||||
|
||||
@@ -13,7 +13,7 @@ const badgeVariants = cva(
|
||||
secondary:
|
||||
"bg-secondary hover:bg-secondary/80 border-transparent text-secondary-foreground",
|
||||
destructive:
|
||||
"bg-destructive hover:bg-secondary/80 border-transparent text-destructive-foreground",
|
||||
"bg-destructive hover:bg-destructive/80 border-transparent text-destructive-foreground",
|
||||
outline: "text-foreground",
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import * as React from "react"
|
||||
import { Slot } from "@radix-ui/react-slot"
|
||||
import { VariantProps, cva } from "class-variance-authority"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
@@ -33,12 +34,15 @@ const buttonVariants = cva(
|
||||
|
||||
export interface ButtonProps
|
||||
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
||||
VariantProps<typeof buttonVariants> {}
|
||||
VariantProps<typeof buttonVariants> {
|
||||
asChild?: boolean
|
||||
}
|
||||
|
||||
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
({ className, variant, size, ...props }, ref) => {
|
||||
({ className, variant, size, asChild = false, ...props }, ref) => {
|
||||
const Comp = asChild ? Slot : "button"
|
||||
return (
|
||||
<button
|
||||
<Comp
|
||||
className={cn(buttonVariants({ variant, size, className }))}
|
||||
ref={ref}
|
||||
{...props}
|
||||
|
||||
@@ -13,13 +13,13 @@ const Checkbox = React.forwardRef<
|
||||
<CheckboxPrimitive.Root
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"peer h-4 w-4 shrink-0 rounded-sm border border-input ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:border-primary",
|
||||
"peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<CheckboxPrimitive.Indicator
|
||||
className={cn("flex items-center justify-center text-primary")}
|
||||
className={cn("flex items-center justify-center text-current")}
|
||||
>
|
||||
<Check className="h-4 w-4" />
|
||||
</CheckboxPrimitive.Indicator>
|
||||
|
||||
@@ -47,7 +47,7 @@ const DropdownMenuSubContent = React.forwardRef<
|
||||
<DropdownMenuPrimitive.SubContent
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"text-on-popover z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 shadow-md animate-in data-[side=bottom]:slide-in-from-top-1 data-[side=left]:slide-in-from-right-1 data-[side=right]:slide-in-from-left-1 data-[side=top]:slide-in-from-bottom-1",
|
||||
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md animate-in data-[side=bottom]:slide-in-from-top-1 data-[side=left]:slide-in-from-right-1 data-[side=right]:slide-in-from-left-1 data-[side=top]:slide-in-from-bottom-1",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -28,13 +28,13 @@ const RadioGroupItem = React.forwardRef<
|
||||
<RadioGroupPrimitive.Item
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"h-4 w-4 rounded-full border border-input ring-offset-background focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
||||
"h-4 w-4 rounded-full border border-primary text-primary ring-offset-background focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<RadioGroupPrimitive.Indicator className="flex items-center justify-center">
|
||||
<Circle className="h-2.5 w-2.5 fill-primary text-primary" />
|
||||
<Circle className="h-2.5 w-2.5 fill-current text-current" />
|
||||
</RadioGroupPrimitive.Indicator>
|
||||
</RadioGroupPrimitive.Item>
|
||||
)
|
||||
|
||||
114
apps/www/components/ui/table.tsx
Normal file
114
apps/www/components/ui/table.tsx
Normal file
@@ -0,0 +1,114 @@
|
||||
import * as React from "react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const Table = React.forwardRef<
|
||||
HTMLTableElement,
|
||||
React.HTMLAttributes<HTMLTableElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div className="w-full overflow-auto">
|
||||
<table
|
||||
ref={ref}
|
||||
className={cn("w-full caption-bottom text-sm", className)}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
))
|
||||
Table.displayName = "Table"
|
||||
|
||||
const TableHeader = React.forwardRef<
|
||||
HTMLTableSectionElement,
|
||||
React.HTMLAttributes<HTMLTableSectionElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<thead ref={ref} className={cn("[&_tr]:border-b", className)} {...props} />
|
||||
))
|
||||
TableHeader.displayName = "TableHeader"
|
||||
|
||||
const TableBody = React.forwardRef<
|
||||
HTMLTableSectionElement,
|
||||
React.HTMLAttributes<HTMLTableSectionElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<tbody
|
||||
ref={ref}
|
||||
className={cn("[&_tr:last-child]:border-0", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
TableBody.displayName = "TableBody"
|
||||
|
||||
const TableFooter = React.forwardRef<
|
||||
HTMLTableSectionElement,
|
||||
React.HTMLAttributes<HTMLTableSectionElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<tfoot
|
||||
ref={ref}
|
||||
className={cn("bg-primary font-medium text-primary-foreground", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
TableFooter.displayName = "TableFooter"
|
||||
|
||||
const TableRow = React.forwardRef<
|
||||
HTMLTableRowElement,
|
||||
React.HTMLAttributes<HTMLTableRowElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<tr
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
TableRow.displayName = "TableRow"
|
||||
|
||||
const TableHead = React.forwardRef<
|
||||
HTMLTableCellElement,
|
||||
React.ThHTMLAttributes<HTMLTableCellElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<th
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
TableHead.displayName = "TableHead"
|
||||
|
||||
const TableCell = React.forwardRef<
|
||||
HTMLTableCellElement,
|
||||
React.TdHTMLAttributes<HTMLTableCellElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<td
|
||||
ref={ref}
|
||||
className={cn("p-4 align-middle [&:has([role=checkbox])]:pr-0", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
TableCell.displayName = "TableCell"
|
||||
|
||||
const TableCaption = React.forwardRef<
|
||||
HTMLTableCaptionElement,
|
||||
React.HTMLAttributes<HTMLTableCaptionElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<caption
|
||||
ref={ref}
|
||||
className={cn("mt-4 text-sm text-muted-foreground", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
TableCaption.displayName = "TableCaption"
|
||||
|
||||
export {
|
||||
Table,
|
||||
TableHeader,
|
||||
TableBody,
|
||||
TableFooter,
|
||||
TableHead,
|
||||
TableRow,
|
||||
TableCell,
|
||||
TableCaption,
|
||||
}
|
||||
@@ -36,6 +36,7 @@ export const components = [
|
||||
{
|
||||
component: "button",
|
||||
name: "Button",
|
||||
dependencies: ["@radix-ui/react-slot"],
|
||||
files: ["components/ui/button.tsx"],
|
||||
},
|
||||
{
|
||||
@@ -173,6 +174,11 @@ export const components = [
|
||||
dependencies: ["@radix-ui/react-switch"],
|
||||
files: ["components/ui/switch.tsx"],
|
||||
},
|
||||
{
|
||||
component: "table",
|
||||
name: "Table",
|
||||
files: ["components/ui/table.tsx"],
|
||||
},
|
||||
{
|
||||
component: "tabs",
|
||||
name: "Tabs",
|
||||
|
||||
@@ -21,7 +21,7 @@ export const docsConfig: DocsConfig = {
|
||||
},
|
||||
{
|
||||
title: "Figma",
|
||||
href: "/figma",
|
||||
href: "/docs/figma",
|
||||
},
|
||||
{
|
||||
title: "GitHub",
|
||||
@@ -70,7 +70,7 @@ export const docsConfig: DocsConfig = {
|
||||
items: [
|
||||
{
|
||||
title: "Figma",
|
||||
href: "/figma",
|
||||
href: "/docs/figma",
|
||||
items: [],
|
||||
},
|
||||
],
|
||||
@@ -86,7 +86,6 @@ export const docsConfig: DocsConfig = {
|
||||
{
|
||||
title: "Alert",
|
||||
href: "/docs/components/alert",
|
||||
label: "New",
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
@@ -107,7 +106,6 @@ export const docsConfig: DocsConfig = {
|
||||
{
|
||||
title: "Badge",
|
||||
href: "/docs/components/badge",
|
||||
label: "New",
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
@@ -118,13 +116,11 @@ export const docsConfig: DocsConfig = {
|
||||
{
|
||||
title: "Calendar",
|
||||
href: "/docs/components/calendar",
|
||||
label: "New",
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: "Card",
|
||||
href: "/docs/components/card",
|
||||
label: "New",
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
@@ -137,6 +133,11 @@ export const docsConfig: DocsConfig = {
|
||||
href: "/docs/components/collapsible",
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: "Combobox",
|
||||
href: "/docs/components/combobox",
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: "Command",
|
||||
href: "/docs/components/command",
|
||||
@@ -147,6 +148,17 @@ export const docsConfig: DocsConfig = {
|
||||
href: "/docs/components/context-menu",
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: "Data Table",
|
||||
href: "/docs/components/data-table",
|
||||
label: "New",
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: "Date Picker",
|
||||
href: "/docs/components/date-picker",
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: "Dialog",
|
||||
href: "/docs/components/dialog",
|
||||
@@ -220,7 +232,6 @@ export const docsConfig: DocsConfig = {
|
||||
{
|
||||
title: "Skeleton",
|
||||
href: "/docs/components/skeleton",
|
||||
label: "New",
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
@@ -233,6 +244,12 @@ export const docsConfig: DocsConfig = {
|
||||
href: "/docs/components/switch",
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: "Table",
|
||||
href: "/docs/components/table",
|
||||
label: "New",
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: "Tabs",
|
||||
href: "/docs/components/tabs",
|
||||
|
||||
@@ -31,7 +31,7 @@ npx shadcn-ui add alert
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/card"
|
||||
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"
|
||||
```
|
||||
|
||||
```tsx
|
||||
|
||||
@@ -20,7 +20,13 @@ npx shadcn-ui add button
|
||||
<AccordionTrigger>Manual Installation</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
|
||||
1. Copy and paste the following code into your project.
|
||||
1. Install the `@radix-ui/react-slot` component from radix-ui:
|
||||
|
||||
```bash
|
||||
npm install @radix-ui/react-slot
|
||||
```
|
||||
|
||||
2.
|
||||
|
||||
<ComponentSource src="/components/ui/button.tsx" />
|
||||
|
||||
@@ -56,6 +62,14 @@ import { buttonVariants } from "@/components/ui/button"
|
||||
<Link className={buttonVariants({ variant: "outline" })}>Click here</Link>
|
||||
```
|
||||
|
||||
Alternatively, you can set the `asChild` parameter and nest the link component.
|
||||
|
||||
```tsx
|
||||
<Button asChild>
|
||||
<Link href="/login">Login</Link>
|
||||
</Button>
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Primary
|
||||
@@ -119,3 +133,11 @@ import { buttonVariants } from "@/components/ui/button"
|
||||
<ComponentExample src="/components/examples/button/loading.tsx">
|
||||
<ButtonLoading />
|
||||
</ComponentExample>
|
||||
|
||||
---
|
||||
|
||||
### As Child
|
||||
|
||||
<ComponentExample src="/components/examples/button/as-child.tsx">
|
||||
<ButtonAsChild />
|
||||
</ComponentExample>
|
||||
|
||||
@@ -64,22 +64,6 @@ return (
|
||||
|
||||
See the [React DayPicker](https://react-day-picker.js.org) documentation for more information.
|
||||
|
||||
## Examples
|
||||
## Date Picker
|
||||
|
||||
### Date Picker
|
||||
|
||||
<ComponentExample src="/components/examples/calendar/date-picker.tsx">
|
||||
<CalendarDatePicker />
|
||||
</ComponentExample>
|
||||
|
||||
### Date Range Picker
|
||||
|
||||
<ComponentExample src="/components/examples/calendar/date-range-picker.tsx">
|
||||
<CalendarDateRangePicker />
|
||||
</ComponentExample>
|
||||
|
||||
## With Presets
|
||||
|
||||
<ComponentExample src="/components/examples/calendar/with-presets.tsx">
|
||||
<CalendarDatePickerWithPresets />
|
||||
</ComponentExample>
|
||||
You can use the `<Calendar>` component to build a date picker. See the [Date Picker](/docs/components/date-picker) page for more information.
|
||||
|
||||
132
apps/www/content/docs/components/combobox.mdx
Normal file
132
apps/www/content/docs/components/combobox.mdx
Normal file
@@ -0,0 +1,132 @@
|
||||
---
|
||||
title: Combobox
|
||||
description: Autocomplete input and command palette with a list of suggestions.
|
||||
component: true
|
||||
---
|
||||
|
||||
<ComponentExample src="/components/examples/combobox/demo.tsx">
|
||||
<ComboboxDemo />
|
||||
</ComponentExample>
|
||||
|
||||
## About
|
||||
|
||||
The Combobox is built using a composition of the `<Popover />` and the `<Command />` components.
|
||||
|
||||
## Installation
|
||||
|
||||
See installation instructions for the [Popover](/docs/components/popover#installation) and the [Command](/docs/components/command#installation) components.
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import { Check, ChevronsUpDown } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import {
|
||||
Command,
|
||||
CommandEmpty,
|
||||
CommandGroup,
|
||||
CommandInput,
|
||||
CommandItem,
|
||||
} from "@/components/ui/command"
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/components/ui/popover"
|
||||
|
||||
const frameworks = [
|
||||
{
|
||||
value: "next.js",
|
||||
label: "Next.js",
|
||||
},
|
||||
{
|
||||
value: "sveltekit",
|
||||
label: "SvelteKit",
|
||||
},
|
||||
{
|
||||
value: "nuxt.js",
|
||||
label: "Nuxt.js",
|
||||
},
|
||||
{
|
||||
value: "remix",
|
||||
label: "Remix",
|
||||
},
|
||||
{
|
||||
value: "astro",
|
||||
label: "Astro",
|
||||
},
|
||||
]
|
||||
|
||||
export function ComboboxDemo() {
|
||||
const [open, setOpen] = React.useState(false)
|
||||
const [value, setValue] = React.useState("")
|
||||
|
||||
return (
|
||||
<Popover open={open} onOpenChange={setOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
role="combobox"
|
||||
aria-expanded={open}
|
||||
className="w-[200px] justify-between"
|
||||
>
|
||||
{value
|
||||
? frameworks.find((framework) => framework.value === value)?.label
|
||||
: "Select framework..."}
|
||||
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-[200px] p-0">
|
||||
<Command>
|
||||
<CommandInput placeholder="Search framework..." />
|
||||
<CommandEmpty>No framework found.</CommandEmpty>
|
||||
<CommandGroup>
|
||||
{frameworks.map((framework) => (
|
||||
<CommandItem
|
||||
key={framework.value}
|
||||
onSelect={(currentValue) => {
|
||||
setValue(currentValue === value ? "" : currentValue)
|
||||
setOpen(false)
|
||||
}}
|
||||
>
|
||||
<Check
|
||||
className={cn(
|
||||
"mr-2 h-4 w-4",
|
||||
value === framework.value ? "opacity-100" : "opacity-0"
|
||||
)}
|
||||
/>
|
||||
{framework.label}
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
</Command>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Combobox
|
||||
|
||||
<ComponentExample src="/components/examples/combobox/demo.tsx">
|
||||
<ComboboxDemo />
|
||||
</ComponentExample>
|
||||
|
||||
### Popover
|
||||
|
||||
<ComponentExample src="/components/examples/combobox/popover.tsx">
|
||||
<ComboboxPopover />
|
||||
</ComponentExample>
|
||||
|
||||
### Dropdown menu
|
||||
|
||||
<ComponentExample src="/components/examples/combobox/dropdown-menu.tsx">
|
||||
<ComboboxDropdownMenu />
|
||||
</ComponentExample>
|
||||
@@ -120,20 +120,6 @@ export function CommandMenu() {
|
||||
}
|
||||
```
|
||||
|
||||
### Combobox
|
||||
## Combobox
|
||||
|
||||
<ComponentExample src="/components/examples/command/combobox.tsx">
|
||||
<CommandCombobox />
|
||||
</ComponentExample>
|
||||
|
||||
### Popover
|
||||
|
||||
<ComponentExample src="/components/examples/command/popover.tsx">
|
||||
<CommandPopover />
|
||||
</ComponentExample>
|
||||
|
||||
### Dropdown menu
|
||||
|
||||
<ComponentExample src="/components/examples/command/dropdown-menu.tsx">
|
||||
<CommandDropdownMenu />
|
||||
</ComponentExample>
|
||||
You can use the `<Command />` component as a combobox. See the [Combobox](/docs/components/combobox) page for more information.
|
||||
|
||||
867
apps/www/content/docs/components/data-table.mdx
Normal file
867
apps/www/content/docs/components/data-table.mdx
Normal file
@@ -0,0 +1,867 @@
|
||||
---
|
||||
title: Data Table
|
||||
description: Powerful table and datagrids built using TanStack Table.
|
||||
component: true
|
||||
---
|
||||
|
||||
<ComponentExample src="/components/examples/data-table/demo.tsx">
|
||||
<DataTableDemo />
|
||||
</ComponentExample>
|
||||
|
||||
## Introduction
|
||||
|
||||
Every data table or datagrid I've created has been unique. They all behave differently, have specific sorting and filtering requirements, and work with different data sources.
|
||||
|
||||
It doesn't make sense to combine all of these variations into a single component. If we do that, we'll lose the flexibility that [headless UI](https://tanstack.com/table/v8/docs/guide/introduction#what-is-headless-ui) provides.
|
||||
|
||||
So instead of a data-table component, I thought it would be more helpful to provide a guide on how to build your own.
|
||||
|
||||
We'll start with the basic `<Table />` component and build a complex data table from scratch.
|
||||
|
||||
<Callout className="mt-4">
|
||||
|
||||
**Tip:** If you find yourself using the same table in multiple places in your app, you can always extract it into a reusable component.
|
||||
|
||||
</Callout>
|
||||
|
||||
## Table of Contents
|
||||
|
||||
This guide will show you how to use [TanStack Table](https://tanstack.com/table) and the `<Table />` component to build your own custom data table. We'll cover the following topics:
|
||||
|
||||
- [Basic Table](#basic-table)
|
||||
- [Row Actions](#row-actions)
|
||||
- [Pagination](#pagination)
|
||||
- [Sorting](#sorting)
|
||||
- [Filtering](#filtering)
|
||||
- [Visibility](#visibility)
|
||||
- [Row Selection](#row-selection)
|
||||
- [Reusable Components](#reusable-components)
|
||||
|
||||
## Installation
|
||||
|
||||
1. Add the `<Table />` component to your project:
|
||||
|
||||
```bash
|
||||
npx shadcn-ui add table
|
||||
```
|
||||
|
||||
2. Add `tanstack/react-table` dependency:
|
||||
|
||||
```bash
|
||||
npm install @tanstack/react-table
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
We are going to build a table to show recent payments. Here's what our data looks like:
|
||||
|
||||
```tsx showLineNumbers
|
||||
type Payment = {
|
||||
id: string
|
||||
amount: number
|
||||
status: "pending" | "processing" | "success" | "failed"
|
||||
email: string
|
||||
}
|
||||
|
||||
export const payments: Payment[] = [
|
||||
{
|
||||
id: "728ed52f",
|
||||
amount: 100,
|
||||
status: "pending",
|
||||
email: "m@example.com",
|
||||
},
|
||||
{
|
||||
id: "489e1d42",
|
||||
amount: 125,
|
||||
status: "processing",
|
||||
email: "example@gmail.com",
|
||||
},
|
||||
// ...
|
||||
]
|
||||
```
|
||||
|
||||
## Project Structure
|
||||
|
||||
Start by creating the following file structure:
|
||||
|
||||
```txt
|
||||
app
|
||||
└── payments
|
||||
├── columns.tsx
|
||||
├── data-table.tsx
|
||||
└── page.tsx
|
||||
```
|
||||
|
||||
I'm using a Next.js example here but this works for any other React framework.
|
||||
|
||||
- `columns.tsx` (client component) will contain our column definitions.
|
||||
- `data-table.tsx` (client component) will contain our `<DataTable />` component.
|
||||
- `page.tsx` (server component) is where we'll fetch data and render our table.
|
||||
|
||||
## Basic Table
|
||||
|
||||
Let's start by building a basic table.
|
||||
|
||||
<Steps>
|
||||
|
||||
### Column Definitions
|
||||
|
||||
First, we'll define our columns.
|
||||
|
||||
```tsx showLineNumbers title="app/payments/columns.tsx" {3,14-27}
|
||||
"use client"
|
||||
|
||||
import { ColumnDef } from "@tanstack/react-table"
|
||||
|
||||
// This type is used to define the shape of our data.
|
||||
// You can use a Zod schema here if you want.
|
||||
export type Payment = {
|
||||
id: string
|
||||
amount: number
|
||||
status: "pending" | "processing" | "success" | "failed"
|
||||
email: string
|
||||
}
|
||||
|
||||
export const columns: ColumnDef<Payment>[] = [
|
||||
{
|
||||
accessorKey: "status",
|
||||
header: "Status",
|
||||
},
|
||||
{
|
||||
accessorKey: "email",
|
||||
header: "Email",
|
||||
},
|
||||
{
|
||||
accessorKey: "amount",
|
||||
header: "Amount",
|
||||
},
|
||||
]
|
||||
```
|
||||
|
||||
<Callout className="mt-4">
|
||||
|
||||
**Note:** Columns are where you define the core of what your table
|
||||
will look like. They define the data that will be displayed, how it will be
|
||||
formatted, sorted and filtered.
|
||||
|
||||
</Callout>
|
||||
|
||||
### `<DataTable />` component
|
||||
|
||||
Next, we'll create a `<DataTable />` component to render our table.
|
||||
|
||||
```tsx showLineNumbers title="app/payments/data-table.tsx"
|
||||
"use client"
|
||||
|
||||
import {
|
||||
ColumnDef,
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
useReactTable,
|
||||
} from "@tanstack/react-table"
|
||||
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/components/ui/table"
|
||||
|
||||
interface DataTableProps<TData, TValue> {
|
||||
columns: ColumnDef<TData, TValue>[]
|
||||
data: TData[]
|
||||
}
|
||||
|
||||
export function DataTable<TData, TValue>({
|
||||
columns,
|
||||
data,
|
||||
}: DataTableProps<TData, TValue>) {
|
||||
const table = useReactTable({
|
||||
data,
|
||||
columns,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
})
|
||||
|
||||
return (
|
||||
<div className="rounded-md border">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<TableRow key={headerGroup.id}>
|
||||
{headerGroup.headers.map((header) => {
|
||||
return (
|
||||
<TableHead key={header.id}>
|
||||
{header.isPlaceholder
|
||||
? null
|
||||
: flexRender(
|
||||
header.column.columnDef.header,
|
||||
header.getContext()
|
||||
)}
|
||||
</TableHead>
|
||||
)
|
||||
})}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{table.getRowModel().rows?.length ? (
|
||||
table.getRowModel().rows.map((row) => (
|
||||
<TableRow
|
||||
key={row.id}
|
||||
data-state={row.getIsSelected() && "selected"}
|
||||
>
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
<TableCell key={cell.id}>
|
||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
))
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell colSpan={columns.length} className="h-24 text-center">
|
||||
No results.
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
<Callout>
|
||||
|
||||
**Tip**: If you find yourself using `<DataTable />` in multiple places, this is the component you could make reusable by extracting it to `components/ui/data-table.tsx`.
|
||||
|
||||
`<DataTable columns={columns} data={data} />`
|
||||
|
||||
</Callout>
|
||||
|
||||
### Render the table
|
||||
|
||||
Finally, we'll render our table in our page component.
|
||||
|
||||
```tsx showLineNumbers title="app/payments/page.tsx" {22}
|
||||
import { Payment, columns } from "./columns"
|
||||
import { DataTable } from "./data-table"
|
||||
|
||||
async function getData(): Promise<Payment[]> {
|
||||
// Fetch data from your API here.
|
||||
return [
|
||||
{
|
||||
id: "728ed52f",
|
||||
amount: 100,
|
||||
status: "pending",
|
||||
email: "m@example.com",
|
||||
},
|
||||
// ...
|
||||
]
|
||||
}
|
||||
|
||||
export default async function DemoPage() {
|
||||
const data = await getData()
|
||||
|
||||
return (
|
||||
<div className="container mx-auto py-10">
|
||||
<DataTable columns={columns} data={data} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
</Steps>
|
||||
|
||||
## Cell Formatting
|
||||
|
||||
Let's format the amount cell to display the dollar amount. We'll also align the cell to the right.
|
||||
|
||||
<Steps>
|
||||
|
||||
### Update columns definition
|
||||
|
||||
Update the `header` and `cell` definitions for amount as follows:
|
||||
|
||||
```tsx showLineNumbers title="app/payments/columns.tsx" {4-15}
|
||||
export const columns: ColumnDef<Payment>[] = [
|
||||
{
|
||||
accessorKey: "amount",
|
||||
header: () => <div className="text-right">Amount</div>,
|
||||
cell: ({ row }) => {
|
||||
const amount = parseFloat(row.getValue("amount"))
|
||||
const formatted = new Intl.NumberFormat("en-US", {
|
||||
style: "currency",
|
||||
currency: "USD",
|
||||
}).format(amount)
|
||||
|
||||
return <div className="text-right font-medium">{formatted}</div>
|
||||
},
|
||||
},
|
||||
]
|
||||
```
|
||||
|
||||
You can use the same approach to format other cells and headers.
|
||||
|
||||
</Steps>
|
||||
|
||||
## Row Actions
|
||||
|
||||
Let's add row actions to our table. We'll use a `<Dropdown />` component for this.
|
||||
|
||||
<Steps>
|
||||
|
||||
### Update columns definition
|
||||
|
||||
Update our columns definition to add a new `actions` column. The `actions` cell returns a `<Dropdown />` component.
|
||||
|
||||
```tsx showLineNumbers title="app/payments/columns.tsx" {4,6-14,18-45}
|
||||
"use client"
|
||||
|
||||
import { ColumnDef } from "@tanstack/react-table"
|
||||
import { MoreHorizontal } from "lucide-react"
|
||||
|
||||
import { Button } from "@/components/ui/button"
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu"
|
||||
|
||||
export const columns: ColumnDef<Payment>[] = [
|
||||
// ...
|
||||
{
|
||||
id: "actions",
|
||||
cell: ({ row }) => {
|
||||
const payment = row.original
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="ghost" className="h-8 w-8 p-0">
|
||||
<span className="sr-only">Open menu</span>
|
||||
<MoreHorizontal className="h-4 w-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuLabel>Actions</DropdownMenuLabel>
|
||||
<DropdownMenuItem
|
||||
onClick={() => navigator.clipboard.writeText(payment.id)}
|
||||
>
|
||||
Copy payment ID
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem>View customer</DropdownMenuItem>
|
||||
<DropdownMenuItem>View payment details</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)
|
||||
},
|
||||
},
|
||||
// ...
|
||||
]
|
||||
```
|
||||
|
||||
You can access the row data using `row.original` in the `cell` function. Use this to handle actions for your row eg. use the `id` to make a DELETE call to your API.
|
||||
|
||||
</Steps>
|
||||
|
||||
## Pagination
|
||||
|
||||
Next, we'll add pagination to our table.
|
||||
|
||||
<Steps>
|
||||
|
||||
### Update `<DataTable>`
|
||||
|
||||
```tsx showLineNumbers title="app/payments/data-table.tsx" {5,17}
|
||||
import {
|
||||
ColumnDef,
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
getPaginationRowModel,
|
||||
useReactTable,
|
||||
} from "@tanstack/react-table"
|
||||
|
||||
export function DataTable<TData, TValue>({
|
||||
columns,
|
||||
data,
|
||||
}: DataTableProps<TData, TValue>) {
|
||||
const table = useReactTable({
|
||||
data,
|
||||
columns,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
})
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
This will automatically paginate your rows into pages of 10. See the [pagination docs](https://tanstack.com/table/v8/docs/api/features/pagination) for more information on customizing page size and implementing manual pagination.
|
||||
|
||||
### Add pagination controls
|
||||
|
||||
We can add pagination controls to our table using the `<Button />` component and the `table.previousPage()`, `table.nextPage()` API methods.
|
||||
|
||||
```tsx showLineNumbers title="app/payments/data-table.tsx" {1,15,21-39}
|
||||
import { Button } from "@/components/ui/button"
|
||||
|
||||
export function DataTable<TData, TValue>({
|
||||
columns,
|
||||
data,
|
||||
}: DataTableProps<TData, TValue>) {
|
||||
const table = useReactTable({
|
||||
data,
|
||||
columns,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
})
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="rounded-md border">
|
||||
<Table>
|
||||
{ // .... }
|
||||
</Table>
|
||||
</div>
|
||||
<div className="flex items-center justify-end space-x-2 py-4">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => table.previousPage()}
|
||||
disabled={!table.getCanPreviousPage()}
|
||||
>
|
||||
Previous
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => table.nextPage()}
|
||||
disabled={!table.getCanNextPage()}
|
||||
>
|
||||
Next
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
See [Reusable Components](#reusable-components) section for a more advanced pagination component.
|
||||
|
||||
</Steps>
|
||||
|
||||
## Sorting
|
||||
|
||||
Let's make the email column sortable.
|
||||
|
||||
<Steps>
|
||||
|
||||
### Update `<DataTable>`
|
||||
|
||||
```tsx showLineNumbers title="app/payments/data-table.tsx" showLineNumbers {3,6,10,18,25-28}
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import {
|
||||
ColumnDef,
|
||||
SortingState,
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
getPaginationRowModel,
|
||||
getSortedRowModel,
|
||||
useReactTable,
|
||||
} from "@tanstack/react-table"
|
||||
|
||||
export function DataTable<TData, TValue>({
|
||||
columns,
|
||||
data,
|
||||
}: DataTableProps<TData, TValue>) {
|
||||
const [sorting, setSorting] = React.useState<SortingState>([])
|
||||
|
||||
const table = useReactTable({
|
||||
data,
|
||||
columns,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
onSortingChange: setSorting,
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
state: {
|
||||
sorting,
|
||||
},
|
||||
})
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="rounded-md border">
|
||||
<Table>{ ... }</Table>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### Make header cell sortable
|
||||
|
||||
We can now update the `email` header cell to add sorting controls.
|
||||
|
||||
```tsx showLineNumbers title="app/payments/columns.tsx" {4,9-19}
|
||||
"use client"
|
||||
|
||||
import { ColumnDef } from "@tanstack/react-table"
|
||||
import { ArrowUpDown, MoreHorizontal } from "lucide-react"
|
||||
|
||||
export const columns: ColumnDef<Payment>[] = [
|
||||
{
|
||||
accessorKey: "email",
|
||||
header: ({ column }) => {
|
||||
return (
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
|
||||
>
|
||||
Email
|
||||
<ArrowUpDown className="ml-2 h-4 w-4" />
|
||||
</Button>
|
||||
)
|
||||
},
|
||||
},
|
||||
]
|
||||
```
|
||||
|
||||
This will automatically sort the table (asc and desc) when the user toggles on the header cell.
|
||||
|
||||
</Steps>
|
||||
|
||||
## Filtering
|
||||
|
||||
Let's add a search input to filter emails in our table.
|
||||
|
||||
<Steps>
|
||||
|
||||
### Update `<DataTable>`
|
||||
|
||||
```tsx showLineNumbers title="app/payments/data-table.tsx" {6,10,17,24-26,35-36,39,45-54}
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import {
|
||||
ColumnDef,
|
||||
ColumnFiltersState,
|
||||
SortingState,
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
getFilteredRowModel,
|
||||
getPaginationRowModel,
|
||||
getSortedRowModel,
|
||||
useReactTable,
|
||||
} from "@tanstack/react-table"
|
||||
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Input } from "@/components/ui/input"
|
||||
|
||||
export function DataTable<TData, TValue>({
|
||||
columns,
|
||||
data,
|
||||
}: DataTableProps<TData, TValue>) {
|
||||
const [sorting, setSorting] = React.useState<SortingState>([])
|
||||
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
||||
[]
|
||||
)
|
||||
|
||||
const table = useReactTable({
|
||||
data,
|
||||
columns,
|
||||
onSortingChange: setSorting,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
onColumnFiltersChange: setColumnFilters,
|
||||
getFilteredRowModel: getFilteredRowModel(),
|
||||
state: {
|
||||
sorting,
|
||||
columnFilters,
|
||||
},
|
||||
})
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="flex items-center py-4">
|
||||
<Input
|
||||
placeholder="Filter emails..."
|
||||
value={(table.getColumn("email")?.getFilterValue() as string) ?? ""}
|
||||
onChange={(event) =>
|
||||
table.getColumn("email")?.setFilterValue(event.target.value)
|
||||
}
|
||||
className="max-w-sm"
|
||||
/>
|
||||
</div>
|
||||
<div className="rounded-md border">
|
||||
<Table>{ ... }</Table>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
Filtering is now enabled for the `email` column. You can add filters to other columns as well. See the [filtering docs](https://tanstack.com/table/v8/docs/guide/filters) for more information on customizing filters.
|
||||
|
||||
</Steps>
|
||||
|
||||
## Visibility
|
||||
|
||||
Adding column visibility is fairly simple using `@tanstack/react-table` visibility API.
|
||||
|
||||
<Steps>
|
||||
|
||||
### Update `<DataTable>`
|
||||
|
||||
```tsx showLineNumbers title="app/payments/data-table.tsx" {8,18-23,33-34,45,49,64-91}
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import {
|
||||
ColumnDef,
|
||||
ColumnFiltersState,
|
||||
SortingState,
|
||||
VisibilityState,
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
getFilteredRowModel,
|
||||
getPaginationRowModel,
|
||||
getSortedRowModel,
|
||||
useReactTable,
|
||||
} from "@tanstack/react-table"
|
||||
|
||||
import { Button } from "@/components/ui/button"
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuCheckboxItem,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu"
|
||||
|
||||
export function DataTable<TData, TValue>({
|
||||
columns,
|
||||
data,
|
||||
}: DataTableProps<TData, TValue>) {
|
||||
const [sorting, setSorting] = React.useState<SortingState>([])
|
||||
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
||||
[]
|
||||
)
|
||||
const [columnVisibility, setColumnVisibility] =
|
||||
React.useState<VisibilityState>({})
|
||||
|
||||
const table = useReactTable({
|
||||
data,
|
||||
columns,
|
||||
onSortingChange: setSorting,
|
||||
onColumnFiltersChange: setColumnFilters,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
getFilteredRowModel: getFilteredRowModel(),
|
||||
onColumnVisibilityChange: setColumnVisibility,
|
||||
state: {
|
||||
sorting,
|
||||
columnFilters,
|
||||
columnVisibility,
|
||||
},
|
||||
})
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="flex items-center py-4">
|
||||
<Input
|
||||
placeholder="Filter emails..."
|
||||
value={table.getColumn("email")?.getFilterValue() as string}
|
||||
onChange={(event) =>
|
||||
table.getColumn("email")?.setFilterValue(event.target.value)
|
||||
}
|
||||
className="max-w-sm"
|
||||
/>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="outline" className="ml-auto">
|
||||
Columns
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
{table
|
||||
.getAllColumns()
|
||||
.filter(
|
||||
(column) => column.getCanHide()
|
||||
)
|
||||
.map((column) => {
|
||||
return (
|
||||
<DropdownMenuCheckboxItem
|
||||
key={column.id}
|
||||
className="capitalize"
|
||||
checked={column.getIsVisible()}
|
||||
onCheckedChange={(value) =>
|
||||
column.toggleVisibility(!!value)
|
||||
}
|
||||
>
|
||||
{column.id}
|
||||
</DropdownMenuCheckboxItem>
|
||||
)
|
||||
})}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
<div className="rounded-md border">
|
||||
<Table>{ ... }</Table>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
This adds a dropdown menu that you can use to toggle column visibility.
|
||||
|
||||
</Steps>
|
||||
|
||||
## Row Selection
|
||||
|
||||
Next, we're going to add row selection to our table.
|
||||
|
||||
<Steps>
|
||||
|
||||
### Update column definitions
|
||||
|
||||
```tsx showLineNumbers title="app/payments/columns.tsx" {6,9-27}
|
||||
"use client"
|
||||
|
||||
import { ColumnDef } from "@tanstack/react-table"
|
||||
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
import { Checkbox } from "@/components/ui/checkbox"
|
||||
|
||||
export const columns: ColumnDef<Payment>[] = [
|
||||
{
|
||||
id: "select",
|
||||
header: ({ table }) => (
|
||||
<Checkbox
|
||||
checked={table.getIsAllPageRowsSelected()}
|
||||
onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
|
||||
aria-label="Select all"
|
||||
/>
|
||||
),
|
||||
cell: ({ row }) => (
|
||||
<Checkbox
|
||||
checked={row.getIsSelected()}
|
||||
onCheckedChange={(value) => row.toggleSelected(!!value)}
|
||||
aria-label="Select row"
|
||||
/>
|
||||
),
|
||||
enableSorting: false,
|
||||
enableHiding: false,
|
||||
},
|
||||
]
|
||||
```
|
||||
|
||||
### Update `<DataTable>`
|
||||
|
||||
```tsx showLineNumbers title="app/payments/data-table.tsx" {11,23,28}
|
||||
export function DataTable<TData, TValue>({
|
||||
columns,
|
||||
data,
|
||||
}: DataTableProps<TData, TValue>) {
|
||||
const [sorting, setSorting] = React.useState<SortingState>([])
|
||||
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
||||
[]
|
||||
)
|
||||
const [columnVisibility, setColumnVisibility] =
|
||||
React.useState<VisibilityState>({})
|
||||
const [rowSelection, setRowSelection] = React.useState({})
|
||||
|
||||
const table = useReactTable({
|
||||
data,
|
||||
columns,
|
||||
onSortingChange: setSorting,
|
||||
onColumnFiltersChange: setColumnFilters,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
getFilteredRowModel: getFilteredRowModel(),
|
||||
onColumnVisibilityChange: setColumnVisibility,
|
||||
onRowSelectionChange: setRowSelection,
|
||||
state: {
|
||||
sorting,
|
||||
columnFilters,
|
||||
columnVisibility,
|
||||
rowSelection,
|
||||
},
|
||||
})
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="rounded-md border">
|
||||
<Table />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
This adds a checkbox to each row and a checkbox in the header to select all rows.
|
||||
|
||||
### Show selected rows
|
||||
|
||||
You can show the number of selected rows using the `table.getFilteredSelectedRowModel()` API.
|
||||
|
||||
```tsx
|
||||
<div className="flex-1 text-sm text-muted-foreground">
|
||||
{table.getFilteredSelectedRowModel().rows.length} of{" "}
|
||||
{table.getFilteredRowModel().rows.length} row(s) selected.
|
||||
</div>
|
||||
```
|
||||
|
||||
</Steps>
|
||||
|
||||
## Reusable Components
|
||||
|
||||
Here are some components you can use to build your data tables. This is from the [Tasks](/examples/tasks) demo.
|
||||
|
||||
### Column header
|
||||
|
||||
Make any column header sortable and hideable.
|
||||
|
||||
<ComponentSource src="/app/examples/tasks/components/data-table-column-header.tsx" />
|
||||
|
||||
```tsx {5}
|
||||
export const columns = [
|
||||
{
|
||||
accessorKey: "email",
|
||||
header: ({ column }) => (
|
||||
<DataTableColumnHeader column={column} title="Email" />
|
||||
),
|
||||
},
|
||||
]
|
||||
```
|
||||
|
||||
### Pagination
|
||||
|
||||
Add pagination controls to your table including page size and selection count.
|
||||
|
||||
<ComponentSource src="/app/examples/tasks/components/data-table-pagination.tsx" />
|
||||
|
||||
```tsx
|
||||
<DataTablePagination table={table} />
|
||||
```
|
||||
|
||||
### Column toggle
|
||||
|
||||
A component to toggle column visibility.
|
||||
|
||||
<ComponentSource src="/app/examples/tasks/components/data-table-view-options.tsx" />
|
||||
|
||||
```tsx
|
||||
<DataTableViewOptions table={table} />
|
||||
```
|
||||
87
apps/www/content/docs/components/date-picker.mdx
Normal file
87
apps/www/content/docs/components/date-picker.mdx
Normal file
@@ -0,0 +1,87 @@
|
||||
---
|
||||
title: Date Picker
|
||||
description: A date picker component with range and presets.
|
||||
component: true
|
||||
---
|
||||
|
||||
<ComponentExample src="/components/examples/date-picker/demo.tsx">
|
||||
<DatePickerDemo />
|
||||
</ComponentExample>
|
||||
|
||||
## About
|
||||
|
||||
The Date Picker is built using a composition of the `<Popover />` and the `<Calendar />` components.
|
||||
|
||||
## Installation
|
||||
|
||||
See installation instructions for the [Popover](/docs/components/popover#installation) and the [Calendar](/docs/components/calendar#installation) components.
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import { format } from "date-fns"
|
||||
import { Calendar as CalendarIcon } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Calendar } from "@/components/ui/calendar"
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/components/ui/popover"
|
||||
|
||||
export function DatePickerDemo() {
|
||||
const [date, setDate] = React.useState<Date>()
|
||||
|
||||
return (
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant={"outline"}
|
||||
className={cn(
|
||||
"w-[280px] justify-start text-left font-normal",
|
||||
!date && "text-muted-foreground"
|
||||
)}
|
||||
>
|
||||
<CalendarIcon className="mr-2 h-4 w-4" />
|
||||
{date ? format(date, "PPP") : <span>Pick a date</span>}
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-auto p-0">
|
||||
<Calendar
|
||||
mode="single"
|
||||
selected={date}
|
||||
onSelect={setDate}
|
||||
initialFocus
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
See the [React DayPicker](https://react-day-picker.js.org) documentation for more information.
|
||||
|
||||
## Examples
|
||||
|
||||
### Date Picker
|
||||
|
||||
<ComponentExample src="/components/examples/date-picker/demo.tsx">
|
||||
<DatePickerDemo />
|
||||
</ComponentExample>
|
||||
|
||||
### Date Range Picker
|
||||
|
||||
<ComponentExample src="/components/examples/date-picker/with-range.tsx">
|
||||
<DatePickerWithRange />
|
||||
</ComponentExample>
|
||||
|
||||
### With Presets
|
||||
|
||||
<ComponentExample src="/components/examples/date-picker/with-presets.tsx">
|
||||
<DatePickerWithPresets />
|
||||
</ComponentExample>
|
||||
78
apps/www/content/docs/components/table.mdx
Normal file
78
apps/www/content/docs/components/table.mdx
Normal file
@@ -0,0 +1,78 @@
|
||||
---
|
||||
title: Table
|
||||
description: A responsive table component.
|
||||
component: true
|
||||
---
|
||||
|
||||
<ComponentExample src="/components/examples/table/demo.tsx">
|
||||
<TableDemo />
|
||||
</ComponentExample>
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
npx shadcn-ui add table
|
||||
```
|
||||
|
||||
<Accordion type="single" collapsible>
|
||||
<AccordionItem value="manual-installation">
|
||||
<AccordionTrigger>Manual Installation</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
|
||||
1. Copy and paste the following code into your project.
|
||||
|
||||
<ComponentSource src="/components/ui/table.tsx" />
|
||||
|
||||
<Callout>
|
||||
This is the `<Table />` primitive. You can place it in a file at
|
||||
`components/ui/table.tsx`.
|
||||
</Callout>
|
||||
|
||||
</AccordionContent>
|
||||
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCaption,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/components/ui/table"
|
||||
```
|
||||
|
||||
```tsx
|
||||
<Table>
|
||||
<TableCaption>A list of your recent invoices.</TableCaption>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead className="w-[100px]">Invoice</TableHead>
|
||||
<TableHead>Status</TableHead>
|
||||
<TableHead>Method</TableHead>
|
||||
<TableHead className="text-right">Amount</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableCell className="font-medium">INV001</TableCell>
|
||||
<TableCell>Paid</TableCell>
|
||||
<TableCell>Credit Card</TableCell>
|
||||
<TableCell className="text-right">$250.00</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
```
|
||||
|
||||
## Data Table
|
||||
|
||||
You can use the `<Table />` component to build more complex data tables. Combine it with [@tanstack/react-table](https://tanstack.com/table/v8) to create tables with sorting, filtering and pagination.
|
||||
|
||||
See the [Data Table](/docs/components/data-table) documentation for more information.
|
||||
|
||||
You can also see an example of a data table in the [Tasks](/examples/tasks) demo.
|
||||
17
apps/www/content/docs/figma.mdx
Normal file
17
apps/www/content/docs/figma.mdx
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
title: Figma
|
||||
description: Every component recreated in Figma. With customizable props, typography and icons.
|
||||
---
|
||||
|
||||
The Figma UI Kit is open sourced by [Pietro Schirano](https://twitter.com/skirano).
|
||||
|
||||
<AspectRatio ratio={16 / 9} className="w-full mt-4">
|
||||
<iframe
|
||||
src="https://embed.figma.com/file/1203061493325953101/hf_embed?community_viewer=true&embed_host=shadcn&hub_file_id=1203061493325953101&kind=&viewer=1"
|
||||
className="h-full w-full overflow-hidden rounded-lg border bg-muted"
|
||||
/>
|
||||
</AspectRatio>
|
||||
|
||||
## Grab a copy
|
||||
|
||||
https://www.figma.com/community/file/1203061493325953101
|
||||
@@ -38,7 +38,7 @@ Which frameworks are supported?
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
|
||||
You can use any framwork that supports React. Next.js, Astro, Remix, Gatsby etc.
|
||||
You can use any framework that supports React. Next.js, Astro, Remix, Gatsby etc.
|
||||
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
|
||||
@@ -217,8 +217,8 @@ Add the following to your `styles/globals.css` file. You can learn more about us
|
||||
--popover: 224 71% 4%;
|
||||
--popover-foreground: 215 20.2% 65.1%;
|
||||
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 222.2 47.4% 11.2%;
|
||||
--card: 224 71% 4%;
|
||||
--card-foreground: 213 31% 91%;
|
||||
|
||||
--border: 216 34% 17%;
|
||||
--input: 216 34% 17%;
|
||||
|
||||
@@ -143,8 +143,8 @@ The following is the default color palette used by the components.
|
||||
--popover: 224 71% 4%;
|
||||
--popover-foreground: 215 20.2% 65.1%;
|
||||
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 222.2 47.4% 11.2%;
|
||||
--card: 224 71% 4%;
|
||||
--card-foreground: 213 31% 91%;
|
||||
|
||||
--border: 216 34% 17%;
|
||||
--input: 216 34% 17%;
|
||||
|
||||
@@ -91,6 +91,16 @@ export default makeSource({
|
||||
return
|
||||
}
|
||||
|
||||
if (codeEl.data?.meta) {
|
||||
// Extract event from meta and pass it down the tree.
|
||||
const regex = /event="([^"]*)"/
|
||||
const match = codeEl.data?.meta.match(regex)
|
||||
if (match) {
|
||||
node.__event__ = match ? match[1] : null
|
||||
codeEl.data.meta = codeEl.data.meta.replace(regex, "")
|
||||
}
|
||||
}
|
||||
|
||||
node.__rawString__ = codeEl.children?.[0].value
|
||||
node.__src__ = node.properties?.__src__
|
||||
}
|
||||
@@ -147,6 +157,10 @@ export default makeSource({
|
||||
if (node.__src__) {
|
||||
preElement.properties["__src__"] = node.__src__
|
||||
}
|
||||
|
||||
if (node.__event__) {
|
||||
preElement.properties["__event__"] = node.__event__
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
24
apps/www/lib/events.ts
Normal file
24
apps/www/lib/events.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import va from "@vercel/analytics"
|
||||
import { z } from "zod"
|
||||
|
||||
const eventSchema = z.object({
|
||||
name: z.enum([
|
||||
"copy_npm_command",
|
||||
"copy_usage_import_code",
|
||||
"copy_usage_code",
|
||||
"copy_primitive_code",
|
||||
]),
|
||||
// declare type AllowedPropertyValues = string | number | boolean | null
|
||||
properties: z
|
||||
.record(z.union([z.string(), z.number(), z.boolean(), z.null()]))
|
||||
.optional(),
|
||||
})
|
||||
|
||||
export type Event = z.infer<typeof eventSchema>
|
||||
|
||||
export function trackEvent(input: Event): void {
|
||||
const event = eventSchema.parse(input)
|
||||
if (event) {
|
||||
va.track(event.name, event.properties)
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ export function rehypeNpmCommand() {
|
||||
return
|
||||
}
|
||||
|
||||
// We'll only deal with the npm install command for now.
|
||||
// npm install.
|
||||
if (node.properties?.["__rawString__"]?.startsWith("npm install")) {
|
||||
const npmCommand = node.properties?.["__rawString__"]
|
||||
node.properties["__npmCommand__"] = npmCommand
|
||||
@@ -21,6 +21,31 @@ export function rehypeNpmCommand() {
|
||||
"pnpm add"
|
||||
)
|
||||
}
|
||||
|
||||
// npx create.
|
||||
if (node.properties?.["__rawString__"]?.startsWith("npx create-")) {
|
||||
const npmCommand = node.properties?.["__rawString__"]
|
||||
node.properties["__npmCommand__"] = npmCommand
|
||||
node.properties["__yarnCommand__"] = npmCommand.replace(
|
||||
"npx create-",
|
||||
"yarn create "
|
||||
)
|
||||
node.properties["__pnpmCommand__"] = npmCommand.replace(
|
||||
"npx create-",
|
||||
"pnpm create "
|
||||
)
|
||||
}
|
||||
|
||||
// npx.
|
||||
if (
|
||||
node.properties?.["__rawString__"]?.startsWith("npx") &&
|
||||
!node.properties?.["__rawString__"]?.startsWith("npx create-")
|
||||
) {
|
||||
const npmCommand = node.properties?.["__rawString__"]
|
||||
node.properties["__npmCommand__"] = npmCommand
|
||||
node.properties["__yarnCommand__"] = npmCommand
|
||||
node.properties["__pnpmCommand__"] = npmCommand.replace("npx", "pnpx")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,11 @@ const nextConfig = {
|
||||
destination: "/docs/components/:path*",
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: "/figma",
|
||||
destination: "/docs/figma",
|
||||
permanent: true,
|
||||
},
|
||||
]
|
||||
},
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
"dev": "concurrently \"contentlayer dev\" \"next dev -p 3001\"",
|
||||
"build": "contentlayer build && pnpm build:components && next build",
|
||||
"build:components": "ts-node --esm --project ./tsconfig.scripts.json ./scripts/build-components.ts",
|
||||
"seed:tasks": "ts-node --esm --project ./tsconfig.scripts.json ./app/examples/tasks/data/seed.ts",
|
||||
"start": "next start -p 3001",
|
||||
"lint": "next lint",
|
||||
"lint:fix": "next lint --fix",
|
||||
@@ -15,6 +16,7 @@
|
||||
"format:check": "prettier --check \"**/*.{ts,tsx,mdx}\" --cache"
|
||||
},
|
||||
"dependencies": {
|
||||
"@faker-js/faker": "^7.6.0",
|
||||
"@radix-ui/react-accessible-icon": "^1.0.1",
|
||||
"@radix-ui/react-accordion": "^1.1.0",
|
||||
"@radix-ui/react-alert-dialog": "^1.0.2",
|
||||
@@ -43,15 +45,16 @@
|
||||
"@radix-ui/react-toggle": "^1.0.1",
|
||||
"@radix-ui/react-toggle-group": "^1.0.1",
|
||||
"@radix-ui/react-tooltip": "^1.0.3",
|
||||
"@vercel/analytics": "^0.1.6",
|
||||
"@tanstack/react-table": "^8.9.1",
|
||||
"@vercel/analytics": "^1.0.0",
|
||||
"@vercel/og": "^0.0.21",
|
||||
"class-variance-authority": "^0.4.0",
|
||||
"clsx": "^1.2.1",
|
||||
"cmdk": "^0.2.0",
|
||||
"contentlayer": "0.3.0",
|
||||
"date-fns": "^2.29.3",
|
||||
"lucide-react": "0.154.0",
|
||||
"next": "13.3.1-canary.8",
|
||||
"lucide-react": "0.214.0",
|
||||
"next": "13.3.5-canary.5",
|
||||
"next-contentlayer": "0.3.0",
|
||||
"next-themes": "^0.2.1",
|
||||
"react": "^18.2.0",
|
||||
|
||||
@@ -80,11 +80,12 @@
|
||||
{
|
||||
"component": "button",
|
||||
"name": "Button",
|
||||
"dependencies": ["@radix-ui/react-slot"],
|
||||
"files": [
|
||||
{
|
||||
"name": "button.tsx",
|
||||
"dir": "components/ui",
|
||||
"content": "import * as React from \"react\"\nimport { VariantProps, cva } from \"class-variance-authority\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst buttonVariants = cva(\n \"inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none ring-offset-background\",\n {\n variants: {\n variant: {\n default: \"bg-primary text-primary-foreground hover:bg-primary/90\",\n destructive:\n \"bg-destructive text-destructive-foreground hover:bg-destructive/90\",\n outline:\n \"border border-input hover:bg-accent hover:text-accent-foreground\",\n secondary:\n \"bg-secondary text-secondary-foreground hover:bg-secondary/80\",\n ghost: \"hover:bg-accent hover:text-accent-foreground\",\n link: \"underline-offset-4 hover:underline text-primary\",\n },\n size: {\n default: \"h-10 py-2 px-4\",\n sm: \"h-9 px-3 rounded-md\",\n lg: \"h-11 px-8 rounded-md\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n }\n)\n\nexport interface ButtonProps\n extends React.ButtonHTMLAttributes<HTMLButtonElement>,\n VariantProps<typeof buttonVariants> {}\n\nconst Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n ({ className, variant, size, ...props }, ref) => {\n return (\n <button\n className={cn(buttonVariants({ variant, size, className }))}\n ref={ref}\n {...props}\n />\n )\n }\n)\nButton.displayName = \"Button\"\n\nexport { Button, buttonVariants }\n"
|
||||
"content": "import * as React from \"react\"\nimport { Slot } from \"@radix-ui/react-slot\"\n\nimport { VariantProps, cva } from \"class-variance-authority\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst buttonVariants = cva(\n \"inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none ring-offset-background\",\n {\n variants: {\n variant: {\n default: \"bg-primary text-primary-foreground hover:bg-primary/90\",\n destructive:\n \"bg-destructive text-destructive-foreground hover:bg-destructive/90\",\n outline:\n \"border border-input hover:bg-accent hover:text-accent-foreground\",\n secondary:\n \"bg-secondary text-secondary-foreground hover:bg-secondary/80\",\n ghost: \"hover:bg-accent hover:text-accent-foreground\",\n link: \"underline-offset-4 hover:underline text-primary\",\n },\n size: {\n default: \"h-10 py-2 px-4\",\n sm: \"h-9 px-3 rounded-md\",\n lg: \"h-11 px-8 rounded-md\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n }\n)\n\nexport interface ButtonProps\n extends React.ButtonHTMLAttributes<HTMLButtonElement>,\n VariantProps<typeof buttonVariants> {}\n\nconst Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n ({ className, variant, size, ...props }, ref) => {\n return (\n <button\n className={cn(buttonVariants({ variant, size, className }))}\n ref={ref}\n {...props}\n />\n )\n }\n)\nButton.displayName = \"Button\"\n\nexport { Button, buttonVariants }\n"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -474,4 +475,4 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
BIN
apps/www/public/examples/tasks-dark.png
Normal file
BIN
apps/www/public/examples/tasks-dark.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 108 KiB |
BIN
apps/www/public/examples/tasks-light.png
Normal file
BIN
apps/www/public/examples/tasks-light.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 86 KiB |
@@ -92,13 +92,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
/** Prevent scrolling on body when modal is open */
|
||||
body[style*="pointer-events: none"] .overflow-auto,
|
||||
body[style*="pointer-events: none"] .overflow-y-auto,
|
||||
body[style*="pointer-events: none"] .overflow-x-auto {
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.container {
|
||||
@apply px-4;
|
||||
|
||||
@@ -18,14 +18,6 @@
|
||||
@apply relative;
|
||||
}
|
||||
|
||||
[data-rehype-pretty-code-fragment] button {
|
||||
@apply opacity-0;
|
||||
}
|
||||
|
||||
[data-rehype-pretty-code-fragment]:hover button {
|
||||
@apply opacity-100;
|
||||
}
|
||||
|
||||
[data-rehype-pretty-code-fragment] code {
|
||||
@apply grid min-w-full break-words rounded-none border-0 bg-transparent p-0;
|
||||
counter-reset: line;
|
||||
@@ -41,13 +33,13 @@
|
||||
}
|
||||
|
||||
[data-rehype-pretty-code-fragment] [data-line-numbers] > .line::before {
|
||||
@apply text-muted-foreground/40 text-xs;
|
||||
counter-increment: line;
|
||||
content: counter(line);
|
||||
display: inline-block;
|
||||
width: 1rem;
|
||||
margin-right: 1rem;
|
||||
width: 1.8rem;
|
||||
margin-right: 1.4rem;
|
||||
text-align: right;
|
||||
color: gray;
|
||||
}
|
||||
|
||||
[data-rehype-pretty-code-fragment] .line--highlighted {
|
||||
|
||||
@@ -8,6 +8,7 @@ export interface UnistNode extends Node {
|
||||
properties?: {
|
||||
__rawString__?: string
|
||||
__className__?: string
|
||||
__event__?: string
|
||||
[key: string]: unknown
|
||||
} & NpmCommands
|
||||
attributes?: {
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
# @shadcn/ui
|
||||
|
||||
## 0.1.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#259](https://github.com/shadcn/ui/pull/259) [`87ad14c`](https://github.com/shadcn/ui/commit/87ad14cb2a27ee2d1000344cbe5f8f4fdbfc939a) Thanks [@ghoshnirmalya](https://github.com/ghoshnirmalya)! - add missing deps for button
|
||||
|
||||
## 0.1.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#271](https://github.com/shadcn/ui/pull/271) [`888a5ad`](https://github.com/shadcn/ui/commit/888a5ad6f602371a27cc88a2573993d5818e745c) Thanks [@shadcn](https://github.com/shadcn)! - fix card color
|
||||
|
||||
## 0.1.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "shadcn-ui",
|
||||
"version": "0.1.1",
|
||||
"version": "0.1.3",
|
||||
"description": "Add components to your apps.",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
||||
@@ -46,8 +46,8 @@ export const STYLES = `@tailwind base;
|
||||
--popover: 224 71% 4%;
|
||||
--popover-foreground: 215 20.2% 65.1%;
|
||||
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 222.2 47.4% 11.2%;
|
||||
--card: 224 71% 4%;
|
||||
--card-foreground: 213 31% 91%;
|
||||
|
||||
--border: 216 34% 17%;
|
||||
--input: 216 34% 17%;
|
||||
|
||||
134
pnpm-lock.yaml
generated
134
pnpm-lock.yaml
generated
@@ -94,6 +94,9 @@ importers:
|
||||
|
||||
apps/www:
|
||||
dependencies:
|
||||
'@faker-js/faker':
|
||||
specifier: ^7.6.0
|
||||
version: 7.6.0
|
||||
'@radix-ui/react-accessible-icon':
|
||||
specifier: ^1.0.1
|
||||
version: 1.0.1(react-dom@18.2.0)(react@18.2.0)
|
||||
@@ -178,9 +181,12 @@ importers:
|
||||
'@radix-ui/react-tooltip':
|
||||
specifier: ^1.0.3
|
||||
version: 1.0.3(@types/react@18.0.22)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@tanstack/react-table':
|
||||
specifier: ^8.9.1
|
||||
version: 8.9.1(react-dom@18.2.0)(react@18.2.0)
|
||||
'@vercel/analytics':
|
||||
specifier: ^0.1.6
|
||||
version: 0.1.6(react@18.2.0)
|
||||
specifier: ^1.0.0
|
||||
version: 1.0.0(react@18.2.0)
|
||||
'@vercel/og':
|
||||
specifier: ^0.0.21
|
||||
version: 0.0.21
|
||||
@@ -200,17 +206,17 @@ importers:
|
||||
specifier: ^2.29.3
|
||||
version: 2.29.3
|
||||
lucide-react:
|
||||
specifier: 0.154.0
|
||||
version: 0.154.0(react@18.2.0)
|
||||
specifier: 0.214.0
|
||||
version: 0.214.0(react@18.2.0)
|
||||
next:
|
||||
specifier: 13.3.1-canary.8
|
||||
version: 13.3.1-canary.8(@babel/core@7.20.7)(@opentelemetry/api@1.1.0)(react-dom@18.2.0)(react@18.2.0)
|
||||
specifier: 13.3.5-canary.5
|
||||
version: 13.3.5-canary.5(@babel/core@7.20.7)(@opentelemetry/api@1.1.0)(react-dom@18.2.0)(react@18.2.0)
|
||||
next-contentlayer:
|
||||
specifier: 0.3.0
|
||||
version: 0.3.0(esbuild@0.17.3)(next@13.3.1-canary.8)(react-dom@18.2.0)(react@18.2.0)
|
||||
version: 0.3.0(esbuild@0.17.3)(next@13.3.5-canary.5)(react-dom@18.2.0)(react@18.2.0)
|
||||
next-themes:
|
||||
specifier: ^0.2.1
|
||||
version: 0.2.1(next@13.3.1-canary.8)(react-dom@18.2.0)(react@18.2.0)
|
||||
version: 0.2.1(next@13.3.5-canary.5)(react-dom@18.2.0)(react@18.2.0)
|
||||
react:
|
||||
specifier: ^18.2.0
|
||||
version: 18.2.0
|
||||
@@ -1610,6 +1616,11 @@ packages:
|
||||
resolution: {integrity: sha512-IoD2MfUnOV58ghIHCiil01PcohxjbYR/qCxsoC+xNgUwh1EY8jOOrYmu3d3a71+tJJ23uscEV4X2HJWMsPJu4g==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
|
||||
/@faker-js/faker@7.6.0:
|
||||
resolution: {integrity: sha512-XK6BTq1NDMo9Xqw/YkYyGjSsg44fbNwYRx7QK2CuoQgyy+f1rrTDHoExVM5PsyXCtfl2vs2vVJ0MN0yN6LppRw==}
|
||||
engines: {node: '>=14.0.0', npm: '>=6.0.0'}
|
||||
dev: false
|
||||
|
||||
/@fal-works/esbuild-plugin-global-externals@2.1.2:
|
||||
resolution: {integrity: sha512-cEee/Z+I12mZcFJshKcCqC8tuX5hG3s+d+9nZ3LabqKF1vKdF41B92pJVCBggjAGORAeOzyyDDKrZwIkLffeOQ==}
|
||||
dev: false
|
||||
@@ -1886,8 +1897,8 @@ packages:
|
||||
resolution: {integrity: sha512-AjppRV4uG3No7L1plinoTQETH+j2F10TEnrMfzbTUYwze5sBUPveeeBAPZPm8OkJZ1epq9OyYKhZrvbD6/9HCQ==}
|
||||
dev: false
|
||||
|
||||
/@next/env@13.3.1-canary.8:
|
||||
resolution: {integrity: sha512-xZfNu7yq3OfiC4rkGuGMcqb25se+ZHRqajSdny8dp+nZzkNSK1SHuNT3W8faI+KGk6dqzO/zAdHR9YrqnQlCAg==}
|
||||
/@next/env@13.3.5-canary.5:
|
||||
resolution: {integrity: sha512-tYs2Lo9kVCaKybcNjYeRqJpuVjD/ATQNDIPBUg5YDsZ6Z5XHF2p9BGhhU93cEGhexwO/9aypOKYmHJDfEcpM/Q==}
|
||||
dev: false
|
||||
|
||||
/@next/eslint-plugin-next@13.0.0:
|
||||
@@ -1911,8 +1922,8 @@ packages:
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/@next/swc-darwin-arm64@13.3.1-canary.8:
|
||||
resolution: {integrity: sha512-BLbvhcaSzwuXbREOmJiqAdXVD7Jl9830hDY5ZTTNg7hXqEZgoMg2LxAEmtaaBMVZRfDQjd5bH3QPBV8fbG4UKg==}
|
||||
/@next/swc-darwin-arm64@13.3.5-canary.5:
|
||||
resolution: {integrity: sha512-Tdn79uIBS05Wj/5F7L9D6JbHyzaFaPBs6mK0xZ2XNE4m3qbKukihKT4f6/8T9Peu+gUFYXjkum/rgWrpVoO4ww==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
@@ -1929,8 +1940,8 @@ packages:
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/@next/swc-darwin-x64@13.3.1-canary.8:
|
||||
resolution: {integrity: sha512-n4tJKPIvFTZshS1TVWrsqaW7h9VW+BmguO/AlZ3Q3NJ9hWxC5L4lxn2T6CTQ4M30Gf+t5u+dPzYLQ5IDtJFnFQ==}
|
||||
/@next/swc-darwin-x64@13.3.5-canary.5:
|
||||
resolution: {integrity: sha512-k/9mW2l691yLQ2TQPi4LUDUfao1M2h/AYw2+d4ONiISzIExMFVGWL3Mt4N2HrwDQOEImOGzyHSYYyXoz0WaR4g==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
@@ -1947,8 +1958,8 @@ packages:
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/@next/swc-linux-arm64-gnu@13.3.1-canary.8:
|
||||
resolution: {integrity: sha512-AxnsgZ56whwVAeejyEZMk8xc8Vapwzb3Zn0YdZzPCR42WKfkcSkM+AWfq33zUOZnjvCmQBDyfHIo4CURVweR6g==}
|
||||
/@next/swc-linux-arm64-gnu@13.3.5-canary.5:
|
||||
resolution: {integrity: sha512-fwTjjs8zI595TxIGX1rJJBkj37iQX0bZncSyE2zSTonSRrAFyQcHeZlWrGzrnvMiGgUBebdD7iTsWl8dn1y3og==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
@@ -1965,8 +1976,8 @@ packages:
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/@next/swc-linux-arm64-musl@13.3.1-canary.8:
|
||||
resolution: {integrity: sha512-zc7rzhtrHMWZ/phvjCNplHGo+ZLembjtluI5J8Xl4iwQQCyZwAtnmQhs37/zkdi6dHZou+wcFBZWRz14awRDBw==}
|
||||
/@next/swc-linux-arm64-musl@13.3.5-canary.5:
|
||||
resolution: {integrity: sha512-BeccDkXIlgkwLXJS/HjC4Draw6n36VjMaf9XYv6YOAJlfzkifttGdKCebbtFSSowH2Zne8hJ/hyXPIGSvtJBQQ==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
@@ -1983,8 +1994,8 @@ packages:
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/@next/swc-linux-x64-gnu@13.3.1-canary.8:
|
||||
resolution: {integrity: sha512-vNbFDiuZ9fWmcznlilDbflZLb04evWPUQlyDT7Tqjd964PlSIaaX3tr64pdYjJOljDaqTr2Kbx0YW74mWF/PEw==}
|
||||
/@next/swc-linux-x64-gnu@13.3.5-canary.5:
|
||||
resolution: {integrity: sha512-B5EDcFBUElnrBekSXhMCUPpYPlAUd2CqEmTQZGYWR8reVQobeDPNbz5+1Q1TttQ4FOChculVMAb+uXzm5P9vcQ==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
@@ -2001,8 +2012,8 @@ packages:
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/@next/swc-linux-x64-musl@13.3.1-canary.8:
|
||||
resolution: {integrity: sha512-/FVBPJEBDZYCNraocRWtd5ObAgNi9VFnzJYGYDYIj4jKkFRWWm/CaWu9A7toQACC/JDy262uPyDPathXT9BAqQ==}
|
||||
/@next/swc-linux-x64-musl@13.3.5-canary.5:
|
||||
resolution: {integrity: sha512-O36kiYlC6QOtDt1QN8gJlxXM9CqBLeVimGZHtZjx6W+ro5lW8S1M0P6TC/ygn58KchnHFlowZ4GfXlxSLAxsDw==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
@@ -2019,8 +2030,8 @@ packages:
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/@next/swc-win32-arm64-msvc@13.3.1-canary.8:
|
||||
resolution: {integrity: sha512-8jMwRCeI26yVZLPwG0AjOi4b1yqSeqYmbHA7r+dqiV0OgFdYjnbyHU1FmiKDaC5SnnJN6LWV2Qjer9GDD0Kcuw==}
|
||||
/@next/swc-win32-arm64-msvc@13.3.5-canary.5:
|
||||
resolution: {integrity: sha512-zFft6tPrrwkKAKC7ZNNVBP8INLnrqICurzare4qLv5sOvMA0qTIUUNviEasX8s+SQzs0YMiw/vhyRYZHHuXPAQ==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
@@ -2037,8 +2048,8 @@ packages:
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/@next/swc-win32-ia32-msvc@13.3.1-canary.8:
|
||||
resolution: {integrity: sha512-kcYB9iSEikFhv0I9uQDdgQ2lm8i3O8LA+GhnED9e5VtURBwOSwED7c6ZpaRQBYSPgnEA9/xiJVChICE/I7Ig1g==}
|
||||
/@next/swc-win32-ia32-msvc@13.3.5-canary.5:
|
||||
resolution: {integrity: sha512-uB4b/UVQBui2hqDJHRnF+TZVz5qxzglstra3Smu8r3ech/Dr29ETOaVx7e0yioxYl3H668L1XCfCnN464lgVNQ==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [ia32]
|
||||
os: [win32]
|
||||
@@ -2055,8 +2066,8 @@ packages:
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/@next/swc-win32-x64-msvc@13.3.1-canary.8:
|
||||
resolution: {integrity: sha512-UKrGHonKVWBNg+HI4J8pXE6Jjjl8GwjhygFau71s8M0+jSy99y5Y+nGH9EmMNWKNvrObukyYvrs6OsAusKdCqw==}
|
||||
/@next/swc-win32-x64-msvc@13.3.5-canary.5:
|
||||
resolution: {integrity: sha512-8FY/B2C7eWno1pruKj4NveMOpEwbuPjZhdc971X2jIIhjjQi+vyee5LU4muRDtnWte+yVbzFSfENu+gGJYdbaw==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
@@ -3352,6 +3363,12 @@ packages:
|
||||
tslib: 2.5.0
|
||||
dev: false
|
||||
|
||||
/@swc/helpers@0.5.1:
|
||||
resolution: {integrity: sha512-sJ902EfIzn1Fa+qYmjdQqh8tPsoxyBz+8yBKC2HKUxyezKJFwPGOn7pv4WY6QuQW//ySQi5lJjA/ZT9sNWWNTg==}
|
||||
dependencies:
|
||||
tslib: 2.5.0
|
||||
dev: false
|
||||
|
||||
/@szmarczak/http-timer@1.1.2:
|
||||
resolution: {integrity: sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==}
|
||||
engines: {node: '>=6'}
|
||||
@@ -3359,6 +3376,23 @@ packages:
|
||||
defer-to-connect: 1.1.3
|
||||
dev: false
|
||||
|
||||
/@tanstack/react-table@8.9.1(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-yHs2m6lk5J5RNcu2dNtsDGux66wNXZjEgzxos6MRCX8gL+nqxeW3ZglqP6eANN0bGElPnjvqiUHGQvdACOr3Cw==}
|
||||
engines: {node: '>=12'}
|
||||
peerDependencies:
|
||||
react: '>=16'
|
||||
react-dom: '>=16'
|
||||
dependencies:
|
||||
'@tanstack/table-core': 8.9.1
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/@tanstack/table-core@8.9.1:
|
||||
resolution: {integrity: sha512-2+R83n8vMZND0q3W1lSiF7co9nFbeWbjAErFf27xwbeA9E0wtUu5ZDfgj+TZ6JzdAEQAgfxkk/QNFAKiS8E4MA==}
|
||||
engines: {node: '>=12'}
|
||||
dev: false
|
||||
|
||||
/@tsconfig/node10@1.0.9:
|
||||
resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==}
|
||||
|
||||
@@ -3649,8 +3683,8 @@ packages:
|
||||
'@typescript-eslint/types': 5.58.0
|
||||
eslint-visitor-keys: 3.4.0
|
||||
|
||||
/@vercel/analytics@0.1.6(react@18.2.0):
|
||||
resolution: {integrity: sha512-zNd5pj3iDvq8IMBQHa1YRcIteiw6ZiPB8AsONHd8ieFXlNpLqhXfIYnf4WvTfZ7S1NSJ++mIM14aJnNac/VMXQ==}
|
||||
/@vercel/analytics@1.0.0(react@18.2.0):
|
||||
resolution: {integrity: sha512-RQmj7pv82JwGDHrnKeRc6TtSw2U7rWNubc2IH0ernTzWTj02yr9zvIYiYJeztsBzrJtWv7m8Nz6vxxb+cdEtJw==}
|
||||
peerDependencies:
|
||||
react: ^16.8||^17||^18
|
||||
dependencies:
|
||||
@@ -6735,8 +6769,8 @@ packages:
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
/lucide-react@0.154.0(react@18.2.0):
|
||||
resolution: {integrity: sha512-KnuJN4QHl4jh0hEHeG5hDRLf1TIdcg2bSyhnin4+IoLyS7GQNB8h69d5vXi/WZaGpmitsPLF8RVJ96NHHKFnFg==}
|
||||
/lucide-react@0.214.0(react@18.2.0):
|
||||
resolution: {integrity: sha512-/vRi1wnFV2lqyIIkghQ3dDLu0eA9zykRQN9GZBwydzv+kB/2Q3S4X6OYB+aRqLXwl438vfVBqyYov2z0LJeoqA==}
|
||||
peerDependencies:
|
||||
react: ^16.5.1 || ^17.0.0 || ^18.0.0
|
||||
dependencies:
|
||||
@@ -7451,7 +7485,7 @@ packages:
|
||||
/natural-compare@1.4.0:
|
||||
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
|
||||
|
||||
/next-contentlayer@0.3.0(esbuild@0.17.3)(next@13.3.1-canary.8)(react-dom@18.2.0)(react@18.2.0):
|
||||
/next-contentlayer@0.3.0(esbuild@0.17.3)(next@13.3.5-canary.5)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-vt+RaD3nIgZ6oXadtZH19a1mpxvGEoiifdtmXqBSz4rHMRcMA1YZCuSWyj+P9uX7MDmIL6JT6QSp+hvTBMaxiw==}
|
||||
peerDependencies:
|
||||
next: ^12 || ^13
|
||||
@@ -7460,7 +7494,7 @@ packages:
|
||||
dependencies:
|
||||
'@contentlayer/core': 0.3.0(esbuild@0.17.3)
|
||||
'@contentlayer/utils': 0.3.0
|
||||
next: 13.3.1-canary.8(@babel/core@7.20.7)(@opentelemetry/api@1.1.0)(react-dom@18.2.0)(react@18.2.0)
|
||||
next: 13.3.5-canary.5(@babel/core@7.20.7)(@opentelemetry/api@1.1.0)(react-dom@18.2.0)(react@18.2.0)
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
transitivePeerDependencies:
|
||||
@@ -7482,14 +7516,14 @@ packages:
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/next-themes@0.2.1(next@13.3.1-canary.8)(react-dom@18.2.0)(react@18.2.0):
|
||||
/next-themes@0.2.1(next@13.3.5-canary.5)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-B+AKNfYNIzh0vqQQKqQItTS8evEouKD7H5Hj3kmuPERwddR2TxvDSFZuTj6T7Jfn1oyeUyJMydPl1Bkxkh0W7A==}
|
||||
peerDependencies:
|
||||
next: '*'
|
||||
react: '*'
|
||||
react-dom: '*'
|
||||
dependencies:
|
||||
next: 13.3.1-canary.8(@babel/core@7.20.7)(@opentelemetry/api@1.1.0)(react-dom@18.2.0)(react@18.2.0)
|
||||
next: 13.3.5-canary.5(@babel/core@7.20.7)(@opentelemetry/api@1.1.0)(react-dom@18.2.0)(react@18.2.0)
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
@@ -7538,9 +7572,9 @@ packages:
|
||||
- babel-plugin-macros
|
||||
dev: false
|
||||
|
||||
/next@13.3.1-canary.8(@babel/core@7.20.7)(@opentelemetry/api@1.1.0)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-z4QUgyAN+hSWSEqb4pvGvC3iRktE6NH2DVLU4AvfqNYpzP+prePiJC8HN/cJpFhGW9YbhyRLi5FliDC631OOag==}
|
||||
engines: {node: '>=14.6.0'}
|
||||
/next@13.3.5-canary.5(@babel/core@7.20.7)(@opentelemetry/api@1.1.0)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-zUpBiUV1K8VtWH49WTGMTMnMLMzasacFv2JiLkyJ+N7fQrgdjtgbige9R6iymyUCl99LSXq+Y6sYNlOeGPUniQ==}
|
||||
engines: {node: '>=16.8.0'}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
'@opentelemetry/api': ^1.1.0
|
||||
@@ -7559,9 +7593,9 @@ packages:
|
||||
sass:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@next/env': 13.3.1-canary.8
|
||||
'@next/env': 13.3.5-canary.5
|
||||
'@opentelemetry/api': 1.1.0
|
||||
'@swc/helpers': 0.4.14
|
||||
'@swc/helpers': 0.5.1
|
||||
busboy: 1.6.0
|
||||
caniuse-lite: 1.0.30001480
|
||||
postcss: 8.4.14
|
||||
@@ -7569,15 +7603,15 @@ packages:
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
styled-jsx: 5.1.1(@babel/core@7.20.7)(react@18.2.0)
|
||||
optionalDependencies:
|
||||
'@next/swc-darwin-arm64': 13.3.1-canary.8
|
||||
'@next/swc-darwin-x64': 13.3.1-canary.8
|
||||
'@next/swc-linux-arm64-gnu': 13.3.1-canary.8
|
||||
'@next/swc-linux-arm64-musl': 13.3.1-canary.8
|
||||
'@next/swc-linux-x64-gnu': 13.3.1-canary.8
|
||||
'@next/swc-linux-x64-musl': 13.3.1-canary.8
|
||||
'@next/swc-win32-arm64-msvc': 13.3.1-canary.8
|
||||
'@next/swc-win32-ia32-msvc': 13.3.1-canary.8
|
||||
'@next/swc-win32-x64-msvc': 13.3.1-canary.8
|
||||
'@next/swc-darwin-arm64': 13.3.5-canary.5
|
||||
'@next/swc-darwin-x64': 13.3.5-canary.5
|
||||
'@next/swc-linux-arm64-gnu': 13.3.5-canary.5
|
||||
'@next/swc-linux-arm64-musl': 13.3.5-canary.5
|
||||
'@next/swc-linux-x64-gnu': 13.3.5-canary.5
|
||||
'@next/swc-linux-x64-musl': 13.3.5-canary.5
|
||||
'@next/swc-win32-arm64-msvc': 13.3.5-canary.5
|
||||
'@next/swc-win32-ia32-msvc': 13.3.5-canary.5
|
||||
'@next/swc-win32-x64-msvc': 13.3.5-canary.5
|
||||
transitivePeerDependencies:
|
||||
- '@babel/core'
|
||||
- babel-plugin-macros
|
||||
|
||||
@@ -8,7 +8,6 @@ COMMIT_MESSAGE=$(git log -1 --pretty=%B)
|
||||
|
||||
for folder in $GLOB; do
|
||||
[ -d "$folder" ] || continue
|
||||
cp -r ./apps/www/components/ui $folder/components
|
||||
cd $BASE
|
||||
|
||||
if [ -n "$(git status --porcelain)" ]; then
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import {
|
||||
Laptop,
|
||||
LucideProps,
|
||||
Moon,
|
||||
SunMedium,
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": false,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noEmit": true,
|
||||
"incremental": true,
|
||||
|
||||
Reference in New Issue
Block a user