Compare commits

...

27 Commits

Author SHA1 Message Date
github-actions[bot]
df9369762a chore(release): version packages (#352)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-05-13 13:35:46 +04:00
Nirmalya Ghosh
87ad14cb2a fix(shadcn-ui): add missing deps for Button (#259)
* fix: add missing deps for Button

* fix: use correct dep

Co-authored-by: Pablo <its.monotype@gmail.com>

* chore: add changeset

---------

Co-authored-by: Pablo <its.monotype@gmail.com>
Co-authored-by: shadcn <m@shadcn.com>
2023-05-13 13:07:39 +04:00
Jack
5a2ce61e2e fix(data-table): uncontrolled input error on data-table (#335) 2023-05-11 10:07:39 +04:00
Bilal Afzal
4a7c07e754 fix(next-template): remove unused import Laptop in icons.tsx (#310)
Remove unused import 'Laptop' in icons.tsx (nextjs-example)

Co-authored-by: shadcn <m@shadcn.com>
2023-05-10 21:48:46 +04:00
shadcn
8eb3e1e160 fix(www): update padding for row actions 2023-05-10 20:51:00 +04:00
shadcn
5bc68894b8 fix(www): filter display for lg 2023-05-10 16:32:57 +04:00
Connor Stevens
21890afa80 docs(www): fix typo
Co-authored-by: shadcn <m@shadcn.com>
2023-05-10 07:49:17 +04:00
Ben Jacobson
84b7200178 docs(www): fix link to DataTable (#322) 2023-05-10 00:09:15 +04:00
shadcn
f8272baf07 feat: add table and data table (#321) 2023-05-09 23:25:26 +04:00
Mike Robinson
9a6b934421 feat(table): add table component (#248)
* Add table.tsx

* Update table.tsx

---------

Co-authored-by: shadcn <m@shadcn.com>
2023-05-08 15:49:19 +04:00
Mike Robinson
b3247d90a6 fix(checkbox,radio-group): Increase contrast ratio of checkboxes and radio buttons (#224)
* Update checkbox.tsx

* Update radio-group.tsx

* fix(radio-group): update colors for contrast

* fix(checkbox): update colors for contrast

---------

Co-authored-by: shadcn <m@shadcn.com>
2023-05-08 15:43:10 +04:00
Danazumi
0c31f60bb0 fix(next-template): strict type check
Co-authored-by: Danazumi <granzin98@gmail.com>
Co-authored-by: shadcn <m@shadcn.com>
2023-05-03 18:47:49 +04:00
James
be64c55901 fix(dropdown-menu): dropdown sub menu using wrong text color (#285) 2023-05-03 17:20:18 +04:00
shadcn
b19199a35d docs: move combobox and date picker to their own page (#283) 2023-05-03 15:59:00 +04:00
shadcn
6e67107170 fix(www): update to canary with scrolling fix (#279) 2023-05-03 10:53:59 +04:00
github-actions[bot]
71c631891f chore(release): version packages (#272)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-05-02 19:25:02 +04:00
shadcn
888a5ad6f6 fix(shadcn-ui): update color of cards in dark mode (#271) 2023-05-02 15:39:16 +04:00
Evan Rosenfeld
d99b992e27 feat(button): support render delegation with asChild parameter (#174) 2023-04-30 12:01:41 +04:00
Elliot Anderson
7dc67be1ad fix(www): set icon fill to current currentColor (#239) 2023-04-29 18:52:25 +04:00
Aron Hafner
d528e9a45e docs(www): set correct colors for card in darkmode (#213) 2023-04-20 16:36:38 +04:00
Jack B
2a61b54096 fix(www): component selection in copy-button (#220)
Co-authored-by: shadcn <m@shadcn.com>
2023-04-20 16:20:14 +04:00
Esteban De la Rosa
5b004f7565 docs(www): Fix typo in alert component usage (#196)
Co-authored-by: shadcn <m@shadcn.com>
2023-04-20 12:49:15 +04:00
shadcn
5e915756d5 feat(www): implement event tracking (#218)
* feat(www): implement event tracking

* fix(www): always show copy button

* fix(www): update align props for copy button
2023-04-20 12:21:50 +04:00
Elia Maino
bfc66148ea fix(badge): update destructive bg color on hover (#189) 2023-04-20 09:34:32 +04:00
Vincent Taneri
2fa78133c8 docs(www): fix a typo in FAQ (#198) 2023-04-19 19:56:04 +04:00
Seo Jun Hyung
3a60500d54 fix(www): remove styles for preventing scroll when modal is open (#209) 2023-04-19 19:43:33 +04:00
shadcn
a5b313ab13 chore: update template 2023-04-19 15:01:29 +04:00
78 changed files with 3866 additions and 316 deletions

View File

@@ -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>

View File

@@ -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>

View File

@@ -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">

View 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} />,
},
]

View File

@@ -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>
)
}

View File

@@ -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>
)
}

View File

@@ -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>
)
}

View File

@@ -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>
)
}

View File

@@ -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>
)
}

View File

@@ -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>
)
}

View 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>
)
}

View 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>
)
}

View 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,
},
]

View 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>

View 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.")

View 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"
}
]

View 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&apos;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>
</>
)
}

View File

@@ -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>
)
}

View File

@@ -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

View File

@@ -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 />

View File

@@ -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">

View File

@@ -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}

View File

@@ -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>

View File

@@ -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>

View 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>
)
}

View File

@@ -41,7 +41,7 @@ const frameworks = [
},
]
export function CommandCombobox() {
export function ComboboxDemo() {
const [open, setOpen] = React.useState(false)
const [value, setValue] = React.useState("")

View File

@@ -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)

View File

@@ -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

View 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>
)
},
},
]

View 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>
)
}

View 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} />
}

View File

@@ -13,7 +13,7 @@ import {
PopoverTrigger,
} from "@/components/ui/popover"
export function CalendarDatePicker() {
export function DatePickerDemo() {
const [date, setDate] = React.useState<Date>()
return (

View File

@@ -20,7 +20,7 @@ import {
SelectValue,
} from "@/components/ui/select"
export function CalendarDatePickerWithPresets() {
export function DatePickerWithPresets() {
const [date, setDate] = React.useState<Date>()
return (

View File

@@ -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>({

View File

@@ -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,

View 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>
)
}

View File

@@ -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) => (

View File

@@ -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} />
),

View File

@@ -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>
)
}

View File

@@ -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>
)}

View File

@@ -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",
},
},

View File

@@ -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}

View File

@@ -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>

View File

@@ -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}

View File

@@ -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>
)

View 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,
}

View File

@@ -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",

View File

@@ -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",

View File

@@ -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

View File

@@ -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>

View File

@@ -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.

View 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>

View File

@@ -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.

View 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} />
```

View 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>

View 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.

View 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

View File

@@ -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>

View File

@@ -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%;

View File

@@ -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%;

View File

@@ -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
View 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)
}
}

View File

@@ -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")
}
})
}
}

View File

@@ -32,6 +32,11 @@ const nextConfig = {
destination: "/docs/components/:path*",
permanent: true,
},
{
source: "/figma",
destination: "/docs/figma",
permanent: true,
},
]
},
}

View File

@@ -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",

View File

@@ -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 @@
}
]
}
]
]

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

View File

@@ -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;

View File

@@ -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 {

View File

@@ -8,6 +8,7 @@ export interface UnistNode extends Node {
properties?: {
__rawString__?: string
__className__?: string
__event__?: string
[key: string]: unknown
} & NpmCommands
attributes?: {

View File

@@ -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

View File

@@ -1,6 +1,6 @@
{
"name": "shadcn-ui",
"version": "0.1.1",
"version": "0.1.3",
"description": "Add components to your apps.",
"publishConfig": {
"access": "public"

View File

@@ -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
View File

@@ -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

View File

@@ -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

View File

@@ -1,5 +1,4 @@
import {
Laptop,
LucideProps,
Moon,
SunMedium,

View File

@@ -3,7 +3,7 @@
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": false,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"incremental": true,