Skip to content

Commit

Permalink
feat: themes (shadcn-ui#1135)
Browse files Browse the repository at this point in the history
  • Loading branch information
shadcn authored Aug 7, 2023
1 parent c598f19 commit 3c9f7ca
Show file tree
Hide file tree
Showing 76 changed files with 6,194 additions and 505 deletions.
14 changes: 14 additions & 0 deletions apps/www/__registry__/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1006,6 +1006,13 @@ export const Index: Record<string, any> = {
component: React.lazy(() => import("@/registry/default/example/mode-toggle")),
files: ["registry/default/example/mode-toggle.tsx"],
},
"cards": {
name: "cards",
type: "components:example",
registryDependencies: undefined,
component: React.lazy(() => import("@/registry/default/example/cards")),
files: ["registry/default/example/cards/cards.tsx"],
},
}, "new-york": {
"accordion": {
name: "accordion",
Expand Down Expand Up @@ -2008,5 +2015,12 @@ export const Index: Record<string, any> = {
component: React.lazy(() => import("@/registry/new-york/example/mode-toggle")),
files: ["registry/new-york/example/mode-toggle.tsx"],
},
"cards": {
name: "cards",
type: "components:example",
registryDependencies: undefined,
component: React.lazy(() => import("@/registry/new-york/example/cards")),
files: ["registry/new-york/example/cards/cards.tsx"],
},
},
}
2 changes: 1 addition & 1 deletion apps/www/app/examples/dashboard/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import { UserNav } from "@/app/examples/dashboard/components/user-nav"

export const metadata: Metadata = {
title: "Dashboard",
description: "Example dashboard app using the components.",
description: "Example dashboard app built using the components.",
}

export default function DashboardPage() {
Expand Down
9 changes: 8 additions & 1 deletion apps/www/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { ThemeProvider } from "@/components/providers"
import { SiteFooter } from "@/components/site-footer"
import { SiteHeader } from "@/components/site-header"
import { TailwindIndicator } from "@/components/tailwind-indicator"
import { ThemeSwitcher } from "@/components/theme-switcher"
import { Toaster as DefaultToaster } from "@/registry/default/ui/toaster"
import { Toaster as NewYorkToaster } from "@/registry/new-york/ui/toaster"

Expand Down Expand Up @@ -82,14 +83,20 @@ export default function RootLayout({ children }: RootLayoutProps) {
fontSans.variable
)}
>
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
<ThemeProvider
attribute="class"
defaultTheme="system"
enableSystem
disableTransitionOnChange
>
<div className="relative flex min-h-screen flex-col">
<SiteHeader />
<div className="flex-1">{children}</div>
<SiteFooter />
</div>
<TailwindIndicator />
</ThemeProvider>
<ThemeSwitcher />
<Analytics />
<NewYorkToaster />
<DefaultToaster />
Expand Down
38 changes: 38 additions & 0 deletions apps/www/app/themes/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { Metadata } from "next"

import "public/registry/themes.css"
import {
PageHeader,
PageHeaderDescription,
PageHeaderHeading,
} from "@/components/page-header"
import { ThemeCustomizer } from "@/components/theme-customizer"
import { ThemeWrapper } from "@/components/theme-wrapper"
import { ThemesTabs } from "@/app/themes/tabs"

export const metadata: Metadata = {
title: "Themes",
description: "Hand-picked themes that you can copy and paste into your apps.",
}

export default function ThemesPage() {
return (
<div className="container">
<ThemeWrapper
defaultTheme="zinc"
className="relative flex flex-col items-start md:flex-row md:items-center"
>
<PageHeader className="relative pb-4 md:pb-8 lg:pb-12">
<PageHeaderHeading>Make it yours.</PageHeaderHeading>
<PageHeaderDescription>
Hand-picked themes that you can copy and paste into your apps.
</PageHeaderDescription>
</PageHeader>
<div className="px-4 pb-8 md:ml-auto md:pb-0">
<ThemeCustomizer />
</div>
</ThemeWrapper>
<ThemesTabs />
</div>
)
}
73 changes: 73 additions & 0 deletions apps/www/app/themes/tabs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
"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>
)}
</div>
)
}
2 changes: 1 addition & 1 deletion apps/www/components/copy-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ interface CopyButtonProps extends React.HTMLAttributes<HTMLButtonElement> {
event?: Event["name"]
}

async function copyToClipboardWithMeta(value: string, event?: Event) {
export async function copyToClipboardWithMeta(value: string, event?: Event) {
navigator.clipboard.writeText(value)
if (event) {
trackEvent(event)
Expand Down
31 changes: 31 additions & 0 deletions apps/www/components/drawer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
"use client"

import { forwardRef } from "react"
import { Drawer as DrawerPrimitive } from "vaul"

import { cn } from "@/lib/utils"

const DrawerTrigger = DrawerPrimitive.Trigger

const DrawerContent = forwardRef<
React.ElementRef<typeof DrawerPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Content>
>(({ className, children, ...props }, ref) => (
<DrawerPrimitive.Portal>
<DrawerPrimitive.Overlay className="fixed inset-0 z-50 bg-zinc-950/60" />
<DrawerPrimitive.Content
ref={ref}
className={cn(
"fixed inset-x-0 bottom-0 z-50 mt-24 h-[96%] rounded-t-[10px] bg-background",
className
)}
{...props}
>
<div className="absolute left-1/2 top-3 h-2 w-[100px] translate-x-[-50%] rounded-full bg-muted" />
{children}
</DrawerPrimitive.Content>
</DrawerPrimitive.Portal>
))
DrawerContent.displayName = "DrawerContent"

export { DrawerTrigger, DrawerContent }
12 changes: 12 additions & 0 deletions apps/www/components/main-nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { usePathname } from "next/navigation"
import { siteConfig } from "@/config/site"
import { cn } from "@/lib/utils"
import { Icons } from "@/components/icons"
import { Badge } from "@/registry/new-york/ui/badge"

export function MainNav() {
const pathname = usePathname()
Expand Down Expand Up @@ -40,6 +41,17 @@ export function MainNav() {
>
Components
</Link>
<Link
href="/themes"
className={cn(
"transition-colors hover:text-foreground/80",
pathname?.startsWith("/themes")
? "text-foreground"
: "text-foreground/60"
)}
>
Themes
</Link>
<Link
href="/examples"
className={cn(
Expand Down
2 changes: 1 addition & 1 deletion apps/www/components/site-header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { buttonVariants } from "@/registry/new-york/ui/button"

export function SiteHeader() {
return (
<header className="supports-backdrop-blur:bg-background/60 sticky top-0 z-40 w-full border-b bg-background/95 backdrop-blur">
<header className="supports-backdrop-blur:bg-background/60 sticky top-0 z-50 w-full border-b bg-background/95 backdrop-blur">
<div className="container flex h-14 items-center">
<MainNav />
<MobileNav />
Expand Down
52 changes: 52 additions & 0 deletions apps/www/components/theme-component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
"use client"

import * as React from "react"
import { Index } from "@/__registry__"

import { cn } from "@/lib/utils"
import { useConfig } from "@/hooks/use-config"
import { Icons } from "@/components/icons"

interface ThemeComponentProps extends React.HTMLAttributes<HTMLDivElement> {
name: string
extractClassname?: boolean
extractedClassNames?: string
align?: "center" | "start" | "end"
}

export function ThemeComponent({ name, ...props }: ThemeComponentProps) {
const [config] = useConfig()

const Preview = React.useMemo(() => {
const Component = Index[config.style][name]?.component

if (!Component) {
return (
<p className="text-sm text-muted-foreground">
Component{" "}
<code className="relative rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-sm">
{name}
</code>{" "}
not found in registry.
</p>
)
}

return <Component />
}, [name, config.style])

return (
<div className={cn("relative")} {...props}>
<React.Suspense
fallback={
<div className="flex items-center text-sm text-muted-foreground">
<Icons.spinner className="mr-2 h-4 w-4 animate-spin" />
Loading...
</div>
}
>
{Preview}
</React.Suspense>
</div>
)
}
Loading

0 comments on commit 3c9f7ca

Please sign in to comment.