mirror of
https://github.com/shadcn-ui/ui.git
synced 2026-06-16 20:31:33 +00:00
Compare commits
47 Commits
shadcn@2.1
...
shadcn@2.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
729b9ec8ca | ||
|
|
a1bed464f3 | ||
|
|
805ed4120a | ||
|
|
600a593c87 | ||
|
|
500dbe2664 | ||
|
|
c577ee0666 | ||
|
|
d5bf0018fd | ||
|
|
fb36ca4159 | ||
|
|
824a35ada1 | ||
|
|
8d520c8d49 | ||
|
|
0873835339 | ||
|
|
4a0d4cfdb9 | ||
|
|
c4c5d8d419 | ||
|
|
9253682b87 | ||
|
|
c7cd16a637 | ||
|
|
eff1918d41 | ||
|
|
366d6b656b | ||
|
|
e489c5e08e | ||
|
|
d87003e0a4 | ||
|
|
a8633075f7 | ||
|
|
432d5e6e28 | ||
|
|
8f0c26f22a | ||
|
|
149b321c1b | ||
|
|
c1ae5a57cc | ||
|
|
b8ed303d8c | ||
|
|
13c97acf9f | ||
|
|
bed277c54d | ||
|
|
f7c42169a6 | ||
|
|
2c2fe97eb9 | ||
|
|
d64374d009 | ||
|
|
e24e51a2fa | ||
|
|
cdfecd1d97 | ||
|
|
2c043e709f | ||
|
|
aed19aa911 | ||
|
|
9e35d229ae | ||
|
|
db1975ef4d | ||
|
|
961e0b62d7 | ||
|
|
70c684c224 | ||
|
|
4ff64ba818 | ||
|
|
500a353816 | ||
|
|
c830780d62 | ||
|
|
debd51a854 | ||
|
|
78426dd862 | ||
|
|
6e47a94a8f | ||
|
|
ab6a856930 | ||
|
|
b33d3868e9 | ||
|
|
9e0a86122a |
@@ -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:
|
||||
|
||||
273
apps/www/__registry__/default/block/_sink/page.tsx
Normal file
273
apps/www/__registry__/default/block/_sink/page.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
451
apps/www/__registry__/icons.tsx
Normal file
451
apps/www/__registry__/icons.tsx
Normal 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
273
apps/www/__registry__/new-york/block/_sink/page.tsx
Normal file
273
apps/www/__registry__/new-york/block/_sink/page.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
@@ -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 (
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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 />
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
]
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
52
apps/www/app/(app)/themes/layout.tsx
Normal file
52
apps/www/app/(app)/themes/layout.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
@@ -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 />
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
</>
|
||||
)
|
||||
|
||||
34
apps/www/app/(internal)/icons/layout.tsx
Normal file
34
apps/www/app/(internal)/icons/layout.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
52
apps/www/app/(internal)/icons/page.tsx
Normal file
52
apps/www/app/(internal)/icons/page.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
3
apps/www/app/(internal)/sink/page.tsx
Normal file
3
apps/www/app/(internal)/sink/page.tsx
Normal file
@@ -0,0 +1,3 @@
|
||||
import SinkPage from "@/registry/new-york/block/_sink/page"
|
||||
|
||||
export default SinkPage
|
||||
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -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 ?? ""),
|
||||
}))
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
452
apps/www/components/block-viewer.tsx
Normal file
452
apps/www/components/block-viewer.tsx
Normal 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 }
|
||||
@@ -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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -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}
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
TooltipTrigger,
|
||||
} from "@/registry/new-york/ui/tooltip"
|
||||
|
||||
export function BlockCopyButton({
|
||||
export function ChartCopyButton({
|
||||
event,
|
||||
name,
|
||||
code,
|
||||
@@ -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)
|
||||
})
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}
|
||||
|
||||
35
apps/www/components/mode-switcher.tsx
Normal file
35
apps/www/components/mode-switcher.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
|
||||
@@ -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}>
|
||||
|
||||
@@ -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" : ""}
|
||||
|
||||
@@ -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{" "}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
)}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -11,3 +11,8 @@ export const siteConfig = {
|
||||
}
|
||||
|
||||
export type SiteConfig = typeof siteConfig
|
||||
|
||||
export const META_THEME_COLORS = {
|
||||
light: "#ffffff",
|
||||
dark: "#09090b",
|
||||
}
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
@@ -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",
|
||||
```
|
||||
|
||||
@@ -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"
|
||||
```
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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>
|
||||
}
|
||||
```
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -5,7 +5,7 @@ description: Install and configure Next.js.
|
||||
|
||||
<Callout>
|
||||
|
||||
**If you're using Next.js 15, see the [Next.js 15 + React 19](/docs/installation/react-19) guide.**
|
||||
**If you're using Next.js 15, see the [Next.js 15 + React 19](/docs/react-19) guide.**
|
||||
|
||||
</Callout>
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -22,9 +22,11 @@ React 19 is now [rc](https://www.npmjs.com/package/react?activeTab=versions) and
|
||||
|
||||
To support React 19, package maintainers will need to test and update their packages to include React 19 as a peer dependency. This is [already](https://github.com/radix-ui/primitives/pull/2952) [in](https://github.com/pacocoursey/cmdk/pull/318) [progress](https://github.com/emilkowalski/vaul/pull/498).
|
||||
|
||||
```diff /^19.0.0/
|
||||
```diff /^19.0/
|
||||
"peerDependencies": {
|
||||
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0",
|
||||
- "react": "^16.8 || ^17.0 || ^18.0",
|
||||
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0",
|
||||
- "react-dom": "^16.8 || ^17.0 || ^18.0"
|
||||
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0"
|
||||
},
|
||||
```
|
||||
@@ -140,8 +142,8 @@ To make it easy for you track the progress of the upgrade, I've created a table
|
||||
| [react-day-picker](https://www.npmjs.com/package/react-day-picker) | ✅ | Works with flag for npm. Work to upgrade to v9 in progress. |
|
||||
| [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 #184](https://github.com/radix-ui/icons/pull/184) |
|
||||
| [cmdk](https://www.npmjs.com/package/cmdk) | 🚧 | See [PR #318](https://github.com/pacocoursey/cmdk/pull/318) |
|
||||
| [@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.
|
||||
|
||||
|
||||
25
apps/www/hooks/use-meta-color.ts
Normal file
25
apps/www/hooks/use-meta-color.ts
Normal 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,
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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: [
|
||||
{
|
||||
|
||||
37
apps/www/lib/registry.test.ts
Normal file
37
apps/www/lib/registry.test.ts
Normal 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
270
apps/www/lib/registry.ts
Normal 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
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
@@ -2,6 +2,11 @@ import { createContentlayerPlugin } from "next-contentlayer2"
|
||||
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
experimental: {
|
||||
outputFileTracingIncludes: {
|
||||
"/blocks/*": ["./registry/**/*"],
|
||||
},
|
||||
},
|
||||
reactStrictMode: true,
|
||||
swcMinify: true,
|
||||
images: {
|
||||
|
||||
@@ -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",
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 246 B After Width: | Height: | Size: 191 B |
Binary file not shown.
|
Before Width: | Height: | Size: 324 B After Width: | Height: | Size: 241 B |
Binary file not shown.
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 379 B |
150
apps/www/public/r/icons/index.json
Normal file
150
apps/www/public/r/icons/index.json
Normal file
@@ -0,0 +1,150 @@
|
||||
{
|
||||
"AlertCircle": {
|
||||
"lucide": "AlertCircle",
|
||||
"radix": "ExclamationTriangleIcon"
|
||||
},
|
||||
"ArrowLeft": {
|
||||
"lucide": "ArrowLeft",
|
||||
"radix": "ArrowLeftIcon"
|
||||
},
|
||||
"ArrowRight": {
|
||||
"lucide": "ArrowRight",
|
||||
"radix": "ArrowRightIcon"
|
||||
},
|
||||
"ArrowUpDown": {
|
||||
"lucide": "ArrowUpDown",
|
||||
"radix": "CaretSortIcon"
|
||||
},
|
||||
"BellRing": {
|
||||
"lucide": "BellRing",
|
||||
"radix": "BellIcon"
|
||||
},
|
||||
"Bold": {
|
||||
"lucide": "Bold",
|
||||
"radix": "FontBoldIcon"
|
||||
},
|
||||
"Calculator": {
|
||||
"lucide": "Calculator",
|
||||
"radix": "ComponentPlaceholderIcon"
|
||||
},
|
||||
"Calendar": {
|
||||
"lucide": "Calendar",
|
||||
"radix": "CalendarIcon"
|
||||
},
|
||||
"Check": {
|
||||
"lucide": "Check",
|
||||
"radix": "CheckIcon"
|
||||
},
|
||||
"ChevronDown": {
|
||||
"lucide": "ChevronDown",
|
||||
"radix": "ChevronDownIcon"
|
||||
},
|
||||
"ChevronLeft": {
|
||||
"lucide": "ChevronLeft",
|
||||
"radix": "ChevronLeftIcon"
|
||||
},
|
||||
"ChevronRight": {
|
||||
"lucide": "ChevronRight",
|
||||
"radix": "ChevronRightIcon"
|
||||
},
|
||||
"ChevronUp": {
|
||||
"lucide": "ChevronUp",
|
||||
"radix": "ChevronUpIcon"
|
||||
},
|
||||
"ChevronsUpDown": {
|
||||
"lucide": "ChevronsUpDown",
|
||||
"radix": "CaretSortIcon"
|
||||
},
|
||||
"Circle": {
|
||||
"lucide": "Circle",
|
||||
"radix": "DotFilledIcon"
|
||||
},
|
||||
"Copy": {
|
||||
"lucide": "Copy",
|
||||
"radix": "CopyIcon"
|
||||
},
|
||||
"CreditCard": {
|
||||
"lucide": "CreditCard",
|
||||
"radix": "ComponentPlaceholderIcon"
|
||||
},
|
||||
"GripVertical": {
|
||||
"lucide": "GripVertical",
|
||||
"radix": "DragHandleDots2Icon"
|
||||
},
|
||||
"Italic": {
|
||||
"lucide": "Italic",
|
||||
"radix": "FontItalicIcon"
|
||||
},
|
||||
"Loader2": {
|
||||
"lucide": "Loader2",
|
||||
"radix": "ReloadIcon"
|
||||
},
|
||||
"Mail": {
|
||||
"lucide": "Mail",
|
||||
"radix": "EnvelopeClosedIcon"
|
||||
},
|
||||
"MailOpen": {
|
||||
"lucide": "MailOpen",
|
||||
"radix": "EnvelopeOpenIcon"
|
||||
},
|
||||
"Minus": {
|
||||
"lucide": "Minus",
|
||||
"radix": "MinusIcon"
|
||||
},
|
||||
"Moon": {
|
||||
"lucide": "Moon",
|
||||
"radix": "MoonIcon"
|
||||
},
|
||||
"MoreHorizontal": {
|
||||
"lucide": "MoreHorizontal",
|
||||
"radix": "DotsHorizontalIcon"
|
||||
},
|
||||
"PanelLeft": {
|
||||
"lucide": "PanelLeft",
|
||||
"radix": "ViewVerticalIcon"
|
||||
},
|
||||
"Plus": {
|
||||
"lucide": "Plus",
|
||||
"radix": "PlusIcon"
|
||||
},
|
||||
"Search": {
|
||||
"lucide": "Search",
|
||||
"radix": "MagnifyingGlassIcon"
|
||||
},
|
||||
"Send": {
|
||||
"lucide": "Send",
|
||||
"radix": "PaperPlaneIcon"
|
||||
},
|
||||
"Settings": {
|
||||
"lucide": "Settings",
|
||||
"radix": "GearIcon"
|
||||
},
|
||||
"Slash": {
|
||||
"lucide": "Slash",
|
||||
"radix": "SlashIcon"
|
||||
},
|
||||
"Smile": {
|
||||
"lucide": "Smile",
|
||||
"radix": "FaceIcon"
|
||||
},
|
||||
"Sun": {
|
||||
"lucide": "Sun",
|
||||
"radix": "SunIcon"
|
||||
},
|
||||
"Terminal": {
|
||||
"lucide": "Terminal",
|
||||
"radix": "RocketIcon"
|
||||
},
|
||||
"Underline": {
|
||||
"lucide": "Underline",
|
||||
"radix": "UnderlineIcon"
|
||||
},
|
||||
"User": {
|
||||
"lucide": "User",
|
||||
"radix": "PersonIcon"
|
||||
},
|
||||
"X": {
|
||||
"lucide": "X",
|
||||
"radix": "Cross2Icon"
|
||||
}
|
||||
}
|
||||
451
apps/www/public/r/styles/default/_sink.json
Normal file
451
apps/www/public/r/styles/default/_sink.json
Normal file
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user