Compare commits

...

47 Commits

Author SHA1 Message Date
github-actions[bot]
729b9ec8ca chore(release): version packages (#5812)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-12-10 12:17:32 +04:00
Jens Astrup
a1bed464f3 chore(apps): Update lodash (#4397)
* chore(apps): Refactor usage of lodash.template to lodash to address security vulnerability

* chore(cli): Refactor usage of lodash.template to lodash to address security vulnerability

* deps: update lock

* chore: changesets

* style: fix format

* fix: import

* chore: build registry

---------

Co-authored-by: shadcn <m@shadcn.com>
2024-12-09 12:33:28 +04:00
shadcn
805ed4120a feat(www): refactor examples page (#5818) 2024-11-13 16:01:14 +04:00
shadcn
600a593c87 docs(www): update manual installation (#5817) 2024-11-13 15:42:52 +04:00
Braden Corbold
500dbe2664 fix(shadcn) arrays and nested deeply nested spread (#5711)
* fix: tailwind config updater parser

* fix: remove quote around spread element

* fix: specify deepmerge option for array

* fix(shadcn): Nested and spread array elements

* add test case for boolean primitive

---------

Co-authored-by: matsuyoshi30 <sfbgwm30@gmail.com>
Co-authored-by: shadcn <m@shadcn.com>
2024-11-13 15:19:21 +04:00
JEM
c577ee0666 docs(contributing): add CLI usage instructions (#5807)
Introduce a detailed workflow for running the CLI locally, ensuring contributors can easily test and develop enhancements in their local environment. This addition helps maintain consistency between CLI and component updates.
2024-11-13 15:17:11 +04:00
shadcn
d5bf0018fd fix(shadcn): handling of tsconfig aliases (#5813)
* fix(shadcn): handling of tsconfig aliases

* chore: add changeset
2024-11-13 15:15:22 +04:00
Tobbe Lundberg
fb36ca4159 fix(transform): Support aliases that are longer than one character (#5678)
* fix(transform): Support aliases that are longer than one character

* feat(shadcn): update handling of aliases

* chore: add changeset

---------

Co-authored-by: shadcn <m@shadcn.com>
2024-11-12 22:13:38 +04:00
JEM
824a35ada1 test(snapshots): update test snapshots for consistency (#5801)
Updated snapshot components to use `React.ComponentProps` instead of custom interfaces for `input` and `Dialog` components. This simplifies the code by leveraging built-in React types, ensuring consistency and reducing potential errors arising from custom definitions.
2024-11-12 12:54:31 +04:00
shadcn
8d520c8d49 fix(www): figma link (#5798) 2024-11-11 20:08:24 +04:00
Temkin Mengsitu
0873835339 fix: ensure block component name is copied in shadcn (#5790) 2024-11-11 12:10:04 +04:00
JEM
4a0d4cfdb9 refactor(components): remove redundant empty interfaces from Input, Command, and Textarea components (#5657)
* refactor(components): remove redundant empty interfaces from Input, Command, and Textarea components

Simplified props handling by removing redundant empty interfaces (`InputProps`, `CommandDialogProps`, `TextareaProps`). Directly extended native HTML attributes/lib for each component, enhancing code clarity and maintainability. This refactor ensures compliance with ESLint rules and streamlines future modifications without altering component functionality.

* updated the registry

* rerun build:registry with updated command

* Refactor input and textarea components to use `React.ComponentProps`

* update registry
2024-11-08 20:26:01 +04:00
shadcn
c4c5d8d419 feat(www): update next-themes 2024-11-08 15:18:32 +04:00
Wing Chung Ng
9253682b87 refactor(www): update ThemeProviderProps type extraction for next-themes v0.4.0 compatibility (#5701)
Since next-themes v0.4.0 no longer provides types in `dist`, replaced the import of `ThemeProviderProps` from `next-themes/dist/types` with a new type extraction using `React.ComponentProps`. This change ensures continued type safety and compatibility with the updated library version.

Co-authored-by: shadcn <m@shadcn.com>
2024-11-08 15:16:10 +04:00
shadcn
c7cd16a637 fix(www): interactive chart 2024-11-07 21:36:19 +04:00
shadcn
eff1918d41 fix: update file handling 2024-11-07 21:21:42 +04:00
shadcn
366d6b656b fix(www): file tracing 2024-11-07 21:05:44 +04:00
shadcn
e489c5e08e fix(www): file tracing 2024-11-07 20:54:26 +04:00
shadcn
d87003e0a4 fix(www): sidebar 2024-11-07 20:40:18 +04:00
shadcn
a8633075f7 fix(www): component source 2024-11-07 20:36:21 +04:00
shadcn
432d5e6e28 fix: code view for mdx 2024-11-07 17:45:34 +04:00
shadcn
8f0c26f22a feat(www): code for blocks (#5756)
* feat: update blocks

* fix: scrollbars

* fix: code viewer

* test(shadcn): fix
2024-11-07 17:09:41 +04:00
shadcn
149b321c1b fix: lint 2024-11-06 17:34:21 +04:00
shadcn
c1ae5a57cc feat(www): nav color 2024-11-06 17:29:53 +04:00
shadcn
b8ed303d8c Merge branch 'main' of github.com:shadcn-ui/ui 2024-11-06 17:19:41 +04:00
shadcn
13c97acf9f fix: font size 2024-11-06 17:16:21 +04:00
shadcn
bed277c54d feat( www): minor updates (#5749)
* feat(www): update mode switcher

* feat(www): update layout

* feat(www): update theme-color handling

* fix(www): docs pages
2024-11-06 17:03:28 +04:00
github-actions[bot]
f7c42169a6 chore(release): version packages (#5733)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-11-06 00:19:13 +04:00
shadcn
2c2fe97eb9 feat: move new-york to lucide-react (#5602)
* feat: move new-york to lucide-react

* fix: mail open

* chore: update registry

* chore: add test:dev

* chore: add changeset

* feat: build an icon registry

* chore: add missing registry icons

* feat: add an icons debug page

* feat: add an icon migration

* chore(www): migrate all radix icons to lucide

* feat: update migration script

* chore: update changeset

* feat(shadcn): implement icons transformer

* fix: missing registry icons

* fix(shadcn): handling of missing icons

* feat: add support for multiple libraries
2024-11-06 00:00:41 +04:00
shadcn
d64374d009 fix(www): chart colors 2024-11-04 23:06:14 +04:00
github-actions[bot]
e24e51a2fa chore(release): version packages (#5709)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-11-04 17:25:59 +04:00
shadcn
cdfecd1d97 Revert "feat: remove npm flags" (#5707)
* Revert "feat: remove npm flags (#5686)"

This reverts commit 4ff64ba818.

* chore: temporarily bring back flag
2024-11-04 17:14:08 +04:00
manfromexistence04
2c043e709f fix: svg props casing to camelCase (#5691) 2024-11-03 19:27:28 +04:00
Ghribi Ouassim
aed19aa911 docs(www): added --all option to add command in cli docs page (#5591) 2024-11-03 14:37:08 +04:00
shadcn
9e35d229ae feat: www updates (#5688)
* feat: www updates

* fix: lint
2024-11-03 14:35:04 +04:00
shadcn
db1975ef4d Merge branch 'main' of github.com:shadcn-ui/ui 2024-11-03 13:27:19 +04:00
shadcn
961e0b62d7 fix: a11y for card and calendar 2024-11-03 13:27:10 +04:00
github-actions[bot]
70c684c224 chore(release): version packages (#5687)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-11-03 12:17:58 +04:00
shadcn
4ff64ba818 feat: remove npm flags (#5686)
* docs: update status table and docs

* feat(shadcn): remove react-19 check

* chore: add changeset
2024-11-03 12:12:41 +04:00
shadcn
500a353816 feat: update home page (#5648) 2024-10-30 21:28:58 +04:00
Nicholas Lim
c830780d62 docs(www): fix diff for peerDependencies (#5640)
Co-authored-by: shadcn <m@shadcn.com>
2024-10-30 17:23:06 +04:00
Felix Lu
debd51a854 refactor(sidebar): improve setOpen callback logic (#5593)
* refactor(sidebar): improve setOpen callback logic
- Simplify state handling
- Ensure consistent cookie setting
- Added ability for users to persist sidebar state via cookies
- Update sidebar.mdx doc with SSR persistence instructions
- Run pnpm registry:build to update component

* docs: updates

---------

Co-authored-by: shadcn <m@shadcn.com>
2024-10-30 17:09:09 +04:00
shadcn
78426dd862 docs: fix diff 2024-10-30 16:31:44 +04:00
shadcn
6e47a94a8f chore: format 2024-10-30 15:40:46 +04:00
Barinderpreet Singh
ab6a856930 Docs: update vite installation docs (#4741)
* docs: update vite installation docs

* Update content config

---------

Co-authored-by: shadcn <m@shadcn.com>
2024-10-30 15:37:32 +04:00
shadcn
b33d3868e9 docs(www): update status for radix icons (#5638) 2024-10-30 11:29:48 +04:00
Maou
9e0a86122a fix(docs): resolve link issue in documentation pages (#5633)
Updated broken link in documentation routing file. 
This ensures that the correct content is loaded based on the slug.
2024-10-30 07:10:15 +00:00
545 changed files with 19476 additions and 3514 deletions

View File

@@ -91,6 +91,42 @@ pnpm --filter=www dev
pnpm --filter=shadcn-ui dev pnpm --filter=shadcn-ui dev
``` ```
## Running the CLI Locally
To run the CLI locally, you can follow the workflow:
1. Start by running the registry (main site) to make sure the components are up to date:
```bash
pnpm www:dev
```
2. Run the development script for the CLI:
```bash
pnpm shadcn:dev
```
3. In another terminal tab, test the CLI by running:
```bash
pnpm shadcn
```
To test the CLI in a specific app, use a command like:
```bash
pnpm shadcn <init | add | ...> -c ~/Desktop/my-app
```
4. To run the tests for the CLI:
```bash
pnpm --filter=shadcn test
```
This workflow ensures that you are running the most recent version of the registry and testing the CLI properly in your local environment.
## Documentation ## Documentation
The documentation for this project is located in the `www` workspace. You can run the documentation locally by running the following command: The documentation for this project is located in the `www` workspace. You can run the documentation locally by running the following command:

View File

@@ -0,0 +1,273 @@
import { AccordionDemo } from "@/registry/default/block/_sink/components/accordion-demo"
import { AlertDemo } from "@/registry/default/block/_sink/components/alert-demo"
import { AlertDialogDemo } from "@/registry/default/block/_sink/components/alert-dialog-demo"
import { AppSidebar } from "@/registry/default/block/_sink/components/app-sidebar"
import { AspectRatioDemo } from "@/registry/default/block/_sink/components/aspect-ratio-demo"
import { AvatarDemo } from "@/registry/default/block/_sink/components/avatar-demo"
import { BadgeDemo } from "@/registry/default/block/_sink/components/badge-demo"
import { BadgeDestructive } from "@/registry/default/block/_sink/components/badge-destructive"
import { BadgeOutline } from "@/registry/default/block/_sink/components/badge-outline"
import { BadgeSecondary } from "@/registry/default/block/_sink/components/badge-secondary"
import { BreadcrumbDemo } from "@/registry/default/block/_sink/components/breadcrumb-demo"
import { ButtonDemo } from "@/registry/default/block/_sink/components/button-demo"
import { ButtonDestructive } from "@/registry/default/block/_sink/components/button-destructive"
import { ButtonGhost } from "@/registry/default/block/_sink/components/button-ghost"
import { ButtonLink } from "@/registry/default/block/_sink/components/button-link"
import { ButtonLoading } from "@/registry/default/block/_sink/components/button-loading"
import { ButtonOutline } from "@/registry/default/block/_sink/components/button-outline"
import { ButtonSecondary } from "@/registry/default/block/_sink/components/button-secondary"
import { ButtonWithIcon } from "@/registry/default/block/_sink/components/button-with-icon"
import { CalendarDemo } from "@/registry/default/block/_sink/components/calendar-demo"
import { CardDemo } from "@/registry/default/block/_sink/components/card-demo"
import { CarouselDemo } from "@/registry/default/block/_sink/components/carousel-demo"
import { CheckboxDemo } from "@/registry/default/block/_sink/components/checkbox-demo"
import { CollapsibleDemo } from "@/registry/default/block/_sink/components/collapsible-demo"
import { ComboboxDemo } from "@/registry/default/block/_sink/components/combobox-demo"
import { CommandDemo } from "@/registry/default/block/_sink/components/command-demo"
import { ComponentWrapper } from "@/registry/default/block/_sink/components/component-wrapper"
import { ContextMenuDemo } from "@/registry/default/block/_sink/components/context-menu-demo"
import { DatePickerDemo } from "@/registry/default/block/_sink/components/date-picker-demo"
import { DialogDemo } from "@/registry/default/block/_sink/components/dialog-demo"
import { DrawerDemo } from "@/registry/default/block/_sink/components/drawer-demo"
import { DropdownMenuDemo } from "@/registry/default/block/_sink/components/dropdown-menu-demo"
import { HoverCardDemo } from "@/registry/default/block/_sink/components/hover-card-demo"
import { InputDemo } from "@/registry/default/block/_sink/components/input-demo"
import { InputOTPDemo } from "@/registry/default/block/_sink/components/input-otp-demo"
import { LabelDemo } from "@/registry/default/block/_sink/components/label-demo"
import { MenubarDemo } from "@/registry/default/block/_sink/components/menubar-demo"
import { NavigationMenuDemo } from "@/registry/default/block/_sink/components/navigation-menu-demo"
import { PaginationDemo } from "@/registry/default/block/_sink/components/pagination-demo"
import { PopoverDemo } from "@/registry/default/block/_sink/components/popover-demo"
import { ProgressDemo } from "@/registry/default/block/_sink/components/progress-demo"
import { RadioGroupDemo } from "@/registry/default/block/_sink/components/radio-group-demo"
import { ResizableHandleDemo } from "@/registry/default/block/_sink/components/resizable-handle"
import { ScrollAreaDemo } from "@/registry/default/block/_sink/components/scroll-area-demo"
import { SelectDemo } from "@/registry/default/block/_sink/components/select-demo"
import { SeparatorDemo } from "@/registry/default/block/_sink/components/separator-demo"
import { SheetDemo } from "@/registry/default/block/_sink/components/sheet-demo"
import { SkeletonDemo } from "@/registry/default/block/_sink/components/skeleton-demo"
import { SliderDemo } from "@/registry/default/block/_sink/components/slider-demo"
import { SonnerDemo } from "@/registry/default/block/_sink/components/sonner-demo"
import { SwitchDemo } from "@/registry/default/block/_sink/components/switch-demo"
import { TableDemo } from "@/registry/default/block/_sink/components/table-demo"
import { TabsDemo } from "@/registry/default/block/_sink/components/tabs-demo"
import { TextareaDemo } from "@/registry/default/block/_sink/components/textarea-demo"
import { ToastDemo } from "@/registry/default/block/_sink/components/toast-demo"
import { ToggleDemo } from "@/registry/default/block/_sink/components/toggle-demo"
import { ToggleDisabled } from "@/registry/default/block/_sink/components/toggle-disabled"
import { ToggleGroupDemo } from "@/registry/default/block/_sink/components/toggle-group-demo"
import { ToggleOutline } from "@/registry/default/block/_sink/components/toggle-outline"
import { ToggleWithText } from "@/registry/default/block/_sink/components/toggle-with-text"
import { TooltipDemo } from "@/registry/default/block/_sink/components/tooltip-demo"
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator,
} from "@/registry/default/ui/breadcrumb"
import { Separator } from "@/registry/default/ui/separator"
import {
SidebarInset,
SidebarProvider,
SidebarTrigger,
} from "@/registry/default/ui/sidebar"
export default function SinkPage() {
return (
<SidebarProvider>
<AppSidebar />
<SidebarInset>
<header className="flex h-16 shrink-0 items-center gap-2 border-b transition-[width,height] ease-linear group-has-[[data-collapsible=icon]]/sidebar-wrapper:h-12">
<div className="flex items-center gap-2 px-4">
<SidebarTrigger className="-ml-1" />
<Separator orientation="vertical" className="mr-2 h-4" />
<Breadcrumb>
<BreadcrumbList>
<BreadcrumbItem className="hidden md:block">
<BreadcrumbLink href="#">
Building Your Application
</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator className="hidden md:block" />
<BreadcrumbItem>
<BreadcrumbPage>Data Fetching</BreadcrumbPage>
</BreadcrumbItem>
</BreadcrumbList>
</Breadcrumb>
</div>
</header>
<div className="flex flex-1 flex-col gap-4 p-4">
<div className="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
<ComponentWrapper name="Accordion">
<AccordionDemo />
</ComponentWrapper>
<ComponentWrapper name="Alert">
<AlertDemo />
</ComponentWrapper>
<ComponentWrapper name="AlertDialog">
<AlertDialogDemo />
</ComponentWrapper>
<ComponentWrapper name="AspectRatio">
<AspectRatioDemo />
</ComponentWrapper>
<ComponentWrapper name="Avatar">
<AvatarDemo />
</ComponentWrapper>
<ComponentWrapper name="Badge">
<BadgeDemo />
<BadgeDestructive />
<BadgeOutline />
<BadgeSecondary />
</ComponentWrapper>
<ComponentWrapper name="Breadcrumb">
<BreadcrumbDemo />
</ComponentWrapper>
<ComponentWrapper name="Button">
<div className="flex items-center gap-2">
<ButtonDemo />
<ButtonDestructive />
<ButtonGhost />
<ButtonLink />
</div>
<div className="flex items-center gap-2">
<ButtonLoading />
<ButtonOutline />
<ButtonSecondary />
</div>
<div className="flex items-center gap-2">
<ButtonWithIcon />
</div>
</ComponentWrapper>
<ComponentWrapper name="Calendar">
<CalendarDemo />
</ComponentWrapper>
<ComponentWrapper name="Card">
<CardDemo className="w-full" />
</ComponentWrapper>
<ComponentWrapper
name="Carousel"
className="[&_.max-w-xs]:max-w-[70%]"
>
<CarouselDemo />
</ComponentWrapper>
<ComponentWrapper name="Checkbox">
<CheckboxDemo />
</ComponentWrapper>
<ComponentWrapper name="Collapsible">
<CollapsibleDemo />
</ComponentWrapper>
<ComponentWrapper name="Combobox">
<ComboboxDemo />
</ComponentWrapper>
<ComponentWrapper
name="Command"
className="[&_[cmdk-root]]:md:min-w-max"
>
<CommandDemo />
</ComponentWrapper>
<ComponentWrapper name="ContextMenu">
<ContextMenuDemo />
</ComponentWrapper>
<ComponentWrapper name="DatePicker">
<DatePickerDemo />
</ComponentWrapper>
<ComponentWrapper name="Dialog">
<DialogDemo />
</ComponentWrapper>
<ComponentWrapper name="Drawer">
<DrawerDemo />
</ComponentWrapper>
<ComponentWrapper name="DropdownMenu">
<DropdownMenuDemo />
</ComponentWrapper>
<ComponentWrapper name="HoverCard">
<HoverCardDemo />
</ComponentWrapper>
<ComponentWrapper name="Input">
<InputDemo />
</ComponentWrapper>
<ComponentWrapper name="InputOTP">
<InputOTPDemo />
</ComponentWrapper>
<ComponentWrapper name="Label">
<LabelDemo />
</ComponentWrapper>
<ComponentWrapper name="Menubar">
<MenubarDemo />
</ComponentWrapper>
<ComponentWrapper name="NavigationMenu" className="col-span-2">
<NavigationMenuDemo />
</ComponentWrapper>
<ComponentWrapper name="Pagination">
<PaginationDemo />
</ComponentWrapper>
<ComponentWrapper name="Popover">
<PopoverDemo />
</ComponentWrapper>
<ComponentWrapper name="Progress">
<ProgressDemo />
</ComponentWrapper>
<ComponentWrapper name="RadioGroup">
<RadioGroupDemo />
</ComponentWrapper>
<ComponentWrapper name="Resizable" className="col-span-2">
<ResizableHandleDemo />
</ComponentWrapper>
<ComponentWrapper name="ScrollArea">
<ScrollAreaDemo />
</ComponentWrapper>
<ComponentWrapper name="Select">
<SelectDemo />
</ComponentWrapper>
<ComponentWrapper name="Separator">
<SeparatorDemo />
</ComponentWrapper>
<ComponentWrapper name="Sheet">
<SheetDemo />
</ComponentWrapper>
<ComponentWrapper name="Skeleton">
<SkeletonDemo />
</ComponentWrapper>
<ComponentWrapper name="Slider">
<SliderDemo />
</ComponentWrapper>
<ComponentWrapper name="Sonner">
<SonnerDemo />
</ComponentWrapper>
<ComponentWrapper name="Switch">
<SwitchDemo />
</ComponentWrapper>
<ComponentWrapper name="Table" className="col-span-2">
<TableDemo />
</ComponentWrapper>
<ComponentWrapper name="Tabs">
<TabsDemo />
</ComponentWrapper>
<ComponentWrapper name="Textarea">
<TextareaDemo />
</ComponentWrapper>
<ComponentWrapper name="Toast">
<ToastDemo />
</ComponentWrapper>
<ComponentWrapper name="Toggle">
<div className="flex items-center gap-2">
<ToggleDemo />
<ToggleDisabled />
<ToggleOutline />
<ToggleWithText />
</div>
</ComponentWrapper>
<ComponentWrapper name="ToggleGroup">
<ToggleGroupDemo />
</ComponentWrapper>
<ComponentWrapper name="Tooltip">
<TooltipDemo />
</ComponentWrapper>
</div>
</div>
</SidebarInset>
</SidebarProvider>
)
}

View File

@@ -0,0 +1,451 @@
// @ts-nocheck
// This file is autogenerated by scripts/build-registry.ts
// Do not edit this file directly.
import * as React from "react"
export const Icons = {
AlertCircle: {
lucide: React.lazy(() =>
import("lucide-react").then((mod) => ({
default: mod.AlertCircle,
}))
),
radix: React.lazy(() =>
import("@radix-ui/react-icons").then((mod) => ({
default: mod.ExclamationTriangleIcon,
}))
),
},
ArrowLeft: {
lucide: React.lazy(() =>
import("lucide-react").then((mod) => ({
default: mod.ArrowLeft,
}))
),
radix: React.lazy(() =>
import("@radix-ui/react-icons").then((mod) => ({
default: mod.ArrowLeftIcon,
}))
),
},
ArrowRight: {
lucide: React.lazy(() =>
import("lucide-react").then((mod) => ({
default: mod.ArrowRight,
}))
),
radix: React.lazy(() =>
import("@radix-ui/react-icons").then((mod) => ({
default: mod.ArrowRightIcon,
}))
),
},
ArrowUpDown: {
lucide: React.lazy(() =>
import("lucide-react").then((mod) => ({
default: mod.ArrowUpDown,
}))
),
radix: React.lazy(() =>
import("@radix-ui/react-icons").then((mod) => ({
default: mod.CaretSortIcon,
}))
),
},
BellRing: {
lucide: React.lazy(() =>
import("lucide-react").then((mod) => ({
default: mod.BellRing,
}))
),
radix: React.lazy(() =>
import("@radix-ui/react-icons").then((mod) => ({
default: mod.BellIcon,
}))
),
},
Bold: {
lucide: React.lazy(() =>
import("lucide-react").then((mod) => ({
default: mod.Bold,
}))
),
radix: React.lazy(() =>
import("@radix-ui/react-icons").then((mod) => ({
default: mod.FontBoldIcon,
}))
),
},
Calculator: {
lucide: React.lazy(() =>
import("lucide-react").then((mod) => ({
default: mod.Calculator,
}))
),
radix: React.lazy(() =>
import("@radix-ui/react-icons").then((mod) => ({
default: mod.ComponentPlaceholderIcon,
}))
),
},
Calendar: {
lucide: React.lazy(() =>
import("lucide-react").then((mod) => ({
default: mod.Calendar,
}))
),
radix: React.lazy(() =>
import("@radix-ui/react-icons").then((mod) => ({
default: mod.CalendarIcon,
}))
),
},
Check: {
lucide: React.lazy(() =>
import("lucide-react").then((mod) => ({
default: mod.Check,
}))
),
radix: React.lazy(() =>
import("@radix-ui/react-icons").then((mod) => ({
default: mod.CheckIcon,
}))
),
},
ChevronDown: {
lucide: React.lazy(() =>
import("lucide-react").then((mod) => ({
default: mod.ChevronDown,
}))
),
radix: React.lazy(() =>
import("@radix-ui/react-icons").then((mod) => ({
default: mod.ChevronDownIcon,
}))
),
},
ChevronLeft: {
lucide: React.lazy(() =>
import("lucide-react").then((mod) => ({
default: mod.ChevronLeft,
}))
),
radix: React.lazy(() =>
import("@radix-ui/react-icons").then((mod) => ({
default: mod.ChevronLeftIcon,
}))
),
},
ChevronRight: {
lucide: React.lazy(() =>
import("lucide-react").then((mod) => ({
default: mod.ChevronRight,
}))
),
radix: React.lazy(() =>
import("@radix-ui/react-icons").then((mod) => ({
default: mod.ChevronRightIcon,
}))
),
},
ChevronUp: {
lucide: React.lazy(() =>
import("lucide-react").then((mod) => ({
default: mod.ChevronUp,
}))
),
radix: React.lazy(() =>
import("@radix-ui/react-icons").then((mod) => ({
default: mod.ChevronUpIcon,
}))
),
},
ChevronsUpDown: {
lucide: React.lazy(() =>
import("lucide-react").then((mod) => ({
default: mod.ChevronsUpDown,
}))
),
radix: React.lazy(() =>
import("@radix-ui/react-icons").then((mod) => ({
default: mod.CaretSortIcon,
}))
),
},
Circle: {
lucide: React.lazy(() =>
import("lucide-react").then((mod) => ({
default: mod.Circle,
}))
),
radix: React.lazy(() =>
import("@radix-ui/react-icons").then((mod) => ({
default: mod.DotFilledIcon,
}))
),
},
Copy: {
lucide: React.lazy(() =>
import("lucide-react").then((mod) => ({
default: mod.Copy,
}))
),
radix: React.lazy(() =>
import("@radix-ui/react-icons").then((mod) => ({
default: mod.CopyIcon,
}))
),
},
CreditCard: {
lucide: React.lazy(() =>
import("lucide-react").then((mod) => ({
default: mod.CreditCard,
}))
),
radix: React.lazy(() =>
import("@radix-ui/react-icons").then((mod) => ({
default: mod.ComponentPlaceholderIcon,
}))
),
},
GripVertical: {
lucide: React.lazy(() =>
import("lucide-react").then((mod) => ({
default: mod.GripVertical,
}))
),
radix: React.lazy(() =>
import("@radix-ui/react-icons").then((mod) => ({
default: mod.DragHandleDots2Icon,
}))
),
},
Italic: {
lucide: React.lazy(() =>
import("lucide-react").then((mod) => ({
default: mod.Italic,
}))
),
radix: React.lazy(() =>
import("@radix-ui/react-icons").then((mod) => ({
default: mod.FontItalicIcon,
}))
),
},
Loader2: {
lucide: React.lazy(() =>
import("lucide-react").then((mod) => ({
default: mod.Loader2,
}))
),
radix: React.lazy(() =>
import("@radix-ui/react-icons").then((mod) => ({
default: mod.ReloadIcon,
}))
),
},
Mail: {
lucide: React.lazy(() =>
import("lucide-react").then((mod) => ({
default: mod.Mail,
}))
),
radix: React.lazy(() =>
import("@radix-ui/react-icons").then((mod) => ({
default: mod.EnvelopeClosedIcon,
}))
),
},
MailOpen: {
lucide: React.lazy(() =>
import("lucide-react").then((mod) => ({
default: mod.MailOpen,
}))
),
radix: React.lazy(() =>
import("@radix-ui/react-icons").then((mod) => ({
default: mod.EnvelopeOpenIcon,
}))
),
},
Minus: {
lucide: React.lazy(() =>
import("lucide-react").then((mod) => ({
default: mod.Minus,
}))
),
radix: React.lazy(() =>
import("@radix-ui/react-icons").then((mod) => ({
default: mod.MinusIcon,
}))
),
},
Moon: {
lucide: React.lazy(() =>
import("lucide-react").then((mod) => ({
default: mod.Moon,
}))
),
radix: React.lazy(() =>
import("@radix-ui/react-icons").then((mod) => ({
default: mod.MoonIcon,
}))
),
},
MoreHorizontal: {
lucide: React.lazy(() =>
import("lucide-react").then((mod) => ({
default: mod.MoreHorizontal,
}))
),
radix: React.lazy(() =>
import("@radix-ui/react-icons").then((mod) => ({
default: mod.DotsHorizontalIcon,
}))
),
},
PanelLeft: {
lucide: React.lazy(() =>
import("lucide-react").then((mod) => ({
default: mod.PanelLeft,
}))
),
radix: React.lazy(() =>
import("@radix-ui/react-icons").then((mod) => ({
default: mod.ViewVerticalIcon,
}))
),
},
Plus: {
lucide: React.lazy(() =>
import("lucide-react").then((mod) => ({
default: mod.Plus,
}))
),
radix: React.lazy(() =>
import("@radix-ui/react-icons").then((mod) => ({
default: mod.PlusIcon,
}))
),
},
Search: {
lucide: React.lazy(() =>
import("lucide-react").then((mod) => ({
default: mod.Search,
}))
),
radix: React.lazy(() =>
import("@radix-ui/react-icons").then((mod) => ({
default: mod.MagnifyingGlassIcon,
}))
),
},
Send: {
lucide: React.lazy(() =>
import("lucide-react").then((mod) => ({
default: mod.Send,
}))
),
radix: React.lazy(() =>
import("@radix-ui/react-icons").then((mod) => ({
default: mod.PaperPlaneIcon,
}))
),
},
Settings: {
lucide: React.lazy(() =>
import("lucide-react").then((mod) => ({
default: mod.Settings,
}))
),
radix: React.lazy(() =>
import("@radix-ui/react-icons").then((mod) => ({
default: mod.GearIcon,
}))
),
},
Slash: {
lucide: React.lazy(() =>
import("lucide-react").then((mod) => ({
default: mod.Slash,
}))
),
radix: React.lazy(() =>
import("@radix-ui/react-icons").then((mod) => ({
default: mod.SlashIcon,
}))
),
},
Smile: {
lucide: React.lazy(() =>
import("lucide-react").then((mod) => ({
default: mod.Smile,
}))
),
radix: React.lazy(() =>
import("@radix-ui/react-icons").then((mod) => ({
default: mod.FaceIcon,
}))
),
},
Sun: {
lucide: React.lazy(() =>
import("lucide-react").then((mod) => ({
default: mod.Sun,
}))
),
radix: React.lazy(() =>
import("@radix-ui/react-icons").then((mod) => ({
default: mod.SunIcon,
}))
),
},
Terminal: {
lucide: React.lazy(() =>
import("lucide-react").then((mod) => ({
default: mod.Terminal,
}))
),
radix: React.lazy(() =>
import("@radix-ui/react-icons").then((mod) => ({
default: mod.RocketIcon,
}))
),
},
Underline: {
lucide: React.lazy(() =>
import("lucide-react").then((mod) => ({
default: mod.Underline,
}))
),
radix: React.lazy(() =>
import("@radix-ui/react-icons").then((mod) => ({
default: mod.UnderlineIcon,
}))
),
},
User: {
lucide: React.lazy(() =>
import("lucide-react").then((mod) => ({
default: mod.User,
}))
),
radix: React.lazy(() =>
import("@radix-ui/react-icons").then((mod) => ({
default: mod.PersonIcon,
}))
),
},
X: {
lucide: React.lazy(() =>
import("lucide-react").then((mod) => ({
default: mod.X,
}))
),
radix: React.lazy(() =>
import("@radix-ui/react-icons").then((mod) => ({
default: mod.Cross2Icon,
}))
),
},
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,273 @@
import { AccordionDemo } from "@/registry/new-york/block/_sink/components/accordion-demo"
import { AlertDemo } from "@/registry/new-york/block/_sink/components/alert-demo"
import { AlertDialogDemo } from "@/registry/new-york/block/_sink/components/alert-dialog-demo"
import { AppSidebar } from "@/registry/new-york/block/_sink/components/app-sidebar"
import { AspectRatioDemo } from "@/registry/new-york/block/_sink/components/aspect-ratio-demo"
import { AvatarDemo } from "@/registry/new-york/block/_sink/components/avatar-demo"
import { BadgeDemo } from "@/registry/new-york/block/_sink/components/badge-demo"
import { BadgeDestructive } from "@/registry/new-york/block/_sink/components/badge-destructive"
import { BadgeOutline } from "@/registry/new-york/block/_sink/components/badge-outline"
import { BadgeSecondary } from "@/registry/new-york/block/_sink/components/badge-secondary"
import { BreadcrumbDemo } from "@/registry/new-york/block/_sink/components/breadcrumb-demo"
import { ButtonDemo } from "@/registry/new-york/block/_sink/components/button-demo"
import { ButtonDestructive } from "@/registry/new-york/block/_sink/components/button-destructive"
import { ButtonGhost } from "@/registry/new-york/block/_sink/components/button-ghost"
import { ButtonLink } from "@/registry/new-york/block/_sink/components/button-link"
import { ButtonLoading } from "@/registry/new-york/block/_sink/components/button-loading"
import { ButtonOutline } from "@/registry/new-york/block/_sink/components/button-outline"
import { ButtonSecondary } from "@/registry/new-york/block/_sink/components/button-secondary"
import { ButtonWithIcon } from "@/registry/new-york/block/_sink/components/button-with-icon"
import { CalendarDemo } from "@/registry/new-york/block/_sink/components/calendar-demo"
import { CardDemo } from "@/registry/new-york/block/_sink/components/card-demo"
import { CarouselDemo } from "@/registry/new-york/block/_sink/components/carousel-demo"
import { CheckboxDemo } from "@/registry/new-york/block/_sink/components/checkbox-demo"
import { CollapsibleDemo } from "@/registry/new-york/block/_sink/components/collapsible-demo"
import { ComboboxDemo } from "@/registry/new-york/block/_sink/components/combobox-demo"
import { CommandDemo } from "@/registry/new-york/block/_sink/components/command-demo"
import { ComponentWrapper } from "@/registry/new-york/block/_sink/components/component-wrapper"
import { ContextMenuDemo } from "@/registry/new-york/block/_sink/components/context-menu-demo"
import { DatePickerDemo } from "@/registry/new-york/block/_sink/components/date-picker-demo"
import { DialogDemo } from "@/registry/new-york/block/_sink/components/dialog-demo"
import { DrawerDemo } from "@/registry/new-york/block/_sink/components/drawer-demo"
import { DropdownMenuDemo } from "@/registry/new-york/block/_sink/components/dropdown-menu-demo"
import { HoverCardDemo } from "@/registry/new-york/block/_sink/components/hover-card-demo"
import { InputDemo } from "@/registry/new-york/block/_sink/components/input-demo"
import { InputOTPDemo } from "@/registry/new-york/block/_sink/components/input-otp-demo"
import { LabelDemo } from "@/registry/new-york/block/_sink/components/label-demo"
import { MenubarDemo } from "@/registry/new-york/block/_sink/components/menubar-demo"
import { NavigationMenuDemo } from "@/registry/new-york/block/_sink/components/navigation-menu-demo"
import { PaginationDemo } from "@/registry/new-york/block/_sink/components/pagination-demo"
import { PopoverDemo } from "@/registry/new-york/block/_sink/components/popover-demo"
import { ProgressDemo } from "@/registry/new-york/block/_sink/components/progress-demo"
import { RadioGroupDemo } from "@/registry/new-york/block/_sink/components/radio-group-demo"
import { ResizableHandleDemo } from "@/registry/new-york/block/_sink/components/resizable-handle"
import { ScrollAreaDemo } from "@/registry/new-york/block/_sink/components/scroll-area-demo"
import { SelectDemo } from "@/registry/new-york/block/_sink/components/select-demo"
import { SeparatorDemo } from "@/registry/new-york/block/_sink/components/separator-demo"
import { SheetDemo } from "@/registry/new-york/block/_sink/components/sheet-demo"
import { SkeletonDemo } from "@/registry/new-york/block/_sink/components/skeleton-demo"
import { SliderDemo } from "@/registry/new-york/block/_sink/components/slider-demo"
import { SonnerDemo } from "@/registry/new-york/block/_sink/components/sonner-demo"
import { SwitchDemo } from "@/registry/new-york/block/_sink/components/switch-demo"
import { TableDemo } from "@/registry/new-york/block/_sink/components/table-demo"
import { TabsDemo } from "@/registry/new-york/block/_sink/components/tabs-demo"
import { TextareaDemo } from "@/registry/new-york/block/_sink/components/textarea-demo"
import { ToastDemo } from "@/registry/new-york/block/_sink/components/toast-demo"
import { ToggleDemo } from "@/registry/new-york/block/_sink/components/toggle-demo"
import { ToggleDisabled } from "@/registry/new-york/block/_sink/components/toggle-disabled"
import { ToggleGroupDemo } from "@/registry/new-york/block/_sink/components/toggle-group-demo"
import { ToggleOutline } from "@/registry/new-york/block/_sink/components/toggle-outline"
import { ToggleWithText } from "@/registry/new-york/block/_sink/components/toggle-with-text"
import { TooltipDemo } from "@/registry/new-york/block/_sink/components/tooltip-demo"
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator,
} from "@/registry/new-york/ui/breadcrumb"
import { Separator } from "@/registry/new-york/ui/separator"
import {
SidebarInset,
SidebarProvider,
SidebarTrigger,
} from "@/registry/new-york/ui/sidebar"
export default function SinkPage() {
return (
<SidebarProvider>
<AppSidebar />
<SidebarInset>
<header className="flex h-16 shrink-0 items-center gap-2 border-b transition-[width,height] ease-linear group-has-[[data-collapsible=icon]]/sidebar-wrapper:h-12">
<div className="flex items-center gap-2 px-4">
<SidebarTrigger className="-ml-1" />
<Separator orientation="vertical" className="mr-2 h-4" />
<Breadcrumb>
<BreadcrumbList>
<BreadcrumbItem className="hidden md:block">
<BreadcrumbLink href="#">
Building Your Application
</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator className="hidden md:block" />
<BreadcrumbItem>
<BreadcrumbPage>Data Fetching</BreadcrumbPage>
</BreadcrumbItem>
</BreadcrumbList>
</Breadcrumb>
</div>
</header>
<div className="flex flex-1 flex-col gap-4 p-4">
<div className="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
<ComponentWrapper name="Accordion">
<AccordionDemo />
</ComponentWrapper>
<ComponentWrapper name="Alert">
<AlertDemo />
</ComponentWrapper>
<ComponentWrapper name="AlertDialog">
<AlertDialogDemo />
</ComponentWrapper>
<ComponentWrapper name="AspectRatio">
<AspectRatioDemo />
</ComponentWrapper>
<ComponentWrapper name="Avatar">
<AvatarDemo />
</ComponentWrapper>
<ComponentWrapper name="Badge">
<BadgeDemo />
<BadgeDestructive />
<BadgeOutline />
<BadgeSecondary />
</ComponentWrapper>
<ComponentWrapper name="Breadcrumb">
<BreadcrumbDemo />
</ComponentWrapper>
<ComponentWrapper name="Button">
<div className="flex items-center gap-2">
<ButtonDemo />
<ButtonDestructive />
<ButtonGhost />
<ButtonLink />
</div>
<div className="flex items-center gap-2">
<ButtonLoading />
<ButtonOutline />
<ButtonSecondary />
</div>
<div className="flex items-center gap-2">
<ButtonWithIcon />
</div>
</ComponentWrapper>
<ComponentWrapper name="Calendar">
<CalendarDemo />
</ComponentWrapper>
<ComponentWrapper name="Card">
<CardDemo className="w-full" />
</ComponentWrapper>
<ComponentWrapper
name="Carousel"
className="[&_.max-w-xs]:max-w-[70%]"
>
<CarouselDemo />
</ComponentWrapper>
<ComponentWrapper name="Checkbox">
<CheckboxDemo />
</ComponentWrapper>
<ComponentWrapper name="Collapsible">
<CollapsibleDemo />
</ComponentWrapper>
<ComponentWrapper name="Combobox">
<ComboboxDemo />
</ComponentWrapper>
<ComponentWrapper
name="Command"
className="[&_[cmdk-root]]:md:min-w-max"
>
<CommandDemo />
</ComponentWrapper>
<ComponentWrapper name="ContextMenu">
<ContextMenuDemo />
</ComponentWrapper>
<ComponentWrapper name="DatePicker">
<DatePickerDemo />
</ComponentWrapper>
<ComponentWrapper name="Dialog">
<DialogDemo />
</ComponentWrapper>
<ComponentWrapper name="Drawer">
<DrawerDemo />
</ComponentWrapper>
<ComponentWrapper name="DropdownMenu">
<DropdownMenuDemo />
</ComponentWrapper>
<ComponentWrapper name="HoverCard">
<HoverCardDemo />
</ComponentWrapper>
<ComponentWrapper name="Input">
<InputDemo />
</ComponentWrapper>
<ComponentWrapper name="InputOTP">
<InputOTPDemo />
</ComponentWrapper>
<ComponentWrapper name="Label">
<LabelDemo />
</ComponentWrapper>
<ComponentWrapper name="Menubar">
<MenubarDemo />
</ComponentWrapper>
<ComponentWrapper name="NavigationMenu" className="col-span-2">
<NavigationMenuDemo />
</ComponentWrapper>
<ComponentWrapper name="Pagination">
<PaginationDemo />
</ComponentWrapper>
<ComponentWrapper name="Popover">
<PopoverDemo />
</ComponentWrapper>
<ComponentWrapper name="Progress">
<ProgressDemo />
</ComponentWrapper>
<ComponentWrapper name="RadioGroup">
<RadioGroupDemo />
</ComponentWrapper>
<ComponentWrapper name="Resizable" className="col-span-2">
<ResizableHandleDemo />
</ComponentWrapper>
<ComponentWrapper name="ScrollArea">
<ScrollAreaDemo />
</ComponentWrapper>
<ComponentWrapper name="Select">
<SelectDemo />
</ComponentWrapper>
<ComponentWrapper name="Separator">
<SeparatorDemo />
</ComponentWrapper>
<ComponentWrapper name="Sheet">
<SheetDemo />
</ComponentWrapper>
<ComponentWrapper name="Skeleton">
<SkeletonDemo />
</ComponentWrapper>
<ComponentWrapper name="Slider">
<SliderDemo />
</ComponentWrapper>
<ComponentWrapper name="Sonner">
<SonnerDemo />
</ComponentWrapper>
<ComponentWrapper name="Switch">
<SwitchDemo />
</ComponentWrapper>
<ComponentWrapper name="Table" className="col-span-2">
<TableDemo />
</ComponentWrapper>
<ComponentWrapper name="Tabs">
<TabsDemo />
</ComponentWrapper>
<ComponentWrapper name="Textarea">
<TextareaDemo />
</ComponentWrapper>
<ComponentWrapper name="Toast">
<ToastDemo />
</ComponentWrapper>
<ComponentWrapper name="Toggle">
<div className="flex items-center gap-2">
<ToggleDemo />
<ToggleDisabled />
<ToggleOutline />
<ToggleWithText />
</div>
</ComponentWrapper>
<ComponentWrapper name="ToggleGroup">
<ToggleGroupDemo />
</ComponentWrapper>
<ComponentWrapper name="Tooltip">
<TooltipDemo />
</ComponentWrapper>
</div>
</div>
</SidebarInset>
</SidebarProvider>
)
}

View File

@@ -141,15 +141,16 @@ export default function Component() {
const filteredData = chartData.filter((item) => { const filteredData = chartData.filter((item) => {
const date = new Date(item.date) const date = new Date(item.date)
const now = new Date() const referenceDate = new Date("2024-06-30")
let daysToSubtract = 90 let daysToSubtract = 90
if (timeRange === "30d") { if (timeRange === "30d") {
daysToSubtract = 30 daysToSubtract = 30
} else if (timeRange === "7d") { } else if (timeRange === "7d") {
daysToSubtract = 7 daysToSubtract = 7
} }
now.setDate(now.getDate() - daysToSubtract) const startDate = new Date(referenceDate)
return date >= now startDate.setDate(startDate.getDate() - daysToSubtract)
return date >= startDate
}) })
return ( return (

View File

@@ -21,7 +21,7 @@ export default function BlocksLayout({
children: React.ReactNode children: React.ReactNode
}) { }) {
return ( return (
<div className="container relative"> <div className="relative">
<PageHeader> <PageHeader>
<Announcement /> <Announcement />
<PageHeaderHeading>Building Blocks for the Web</PageHeaderHeading> <PageHeaderHeading>Building Blocks for the Web</PageHeaderHeading>
@@ -42,9 +42,11 @@ export default function BlocksLayout({
</Button> </Button>
</PageActions> </PageActions>
</PageHeader> </PageHeader>
<section id="blocks" className="scroll-mt-24"> <div className="container py-6">
{children} <section id="blocks" className="scroll-mt-24">
</section> {children}
</section>
</div>
</div> </div>
) )
} }

View File

@@ -1,27 +1,18 @@
import * as React from "react" import * as React from "react"
import { unstable_cache } from "next/cache"
import { getAllBlockIds } from "@/lib/blocks" import { getAllBlockIds } from "@/lib/blocks"
import { BlockDisplay } from "@/components/block-display" import { BlockDisplay } from "@/components/block-display"
const BLOCKS_WHITELIST_PREFIXES = ["sidebar", "login"] import "@/styles/mdx.css"
const getBlocks = unstable_cache(async () => {
return (await getAllBlockIds()).filter((name) =>
BLOCKS_WHITELIST_PREFIXES.some((prefix) => name.startsWith(prefix))
)
}, ["blocks"])
export default async function BlocksPage() { export default async function BlocksPage() {
const blocks = await getBlocks() const blocks = await getAllBlockIds()
return ( return (
<div className="gap-3 md:flex md:flex-row-reverse md:items-start"> <div className="gap-3 md:flex md:flex-row-reverse md:items-start">
<div className="grid flex-1 gap-12 md:gap-24 lg:gap-48"> <div className="grid flex-1 gap-12 md:gap-24 lg:gap-48">
{blocks.map((name, index) => ( {blocks.map((name) => (
<React.Suspense key={`${name}-${index}`}> <BlockDisplay key={name} name={name} />
<BlockDisplay name={name} />
</React.Suspense>
))} ))}
</div> </div>
</div> </div>

View File

@@ -22,7 +22,7 @@ export default function ChartsLayout({
children: React.ReactNode children: React.ReactNode
}) { }) {
return ( return (
<div className="container relative"> <div className="relative">
<PageHeader> <PageHeader>
<Announcement /> <Announcement />
<PageHeaderHeading>Beautiful Charts</PageHeaderHeading> <PageHeaderHeading>Beautiful Charts</PageHeaderHeading>
@@ -38,9 +38,11 @@ export default function ChartsLayout({
</Button> </Button>
</PageActions> </PageActions>
</PageHeader> </PageHeader>
<section id="charts" className="scroll-mt-20"> <div className="container py-6">
{children} <section id="charts" className="scroll-mt-20">
</section> {children}
</section>
</div>
</div> </div>
) )
} }

View File

@@ -15,13 +15,13 @@ export const metadata: Metadata = {
description: "All colors in all formats.", description: "All colors in all formats.",
} }
export default function ChartsLayout({ export default function ColorsLayout({
children, children,
}: { }: {
children: React.ReactNode children: React.ReactNode
}) { }) {
return ( return (
<div className="container relative"> <div className="relative">
<PageHeader> <PageHeader>
<Announcement /> <Announcement />
<PageHeaderHeading>Tailwind Colors</PageHeaderHeading> <PageHeaderHeading>Tailwind Colors</PageHeaderHeading>
@@ -37,9 +37,11 @@ export default function ChartsLayout({
</Button> </Button>
</PageActions> </PageActions>
</PageHeader> </PageHeader>
<section id="charts" className="scroll-mt-20"> <div className="container py-6">
{children} <section id="colors" className="scroll-mt-20">
</section> {children}
</section>
</div>
</div> </div>
) )
} }

View File

@@ -4,7 +4,7 @@ import { allDocs } from "contentlayer/generated"
import "@/styles/mdx.css" import "@/styles/mdx.css"
import type { Metadata } from "next" import type { Metadata } from "next"
import Link from "next/link" import Link from "next/link"
import { ChevronRightIcon, ExternalLinkIcon } from "@radix-ui/react-icons" import { ChevronRight, ExternalLink } from "lucide-react"
import Balancer from "react-wrap-balancer" import Balancer from "react-wrap-balancer"
import { siteConfig } from "@/config/site" import { siteConfig } from "@/config/site"
@@ -15,7 +15,6 @@ import { OpenInV0Cta } from "@/components/open-in-v0-cta"
import { DocsPager } from "@/components/pager" import { DocsPager } from "@/components/pager"
import { DashboardTableOfContents } from "@/components/toc" import { DashboardTableOfContents } from "@/components/toc"
import { badgeVariants } from "@/registry/new-york/ui/badge" import { badgeVariants } from "@/registry/new-york/ui/badge"
import { ScrollArea } from "@/registry/new-york/ui/scroll-area"
interface DocPageProps { interface DocPageProps {
params: { params: {
@@ -89,10 +88,10 @@ export default async function DocPage({ params }: DocPageProps) {
return ( return (
<main className="relative py-6 lg:gap-10 lg:py-8 xl:grid xl:grid-cols-[1fr_300px]"> <main className="relative py-6 lg:gap-10 lg:py-8 xl:grid xl:grid-cols-[1fr_300px]">
<div className="mx-auto w-full min-w-0"> <div className="mx-auto w-full min-w-0 max-w-3xl">
<div className="mb-4 flex items-center space-x-1 text-sm leading-none text-muted-foreground"> <div className="mb-4 flex items-center space-x-1 text-sm leading-none text-muted-foreground">
<div className="truncate">Docs</div> <div className="truncate">Docs</div>
<ChevronRightIcon className="h-3.5 w-3.5" /> <ChevronRight className="h-3.5 w-3.5" />
<div className="text-foreground">{doc.title}</div> <div className="text-foreground">{doc.title}</div>
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
@@ -115,7 +114,7 @@ export default async function DocPage({ params }: DocPageProps) {
className={cn(badgeVariants({ variant: "secondary" }), "gap-1")} className={cn(badgeVariants({ variant: "secondary" }), "gap-1")}
> >
Docs Docs
<ExternalLinkIcon className="h-3 w-3" /> <ExternalLink className="h-3 w-3" />
</Link> </Link>
)} )}
{doc.links?.api && ( {doc.links?.api && (
@@ -126,7 +125,7 @@ export default async function DocPage({ params }: DocPageProps) {
className={cn(badgeVariants({ variant: "secondary" }), "gap-1")} className={cn(badgeVariants({ variant: "secondary" }), "gap-1")}
> >
API Reference API Reference
<ExternalLinkIcon className="h-3 w-3" /> <ExternalLink className="h-3 w-3" />
</Link> </Link>
)} )}
</div> </div>
@@ -137,11 +136,11 @@ export default async function DocPage({ params }: DocPageProps) {
<DocsPager doc={doc} /> <DocsPager doc={doc} />
</div> </div>
<div className="hidden text-sm xl:block"> <div className="hidden text-sm xl:block">
<div className="sticky top-16 -mt-10 h-[calc(100vh-3.5rem)] pt-4"> <div className="sticky top-20 -mt-6 h-[calc(100vh-3.5rem)] pt-4">
<ScrollArea className="h-full pb-10"> <div className="no-scrollbar h-full overflow-auto pb-10">
{doc.toc && <DashboardTableOfContents toc={toc} />} {doc.toc && <DashboardTableOfContents toc={toc} />}
<OpenInV0Cta className="mt-6 max-w-[80%]" /> <OpenInV0Cta className="mt-6 max-w-[80%]" />
</ScrollArea> </div>
</div> </div>
</div> </div>
</main> </main>

View File

@@ -8,15 +8,13 @@ interface DocsLayoutProps {
export default function DocsLayout({ children }: DocsLayoutProps) { export default function DocsLayout({ children }: DocsLayoutProps) {
return ( return (
<div className="border-b"> <div className="container flex-1 items-start md:grid md:grid-cols-[220px_minmax(0,1fr)] md:gap-6 lg:grid-cols-[240px_minmax(0,1fr)] lg:gap-10">
<div className="container flex-1 items-start md:grid md:grid-cols-[220px_minmax(0,1fr)] md:gap-6 lg:grid-cols-[240px_minmax(0,1fr)] lg:gap-10"> <aside className="fixed top-14 z-30 hidden h-[calc(100vh-3.5rem)] w-full shrink-0 border-r border-border/40 dark:border-border md:sticky md:block">
<aside className="fixed top-14 z-30 -ml-2 hidden h-[calc(100vh-3.5rem)] w-full shrink-0 md:sticky md:block"> <div className="no-scrollbar h-full overflow-auto py-6 pr-6 lg:py-8">
<ScrollArea className="h-full py-6 pr-6 lg:py-8"> <DocsSidebarNav config={docsConfig} />
<DocsSidebarNav config={docsConfig} /> </div>
</ScrollArea> </aside>
</aside> {children}
{children}
</div>
</div> </div>
) )
} }

View File

@@ -1,9 +1,4 @@
import { import { ChevronDown, Circle, Plus, Star } from "lucide-react"
ChevronDownIcon,
CircleIcon,
PlusIcon,
StarIcon,
} from "@radix-ui/react-icons"
import { Button } from "@/registry/new-york/ui/button" import { Button } from "@/registry/new-york/ui/button"
import { import {
@@ -37,14 +32,14 @@ export function DemoGithub() {
</div> </div>
<div className="flex items-center space-x-1 rounded-md bg-secondary text-secondary-foreground"> <div className="flex items-center space-x-1 rounded-md bg-secondary text-secondary-foreground">
<Button variant="secondary" className="px-3 shadow-none"> <Button variant="secondary" className="px-3 shadow-none">
<StarIcon className="mr-2 h-4 w-4" /> <Star />
Star Star
</Button> </Button>
<Separator orientation="vertical" className="h-[20px]" /> <Separator orientation="vertical" className="h-[20px]" />
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger asChild> <DropdownMenuTrigger asChild>
<Button variant="secondary" className="px-2 shadow-none"> <Button variant="secondary" className="px-2 shadow-none">
<ChevronDownIcon className="h-4 w-4 text-secondary-foreground" /> <ChevronDown className="text-secondary-foreground" />
</Button> </Button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent <DropdownMenuContent
@@ -62,7 +57,7 @@ export function DemoGithub() {
<DropdownMenuCheckboxItem>Inspiration</DropdownMenuCheckboxItem> <DropdownMenuCheckboxItem>Inspiration</DropdownMenuCheckboxItem>
<DropdownMenuSeparator /> <DropdownMenuSeparator />
<DropdownMenuItem> <DropdownMenuItem>
<PlusIcon className="mr-2 h-4 w-4" /> Create List <Plus /> Create List
</DropdownMenuItem> </DropdownMenuItem>
</DropdownMenuContent> </DropdownMenuContent>
</DropdownMenu> </DropdownMenu>
@@ -71,11 +66,11 @@ export function DemoGithub() {
<CardContent> <CardContent>
<div className="flex space-x-4 text-sm text-muted-foreground"> <div className="flex space-x-4 text-sm text-muted-foreground">
<div className="flex items-center"> <div className="flex items-center">
<CircleIcon className="mr-1 h-3 w-3 fill-sky-400 text-sky-400" /> <Circle className="mr-1 h-3 w-3 fill-sky-400 text-sky-400" />
TypeScript TypeScript
</div> </div>
<div className="flex items-center"> <div className="flex items-center">
<StarIcon className="mr-1 h-3 w-3" /> <Star className="mr-1 h-3 w-3" />
20k 20k
</div> </div>
<div>Updated April 2023</div> <div>Updated April 2023</div>

View File

@@ -1,4 +1,4 @@
import { BellIcon, EyeNoneIcon, PersonIcon } from "@radix-ui/react-icons" import { Bell, EyeOff, User } from "lucide-react"
import { import {
Card, Card,
@@ -19,7 +19,7 @@ export function DemoNotifications() {
</CardHeader> </CardHeader>
<CardContent className="grid gap-1"> <CardContent className="grid gap-1">
<div className="-mx-2 flex items-start space-x-4 rounded-md p-2 transition-all hover:bg-accent hover:text-accent-foreground"> <div className="-mx-2 flex items-start space-x-4 rounded-md p-2 transition-all hover:bg-accent hover:text-accent-foreground">
<BellIcon className="mt-px h-5 w-5" /> <Bell className="mt-px h-5 w-5" />
<div className="space-y-1"> <div className="space-y-1">
<p className="text-sm font-medium leading-none">Everything</p> <p className="text-sm font-medium leading-none">Everything</p>
<p className="text-sm text-muted-foreground"> <p className="text-sm text-muted-foreground">
@@ -28,7 +28,7 @@ export function DemoNotifications() {
</div> </div>
</div> </div>
<div className="-mx-2 flex items-start space-x-4 rounded-md bg-accent p-2 text-accent-foreground transition-all"> <div className="-mx-2 flex items-start space-x-4 rounded-md bg-accent p-2 text-accent-foreground transition-all">
<PersonIcon className="mt-px h-5 w-5" /> <User className="mt-px h-5 w-5" />
<div className="space-y-1"> <div className="space-y-1">
<p className="text-sm font-medium leading-none">Available</p> <p className="text-sm font-medium leading-none">Available</p>
<p className="text-sm text-muted-foreground"> <p className="text-sm text-muted-foreground">
@@ -37,7 +37,7 @@ export function DemoNotifications() {
</div> </div>
</div> </div>
<div className="-mx-2 flex items-start space-x-4 rounded-md p-2 transition-all hover:bg-accent hover:text-accent-foreground"> <div className="-mx-2 flex items-start space-x-4 rounded-md p-2 transition-all hover:bg-accent hover:text-accent-foreground">
<EyeNoneIcon className="mt-px h-5 w-5" /> <EyeOff className="mt-px h-5 w-5" />
<div className="space-y-1"> <div className="space-y-1">
<p className="text-sm font-medium leading-none">Ignoring</p> <p className="text-sm font-medium leading-none">Ignoring</p>
<p className="text-sm text-muted-foreground"> <p className="text-sm text-muted-foreground">

View File

@@ -41,7 +41,7 @@ export function DemoShareDocument() {
</div> </div>
<Separator className="my-4" /> <Separator className="my-4" />
<div className="space-y-4"> <div className="space-y-4">
<h4 className="text-sm font-medium">People with access</h4> <div className="text-sm font-medium">People with access</div>
<div className="grid gap-6"> <div className="grid gap-6">
<div className="flex items-center justify-between space-x-4"> <div className="flex items-center justify-between space-x-4">
<div className="flex items-center space-x-4"> <div className="flex items-center space-x-4">

View File

@@ -1,4 +1,4 @@
import { ChevronDownIcon } from "@radix-ui/react-icons" import { ChevronDown } from "lucide-react"
import { import {
Avatar, Avatar,
@@ -51,8 +51,7 @@ export function DemoTeamMembers() {
<Popover> <Popover>
<PopoverTrigger asChild> <PopoverTrigger asChild>
<Button variant="outline" className="ml-auto"> <Button variant="outline" className="ml-auto">
Owner{" "} Owner <ChevronDown className="text-muted-foreground" />
<ChevronDownIcon className="ml-2 h-4 w-4 text-muted-foreground" />
</Button> </Button>
</PopoverTrigger> </PopoverTrigger>
<PopoverContent className="p-0" align="end"> <PopoverContent className="p-0" align="end">
@@ -105,8 +104,7 @@ export function DemoTeamMembers() {
<Popover> <Popover>
<PopoverTrigger asChild> <PopoverTrigger asChild>
<Button variant="outline" className="ml-auto"> <Button variant="outline" className="ml-auto">
Member{" "} Member <ChevronDown className="text-muted-foreground" />
<ChevronDownIcon className="ml-2 h-4 w-4 text-muted-foreground" />
</Button> </Button>
</PopoverTrigger> </PopoverTrigger>
<PopoverContent className="p-0" align="end"> <PopoverContent className="p-0" align="end">

View File

@@ -1,8 +1,8 @@
"use client" "use client"
import * as React from "react" import * as React from "react"
import { CalendarIcon } from "@radix-ui/react-icons"
import { addDays, format } from "date-fns" import { addDays, format } from "date-fns"
import { CalendarIcon } from "lucide-react"
import { DateRange } from "react-day-picker" import { DateRange } from "react-day-picker"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"

View File

@@ -1,11 +1,7 @@
"use client" "use client"
import * as React from "react" import * as React from "react"
import { import { Check, ChevronsUpDown, PlusCircle } from "lucide-react"
CaretSortIcon,
CheckIcon,
PlusCircledIcon,
} from "@radix-ui/react-icons"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
import { import {
@@ -105,7 +101,7 @@ export default function TeamSwitcher({ className }: TeamSwitcherProps) {
<AvatarFallback>SC</AvatarFallback> <AvatarFallback>SC</AvatarFallback>
</Avatar> </Avatar>
{selectedTeam.label} {selectedTeam.label}
<CaretSortIcon className="ml-auto h-4 w-4 shrink-0 opacity-50" /> <ChevronsUpDown className="ml-auto opacity-50" />
</Button> </Button>
</PopoverTrigger> </PopoverTrigger>
<PopoverContent className="w-[200px] p-0"> <PopoverContent className="w-[200px] p-0">
@@ -133,9 +129,9 @@ export default function TeamSwitcher({ className }: TeamSwitcherProps) {
<AvatarFallback>SC</AvatarFallback> <AvatarFallback>SC</AvatarFallback>
</Avatar> </Avatar>
{team.label} {team.label}
<CheckIcon <Check
className={cn( className={cn(
"ml-auto h-4 w-4", "ml-auto",
selectedTeam.value === team.value selectedTeam.value === team.value
? "opacity-100" ? "opacity-100"
: "opacity-0" : "opacity-0"
@@ -156,7 +152,7 @@ export default function TeamSwitcher({ className }: TeamSwitcherProps) {
setShowNewTeamDialog(true) setShowNewTeamDialog(true)
}} }}
> >
<PlusCircledIcon className="mr-2 h-5 w-5" /> <PlusCircle className="h-5 w-5" />
Create Team Create Team
</CommandItem> </CommandItem>
</DialogTrigger> </DialogTrigger>

View File

@@ -1,8 +1,8 @@
"use client" "use client"
import { zodResolver } from "@hookform/resolvers/zod" import { zodResolver } from "@hookform/resolvers/zod"
import { CalendarIcon, CaretSortIcon, CheckIcon } from "@radix-ui/react-icons"
import { format } from "date-fns" import { format } from "date-fns"
import { CalendarIcon, Check, ChevronsUpDown } from "lucide-react"
import { useForm } from "react-hook-form" import { useForm } from "react-hook-form"
import { z } from "zod" import { z } from "zod"
@@ -174,7 +174,7 @@ export function AccountForm() {
(language) => language.value === field.value (language) => language.value === field.value
)?.label )?.label
: "Select language"} : "Select language"}
<CaretSortIcon className="ml-2 h-4 w-4 shrink-0 opacity-50" /> <ChevronsUpDown className="opacity-50" />
</Button> </Button>
</FormControl> </FormControl>
</PopoverTrigger> </PopoverTrigger>
@@ -192,9 +192,9 @@ export function AccountForm() {
form.setValue("language", language.value) form.setValue("language", language.value)
}} }}
> >
<CheckIcon <Check
className={cn( className={cn(
"mr-2 h-4 w-4", "mr-2",
language.value === field.value language.value === field.value
? "opacity-100" ? "opacity-100"
: "opacity-0" : "opacity-0"

View File

@@ -1,7 +1,7 @@
"use client" "use client"
import { zodResolver } from "@hookform/resolvers/zod" import { zodResolver } from "@hookform/resolvers/zod"
import { ChevronDownIcon } from "@radix-ui/react-icons" import { ChevronDown } from "lucide-react"
import { useForm } from "react-hook-form" import { useForm } from "react-hook-form"
import { z } from "zod" import { z } from "zod"
@@ -76,7 +76,7 @@ export function AppearanceForm() {
<option value="system">System</option> <option value="system">System</option>
</select> </select>
</FormControl> </FormControl>
<ChevronDownIcon className="absolute right-3 top-2.5 h-4 w-4 opacity-50" /> <ChevronDown className="absolute right-3 top-2.5 h-4 w-4 opacity-50" />
</div> </div>
<FormDescription> <FormDescription>
Set the font you want to use in the dashboard. Set the font you want to use in the dashboard.

View File

@@ -1,7 +1,7 @@
import { Metadata } from "next" import { Metadata } from "next"
import Link from "next/link" import Link from "next/link"
import { cn } from "@/lib/utils" import { siteConfig } from "@/config/site"
import { Announcement } from "@/components/announcement" import { Announcement } from "@/components/announcement"
import { ExamplesNav } from "@/components/examples-nav" import { ExamplesNav } from "@/components/examples-nav"
import { import {
@@ -10,7 +10,7 @@ import {
PageHeaderDescription, PageHeaderDescription,
PageHeaderHeading, PageHeaderHeading,
} from "@/components/page-header" } from "@/components/page-header"
import { Button, buttonVariants } from "@/registry/new-york/ui/button" import { Button } from "@/registry/new-york/ui/button"
export const metadata: Metadata = { export const metadata: Metadata = {
title: "Examples", title: "Examples",
@@ -23,32 +23,37 @@ interface ExamplesLayoutProps {
export default function ExamplesLayout({ children }: ExamplesLayoutProps) { export default function ExamplesLayout({ children }: ExamplesLayoutProps) {
return ( return (
<div className="container relative"> <div className="relative">
<PageHeader> <PageHeader>
<Announcement /> <Announcement />
<PageHeaderHeading className="hidden md:block"> <PageHeaderHeading>Build your component library</PageHeaderHeading>
Check out some examples
</PageHeaderHeading>
<PageHeaderHeading className="md:hidden">Examples</PageHeaderHeading>
<PageHeaderDescription> <PageHeaderDescription>
Dashboard, cards, authentication. Some examples built using the Beautifully designed components that you can copy and paste into your
components. Use this as a guide to build your own. apps. Made with Tailwind CSS. Open source.
</PageHeaderDescription> </PageHeaderDescription>
<PageActions> <PageActions>
<Button asChild size="sm"> <Button asChild size="sm">
<Link href="/docs">Get Started</Link> <Link href="/docs">Get Started</Link>
</Button> </Button>
<Button asChild size="sm" variant="ghost"> <Button asChild size="sm" variant="ghost">
<Link href="/components">Components</Link> <Link
target="_blank"
rel="noreferrer"
href={siteConfig.links.github}
>
GitHub
</Link>
</Button> </Button>
</PageActions> </PageActions>
</PageHeader> </PageHeader>
<section> <div className="container py-6">
<ExamplesNav /> <section>
<div className="overflow-hidden rounded-[0.5rem] border bg-background shadow"> <ExamplesNav />
{children} <div className="overflow-hidden rounded-[0.5rem] border bg-background shadow">
</div> {children}
</section> </div>
</section>
</div>
</div> </div>
) )
} }

View File

@@ -1,5 +1,5 @@
import Image from "next/image" import Image from "next/image"
import { PlusCircledIcon } from "@radix-ui/react-icons" import { PlusCircle } from "lucide-react"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
import { import {
@@ -54,7 +54,7 @@ export function AlbumArtwork({
<ContextMenuSubTrigger>Add to Playlist</ContextMenuSubTrigger> <ContextMenuSubTrigger>Add to Playlist</ContextMenuSubTrigger>
<ContextMenuSubContent className="w-48"> <ContextMenuSubContent className="w-48">
<ContextMenuItem> <ContextMenuItem>
<PlusCircledIcon className="mr-2 h-4 w-4" /> <PlusCircle className="mr-2 h-4 w-4" />
New Playlist New Playlist
</ContextMenuItem> </ContextMenuItem>
<ContextMenuSeparator /> <ContextMenuSeparator />

View File

@@ -1,6 +1,6 @@
import { Metadata } from "next" import { Metadata } from "next"
import Image from "next/image" import Image from "next/image"
import { PlusCircledIcon } from "@radix-ui/react-icons" import { PlusCircle } from "lucide-react"
import { Button } from "@/registry/new-york/ui/button" import { Button } from "@/registry/new-york/ui/button"
import { ScrollArea, ScrollBar } from "@/registry/new-york/ui/scroll-area" import { ScrollArea, ScrollBar } from "@/registry/new-york/ui/scroll-area"
@@ -64,7 +64,7 @@ export default function MusicPage() {
</TabsList> </TabsList>
<div className="ml-auto mr-4"> <div className="ml-auto mr-4">
<Button> <Button>
<PlusCircledIcon className="mr-2 h-4 w-4" /> <PlusCircle />
Add music Add music
</Button> </Button>
</div> </div>

View File

@@ -1,8 +1,8 @@
"use client" "use client"
import * as React from "react" import * as React from "react"
import { CaretSortIcon, CheckIcon } from "@radix-ui/react-icons"
import { PopoverProps } from "@radix-ui/react-popover" import { PopoverProps } from "@radix-ui/react-popover"
import { Check, ChevronsUpDown } from "lucide-react"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
import { useMutationObserver } from "@/hooks/use-mutation-observer" import { useMutationObserver } from "@/hooks/use-mutation-observer"
@@ -64,7 +64,7 @@ export function ModelSelector({ models, types, ...props }: ModelSelectorProps) {
className="w-full justify-between" className="w-full justify-between"
> >
{selectedModel ? selectedModel.name : "Select a model..."} {selectedModel ? selectedModel.name : "Select a model..."}
<CaretSortIcon className="ml-2 h-4 w-4 shrink-0 opacity-50" /> <ChevronsUpDown className="opacity-50" />
</Button> </Button>
</PopoverTrigger> </PopoverTrigger>
<PopoverContent align="end" className="w-[250px] p-0"> <PopoverContent align="end" className="w-[250px] p-0">
@@ -154,11 +154,8 @@ function ModelItem({ model, isSelected, onSelect, onPeek }: ModelItemProps) {
className="data-[selected=true]:bg-primary data-[selected=true]:text-primary-foreground" className="data-[selected=true]:bg-primary data-[selected=true]:text-primary-foreground"
> >
{model.name} {model.name}
<CheckIcon <Check
className={cn( className={cn("ml-auto", isSelected ? "opacity-100" : "opacity-0")}
"ml-auto h-4 w-4",
isSelected ? "opacity-100" : "opacity-0"
)}
/> />
</CommandItem> </CommandItem>
) )

View File

@@ -2,7 +2,7 @@
import * as React from "react" import * as React from "react"
import { Dialog } from "@radix-ui/react-dialog" import { Dialog } from "@radix-ui/react-dialog"
import { DotsHorizontalIcon } from "@radix-ui/react-icons" import { MoreHorizontal } from "lucide-react"
import { toast } from "@/registry/new-york/hooks/use-toast" import { toast } from "@/registry/new-york/hooks/use-toast"
import { import {
@@ -40,9 +40,9 @@ export function PresetActions() {
<> <>
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger asChild> <DropdownMenuTrigger asChild>
<Button variant="secondary"> <Button variant="secondary" size="icon">
<span className="sr-only">Actions</span> <span className="sr-only">Actions</span>
<DotsHorizontalIcon className="h-4 w-4" /> <MoreHorizontal />
</Button> </Button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent align="end"> <DropdownMenuContent align="end">

View File

@@ -2,8 +2,8 @@
import * as React from "react" import * as React from "react"
import { useRouter } from "next/navigation" import { useRouter } from "next/navigation"
import { CaretSortIcon, CheckIcon } from "@radix-ui/react-icons"
import { PopoverProps } from "@radix-ui/react-popover" import { PopoverProps } from "@radix-ui/react-popover"
import { Check, ChevronsUpDown } from "lucide-react"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
import { Button } from "@/registry/new-york/ui/button" import { Button } from "@/registry/new-york/ui/button"
@@ -43,7 +43,7 @@ export function PresetSelector({ presets, ...props }: PresetSelectorProps) {
className="flex-1 justify-between md:max-w-[200px] lg:max-w-[300px]" className="flex-1 justify-between md:max-w-[200px] lg:max-w-[300px]"
> >
{selectedPreset ? selectedPreset.name : "Load a preset..."} {selectedPreset ? selectedPreset.name : "Load a preset..."}
<CaretSortIcon className="ml-2 h-4 w-4 shrink-0 opacity-50" /> <ChevronsUpDown className="opacity-50" />
</Button> </Button>
</PopoverTrigger> </PopoverTrigger>
<PopoverContent className="w-[300px] p-0"> <PopoverContent className="w-[300px] p-0">
@@ -61,9 +61,9 @@ export function PresetSelector({ presets, ...props }: PresetSelectorProps) {
}} }}
> >
{preset.name} {preset.name}
<CheckIcon <Check
className={cn( className={cn(
"ml-auto h-4 w-4", "ml-auto",
selectedPreset?.id === preset.id selectedPreset?.id === preset.id
? "opacity-100" ? "opacity-100"
: "opacity-0" : "opacity-0"

View File

@@ -1,4 +1,4 @@
import { CopyIcon } from "@radix-ui/react-icons" import { Copy } from "lucide-react"
import { Button } from "@/registry/new-york/ui/button" import { Button } from "@/registry/new-york/ui/button"
import { Input } from "@/registry/new-york/ui/input" import { Input } from "@/registry/new-york/ui/input"
@@ -37,7 +37,7 @@ export function PresetShare() {
</div> </div>
<Button type="submit" size="sm" className="px-3"> <Button type="submit" size="sm" className="px-3">
<span className="sr-only">Copy</span> <span className="sr-only">Copy</span>
<CopyIcon className="h-4 w-4" /> <Copy />
</Button> </Button>
</div> </div>
</PopoverContent> </PopoverContent>

View File

@@ -1,6 +1,6 @@
import { Metadata } from "next" import { Metadata } from "next"
import Image from "next/image" import Image from "next/image"
import { CounterClockwiseClockIcon } from "@radix-ui/react-icons" import { RotateCcw } from "lucide-react"
import { Button } from "@/registry/new-york/ui/button" import { Button } from "@/registry/new-york/ui/button"
import { import {
@@ -265,7 +265,7 @@ export default function PlaygroundPage() {
<Button>Submit</Button> <Button>Submit</Button>
<Button variant="secondary"> <Button variant="secondary">
<span className="sr-only">Show history</span> <span className="sr-only">Show history</span>
<CounterClockwiseClockIcon className="h-4 w-4" /> <RotateCcw />
</Button> </Button>
</div> </div>
</div> </div>
@@ -283,7 +283,7 @@ export default function PlaygroundPage() {
<Button>Submit</Button> <Button>Submit</Button>
<Button variant="secondary"> <Button variant="secondary">
<span className="sr-only">Show history</span> <span className="sr-only">Show history</span>
<CounterClockwiseClockIcon className="h-4 w-4" /> <RotateCcw />
</Button> </Button>
</div> </div>
</div> </div>
@@ -314,7 +314,7 @@ export default function PlaygroundPage() {
<Button>Submit</Button> <Button>Submit</Button>
<Button variant="secondary"> <Button variant="secondary">
<span className="sr-only">Show history</span> <span className="sr-only">Show history</span>
<CounterClockwiseClockIcon className="h-4 w-4" /> <RotateCcw />
</Button> </Button>
</div> </div>
</div> </div>

View File

@@ -1,10 +1,5 @@
import {
ArrowDownIcon,
ArrowUpIcon,
CaretSortIcon,
EyeNoneIcon,
} from "@radix-ui/react-icons"
import { Column } from "@tanstack/react-table" import { Column } from "@tanstack/react-table"
import { ArrowDown, ArrowUp, ChevronsUpDown, EyeOff } from "lucide-react"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
import { Button } from "@/registry/new-york/ui/button" import { Button } from "@/registry/new-york/ui/button"
@@ -42,26 +37,26 @@ export function DataTableColumnHeader<TData, TValue>({
> >
<span>{title}</span> <span>{title}</span>
{column.getIsSorted() === "desc" ? ( {column.getIsSorted() === "desc" ? (
<ArrowDownIcon className="ml-2 h-4 w-4" /> <ArrowDown />
) : column.getIsSorted() === "asc" ? ( ) : column.getIsSorted() === "asc" ? (
<ArrowUpIcon className="ml-2 h-4 w-4" /> <ArrowUp />
) : ( ) : (
<CaretSortIcon className="ml-2 h-4 w-4" /> <ChevronsUpDown />
)} )}
</Button> </Button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent align="start"> <DropdownMenuContent align="start">
<DropdownMenuItem onClick={() => column.toggleSorting(false)}> <DropdownMenuItem onClick={() => column.toggleSorting(false)}>
<ArrowUpIcon className="mr-2 h-3.5 w-3.5 text-muted-foreground/70" /> <ArrowUp className="h-3.5 w-3.5 text-muted-foreground/70" />
Asc Asc
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem onClick={() => column.toggleSorting(true)}> <DropdownMenuItem onClick={() => column.toggleSorting(true)}>
<ArrowDownIcon className="mr-2 h-3.5 w-3.5 text-muted-foreground/70" /> <ArrowDown className="h-3.5 w-3.5 text-muted-foreground/70" />
Desc Desc
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuSeparator /> <DropdownMenuSeparator />
<DropdownMenuItem onClick={() => column.toggleVisibility(false)}> <DropdownMenuItem onClick={() => column.toggleVisibility(false)}>
<EyeNoneIcon className="mr-2 h-3.5 w-3.5 text-muted-foreground/70" /> <EyeOff className="h-3.5 w-3.5 text-muted-foreground/70" />
Hide Hide
</DropdownMenuItem> </DropdownMenuItem>
</DropdownMenuContent> </DropdownMenuContent>

View File

@@ -1,6 +1,6 @@
import * as React from "react" import * as React from "react"
import { CheckIcon, PlusCircledIcon } from "@radix-ui/react-icons"
import { Column } from "@tanstack/react-table" import { Column } from "@tanstack/react-table"
import { Check, PlusCircle } from "lucide-react"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
import { Badge } from "@/registry/new-york/ui/badge" import { Badge } from "@/registry/new-york/ui/badge"
@@ -43,7 +43,7 @@ export function DataTableFacetedFilter<TData, TValue>({
<Popover> <Popover>
<PopoverTrigger asChild> <PopoverTrigger asChild>
<Button variant="outline" size="sm" className="h-8 border-dashed"> <Button variant="outline" size="sm" className="h-8 border-dashed">
<PlusCircledIcon className="mr-2 h-4 w-4" /> <PlusCircle />
{title} {title}
{selectedValues?.size > 0 && ( {selectedValues?.size > 0 && (
<> <>
@@ -111,7 +111,7 @@ export function DataTableFacetedFilter<TData, TValue>({
: "opacity-50 [&_svg]:invisible" : "opacity-50 [&_svg]:invisible"
)} )}
> >
<CheckIcon className={cn("h-4 w-4")} /> <Check />
</div> </div>
{option.icon && ( {option.icon && (
<option.icon className="mr-2 h-4 w-4 text-muted-foreground" /> <option.icon className="mr-2 h-4 w-4 text-muted-foreground" />

View File

@@ -1,10 +1,10 @@
import {
ChevronLeftIcon,
ChevronRightIcon,
DoubleArrowLeftIcon,
DoubleArrowRightIcon,
} from "@radix-ui/react-icons"
import { Table } from "@tanstack/react-table" import { Table } from "@tanstack/react-table"
import {
ChevronLeft,
ChevronRight,
ChevronsLeft,
ChevronsRight,
} from "lucide-react"
import { Button } from "@/registry/new-york/ui/button" import { Button } from "@/registry/new-york/ui/button"
import { import {
@@ -61,7 +61,7 @@ export function DataTablePagination<TData>({
disabled={!table.getCanPreviousPage()} disabled={!table.getCanPreviousPage()}
> >
<span className="sr-only">Go to first page</span> <span className="sr-only">Go to first page</span>
<DoubleArrowLeftIcon className="h-4 w-4" /> <ChevronsLeft />
</Button> </Button>
<Button <Button
variant="outline" variant="outline"
@@ -70,7 +70,7 @@ export function DataTablePagination<TData>({
disabled={!table.getCanPreviousPage()} disabled={!table.getCanPreviousPage()}
> >
<span className="sr-only">Go to previous page</span> <span className="sr-only">Go to previous page</span>
<ChevronLeftIcon className="h-4 w-4" /> <ChevronLeft />
</Button> </Button>
<Button <Button
variant="outline" variant="outline"
@@ -79,7 +79,7 @@ export function DataTablePagination<TData>({
disabled={!table.getCanNextPage()} disabled={!table.getCanNextPage()}
> >
<span className="sr-only">Go to next page</span> <span className="sr-only">Go to next page</span>
<ChevronRightIcon className="h-4 w-4" /> <ChevronRight />
</Button> </Button>
<Button <Button
variant="outline" variant="outline"
@@ -88,7 +88,7 @@ export function DataTablePagination<TData>({
disabled={!table.getCanNextPage()} disabled={!table.getCanNextPage()}
> >
<span className="sr-only">Go to last page</span> <span className="sr-only">Go to last page</span>
<DoubleArrowRightIcon className="h-4 w-4" /> <ChevronsRight />
</Button> </Button>
</div> </div>
</div> </div>

View File

@@ -1,7 +1,7 @@
"use client" "use client"
import { DotsHorizontalIcon } from "@radix-ui/react-icons"
import { Row } from "@tanstack/react-table" import { Row } from "@tanstack/react-table"
import { MoreHorizontal } from "lucide-react"
import { Button } from "@/registry/new-york/ui/button" import { Button } from "@/registry/new-york/ui/button"
import { import {
@@ -37,7 +37,7 @@ export function DataTableRowActions<TData>({
variant="ghost" variant="ghost"
className="flex h-8 w-8 p-0 data-[state=open]:bg-muted" className="flex h-8 w-8 p-0 data-[state=open]:bg-muted"
> >
<DotsHorizontalIcon className="h-4 w-4" /> <MoreHorizontal />
<span className="sr-only">Open menu</span> <span className="sr-only">Open menu</span>
</Button> </Button>
</DropdownMenuTrigger> </DropdownMenuTrigger>

View File

@@ -1,7 +1,7 @@
"use client" "use client"
import { Cross2Icon } from "@radix-ui/react-icons"
import { Table } from "@tanstack/react-table" import { Table } from "@tanstack/react-table"
import { X } from "lucide-react"
import { Button } from "@/registry/new-york/ui/button" import { Button } from "@/registry/new-york/ui/button"
import { Input } from "@/registry/new-york/ui/input" import { Input } from "@/registry/new-york/ui/input"
@@ -51,7 +51,7 @@ export function DataTableToolbar<TData>({
className="h-8 px-2 lg:px-3" className="h-8 px-2 lg:px-3"
> >
Reset Reset
<Cross2Icon className="ml-2 h-4 w-4" /> <X />
</Button> </Button>
)} )}
</div> </div>

View File

@@ -1,8 +1,8 @@
"use client" "use client"
import { DropdownMenuTrigger } from "@radix-ui/react-dropdown-menu" import { DropdownMenuTrigger } from "@radix-ui/react-dropdown-menu"
import { MixerHorizontalIcon } from "@radix-ui/react-icons"
import { Table } from "@tanstack/react-table" import { Table } from "@tanstack/react-table"
import { Settings2 } from "lucide-react"
import { Button } from "@/registry/new-york/ui/button" import { Button } from "@/registry/new-york/ui/button"
import { import {
@@ -28,7 +28,7 @@ export function DataTableViewOptions<TData>({
size="sm" size="sm"
className="ml-auto hidden h-8 lg:flex" className="ml-auto hidden h-8 lg:flex"
> >
<MixerHorizontalIcon className="mr-2 h-4 w-4" /> <Settings2 />
View View
</Button> </Button>
</DropdownMenuTrigger> </DropdownMenuTrigger>

View File

@@ -1,13 +1,13 @@
import { import {
ArrowDownIcon, ArrowDown,
ArrowRightIcon, ArrowRight,
ArrowUpIcon, ArrowUp,
CheckCircledIcon, CheckCircle,
CircleIcon, Circle,
CrossCircledIcon, CircleOff,
QuestionMarkCircledIcon, HelpCircle,
StopwatchIcon, Timer,
} from "@radix-ui/react-icons" } from "lucide-react"
export const labels = [ export const labels = [
{ {
@@ -28,27 +28,27 @@ export const statuses = [
{ {
value: "backlog", value: "backlog",
label: "Backlog", label: "Backlog",
icon: QuestionMarkCircledIcon, icon: HelpCircle,
}, },
{ {
value: "todo", value: "todo",
label: "Todo", label: "Todo",
icon: CircleIcon, icon: Circle,
}, },
{ {
value: "in progress", value: "in progress",
label: "In Progress", label: "In Progress",
icon: StopwatchIcon, icon: Timer,
}, },
{ {
value: "done", value: "done",
label: "Done", label: "Done",
icon: CheckCircledIcon, icon: CheckCircle,
}, },
{ {
value: "canceled", value: "canceled",
label: "Canceled", label: "Canceled",
icon: CrossCircledIcon, icon: CircleOff,
}, },
] ]
@@ -56,16 +56,16 @@ export const priorities = [
{ {
label: "Low", label: "Low",
value: "low", value: "low",
icon: ArrowDownIcon, icon: ArrowDown,
}, },
{ {
label: "Medium", label: "Medium",
value: "medium", value: "medium",
icon: ArrowRightIcon, icon: ArrowRight,
}, },
{ {
label: "High", label: "High",
value: "high", value: "high",
icon: ArrowUpIcon, icon: ArrowUp,
}, },
] ]

View File

@@ -7,10 +7,12 @@ interface AppLayoutProps {
export default function AppLayout({ children }: AppLayoutProps) { export default function AppLayout({ children }: AppLayoutProps) {
return ( return (
<> <div data-wrapper="" className="border-border/40 dark:border-border">
<SiteHeader /> <div className="mx-auto w-full border-border/40 dark:border-border min-[1800px]:max-w-[1536px] min-[1800px]:border-x">
<main className="flex-1">{children}</main> <SiteHeader />
<SiteFooter /> <main className="flex-1">{children}</main>
</> <SiteFooter />
</div>
</div>
) )
} }

View File

@@ -10,18 +10,18 @@ import {
PageHeaderDescription, PageHeaderDescription,
PageHeaderHeading, PageHeaderHeading,
} from "@/components/page-header" } from "@/components/page-header"
import CardsNewYork from "@/registry/new-york/example/cards"
import { Button } from "@/registry/new-york/ui/button" import { Button } from "@/registry/new-york/ui/button"
import MailPage from "@/app/(app)/examples/mail/page"
export default function IndexPage() { export default function IndexPage() {
return ( return (
<div className="container relative"> <div className="relative">
<PageHeader> <PageHeader>
<Announcement /> <Announcement />
<PageHeaderHeading>Build your component library</PageHeaderHeading> <PageHeaderHeading>Build your component library</PageHeaderHeading>
<PageHeaderDescription> <PageHeaderDescription>
Beautifully designed components that you can copy and paste into your Beautifully designed components that you can copy and paste into your
apps. apps. Made with Tailwind CSS. Open source.
</PageHeaderDescription> </PageHeaderDescription>
<PageActions> <PageActions>
<Button asChild size="sm"> <Button asChild size="sm">
@@ -38,28 +38,28 @@ export default function IndexPage() {
</Button> </Button>
</PageActions> </PageActions>
</PageHeader> </PageHeader>
<ExamplesNav className="[&>a:first-child]:text-primary" /> <div className="container py-6">
<section className="overflow-hidden rounded-lg border bg-background shadow-md md:hidden md:shadow-xl"> <ExamplesNav className="[&>a:first-child]:text-primary" />
<Image <section className="overflow-hidden rounded-lg border bg-background shadow-md md:hidden md:shadow-xl">
src="/examples/mail-dark.png" <Image
width={1280} src="/examples/cards-light.png"
height={727} width={1280}
alt="Mail" height={1214}
className="hidden dark:block" alt="Cards"
/> className="block dark:hidden"
<Image />
src="/examples/mail-light.png" <Image
width={1280} src="/examples/cards-dark.png"
height={727} width={1280}
alt="Mail" height={1214}
className="block dark:hidden" alt="Cards"
/> className="hidden dark:block"
</section> />
<section className="hidden md:block"> </section>
<div className="overflow-hidden rounded-lg border bg-background shadow"> <section className="hidden md:block [&>div]:p-0">
<MailPage /> <CardsNewYork />
</div> </section>
</section> </div>
</div> </div>
) )
} }

View File

@@ -1,27 +0,0 @@
import Link from "next/link"
import { ThemeWrapper } from "@/components/theme-wrapper"
import { styles } from "@/registry/registry-styles"
interface SinkLayoutProps {
children: React.ReactNode
}
export default function SinkLayout({ children }: SinkLayoutProps) {
return (
<div className="flex flex-col">
<div className="container">
<div className="flex space-x-2 px-2 py-4">
{styles.map((style) => (
<Link href={`/sink/${style.name}`} key={style.name}>
{style.label}
</Link>
))}
</div>
</div>
<div className="flex-1">
<ThemeWrapper>{children}</ThemeWrapper>
</div>
</div>
)
}

View File

@@ -1,206 +0,0 @@
import * as React from "react"
import Link from "next/link"
import { cn } from "@/lib/utils"
import AccordionDemo from "@/registry/new-york/example/accordion-demo"
import AlertDialogDemo from "@/registry/new-york/example/alert-dialog-demo"
import AspectRatioDemo from "@/registry/new-york/example/aspect-ratio-demo"
import AvatarDemo from "@/registry/new-york/example/avatar-demo"
import BadgeDemo from "@/registry/new-york/example/badge-demo"
import BadgeDestructive from "@/registry/new-york/example/badge-destructive"
import BadgeOutline from "@/registry/new-york/example/badge-outline"
import BadgeSecondary from "@/registry/new-york/example/badge-secondary"
import ButtonDemo from "@/registry/new-york/example/button-demo"
import ButtonDestructive from "@/registry/new-york/example/button-destructive"
import ButtonGhost from "@/registry/new-york/example/button-ghost"
import ButtonLink from "@/registry/new-york/example/button-link"
import ButtonLoading from "@/registry/new-york/example/button-loading"
import ButtonOutline from "@/registry/new-york/example/button-outline"
import ButtonSecondary from "@/registry/new-york/example/button-secondary"
import ButtonWithIcon from "@/registry/new-york/example/button-with-icon"
import CardDemo from "@/registry/new-york/example/card-demo"
import CheckboxDemo from "@/registry/new-york/example/checkbox-demo"
import CollapsibleDemo from "@/registry/new-york/example/collapsible-demo"
import CommandDemo from "@/registry/new-york/example/command-demo"
import ContextMenuDemo from "@/registry/new-york/example/context-menu-demo"
import DatePickerDemo from "@/registry/new-york/example/date-picker-demo"
import DialogDemo from "@/registry/new-york/example/dialog-demo"
import DropdownMenuDemo from "@/registry/new-york/example/dropdown-menu-demo"
import HoverCardDemo from "@/registry/new-york/example/hover-card-demo"
import MenubarDemo from "@/registry/new-york/example/menubar-demo"
import NavigationMenuDemo from "@/registry/new-york/example/navigation-menu-demo"
import PopoverDemo from "@/registry/new-york/example/popover-demo"
import ProgressDemo from "@/registry/new-york/example/progress-demo"
import RadioGroupDemo from "@/registry/new-york/example/radio-group-demo"
import ScrollAreaDemo from "@/registry/new-york/example/scroll-area-demo"
import SelectDemo from "@/registry/new-york/example/select-demo"
import SeparatorDemo from "@/registry/new-york/example/separator-demo"
import SheetDemo from "@/registry/new-york/example/sheet-demo"
import SkeletonDemo from "@/registry/new-york/example/skeleton-demo"
import SliderDemo from "@/registry/new-york/example/slider-demo"
import SwitchDemo from "@/registry/new-york/example/switch-demo"
import TabsDemo from "@/registry/new-york/example/tabs-demo"
import ToastDemo from "@/registry/new-york/example/toast-demo"
import ToggleDemo from "@/registry/new-york/example/toggle-demo"
import ToggleDisabled from "@/registry/new-york/example/toggle-disabled"
import ToggleOutline from "@/registry/new-york/example/toggle-outline"
import ToggleWithText from "@/registry/new-york/example/toggle-with-text"
import TooltipDemo from "@/registry/new-york/example/tooltip-demo"
import { Button } from "@/registry/new-york/ui/button"
export default function KitchenSinkPage() {
return (
<div className="container">
<div className="grid gap-4">
<div className="grid grid-cols-3 items-start gap-4">
<div className="grid gap-4">
<ComponentWrapper>
<CardDemo className="w-full" />
</ComponentWrapper>
<ComponentWrapper>
<SliderDemo className="w-full" />
</ComponentWrapper>
<ComponentWrapper
className="spa flex-col items-start space-x-0
space-y-2"
>
<p className="text-sm text-muted-foreground">Documentation</p>
<p className="text-sm font-medium leading-none">
You can customize the theme using{" "}
<code className="relative rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-sm font-semibold text-foreground">
CSS variables
</code>
.{" "}
<Link
href="#"
className="font-medium text-primary underline underline-offset-4"
>
Click here
</Link>{" "}
to learn more.
</p>
</ComponentWrapper>
<ComponentWrapper>
<CheckboxDemo />
<HoverCardDemo />
</ComponentWrapper>
<ComponentWrapper className="[&>div]:w-full">
<TabsDemo />
</ComponentWrapper>
</div>
<div className="grid gap-4">
<ComponentWrapper>
<MenubarDemo />
<AvatarDemo />
</ComponentWrapper>
<ComponentWrapper className="flex-col items-start space-x-0 space-y-2">
<div className="flex space-x-2">
<ButtonDemo />
<ButtonSecondary />
<ButtonDestructive />
</div>
<div className="flex space-x-2">
<ButtonOutline />
<ButtonLink />
<ButtonGhost />
</div>
<div className="flex space-x-2">
<ButtonWithIcon />
<ButtonLoading />
</div>
<div className="flex space-x-2">
<Button size="lg">Large</Button>
<Button size="sm">Small</Button>
</div>
</ComponentWrapper>
<ComponentWrapper>
<DatePickerDemo />
</ComponentWrapper>
<ComponentWrapper>
<AccordionDemo />
</ComponentWrapper>
<ComponentWrapper className="[&_ul>li:last-child]:hidden">
<NavigationMenuDemo />
</ComponentWrapper>
<ComponentWrapper className="justify-between">
<SwitchDemo />
<SelectDemo />
</ComponentWrapper>
<ComponentWrapper>
<SeparatorDemo />
</ComponentWrapper>
<ComponentWrapper>
<AspectRatioDemo />
</ComponentWrapper>
<ComponentWrapper>
<PopoverDemo />
<ToastDemo />
</ComponentWrapper>
</div>
<div className="grid gap-4">
<ComponentWrapper>
<TooltipDemo />
<SheetDemo />
<ProgressDemo />
</ComponentWrapper>
<ComponentWrapper>
<CommandDemo />
</ComponentWrapper>
<ComponentWrapper className="[&>span]:h-[80px] [&>span]:w-[200px]">
<RadioGroupDemo />
<ContextMenuDemo />
</ComponentWrapper>
<ComponentWrapper>
<div className="flex space-x-2">
<DropdownMenuDemo />
<AlertDialogDemo />
<DialogDemo />
</div>
</ComponentWrapper>
<ComponentWrapper>
<div className="flex space-x-2">
<BadgeDemo />
<BadgeSecondary />
<BadgeDestructive />
<BadgeOutline />
</div>
</ComponentWrapper>
<ComponentWrapper>
<SkeletonDemo />
</ComponentWrapper>
<ComponentWrapper className="[&>div]:w-full">
<CollapsibleDemo />
</ComponentWrapper>
<ComponentWrapper>
<div className="flex space-x-2">
<ToggleDemo />
<ToggleOutline />
<ToggleDisabled />
<ToggleWithText />
</div>
</ComponentWrapper>
<ComponentWrapper>
<ScrollAreaDemo />
</ComponentWrapper>
</div>
</div>
</div>
</div>
)
}
function ComponentWrapper({
className,
children,
}: React.HTMLAttributes<HTMLDivElement>) {
return (
<div
className={cn(
"flex items-center justify-between space-x-4 rounded-md p-4",
className
)}
>
{children}
</div>
)
}

View File

@@ -1,210 +0,0 @@
import * as React from "react"
import Link from "next/link"
import { cn } from "@/lib/utils"
import AccordionDemo from "@/registry/default/example/accordion-demo"
import AlertDialogDemo from "@/registry/default/example/alert-dialog-demo"
import AspectRatioDemo from "@/registry/default/example/aspect-ratio-demo"
import AvatarDemo from "@/registry/default/example/avatar-demo"
import BadgeDemo from "@/registry/default/example/badge-demo"
import BadgeDestructive from "@/registry/default/example/badge-destructive"
import BadgeOutline from "@/registry/default/example/badge-outline"
import BadgeSecondary from "@/registry/default/example/badge-secondary"
import ButtonDemo from "@/registry/default/example/button-demo"
import ButtonDestructive from "@/registry/default/example/button-destructive"
import ButtonGhost from "@/registry/default/example/button-ghost"
import ButtonLink from "@/registry/default/example/button-link"
import ButtonLoading from "@/registry/default/example/button-loading"
import ButtonOutline from "@/registry/default/example/button-outline"
import ButtonSecondary from "@/registry/default/example/button-secondary"
import ButtonWithIcon from "@/registry/default/example/button-with-icon"
import CardDemo from "@/registry/default/example/card-demo"
import CheckboxDemo from "@/registry/default/example/checkbox-demo"
import CollapsibleDemo from "@/registry/default/example/collapsible-demo"
import CommandDemo from "@/registry/default/example/command-demo"
import ContextMenuDemo from "@/registry/default/example/context-menu-demo"
import DatePickerDemo from "@/registry/default/example/date-picker-demo"
import DialogDemo from "@/registry/default/example/dialog-demo"
import DropdownMenuDemo from "@/registry/default/example/dropdown-menu-demo"
import HoverCardDemo from "@/registry/default/example/hover-card-demo"
import MenubarDemo from "@/registry/default/example/menubar-demo"
import NavigationMenuDemo from "@/registry/default/example/navigation-menu-demo"
import PopoverDemo from "@/registry/default/example/popover-demo"
import ProgressDemo from "@/registry/default/example/progress-demo"
import RadioGroupDemo from "@/registry/default/example/radio-group-demo"
import ScrollAreaDemo from "@/registry/default/example/scroll-area-demo"
import SelectDemo from "@/registry/default/example/select-demo"
import SeparatorDemo from "@/registry/default/example/separator-demo"
import SheetDemo from "@/registry/default/example/sheet-demo"
import SkeletonDemo from "@/registry/default/example/skeleton-demo"
import SliderDemo from "@/registry/default/example/slider-demo"
import SwitchDemo from "@/registry/default/example/switch-demo"
import TabsDemo from "@/registry/default/example/tabs-demo"
import ToastDemo from "@/registry/default/example/toast-demo"
import ToggleDemo from "@/registry/default/example/toggle-demo"
import ToggleDisabled from "@/registry/default/example/toggle-disabled"
import ToggleGroupDemo from "@/registry/default/example/toggle-group-demo"
import ToggleOutline from "@/registry/default/example/toggle-outline"
import ToggleWithText from "@/registry/default/example/toggle-with-text"
import TooltipDemo from "@/registry/default/example/tooltip-demo"
import { Button } from "@/registry/default/ui/button"
export default function KitchenSinkPage() {
return (
<div className="container">
<div className="grid gap-4">
<div className="grid grid-cols-3 items-start gap-4">
<div className="grid gap-4">
<ComponentWrapper>
<CardDemo className="w-full" />
</ComponentWrapper>
<ComponentWrapper>
<SliderDemo className="w-full" />
</ComponentWrapper>
<ComponentWrapper
className="spa flex-col items-start space-x-0
space-y-2"
>
<p className="text-sm text-muted-foreground">Documentation</p>
<p className="text-sm font-medium leading-none">
You can customize the theme using{" "}
<code className="relative rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-sm font-semibold text-foreground">
CSS variables
</code>
.{" "}
<Link
href="#"
className="font-medium text-primary underline underline-offset-4"
>
Click here
</Link>{" "}
to learn more.
</p>
</ComponentWrapper>
<ComponentWrapper>
<CheckboxDemo />
<HoverCardDemo />
</ComponentWrapper>
<ComponentWrapper className="[&>div]:w-full">
<TabsDemo />
</ComponentWrapper>
</div>
<div className="grid gap-4">
<ComponentWrapper>
<MenubarDemo />
<AvatarDemo />
</ComponentWrapper>
<ComponentWrapper className="flex-col items-start space-x-0 space-y-2">
<div className="flex space-x-2">
<ButtonDemo />
<ButtonSecondary />
<ButtonDestructive />
</div>
<div className="flex space-x-2">
<ButtonOutline />
<ButtonLink />
<ButtonGhost />
</div>
<div className="flex space-x-2">
<ButtonWithIcon />
<ButtonLoading />
</div>
<div className="flex space-x-2">
<Button size="lg">Large</Button>
<Button size="sm">Small</Button>
</div>
</ComponentWrapper>
<ComponentWrapper>
<DatePickerDemo />
</ComponentWrapper>
<ComponentWrapper>
<AccordionDemo />
</ComponentWrapper>
<ComponentWrapper className="[&_ul>li:last-child]:hidden">
<NavigationMenuDemo />
</ComponentWrapper>
<ComponentWrapper className="justify-between">
<SwitchDemo />
<SelectDemo />
</ComponentWrapper>
<ComponentWrapper>
<ToggleGroupDemo />
</ComponentWrapper>
<ComponentWrapper>
<SeparatorDemo />
</ComponentWrapper>
<ComponentWrapper>
<AspectRatioDemo />
</ComponentWrapper>
<ComponentWrapper>
<PopoverDemo />
<ToastDemo />
</ComponentWrapper>
</div>
<div className="grid gap-4">
<ComponentWrapper>
<TooltipDemo />
<SheetDemo />
<ProgressDemo />
</ComponentWrapper>
<ComponentWrapper>
<CommandDemo />
</ComponentWrapper>
<ComponentWrapper className="[&>span]:h-[80px] [&>span]:w-[200px]">
<RadioGroupDemo />
<ContextMenuDemo />
</ComponentWrapper>
<ComponentWrapper>
<div className="flex space-x-2">
<DropdownMenuDemo />
<AlertDialogDemo />
<DialogDemo />
</div>
</ComponentWrapper>
<ComponentWrapper>
<div className="flex space-x-2">
<BadgeDemo />
<BadgeSecondary />
<BadgeDestructive />
<BadgeOutline />
</div>
</ComponentWrapper>
<ComponentWrapper>
<SkeletonDemo />
</ComponentWrapper>
<ComponentWrapper className="[&>div]:w-full">
<CollapsibleDemo />
</ComponentWrapper>
<ComponentWrapper>
<div className="flex space-x-2">
<ToggleDemo />
<ToggleOutline />
<ToggleDisabled />
<ToggleWithText />
</div>
</ComponentWrapper>
<ComponentWrapper>
<ScrollAreaDemo />
</ComponentWrapper>
</div>
</div>
</div>
</div>
)
}
function ComponentWrapper({
className,
children,
}: React.HTMLAttributes<HTMLDivElement>) {
return (
<div
className={cn(
"flex items-center justify-between space-x-4 rounded-md p-4",
className
)}
>
{children}
</div>
)
}

View File

@@ -0,0 +1,52 @@
import { Metadata } from "next"
import { Announcement } from "@/components/announcement"
import {
PageActions,
PageHeader,
PageHeaderDescription,
PageHeaderHeading,
} from "@/components/page-header"
import { ThemeCustomizer } from "@/components/theme-customizer"
import { ThemeWrapper } from "@/components/theme-wrapper"
export const metadata: Metadata = {
title: "Themes",
description: "Hand-picked themes that you can copy and paste into your apps.",
}
export default function ThemesLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<div className="relative">
<ThemeWrapper
defaultTheme="zinc"
className="relative flex w-full flex-col items-start md:flex-row"
>
<PageHeader>
<Announcement />
<PageHeaderHeading className="hidden md:block">
Add colors. Make it yours.
</PageHeaderHeading>
<PageHeaderHeading className="md:hidden">
Make it yours
</PageHeaderHeading>
<PageHeaderDescription>
Hand-picked themes that you can copy and paste into your apps.
</PageHeaderDescription>
<PageActions>
<ThemeCustomizer />
</PageActions>
</PageHeader>
</ThemeWrapper>
<div className="container py-6">
<section id="themes" className="scroll-mt-20">
{children}
</section>
</div>
</div>
)
}

View File

@@ -1,46 +1,7 @@
import { Metadata } from "next"
import "public/registry/themes.css"
import { Announcement } from "@/components/announcement"
import {
PageActions,
PageHeader,
PageHeaderDescription,
PageHeaderHeading,
} from "@/components/page-header"
import { ThemeCustomizer } from "@/components/theme-customizer"
import { ThemeWrapper } from "@/components/theme-wrapper"
import { ThemesTabs } from "@/app/(app)/themes/tabs" import { ThemesTabs } from "@/app/(app)/themes/tabs"
export const metadata: Metadata = { import "public/registry/themes.css"
title: "Themes",
description: "Hand-picked themes that you can copy and paste into your apps.",
}
export default function ThemesPage() { export default function ThemesPage() {
return ( return <ThemesTabs />
<div className="container">
<ThemeWrapper
defaultTheme="zinc"
className="relative flex w-full flex-col items-start md:flex-row"
>
<PageHeader className="w-full">
<Announcement />
<PageHeaderHeading className="hidden md:block">
Add colors. Make it yours.
</PageHeaderHeading>
<PageHeaderHeading className="md:hidden">
Make it yours
</PageHeaderHeading>
<PageHeaderDescription>
Hand-picked themes that you can copy and paste into your apps.
</PageHeaderDescription>
<PageActions>
<ThemeCustomizer />
</PageActions>
</PageHeader>
</ThemeWrapper>
<ThemesTabs />
</div>
)
} }

View File

@@ -1,73 +1,14 @@
"use client"
import * as React from "react" import * as React from "react"
import { useConfig } from "@/hooks/use-config"
import { ThemeWrapper } from "@/components/theme-wrapper" import { ThemeWrapper } from "@/components/theme-wrapper"
import CardsDefault from "@/registry/default/example/cards"
import { Skeleton } from "@/registry/default/ui/skeleton"
import CardsNewYork from "@/registry/new-york/example/cards" import CardsNewYork from "@/registry/new-york/example/cards"
export function ThemesTabs() { export function ThemesTabs() {
const [mounted, setMounted] = React.useState(false)
const [config] = useConfig()
React.useEffect(() => {
setMounted(true)
}, [])
return ( return (
<div className="space-y-8"> <div className="space-y-8">
{!mounted ? ( <ThemeWrapper>
<div className="md:grids-col-2 grid md:gap-4 lg:grid-cols-10 xl:gap-6"> <CardsNewYork />
<div className="space-y-4 lg:col-span-4 xl:col-span-6 xl:space-y-6"> </ThemeWrapper>
<Skeleton className="h-[218px] w-full" />
<div className="grid gap-1 sm:grid-cols-[260px_1fr] md:hidden">
<Skeleton className="h-[218px] w-full" />
<div className="pt-3 sm:pl-2 sm:pt-0 xl:pl-4">
<Skeleton className="h-[218px] w-full" />
</div>
<div className="pt-3 sm:col-span-2 xl:pt-4">
<Skeleton className="h-[218px] w-full" />
</div>
</div>
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-1 xl:grid-cols-2">
<div className="space-y-4 xl:space-y-6">
<Skeleton className="h-[218px] w-full" />
<Skeleton className="h-[218px] w-full" />
<Skeleton className="h-[218px] w-full" />
</div>
<div className="space-y-4 xl:space-y-6">
<Skeleton className="h-[218px] w-full" />
<Skeleton className="h-[218px] w-full" />
<div className="hidden xl:block">
<Skeleton className="h-[218px] w-full" />
</div>
</div>
</div>
</div>
<div className="space-y-4 lg:col-span-6 xl:col-span-4 xl:space-y-6">
<div className="hidden gap-1 sm:grid-cols-[260px_1fr] md:grid">
<Skeleton className="h-[218px] w-full" />
<div className="pt-3 sm:pl-2 sm:pt-0 xl:pl-4">
<Skeleton className="h-[218px] w-full" />
</div>
<div className="pt-3 sm:col-span-2 xl:pt-4">
<Skeleton className="h-[218px] w-full" />
</div>
</div>
<div className="hidden md:block">
<Skeleton className="h-[218px] w-full" />
</div>
<Skeleton className="h-[218px] w-full" />
</div>
</div>
) : (
<ThemeWrapper>
{config.style === "new-york" && <CardsNewYork />}
{config.style === "default" && <CardsDefault />}
</ThemeWrapper>
)}
</div> </div>
) )
} }

View File

@@ -1,14 +1,20 @@
import * as React from "react"
import { Metadata } from "next" import { Metadata } from "next"
import { notFound } from "next/navigation" import { notFound } from "next/navigation"
import { siteConfig } from "@/config/site" import { siteConfig } from "@/config/site"
import { getAllBlockIds, getBlock } from "@/lib/blocks" import { getAllBlockIds } from "@/lib/blocks"
import { absoluteUrl, cn } from "@/lib/utils" import { absoluteUrl, cn } from "@/lib/utils"
import { BlockChunk } from "@/components/block-chunk"
import { BlockWrapper } from "@/components/block-wrapper"
import { Style, styles } from "@/registry/registry-styles" import { Style, styles } from "@/registry/registry-styles"
import "@/styles/mdx.css" import "@/styles/mdx.css"
import { getRegistryComponent, getRegistryItem } from "@/lib/registry"
const getCachedRegistryItem = React.cache(
async (name: string, style: Style["name"]) => {
return await getRegistryItem(name, style)
}
)
export async function generateMetadata({ export async function generateMetadata({
params, params,
@@ -19,23 +25,23 @@ export async function generateMetadata({
} }
}): Promise<Metadata> { }): Promise<Metadata> {
const { name, style } = params const { name, style } = params
const block = await getBlock(name, style) const item = await getCachedRegistryItem(name, style)
if (!block) { if (!item) {
return {} return {}
} }
const title = block.name const title = item.name
const description = block.description const description = item.description
return { return {
title: `${block.description} - ${block.name}`, title: `${item.name}${item.description ? ` - ${item.description}` : ""}`,
description, description,
openGraph: { openGraph: {
title, title,
description, description,
type: "article", type: "article",
url: absoluteUrl(`/blocks/${block.name}`), url: absoluteUrl(`/blocks/${style}/${item.name}`),
images: [ images: [
{ {
url: siteConfig.ogImage, url: siteConfig.ogImage,
@@ -76,39 +82,22 @@ export default async function BlockPage({
} }
}) { }) {
const { name, style } = params const { name, style } = params
const block = await getBlock(name, style) const item = await getCachedRegistryItem(name, style)
const Component = getRegistryComponent(name, style)
if (!block) { if (!item || !Component) {
return notFound() return notFound()
} }
const Component = block.component
const chunks = block.chunks?.map((chunk) => ({ ...chunk }))
delete block.component
block.chunks?.map((chunk) => delete chunk.component)
return ( return (
<> <>
{/* <ThemesStyle /> */}
<div <div
className={cn( className={cn(
"themes-wrapper bg-background", "themes-wrapper bg-background",
block.container?.className item.meta?.containerClassName
)} )}
> >
<BlockWrapper block={block}> <Component />
<Component />
{chunks?.map((chunk, index) => (
<BlockChunk
key={chunk.name}
block={block}
chunk={block.chunks?.[index]}
>
<chunk.component />
</BlockChunk>
))}
</BlockWrapper>
</div> </div>
</> </>
) )

View File

@@ -0,0 +1,34 @@
import { Metadata } from "next"
import { Announcement } from "@/components/announcement"
import {
PageHeader,
PageHeaderDescription,
PageHeaderHeading,
} from "@/components/page-header"
export const metadata: Metadata = {
title: "Icons",
description: "All icons in all libraries.",
}
export default function IconsLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<div className="container relative">
<PageHeader>
<Announcement />
<PageHeaderHeading>Icons</PageHeaderHeading>
<PageHeaderDescription>
All icons in all libraries.
</PageHeaderDescription>
</PageHeader>
<section id="icons" className="scroll-mt-20">
{children}
</section>
</div>
)
}

View File

@@ -0,0 +1,52 @@
"use client"
import * as React from "react"
import { Icons } from "@/__registry__/icons"
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/registry/new-york/ui/table"
import { iconLibraries } from "@/registry/registry-icons"
export default function IconsPage() {
return (
<div>
<Table>
<TableHeader>
<TableRow>
<TableHead className="w-[240px]" align="left">
name
</TableHead>
{Object.keys(iconLibraries).map((library) => (
<TableHead key={library}>{library}</TableHead>
))}
</TableRow>
</TableHeader>
<TableBody>
{Object.entries(Icons).map(([name, icon]) => (
<TableRow key={name}>
<TableCell align="left">
<code>{name}</code>
</TableCell>
{Object.entries(iconLibraries).map(([library, name]) => {
const IconComponent = icon[library as keyof typeof icon]
return (
<TableCell key={library} className="[&_svg]:h-4 [&_svg]:w-4">
<React.Suspense fallback={<div>Loading...</div>}>
{IconComponent && <IconComponent />}
</React.Suspense>
</TableCell>
)
})}
</TableRow>
))}
</TableBody>
</Table>
</div>
)
}

View File

@@ -0,0 +1,3 @@
import SinkPage from "@/registry/new-york/block/_sink/page"
export default SinkPage

View File

@@ -1,8 +1,8 @@
import "@/styles/globals.css" import "@/styles/globals.css"
import { Metadata, Viewport } from "next" import { Metadata, Viewport } from "next"
import { siteConfig } from "@/config/site" import { META_THEME_COLORS, siteConfig } from "@/config/site"
import { fontSans } from "@/lib/fonts" import { fontMono, fontSans } from "@/lib/fonts"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
import { Analytics } from "@/components/analytics" import { Analytics } from "@/components/analytics"
import { ThemeProvider } from "@/components/providers" import { ThemeProvider } from "@/components/providers"
@@ -65,10 +65,7 @@ export const metadata: Metadata = {
} }
export const viewport: Viewport = { export const viewport: Viewport = {
themeColor: [ themeColor: META_THEME_COLORS.light,
{ media: "(prefers-color-scheme: light)", color: "white" },
{ media: "(prefers-color-scheme: dark)", color: "black" },
],
} }
interface RootLayoutProps { interface RootLayoutProps {
@@ -79,11 +76,24 @@ export default function RootLayout({ children }: RootLayoutProps) {
return ( return (
<> <>
<html lang="en" suppressHydrationWarning> <html lang="en" suppressHydrationWarning>
<head /> <head>
<script
dangerouslySetInnerHTML={{
__html: `
try {
if (localStorage.theme === 'dark' || ((!('theme' in localStorage) || localStorage.theme === 'system') && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
document.querySelector('meta[name="theme-color"]').setAttribute('content', '${META_THEME_COLORS.dark}')
}
} catch (_) {}
`,
}}
/>
</head>
<body <body
className={cn( className={cn(
"min-h-screen bg-background font-sans antialiased", "min-h-screen bg-background font-sans antialiased",
fontSans.variable fontSans.variable,
fontMono.variable
)} )}
> >
<ThemeProvider <ThemeProvider
@@ -91,6 +101,7 @@ export default function RootLayout({ children }: RootLayoutProps) {
defaultTheme="system" defaultTheme="system"
enableSystem enableSystem
disableTransitionOnChange disableTransitionOnChange
enableColorScheme
> >
<div vaul-drawer-wrapper=""> <div vaul-drawer-wrapper="">
<div className="relative flex min-h-screen flex-col bg-background"> <div className="relative flex min-h-screen flex-col bg-background">

View File

@@ -1,21 +1,16 @@
import Link from "next/link" import Link from "next/link"
import { ArrowRightIcon } from "@radix-ui/react-icons" import { ArrowRight } from "lucide-react"
import { Blocks, PieChart } from "lucide-react"
import { Separator } from "@/registry/new-york/ui/separator"
export function Announcement() { export function Announcement() {
return ( return (
<Link <Link
href="/docs/components/sidebar" href="/docs/components/sidebar"
className="group inline-flex items-center px-0.5 text-sm font-medium" className="group mb-2 inline-flex items-center px-0.5 text-sm font-medium"
> >
<PieChart className="h-4 w-4" />{" "}
<Separator className="mx-2 h-4" orientation="vertical" />{" "}
<span className="underline-offset-4 group-hover:underline"> <span className="underline-offset-4 group-hover:underline">
New sidebar component New sidebar component
</span> </span>
<ArrowRightIcon className="ml-1 h-4 w-4" /> <ArrowRight className="ml-1 h-4 w-4" />
</Link> </Link>
) )
} }

View File

@@ -1,57 +0,0 @@
"use client"
import * as React from "react"
import { AnimatePresence, motion } from "framer-motion"
import { cn } from "@/lib/utils"
import { useLiftMode } from "@/hooks/use-lift-mode"
import { BlockCopyButton } from "@/components/block-copy-button"
import { V0Button } from "@/components/v0-button"
import { Block, type BlockChunk } from "@/registry/schema"
export function BlockChunk({
block,
chunk,
children,
...props
}: React.PropsWithChildren<{ block: Block; chunk?: BlockChunk }>) {
const { isLiftMode } = useLiftMode(block.name)
if (!chunk) {
return null
}
return (
<AnimatePresence>
{isLiftMode && (
<motion.div
key={chunk.name}
x-chunk-container={chunk.name}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0, transition: { ease: "easeOut", duration: 0.2 } }}
transition={{ ease: "easeIn", duration: 0.2 }}
className={cn(
"group rounded-xl bg-background shadow-xl transition",
chunk.container?.className
)}
{...props}
>
<div className="relative z-30">{children}</div>
{chunk.code && (
<div className="absolute inset-x-0 top-0 z-30 flex px-4 py-3 opacity-0 transition-all duration-200 ease-in group-hover:-translate-y-12 group-hover:opacity-100">
<div className="flex w-full items-center justify-end gap-2">
<BlockCopyButton
event="copy_chunk_code"
name={chunk.name}
code={chunk.code}
/>
<V0Button size="icon" name={chunk.name} />
</div>
</div>
)}
</motion.div>
)}
</AnimatePresence>
)
}

View File

@@ -1,32 +1,52 @@
import { unstable_cache } from "next/cache" import * as React from "react"
import { z } from "zod"
import { getBlock } from "@/lib/blocks" import { highlightCode } from "@/lib/highlight-code"
import { BlockPreview } from "@/components/block-preview" import {
createFileTreeForRegistryItemFiles,
const getBlockByName = unstable_cache( getRegistryItem,
async (name: string) => { } from "@/lib/registry"
const block = await getBlock(name) import { BlockViewer } from "@/components/block-viewer"
import { registryItemFileSchema } from "@/registry/schema"
if (!block) {
return null
}
return {
name: block.name,
style: block.style,
description: block.description,
container: block.container,
}
},
["block"]
)
export async function BlockDisplay({ name }: { name: string }) { export async function BlockDisplay({ name }: { name: string }) {
const block = await getBlockByName(name) const item = await getCachedRegistryItem(name)
if (!block) { if (!item?.files) {
return null return null
} }
return <BlockPreview key={block.name} block={block} /> const [tree, highlightedFiles] = await Promise.all([
getCachedFileTree(item.files),
getCachedHighlightedFiles(item.files),
])
return (
<BlockViewer item={item} tree={tree} highlightedFiles={highlightedFiles} />
)
} }
const getCachedRegistryItem = React.cache(async (name: string) => {
return await getRegistryItem(name)
})
const getCachedFileTree = React.cache(
async (files: Array<{ path: string; target?: string }>) => {
if (!files) {
return null
}
return await createFileTreeForRegistryItemFiles(files)
}
)
const getCachedHighlightedFiles = React.cache(
async (files: z.infer<typeof registryItemFileSchema>[]) => {
return await Promise.all(
files.map(async (file) => ({
...file,
highlightedContent: await highlightCode(file.content ?? ""),
}))
)
}
)

View File

@@ -1,67 +0,0 @@
"use client"
import * as React from "react"
import Image from "next/image"
import { ImperativePanelHandle } from "react-resizable-panels"
import { BlockToolbar } from "@/components/block-toolbar"
import {
ResizableHandle,
ResizablePanel,
ResizablePanelGroup,
} from "@/registry/new-york/ui/resizable"
import { type Block } from "@/registry/schema"
export function BlockPreview({
block,
}: {
block: Pick<Block, "name" | "style" | "description" | "container">
}) {
const ref = React.useRef<ImperativePanelHandle>(null)
return (
<div
id={block.name}
className="relative grid w-full scroll-m-20 gap-4"
style={
{
"--container-height": block.container?.height,
} as React.CSSProperties
}
>
<BlockToolbar block={block} resizablePanelRef={ref} />
<ResizablePanelGroup direction="horizontal" className="relative z-10">
<ResizablePanel
ref={ref}
className="relative aspect-[4/2.5] rounded-lg border bg-background md:aspect-auto"
defaultSize={100}
minSize={30}
>
<Image
src={`/images/blocks/${block.name}.png`}
alt={block.name}
data-block={block.name}
width={1440}
height={900}
className="absolute left-0 top-0 z-20 w-[970px] max-w-none bg-background data-[block=sidebar-10]:left-auto data-[block=sidebar-10]:right-0 data-[block=sidebar-11]:-top-1/3 data-[block=sidebar-14]:left-auto data-[block=sidebar-14]:right-0 data-[block=login-01]:max-w-full data-[block=sidebar-13]:max-w-full data-[block=sidebar-15]:max-w-full dark:hidden sm:w-[1280px] md:hidden md:dark:hidden"
/>
<Image
src={`/images/blocks/${block.name}-dark.png`}
alt={block.name}
data-block={block.name}
width={1440}
height={900}
className="absolute left-0 top-0 z-20 hidden w-[970px] max-w-none bg-background data-[block=sidebar-10]:left-auto data-[block=sidebar-10]:right-0 data-[block=sidebar-11]:-top-1/3 data-[block=sidebar-14]:left-auto data-[block=sidebar-14]:right-0 data-[block=login-01]:max-w-full data-[block=sidebar-13]:max-w-full data-[block=sidebar-15]:max-w-full dark:block sm:w-[1280px] md:hidden md:dark:hidden"
/>
<iframe
src={`/blocks/${block.style}/${block.name}`}
height={block.container?.height ?? 450}
className="chunk-mode relative z-20 hidden w-full bg-background md:block"
/>
</ResizablePanel>
<ResizableHandle className="relative hidden w-3 bg-transparent p-0 after:absolute after:right-0 after:top-1/2 after:h-8 after:w-[6px] after:-translate-y-1/2 after:translate-x-[-1px] after:rounded-full after:bg-border after:transition-all after:hover:h-10 sm:block" />
<ResizablePanel defaultSize={0} minSize={0} />
</ResizablePanelGroup>
</div>
)
}

View File

@@ -1,110 +0,0 @@
"use client"
import * as React from "react"
import Link from "next/link"
import {
Check,
Fullscreen,
Monitor,
Smartphone,
Tablet,
Terminal,
} from "lucide-react"
import { ImperativePanelHandle } from "react-resizable-panels"
import { useCopyToClipboard } from "@/hooks/use-copy-to-clipboard"
import { V0Button } from "@/components/v0-button"
import { Button } from "@/registry/new-york/ui/button"
import { Separator } from "@/registry/new-york/ui/separator"
import {
ToggleGroup,
ToggleGroupItem,
} from "@/registry/new-york/ui/toggle-group"
import { Block } from "@/registry/schema"
export function BlockToolbar({
block,
resizablePanelRef,
}: {
block: Pick<Block, "name" | "style" | "description" | "container">
resizablePanelRef: React.RefObject<ImperativePanelHandle>
}) {
const { copyToClipboard, isCopied } = useCopyToClipboard()
return (
<div className="flex items-center gap-2 md:gap-4">
<Button asChild variant="link" className="whitespace-normal px-1 md:px-2">
<a href={`#${block.name}`}>{block.description}</a>
</Button>
<div className="ml-auto hidden items-center gap-2 md:flex md:pr-[14px]">
<Button
variant="ghost"
className="h-7 rounded-md border bg-muted shadow-none"
size="sm"
onClick={() => {
copyToClipboard(`npx shadcn@latest add ${block.name}`)
}}
>
{isCopied ? <Check /> : <Terminal />}
npx shadcn add {block.name}
</Button>
<Separator orientation="vertical" className="mx-2 hidden h-4 md:flex" />
<div className="hidden h-7 items-center gap-1.5 rounded-md border p-[2px] shadow-none md:flex">
<ToggleGroup
type="single"
defaultValue="100"
onValueChange={(value) => {
if (resizablePanelRef.current) {
resizablePanelRef.current.resize(parseInt(value))
}
}}
>
<ToggleGroupItem
value="100"
className="h-[22px] w-[22px] rounded-sm p-0"
title="Desktop"
>
<Monitor className="h-3.5 w-3.5" />
</ToggleGroupItem>
<ToggleGroupItem
value="60"
className="h-[22px] w-[22px] rounded-sm p-0"
title="Tablet"
>
<Tablet className="h-3.5 w-3.5" />
</ToggleGroupItem>
<ToggleGroupItem
value="30"
className="h-[22px] w-[22px] rounded-sm p-0"
title="Mobile"
>
<Smartphone className="h-3.5 w-3.5" />
</ToggleGroupItem>
<Separator orientation="vertical" className="h-4" />
<Button
size="icon"
variant="ghost"
className="h-[22px] w-[22px] rounded-sm p-0"
asChild
title="Open in New Tab"
>
<Link
href={`/blocks/${block.style}/${block.name}`}
target="_blank"
>
<span className="sr-only">Open in New Tab</span>
<Fullscreen className="h-3.5 w-3.5" />
</Link>
</Button>
</ToggleGroup>
</div>
<Separator orientation="vertical" className="mx-2 hidden h-4 md:flex" />
<V0Button
className="hidden shadow-none md:flex"
id={`v0-button-${block.name}`}
name={`v0-${block.name}`}
/>
</div>
</div>
)
}

View File

@@ -0,0 +1,452 @@
"use client"
import * as React from "react"
import Image from "next/image"
import Link from "next/link"
import {
Check,
ChevronRight,
Clipboard,
File,
Folder,
Fullscreen,
Monitor,
Smartphone,
Tablet,
Terminal,
} from "lucide-react"
import { ImperativePanelHandle } from "react-resizable-panels"
import { z } from "zod"
import { trackEvent } from "@/lib/events"
import { FileTree, createFileTreeForRegistryItemFiles } from "@/lib/registry"
import { useCopyToClipboard } from "@/hooks/use-copy-to-clipboard"
import { V0Button } from "@/components/v0-button"
import { Button } from "@/registry/new-york/ui/button"
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "@/registry/new-york/ui/collapsible"
import {
ResizableHandle,
ResizablePanel,
ResizablePanelGroup,
} from "@/registry/new-york/ui/resizable"
import { Separator } from "@/registry/new-york/ui/separator"
import {
Sidebar,
SidebarGroup,
SidebarGroupContent,
SidebarGroupLabel,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
SidebarMenuSub,
SidebarProvider,
} from "@/registry/new-york/ui/sidebar"
import { Tabs, TabsList, TabsTrigger } from "@/registry/new-york/ui/tabs"
import {
ToggleGroup,
ToggleGroupItem,
} from "@/registry/new-york/ui/toggle-group"
import { Style } from "@/registry/registry-styles"
import { registryEntrySchema, registryItemFileSchema } from "@/registry/schema"
type BlockViewerContext = {
item: z.infer<typeof registryEntrySchema>
view: "code" | "preview"
setView: (view: "code" | "preview") => void
style?: Style["name"]
setStyle: (style: Style["name"]) => void
activeFile: string | null
setActiveFile: (file: string) => void
resizablePanelRef: React.RefObject<ImperativePanelHandle> | null
tree: ReturnType<typeof createFileTreeForRegistryItemFiles> | null
highlightedFiles:
| (z.infer<typeof registryItemFileSchema> & {
highlightedContent: string
})[]
| null
}
const BlockViewerContext = React.createContext<BlockViewerContext | null>(null)
function useBlockViewer() {
const context = React.useContext(BlockViewerContext)
if (!context) {
throw new Error("useBlockViewer must be used within a BlockViewerProvider.")
}
return context
}
function BlockViewerProvider({
item,
tree,
highlightedFiles,
children,
}: Pick<BlockViewerContext, "item" | "tree" | "highlightedFiles"> & {
children: React.ReactNode
}) {
const [view, setView] = React.useState<BlockViewerContext["view"]>("preview")
const [style, setStyle] =
React.useState<BlockViewerContext["style"]>("new-york")
const [activeFile, setActiveFile] = React.useState<
BlockViewerContext["activeFile"]
>(highlightedFiles?.[0].target ?? null)
const resizablePanelRef = React.useRef<ImperativePanelHandle>(null)
return (
<BlockViewerContext.Provider
value={{
item,
view,
setView,
style,
setStyle,
resizablePanelRef,
activeFile,
setActiveFile,
tree,
highlightedFiles,
}}
>
<div
id={item.name}
data-view={view}
className="group/block-view-wrapper flex min-w-0 flex-col items-stretch gap-4"
style={
{
"--height": item.meta?.iframeHeight ?? 450,
} as React.CSSProperties
}
>
{children}
</div>
</BlockViewerContext.Provider>
)
}
function BlockViewerToolbar() {
const { setView, item, resizablePanelRef, style } = useBlockViewer()
const { copyToClipboard, isCopied } = useCopyToClipboard()
return (
<div className="flex w-full items-center gap-2 md:pr-[14px]">
<Tabs
defaultValue="preview"
onValueChange={(value) => setView(value as "preview" | "code")}
className="hidden lg:flex"
>
<TabsList className="h-7 items-center rounded-md p-0 px-[calc(theme(spacing.1)_-_2px)] py-[theme(spacing.1)]">
<TabsTrigger
value="preview"
className="h-[1.45rem] rounded-sm px-2 text-xs"
>
Preview
</TabsTrigger>
<TabsTrigger
value="code"
className="h-[1.45rem] rounded-sm px-2 text-xs"
>
Code
</TabsTrigger>
</TabsList>
</Tabs>
<Separator orientation="vertical" className="mx-2 hidden h-4 lg:flex" />
<a
href={`#${item.name}`}
className="text-sm font-medium underline-offset-2 hover:underline"
>
{item.description}
</a>
<div className="ml-auto flex items-center gap-2">
<Button
variant="ghost"
className="hidden h-7 w-7 rounded-md border bg-transparent shadow-none md:flex lg:w-auto"
size="sm"
onClick={() => {
copyToClipboard(`npx shadcn@latest add ${item.name}`)
}}
>
{isCopied ? <Check /> : <Terminal />}
<span className="hidden lg:inline">npx shadcn add {item.name}</span>
</Button>
<Separator orientation="vertical" className="mx-2 hidden h-4 md:flex" />
<div className="hidden h-7 items-center gap-1.5 rounded-md border p-[2px] shadow-none lg:flex">
<ToggleGroup
type="single"
defaultValue="100"
onValueChange={(value) => {
if (resizablePanelRef?.current) {
resizablePanelRef.current.resize(parseInt(value))
}
}}
>
<ToggleGroupItem
value="100"
className="h-[22px] w-[22px] rounded-sm p-0"
title="Desktop"
>
<Monitor className="h-3.5 w-3.5" />
</ToggleGroupItem>
<ToggleGroupItem
value="60"
className="h-[22px] w-[22px] rounded-sm p-0"
title="Tablet"
>
<Tablet className="h-3.5 w-3.5" />
</ToggleGroupItem>
<ToggleGroupItem
value="30"
className="h-[22px] w-[22px] rounded-sm p-0"
title="Mobile"
>
<Smartphone className="h-3.5 w-3.5" />
</ToggleGroupItem>
<Separator orientation="vertical" className="h-4" />
<Button
size="icon"
variant="ghost"
className="h-[22px] w-[22px] rounded-sm p-0"
asChild
title="Open in New Tab"
>
<Link href={`/blocks/${style}/${item.name}`} target="_blank">
<span className="sr-only">Open in New Tab</span>
<Fullscreen className="h-3.5 w-3.5" />
</Link>
</Button>
</ToggleGroup>
</div>
<Separator orientation="vertical" className="mx-2 hidden h-4 xl:flex" />
<V0Button
className="hidden shadow-none sm:flex"
id={`v0-button-${item.name}`}
name={`v0-${item.name}`}
/>
</div>
</div>
)
}
function BlockViewerView() {
const { item, style, resizablePanelRef } = useBlockViewer()
return (
<div className="h-[--height] group-data-[view=code]/block-view-wrapper:hidden">
<div className="grid w-full gap-4">
<ResizablePanelGroup direction="horizontal" className="relative z-10">
<ResizablePanel
ref={resizablePanelRef}
className="relative aspect-[4/2.5] rounded-xl border bg-background md:aspect-auto"
defaultSize={100}
minSize={30}
>
<Image
src={`/images/blocks/${item.name}.png`}
alt={item.name}
data-block={item.name}
width={1440}
height={900}
className="absolute left-0 top-0 z-20 w-[970px] max-w-none bg-background data-[block=sidebar-10]:left-auto data-[block=sidebar-10]:right-0 data-[block=sidebar-11]:-top-1/3 data-[block=sidebar-14]:left-auto data-[block=sidebar-14]:right-0 data-[block=login-01]:max-w-full data-[block=sidebar-13]:max-w-full data-[block=sidebar-15]:max-w-full dark:hidden sm:w-[1280px] md:hidden md:dark:hidden"
/>
<Image
src={`/images/blocks/${item.name}-dark.png`}
alt={item.name}
data-block={item.name}
width={1440}
height={900}
className="absolute left-0 top-0 z-20 hidden w-[970px] max-w-none bg-background data-[block=sidebar-10]:left-auto data-[block=sidebar-10]:right-0 data-[block=sidebar-11]:-top-1/3 data-[block=sidebar-14]:left-auto data-[block=sidebar-14]:right-0 data-[block=login-01]:max-w-full data-[block=sidebar-13]:max-w-full data-[block=sidebar-15]:max-w-full dark:block sm:w-[1280px] md:hidden md:dark:hidden"
/>
<iframe
src={`/blocks/${style}/${item.name}`}
height={item.meta?.iframeHeight ?? 450}
className="chunk-mode relative z-20 hidden w-full bg-background md:block"
/>
</ResizablePanel>
<ResizableHandle className="relative hidden w-3 bg-transparent p-0 after:absolute after:right-0 after:top-1/2 after:h-8 after:w-[6px] after:-translate-y-1/2 after:translate-x-[-1px] after:rounded-full after:bg-border after:transition-all after:hover:h-10 md:block" />
<ResizablePanel defaultSize={0} minSize={0} />
</ResizablePanelGroup>
</div>
</div>
)
}
function BlockViewerCode() {
const { activeFile, highlightedFiles } = useBlockViewer()
const file = React.useMemo(() => {
return highlightedFiles?.find((file) => file.target === activeFile)
}, [highlightedFiles, activeFile])
if (!file) {
return null
}
return (
<div className="mr-[14px] flex h-[--height] overflow-hidden rounded-xl bg-zinc-950 text-white group-data-[view=preview]/block-view-wrapper:hidden">
<div className="w-[280px]">
<BlockViewerFileTree />
</div>
<div className="flex min-w-0 flex-1 flex-col">
<div className="flex h-12 items-center gap-2 border-b border-zinc-700 bg-zinc-900 px-4 text-sm font-medium">
<File className="size-4" />
{file.target}
<div className="ml-auto flex items-center gap-2">
<BlockCopyCodeButton />
</div>
</div>
<div
key={file?.path}
data-rehype-pretty-code-fragment
dangerouslySetInnerHTML={{ __html: file?.highlightedContent ?? "" }}
className="relative flex-1 overflow-hidden after:absolute after:inset-y-0 after:left-0 after:w-10 after:bg-zinc-950 [&_.line:before]:sticky [&_.line:before]:left-2 [&_.line:before]:z-10 [&_.line:before]:translate-y-[-1px] [&_.line:before]:pr-1 [&_pre]:h-[--height] [&_pre]:overflow-auto [&_pre]:!bg-transparent [&_pre]:pb-20 [&_pre]:pt-4 [&_pre]:font-mono [&_pre]:text-sm [&_pre]:leading-relaxed"
/>
</div>
</div>
)
}
export function BlockViewerFileTree() {
const { tree } = useBlockViewer()
if (!tree) {
return null
}
return (
<SidebarProvider className="flex !min-h-full flex-col">
<Sidebar
collapsible="none"
className="w-full flex-1 border-r border-zinc-700 bg-zinc-900 text-white"
>
<SidebarGroupLabel className="h-12 rounded-none border-b border-zinc-700 px-4 text-sm text-white">
Files
</SidebarGroupLabel>
<SidebarGroup className="p-0">
<SidebarGroupContent>
<SidebarMenu className="gap-1.5">
{tree.map((file, index) => (
<Tree key={index} item={file} index={1} />
))}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
</Sidebar>
</SidebarProvider>
)
}
function Tree({ item, index }: { item: FileTree; index: number }) {
const { activeFile, setActiveFile } = useBlockViewer()
if (!item.children) {
return (
<SidebarMenuItem>
<SidebarMenuButton
isActive={item.path === activeFile}
onClick={() => item.path && setActiveFile(item.path)}
className="whitespace-nowrap rounded-none pl-[--index] hover:bg-zinc-700 hover:text-white focus:bg-zinc-700 focus:text-white focus-visible:bg-zinc-700 focus-visible:text-white active:bg-zinc-700 active:text-white data-[active=true]:bg-zinc-700 data-[active=true]:text-white"
data-index={index}
style={
{
"--index": `${index * (index === 2 ? 1.2 : 1.3)}rem`,
} as React.CSSProperties
}
>
<ChevronRight className="invisible" />
<File className="h-4 w-4" />
{item.name}
</SidebarMenuButton>
</SidebarMenuItem>
)
}
return (
<SidebarMenuItem>
<Collapsible
className="group/collapsible [&[data-state=open]>button>svg:first-child]:rotate-90"
defaultOpen
>
<CollapsibleTrigger asChild>
<SidebarMenuButton
className="whitespace-nowrap rounded-none pl-[--index] hover:bg-zinc-700 hover:text-white focus-visible:bg-zinc-700 focus-visible:text-white active:bg-zinc-700 active:text-white data-[active=true]:bg-zinc-700 data-[active=true]:text-white data-[state=open]:hover:bg-zinc-700 data-[state=open]:hover:text-white"
style={
{
"--index": `${index * (index === 1 ? 1 : 1.2)}rem`,
} as React.CSSProperties
}
>
<ChevronRight className="h-4 w-4 transition-transform" />
<Folder className="h-4 w-4" />
{item.name}
</SidebarMenuButton>
</CollapsibleTrigger>
<CollapsibleContent>
<SidebarMenuSub className="m-0 w-full border-none p-0">
{item.children.map((subItem, key) => (
<Tree key={key} item={subItem} index={index + 1} />
))}
</SidebarMenuSub>
</CollapsibleContent>
</Collapsible>
</SidebarMenuItem>
)
}
function BlockCopyCodeButton() {
const { activeFile, item } = useBlockViewer()
const { copyToClipboard, isCopied } = useCopyToClipboard()
const file = React.useMemo(() => {
return item.files?.find((file) => file.target === activeFile)
}, [activeFile, item.files])
const content = file?.content
if (!content) {
return null
}
return (
<Button
onClick={() => {
copyToClipboard(content)
trackEvent({
name: "copy_block_code",
properties: {
name: item.name,
file: file.path,
},
})
}}
className="h-7 w-7 shrink-0 rounded-lg p-0 hover:bg-zinc-700 hover:text-white focus:bg-zinc-700 focus:text-white focus-visible:bg-zinc-700 focus-visible:text-white active:bg-zinc-700 active:text-white data-[active=true]:bg-zinc-700 data-[active=true]:text-white [&>svg]:size-3"
variant="ghost"
>
{isCopied ? <Check /> : <Clipboard />}
</Button>
)
}
function BlockViewer({
item,
tree,
highlightedFiles,
...props
}: Pick<BlockViewerContext, "item" | "tree" | "highlightedFiles">) {
return (
<BlockViewerProvider
item={item}
tree={tree}
highlightedFiles={highlightedFiles}
{...props}
>
<BlockViewerToolbar />
<BlockViewerView />
<BlockViewerCode />
</BlockViewerProvider>
)
}
export { BlockViewer }

View File

@@ -1,69 +0,0 @@
"use client"
import * as React from "react"
import { AnimatePresence, motion } from "framer-motion"
import { useLiftMode } from "@/hooks/use-lift-mode"
import { Block } from "@/registry/schema"
export function BlockWrapper({
block,
children,
}: React.PropsWithChildren<{ block: Block }>) {
const { isLiftMode } = useLiftMode(block.name)
React.useEffect(() => {
const components = document.querySelectorAll("[x-chunk]")
block.chunks?.map((chunk, index) => {
const $chunk = document.querySelector<HTMLElement>(
`[x-chunk="${chunk.name}"]`
)
const $wrapper = document.querySelector<HTMLElement>(
`[x-chunk-container="${chunk.name}"]`
)
const $component = components[index]
if (!$chunk || !$component) {
return
}
const position = $component.getBoundingClientRect()
$chunk.style.zIndex = "40"
// $chunk.style.position = "absolute"
// $chunk.style.top = `${position.top}px`
// $chunk.style.left = `${position.left}px`
$chunk.style.width = `${position.width}px`
$chunk.style.height = `${position.height}px`
if ($wrapper) {
$wrapper.style.zIndex = "40"
$wrapper.style.position = "absolute"
$wrapper.style.top = `${position.top}px`
$wrapper.style.left = `${position.left}px`
$wrapper.style.width = `${position.width}px`
$wrapper.style.height = `${position.height}px`
}
})
}, [block.chunks, isLiftMode])
return (
<>
{children}
<AnimatePresence>
{isLiftMode && (
<motion.div
className="absolute inset-0 z-30 bg-background/90 fill-mode-backwards"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{
opacity: 0,
transition: { ease: "easeOut", duration: 0.38 },
}}
transition={{ ease: "easeOut", duration: 0.2, delay: 0.18 }}
/>
)}
</AnimatePresence>
</>
)
}

View File

@@ -3,7 +3,8 @@ import * as React from "react"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
import { useMediaQuery } from "@/hooks/use-media-query" import { useMediaQuery } from "@/hooks/use-media-query"
import { useThemesConfig } from "@/hooks/use-themes-config" import { useThemesConfig } from "@/hooks/use-themes-config"
import { BlockCopyButton } from "@/components/block-copy-button" import { ChartCopyButton } from "@/components/chart-copy-button"
import { Chart } from "@/components/chart-display"
import { V0Button } from "@/components/v0-button" import { V0Button } from "@/components/v0-button"
import { Button } from "@/registry/new-york/ui/button" import { Button } from "@/registry/new-york/ui/button"
import { import {
@@ -18,13 +19,14 @@ import {
TabsList, TabsList,
TabsTrigger, TabsTrigger,
} from "@/registry/new-york/ui/tabs" } from "@/registry/new-york/ui/tabs"
import { Block } from "@/registry/schema"
export function ChartCodeViewer({ export function ChartCodeViewer({
chart, chart,
className, className,
children, children,
}: { chart: Block } & React.ComponentProps<"div">) { }: {
chart: Chart
} & React.ComponentProps<"div">) {
const [tab, setTab] = React.useState("code") const [tab, setTab] = React.useState("code")
const { themesConfig } = useThemesConfig() const { themesConfig } = useThemesConfig()
const isDesktop = useMediaQuery("(min-width: 768px)") const isDesktop = useMediaQuery("(min-width: 768px)")
@@ -85,10 +87,10 @@ ${Object.entries(themesConfig?.activeTheme.cssVars.dark || {})
</TabsList> </TabsList>
{tab === "code" && ( {tab === "code" && (
<div className="ml-auto flex items-center justify-center gap-2"> <div className="ml-auto flex items-center justify-center gap-2">
<BlockCopyButton <ChartCopyButton
event="copy_chart_code" event="copy_chart_code"
name={chart.name} name={chart.name}
code={chart.code} code={chart.files?.[0]?.content ?? ""}
/> />
<V0Button <V0Button
id={`v0-button-${chart.name}`} id={`v0-button-${chart.name}`}
@@ -98,7 +100,7 @@ ${Object.entries(themesConfig?.activeTheme.cssVars.dark || {})
</div> </div>
)} )}
{tab === "theme" && ( {tab === "theme" && (
<BlockCopyButton <ChartCopyButton
event="copy_chart_theme" event="copy_chart_theme"
name={chart.name} name={chart.name}
code={themeCode} code={themeCode}

View File

@@ -12,7 +12,7 @@ import {
TooltipTrigger, TooltipTrigger,
} from "@/registry/new-york/ui/tooltip" } from "@/registry/new-york/ui/tooltip"
export function BlockCopyButton({ export function ChartCopyButton({
event, event,
name, name,
code, code,

View File

@@ -1,19 +1,27 @@
import { getBlock } from "@/lib/blocks" import * as React from "react"
import { z } from "zod"
import { highlightCode } from "@/lib/highlight-code"
import { getRegistryItem } from "@/lib/registry"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
import { ChartToolbar } from "@/components/chart-toolbar" import { ChartToolbar } from "@/components/chart-toolbar"
import { registryEntrySchema } from "@/registry/schema"
export type Chart = z.infer<typeof registryEntrySchema> & {
highlightedCode: string
}
export async function ChartDisplay({ export async function ChartDisplay({
name, name,
children, children,
className, className,
}: { name: string } & React.ComponentProps<"div">) { }: { name: string } & React.ComponentProps<"div">) {
const chart = await getBlock(name) const chart = await getCachedRegistryItem(name)
const highlightedCode = await getChartHighlightedCode(
chart?.files?.[0]?.content ?? ""
)
// Cannot (and don't need to) pass to the client. if (!chart || !highlightedCode) {
delete chart?.component
delete chart?.chunks
if (!chart) {
return null return null
} }
@@ -25,7 +33,7 @@ export async function ChartDisplay({
)} )}
> >
<ChartToolbar <ChartToolbar
chart={chart} chart={{ ...chart, highlightedCode }}
className="relative z-20 flex justify-end border-b bg-card px-3 py-2.5 text-card-foreground" className="relative z-20 flex justify-end border-b bg-card px-3 py-2.5 text-card-foreground"
> >
{children} {children}
@@ -36,3 +44,11 @@ export async function ChartDisplay({
</div> </div>
) )
} }
const getCachedRegistryItem = React.cache(async (name: string) => {
return await getRegistryItem(name)
})
const getChartHighlightedCode = React.cache(async (content: string) => {
return await highlightCode(content)
})

View File

@@ -1,10 +1,8 @@
"use client" "use client"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
import { BlockCopyButton } from "@/components/block-copy-button"
import { ChartCodeViewer } from "@/components/chart-code-viewer" import { ChartCodeViewer } from "@/components/chart-code-viewer"
import { Separator } from "@/registry/new-york/ui/separator" import { Separator } from "@/registry/new-york/ui/separator"
import { Block } from "@/registry/schema"
import "@/styles/mdx.css" import "@/styles/mdx.css"
import { import {
@@ -17,21 +15,26 @@ import {
Radar, Radar,
} from "lucide-react" } from "lucide-react"
import { ChartCopyButton } from "@/components/chart-copy-button"
import { Chart } from "@/components/chart-display"
export function ChartToolbar({ export function ChartToolbar({
chart, chart,
className, className,
children, children,
}: { chart: Block } & React.ComponentProps<"div">) { }: {
chart: Chart
} & React.ComponentProps<"div">) {
return ( return (
<div className={cn("flex items-center gap-2", className)}> <div className={cn("flex items-center gap-2", className)}>
<div className="flex items-center gap-1.5 pl-1 text-[13px] text-muted-foreground [&>svg]:h-[0.9rem] [&>svg]:w-[0.9rem]"> <div className="flex items-center gap-1.5 pl-1 text-[13px] text-muted-foreground [&>svg]:h-[0.9rem] [&>svg]:w-[0.9rem]">
<ChartTitle chart={chart} /> <ChartTitle chart={chart} />
</div> </div>
<div className="ml-auto flex items-center gap-2 [&>form]:flex"> <div className="ml-auto flex items-center gap-2 [&>form]:flex">
<BlockCopyButton <ChartCopyButton
event="copy_chart_code" event="copy_chart_code"
name={chart.name} name={chart.name}
code={chart.code} code={chart.files?.[0]?.content ?? ""}
className="[&_svg]-h-3 h-6 w-6 rounded-[6px] bg-transparent text-foreground shadow-none hover:bg-muted dark:text-foreground [&_svg]:w-3" className="[&_svg]-h-3 h-6 w-6 rounded-[6px] bg-transparent text-foreground shadow-none hover:bg-muted dark:text-foreground [&_svg]:w-3"
/> />
<Separator orientation="vertical" className="mx-0 hidden h-4 md:flex" /> <Separator orientation="vertical" className="mx-0 hidden h-4 md:flex" />
@@ -41,7 +44,7 @@ export function ChartToolbar({
) )
} }
function ChartTitle({ chart }: { chart: Block }) { function ChartTitle({ chart }: { chart: Chart }) {
const { subcategory } = chart const { subcategory } = chart
if (!subcategory) { if (!subcategory) {

View File

@@ -7,7 +7,6 @@ import { type Color } from "@/lib/colors"
import { trackEvent } from "@/lib/events" import { trackEvent } from "@/lib/events"
import { useColors } from "@/hooks/use-colors" import { useColors } from "@/hooks/use-colors"
import { useCopyToClipboard } from "@/hooks/use-copy-to-clipboard" import { useCopyToClipboard } from "@/hooks/use-copy-to-clipboard"
import { copyToClipboardWithMeta } from "@/components/copy-button"
export function Color({ color }: { color: Color }) { export function Color({ color }: { color: Color }) {
const { format } = useColors() const { format } = useColors()

View File

@@ -3,13 +3,7 @@
import * as React from "react" import * as React from "react"
import { useRouter } from "next/navigation" import { useRouter } from "next/navigation"
import { type DialogProps } from "@radix-ui/react-dialog" import { type DialogProps } from "@radix-ui/react-dialog"
import { import { Circle, File, Laptop, Moon, Sun } from "lucide-react"
CircleIcon,
FileIcon,
LaptopIcon,
MoonIcon,
SunIcon,
} from "@radix-ui/react-icons"
import { useTheme } from "next-themes" import { useTheme } from "next-themes"
import { docsConfig } from "@/config/docs" import { docsConfig } from "@/config/docs"
@@ -61,7 +55,7 @@ export function CommandMenu({ ...props }: DialogProps) {
<Button <Button
variant="outline" variant="outline"
className={cn( className={cn(
"relative h-8 w-full justify-start rounded-[0.5rem] bg-muted/50 text-sm font-normal text-muted-foreground shadow-none sm:pr-12 md:w-40 lg:w-64" "relative h-8 w-full justify-start rounded-[0.5rem] bg-muted/50 text-sm font-normal text-muted-foreground shadow-none sm:pr-12 md:w-40 lg:w-56 xl:w-64"
)} )}
onClick={() => setOpen(true)} onClick={() => setOpen(true)}
{...props} {...props}
@@ -87,7 +81,7 @@ export function CommandMenu({ ...props }: DialogProps) {
runCommand(() => router.push(navItem.href as string)) runCommand(() => router.push(navItem.href as string))
}} }}
> >
<FileIcon className="mr-2 h-4 w-4" /> <File />
{navItem.title} {navItem.title}
</CommandItem> </CommandItem>
))} ))}
@@ -103,7 +97,7 @@ export function CommandMenu({ ...props }: DialogProps) {
}} }}
> >
<div className="mr-2 flex h-4 w-4 items-center justify-center"> <div className="mr-2 flex h-4 w-4 items-center justify-center">
<CircleIcon className="h-3 w-3" /> <Circle className="h-3 w-3" />
</div> </div>
{navItem.title} {navItem.title}
</CommandItem> </CommandItem>
@@ -113,15 +107,15 @@ export function CommandMenu({ ...props }: DialogProps) {
<CommandSeparator /> <CommandSeparator />
<CommandGroup heading="Theme"> <CommandGroup heading="Theme">
<CommandItem onSelect={() => runCommand(() => setTheme("light"))}> <CommandItem onSelect={() => runCommand(() => setTheme("light"))}>
<SunIcon className="mr-2 h-4 w-4" /> <Sun />
Light Light
</CommandItem> </CommandItem>
<CommandItem onSelect={() => runCommand(() => setTheme("dark"))}> <CommandItem onSelect={() => runCommand(() => setTheme("dark"))}>
<MoonIcon className="mr-2 h-4 w-4" /> <Moon />
Dark Dark
</CommandItem> </CommandItem>
<CommandItem onSelect={() => runCommand(() => setTheme("system"))}> <CommandItem onSelect={() => runCommand(() => setTheme("system"))}>
<LaptopIcon className="mr-2 h-4 w-4" /> <Laptop />
System System
</CommandItem> </CommandItem>
</CommandGroup> </CommandGroup>

View File

@@ -94,10 +94,7 @@ export function ComponentPreview({
className="absolute left-0 top-0 z-20 hidden w-[970px] max-w-none bg-background dark:block sm:w-[1280px] md:hidden md:dark:hidden" className="absolute left-0 top-0 z-20 hidden w-[970px] max-w-none bg-background dark:block sm:w-[1280px] md:hidden md:dark:hidden"
/> />
<div className="absolute inset-0 hidden w-[1600px] bg-background md:block"> <div className="absolute inset-0 hidden w-[1600px] bg-background md:block">
<iframe <iframe src={`/blocks/new-york/${name}`} className="size-full" />
src={`/blocks/${config.style}/${name}`}
className="size-full"
/>
</div> </div>
</div> </div>
) )

View File

@@ -2,12 +2,17 @@
import Link from "next/link" import Link from "next/link"
import { usePathname } from "next/navigation" import { usePathname } from "next/navigation"
import { ArrowRightIcon } from "@radix-ui/react-icons" import { ArrowRight } from "lucide-react"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
import { ScrollArea, ScrollBar } from "@/registry/new-york/ui/scroll-area" import { ScrollArea, ScrollBar } from "@/registry/new-york/ui/scroll-area"
const examples = [ const examples = [
{
name: "Examples",
href: "/examples/cards",
code: "https://github.com/shadcn/ui/tree/main/apps/www/app/(app)/examples/mail",
},
{ {
name: "Mail", name: "Mail",
href: "/examples/mail", href: "/examples/mail",
@@ -18,11 +23,6 @@ const examples = [
href: "/examples/dashboard", href: "/examples/dashboard",
code: "https://github.com/shadcn/ui/tree/main/apps/www/app/(app)/examples/dashboard", code: "https://github.com/shadcn/ui/tree/main/apps/www/app/(app)/examples/dashboard",
}, },
{
name: "Cards",
href: "/examples/cards",
code: "https://github.com/shadcn/ui/tree/main/apps/www/app/(app)/examples/cards",
},
{ {
name: "Tasks", name: "Tasks",
href: "/examples/tasks", href: "/examples/tasks",
@@ -59,14 +59,13 @@ export function ExamplesNav({ className, ...props }: ExamplesNavProps) {
<div className="relative"> <div className="relative">
<ScrollArea className="max-w-[600px] lg:max-w-none"> <ScrollArea className="max-w-[600px] lg:max-w-none">
<div className={cn("mb-4 flex items-center", className)} {...props}> <div className={cn("mb-4 flex items-center", className)} {...props}>
{examples.map((example, index) => ( {examples.map((example) => (
<Link <Link
href={example.href} href={example.href}
key={example.href} key={example.href}
className={cn( className={cn(
"flex h-7 items-center justify-center rounded-full px-4 text-center text-sm transition-colors hover:text-primary", "flex h-7 items-center justify-center rounded-full px-4 text-center text-sm transition-colors hover:text-primary",
pathname?.startsWith(example.href) || pathname?.startsWith(example.href)
(index === 0 && pathname === "/")
? "bg-muted font-medium text-primary" ? "bg-muted font-medium text-primary"
: "text-muted-foreground" : "text-muted-foreground"
)} )}
@@ -100,7 +99,7 @@ export function ExampleCodeLink({ pathname }: ExampleCodeLinkProps) {
className="absolute right-0 top-0 hidden items-center rounded-[0.5rem] text-sm font-medium md:flex" className="absolute right-0 top-0 hidden items-center rounded-[0.5rem] text-sm font-medium md:flex"
> >
View code View code
<ArrowRightIcon className="ml-1 h-4 w-4" /> <ArrowRight className="ml-1 h-4 w-4" />
</Link> </Link>
) )
} }

View File

@@ -12,18 +12,18 @@ export function MainNav() {
return ( return (
<div className="mr-4 hidden md:flex"> <div className="mr-4 hidden md:flex">
<Link href="/" className="mr-4 flex items-center space-x-2 lg:mr-6"> <Link href="/" className="mr-4 flex items-center gap-2 lg:mr-6">
<Icons.logo className="h-6 w-6" /> <Icons.logo className="h-6 w-6" />
<span className="hidden font-bold lg:inline-block"> <span className="hidden font-bold lg:inline-block">
{siteConfig.name} {siteConfig.name}
</span> </span>
</Link> </Link>
<nav className="flex items-center gap-4 text-sm lg:gap-6"> <nav className="flex items-center gap-4 text-sm xl:gap-6">
<Link <Link
href="/docs" href="/docs"
className={cn( className={cn(
"transition-colors hover:text-foreground/80", "transition-colors hover:text-foreground/80",
pathname === "/docs" ? "text-foreground" : "text-foreground/60" pathname === "/docs" ? "text-foreground" : "text-foreground/80"
)} )}
> >
Docs Docs
@@ -35,7 +35,7 @@ export function MainNav() {
pathname?.startsWith("/docs/components") && pathname?.startsWith("/docs/components") &&
!pathname?.startsWith("/docs/component/chart") !pathname?.startsWith("/docs/component/chart")
? "text-foreground" ? "text-foreground"
: "text-foreground/60" : "text-foreground/80"
)} )}
> >
Components Components
@@ -46,7 +46,7 @@ export function MainNav() {
"transition-colors hover:text-foreground/80", "transition-colors hover:text-foreground/80",
pathname?.startsWith("/blocks") pathname?.startsWith("/blocks")
? "text-foreground" ? "text-foreground"
: "text-foreground/60" : "text-foreground/80"
)} )}
> >
Blocks Blocks
@@ -58,7 +58,7 @@ export function MainNav() {
pathname?.startsWith("/docs/component/chart") || pathname?.startsWith("/docs/component/chart") ||
pathname?.startsWith("/charts") pathname?.startsWith("/charts")
? "text-foreground" ? "text-foreground"
: "text-foreground/60" : "text-foreground/80"
)} )}
> >
Charts Charts
@@ -69,29 +69,18 @@ export function MainNav() {
"transition-colors hover:text-foreground/80", "transition-colors hover:text-foreground/80",
pathname?.startsWith("/themes") pathname?.startsWith("/themes")
? "text-foreground" ? "text-foreground"
: "text-foreground/60" : "text-foreground/80"
)} )}
> >
Themes Themes
</Link> </Link>
<Link
href="/examples"
className={cn(
"transition-colors hover:text-foreground/80",
pathname?.startsWith("/examples")
? "text-foreground"
: "text-foreground/60"
)}
>
Examples
</Link>
<Link <Link
href="/colors" href="/colors"
className={cn( className={cn(
"transition-colors hover:text-foreground/80", "transition-colors hover:text-foreground/80",
pathname?.startsWith("/colors") pathname?.startsWith("/colors")
? "text-foreground" ? "text-foreground"
: "text-foreground/60" : "text-foreground/80"
)} )}
> >
Colors Colors

View File

@@ -3,68 +3,55 @@
import * as React from "react" import * as React from "react"
import Link, { LinkProps } from "next/link" import Link, { LinkProps } from "next/link"
import { useRouter } from "next/navigation" import { useRouter } from "next/navigation"
import { ViewVerticalIcon } from "@radix-ui/react-icons"
import { docsConfig } from "@/config/docs" import { docsConfig } from "@/config/docs"
import { siteConfig } from "@/config/site"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
import { Icons } from "@/components/icons" import { useMetaColor } from "@/hooks/use-meta-color"
import { Button } from "@/registry/new-york/ui/button" import { Button } from "@/registry/new-york/ui/button"
import { ScrollArea } from "@/registry/new-york/ui/scroll-area" import {
import { Sheet, SheetContent, SheetTrigger } from "@/registry/new-york/ui/sheet" Drawer,
DrawerContent,
DrawerTrigger,
} from "@/registry/new-york/ui/drawer"
export function MobileNav() { export function MobileNav() {
const [open, setOpen] = React.useState(false) const [open, setOpen] = React.useState(false)
const { setMetaColor, metaColor } = useMetaColor()
const onOpenChange = React.useCallback(
(open: boolean) => {
setOpen(open)
setMetaColor(open ? "#09090b" : metaColor)
},
[setMetaColor, metaColor]
)
return ( return (
<Sheet open={open} onOpenChange={setOpen}> <Drawer open={open} onOpenChange={onOpenChange}>
<SheetTrigger asChild> <DrawerTrigger asChild>
<Button <Button
variant="ghost" variant="ghost"
className="mr-2 px-0 text-base hover:bg-transparent focus-visible:bg-transparent focus-visible:ring-0 focus-visible:ring-offset-0 md:hidden" className="-ml-2 mr-2 h-8 w-8 px-0 text-base hover:bg-transparent focus-visible:bg-transparent focus-visible:ring-0 focus-visible:ring-offset-0 md:hidden"
> >
<svg <svg
strokeWidth="1.5"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
className="h-5 w-5" fill="none"
viewBox="0 0 24 24"
strokeWidth="1.5"
stroke="currentColor"
className="!size-6"
> >
<path <path
d="M3 5H11"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round" strokeLinecap="round"
strokeLinejoin="round" strokeLinejoin="round"
></path> d="M3.75 9h16.5m-16.5 6.75h16.5"
<path />
d="M3 12H16"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
></path>
<path
d="M3 19H21"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
></path>
</svg> </svg>
<span className="sr-only">Toggle Menu</span> <span className="sr-only">Toggle Menu</span>
</Button> </Button>
</SheetTrigger> </DrawerTrigger>
<SheetContent side="left" className="pr-0"> <DrawerContent className="max-h-[60svh] p-0">
<MobileLink <div className="overflow-auto p-6">
href="/"
className="flex items-center"
onOpenChange={setOpen}
>
<Icons.logo className="mr-2 h-4 w-4" />
<span className="font-bold">{siteConfig.name}</span>
</MobileLink>
<ScrollArea className="my-4 h-[calc(100vh-8rem)] pb-10 pl-6">
<div className="flex flex-col space-y-3"> <div className="flex flex-col space-y-3">
{docsConfig.mainNav?.map( {docsConfig.mainNav?.map(
(item) => (item) =>
@@ -108,9 +95,9 @@ export function MobileNav() {
</div> </div>
))} ))}
</div> </div>
</ScrollArea> </div>
</SheetContent> </DrawerContent>
</Sheet> </Drawer>
) )
} }
@@ -135,7 +122,7 @@ function MobileLink({
router.push(href.toString()) router.push(href.toString())
onOpenChange?.(false) onOpenChange?.(false)
}} }}
className={cn(className)} className={cn("text-base", className)}
{...props} {...props}
> >
{children} {children}

View File

@@ -0,0 +1,35 @@
"use client"
import * as React from "react"
import { MoonIcon, SunIcon } from "lucide-react"
import { useTheme } from "next-themes"
import { META_THEME_COLORS } from "@/config/site"
import { useMetaColor } from "@/hooks/use-meta-color"
import { Button } from "@/registry/new-york/ui/button"
export function ModeSwitcher() {
const { setTheme, resolvedTheme } = useTheme()
const { setMetaColor } = useMetaColor()
const toggleTheme = React.useCallback(() => {
setTheme(resolvedTheme === "dark" ? "light" : "dark")
setMetaColor(
resolvedTheme === "dark"
? META_THEME_COLORS.light
: META_THEME_COLORS.dark
)
}, [resolvedTheme, setTheme, setMetaColor])
return (
<Button
variant="ghost"
className="group/toggle h-8 w-8 px-0"
onClick={toggleTheme}
>
<SunIcon className="hidden [html.dark_&]:block" />
<MoonIcon className="hidden [html.light_&]:block" />
<span className="sr-only">Toggle theme</span>
</Button>
)
}

View File

@@ -1,7 +1,7 @@
"use client" "use client"
import * as React from "react" import * as React from "react"
import { MoonIcon, SunIcon } from "@radix-ui/react-icons" import { MoonIcon, SunIcon } from "lucide-react"
import { useTheme } from "next-themes" import { useTheme } from "next-themes"
import { Button } from "@/registry/new-york/ui/button" import { Button } from "@/registry/new-york/ui/button"

View File

@@ -8,12 +8,12 @@ function PageHeader({
return ( return (
<section <section
className={cn( className={cn(
"mx-auto flex flex-col items-start gap-2 px-4 py-8 md:py-12 md:pb-8 lg:py-12 lg:pb-10", "flex flex-col items-start gap-2 border-b border-border/40 py-8 dark:border-border md:py-10 lg:py-12",
className className
)} )}
{...props} {...props}
> >
{children} <div className="container">{children}</div>
</section> </section>
) )
} }

View File

@@ -1,11 +1,10 @@
import Link from "next/link" import Link from "next/link"
import { ChevronLeftIcon, ChevronRightIcon } from "@radix-ui/react-icons"
import { Doc } from "contentlayer/generated" import { Doc } from "contentlayer/generated"
import { ChevronLeft, ChevronRight } from "lucide-react"
import { NavItem, NavItemWithChildren } from "types/nav" import { NavItem, NavItemWithChildren } from "types/nav"
import { docsConfig } from "@/config/docs" import { docsConfig } from "@/config/docs"
import { cn } from "@/lib/utils" import { Button } from "@/registry/new-york/ui/button"
import { buttonVariants } from "@/registry/new-york/ui/button"
interface DocsPagerProps { interface DocsPagerProps {
doc: Doc doc: Doc
@@ -21,22 +20,20 @@ export function DocsPager({ doc }: DocsPagerProps) {
return ( return (
<div className="flex flex-row items-center justify-between"> <div className="flex flex-row items-center justify-between">
{pager?.prev?.href && ( {pager?.prev?.href && (
<Link <Button variant="ghost" asChild>
href={pager.prev.href} <Link href={pager.prev.href}>
className={buttonVariants({ variant: "outline" })} <ChevronLeft />
> {pager.prev.title}
<ChevronLeftIcon className="mr-2 h-4 w-4" /> </Link>
{pager.prev.title} </Button>
</Link>
)} )}
{pager?.next?.href && ( {pager?.next?.href && (
<Link <Button variant="ghost" className="ml-auto" asChild>
href={pager.next.href} <Link href={pager.next.href}>
className={cn(buttonVariants({ variant: "outline" }), "ml-auto")} {pager.next.title}
> <ChevronRight />
{pager.next.title} </Link>
<ChevronRightIcon className="ml-2 h-4 w-4" /> </Button>
</Link>
)} )}
</div> </div>
) )

View File

@@ -3,11 +3,13 @@
import * as React from "react" import * as React from "react"
import { Provider as JotaiProvider } from "jotai" import { Provider as JotaiProvider } from "jotai"
import { ThemeProvider as NextThemesProvider } from "next-themes" import { ThemeProvider as NextThemesProvider } from "next-themes"
import { ThemeProviderProps } from "next-themes/dist/types"
import { TooltipProvider } from "@/registry/new-york/ui/tooltip" import { TooltipProvider } from "@/registry/new-york/ui/tooltip"
export function ThemeProvider({ children, ...props }: ThemeProviderProps) { export function ThemeProvider({
children,
...props
}: React.ComponentProps<typeof NextThemesProvider>) {
return ( return (
<JotaiProvider> <JotaiProvider>
<NextThemesProvider {...props}> <NextThemesProvider {...props}>

View File

@@ -44,18 +44,16 @@ export function DocsSidebarNavItems({
pathname, pathname,
}: DocsSidebarNavItemsProps) { }: DocsSidebarNavItemsProps) {
return items?.length ? ( return items?.length ? (
<div className="grid grid-flow-row auto-rows-max text-sm"> <div className="grid grid-flow-row auto-rows-max gap-0.5 text-sm">
{items.map((item, index) => {items.map((item, index) =>
item.href && !item.disabled ? ( item.href && !item.disabled ? (
<Link <Link
key={index} key={index}
href={item.href} href={item.href}
className={cn( className={cn(
"group flex w-full items-center rounded-md border border-transparent px-2 py-1 hover:underline", "group flex w-full items-center px-2 py-1 font-normal text-foreground underline-offset-2 hover:underline",
item.disabled && "cursor-not-allowed opacity-60", item.disabled && "cursor-not-allowed opacity-60",
pathname === item.href pathname === item.href && "underline"
? "font-medium text-foreground"
: "text-muted-foreground"
)} )}
target={item.external ? "_blank" : ""} target={item.external ? "_blank" : ""}
rel={item.external ? "noreferrer" : ""} rel={item.external ? "noreferrer" : ""}

View File

@@ -2,7 +2,7 @@ import { siteConfig } from "@/config/site"
export function SiteFooter() { export function SiteFooter() {
return ( return (
<footer className="py-6 md:px-8 md:py-0"> <footer className="border-t border-border/40 py-6 dark:border-border md:px-8 md:py-0">
<div className="container flex flex-col items-center justify-between gap-4 md:h-24 md:flex-row"> <div className="container flex flex-col items-center justify-between gap-4 md:h-24 md:flex-row">
<p className="text-balance text-center text-sm leading-loose text-muted-foreground md:text-left"> <p className="text-balance text-center text-sm leading-loose text-muted-foreground md:text-left">
Built by{" "} Built by{" "}

View File

@@ -1,60 +1,35 @@
import Link from "next/link" import Link from "next/link"
import { siteConfig } from "@/config/site" import { siteConfig } from "@/config/site"
import { cn } from "@/lib/utils"
import { CommandMenu } from "@/components/command-menu" import { CommandMenu } from "@/components/command-menu"
import { Icons } from "@/components/icons" import { Icons } from "@/components/icons"
import { MainNav } from "@/components/main-nav" import { MainNav } from "@/components/main-nav"
import { MobileNav } from "@/components/mobile-nav" import { MobileNav } from "@/components/mobile-nav"
import { ModeToggle } from "@/components/mode-toggle" import { ModeSwitcher } from "@/components/mode-switcher"
import { buttonVariants } from "@/registry/new-york/ui/button" import { Button } from "@/registry/new-york/ui/button"
export function SiteHeader() { export function SiteHeader() {
return ( return (
<header className="sticky top-0 z-50 w-full border-border/40 bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60"> <header className="sticky top-0 z-50 w-full border-b border-border/40 bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60 dark:border-border">
<div className="container flex h-14 max-w-screen-2xl items-center"> <div className="flex h-14 items-center px-4">
<MainNav /> <MainNav />
<MobileNav /> <MobileNav />
<div className="flex flex-1 items-center justify-between space-x-2 md:justify-end"> <div className="flex flex-1 items-center justify-between gap-2 md:justify-end">
<div className="w-full flex-1 md:w-auto md:flex-none"> <div className="w-full flex-1 md:w-auto md:flex-none">
<CommandMenu /> <CommandMenu />
</div> </div>
<nav className="flex items-center"> <nav className="flex items-center gap-0.5">
<Link <Button variant="ghost" size="icon" className="h-8 w-8 px-0">
href={siteConfig.links.github} <Link
target="_blank" href={siteConfig.links.github}
rel="noreferrer" target="_blank"
> rel="noreferrer"
<div
className={cn(
buttonVariants({
variant: "ghost",
}),
"h-8 w-8 px-0"
)}
> >
<Icons.gitHub className="h-4 w-4" /> <Icons.gitHub className="h-4 w-4" />
<span className="sr-only">GitHub</span> <span className="sr-only">GitHub</span>
</div> </Link>
</Link> </Button>
<Link <ModeSwitcher />
href={siteConfig.links.twitter}
target="_blank"
rel="noreferrer"
>
<div
className={cn(
buttonVariants({
variant: "ghost",
}),
"h-8 w-8 px-0"
)}
>
<Icons.twitter className="h-3 w-3 fill-current" />
<span className="sr-only">Twitter</span>
</div>
</Link>
<ModeToggle />
</nav> </nav>
</div> </div>
</div> </div>

View File

@@ -1,15 +1,8 @@
"use client" "use client"
import * as React from "react" import * as React from "react"
import { import template from "lodash/template"
CheckIcon, import { Check, Copy, Moon, Repeat, Sun } from "lucide-react"
CopyIcon,
InfoCircledIcon,
MoonIcon,
ResetIcon,
SunIcon,
} from "@radix-ui/react-icons"
import template from "lodash.template"
import { useTheme } from "next-themes" import { useTheme } from "next-themes"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
@@ -79,77 +72,6 @@ export function ThemeCustomizer() {
<Customizer /> <Customizer />
</PopoverContent> </PopoverContent>
</Popover> </Popover>
<div className="ml-2 hidden items-center gap-0.5">
{mounted ? (
<>
{["zinc", "rose", "blue", "green", "orange"].map((color) => {
const baseColor = baseColors.find(
(baseColor) => baseColor.name === color
)
const isActive = config.theme === color
if (!baseColor) {
return null
}
return (
<Tooltip key={baseColor.name}>
<TooltipTrigger asChild>
<button
onClick={() =>
setConfig({
...config,
theme: baseColor.name,
})
}
className={cn(
"flex h-8 w-8 items-center justify-center rounded-full border-2 text-xs",
isActive
? "border-[--theme-primary]"
: "border-transparent"
)}
style={
{
"--theme-primary": `hsl(${
baseColor?.activeColor[
mode === "dark" ? "dark" : "light"
]
})`,
} as React.CSSProperties
}
>
<span
className={cn(
"flex h-5 w-5 items-center justify-center rounded-full bg-[--theme-primary]"
)}
>
{isActive && (
<CheckIcon className="h-4 w-4 text-white" />
)}
</span>
<span className="sr-only">{baseColor.label}</span>
</button>
</TooltipTrigger>
<TooltipContent
align="center"
className="rounded-[0.5rem] bg-zinc-900 text-zinc-50"
>
{baseColor.label}
</TooltipContent>
</Tooltip>
)
})}
</>
) : (
<div className="mr-1 flex items-center gap-4">
<Skeleton className="h-5 w-5 rounded-full" />
<Skeleton className="h-5 w-5 rounded-full" />
<Skeleton className="h-5 w-5 rounded-full" />
<Skeleton className="h-5 w-5 rounded-full" />
<Skeleton className="h-5 w-5 rounded-full" />
</div>
)}
</div>
</div> </div>
<CopyCodeButton variant="ghost" size="sm" className="[&_svg]:hidden" /> <CopyCodeButton variant="ghost" size="sm" className="[&_svg]:hidden" />
</div> </div>
@@ -173,10 +95,10 @@ function Customizer() {
<div className="flex items-start pt-4 md:pt-0"> <div className="flex items-start pt-4 md:pt-0">
<div className="space-y-1 pr-2"> <div className="space-y-1 pr-2">
<div className="font-semibold leading-none tracking-tight"> <div className="font-semibold leading-none tracking-tight">
Customize Theme Customizer
</div> </div>
<div className="text-xs text-muted-foreground"> <div className="text-xs text-muted-foreground">
Pick a style and color for your components. Customize your components colors.
</div> </div>
</div> </div>
<Button <Button
@@ -191,110 +113,58 @@ function Customizer() {
}) })
}} }}
> >
<ResetIcon /> <Repeat />
<span className="sr-only">Reset</span> <span className="sr-only">Reset</span>
</Button> </Button>
</div> </div>
<div className="flex flex-1 flex-col space-y-4 md:space-y-6"> <div className="flex flex-1 flex-col space-y-4 md:space-y-6">
<div className="space-y-1.5">
<div className="flex w-full items-center">
<Label className="text-xs">Style</Label>
<Popover>
<PopoverTrigger>
<InfoCircledIcon className="ml-1 h-3 w-3" />
<span className="sr-only">About styles</span>
</PopoverTrigger>
<PopoverContent
className="space-y-3 rounded-[0.5rem] text-sm"
side="right"
align="start"
alignOffset={-20}
>
<p className="font-medium">
What is the difference between the New York and Default style?
</p>
<p>
A style comes with its own set of components, animations,
icons and more.
</p>
<p>
The <span className="font-medium">Default</span> style has
larger inputs, uses lucide-react for icons and
tailwindcss-animate for animations.
</p>
<p>
The <span className="font-medium">New York</span> style ships
with smaller buttons and cards with shadows. It uses icons
from Radix Icons.
</p>
</PopoverContent>
</Popover>
</div>
<div className="grid grid-cols-3 gap-2">
<Button
variant={"outline"}
size="sm"
onClick={() => setConfig({ ...config, style: "default" })}
className={cn(
config.style === "default" && "border-2 border-primary"
)}
>
Default
</Button>
<Button
variant={"outline"}
size="sm"
onClick={() => setConfig({ ...config, style: "new-york" })}
className={cn(
config.style === "new-york" && "border-2 border-primary"
)}
>
New York
</Button>
</div>
</div>
<div className="space-y-1.5"> <div className="space-y-1.5">
<Label className="text-xs">Color</Label> <Label className="text-xs">Color</Label>
<div className="grid grid-cols-3 gap-2"> <div className="grid grid-cols-3 gap-2">
{baseColors.map((theme) => { {baseColors
const isActive = config.theme === theme.name .filter(
(theme) =>
return mounted ? ( !["slate", "stone", "gray", "neutral"].includes(theme.name)
<Button
variant={"outline"}
size="sm"
key={theme.name}
onClick={() => {
setConfig({
...config,
theme: theme.name,
})
}}
className={cn(
"justify-start",
isActive && "border-2 border-primary"
)}
style={
{
"--theme-primary": `hsl(${
theme?.activeColor[mode === "dark" ? "dark" : "light"]
})`,
} as React.CSSProperties
}
>
<span
className={cn(
"mr-1 flex h-5 w-5 shrink-0 -translate-x-1 items-center justify-center rounded-full bg-[--theme-primary]"
)}
>
{isActive && <CheckIcon className="h-4 w-4 text-white" />}
</span>
{theme.label}
</Button>
) : (
<Skeleton className="h-8 w-full" key={theme.name} />
) )
})} .map((theme) => {
const isActive = config.theme === theme.name
return mounted ? (
<Button
variant={"outline"}
size="sm"
key={theme.name}
onClick={() => {
setConfig({
...config,
theme: theme.name,
})
}}
className={cn(
"justify-start",
isActive && "border-2 border-primary"
)}
style={
{
"--theme-primary": `hsl(${
theme?.activeColor[mode === "dark" ? "dark" : "light"]
})`,
} as React.CSSProperties
}
>
<span
className={cn(
"mr-1 flex h-5 w-5 shrink-0 -translate-x-1 items-center justify-center rounded-full bg-[--theme-primary]"
)}
>
{isActive && <Check className="h-4 w-4 text-white" />}
</span>
{theme.label}
</Button>
) : (
<Skeleton className="h-8 w-full" key={theme.name} />
)
})}
</div> </div>
</div> </div>
<div className="space-y-1.5"> <div className="space-y-1.5">
@@ -334,7 +204,7 @@ function Customizer() {
onClick={() => setMode("light")} onClick={() => setMode("light")}
className={cn(mode === "light" && "border-2 border-primary")} className={cn(mode === "light" && "border-2 border-primary")}
> >
<SunIcon className="mr-1 -translate-x-1" /> <Sun className="mr-1 -translate-x-1" />
Light Light
</Button> </Button>
<Button <Button
@@ -343,7 +213,7 @@ function Customizer() {
onClick={() => setMode("dark")} onClick={() => setMode("dark")}
className={cn(mode === "dark" && "border-2 border-primary")} className={cn(mode === "dark" && "border-2 border-primary")}
> >
<MoonIcon className="mr-1 -translate-x-1" /> <Moon className="mr-1 -translate-x-1" />
Dark Dark
</Button> </Button>
</> </>
@@ -391,11 +261,7 @@ function CopyCodeButton({
className={cn("md:hidden", className)} className={cn("md:hidden", className)}
{...props} {...props}
> >
{hasCopied ? ( {hasCopied ? <Check /> : <Copy />}
<CheckIcon className="mr-2 h-4 w-4" />
) : (
<CopyIcon className="mr-2 h-4 w-4" />
)}
Copy code Copy code
</Button> </Button>
)} )}
@@ -432,11 +298,7 @@ function CopyCodeButton({
}} }}
className="absolute right-4 top-4 bg-muted text-muted-foreground hover:bg-muted hover:text-muted-foreground" className="absolute right-4 top-4 bg-muted text-muted-foreground hover:bg-muted hover:text-muted-foreground"
> >
{hasCopied ? ( {hasCopied ? <Check /> : <Copy />}
<CheckIcon className="mr-2 h-4 w-4" />
) : (
<CopyIcon className="mr-2 h-4 w-4" />
)}
Copy Copy
</Button> </Button>
)} )}

View File

@@ -8,6 +8,10 @@ export interface DocsConfig {
export const docsConfig: DocsConfig = { export const docsConfig: DocsConfig = {
mainNav: [ mainNav: [
{
title: "Home",
href: "/",
},
{ {
title: "Documentation", title: "Documentation",
href: "/docs", href: "/docs",
@@ -28,10 +32,6 @@ export const docsConfig: DocsConfig = {
title: "Themes", title: "Themes",
href: "/themes", href: "/themes",
}, },
{
title: "Examples",
href: "/examples",
},
{ {
title: "Colors", title: "Colors",
href: "/colors", href: "/colors",

View File

@@ -11,3 +11,8 @@ export const siteConfig = {
} }
export type SiteConfig = typeof siteConfig export type SiteConfig = typeof siteConfig
export const META_THEME_COLORS = {
light: "#ffffff",
dark: "#09090b",
}

View File

@@ -85,6 +85,7 @@ Options:
-y, --yes skip confirmation prompt. (default: false) -y, --yes skip confirmation prompt. (default: false)
-o, --overwrite overwrite existing files. (default: false) -o, --overwrite overwrite existing files. (default: false)
-c, --cwd <cwd> the working directory. defaults to the current directory. -c, --cwd <cwd> the working directory. defaults to the current directory.
-a, --all add all available components. (default: false)
-p, --path <path> the path to add the component to. -p, --path <path> the path to add the component to.
-h, --help display help for command -h, --help display help for command
``` ```

View File

@@ -88,3 +88,14 @@ You can use the `<Calendar>` component to build a date picker. See the [Date Pic
### Form ### Form
<ComponentPreview name="calendar-form" /> <ComponentPreview name="calendar-form" />
## Changelog
### 11-03-2024 day_outside color
- Changed the color of the `day_outside` class to the following to improve contrast:
```tsx showLineNumbers title="calendar.tsx"
"day_outside:
"day-outside text-muted-foreground aria-selected:bg-accent/50 aria-selected:text-muted-foreground",
```

View File

@@ -72,3 +72,35 @@ import {
name="card-demo" name="card-demo"
description="A card showing notifications settings." description="A card showing notifications settings."
/> />
## Changelog
### 11-03-2024 a11y for title and description
- Changed the `CardTitle` and `CardDescription` components to use `div` instead of `h3` and `p` to improve accessibility.
```tsx showLineNumbers title="card.tsx"
const CardTitle = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("font-semibold leading-none tracking-tight", className)}
{...props}
/>
))
CardTitle.displayName = "CardTitle"
const CardDescription = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
))
CardDescription.displayName = "CardDescription"
```

View File

@@ -406,6 +406,40 @@ You can change the keyboard shortcut by updating the `SIDEBAR_KEYBOARD_SHORTCUT`
const SIDEBAR_KEYBOARD_SHORTCUT = "b" const SIDEBAR_KEYBOARD_SHORTCUT = "b"
``` ```
### Persisted State
The `SidebarProvider` supports persisting the sidebar state across page reloads and server-side rendering. It uses cookies to store the current state of the sidebar. When the sidebar state changes, a default cookie named `sidebar:state` is set with the current open/closed state. This cookie is then read on subsequent page loads to restore the sidebar state.
To persist sidebar state in Next.js, set up your `SidebarProvider` in `app/layout.tsx` like this:
```tsx showLineNumbers title="app/layout.tsx"
import { cookies } from "next/headers"
import { SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar"
import { AppSidebar } from "@/components/app-sidebar"
export async function Layout({ children }: { children: React.ReactNode }) {
const cookieStore = await cookies()
const defaultOpen = cookieStore.get("sidebar:state")?.value === "true"
return (
<SidebarProvider defaultOpen={defaultOpen}>
<AppSidebar />
<main>
<SidebarTrigger />
{children}
</main>
</SidebarProvider>
)
}
```
You can change the name of the cookie by updating the `SIDEBAR_COOKIE_NAME` variable in `sidebar.tsx`.
```tsx showLineNumbers title="components/ui/sidebar.tsx"
const SIDEBAR_COOKIE_NAME = "sidebar:state"
```
## Sidebar ## Sidebar
The main `Sidebar` component used to render a collapsible sidebar. The main `Sidebar` component used to render a collapsible sidebar.
@@ -1323,7 +1357,30 @@ You can find more tips on using states for styling in this [Twitter thread](http
## Changelog ## Changelog
### 2024-10-21 ### 2024-10-30 Cookie handling in setOpen
- [#5593](https://github.com/shadcn-ui/ui/pull/5593) - Improved setOpen callback logic in `<SidebarProvider>`.
Update the `setOpen` callback in `<SidebarProvider>` as follows:
```tsx showLineNumbers
const setOpen = React.useCallback(
(value: boolean | ((value: boolean) => boolean)) => {
const openState = typeof value === "function" ? value(open) : value
if (setOpenProp) {
setOpenProp(openState)
} else {
_setOpen(openState)
}
// This sets the cookie to keep the sidebar state.
document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`
},
[setOpenProp, open]
)
```
### 2024-10-21 Fixed `text-sidebar-foreground`
- [#5491](https://github.com/shadcn-ui/ui/pull/5491) - Moved `text-sidebar-foreground` from `<SidebarProvider>` to `<Sidebar>` component. - [#5491](https://github.com/shadcn-ui/ui/pull/5491) - Moved `text-sidebar-foreground` from `<SidebarProvider>` to `<Sidebar>` component.

View File

@@ -22,9 +22,11 @@ npm install next-themes
import * as React from "react" import * as React from "react"
import { ThemeProvider as NextThemesProvider } from "next-themes" import { ThemeProvider as NextThemesProvider } from "next-themes"
import { type ThemeProviderProps } from "next-themes/dist/types"
export function ThemeProvider({ children, ...props }: ThemeProviderProps) { export function ThemeProvider({
children,
...props
}: React.ComponentProps<typeof NextThemesProvider>) {
return <NextThemesProvider {...props}>{children}</NextThemesProvider> return <NextThemesProvider {...props}>{children}</NextThemesProvider>
} }
``` ```

View File

@@ -5,7 +5,7 @@ description: Every component recreated in Figma. With customizable props, typogr
## Paid ## Paid
- [shadcn/ui kit](http://shadcndesign.com) by [ Matt Wierzbicki](https://x.com/matsugfx) - A premium, always up-to-date UI kit for Figma - shadcn/ui compatible and optimized for smooth design-to-dev handoff. - [shadcn/ui kit](https://shadcndesign.com) by [ Matt Wierzbicki](https://x.com/matsugfx) - A premium, always up-to-date UI kit for Figma - shadcn/ui compatible and optimized for smooth design-to-dev handoff.
## Free ## Free

View File

@@ -16,28 +16,14 @@ Components are styled using Tailwind CSS. You need to install Tailwind CSS in yo
Add the following dependencies to your project: Add the following dependencies to your project:
```bash ```bash
npm install tailwindcss-animate class-variance-authority clsx tailwind-merge npm install tailwindcss-animate class-variance-authority clsx tailwind-merge lucide-react
```
### Add icon library
If you're using the `default` style, install `lucide-react`:
```bash
npm install lucide-react
```
If you're using the `new-york` style, install `@radix-ui/react-icons`:
```bash
npm install @radix-ui/react-icons
``` ```
### Configure path aliases ### Configure path aliases
I use the `@` alias. This is how I configure it in tsconfig.json: Configure the path aliases in your `tsconfig.json` file.
```json {3-6} title="tsconfig.json" ```json {3-6} title="tsconfig.json" showLineNumbers
{ {
"compilerOptions": { "compilerOptions": {
"baseUrl": ".", "baseUrl": ".",
@@ -50,27 +36,16 @@ I use the `@` alias. This is how I configure it in tsconfig.json:
The `@` alias is a preference. You can use other aliases if you want. The `@` alias is a preference. You can use other aliases if you want.
**If you use a different alias such as ~, you'll need to update import statements when adding components.**
### Configure tailwind.config.js ### Configure tailwind.config.js
Here's what my `tailwind.config.js` file looks like: Here's what my `tailwind.config.js` file looks like:
```js title="tailwind.config.js" ```js title="tailwind.config.js"
const { fontFamily } = require("tailwindcss/defaultTheme")
/** @type {import('tailwindcss').Config} */ /** @type {import('tailwindcss').Config} */
module.exports = { module.exports = {
darkMode: ["class"], darkMode: ["class"],
content: ["app/**/*.{ts,tsx}", "components/**/*.{ts,tsx}"], content: ["app/**/*.{ts,tsx}", "components/**/*.{ts,tsx}"],
theme: { theme: {
container: {
center: true,
padding: "2rem",
screens: {
"2xl": "1400px",
},
},
extend: { extend: {
colors: { colors: {
border: "hsl(var(--border))", border: "hsl(var(--border))",
@@ -112,23 +87,6 @@ module.exports = {
md: `calc(var(--radius) - 2px)`, md: `calc(var(--radius) - 2px)`,
sm: "calc(var(--radius) - 4px)", sm: "calc(var(--radius) - 4px)",
}, },
fontFamily: {
sans: ["var(--font-sans)", ...fontFamily.sans],
},
keyframes: {
"accordion-down": {
from: { height: "0" },
to: { height: "var(--radix-accordion-content-height)" },
},
"accordion-up": {
from: { height: "var(--radix-accordion-content-height)" },
to: { height: "0" },
},
},
animation: {
"accordion-down": "accordion-down 0.2s ease-out",
"accordion-up": "accordion-up 0.2s ease-out",
},
}, },
}, },
plugins: [require("tailwindcss-animate")], plugins: [require("tailwindcss-animate")],
@@ -148,67 +106,46 @@ Add the following to your styles/globals.css file. You can learn more about usin
:root { :root {
--background: 0 0% 100%; --background: 0 0% 100%;
--foreground: 222.2 47.4% 11.2%; --foreground: 222.2 47.4% 11.2%;
--muted: 210 40% 96.1%; --muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%; --muted-foreground: 215.4 16.3% 46.9%;
--popover: 0 0% 100%; --popover: 0 0% 100%;
--popover-foreground: 222.2 47.4% 11.2%; --popover-foreground: 222.2 47.4% 11.2%;
--border: 214.3 31.8% 91.4%; --border: 214.3 31.8% 91.4%;
--input: 214.3 31.8% 91.4%; --input: 214.3 31.8% 91.4%;
--card: 0 0% 100%; --card: 0 0% 100%;
--card-foreground: 222.2 47.4% 11.2%; --card-foreground: 222.2 47.4% 11.2%;
--primary: 222.2 47.4% 11.2%; --primary: 222.2 47.4% 11.2%;
--primary-foreground: 210 40% 98%; --primary-foreground: 210 40% 98%;
--secondary: 210 40% 96.1%; --secondary: 210 40% 96.1%;
--secondary-foreground: 222.2 47.4% 11.2%; --secondary-foreground: 222.2 47.4% 11.2%;
--accent: 210 40% 96.1%; --accent: 210 40% 96.1%;
--accent-foreground: 222.2 47.4% 11.2%; --accent-foreground: 222.2 47.4% 11.2%;
--destructive: 0 100% 50%; --destructive: 0 100% 50%;
--destructive-foreground: 210 40% 98%; --destructive-foreground: 210 40% 98%;
--ring: 215 20.2% 65.1%; --ring: 215 20.2% 65.1%;
--radius: 0.5rem; --radius: 0.5rem;
} }
.dark { .dark {
--background: 224 71% 4%; --background: 224 71% 4%;
--foreground: 213 31% 91%; --foreground: 213 31% 91%;
--muted: 223 47% 11%; --muted: 223 47% 11%;
--muted-foreground: 215.4 16.3% 56.9%; --muted-foreground: 215.4 16.3% 56.9%;
--accent: 216 34% 17%; --accent: 216 34% 17%;
--accent-foreground: 210 40% 98%; --accent-foreground: 210 40% 98%;
--popover: 224 71% 4%; --popover: 224 71% 4%;
--popover-foreground: 215 20.2% 65.1%; --popover-foreground: 215 20.2% 65.1%;
--border: 216 34% 17%; --border: 216 34% 17%;
--input: 216 34% 17%; --input: 216 34% 17%;
--card: 224 71% 4%; --card: 224 71% 4%;
--card-foreground: 213 31% 91%; --card-foreground: 213 31% 91%;
--primary: 210 40% 98%; --primary: 210 40% 98%;
--primary-foreground: 222.2 47.4% 1.2%; --primary-foreground: 222.2 47.4% 1.2%;
--secondary: 222.2 47.4% 11.2%; --secondary: 222.2 47.4% 11.2%;
--secondary-foreground: 210 40% 98%; --secondary-foreground: 210 40% 98%;
--destructive: 0 63% 31%; --destructive: 0 63% 31%;
--destructive-foreground: 210 40% 98%; --destructive-foreground: 210 40% 98%;
--ring: 216 34% 17%; --ring: 216 34% 17%;
--radius: 0.5rem;
} }
} }
@@ -217,17 +154,14 @@ Add the following to your styles/globals.css file. You can learn more about usin
@apply border-border; @apply border-border;
} }
body { body {
@apply bg-background text-foreground; @apply font-sans antialiased bg-background text-foreground;
font-feature-settings: "rlig" 1, "calt" 1;
} }
} }
``` ```
### Add a cn helper ### Add a cn helper
I use a `cn` helper to make it easier to conditionally add Tailwind CSS classes. Here's how I define it in `lib/utils.ts`: ```ts title="lib/utils.ts" showLineNumbers
```ts title="lib/utils.ts"
import { clsx, type ClassValue } from "clsx" import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge" import { twMerge } from "tailwind-merge"
@@ -236,6 +170,34 @@ export function cn(...inputs: ClassValue[]) {
} }
``` ```
### Create a `components.json` file
Create a `components.json` file in the root of your project.
```json title="components.json" showLineNumbers
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"rsc": false,
"tsx": true,
"tailwind": {
"config": "tailwind.config.js",
"css": "src/index.css",
"baseColor": "zinc",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
},
"iconLibrary": "lucide"
}
```
### That's it ### That's it
You can now start adding components to your project. You can now start adding components to your project.

View File

@@ -5,7 +5,7 @@ description: Install and configure Next.js.
<Callout> <Callout>
**If you're using Next.js 15, see the [Next.js 15 + React 19](/docs/installation/react-19) guide.** **If you're using Next.js 15, see the [Next.js 15 + React 19](/docs/react-19) guide.**
</Callout> </Callout>

View File

@@ -23,6 +23,29 @@ npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p npx tailwindcss init -p
``` ```
Add this import header in your main css file, `src/index.css` in our case:
```css {1-3} showLineNumbers
@tailwind base;
@tailwind components;
@tailwind utilities;
/* ... */
```
Configure the tailwind template paths in `tailwind.config.js`:
```js {3}
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ["./index.html", "./src/**/*.{ts,tsx,js,jsx}"],
theme: {
extend: {},
},
plugins: [],
}
```
### Edit tsconfig.json file ### Edit tsconfig.json file
The current version of Vite splits TypeScript configuration into three files, two of which need to be edited. The current version of Vite splits TypeScript configuration into three files, two of which need to be edited.

View File

@@ -22,9 +22,11 @@ React 19 is now [rc](https://www.npmjs.com/package/react?activeTab=versions) and
To support React 19, package maintainers will need to test and update their packages to include React 19 as a peer dependency. This is [already](https://github.com/radix-ui/primitives/pull/2952) [in](https://github.com/pacocoursey/cmdk/pull/318) [progress](https://github.com/emilkowalski/vaul/pull/498). To support React 19, package maintainers will need to test and update their packages to include React 19 as a peer dependency. This is [already](https://github.com/radix-ui/primitives/pull/2952) [in](https://github.com/pacocoursey/cmdk/pull/318) [progress](https://github.com/emilkowalski/vaul/pull/498).
```diff /^19.0.0/ ```diff /^19.0/
"peerDependencies": { "peerDependencies": {
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0", - "react": "^16.8 || ^17.0 || ^18.0",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0",
- "react-dom": "^16.8 || ^17.0 || ^18.0"
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0" + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0"
}, },
``` ```
@@ -140,8 +142,8 @@ To make it easy for you track the progress of the upgrade, I've created a table
| [react-day-picker](https://www.npmjs.com/package/react-day-picker) | ✅ | Works with flag for npm. Work to upgrade to v9 in progress. | | [react-day-picker](https://www.npmjs.com/package/react-day-picker) | ✅ | Works with flag for npm. Work to upgrade to v9 in progress. |
| [input-otp](https://www.npmjs.com/package/input-otp) | ✅ | | | [input-otp](https://www.npmjs.com/package/input-otp) | ✅ | |
| [vaul](https://www.npmjs.com/package/vaul) | ✅ | | | [vaul](https://www.npmjs.com/package/vaul) | ✅ | |
| [@radix-ui/react-icons](https://www.npmjs.com/package/@radix-ui/react-icons) | 🚧 | See [PR #184](https://github.com/radix-ui/icons/pull/184) | | [@radix-ui/react-icons](https://www.npmjs.com/package/@radix-ui/react-icons) | 🚧 | See [PR #194](https://github.com/radix-ui/icons/pull/194) |
| [cmdk](https://www.npmjs.com/package/cmdk) | 🚧 | See [PR #318](https://github.com/pacocoursey/cmdk/pull/318) | | [cmdk](https://www.npmjs.com/package/cmdk) | | |
If you have any questions, please [open an issue](https://github.com/shadcn/ui/issues) on GitHub. If you have any questions, please [open an issue](https://github.com/shadcn/ui/issues) on GitHub.

View File

@@ -0,0 +1,25 @@
import * as React from "react"
import { useTheme } from "next-themes"
import { META_THEME_COLORS } from "@/config/site"
export function useMetaColor() {
const { resolvedTheme } = useTheme()
const metaColor = React.useMemo(() => {
return resolvedTheme !== "dark"
? META_THEME_COLORS.light
: META_THEME_COLORS.dark
}, [resolvedTheme])
const setMetaColor = React.useCallback((color: string) => {
document
.querySelector('meta[name="theme-color"]')
?.setAttribute("content", color)
}, [])
return {
metaColor,
setMetaColor,
}
}

View File

@@ -1,21 +1,14 @@
"use server" "use server"
import { promises as fs } from "fs"
import { tmpdir } from "os"
import path from "path"
import { Index } from "@/__registry__" import { Index } from "@/__registry__"
import { Project, ScriptKind, SourceFile, SyntaxKind } from "ts-morph"
import { z } from "zod" import { z } from "zod"
import { highlightCode } from "@/lib/highlight-code"
import { Style } from "@/registry/registry-styles" import { Style } from "@/registry/registry-styles"
import { BlockChunk, blockSchema, registryEntrySchema } from "@/registry/schema" import { registryEntrySchema } from "@/registry/schema"
const DEFAULT_BLOCKS_STYLE = "new-york" satisfies Style["name"] const DEFAULT_BLOCKS_STYLE = "new-york" satisfies Style["name"]
const BLOCKS_WHITELIST_PREFIXES = ["sidebar", "login"]
const project = new Project({ const REGISTRY_BLOCK_TYPES = ["registry:block"]
compilerOptions: {},
})
export async function getAllBlockIds( export async function getAllBlockIds(
style: Style["name"] = DEFAULT_BLOCKS_STYLE style: Style["name"] = DEFAULT_BLOCKS_STYLE
@@ -24,136 +17,14 @@ export async function getAllBlockIds(
return blocks.map((block) => block.name) return blocks.map((block) => block.name)
} }
export async function getBlock(
name: string,
style: Style["name"] = DEFAULT_BLOCKS_STYLE
) {
const entry = Index[style][name]
const content = await _getBlockContent(name, style)
const chunks = entry?.chunks
? await Promise.all(
entry?.chunks?.map(async (chunk: BlockChunk) => {
const code = await readFile(chunk.file)
const tempFile = await createTempSourceFile(`${chunk.name}.tsx`)
const sourceFile = project.createSourceFile(tempFile, code, {
scriptKind: ScriptKind.TSX,
})
sourceFile
.getDescendantsOfKind(SyntaxKind.JsxOpeningElement)
.filter((node) => {
return node.getAttribute("x-chunk") !== undefined
})
?.map((component) => {
component
.getAttribute("x-chunk")
?.asKind(SyntaxKind.JsxAttribute)
?.remove()
})
return {
...chunk,
code: sourceFile
.getText()
.replaceAll(`@/registry/${style}/`, "@/components/"),
}
})
)
: []
const block = {
style,
highlightedCode: content.code ? await highlightCode(content.code) : "",
...entry,
...content,
chunks,
type: "registry:block",
}
const result = blockSchema.safeParse(block)
if (!result.success) {
console.log(block)
return null
}
return result.data
}
async function _getAllBlocks(style: Style["name"] = DEFAULT_BLOCKS_STYLE) { async function _getAllBlocks(style: Style["name"] = DEFAULT_BLOCKS_STYLE) {
const index = z.record(registryEntrySchema).parse(Index[style]) const index = z.record(registryEntrySchema).parse(Index[style])
return Object.values(index).filter((block) => block.type === "registry:block") return Object.values(index).filter((block) =>
} BLOCKS_WHITELIST_PREFIXES.some(
(prefix) =>
async function _getBlockCode( block.name.startsWith(prefix) &&
name: string, REGISTRY_BLOCK_TYPES.includes(block.type)
style: Style["name"] = DEFAULT_BLOCKS_STYLE )
) { )
const entry = Index[style][name]
if (!entry) {
console.error(`Block ${name} not found in style ${style}`)
return ""
}
const block = registryEntrySchema.parse(entry)
if (!block.source) {
return ""
}
return await readFile(block.source)
}
async function readFile(source: string) {
const filepath = path.join(process.cwd(), source)
return await fs.readFile(filepath, "utf-8")
}
async function createTempSourceFile(filename: string) {
const dir = await fs.mkdtemp(path.join(tmpdir(), "codex-"))
return path.join(dir, filename)
}
async function _getBlockContent(name: string, style: Style["name"]) {
const raw = await _getBlockCode(name, style)
const tempFile = await createTempSourceFile(`${name}.tsx`)
const sourceFile = project.createSourceFile(tempFile, raw, {
scriptKind: ScriptKind.TSX,
})
// Extract meta.
const iframeHeight = _extractVariable(sourceFile, "iframeHeight")
const containerClassName = _extractVariable(sourceFile, "containerClassName")
// Format the code.
let code = sourceFile.getText()
code = code.replaceAll(`@/registry/${style}/`, "@/components/")
code = code.replaceAll("export default", "export")
return {
code,
container: {
height: iframeHeight,
className: containerClassName,
},
}
}
function _extractVariable(sourceFile: SourceFile, name: string) {
const variable = sourceFile.getVariableDeclaration(name)
if (!variable) {
return null
}
const value = variable
.getInitializerIfKindOrThrow(SyntaxKind.StringLiteral)
.getLiteralValue()
variable.remove()
return value
} }

View File

@@ -1,15 +1,6 @@
// import { JetBrains_Mono as FontMono, Inter as FontSans } from "next/font/google" import { GeistMono } from "geist/font/mono"
import { JetBrains_Mono as FontMono } from "next/font/google"
// import { GeistMono } from "geist/font/mono"
import { GeistSans } from "geist/font/sans" import { GeistSans } from "geist/font/sans"
// export const fontSans = FontSans({
// subsets: ["latin"],
// variable: "--font-sans",
// })
export const fontSans = GeistSans export const fontSans = GeistSans
export const fontMono = FontMono({ export const fontMono = GeistMono
subsets: ["latin"],
variable: "--font-mono",
})

View File

@@ -1,10 +1,8 @@
"use server"
import { codeToHtml } from "shiki" import { codeToHtml } from "shiki"
export async function highlightCode(code: string) { export async function highlightCode(code: string) {
const html = codeToHtml(code, { const html = await codeToHtml(code, {
lang: "typescript", lang: "jsx",
theme: "github-dark-default", theme: "github-dark-default",
transformers: [ transformers: [
{ {

View File

@@ -0,0 +1,37 @@
import { describe, expect, it } from "vitest"
import { createFileTreeForRegistryItemFiles } from "@/lib/registry"
describe("createFileTreeForRegistryItemFiles", () => {
it("should create a nested file tree structure", async () => {
const files = [
{ path: "page.tsx" },
{ path: "components/foo.tsx" },
{ path: "components/baz.tsx" },
{ path: "components/boo/quip.tsx" },
]
const expectedOutput = [
{ name: "page.tsx", path: "page.tsx" },
{
name: "components",
children: [
{ name: "foo.tsx", path: "components/foo.tsx" },
{ name: "baz.tsx", path: "components/baz.tsx" },
{
name: "boo",
children: [{ name: "quip.tsx", path: "components/boo/quip.tsx" }],
},
],
},
]
const result = await createFileTreeForRegistryItemFiles(files)
expect(result).toEqual(expectedOutput)
})
it("should return an empty array for empty input", async () => {
const result = await createFileTreeForRegistryItemFiles([])
expect(result).toEqual([])
})
})

270
apps/www/lib/registry.ts Normal file
View File

@@ -0,0 +1,270 @@
import { promises as fs } from "fs"
import { tmpdir } from "os"
import path from "path"
import { Index } from "@/__registry__"
import { Project, ScriptKind, SourceFile, SyntaxKind } from "ts-morph"
import { z } from "zod"
import { Style } from "@/registry/registry-styles"
import { registryEntrySchema, registryItemFileSchema } from "@/registry/schema"
export const DEFAULT_REGISTRY_STYLE = "new-york" satisfies Style["name"]
const memoizedIndex: typeof Index = Object.fromEntries(
Object.entries(Index).map(([style, items]) => [style, { ...items }])
)
export function getRegistryComponent(
name: string,
style: Style["name"] = DEFAULT_REGISTRY_STYLE
) {
return memoizedIndex[style][name]?.component
}
export async function getRegistryItem(
name: string,
style: Style["name"] = DEFAULT_REGISTRY_STYLE
) {
const item = memoizedIndex[style][name]
if (!item) {
return null
}
// Convert all file paths to object.
// TODO: remove when we migrate to new registry.
item.files = item.files.map((file: unknown) =>
typeof file === "string" ? { path: file } : file
)
// Fail early before doing expensive file operations.
const result = registryEntrySchema.safeParse(item)
if (!result.success) {
return null
}
let files: typeof result.data.files = []
for (const file of item.files) {
const content = await getFileContent(file.path)
const relativePath = path.relative(process.cwd(), file.path)
files.push({
...file,
path: relativePath,
content,
})
}
// Get meta.
// Assume the first file is the main file.
const meta = await getFileMeta(files[0].path)
// Fix file paths.
files = fixFilePaths(files)
const parsed = registryEntrySchema.safeParse({
...result.data,
files,
meta,
})
if (!parsed.success) {
console.error(parsed.error.message)
return null
}
return parsed.data
}
async function getFileContent(filePath: string) {
const raw = await fs.readFile(filePath, "utf-8")
const project = new Project({
compilerOptions: {},
})
const tempFile = await createTempSourceFile(filePath)
const sourceFile = project.createSourceFile(tempFile, raw, {
scriptKind: ScriptKind.TSX,
})
// Remove meta variables.
removeVariable(sourceFile, "iframeHeight")
removeVariable(sourceFile, "containerClassName")
removeVariable(sourceFile, "description")
let code = sourceFile.getFullText()
// Format the code.
code = code.replaceAll("export default", "export")
// Fix imports.
code = fixImport(code)
return code
}
async function getFileMeta(filePath: string) {
const raw = await fs.readFile(filePath, "utf-8")
const project = new Project({
compilerOptions: {},
})
const tempFile = await createTempSourceFile(filePath)
const sourceFile = project.createSourceFile(tempFile, raw, {
scriptKind: ScriptKind.TSX,
})
const iframeHeight = extractVariable(sourceFile, "iframeHeight")
const containerClassName = extractVariable(sourceFile, "containerClassName")
const description = extractVariable(sourceFile, "description")
return {
iframeHeight,
containerClassName,
description,
}
}
function getFileTarget(file: z.infer<typeof registryItemFileSchema>) {
let target = file.target
if (!target || target === "") {
const fileName = file.path.split("/").pop()
if (
file.type === "registry:block" ||
file.type === "registry:component" ||
file.type === "registry:example"
) {
target = `components/${fileName}`
}
if (file.type === "registry:ui") {
target = `components/ui/${fileName}`
}
if (file.type === "registry:hook") {
target = `hooks/${fileName}`
}
if (file.type === "registry:lib") {
target = `lib/${fileName}`
}
}
return target
}
async function createTempSourceFile(filename: string) {
const dir = await fs.mkdtemp(path.join(tmpdir(), "shadcn-"))
return path.join(dir, filename)
}
function removeVariable(sourceFile: SourceFile, name: string) {
sourceFile.getVariableDeclaration(name)?.remove()
}
function extractVariable(sourceFile: SourceFile, name: string) {
const variable = sourceFile.getVariableDeclaration(name)
if (!variable) {
return null
}
const value = variable
.getInitializerIfKindOrThrow(SyntaxKind.StringLiteral)
.getLiteralValue()
variable.remove()
return value
}
function fixFilePaths(files: z.infer<typeof registryEntrySchema>["files"]) {
if (!files) {
return []
}
// Resolve all paths relative to the first file's directory.
const firstFilePath = files[0].path
const firstFilePathDir = path.dirname(firstFilePath)
return files.map((file) => {
return {
...file,
path: path.relative(firstFilePathDir, file.path),
target: getFileTarget(file),
}
})
}
export function fixImport(content: string) {
const regex = /@\/(.+?)\/((?:.*?\/)?(?:components|ui|hooks|lib))\/([\w-]+)/g
const replacement = (
match: string,
path: string,
type: string,
component: string
) => {
if (type.endsWith("components")) {
return `@/components/${component}`
} else if (type.endsWith("ui")) {
return `@/components/ui/${component}`
} else if (type.endsWith("hooks")) {
return `@/hooks/${component}`
} else if (type.endsWith("lib")) {
return `@/lib/${component}`
}
return match
}
return content.replace(regex, replacement)
}
export type FileTree = {
name: string
path?: string
children?: FileTree[]
}
export function createFileTreeForRegistryItemFiles(
files: Array<{ path: string; target?: string }>
) {
const root: FileTree[] = []
for (const file of files) {
const path = file.target ?? file.path
const parts = path.split("/")
let currentLevel = root
for (let i = 0; i < parts.length; i++) {
const part = parts[i]
const isFile = i === parts.length - 1
const existingNode = currentLevel.find((node) => node.name === part)
if (existingNode) {
if (isFile) {
// Update existing file node with full path
existingNode.path = path
} else {
// Move to next level in the tree
currentLevel = existingNode.children!
}
} else {
const newNode: FileTree = isFile
? { name: part, path }
: { name: part, children: [] }
currentLevel.push(newNode)
if (!isFile) {
currentLevel = newNode.children!
}
}
}
}
return root
}

View File

@@ -42,12 +42,12 @@ export function rehypeComponent() {
file.endsWith(`${fileName}.tsx`) || file.endsWith(`${fileName}.tsx`) ||
file.endsWith(`${fileName}.ts`) file.endsWith(`${fileName}.ts`)
) )
}) || component.files[0] }) || component.files[0]?.path
: component.files[0] : component.files[0]?.path
} }
// Read the source file. // Read the source file.
const filePath = path.join(process.cwd(), src) const filePath = src
let source = fs.readFileSync(filePath, "utf8") let source = fs.readFileSync(filePath, "utf8")
// Replace imports. // Replace imports.
@@ -106,10 +106,10 @@ export function rehypeComponent() {
try { try {
for (const style of styles) { for (const style of styles) {
const component = Index[style.name][name] const component = Index[style.name][name]
const src = component.files[0] const src = component.files[0]?.path
// Read the source file. // Read the source file.
const filePath = path.join(process.cwd(), src) const filePath = src
let source = fs.readFileSync(filePath, "utf8") let source = fs.readFileSync(filePath, "utf8")
// Replace imports. // Replace imports.

View File

@@ -2,6 +2,11 @@ import { createContentlayerPlugin } from "next-contentlayer2"
/** @type {import('next').NextConfig} */ /** @type {import('next').NextConfig} */
const nextConfig = { const nextConfig = {
experimental: {
outputFileTracingIncludes: {
"/blocks/*": ["./registry/**/*"],
},
},
reactStrictMode: true, reactStrictMode: true,
swcMinify: true, swcMinify: true,
images: { images: {

View File

@@ -67,12 +67,12 @@
"geist": "^1.2.2", "geist": "^1.2.2",
"input-otp": "^1.2.2", "input-otp": "^1.2.2",
"jotai": "^2.1.0", "jotai": "^2.1.0",
"lodash.template": "^4.5.0", "lodash": "^4.17.21",
"lucide-react": "0.359.0", "lucide-react": "0.359.0",
"markdown-wasm": "^1.2.0", "markdown-wasm": "^1.2.0",
"next": "14.3.0-canary.43", "next": "14.3.0-canary.43",
"next-contentlayer2": "^0.4.6", "next-contentlayer2": "^0.4.6",
"next-themes": "^0.2.1", "next-themes": "^0.4.3",
"react": "^18.2.0", "react": "^18.2.0",
"react-day-picker": "^8.7.1", "react-day-picker": "^8.7.1",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
@@ -85,12 +85,12 @@
"swr": "2.2.6-beta.3", "swr": "2.2.6-beta.3",
"tailwind-merge": "^1.12.0", "tailwind-merge": "^1.12.0",
"ts-morph": "^22.0.0", "ts-morph": "^22.0.0",
"vaul": "0.9.0", "vaul": "1.1.1",
"zod": "^3.21.4" "zod": "^3.21.4"
}, },
"devDependencies": { "devDependencies": {
"@shikijs/compat": "^1.1.7", "@shikijs/compat": "^1.1.7",
"@types/lodash.template": "^4.5.1", "@types/lodash": "^4.17.7",
"@types/node": "^17.0.45", "@types/node": "^17.0.45",
"@types/react": "^18.2.65", "@types/react": "^18.2.65",
"@types/react-color": "^3.0.6", "@types/react-color": "^3.0.6",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 246 B

After

Width:  |  Height:  |  Size: 191 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 324 B

After

Width:  |  Height:  |  Size: 241 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 379 B

View File

@@ -0,0 +1,150 @@
{
"AlertCircle": {
"lucide": "AlertCircle",
"radix": "ExclamationTriangleIcon"
},
"ArrowLeft": {
"lucide": "ArrowLeft",
"radix": "ArrowLeftIcon"
},
"ArrowRight": {
"lucide": "ArrowRight",
"radix": "ArrowRightIcon"
},
"ArrowUpDown": {
"lucide": "ArrowUpDown",
"radix": "CaretSortIcon"
},
"BellRing": {
"lucide": "BellRing",
"radix": "BellIcon"
},
"Bold": {
"lucide": "Bold",
"radix": "FontBoldIcon"
},
"Calculator": {
"lucide": "Calculator",
"radix": "ComponentPlaceholderIcon"
},
"Calendar": {
"lucide": "Calendar",
"radix": "CalendarIcon"
},
"Check": {
"lucide": "Check",
"radix": "CheckIcon"
},
"ChevronDown": {
"lucide": "ChevronDown",
"radix": "ChevronDownIcon"
},
"ChevronLeft": {
"lucide": "ChevronLeft",
"radix": "ChevronLeftIcon"
},
"ChevronRight": {
"lucide": "ChevronRight",
"radix": "ChevronRightIcon"
},
"ChevronUp": {
"lucide": "ChevronUp",
"radix": "ChevronUpIcon"
},
"ChevronsUpDown": {
"lucide": "ChevronsUpDown",
"radix": "CaretSortIcon"
},
"Circle": {
"lucide": "Circle",
"radix": "DotFilledIcon"
},
"Copy": {
"lucide": "Copy",
"radix": "CopyIcon"
},
"CreditCard": {
"lucide": "CreditCard",
"radix": "ComponentPlaceholderIcon"
},
"GripVertical": {
"lucide": "GripVertical",
"radix": "DragHandleDots2Icon"
},
"Italic": {
"lucide": "Italic",
"radix": "FontItalicIcon"
},
"Loader2": {
"lucide": "Loader2",
"radix": "ReloadIcon"
},
"Mail": {
"lucide": "Mail",
"radix": "EnvelopeClosedIcon"
},
"MailOpen": {
"lucide": "MailOpen",
"radix": "EnvelopeOpenIcon"
},
"Minus": {
"lucide": "Minus",
"radix": "MinusIcon"
},
"Moon": {
"lucide": "Moon",
"radix": "MoonIcon"
},
"MoreHorizontal": {
"lucide": "MoreHorizontal",
"radix": "DotsHorizontalIcon"
},
"PanelLeft": {
"lucide": "PanelLeft",
"radix": "ViewVerticalIcon"
},
"Plus": {
"lucide": "Plus",
"radix": "PlusIcon"
},
"Search": {
"lucide": "Search",
"radix": "MagnifyingGlassIcon"
},
"Send": {
"lucide": "Send",
"radix": "PaperPlaneIcon"
},
"Settings": {
"lucide": "Settings",
"radix": "GearIcon"
},
"Slash": {
"lucide": "Slash",
"radix": "SlashIcon"
},
"Smile": {
"lucide": "Smile",
"radix": "FaceIcon"
},
"Sun": {
"lucide": "Sun",
"radix": "SunIcon"
},
"Terminal": {
"lucide": "Terminal",
"radix": "RocketIcon"
},
"Underline": {
"lucide": "Underline",
"radix": "UnderlineIcon"
},
"User": {
"lucide": "User",
"radix": "PersonIcon"
},
"X": {
"lucide": "X",
"radix": "Cross2Icon"
}
}

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More