mirror of
https://github.com/shadcn-ui/ui.git
synced 2026-06-17 04:41:32 +00:00
Compare commits
17 Commits
@shadcn/ui
...
shadcn-ui@
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d3cc7a3a6f | ||
|
|
96f300ea74 | ||
|
|
a243262748 | ||
|
|
b5d43688b9 | ||
|
|
3819f07d9d | ||
|
|
81ee0de71c | ||
|
|
4444c28b9f | ||
|
|
3899b1cc10 | ||
|
|
49dbba624f | ||
|
|
309267b73c | ||
|
|
d6933321dc | ||
|
|
272c7c0980 | ||
|
|
29a333721a | ||
|
|
a86af454cc | ||
|
|
33e72a6796 | ||
|
|
3961f15dc6 | ||
|
|
faf05aa086 |
4
.github/workflows/prerelease-comment.yml
vendored
4
.github/workflows/prerelease-comment.yml
vendored
@@ -28,7 +28,7 @@ jobs:
|
||||
|
||||
for (const artifact of allArtifacts.data.artifacts) {
|
||||
// Extract the PR number and package version from the artifact name
|
||||
const match = /^npm-package-@shadcn-ui@(.*?)-pr-(\d+)/.exec(artifact.name);
|
||||
const match = /^npm-package-shadcn-ui@(.*?)-pr-(\d+)/.exec(artifact.name);
|
||||
|
||||
if (match) {
|
||||
require("fs").appendFileSync(
|
||||
@@ -49,7 +49,7 @@ jobs:
|
||||
A new prerelease is available for testing:
|
||||
|
||||
```sh
|
||||
pnpm @shadcn/ui@${{ env.BETA_PACKAGE_VERSION }}
|
||||
npx shadcn-ui@${{ env.BETA_PACKAGE_VERSION }}
|
||||
```
|
||||
|
||||
- name: "Remove the autorelease label once published"
|
||||
|
||||
2
.github/workflows/prerelease.yml
vendored
2
.github/workflows/prerelease.yml
vendored
@@ -54,5 +54,5 @@ jobs:
|
||||
- name: Upload packaged artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: npm-package-@shadcn-ui@${{ steps.package-version.outputs.current-version }}-pr-${{ github.event.number }} # encode the PR number into the artifact name
|
||||
name: npm-package-shadcn-ui@${{ steps.package-version.outputs.current-version }}-pr-${{ github.event.number }} # encode the PR number into the artifact name
|
||||
path: packages/cli/dist/index.js
|
||||
|
||||
4
.github/workflows/release.yml
vendored
4
.github/workflows/release.yml
vendored
@@ -30,8 +30,8 @@ jobs:
|
||||
- name: Install NPM Dependencies
|
||||
run: pnpm install
|
||||
|
||||
- name: Check for errors
|
||||
run: pnpm check
|
||||
# - name: Check for errors
|
||||
# run: pnpm check
|
||||
|
||||
- name: Build the package
|
||||
run: pnpm build:cli
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Inter as FontSans } from "@next/font/google"
|
||||
import { Inter as FontSans } from "next/font/google"
|
||||
|
||||
import "@/styles/globals.css"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
@@ -4,6 +4,7 @@ import { siteConfig } from "@/config/site"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { AppleMusicDemo } from "@/components/apple-music-demo"
|
||||
import { CopyButton } from "@/components/copy-button"
|
||||
import { Icons } from "@/components/icons"
|
||||
import { PromoVideo } from "@/components/promo-video"
|
||||
import { buttonVariants } from "@/components/ui/button"
|
||||
|
||||
@@ -30,7 +31,7 @@ export default function IndexPage() {
|
||||
</div>
|
||||
<div className="flex flex-col space-y-4 sm:flex-row sm:space-y-0 sm:space-x-4 md:flex-row">
|
||||
<Link href="/docs" className={buttonVariants({ size: "lg" })}>
|
||||
Documentation
|
||||
Get Started
|
||||
</Link>
|
||||
<Link
|
||||
target="_blank"
|
||||
@@ -38,20 +39,12 @@ export default function IndexPage() {
|
||||
href={siteConfig.links.github}
|
||||
className={cn(
|
||||
buttonVariants({ variant: "outline", size: "lg" }),
|
||||
"md:hidden"
|
||||
"pl-6"
|
||||
)}
|
||||
>
|
||||
<Icons.gitHub className="mr-2 h-4 w-4" />
|
||||
GitHub
|
||||
</Link>
|
||||
<pre className="hidden h-11 items-center justify-between space-x-2 overflow-x-auto rounded-lg border border-slate-100 bg-slate-100 pr-2 pl-6 dark:border-slate-700 dark:bg-black md:flex">
|
||||
<code className="font-mono text-sm font-semibold text-slate-900 dark:text-slate-50">
|
||||
npx create-next-app -e https://github.com/shadcn/next-template
|
||||
</code>
|
||||
<CopyButton
|
||||
value="npx create-next-app -e https://github.com/shadcn/next-template"
|
||||
className="border-none text-slate-900 hover:bg-transparent dark:text-slate-50"
|
||||
/>
|
||||
</pre>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-slate-500 dark:text-slate-400">
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import {
|
||||
JetBrains_Mono as FontMono,
|
||||
Inter as FontSans,
|
||||
} from "@next/font/google"
|
||||
} from "next/font/google"
|
||||
|
||||
const fontSans = FontSans({
|
||||
subsets: ["latin"],
|
||||
|
||||
@@ -1,23 +1,8 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import Link from "next/link"
|
||||
import { allDocs } from "contentlayer/generated"
|
||||
|
||||
import { siteConfig } from "@/config/site"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { Icons } from "@/components/icons"
|
||||
import { buttonVariants } from "@/components/ui/button"
|
||||
import {
|
||||
NavigationMenu,
|
||||
NavigationMenuContent,
|
||||
NavigationMenuItem,
|
||||
NavigationMenuLink,
|
||||
NavigationMenuList,
|
||||
NavigationMenuTrigger,
|
||||
navigationMenuTriggerStyle,
|
||||
} from "@/components/ui/navigation-menu"
|
||||
import { Separator } from "@/components/ui/separator"
|
||||
|
||||
export function MainNav() {
|
||||
return (
|
||||
@@ -28,114 +13,12 @@ export function MainNav() {
|
||||
{siteConfig.name}
|
||||
</span>
|
||||
</Link>
|
||||
<NavigationMenu>
|
||||
<NavigationMenuList>
|
||||
<NavigationMenuItem>
|
||||
<NavigationMenuTrigger className="h-9">
|
||||
Getting started
|
||||
</NavigationMenuTrigger>
|
||||
<NavigationMenuContent>
|
||||
<ul className="grid gap-3 p-6 md:w-[400px] lg:w-[500px] lg:grid-cols-[.75fr_1fr]">
|
||||
<li className="row-span-3">
|
||||
<Link href="/" passHref legacyBehavior>
|
||||
<NavigationMenuLink
|
||||
className="flex h-full w-full select-none
|
||||
flex-col justify-end space-y-2 rounded-md bg-gradient-to-b from-rose-500 to-indigo-700 p-6 no-underline outline-none focus:shadow-md"
|
||||
>
|
||||
<div className="text-lg font-medium text-white">
|
||||
{siteConfig.name}
|
||||
</div>
|
||||
<p className="text-sm leading-snug text-white/90">
|
||||
{siteConfig.description}
|
||||
</p>
|
||||
</NavigationMenuLink>
|
||||
</Link>
|
||||
</li>
|
||||
<ListItem href="/docs" title="Introduction">
|
||||
Re-usable components built using Radix UI and Tailwind CSS.
|
||||
</ListItem>
|
||||
<ListItem href="/docs/installation" title="Installation">
|
||||
How to install dependencies and structure your app.
|
||||
</ListItem>
|
||||
<ListItem href="/docs/primitives/typography" title="Typography">
|
||||
Styles for headings, paragraphs, lists...etc
|
||||
</ListItem>
|
||||
</ul>
|
||||
</NavigationMenuContent>
|
||||
</NavigationMenuItem>
|
||||
<NavigationMenuItem>
|
||||
<NavigationMenuTrigger className="h-9">
|
||||
Components
|
||||
</NavigationMenuTrigger>
|
||||
<NavigationMenuContent>
|
||||
<ul className="grid w-[600px] grid-cols-2 gap-3 p-4">
|
||||
{allDocs
|
||||
.filter((doc) => doc.featured)
|
||||
.map((doc) => (
|
||||
<ListItem key={doc._id} title={doc.title} href={doc.slug}>
|
||||
{doc.description}
|
||||
</ListItem>
|
||||
))}
|
||||
</ul>
|
||||
<div className="p-4 pt-0">
|
||||
<Separator className="mb-4" />
|
||||
<Link href="/docs/primitives/accordion" passHref legacyBehavior>
|
||||
<NavigationMenuLink
|
||||
className={cn(
|
||||
buttonVariants({ variant: "outline" }),
|
||||
"w-full dark:hover:bg-slate-700"
|
||||
)}
|
||||
>
|
||||
Browse components
|
||||
</NavigationMenuLink>
|
||||
</Link>
|
||||
</div>
|
||||
</NavigationMenuContent>
|
||||
</NavigationMenuItem>
|
||||
<NavigationMenuItem className="hidden lg:flex">
|
||||
<Link href="/figma" legacyBehavior passHref>
|
||||
<NavigationMenuLink
|
||||
className={cn(navigationMenuTriggerStyle(), "h-9")}
|
||||
>
|
||||
Figma
|
||||
</NavigationMenuLink>
|
||||
</Link>
|
||||
</NavigationMenuItem>
|
||||
<NavigationMenuItem className="hidden lg:flex">
|
||||
<Link href={siteConfig.links.github} legacyBehavior passHref>
|
||||
<NavigationMenuLink
|
||||
className={cn(navigationMenuTriggerStyle(), "h-9")}
|
||||
>
|
||||
GitHub
|
||||
</NavigationMenuLink>
|
||||
</Link>
|
||||
</NavigationMenuItem>
|
||||
</NavigationMenuList>
|
||||
</NavigationMenu>
|
||||
<nav className="flex items-center space-x-6 text-sm font-medium">
|
||||
<Link href="/docs">Documentation</Link>
|
||||
<Link href="/docs/primitives/accordion">Components</Link>
|
||||
<Link href="/figma">Figma</Link>
|
||||
<Link href={siteConfig.links.github}>GitHub</Link>
|
||||
</nav>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const ListItem = React.forwardRef<
|
||||
React.ElementRef<typeof Link>,
|
||||
React.ComponentPropsWithoutRef<typeof Link>
|
||||
>(({ className, title, children, href, ...props }, ref) => {
|
||||
return (
|
||||
<li>
|
||||
<Link href={href} passHref legacyBehavior {...props}>
|
||||
<NavigationMenuLink
|
||||
className={cn(
|
||||
"block select-none space-y-1 rounded-md p-3 leading-none no-underline outline-none transition-colors hover:bg-slate-100 focus:bg-slate-100 dark:hover:bg-slate-700 dark:focus:bg-slate-700",
|
||||
className
|
||||
)}
|
||||
>
|
||||
<div className="text-sm font-medium leading-none">{title}</div>
|
||||
<p className="line-clamp-2 text-sm leading-snug text-slate-500 dark:text-slate-400">
|
||||
{children}
|
||||
</p>
|
||||
</NavigationMenuLink>
|
||||
</Link>
|
||||
</li>
|
||||
)
|
||||
})
|
||||
ListItem.displayName = "ListItem"
|
||||
|
||||
@@ -4,7 +4,7 @@ import { VariantProps, cva } from "class-variance-authority"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const buttonVariants = cva(
|
||||
"active:scale-95 inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-slate-400 focus:ring-offset-2 dark:hover:bg-slate-800 dark:hover:text-slate-100 disabled:opacity-50 dark:focus:ring-slate-400 disabled:pointer-events-none dark:focus:ring-offset-slate-900 data-[state=open]:bg-slate-100 dark:data-[state=open]:bg-slate-800",
|
||||
"inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-slate-400 focus:ring-offset-2 dark:hover:bg-slate-800 dark:hover:text-slate-100 disabled:opacity-50 dark:focus:ring-slate-400 disabled:pointer-events-none dark:focus:ring-offset-slate-900 data-[state=open]:bg-slate-100 dark:data-[state=open]:bg-slate-800",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
|
||||
189
apps/www/components/ui/use-toast.ts
Normal file
189
apps/www/components/ui/use-toast.ts
Normal file
@@ -0,0 +1,189 @@
|
||||
// Inspired by react-hot-toast library
|
||||
import * as React from "react"
|
||||
|
||||
import { ToastActionElement, type ToastProps } from "@/components/ui/toast"
|
||||
|
||||
const TOAST_LIMIT = 1
|
||||
const TOAST_REMOVE_DELAY = 1000
|
||||
|
||||
type ToasterToast = ToastProps & {
|
||||
id: string
|
||||
title?: React.ReactNode
|
||||
description?: React.ReactNode
|
||||
action?: ToastActionElement
|
||||
}
|
||||
|
||||
const actionTypes = {
|
||||
ADD_TOAST: "ADD_TOAST",
|
||||
UPDATE_TOAST: "UPDATE_TOAST",
|
||||
DISMISS_TOAST: "DISMISS_TOAST",
|
||||
REMOVE_TOAST: "REMOVE_TOAST",
|
||||
} as const
|
||||
|
||||
let count = 0
|
||||
|
||||
function genId() {
|
||||
count = (count + 1) % Number.MAX_VALUE
|
||||
return count.toString()
|
||||
}
|
||||
|
||||
type ActionType = typeof actionTypes
|
||||
|
||||
type Action =
|
||||
| {
|
||||
type: ActionType["ADD_TOAST"]
|
||||
toast: ToasterToast
|
||||
}
|
||||
| {
|
||||
type: ActionType["UPDATE_TOAST"]
|
||||
toast: Partial<ToasterToast>
|
||||
}
|
||||
| {
|
||||
type: ActionType["DISMISS_TOAST"]
|
||||
toastId?: ToasterToast["id"]
|
||||
}
|
||||
| {
|
||||
type: ActionType["REMOVE_TOAST"]
|
||||
toastId?: ToasterToast["id"]
|
||||
}
|
||||
|
||||
interface State {
|
||||
toasts: ToasterToast[]
|
||||
}
|
||||
|
||||
const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>()
|
||||
|
||||
const addToRemoveQueue = (toastId: string) => {
|
||||
if (toastTimeouts.has(toastId)) {
|
||||
return
|
||||
}
|
||||
|
||||
const timeout = setTimeout(() => {
|
||||
toastTimeouts.delete(toastId)
|
||||
dispatch({
|
||||
type: "REMOVE_TOAST",
|
||||
toastId: toastId,
|
||||
})
|
||||
}, TOAST_REMOVE_DELAY)
|
||||
|
||||
toastTimeouts.set(toastId, timeout)
|
||||
}
|
||||
|
||||
export const reducer = (state: State, action: Action): State => {
|
||||
switch (action.type) {
|
||||
case "ADD_TOAST":
|
||||
return {
|
||||
...state,
|
||||
toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
|
||||
}
|
||||
|
||||
case "UPDATE_TOAST":
|
||||
return {
|
||||
...state,
|
||||
toasts: state.toasts.map((t) =>
|
||||
t.id === action.toast.id ? { ...t, ...action.toast } : t
|
||||
),
|
||||
}
|
||||
|
||||
case "DISMISS_TOAST": {
|
||||
const { toastId } = action
|
||||
|
||||
// ! Side effects ! - This could be extracted into a dismissToast() action,
|
||||
// but I'll keep it here for simplicity
|
||||
if (toastId) {
|
||||
addToRemoveQueue(toastId)
|
||||
} else {
|
||||
state.toasts.forEach((toast) => {
|
||||
addToRemoveQueue(toast.id)
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
toasts: state.toasts.map((t) =>
|
||||
t.id === toastId || toastId === undefined
|
||||
? {
|
||||
...t,
|
||||
open: false,
|
||||
}
|
||||
: t
|
||||
),
|
||||
}
|
||||
}
|
||||
case "REMOVE_TOAST":
|
||||
if (action.toastId === undefined) {
|
||||
return {
|
||||
...state,
|
||||
toasts: [],
|
||||
}
|
||||
}
|
||||
return {
|
||||
...state,
|
||||
toasts: state.toasts.filter((t) => t.id !== action.toastId),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const listeners: Array<(state: State) => void> = []
|
||||
|
||||
let memoryState: State = { toasts: [] }
|
||||
|
||||
function dispatch(action: Action) {
|
||||
memoryState = reducer(memoryState, action)
|
||||
listeners.forEach((listener) => {
|
||||
listener(memoryState)
|
||||
})
|
||||
}
|
||||
|
||||
interface Toast extends Omit<ToasterToast, "id"> {}
|
||||
|
||||
function toast({ ...props }: Toast) {
|
||||
const id = genId()
|
||||
|
||||
const update = (props: ToasterToast) =>
|
||||
dispatch({
|
||||
type: "UPDATE_TOAST",
|
||||
toast: { ...props, id },
|
||||
})
|
||||
const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id })
|
||||
|
||||
dispatch({
|
||||
type: "ADD_TOAST",
|
||||
toast: {
|
||||
...props,
|
||||
id,
|
||||
open: true,
|
||||
onOpenChange: (open) => {
|
||||
if (!open) dismiss()
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
return {
|
||||
id: id,
|
||||
dismiss,
|
||||
update,
|
||||
}
|
||||
}
|
||||
|
||||
function useToast() {
|
||||
const [state, setState] = React.useState<State>(memoryState)
|
||||
|
||||
React.useEffect(() => {
|
||||
listeners.push(setState)
|
||||
return () => {
|
||||
const index = listeners.indexOf(setState)
|
||||
if (index > -1) {
|
||||
listeners.splice(index, 1)
|
||||
}
|
||||
}
|
||||
}, [state])
|
||||
|
||||
return {
|
||||
...state,
|
||||
toast,
|
||||
dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }),
|
||||
}
|
||||
}
|
||||
|
||||
export { useToast, toast }
|
||||
@@ -1,295 +1,179 @@
|
||||
export const components = [
|
||||
{
|
||||
component: "accordion",
|
||||
name: "Accordion",
|
||||
dependencies: ["@radix-ui/react-accordion"],
|
||||
files: [
|
||||
{
|
||||
name: "accordion.tsx",
|
||||
dir: "components/ui",
|
||||
},
|
||||
],
|
||||
files: ["components/ui/accordion.tsx"],
|
||||
},
|
||||
{
|
||||
component: "alert-dialog",
|
||||
name: "Alert Dialog",
|
||||
dependencies: ["@radix-ui/react-alert-dialog"],
|
||||
files: [
|
||||
{
|
||||
name: "alert-dialog.tsx",
|
||||
dir: "components/ui",
|
||||
},
|
||||
],
|
||||
files: ["components/ui/alert-dialog.tsx"],
|
||||
},
|
||||
{
|
||||
component: "aspect-ratio",
|
||||
name: "Aspect Ratio",
|
||||
dependencies: ["@radix-ui/react-aspect-ratio"],
|
||||
files: [
|
||||
{
|
||||
name: "aspect-ratio.tsx",
|
||||
dir: "components/ui",
|
||||
},
|
||||
],
|
||||
files: ["components/ui/aspect-ratio.tsx"],
|
||||
},
|
||||
{
|
||||
component: "avatar",
|
||||
name: "Avatar",
|
||||
dependencies: ["@radix-ui/react-avatar"],
|
||||
files: [
|
||||
{
|
||||
name: "avatar.tsx",
|
||||
dir: "components/ui",
|
||||
},
|
||||
],
|
||||
files: ["components/ui/avatar.tsx"],
|
||||
},
|
||||
{
|
||||
component: "button",
|
||||
name: "Button",
|
||||
files: [
|
||||
{
|
||||
name: "button.tsx",
|
||||
dir: "components/ui",
|
||||
},
|
||||
],
|
||||
files: ["components/ui/button.tsx"],
|
||||
},
|
||||
{
|
||||
component: "checkbox",
|
||||
name: "Checkbox",
|
||||
dependencies: ["@radix-ui/react-checkbox"],
|
||||
files: [
|
||||
{
|
||||
name: "checkbox.tsx",
|
||||
dir: "components/ui",
|
||||
},
|
||||
],
|
||||
files: ["components/ui/checkbox.tsx"],
|
||||
},
|
||||
{
|
||||
component: "collapsible",
|
||||
name: "Collapsible",
|
||||
dependencies: ["@radix-ui/react-collapsible"],
|
||||
files: [
|
||||
{
|
||||
name: "collapsible.tsx",
|
||||
dir: "components/ui",
|
||||
},
|
||||
],
|
||||
files: ["components/ui/collapsible.tsx"],
|
||||
},
|
||||
{
|
||||
component: "command",
|
||||
name: "Command",
|
||||
dependencies: ["cmdk"],
|
||||
files: [
|
||||
{
|
||||
name: "command.tsx",
|
||||
dir: "components/ui",
|
||||
},
|
||||
],
|
||||
files: ["components/ui/command.tsx"],
|
||||
},
|
||||
{
|
||||
component: "context-menu",
|
||||
name: "Context Menu",
|
||||
dependencies: ["@radix-ui/react-context-menu"],
|
||||
files: [
|
||||
{
|
||||
name: "context-menu.tsx",
|
||||
dir: "components/ui",
|
||||
},
|
||||
],
|
||||
files: ["components/ui/context-menu.tsx"],
|
||||
},
|
||||
{
|
||||
component: "dialog",
|
||||
name: "Dialog",
|
||||
dependencies: ["@radix-ui/react-dialog"],
|
||||
files: [
|
||||
{
|
||||
name: "dialog.tsx",
|
||||
dir: "components/ui",
|
||||
},
|
||||
],
|
||||
files: ["components/ui/dialog.tsx"],
|
||||
},
|
||||
{
|
||||
component: "dropdown-menu",
|
||||
name: "Dropdown Menu",
|
||||
dependencies: ["@radix-ui/react-dropdown-menu"],
|
||||
files: [
|
||||
{
|
||||
name: "dropdown-menu.tsx",
|
||||
dir: "components/ui",
|
||||
},
|
||||
],
|
||||
files: ["components/ui/dropdown-menu.tsx"],
|
||||
},
|
||||
{
|
||||
component: "hover-card",
|
||||
name: "Hover Card",
|
||||
dependencies: ["@radix-ui/react-hover-card"],
|
||||
files: [
|
||||
{
|
||||
name: "hover-card.tsx",
|
||||
dir: "components/ui",
|
||||
},
|
||||
],
|
||||
files: ["components/ui/hover-card.tsx"],
|
||||
},
|
||||
{ name: "Input", files: [{ name: "input.tsx", dir: "components/ui" }] },
|
||||
{
|
||||
component: "input",
|
||||
name: "Input",
|
||||
files: ["components/ui/input.tsx"],
|
||||
},
|
||||
{
|
||||
component: "label",
|
||||
name: "Label",
|
||||
dependencies: ["@radix-ui/react-label"],
|
||||
files: [
|
||||
{
|
||||
name: "label.tsx",
|
||||
dir: "components/ui",
|
||||
},
|
||||
],
|
||||
files: ["components/ui/label.tsx"],
|
||||
},
|
||||
{
|
||||
component: "menubar",
|
||||
name: "Menubar",
|
||||
dependencies: ["@radix-ui/react-menubar"],
|
||||
files: [
|
||||
{
|
||||
name: "menubar.tsx",
|
||||
dir: "components/ui",
|
||||
},
|
||||
],
|
||||
files: ["components/ui/menubar.tsx"],
|
||||
},
|
||||
{
|
||||
component: "navigation-menu",
|
||||
name: "Navigation Menu",
|
||||
dependencies: ["@radix-ui/react-navigation-menu"],
|
||||
files: [
|
||||
{
|
||||
name: "navigation-menu.tsx",
|
||||
dir: "components/ui",
|
||||
},
|
||||
],
|
||||
files: ["components/ui/navigation-menu.tsx"],
|
||||
},
|
||||
{
|
||||
component: "popover",
|
||||
name: "Popover",
|
||||
dependencies: ["@radix-ui/react-popover"],
|
||||
files: [
|
||||
{
|
||||
name: "popover.tsx",
|
||||
dir: "components/ui",
|
||||
},
|
||||
],
|
||||
files: ["components/ui/popover.tsx"],
|
||||
},
|
||||
{
|
||||
component: "progress",
|
||||
name: "Progress",
|
||||
dependencies: ["@radix-ui/react-progress"],
|
||||
files: [
|
||||
{
|
||||
name: "progress.tsx",
|
||||
dir: "components/ui",
|
||||
},
|
||||
],
|
||||
files: ["components/ui/progress.tsx"],
|
||||
},
|
||||
{
|
||||
component: "radio-group",
|
||||
name: "Radio Group",
|
||||
dependencies: ["@radix-ui/react-radio-group"],
|
||||
files: [
|
||||
{
|
||||
name: "radio-group.tsx",
|
||||
dir: "components/ui",
|
||||
},
|
||||
],
|
||||
files: ["components/ui/radio-group.tsx"],
|
||||
},
|
||||
{
|
||||
component: "scroll-area",
|
||||
name: "Scroll-area",
|
||||
dependencies: ["@radix-ui/react-scroll-area"],
|
||||
files: [
|
||||
{
|
||||
name: "scroll-area.tsx",
|
||||
dir: "components/ui",
|
||||
},
|
||||
],
|
||||
files: ["components/ui/scroll-area.tsx"],
|
||||
},
|
||||
{
|
||||
component: "select",
|
||||
name: "Select",
|
||||
dependencies: ["@radix-ui/react-select"],
|
||||
files: [
|
||||
{
|
||||
name: "select.tsx",
|
||||
dir: "components/ui",
|
||||
},
|
||||
],
|
||||
files: ["components/ui/select.tsx"],
|
||||
},
|
||||
{
|
||||
component: "separator",
|
||||
name: "Separator",
|
||||
dependencies: ["@radix-ui/react-separator"],
|
||||
files: [
|
||||
{
|
||||
name: "separator.tsx",
|
||||
dir: "components/ui",
|
||||
},
|
||||
],
|
||||
files: ["components/ui/separator.tsx"],
|
||||
},
|
||||
{
|
||||
component: "sheet",
|
||||
name: "Sheet",
|
||||
dependencies: ["@radix-ui/react-dialog"],
|
||||
files: [
|
||||
{
|
||||
name: "sheet.tsx",
|
||||
dir: "components/ui",
|
||||
},
|
||||
],
|
||||
files: ["components/ui/sheet.tsx"],
|
||||
},
|
||||
{
|
||||
component: "slider",
|
||||
name: "Slider",
|
||||
dependencies: ["@radix-ui/react-slider"],
|
||||
files: [
|
||||
{
|
||||
name: "slider.tsx",
|
||||
dir: "components/ui",
|
||||
},
|
||||
],
|
||||
files: ["components/ui/slider.tsx"],
|
||||
},
|
||||
{
|
||||
component: "switch",
|
||||
name: "Switch",
|
||||
dependencies: ["@radix-ui/react-switch"],
|
||||
files: [
|
||||
{
|
||||
name: "switch.tsx",
|
||||
dir: "components/ui",
|
||||
},
|
||||
],
|
||||
files: ["components/ui/switch.tsx"],
|
||||
},
|
||||
{
|
||||
component: "tabs",
|
||||
name: "Tabs",
|
||||
dependencies: ["@radix-ui/react-tabs"],
|
||||
files: [
|
||||
{
|
||||
name: "tabs.tsx",
|
||||
dir: "components/ui",
|
||||
},
|
||||
],
|
||||
files: ["components/ui/tabs.tsx"],
|
||||
},
|
||||
{
|
||||
component: "textarea",
|
||||
name: "Textarea",
|
||||
files: [
|
||||
{
|
||||
name: "textarea.tsx",
|
||||
dir: "components/ui",
|
||||
},
|
||||
],
|
||||
files: ["components/ui/textarea.tsx"],
|
||||
},
|
||||
{
|
||||
component: "toast",
|
||||
name: "Toast",
|
||||
dependencies: ["@radix-ui/react-toast"],
|
||||
files: [
|
||||
{
|
||||
name: "toast.tsx",
|
||||
dir: "components/ui",
|
||||
},
|
||||
{
|
||||
name: "use-toast.ts",
|
||||
dir: "hooks",
|
||||
},
|
||||
],
|
||||
files: ["components/ui/toast.tsx", "components/ui/use-toast.ts"],
|
||||
},
|
||||
{
|
||||
component: "toggle",
|
||||
name: "Toggle",
|
||||
dependencies: ["@radix-ui/react-toggle"],
|
||||
files: [
|
||||
{
|
||||
name: "toggle.tsx",
|
||||
dir: "components/ui",
|
||||
},
|
||||
],
|
||||
files: ["components/ui/toggle.tsx"],
|
||||
},
|
||||
{
|
||||
component: "tooltip",
|
||||
name: "Tooltip",
|
||||
dependencies: ["@radix-ui/react-tooltip"],
|
||||
files: [
|
||||
{
|
||||
name: "tooltip.tsx",
|
||||
dir: "components/ui",
|
||||
},
|
||||
],
|
||||
files: ["components/ui/tooltip.tsx"],
|
||||
},
|
||||
]
|
||||
|
||||
@@ -57,7 +57,7 @@ I did. But I'm not really a designer. Most of what you see here, I learned by lo
|
||||
|
||||
### Is it ready for production?
|
||||
|
||||
Almost. I'm using it in production, but I'm still adding components and fixing bugs. See the roadmap on [GitHub](https://github.com/shadcn/ui).
|
||||
Yes. You can use it in your production apps.
|
||||
|
||||
### How do I customize the colors?
|
||||
|
||||
|
||||
@@ -33,7 +33,11 @@ This is the `<Collapsible />` primitive. You can place it in a file at `componen
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import { Collapsible } from "@/components/ui/collapsible"
|
||||
import {
|
||||
Collapsible,
|
||||
CollapsibleContent,
|
||||
CollapsibleTrigger,
|
||||
} from "@/components/ui/collapsible"
|
||||
```
|
||||
|
||||
```tsx
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: Dialog
|
||||
description: A modal dialog that interrupts the user with important content and expects a response.
|
||||
description: A window overlaid on either the primary window or another dialog window, rendering the content underneath inert.
|
||||
featured: true
|
||||
component: true
|
||||
radix:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: Scroll-area
|
||||
description: Visually or semantically separates content.
|
||||
description: Augments native scroll functionality for custom, cross-browser styling.
|
||||
component: true
|
||||
radix:
|
||||
link: https://www.radix-ui.com/docs/primitives/components/scroll-area
|
||||
|
||||
@@ -39,7 +39,7 @@ import {
|
||||
SheetHeader,
|
||||
SheetTitle,
|
||||
SheetTrigger,
|
||||
} from "@/components/ui/dialog"
|
||||
} from "@/components/ui/sheet"
|
||||
```
|
||||
|
||||
```tsx
|
||||
|
||||
1
apps/www/next-env.d.ts
vendored
1
apps/www/next-env.d.ts
vendored
@@ -1,5 +1,6 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
/// <reference types="next/navigation-types/compat/navigation" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
"format:check": "prettier --check \"**/*.{ts,tsx,mdx}\" --cache"
|
||||
},
|
||||
"dependencies": {
|
||||
"@next/font": "^13.1.6",
|
||||
"@radix-ui/react-accessible-icon": "^1.0.1",
|
||||
"@radix-ui/react-accordion": "^1.1.0",
|
||||
"@radix-ui/react-alert-dialog": "^1.0.2",
|
||||
@@ -50,7 +49,7 @@
|
||||
"cmdk": "^0.1.21",
|
||||
"contentlayer": "^0.3.0",
|
||||
"lucide-react": "0.105.0-alpha.4",
|
||||
"next": "^13.1.6",
|
||||
"next": "13.2.4",
|
||||
"next-contentlayer": "^0.3.0",
|
||||
"next-themes": "^0.2.1",
|
||||
"react": "^18.2.0",
|
||||
@@ -77,7 +76,6 @@
|
||||
"shiki": "^0.12.1",
|
||||
"tailwindcss": "^3.1.7",
|
||||
"typescript": "^4.5.3",
|
||||
"unist": "^8.0.11",
|
||||
"unist-builder": "^3.0.0",
|
||||
"unist-util-visit": "^4.1.1"
|
||||
},
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -6,13 +6,11 @@ import { components } from "../config/components"
|
||||
const payload = components
|
||||
.map((component) => {
|
||||
const files = component.files?.map((file) => {
|
||||
const content = fs.readFileSync(
|
||||
path.join(process.cwd(), file.dir, file.name),
|
||||
"utf8"
|
||||
)
|
||||
const content = fs.readFileSync(path.join(process.cwd(), file), "utf8")
|
||||
|
||||
return {
|
||||
...file,
|
||||
name: basename(file),
|
||||
dir: dirname(file),
|
||||
content,
|
||||
}
|
||||
})
|
||||
|
||||
@@ -16,10 +16,11 @@
|
||||
],
|
||||
"scripts": {
|
||||
"build": "turbo run build",
|
||||
"build:cli": "turbo --filter=@shadcn/ui build",
|
||||
"build:cli": "turbo --filter=shadcn-ui build",
|
||||
"build:components": "pnpm --filter=www build:components",
|
||||
"dev": "turbo run dev --parallel",
|
||||
"dev:cli": "turbo --filter=@shadcn/ui dev",
|
||||
"start:cli": "pnpm --filter=@shadcn/ui start:dev",
|
||||
"dev:cli": "turbo --filter=shadcn-ui dev",
|
||||
"start:cli": "pnpm --filter=shadcn-ui start:dev",
|
||||
"lint": "turbo run lint",
|
||||
"preview": "turbo run preview",
|
||||
"typecheck": "turbo run typecheck",
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
# @shadcn/ui
|
||||
|
||||
## 0.1.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- [#178](https://github.com/shadcn/ui/pull/178) [`96f300e`](https://github.com/shadcn/ui/commit/96f300ea7471de9de9d433114d010d8fef2c8bae) Thanks [@shadcn](https://github.com/shadcn)! - add init command
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#175](https://github.com/shadcn/ui/pull/175) [`b5d4368`](https://github.com/shadcn/ui/commit/b5d43688b975eb66b95b71af0396d07f94dde247) Thanks [@shadcn](https://github.com/shadcn)! - rename package to shadcn-ui
|
||||
|
||||
## 0.0.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# @shadcn/ui
|
||||
# shadcn-ui
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
npx @shadcn/ui
|
||||
npx shadcn-ui add
|
||||
```
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@shadcn/ui",
|
||||
"version": "0.0.4",
|
||||
"description": "Add @shadcn/ui components to your app.",
|
||||
"name": "shadcn-ui",
|
||||
"version": "0.1.0",
|
||||
"description": "Add components to your apps.",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
@@ -15,6 +15,9 @@
|
||||
"url": "https://github.com/shadcn/ui.git",
|
||||
"directory": "packages/cli"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"keywords": [
|
||||
"components",
|
||||
"ui",
|
||||
@@ -24,15 +27,13 @@
|
||||
],
|
||||
"type": "module",
|
||||
"exports": "./dist/index.js",
|
||||
"bin": {
|
||||
"@shadcn/ui": "./dist/index.js"
|
||||
},
|
||||
"bin": "./dist/index.js",
|
||||
"scripts": {
|
||||
"dev": "tsup --watch",
|
||||
"build": "tsup",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"clean": "rimraf dist && rimraf components",
|
||||
"start:dev": "cross-env COMPONENTS_BASE_URL=http://localhost:3000 node dist/index.js",
|
||||
"start:dev": "cross-env COMPONENTS_BASE_URL=http://localhost:3001 node dist/index.js",
|
||||
"start": "node dist/index.js",
|
||||
"format:write": "prettier --write \"**/*.{ts,tsx,mdx}\" --cache",
|
||||
"format:check": "prettier --check \"**/*.{ts,tsx,mdx}\" --cache",
|
||||
@@ -57,6 +58,6 @@
|
||||
"rimraf": "^4.1.3",
|
||||
"tsup": "^6.6.3",
|
||||
"type-fest": "^3.6.1",
|
||||
"typescript": "^4.9.5"
|
||||
"typescript": "^4.5.5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,27 +9,123 @@ import prompts from "prompts"
|
||||
import { Component, getAvailableComponents } from "./utils/get-components"
|
||||
import { getPackageInfo } from "./utils/get-package-info"
|
||||
import { getPackageManager } from "./utils/get-package-manager"
|
||||
import { getProjectInfo } from "./utils/get-project-info"
|
||||
import { logger } from "./utils/logger"
|
||||
import { STYLES, TAILWIND_CONFIG, UTILS } from "./utils/templates"
|
||||
|
||||
process.on("SIGINT", () => process.exit(0))
|
||||
process.on("SIGTERM", () => process.exit(0))
|
||||
|
||||
const PROJECT_DEPENDENCIES = [
|
||||
"tailwindcss-animate",
|
||||
"class-variance-authority",
|
||||
"clsx",
|
||||
"tailwind-merge",
|
||||
"lucide-react",
|
||||
]
|
||||
|
||||
async function main() {
|
||||
const packageInfo = await getPackageInfo()
|
||||
const projectInfo = await getProjectInfo()
|
||||
const packageManager = getPackageManager()
|
||||
|
||||
const program = new Command()
|
||||
.name("@shadcn/ui")
|
||||
.description("Add @shadcn/ui components to your project")
|
||||
.name("shadcn-ui")
|
||||
.description("Add shadcn-ui components to your project")
|
||||
.version(
|
||||
packageInfo.version || "1.0.0",
|
||||
"-v, --version",
|
||||
"display the version number"
|
||||
)
|
||||
|
||||
program
|
||||
.command("init")
|
||||
.description("Configure your Next.js project.")
|
||||
.option("-y, --yes", "Skip confirmation prompt.")
|
||||
.action(async (options) => {
|
||||
logger.warn(
|
||||
"Running the following command will overwrite existing files."
|
||||
)
|
||||
logger.warn(
|
||||
"Make sure you have committed your changes before proceeding."
|
||||
)
|
||||
logger.warn("")
|
||||
logger.warn(
|
||||
"This command assumes a Next.js project with TypeScript and Tailwind CSS."
|
||||
)
|
||||
logger.warn(
|
||||
"If you don't have these, follow the manual steps at https://ui.shadcn.com/docs/installation."
|
||||
)
|
||||
logger.warn("")
|
||||
|
||||
if (!options.yes) {
|
||||
const { proceed } = await prompts({
|
||||
type: "confirm",
|
||||
name: "proceed",
|
||||
message:
|
||||
"Running this command will install dependencies and overwrite files. Proceed?",
|
||||
initial: true,
|
||||
})
|
||||
|
||||
if (!proceed) {
|
||||
process.exit(0)
|
||||
}
|
||||
}
|
||||
|
||||
// Install dependencies.
|
||||
const dependenciesSpinner = ora(`Installing dependencies...`).start()
|
||||
await execa(packageManager, [
|
||||
packageManager === "npm" ? "install" : "add",
|
||||
...PROJECT_DEPENDENCIES,
|
||||
])
|
||||
dependenciesSpinner.succeed()
|
||||
|
||||
// Ensure styles directory exists.
|
||||
if (!projectInfo?.appDir) {
|
||||
const stylesDir = projectInfo?.srcDir ? "./src/styles" : "./styles"
|
||||
if (!existsSync(path.resolve(stylesDir))) {
|
||||
await fs.mkdir(path.resolve(stylesDir), { recursive: true })
|
||||
}
|
||||
}
|
||||
|
||||
// Update styles.css
|
||||
let stylesDestination = projectInfo?.srcDir
|
||||
? "./src/styles/globals.css"
|
||||
: "./styles/globals.css"
|
||||
if (projectInfo?.appDir) {
|
||||
stylesDestination = projectInfo?.srcDir
|
||||
? "./src/app/globals.css"
|
||||
: "./app/globals.css"
|
||||
}
|
||||
const stylesSpinner = ora(`Adding styles with CSS variables...`).start()
|
||||
await fs.writeFile(stylesDestination, STYLES, "utf8")
|
||||
stylesSpinner.succeed()
|
||||
|
||||
// Ensure lib directory exists.
|
||||
const libDir = projectInfo?.srcDir ? "./src/lib" : "./lib"
|
||||
if (!existsSync(path.resolve(libDir))) {
|
||||
await fs.mkdir(path.resolve(libDir), { recursive: true })
|
||||
}
|
||||
|
||||
// Create lib/utils.ts
|
||||
const utilsDestination = projectInfo?.srcDir
|
||||
? "./src/lib/utils.ts"
|
||||
: "./lib/utils.ts"
|
||||
const utilsSpinner = ora(`Adding utils...`).start()
|
||||
await fs.writeFile(utilsDestination, UTILS, "utf8")
|
||||
utilsSpinner.succeed()
|
||||
|
||||
const tailwindDestination = "./tailwind.config.js"
|
||||
const tailwindSpinner = ora(`Updating tailwind.config.js...`).start()
|
||||
await fs.writeFile(tailwindDestination, TAILWIND_CONFIG, "utf8")
|
||||
tailwindSpinner.succeed()
|
||||
})
|
||||
|
||||
program
|
||||
.command("add")
|
||||
.description("add components to your project")
|
||||
.action(async () => {
|
||||
.argument("[components...]", "name of components")
|
||||
.action(async (components: string[]) => {
|
||||
logger.warn(
|
||||
"Running the following command will overwrite existing files."
|
||||
)
|
||||
@@ -38,8 +134,26 @@ async function main() {
|
||||
)
|
||||
logger.warn("")
|
||||
|
||||
const { components, dir } = await promptForAddOptions()
|
||||
if (!components?.length) {
|
||||
const availableComponents = await getAvailableComponents()
|
||||
|
||||
if (!availableComponents?.length) {
|
||||
logger.error(
|
||||
"An error occurred while fetching components. Please try again."
|
||||
)
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
let selectedComponents = availableComponents.filter((component) =>
|
||||
components.includes(component.component)
|
||||
)
|
||||
|
||||
if (!selectedComponents?.length) {
|
||||
selectedComponents = await promptForComponents(availableComponents)
|
||||
}
|
||||
|
||||
const dir = await promptForDestinationDir()
|
||||
|
||||
if (!selectedComponents?.length) {
|
||||
logger.warn("No components selected. Nothing to install.")
|
||||
process.exit(0)
|
||||
}
|
||||
@@ -52,14 +166,19 @@ async function main() {
|
||||
spinner.succeed()
|
||||
}
|
||||
|
||||
const packageManager = getPackageManager()
|
||||
|
||||
logger.success(`Installing components...`)
|
||||
for (const component of components) {
|
||||
logger.success(
|
||||
`Installing ${selectedComponents.length} component(s) and dependencies...`
|
||||
)
|
||||
for (const component of selectedComponents) {
|
||||
const componentSpinner = ora(`${component.name}...`).start()
|
||||
|
||||
// Write the files.
|
||||
for (const file of component.files) {
|
||||
// Replace alias with the project's alias.
|
||||
if (projectInfo?.alias) {
|
||||
file.content = file.content.replace(/@\//g, projectInfo.alias)
|
||||
}
|
||||
|
||||
const filePath = path.resolve(dir, file.name)
|
||||
await fs.writeFile(filePath, file.content)
|
||||
}
|
||||
@@ -79,34 +198,24 @@ async function main() {
|
||||
program.parse()
|
||||
}
|
||||
|
||||
type AddOptions = {
|
||||
components: Component[]
|
||||
dir: string
|
||||
async function promptForComponents(components: Component[]) {
|
||||
const { components: selectedComponents } = await prompts({
|
||||
type: "autocompleteMultiselect",
|
||||
name: "components",
|
||||
message: "Which component(s) would you like to add?",
|
||||
hint: "Space to select. A to select all. I to invert selection.",
|
||||
instructions: false,
|
||||
choices: components.map((component) => ({
|
||||
title: component.name,
|
||||
value: component,
|
||||
})),
|
||||
})
|
||||
|
||||
return selectedComponents
|
||||
}
|
||||
|
||||
async function promptForAddOptions() {
|
||||
const availableComponents = await getAvailableComponents()
|
||||
|
||||
if (!availableComponents?.length) {
|
||||
logger.error(
|
||||
"An error occurred while fetching components. Please try again."
|
||||
)
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
const options = await prompts([
|
||||
{
|
||||
type: "multiselect",
|
||||
name: "components",
|
||||
message: "Which component(s) would you like to add?",
|
||||
hint: "Space to select. A to select all. I to invert selection.",
|
||||
instructions: false,
|
||||
|
||||
choices: availableComponents.map((component) => ({
|
||||
title: component.name,
|
||||
value: component,
|
||||
})),
|
||||
},
|
||||
async function promptForDestinationDir() {
|
||||
const { dir } = await prompts([
|
||||
{
|
||||
type: "text",
|
||||
name: "dir",
|
||||
@@ -115,7 +224,7 @@ async function promptForAddOptions() {
|
||||
},
|
||||
])
|
||||
|
||||
return options as AddOptions
|
||||
return dir
|
||||
}
|
||||
|
||||
main()
|
||||
|
||||
@@ -4,6 +4,7 @@ import * as z from "zod"
|
||||
const baseUrl = process.env.COMPONENTS_BASE_URL ?? "https://ui.shadcn.com"
|
||||
|
||||
const componentSchema = z.object({
|
||||
component: z.string(),
|
||||
name: z.string(),
|
||||
dependencies: z.array(z.string()).optional(),
|
||||
files: z.array(
|
||||
|
||||
44
packages/cli/src/utils/get-project-info.ts
Normal file
44
packages/cli/src/utils/get-project-info.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import { existsSync } from "fs"
|
||||
import path from "path"
|
||||
import fs from "fs-extra"
|
||||
|
||||
export async function getProjectInfo() {
|
||||
const info = {
|
||||
tsconfig: null,
|
||||
alias: null,
|
||||
srcDir: false,
|
||||
appDir: false,
|
||||
}
|
||||
|
||||
try {
|
||||
const tsconfig = await getTsConfig()
|
||||
const paths = tsconfig?.compilerOptions?.paths
|
||||
const alias = paths ? Object.keys(paths)[0].replace("*", "") : null
|
||||
|
||||
return {
|
||||
tsconfig,
|
||||
alias,
|
||||
srcDir: existsSync(path.resolve("./src")),
|
||||
appDir:
|
||||
existsSync(path.resolve("./app")) ||
|
||||
existsSync(path.resolve("./src/app")),
|
||||
}
|
||||
} catch (error) {
|
||||
return info
|
||||
}
|
||||
}
|
||||
|
||||
export async function getTsConfig() {
|
||||
try {
|
||||
const tsconfigPath = path.join("tsconfig.json")
|
||||
const tsconfig = await fs.readJSON(tsconfigPath)
|
||||
|
||||
if (!tsconfig) {
|
||||
throw new Error("tsconfig.json is missing")
|
||||
}
|
||||
|
||||
return tsconfig
|
||||
} catch (error) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
165
packages/cli/src/utils/templates.ts
Normal file
165
packages/cli/src/utils/templates.ts
Normal file
@@ -0,0 +1,165 @@
|
||||
export const STYLES = `@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer base {
|
||||
: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%;
|
||||
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 222.2 47.4% 11.2%;
|
||||
|
||||
--border: 214.3 31.8% 91.4%;
|
||||
--input: 214.3 31.8% 91.4%;
|
||||
|
||||
--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%;
|
||||
|
||||
--popover: 224 71% 4%;
|
||||
--popover-foreground: 215 20.2% 65.1%;
|
||||
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 222.2 47.4% 11.2%;
|
||||
|
||||
--border: 216 34% 17%;
|
||||
--input: 216 34% 17%;
|
||||
|
||||
--primary: 210 40% 98%;
|
||||
--primary-foreground: 222.2 47.4% 1.2%;
|
||||
|
||||
--secondary: 222.2 47.4% 11.2%;
|
||||
--secondary-foreground: 210 40% 98%;
|
||||
|
||||
--accent: 216 34% 17%;
|
||||
--accent-foreground: 210 40% 98%;
|
||||
|
||||
--destructive: 0 63% 31%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
|
||||
--ring: 216 34% 17%;
|
||||
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border;
|
||||
}
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
font-feature-settings: "rlig" 1, "calt" 1;
|
||||
}
|
||||
}`
|
||||
|
||||
export const UTILS = `import { ClassValue, clsx } from "clsx"
|
||||
import { twMerge } from "tailwind-merge"
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs))
|
||||
}
|
||||
`
|
||||
|
||||
export const TAILWIND_CONFIG = `/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
darkMode: ["class"],
|
||||
content: [
|
||||
'./pages/**/*.{ts,tsx}',
|
||||
'./components/**/*.{ts,tsx}',
|
||||
'./app/**/*.{ts,tsx}',
|
||||
],
|
||||
theme: {
|
||||
container: {
|
||||
center: true,
|
||||
padding: "2rem",
|
||||
screens: {
|
||||
"2xl": "1400px",
|
||||
},
|
||||
},
|
||||
extend: {
|
||||
colors: {
|
||||
border: "hsl(var(--border))",
|
||||
input: "hsl(var(--input))",
|
||||
ring: "hsl(var(--ring))",
|
||||
background: "hsl(var(--background))",
|
||||
foreground: "hsl(var(--foreground))",
|
||||
primary: {
|
||||
DEFAULT: "hsl(var(--primary))",
|
||||
foreground: "hsl(var(--primary-foreground))",
|
||||
},
|
||||
secondary: {
|
||||
DEFAULT: "hsl(var(--secondary))",
|
||||
foreground: "hsl(var(--secondary-foreground))",
|
||||
},
|
||||
destructive: {
|
||||
DEFAULT: "hsl(var(--destructive))",
|
||||
foreground: "hsl(var(--destructive-foreground))",
|
||||
},
|
||||
muted: {
|
||||
DEFAULT: "hsl(var(--muted))",
|
||||
foreground: "hsl(var(--muted-foreground))",
|
||||
},
|
||||
accent: {
|
||||
DEFAULT: "hsl(var(--accent))",
|
||||
foreground: "hsl(var(--accent-foreground))",
|
||||
},
|
||||
popover: {
|
||||
DEFAULT: "hsl(var(--popover))",
|
||||
foreground: "hsl(var(--popover-foreground))",
|
||||
},
|
||||
card: {
|
||||
DEFAULT: "hsl(var(--card))",
|
||||
foreground: "hsl(var(--card-foreground))",
|
||||
},
|
||||
},
|
||||
borderRadius: {
|
||||
lg: "var(--radius)",
|
||||
md: "calc(var(--radius) - 2px)",
|
||||
sm: "calc(var(--radius) - 4px)",
|
||||
},
|
||||
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")],
|
||||
}`
|
||||
4903
pnpm-lock.yaml
generated
4903
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -50,7 +50,7 @@ const AccordionContent = React.forwardRef<
|
||||
<AccordionPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"overflow-hidden text-sm transition-all data-[state=open]:animate-accordion-down data-[state=closed]:animate-accordion-up",
|
||||
"data-[state=open]:animate-accordion-down data-[state=closed]:animate-accordion-up overflow-hidden text-sm transition-all",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -28,7 +28,7 @@ const AlertDialogOverlay = React.forwardRef<
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<AlertDialogPrimitive.Overlay
|
||||
className={cn(
|
||||
"fixed inset-0 z-50 bg-black/50 backdrop-blur-sm transition-opacity animate-in fade-in",
|
||||
"animate-in fade-in fixed inset-0 z-50 bg-black/50 backdrop-blur-sm transition-opacity",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -46,7 +46,7 @@ const AlertDialogContent = React.forwardRef<
|
||||
<AlertDialogPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"fixed z-50 grid w-full max-w-lg scale-100 gap-4 bg-white p-6 opacity-100 animate-in fade-in-90 slide-in-from-bottom-10 sm:rounded-lg sm:zoom-in-90 sm:slide-in-from-bottom-0 md:w-full",
|
||||
"animate-in fade-in-90 slide-in-from-bottom-10 sm:zoom-in-90 sm:slide-in-from-bottom-0 fixed z-50 grid w-full max-w-lg scale-100 gap-4 bg-white p-6 opacity-100 sm:rounded-lg md:w-full",
|
||||
"dark:bg-slate-900",
|
||||
className
|
||||
)}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { VariantProps, cva } from "class-variance-authority"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const buttonVariants = cva(
|
||||
"active:scale-95 inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-slate-400 focus:ring-offset-2 dark:hover:bg-slate-800 dark:hover:text-slate-100 disabled:opacity-50 dark:focus:ring-slate-400 disabled:pointer-events-none dark:focus:ring-offset-slate-900 data-[state=open]:bg-slate-100 dark:data-[state=open]:bg-slate-800",
|
||||
"inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-slate-400 focus:ring-offset-2 dark:hover:bg-slate-800 dark:hover:text-slate-100 disabled:opacity-50 dark:focus:ring-slate-400 disabled:pointer-events-none dark:focus:ring-offset-slate-900 data-[state=open]:bg-slate-100 dark:data-[state=open]:bg-slate-800",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
import * as React from "react"
|
||||
import { DialogProps } from "@radix-ui/react-dialog"
|
||||
import { Command as CommandPrimitive, useCommandState } from "cmdk"
|
||||
import { ChevronsUpDown, Search } from "lucide-react"
|
||||
import { Command as CommandPrimitive } from "cmdk"
|
||||
import { Search } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { Dialog, DialogContent } from "@/components/ui/dialog"
|
||||
|
||||
@@ -46,7 +46,7 @@ const ContextMenuSubContent = React.forwardRef<
|
||||
<ContextMenuPrimitive.SubContent
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"z-50 min-w-[8rem] overflow-hidden rounded-md border border-slate-100 bg-white p-1 shadow-md animate-in slide-in-from-left-1 dark:border-slate-700 dark:bg-slate-800",
|
||||
"animate-in slide-in-from-left-1 z-50 min-w-[8rem] overflow-hidden rounded-md border border-slate-100 bg-white p-1 shadow-md dark:border-slate-700 dark:bg-slate-800",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -62,7 +62,7 @@ const ContextMenuContent = React.forwardRef<
|
||||
<ContextMenuPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"z-50 min-w-[8rem] overflow-hidden rounded-md border border-slate-100 bg-white p-1 text-slate-700 shadow-md animate-in fade-in-80 dark:border-slate-800 dark:bg-slate-800 dark:text-slate-400",
|
||||
"animate-in fade-in-80 z-50 min-w-[8rem] overflow-hidden rounded-md border border-slate-100 bg-white p-1 text-slate-700 shadow-md dark:border-slate-800 dark:bg-slate-800 dark:text-slate-400",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -29,7 +29,7 @@ const DialogOverlay = React.forwardRef<
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<DialogPrimitive.Overlay
|
||||
className={cn(
|
||||
"fixed inset-0 z-50 bg-black/50 backdrop-blur-sm transition-all duration-100 data-[state=closed]:animate-out data-[state=open]:fade-in data-[state=closed]:fade-out",
|
||||
"data-[state=closed]:animate-out data-[state=open]:fade-in data-[state=closed]:fade-out fixed inset-0 z-50 bg-black/50 backdrop-blur-sm transition-all duration-100",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -47,7 +47,7 @@ const DialogContent = React.forwardRef<
|
||||
<DialogPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"fixed z-50 grid w-full gap-4 rounded-b-lg bg-white p-6 animate-in data-[state=open]:fade-in-90 data-[state=open]:slide-in-from-bottom-10 sm:max-w-lg sm:rounded-lg sm:zoom-in-90 data-[state=open]:sm:slide-in-from-bottom-0",
|
||||
"animate-in data-[state=open]:fade-in-90 data-[state=open]:slide-in-from-bottom-10 sm:zoom-in-90 data-[state=open]:sm:slide-in-from-bottom-0 fixed z-50 grid w-full gap-4 rounded-b-lg bg-white p-6 sm:max-w-lg sm:rounded-lg",
|
||||
"dark:bg-slate-900",
|
||||
className
|
||||
)}
|
||||
|
||||
@@ -47,7 +47,7 @@ const DropdownMenuSubContent = React.forwardRef<
|
||||
<DropdownMenuPrimitive.SubContent
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"z-50 min-w-[8rem] overflow-hidden rounded-md border border-slate-100 bg-white p-1 text-slate-700 shadow-md animate-in slide-in-from-left-1 dark:border-slate-800 dark:bg-slate-800 dark:text-slate-400",
|
||||
"animate-in slide-in-from-left-1 z-50 min-w-[8rem] overflow-hidden rounded-md border border-slate-100 bg-white p-1 text-slate-700 shadow-md dark:border-slate-800 dark:bg-slate-800 dark:text-slate-400",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -65,7 +65,7 @@ const DropdownMenuContent = React.forwardRef<
|
||||
ref={ref}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"z-50 min-w-[8rem] overflow-hidden rounded-md border border-slate-100 bg-white p-1 text-slate-700 shadow-md animate-in data-[side=right]:slide-in-from-left-2 data-[side=left]:slide-in-from-right-2 data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2 dark:border-slate-800 dark:bg-slate-800 dark:text-slate-400",
|
||||
"animate-in data-[side=right]:slide-in-from-left-2 data-[side=left]:slide-in-from-right-2 data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] overflow-hidden rounded-md border border-slate-100 bg-white p-1 text-slate-700 shadow-md dark:border-slate-800 dark:bg-slate-800 dark:text-slate-400",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -18,7 +18,7 @@ const HoverCardContent = React.forwardRef<
|
||||
align={align}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"z-50 w-64 rounded-md border border-slate-100 bg-white p-4 shadow-md outline-none animate-in zoom-in-90 dark:border-slate-800 dark:bg-slate-800",
|
||||
"animate-in zoom-in-90 z-50 w-64 rounded-md border border-slate-100 bg-white p-4 shadow-md outline-none dark:border-slate-800 dark:bg-slate-800",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -74,7 +74,7 @@ const MenubarSubContent = React.forwardRef<
|
||||
<MenubarPrimitive.SubContent
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"z-50 min-w-[8rem] overflow-hidden rounded-md border border-slate-100 bg-white p-1 shadow-md animate-in slide-in-from-left-1 dark:border-slate-700 dark:bg-slate-800",
|
||||
"animate-in slide-in-from-left-1 z-50 min-w-[8rem] overflow-hidden rounded-md border border-slate-100 bg-white p-1 shadow-md dark:border-slate-700 dark:bg-slate-800",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -97,7 +97,7 @@ const MenubarContent = React.forwardRef<
|
||||
alignOffset={alignOffset}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"z-50 min-w-[12rem] overflow-hidden rounded-md border border-slate-100 bg-white p-1 text-slate-700 shadow-md animate-in slide-in-from-top-1 dark:border-slate-800 dark:bg-slate-800 dark:text-slate-400",
|
||||
"animate-in slide-in-from-top-1 z-50 min-w-[12rem] overflow-hidden rounded-md border border-slate-100 bg-white p-1 text-slate-700 shadow-md dark:border-slate-800 dark:bg-slate-800 dark:text-slate-400",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -69,7 +69,7 @@ const NavigationMenuContent = React.forwardRef<
|
||||
<NavigationMenuPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"top-0 left-0 w-full data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=to-start]:slide-out-to-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=from-end]:slide-in-from-right-52 md:absolute md:w-auto ",
|
||||
"data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=to-start]:slide-out-to-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=from-end]:slide-in-from-right-52 top-0 left-0 w-full md:absolute md:w-auto ",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -86,7 +86,7 @@ const NavigationMenuViewport = React.forwardRef<
|
||||
<div className={cn("absolute left-0 top-full flex justify-center")}>
|
||||
<NavigationMenuPrimitive.Viewport
|
||||
className={cn(
|
||||
"origin-top-center relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border border-slate-200 bg-white shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=open]:zoom-in-90 data-[state=closed]:zoom-out-95 dark:border-slate-700 dark:bg-slate-800 md:w-[var(--radix-navigation-menu-viewport-width)]",
|
||||
"origin-top-center data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=open]:zoom-in-90 data-[state=closed]:zoom-out-95 relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border border-slate-200 bg-white shadow-lg dark:border-slate-700 dark:bg-slate-800 md:w-[var(--radix-navigation-menu-viewport-width)]",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
@@ -104,7 +104,7 @@ const NavigationMenuIndicator = React.forwardRef<
|
||||
<NavigationMenuPrimitive.Indicator
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=visible]:fade-in data-[state=hidden]:fade-out",
|
||||
"data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=visible]:fade-in data-[state=hidden]:fade-out top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -19,7 +19,7 @@ const PopoverContent = React.forwardRef<
|
||||
align={align}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"z-50 w-72 rounded-md border border-slate-100 bg-white p-4 shadow-md outline-none animate-in data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2 data-[side=right]:slide-in-from-left-2 data-[side=left]:slide-in-from-right-2 dark:border-slate-800 dark:bg-slate-800",
|
||||
"animate-in data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2 data-[side=right]:slide-in-from-left-2 data-[side=left]:slide-in-from-right-2 z-50 w-72 rounded-md border border-slate-100 bg-white p-4 shadow-md outline-none dark:border-slate-800 dark:bg-slate-800",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -38,7 +38,7 @@ const SelectContent = React.forwardRef<
|
||||
<SelectPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative z-50 min-w-[8rem] overflow-hidden rounded-md border border-slate-100 bg-white text-slate-700 shadow-md animate-in fade-in-80 dark:border-slate-800 dark:bg-slate-800 dark:text-slate-400",
|
||||
"animate-in fade-in-80 relative z-50 min-w-[8rem] overflow-hidden rounded-md border border-slate-100 bg-white text-slate-700 shadow-md dark:border-slate-800 dark:bg-slate-800 dark:text-slate-400",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -45,7 +45,7 @@ const SheetOverlay = React.forwardRef<
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<SheetPrimitive.Overlay
|
||||
className={cn(
|
||||
"fixed inset-0 z-50 bg-black/50 backdrop-blur-sm transition-all duration-100 data-[state=closed]:animate-out data-[state=open]:fade-in data-[state=closed]:fade-out",
|
||||
"data-[state=closed]:animate-out data-[state=open]:fade-in data-[state=closed]:fade-out fixed inset-0 z-50 bg-black/50 backdrop-blur-sm transition-all duration-100",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -20,7 +20,7 @@ const TooltipContent = React.forwardRef<
|
||||
ref={ref}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"z-50 overflow-hidden rounded-md border border-slate-100 bg-white px-3 py-1.5 text-sm text-slate-700 shadow-md animate-in fade-in-50 data-[side=bottom]:slide-in-from-top-1 data-[side=top]:slide-in-from-bottom-1 data-[side=left]:slide-in-from-right-1 data-[side=right]:slide-in-from-left-1 dark:border-slate-800 dark:bg-slate-800 dark:text-slate-400",
|
||||
"animate-in fade-in-50 data-[side=bottom]:slide-in-from-top-1 data-[side=top]:slide-in-from-bottom-1 data-[side=left]:slide-in-from-right-1 data-[side=right]:slide-in-from-left-1 z-50 overflow-hidden rounded-md border border-slate-100 bg-white px-3 py-1.5 text-sm text-slate-700 shadow-md dark:border-slate-800 dark:bg-slate-800 dark:text-slate-400",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -14,10 +14,10 @@ export default function App({ Component, pageProps }: AppProps) {
|
||||
return (
|
||||
<>
|
||||
<style jsx global>{`
|
||||
:root {
|
||||
--font-sans: ${fontSans.style.fontFamily};
|
||||
}
|
||||
}`}</style>
|
||||
:root {
|
||||
--font-sans: ${fontSans.style.fontFamily};
|
||||
}
|
||||
`}</style>
|
||||
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
|
||||
<Component {...pageProps} />
|
||||
</ThemeProvider>
|
||||
|
||||
Reference in New Issue
Block a user