Compare commits

...

53 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
github-actions[bot]
2b276de95a chore(release): version packages (#5625)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-10-29 21:39:58 +04:00
shadcn
64739f8399 feat: react 19 (#5621)
* feat(shadcn): add flag prompt for npm

* docs: add docs for react 19

* chore: add changeset

* test: update snapshots

* docs: add notes for recharts

* docs: fix

* fix

* fix: linting
2024-10-29 21:30:05 +04:00
shadcn
f0cff7e0eb fix: gaps in command and dropdown-menu (#5570)
* fix: gaps in command and dropdown-menu

* chore: update snapshots
2024-10-25 18:13:13 +04:00
github-actions[bot]
e242adaa9c chore(release): version packages (#5568)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-10-25 17:52:32 +04:00
Kiyan
986c00ee0e fix: shadcn init with correct packageManager (#5299)
* fix: shadcn init with correct  packageManager

* chore: changeset

---------

Co-authored-by: shadcn <m@shadcn.com>
2024-10-25 17:46:48 +04:00
shadcn
d0eece06d4 fix: update registry (#5530) 2024-10-23 17:35:26 +04:00
1001 changed files with 20717 additions and 4596 deletions

View File

@@ -91,6 +91,42 @@ pnpm --filter=www 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
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 date = new Date(item.date)
const now = new Date()
const referenceDate = new Date("2024-06-30")
let daysToSubtract = 90
if (timeRange === "30d") {
daysToSubtract = 30
} else if (timeRange === "7d") {
daysToSubtract = 7
}
now.setDate(now.getDate() - daysToSubtract)
return date >= now
const startDate = new Date(referenceDate)
startDate.setDate(startDate.getDate() - daysToSubtract)
return date >= startDate
})
return (

View File

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

View File

@@ -1,27 +1,18 @@
import * as React from "react"
import { unstable_cache } from "next/cache"
import { getAllBlockIds } from "@/lib/blocks"
import { BlockDisplay } from "@/components/block-display"
const BLOCKS_WHITELIST_PREFIXES = ["sidebar", "login"]
const getBlocks = unstable_cache(async () => {
return (await getAllBlockIds()).filter((name) =>
BLOCKS_WHITELIST_PREFIXES.some((prefix) => name.startsWith(prefix))
)
}, ["blocks"])
import "@/styles/mdx.css"
export default async function BlocksPage() {
const blocks = await getBlocks()
const blocks = await getAllBlockIds()
return (
<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">
{blocks.map((name, index) => (
<React.Suspense key={`${name}-${index}`}>
<BlockDisplay name={name} />
</React.Suspense>
{blocks.map((name) => (
<BlockDisplay key={name} name={name} />
))}
</div>
</div>

View File

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

View File

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

View File

@@ -4,7 +4,7 @@ import { allDocs } from "contentlayer/generated"
import "@/styles/mdx.css"
import type { Metadata } from "next"
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 { siteConfig } from "@/config/site"
@@ -15,7 +15,6 @@ import { OpenInV0Cta } from "@/components/open-in-v0-cta"
import { DocsPager } from "@/components/pager"
import { DashboardTableOfContents } from "@/components/toc"
import { badgeVariants } from "@/registry/new-york/ui/badge"
import { ScrollArea } from "@/registry/new-york/ui/scroll-area"
interface DocPageProps {
params: {
@@ -89,10 +88,10 @@ export default async function DocPage({ params }: DocPageProps) {
return (
<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="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>
<div className="space-y-2">
@@ -115,7 +114,7 @@ export default async function DocPage({ params }: DocPageProps) {
className={cn(badgeVariants({ variant: "secondary" }), "gap-1")}
>
Docs
<ExternalLinkIcon className="h-3 w-3" />
<ExternalLink className="h-3 w-3" />
</Link>
)}
{doc.links?.api && (
@@ -126,7 +125,7 @@ export default async function DocPage({ params }: DocPageProps) {
className={cn(badgeVariants({ variant: "secondary" }), "gap-1")}
>
API Reference
<ExternalLinkIcon className="h-3 w-3" />
<ExternalLink className="h-3 w-3" />
</Link>
)}
</div>
@@ -137,11 +136,11 @@ export default async function DocPage({ params }: DocPageProps) {
<DocsPager doc={doc} />
</div>
<div className="hidden text-sm xl:block">
<div className="sticky top-16 -mt-10 h-[calc(100vh-3.5rem)] pt-4">
<ScrollArea className="h-full pb-10">
<div className="sticky top-20 -mt-6 h-[calc(100vh-3.5rem)] pt-4">
<div className="no-scrollbar h-full overflow-auto pb-10">
{doc.toc && <DashboardTableOfContents toc={toc} />}
<OpenInV0Cta className="mt-6 max-w-[80%]" />
</ScrollArea>
</div>
</div>
</div>
</main>

View File

@@ -8,15 +8,13 @@ interface DocsLayoutProps {
export default function DocsLayout({ children }: DocsLayoutProps) {
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">
<aside className="fixed top-14 z-30 -ml-2 hidden h-[calc(100vh-3.5rem)] w-full shrink-0 md:sticky md:block">
<ScrollArea className="h-full py-6 pr-6 lg:py-8">
<DocsSidebarNav config={docsConfig} />
</ScrollArea>
</aside>
{children}
</div>
<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">
<div className="no-scrollbar h-full overflow-auto py-6 pr-6 lg:py-8">
<DocsSidebarNav config={docsConfig} />
</div>
</aside>
{children}
</div>
)
}

View File

@@ -1,9 +1,4 @@
import {
ChevronDownIcon,
CircleIcon,
PlusIcon,
StarIcon,
} from "@radix-ui/react-icons"
import { ChevronDown, Circle, Plus, Star } from "lucide-react"
import { Button } from "@/registry/new-york/ui/button"
import {
@@ -37,14 +32,14 @@ export function DemoGithub() {
</div>
<div className="flex items-center space-x-1 rounded-md bg-secondary text-secondary-foreground">
<Button variant="secondary" className="px-3 shadow-none">
<StarIcon className="mr-2 h-4 w-4" />
<Star />
Star
</Button>
<Separator orientation="vertical" className="h-[20px]" />
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="secondary" className="px-2 shadow-none">
<ChevronDownIcon className="h-4 w-4 text-secondary-foreground" />
<ChevronDown className="text-secondary-foreground" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent
@@ -62,7 +57,7 @@ export function DemoGithub() {
<DropdownMenuCheckboxItem>Inspiration</DropdownMenuCheckboxItem>
<DropdownMenuSeparator />
<DropdownMenuItem>
<PlusIcon className="mr-2 h-4 w-4" /> Create List
<Plus /> Create List
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
@@ -71,11 +66,11 @@ export function DemoGithub() {
<CardContent>
<div className="flex space-x-4 text-sm text-muted-foreground">
<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
</div>
<div className="flex items-center">
<StarIcon className="mr-1 h-3 w-3" />
<Star className="mr-1 h-3 w-3" />
20k
</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 {
Card,
@@ -19,7 +19,7 @@ export function DemoNotifications() {
</CardHeader>
<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">
<BellIcon className="mt-px h-5 w-5" />
<Bell className="mt-px h-5 w-5" />
<div className="space-y-1">
<p className="text-sm font-medium leading-none">Everything</p>
<p className="text-sm text-muted-foreground">
@@ -28,7 +28,7 @@ export function DemoNotifications() {
</div>
</div>
<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">
<p className="text-sm font-medium leading-none">Available</p>
<p className="text-sm text-muted-foreground">
@@ -37,7 +37,7 @@ export function DemoNotifications() {
</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">
<EyeNoneIcon className="mt-px h-5 w-5" />
<EyeOff className="mt-px h-5 w-5" />
<div className="space-y-1">
<p className="text-sm font-medium leading-none">Ignoring</p>
<p className="text-sm text-muted-foreground">

View File

@@ -41,7 +41,7 @@ export function DemoShareDocument() {
</div>
<Separator className="my-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="flex items-center justify-between 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 {
Avatar,
@@ -51,8 +51,7 @@ export function DemoTeamMembers() {
<Popover>
<PopoverTrigger asChild>
<Button variant="outline" className="ml-auto">
Owner{" "}
<ChevronDownIcon className="ml-2 h-4 w-4 text-muted-foreground" />
Owner <ChevronDown className="text-muted-foreground" />
</Button>
</PopoverTrigger>
<PopoverContent className="p-0" align="end">
@@ -105,8 +104,7 @@ export function DemoTeamMembers() {
<Popover>
<PopoverTrigger asChild>
<Button variant="outline" className="ml-auto">
Member{" "}
<ChevronDownIcon className="ml-2 h-4 w-4 text-muted-foreground" />
Member <ChevronDown className="text-muted-foreground" />
</Button>
</PopoverTrigger>
<PopoverContent className="p-0" align="end">

View File

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

View File

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

View File

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

View File

@@ -1,7 +1,7 @@
"use client"
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 { z } from "zod"
@@ -76,7 +76,7 @@ export function AppearanceForm() {
<option value="system">System</option>
</select>
</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>
<FormDescription>
Set the font you want to use in the dashboard.

View File

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

View File

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

View File

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

View File

@@ -1,8 +1,8 @@
"use client"
import * as React from "react"
import { CaretSortIcon, CheckIcon } from "@radix-ui/react-icons"
import { PopoverProps } from "@radix-ui/react-popover"
import { Check, ChevronsUpDown } from "lucide-react"
import { cn } from "@/lib/utils"
import { useMutationObserver } from "@/hooks/use-mutation-observer"
@@ -64,7 +64,7 @@ export function ModelSelector({ models, types, ...props }: ModelSelectorProps) {
className="w-full justify-between"
>
{selectedModel ? selectedModel.name : "Select a model..."}
<CaretSortIcon className="ml-2 h-4 w-4 shrink-0 opacity-50" />
<ChevronsUpDown className="opacity-50" />
</Button>
</PopoverTrigger>
<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"
>
{model.name}
<CheckIcon
className={cn(
"ml-auto h-4 w-4",
isSelected ? "opacity-100" : "opacity-0"
)}
<Check
className={cn("ml-auto", isSelected ? "opacity-100" : "opacity-0")}
/>
</CommandItem>
)

View File

@@ -2,7 +2,7 @@
import * as React from "react"
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 {
@@ -40,9 +40,9 @@ export function PresetActions() {
<>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="secondary">
<Button variant="secondary" size="icon">
<span className="sr-only">Actions</span>
<DotsHorizontalIcon className="h-4 w-4" />
<MoreHorizontal />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">

View File

@@ -2,8 +2,8 @@
import * as React from "react"
import { useRouter } from "next/navigation"
import { CaretSortIcon, CheckIcon } from "@radix-ui/react-icons"
import { PopoverProps } from "@radix-ui/react-popover"
import { Check, ChevronsUpDown } from "lucide-react"
import { cn } from "@/lib/utils"
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]"
>
{selectedPreset ? selectedPreset.name : "Load a preset..."}
<CaretSortIcon className="ml-2 h-4 w-4 shrink-0 opacity-50" />
<ChevronsUpDown className="opacity-50" />
</Button>
</PopoverTrigger>
<PopoverContent className="w-[300px] p-0">
@@ -61,9 +61,9 @@ export function PresetSelector({ presets, ...props }: PresetSelectorProps) {
}}
>
{preset.name}
<CheckIcon
<Check
className={cn(
"ml-auto h-4 w-4",
"ml-auto",
selectedPreset?.id === preset.id
? "opacity-100"
: "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 { Input } from "@/registry/new-york/ui/input"
@@ -37,7 +37,7 @@ export function PresetShare() {
</div>
<Button type="submit" size="sm" className="px-3">
<span className="sr-only">Copy</span>
<CopyIcon className="h-4 w-4" />
<Copy />
</Button>
</div>
</PopoverContent>

View File

@@ -1,6 +1,6 @@
import { Metadata } from "next"
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 {
@@ -265,7 +265,7 @@ export default function PlaygroundPage() {
<Button>Submit</Button>
<Button variant="secondary">
<span className="sr-only">Show history</span>
<CounterClockwiseClockIcon className="h-4 w-4" />
<RotateCcw />
</Button>
</div>
</div>
@@ -283,7 +283,7 @@ export default function PlaygroundPage() {
<Button>Submit</Button>
<Button variant="secondary">
<span className="sr-only">Show history</span>
<CounterClockwiseClockIcon className="h-4 w-4" />
<RotateCcw />
</Button>
</div>
</div>
@@ -314,7 +314,7 @@ export default function PlaygroundPage() {
<Button>Submit</Button>
<Button variant="secondary">
<span className="sr-only">Show history</span>
<CounterClockwiseClockIcon className="h-4 w-4" />
<RotateCcw />
</Button>
</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 { ArrowDown, ArrowUp, ChevronsUpDown, EyeOff } from "lucide-react"
import { cn } from "@/lib/utils"
import { Button } from "@/registry/new-york/ui/button"
@@ -42,26 +37,26 @@ export function DataTableColumnHeader<TData, TValue>({
>
<span>{title}</span>
{column.getIsSorted() === "desc" ? (
<ArrowDownIcon className="ml-2 h-4 w-4" />
<ArrowDown />
) : column.getIsSorted() === "asc" ? (
<ArrowUpIcon className="ml-2 h-4 w-4" />
<ArrowUp />
) : (
<CaretSortIcon className="ml-2 h-4 w-4" />
<ChevronsUpDown />
)}
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start">
<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
</DropdownMenuItem>
<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
</DropdownMenuItem>
<DropdownMenuSeparator />
<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
</DropdownMenuItem>
</DropdownMenuContent>

View File

@@ -1,6 +1,6 @@
import * as React from "react"
import { CheckIcon, PlusCircledIcon } from "@radix-ui/react-icons"
import { Column } from "@tanstack/react-table"
import { Check, PlusCircle } from "lucide-react"
import { cn } from "@/lib/utils"
import { Badge } from "@/registry/new-york/ui/badge"
@@ -43,7 +43,7 @@ export function DataTableFacetedFilter<TData, TValue>({
<Popover>
<PopoverTrigger asChild>
<Button variant="outline" size="sm" className="h-8 border-dashed">
<PlusCircledIcon className="mr-2 h-4 w-4" />
<PlusCircle />
{title}
{selectedValues?.size > 0 && (
<>
@@ -111,7 +111,7 @@ export function DataTableFacetedFilter<TData, TValue>({
: "opacity-50 [&_svg]:invisible"
)}
>
<CheckIcon className={cn("h-4 w-4")} />
<Check />
</div>
{option.icon && (
<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 {
ChevronLeft,
ChevronRight,
ChevronsLeft,
ChevronsRight,
} from "lucide-react"
import { Button } from "@/registry/new-york/ui/button"
import {
@@ -61,7 +61,7 @@ export function DataTablePagination<TData>({
disabled={!table.getCanPreviousPage()}
>
<span className="sr-only">Go to first page</span>
<DoubleArrowLeftIcon className="h-4 w-4" />
<ChevronsLeft />
</Button>
<Button
variant="outline"
@@ -70,7 +70,7 @@ export function DataTablePagination<TData>({
disabled={!table.getCanPreviousPage()}
>
<span className="sr-only">Go to previous page</span>
<ChevronLeftIcon className="h-4 w-4" />
<ChevronLeft />
</Button>
<Button
variant="outline"
@@ -79,7 +79,7 @@ export function DataTablePagination<TData>({
disabled={!table.getCanNextPage()}
>
<span className="sr-only">Go to next page</span>
<ChevronRightIcon className="h-4 w-4" />
<ChevronRight />
</Button>
<Button
variant="outline"
@@ -88,7 +88,7 @@ export function DataTablePagination<TData>({
disabled={!table.getCanNextPage()}
>
<span className="sr-only">Go to last page</span>
<DoubleArrowRightIcon className="h-4 w-4" />
<ChevronsRight />
</Button>
</div>
</div>

View File

@@ -1,7 +1,7 @@
"use client"
import { DotsHorizontalIcon } from "@radix-ui/react-icons"
import { Row } from "@tanstack/react-table"
import { MoreHorizontal } from "lucide-react"
import { Button } from "@/registry/new-york/ui/button"
import {
@@ -37,7 +37,7 @@ export function DataTableRowActions<TData>({
variant="ghost"
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>
</Button>
</DropdownMenuTrigger>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,73 +1,14 @@
"use client"
import * as React from "react"
import { useConfig } from "@/hooks/use-config"
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"
export function ThemesTabs() {
const [mounted, setMounted] = React.useState(false)
const [config] = useConfig()
React.useEffect(() => {
setMounted(true)
}, [])
return (
<div className="space-y-8">
{!mounted ? (
<div className="md:grids-col-2 grid md:gap-4 lg:grid-cols-10 xl:gap-6">
<div className="space-y-4 lg:col-span-4 xl:col-span-6 xl:space-y-6">
<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>
)}
<ThemeWrapper>
<CardsNewYork />
</ThemeWrapper>
</div>
)
}

View File

@@ -1,14 +1,20 @@
import * as React from "react"
import { Metadata } from "next"
import { notFound } from "next/navigation"
import { siteConfig } from "@/config/site"
import { getAllBlockIds, getBlock } from "@/lib/blocks"
import { getAllBlockIds } from "@/lib/blocks"
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 "@/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({
params,
@@ -19,23 +25,23 @@ export async function generateMetadata({
}
}): Promise<Metadata> {
const { name, style } = params
const block = await getBlock(name, style)
const item = await getCachedRegistryItem(name, style)
if (!block) {
if (!item) {
return {}
}
const title = block.name
const description = block.description
const title = item.name
const description = item.description
return {
title: `${block.description} - ${block.name}`,
title: `${item.name}${item.description ? ` - ${item.description}` : ""}`,
description,
openGraph: {
title,
description,
type: "article",
url: absoluteUrl(`/blocks/${block.name}`),
url: absoluteUrl(`/blocks/${style}/${item.name}`),
images: [
{
url: siteConfig.ogImage,
@@ -76,39 +82,22 @@ export default async function BlockPage({
}
}) {
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()
}
const Component = block.component
const chunks = block.chunks?.map((chunk) => ({ ...chunk }))
delete block.component
block.chunks?.map((chunk) => delete chunk.component)
return (
<>
{/* <ThemesStyle /> */}
<div
className={cn(
"themes-wrapper bg-background",
block.container?.className
item.meta?.containerClassName
)}
>
<BlockWrapper block={block}>
<Component />
{chunks?.map((chunk, index) => (
<BlockChunk
key={chunk.name}
block={block}
chunk={block.chunks?.[index]}
>
<chunk.component />
</BlockChunk>
))}
</BlockWrapper>
<Component />
</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 { Metadata, Viewport } from "next"
import { siteConfig } from "@/config/site"
import { fontSans } from "@/lib/fonts"
import { META_THEME_COLORS, siteConfig } from "@/config/site"
import { fontMono, fontSans } from "@/lib/fonts"
import { cn } from "@/lib/utils"
import { Analytics } from "@/components/analytics"
import { ThemeProvider } from "@/components/providers"
@@ -65,10 +65,7 @@ export const metadata: Metadata = {
}
export const viewport: Viewport = {
themeColor: [
{ media: "(prefers-color-scheme: light)", color: "white" },
{ media: "(prefers-color-scheme: dark)", color: "black" },
],
themeColor: META_THEME_COLORS.light,
}
interface RootLayoutProps {
@@ -79,11 +76,24 @@ export default function RootLayout({ children }: RootLayoutProps) {
return (
<>
<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
className={cn(
"min-h-screen bg-background font-sans antialiased",
fontSans.variable
fontSans.variable,
fontMono.variable
)}
>
<ThemeProvider
@@ -91,6 +101,7 @@ export default function RootLayout({ children }: RootLayoutProps) {
defaultTheme="system"
enableSystem
disableTransitionOnChange
enableColorScheme
>
<div vaul-drawer-wrapper="">
<div className="relative flex min-h-screen flex-col bg-background">

View File

@@ -1,21 +1,16 @@
import Link from "next/link"
import { ArrowRightIcon } from "@radix-ui/react-icons"
import { Blocks, PieChart } from "lucide-react"
import { Separator } from "@/registry/new-york/ui/separator"
import { ArrowRight } from "lucide-react"
export function Announcement() {
return (
<Link
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">
New sidebar component
</span>
<ArrowRightIcon className="ml-1 h-4 w-4" />
<ArrowRight className="ml-1 h-4 w-4" />
</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 { BlockPreview } from "@/components/block-preview"
const getBlockByName = unstable_cache(
async (name: string) => {
const block = await getBlock(name)
if (!block) {
return null
}
return {
name: block.name,
style: block.style,
description: block.description,
container: block.container,
}
},
["block"]
)
import { highlightCode } from "@/lib/highlight-code"
import {
createFileTreeForRegistryItemFiles,
getRegistryItem,
} from "@/lib/registry"
import { BlockViewer } from "@/components/block-viewer"
import { registryItemFileSchema } from "@/registry/schema"
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 <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

@@ -1,18 +1,19 @@
import { cn } from "@/lib/utils"
import {
Alert,
AlertDescription,
AlertTitle,
} from "@/registry/new-york/ui/alert"
interface CalloutProps {
icon?: string
title?: string
children?: React.ReactNode
}
export function Callout({ title, children, icon, ...props }: CalloutProps) {
export function Callout({
title,
children,
icon,
className,
...props
}: React.ComponentProps<typeof Alert> & { icon?: string }) {
return (
<Alert {...props}>
<Alert className={cn("bg-muted/50", className)} {...props}>
{icon && <span className="mr-4 text-2xl">{icon}</span>}
{title && <AlertTitle>{title}</AlertTitle>}
<AlertDescription>{children}</AlertDescription>

View File

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

View File

@@ -12,7 +12,7 @@ import {
TooltipTrigger,
} from "@/registry/new-york/ui/tooltip"
export function BlockCopyButton({
export function ChartCopyButton({
event,
name,
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 { ChartToolbar } from "@/components/chart-toolbar"
import { registryEntrySchema } from "@/registry/schema"
export type Chart = z.infer<typeof registryEntrySchema> & {
highlightedCode: string
}
export async function ChartDisplay({
name,
children,
className,
}: { 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.
delete chart?.component
delete chart?.chunks
if (!chart) {
if (!chart || !highlightedCode) {
return null
}
@@ -25,7 +33,7 @@ export async function ChartDisplay({
)}
>
<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"
>
{children}
@@ -36,3 +44,11 @@ export async function ChartDisplay({
</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"
import { cn } from "@/lib/utils"
import { BlockCopyButton } from "@/components/block-copy-button"
import { ChartCodeViewer } from "@/components/chart-code-viewer"
import { Separator } from "@/registry/new-york/ui/separator"
import { Block } from "@/registry/schema"
import "@/styles/mdx.css"
import {
@@ -17,21 +15,26 @@ import {
Radar,
} from "lucide-react"
import { ChartCopyButton } from "@/components/chart-copy-button"
import { Chart } from "@/components/chart-display"
export function ChartToolbar({
chart,
className,
children,
}: { chart: Block } & React.ComponentProps<"div">) {
}: {
chart: Chart
} & React.ComponentProps<"div">) {
return (
<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]">
<ChartTitle chart={chart} />
</div>
<div className="ml-auto flex items-center gap-2 [&>form]:flex">
<BlockCopyButton
<ChartCopyButton
event="copy_chart_code"
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"
/>
<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
if (!subcategory) {

View File

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

View File

@@ -3,13 +3,7 @@
import * as React from "react"
import { useRouter } from "next/navigation"
import { type DialogProps } from "@radix-ui/react-dialog"
import {
CircleIcon,
FileIcon,
LaptopIcon,
MoonIcon,
SunIcon,
} from "@radix-ui/react-icons"
import { Circle, File, Laptop, Moon, Sun } from "lucide-react"
import { useTheme } from "next-themes"
import { docsConfig } from "@/config/docs"
@@ -61,7 +55,7 @@ export function CommandMenu({ ...props }: DialogProps) {
<Button
variant="outline"
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)}
{...props}
@@ -87,7 +81,7 @@ export function CommandMenu({ ...props }: DialogProps) {
runCommand(() => router.push(navItem.href as string))
}}
>
<FileIcon className="mr-2 h-4 w-4" />
<File />
{navItem.title}
</CommandItem>
))}
@@ -103,7 +97,7 @@ export function CommandMenu({ ...props }: DialogProps) {
}}
>
<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>
{navItem.title}
</CommandItem>
@@ -113,15 +107,15 @@ export function CommandMenu({ ...props }: DialogProps) {
<CommandSeparator />
<CommandGroup heading="Theme">
<CommandItem onSelect={() => runCommand(() => setTheme("light"))}>
<SunIcon className="mr-2 h-4 w-4" />
<Sun />
Light
</CommandItem>
<CommandItem onSelect={() => runCommand(() => setTheme("dark"))}>
<MoonIcon className="mr-2 h-4 w-4" />
<Moon />
Dark
</CommandItem>
<CommandItem onSelect={() => runCommand(() => setTheme("system"))}>
<LaptopIcon className="mr-2 h-4 w-4" />
<Laptop />
System
</CommandItem>
</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"
/>
<div className="absolute inset-0 hidden w-[1600px] bg-background md:block">
<iframe
src={`/blocks/${config.style}/${name}`}
className="size-full"
/>
<iframe src={`/blocks/new-york/${name}`} className="size-full" />
</div>
</div>
)

View File

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

View File

@@ -12,18 +12,18 @@ export function MainNav() {
return (
<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" />
<span className="hidden font-bold lg:inline-block">
{siteConfig.name}
</span>
</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
href="/docs"
className={cn(
"transition-colors hover:text-foreground/80",
pathname === "/docs" ? "text-foreground" : "text-foreground/60"
pathname === "/docs" ? "text-foreground" : "text-foreground/80"
)}
>
Docs
@@ -35,7 +35,7 @@ export function MainNav() {
pathname?.startsWith("/docs/components") &&
!pathname?.startsWith("/docs/component/chart")
? "text-foreground"
: "text-foreground/60"
: "text-foreground/80"
)}
>
Components
@@ -46,7 +46,7 @@ export function MainNav() {
"transition-colors hover:text-foreground/80",
pathname?.startsWith("/blocks")
? "text-foreground"
: "text-foreground/60"
: "text-foreground/80"
)}
>
Blocks
@@ -58,7 +58,7 @@ export function MainNav() {
pathname?.startsWith("/docs/component/chart") ||
pathname?.startsWith("/charts")
? "text-foreground"
: "text-foreground/60"
: "text-foreground/80"
)}
>
Charts
@@ -69,29 +69,18 @@ export function MainNav() {
"transition-colors hover:text-foreground/80",
pathname?.startsWith("/themes")
? "text-foreground"
: "text-foreground/60"
: "text-foreground/80"
)}
>
Themes
</Link>
<Link
href="/examples"
className={cn(
"transition-colors hover:text-foreground/80",
pathname?.startsWith("/examples")
? "text-foreground"
: "text-foreground/60"
)}
>
Examples
</Link>
<Link
href="/colors"
className={cn(
"transition-colors hover:text-foreground/80",
pathname?.startsWith("/colors")
? "text-foreground"
: "text-foreground/60"
: "text-foreground/80"
)}
>
Colors

View File

@@ -139,10 +139,10 @@ const components = {
<hr className="my-4 md:my-8" {...props} />
),
table: ({ className, ...props }: React.HTMLAttributes<HTMLTableElement>) => (
<div className="my-6 w-full overflow-y-auto rounded-lg">
<div className="my-6 w-full overflow-y-auto">
<table
className={cn(
"relative w-full overflow-hidden text-sm after:absolute after:inset-0 after:rounded-lg after:ring-1 after:ring-border",
"relative w-full overflow-hidden border-none text-sm",
className
)}
{...props}
@@ -150,12 +150,15 @@ const components = {
</div>
),
tr: ({ className, ...props }: React.HTMLAttributes<HTMLTableRowElement>) => (
<tr className={cn("m-0 border-t", className)} {...props} />
<tr
className={cn("last:border-b-none m-0 border-b", className)}
{...props}
/>
),
th: ({ className, ...props }: React.HTMLAttributes<HTMLTableCellElement>) => (
<th
className={cn(
"border px-4 py-2 text-left font-bold [&[align=center]]:text-center [&[align=right]]:text-right",
"px-4 py-2 text-left font-bold [&[align=center]]:text-center [&[align=right]]:text-right",
className
)}
{...props}
@@ -164,7 +167,7 @@ const components = {
td: ({ className, ...props }: React.HTMLAttributes<HTMLTableCellElement>) => (
<td
className={cn(
"border px-4 py-2 text-left [&[align=center]]:text-center [&[align=right]]:text-right",
"px-4 py-2 text-left [&[align=center]]:text-center [&[align=right]]:text-right",
className
)}
{...props}

View File

@@ -3,68 +3,55 @@
import * as React from "react"
import Link, { LinkProps } from "next/link"
import { useRouter } from "next/navigation"
import { ViewVerticalIcon } from "@radix-ui/react-icons"
import { docsConfig } from "@/config/docs"
import { siteConfig } from "@/config/site"
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 { ScrollArea } from "@/registry/new-york/ui/scroll-area"
import { Sheet, SheetContent, SheetTrigger } from "@/registry/new-york/ui/sheet"
import {
Drawer,
DrawerContent,
DrawerTrigger,
} from "@/registry/new-york/ui/drawer"
export function MobileNav() {
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 (
<Sheet open={open} onOpenChange={setOpen}>
<SheetTrigger asChild>
<Drawer open={open} onOpenChange={onOpenChange}>
<DrawerTrigger asChild>
<Button
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
strokeWidth="1.5"
viewBox="0 0 24 24"
fill="none"
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
d="M3 5H11"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
></path>
<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>
d="M3.75 9h16.5m-16.5 6.75h16.5"
/>
</svg>
<span className="sr-only">Toggle Menu</span>
</Button>
</SheetTrigger>
<SheetContent side="left" className="pr-0">
<MobileLink
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">
</DrawerTrigger>
<DrawerContent className="max-h-[60svh] p-0">
<div className="overflow-auto p-6">
<div className="flex flex-col space-y-3">
{docsConfig.mainNav?.map(
(item) =>
@@ -108,9 +95,9 @@ export function MobileNav() {
</div>
))}
</div>
</ScrollArea>
</SheetContent>
</Sheet>
</div>
</DrawerContent>
</Drawer>
)
}
@@ -135,7 +122,7 @@ function MobileLink({
router.push(href.toString())
onOpenChange?.(false)
}}
className={cn(className)}
className={cn("text-base", className)}
{...props}
>
{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"
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 { Button } from "@/registry/new-york/ui/button"

View File

@@ -8,12 +8,12 @@ function PageHeader({
return (
<section
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
)}
{...props}
>
{children}
<div className="container">{children}</div>
</section>
)
}

View File

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

View File

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

View File

@@ -44,18 +44,16 @@ export function DocsSidebarNavItems({
pathname,
}: DocsSidebarNavItemsProps) {
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) =>
item.href && !item.disabled ? (
<Link
key={index}
href={item.href}
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",
pathname === item.href
? "font-medium text-foreground"
: "text-muted-foreground"
pathname === item.href && "underline"
)}
target={item.external ? "_blank" : ""}
rel={item.external ? "noreferrer" : ""}

View File

@@ -2,7 +2,7 @@ import { siteConfig } from "@/config/site"
export function SiteFooter() {
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">
<p className="text-balance text-center text-sm leading-loose text-muted-foreground md:text-left">
Built by{" "}

View File

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

View File

@@ -1,15 +1,8 @@
"use client"
import * as React from "react"
import {
CheckIcon,
CopyIcon,
InfoCircledIcon,
MoonIcon,
ResetIcon,
SunIcon,
} from "@radix-ui/react-icons"
import template from "lodash.template"
import template from "lodash/template"
import { Check, Copy, Moon, Repeat, Sun } from "lucide-react"
import { useTheme } from "next-themes"
import { cn } from "@/lib/utils"
@@ -79,77 +72,6 @@ export function ThemeCustomizer() {
<Customizer />
</PopoverContent>
</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>
<CopyCodeButton variant="ghost" size="sm" className="[&_svg]:hidden" />
</div>
@@ -173,10 +95,10 @@ function Customizer() {
<div className="flex items-start pt-4 md:pt-0">
<div className="space-y-1 pr-2">
<div className="font-semibold leading-none tracking-tight">
Customize
Theme Customizer
</div>
<div className="text-xs text-muted-foreground">
Pick a style and color for your components.
Customize your components colors.
</div>
</div>
<Button
@@ -191,110 +113,58 @@ function Customizer() {
})
}}
>
<ResetIcon />
<Repeat />
<span className="sr-only">Reset</span>
</Button>
</div>
<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">
<Label className="text-xs">Color</Label>
<div className="grid grid-cols-3 gap-2">
{baseColors.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 && <CheckIcon className="h-4 w-4 text-white" />}
</span>
{theme.label}
</Button>
) : (
<Skeleton className="h-8 w-full" key={theme.name} />
{baseColors
.filter(
(theme) =>
!["slate", "stone", "gray", "neutral"].includes(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 className="space-y-1.5">
@@ -334,7 +204,7 @@ function Customizer() {
onClick={() => setMode("light")}
className={cn(mode === "light" && "border-2 border-primary")}
>
<SunIcon className="mr-1 -translate-x-1" />
<Sun className="mr-1 -translate-x-1" />
Light
</Button>
<Button
@@ -343,7 +213,7 @@ function Customizer() {
onClick={() => setMode("dark")}
className={cn(mode === "dark" && "border-2 border-primary")}
>
<MoonIcon className="mr-1 -translate-x-1" />
<Moon className="mr-1 -translate-x-1" />
Dark
</Button>
</>
@@ -391,11 +261,7 @@ function CopyCodeButton({
className={cn("md:hidden", className)}
{...props}
>
{hasCopied ? (
<CheckIcon className="mr-2 h-4 w-4" />
) : (
<CopyIcon className="mr-2 h-4 w-4" />
)}
{hasCopied ? <Check /> : <Copy />}
Copy code
</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"
>
{hasCopied ? (
<CheckIcon className="mr-2 h-4 w-4" />
) : (
<CopyIcon className="mr-2 h-4 w-4" />
)}
{hasCopied ? <Check /> : <Copy />}
Copy
</Button>
)}

View File

@@ -8,6 +8,10 @@ export interface DocsConfig {
export const docsConfig: DocsConfig = {
mainNav: [
{
title: "Home",
href: "/",
},
{
title: "Documentation",
href: "/docs",
@@ -28,10 +32,6 @@ export const docsConfig: DocsConfig = {
title: "Themes",
href: "/themes",
},
{
title: "Examples",
href: "/examples",
},
{
title: "Colors",
href: "/colors",
@@ -71,6 +71,11 @@ export const docsConfig: DocsConfig = {
href: "/docs/cli",
items: [],
},
{
title: "Next.js 15 + React 19",
href: "/docs/react-19",
items: [],
},
{
title: "Typography",
href: "/docs/components/typography",

View File

@@ -11,3 +11,8 @@ export const 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)
-o, --overwrite overwrite existing files. (default: false)
-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.
-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
<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"
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

@@ -4,6 +4,12 @@ description: Beautiful charts. Built using Recharts. Copy and paste into your ap
component: true
---
<Callout className="mb-6">
**Note:** If you are using charts with **React 19** or the **Next.js 15**, see the note [here](/docs/react-19#recharts).
</Callout>
<ComponentPreview
name="chart-bar-interactive"
className="-mt-4 [&_.preview]:p-0 [&_.preview]:border-t [&_.preview>div]:shadow-none [&_.preview>div]:border-none [&_.preview>div]:w-full [&_.preview]:lg:min-h-[404px]"
@@ -47,7 +53,7 @@ We do not wrap Recharts. This means you're not locked into an abstraction. When
<Callout className="mt-4">
**Note:** If you are trying to use charts with **React 19** or the **Next.js 15**, you will need the [recharts@alpha](https://github.com/recharts/recharts/releases/tag/v2.13.0-alpha.4) release currently.
**Note:** If you are using charts with **React 19** or the **Next.js 15**, see the note [here](/docs/react-19#recharts).
</Callout>

View File

@@ -136,3 +136,27 @@ export function CommandMenu() {
### Combobox
You can use the `<Command />` component as a combobox. See the [Combobox](/docs/components/combobox) page for more information.
## Changelog
### 2024-10-25 Classes for icons
Added `gap-2 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0` to the `<CommandItem />` to automatically style icon inside.
Add the following classes to the `cva` call in your `command.tsx` file.
```tsx showLineNumbers title="command.tsx"
const CommandItem = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Item>
>(({ className, ...props }, ref) => (
<CommandPrimitive.Item
ref={ref}
className={cn(
"... gap-2 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
className
)}
{...props}
/>
))
```

View File

@@ -120,3 +120,23 @@ const DropdownMenuItem = React.forwardRef<
/>
))
```
### 2024-10-25 Classes for `<DropdownMenuSubTrigger />`
Added `gap-2 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0` to the `<DropdownMenuSubTrigger />` to automatically style icon inside.
Add the following classes to the `cva` call in your `dropdown-menu.tsx` file.
```tsx title="dropdown-menu.tsx"
<DropdownMenuPrimitive.SubTrigger
ref={ref}
className={cn(
"flex cursor-default gap-2 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
inset && "pl-8",
className
)}
{...props}
>
{/* ... */}
</DropdownMenuPrimitive.SubTrigger>
```

View File

@@ -406,6 +406,40 @@ You can change the keyboard shortcut by updating the `SIDEBAR_KEYBOARD_SHORTCUT`
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
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
### 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.

View File

@@ -22,9 +22,11 @@ npm install next-themes
import * as React from "react"
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>
}
```

View File

@@ -5,7 +5,7 @@ description: Every component recreated in Figma. With customizable props, typogr
## 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

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:
```bash
npm install tailwindcss-animate class-variance-authority clsx tailwind-merge
```
### 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
npm install tailwindcss-animate class-variance-authority clsx tailwind-merge lucide-react
```
### 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": {
"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.
**If you use a different alias such as ~, you'll need to update import statements when adding components.**
### Configure tailwind.config.js
Here's what my `tailwind.config.js` file looks like:
```js title="tailwind.config.js"
const { fontFamily } = require("tailwindcss/defaultTheme")
/** @type {import('tailwindcss').Config} */
module.exports = {
darkMode: ["class"],
content: ["app/**/*.{ts,tsx}", "components/**/*.{ts,tsx}"],
theme: {
container: {
center: true,
padding: "2rem",
screens: {
"2xl": "1400px",
},
},
extend: {
colors: {
border: "hsl(var(--border))",
@@ -112,23 +87,6 @@ module.exports = {
md: `calc(var(--radius) - 2px)`,
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")],
@@ -148,67 +106,46 @@ Add the following to your styles/globals.css file. You can learn more about usin
:root {
--background: 0 0% 100%;
--foreground: 222.2 47.4% 11.2%;
--muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%;
--popover: 0 0% 100%;
--popover-foreground: 222.2 47.4% 11.2%;
--border: 214.3 31.8% 91.4%;
--input: 214.3 31.8% 91.4%;
--card: 0 0% 100%;
--card-foreground: 222.2 47.4% 11.2%;
--primary: 222.2 47.4% 11.2%;
--primary-foreground: 210 40% 98%;
--secondary: 210 40% 96.1%;
--secondary-foreground: 222.2 47.4% 11.2%;
--accent: 210 40% 96.1%;
--accent-foreground: 222.2 47.4% 11.2%;
--destructive: 0 100% 50%;
--destructive-foreground: 210 40% 98%;
--ring: 215 20.2% 65.1%;
--radius: 0.5rem;
}
.dark {
--background: 224 71% 4%;
--foreground: 213 31% 91%;
--muted: 223 47% 11%;
--muted-foreground: 215.4 16.3% 56.9%;
--accent: 216 34% 17%;
--accent-foreground: 210 40% 98%;
--popover: 224 71% 4%;
--popover-foreground: 215 20.2% 65.1%;
--border: 216 34% 17%;
--input: 216 34% 17%;
--card: 224 71% 4%;
--card-foreground: 213 31% 91%;
--primary: 210 40% 98%;
--primary-foreground: 222.2 47.4% 1.2%;
--secondary: 222.2 47.4% 11.2%;
--secondary-foreground: 210 40% 98%;
--destructive: 0 63% 31%;
--destructive-foreground: 210 40% 98%;
--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;
}
body {
@apply bg-background text-foreground;
font-feature-settings: "rlig" 1, "calt" 1;
@apply font-sans antialiased bg-background text-foreground;
}
}
```
### 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"
```ts title="lib/utils.ts" showLineNumbers
import { clsx, type ClassValue } from "clsx"
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
You can now start adding components to your project.

View File

@@ -3,6 +3,12 @@ title: Next.js
description: Install and configure Next.js.
---
<Callout>
**If you're using Next.js 15, see the [Next.js 15 + React 19](/docs/react-19) guide.**
</Callout>
<Steps>
### Create project

View File

@@ -23,6 +23,29 @@ npm install -D tailwindcss postcss autoprefixer
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
The current version of Vite splits TypeScript configuration into three files, two of which need to be edited.

View File

@@ -0,0 +1,168 @@
---
title: Next.js 15 + React 19
description: Using shadcn/ui with Next.js 15 and React 19.
---
<Callout>
**The following guide applies to any framework that supports React 19**. I
titled this page "Next.js 15 + React 19" to help people upgrading to Next.js
15 find it. We are working with package maintainers to help upgrade to React
19.
</Callout>
## TL;DR
If you're using `npm`, you can install shadcn/ui dependencies with a flag. The `shadcn` CLI will prompt you to select a flag when you run it. No flags required for pnpm, bun, or yarn.
See [Upgrade Status](#upgrade-status) for the status of React 19 support for each package.
## What's happening?
React 19 is now [rc](https://www.npmjs.com/package/react?activeTab=versions) and is [tested and supported in the latest Next.js 15 release](https://nextjs.org/blog/next-15#react-19).
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/
"peerDependencies": {
- "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"
},
```
<Callout>
You can check if a package lists React 19 as a peer dependency by running
`npm info <package> peerDependencies`.
</Callout>
In the meantime, if you are installing a package that **does not** list React 19 as a peer dependency, you will see an error message like this:
```bash
npm error code ERESOLVE
npm error ERESOLVE unable to resolve dependency tree
npm error
npm error While resolving: my-app@0.1.0
npm error Found: react@19.0.0-rc-69d4b800-20241021
npm error node_modules/react
npm error react@"19.0.0-rc-69d4b800-20241021" from the root project
```
<Callout>
**Note:** This is npm only. PNPM and Bun will only show a silent warning.
</Callout>
## How to fix this
### Solution 1: `--force` or `--legacy-peer-deps`
You can force install a package with the `--force` or the `--legacy-peer-deps` flag.
```bash
npm i <package> --force
npm i <package> --legacy-peer-deps
```
This will install the package and ignore the peer dependency warnings.
<Accordion type="multiple">
<AccordionItem value="flags">
<AccordionTrigger className="font-medium">
What do the `--force` and `--legacy-peer-deps` flag do?
</AccordionTrigger>
<AccordionContent className="[&_ul]:mt-0">
- `--force`: Ignores and overrides any dependency conflicts, forcing the
installation of packages.
- `--legacy-peer-deps`: Skips strict peer dependency checks, allowing
installation of packages with unmet peer dependencies to avoid errors.
</AccordionContent>
</AccordionItem>
</Accordion>
### Solution 2: Use React 18
You can downgrade `react` and `react-dom` to version 18, which is compatible with the package you are installing and upgrade when the dependency is updated.
```bash
npm i react@18 react-dom@18
```
Whichever solution you choose, make sure you test your app thoroughly to ensure
there are no regressions.
## Using shadcn/ui on Next.js 15
### Using pnpm, bun, or yarn
Follow the instructions in the [installation guide](/docs/installation/next) to install shadcn/ui. No flags are needed.
### Using npm
When you run `npx shadcn@latest init -d`, you will be prompted to select an option to resolve the peer dependency issues.
```bash
It looks like you are using React 19.
Some packages may fail to install due to peer dependency issues (see https://ui.shadcn.com/react-19).
? How would you like to proceed? - Use arrow-keys. Return to submit.
Use --force
Use --legacy-peer-deps
```
You can then run the command with the flag you choose.
## Adding components
The process for adding components is the same as above. Select a flag to resolve the peer dependency issues.
**Remember to always test your app after installing new dependencies.**
## Upgrade Status
To make it easy for you track the progress of the upgrade, I've created a table below with React 19 support status for the shadcn/ui dependencies.
- ✅ - Works with React 19 using npm, pnpm, and bun.
- 🚧 - Works with React 19 using pnpm and bun. Requires flag for npm. PR is in progress.
| Package | Status | Note |
| ---------------------------------------------------------------------------------- | ------ | ----------------------------------------------------------- |
| [radix-ui](https://www.npmjs.com/package/@radix-ui/react-icons) | ✅ | |
| [lucide-react](https://www.npmjs.com/package/lucide-react) | ✅ | |
| [class-variance-authority](https://www.npmjs.com/package/class-variance-authority) | ✅ | Does not list React 19 as a peer dependency. |
| [tailwindcss-animate](https://www.npmjs.com/package/tailwindcss-animate) | ✅ | Does not list React 19 as a peer dependency. |
| [embla-carousel-react](https://www.npmjs.com/package/embla-carousel-react) | ✅ | |
| [recharts](https://www.npmjs.com/package/recharts) | ✅ | See note [below](#recharts) |
| [react-hook-form](https://www.npmjs.com/package/react-hook-form) | ✅ | |
| [react-resizable-panels](https://www.npmjs.com/package/react-resizable-panels) | ✅ | |
| [sonner](https://www.npmjs.com/package/sonner) | ✅ | |
| [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) | ✅ | |
| [vaul](https://www.npmjs.com/package/vaul) | ✅ | |
| [@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) | ✅ | |
If you have any questions, please [open an issue](https://github.com/shadcn/ui/issues) on GitHub.
## Recharts
To use recharts with React 19, you will need to override the `react-is` dependency.
<Steps>
<Step>Add the following to your `package.json`</Step>
```json title="package.json"
"overrides": {
"react-is": "^19.0.0-rc-69d4b800-20241021"
}
```
Note: the `react-is` version needs to match the version of React 19 you are using. The above is an example.
<Step>Run `npm install --legacy-peer-deps`</Step>
</Steps>

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"
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 { highlightCode } from "@/lib/highlight-code"
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 project = new Project({
compilerOptions: {},
})
const BLOCKS_WHITELIST_PREFIXES = ["sidebar", "login"]
const REGISTRY_BLOCK_TYPES = ["registry:block"]
export async function getAllBlockIds(
style: Style["name"] = DEFAULT_BLOCKS_STYLE
@@ -24,136 +17,14 @@ export async function getAllBlockIds(
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) {
const index = z.record(registryEntrySchema).parse(Index[style])
return Object.values(index).filter((block) => block.type === "registry:block")
}
async function _getBlockCode(
name: string,
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
return Object.values(index).filter((block) =>
BLOCKS_WHITELIST_PREFIXES.some(
(prefix) =>
block.name.startsWith(prefix) &&
REGISTRY_BLOCK_TYPES.includes(block.type)
)
)
}

View File

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

View File

@@ -1,10 +1,8 @@
"use server"
import { codeToHtml } from "shiki"
export async function highlightCode(code: string) {
const html = codeToHtml(code, {
lang: "typescript",
const html = await codeToHtml(code, {
lang: "jsx",
theme: "github-dark-default",
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}.ts`)
)
}) || component.files[0]
: component.files[0]
}) || component.files[0]?.path
: component.files[0]?.path
}
// Read the source file.
const filePath = path.join(process.cwd(), src)
const filePath = src
let source = fs.readFileSync(filePath, "utf8")
// Replace imports.
@@ -106,10 +106,10 @@ export function rehypeComponent() {
try {
for (const style of styles) {
const component = Index[style.name][name]
const src = component.files[0]
const src = component.files[0]?.path
// Read the source file.
const filePath = path.join(process.cwd(), src)
const filePath = src
let source = fs.readFileSync(filePath, "utf8")
// Replace imports.

View File

@@ -2,6 +2,11 @@ import { createContentlayerPlugin } from "next-contentlayer2"
/** @type {import('next').NextConfig} */
const nextConfig = {
experimental: {
outputFileTracingIncludes: {
"/blocks/*": ["./registry/**/*"],
},
},
reactStrictMode: true,
swcMinify: true,
images: {
@@ -58,6 +63,11 @@ const nextConfig = {
destination: "/docs/components/sidebar",
permanent: true,
},
{
source: "/react-19",
destination: "/docs/react-19",
permanent: true,
},
]
},
}

View File

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

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