Feat/design system site (#26586)
* init site * add nav menu * add props table component * init all the examples • also includes moving a bunch of ./ui files to ./ui-patterns * update package • might need to remove some of these * Update rehype-component.ts * move nav * more changes * add new source information to contentlayer * fix copy buttons * add text-confirm. start new concept of "fragments" * move base path. add homepage. add theme selection * added colors * add form-item-layout * temporary fix code blocks in light mode. they currently don't switch theme to theme * start adding code themes * Update dark.json * update code block themes * fix animations * add cdmk search * export registry of icons in ./icons package * add comments * add icon copy stuff * Update icons.tsx * more docs * more docs. cleaned up colors and icons docs * Update theming.mdx * add more docs * update * add package * Update tailwind.config.js * update content * Update drawer-demo.tsx * Update aspect-ratio-demo.tsx * add new blocks * Update source-panel.tsx * Update source-panel.tsx * Update source-panel.tsx * add new source block * Update source-panel.tsx * Update _app.tsx * Update page.tsx * Delete layout-old.tsx * remove old themes * remove old ui registry * comment out nav items * Update package-lock.json * Update AssistantChatForm.tsx * move back * move again * update * Update index.tsx * Update package-lock.json * Update package-lock.json * Update package-lock.json * update package * udpate prettier * fix tag issues * Update package-lock.json * update to contentlayer2 * update package-lock.json * fix type errors * ignore spelling * Update avoid-typos.yml * Update avoid-typos.yml * Update package-lock.json * Use ui type definitions. * move tsconfig base stuff. fix content layer issue * Update package-lock.json * Update package-lock.json --------- Co-authored-by: Alaister Young <a@alaisteryoung.com> Co-authored-by: Ivan Vasilov <vasilov.ivan@gmail.com>
This commit is contained in:
committed by
GitHub
parent
cd43912a2f
commit
b8243e06d8
@@ -16,3 +16,4 @@ apps/docs/pages/guides/integrations/*.mdx
|
||||
apps/studio/public
|
||||
apps/**/.turbo
|
||||
apps/docs/CONTRIBUTING.md
|
||||
apps/design-system/__registry__
|
||||
3
apps/design-system/.eslintrc.json
Normal file
3
apps/design-system/.eslintrc.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": "next/core-web-vitals"
|
||||
}
|
||||
38
apps/design-system/.gitignore
vendored
Normal file
38
apps/design-system/.gitignore
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
.yarn/install-state.gz
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# local env files
|
||||
.env*.local
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
|
||||
.contentlayer
|
||||
36
apps/design-system/README.md
Normal file
36
apps/design-system/README.md
Normal file
@@ -0,0 +1,36 @@
|
||||
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
|
||||
|
||||
## Getting Started
|
||||
|
||||
First, run the development server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
# or
|
||||
yarn dev
|
||||
# or
|
||||
pnpm dev
|
||||
# or
|
||||
bun dev
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
|
||||
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
|
||||
|
||||
This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
|
||||
|
||||
## Learn More
|
||||
|
||||
To learn more about Next.js, take a look at the following resources:
|
||||
|
||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||
|
||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
|
||||
|
||||
## Deploy on Vercel
|
||||
|
||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
||||
|
||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
|
||||
1593
apps/design-system/__registry__/index.tsx
Normal file
1593
apps/design-system/__registry__/index.tsx
Normal file
File diff suppressed because it is too large
Load Diff
119
apps/design-system/app/(app)/docs/[[...slug]]/page.tsx
Normal file
119
apps/design-system/app/(app)/docs/[[...slug]]/page.tsx
Normal file
@@ -0,0 +1,119 @@
|
||||
import { Mdx } from '@/components/mdx-components'
|
||||
import { DocsPager } from '@/components/pager'
|
||||
import { SourcePanel } from '@/components/source-panel'
|
||||
import { DashboardTableOfContents } from '@/components/toc'
|
||||
import { siteConfig } from '@/config/site'
|
||||
import { getTableOfContents } from '@/lib/toc'
|
||||
import { absoluteUrl, cn } from '@/lib/utils'
|
||||
import '@/styles/code-block-variables.css'
|
||||
import '@/styles/mdx.css'
|
||||
import { allDocs } from 'contentlayer/generated'
|
||||
import { ChevronRight } from 'lucide-react'
|
||||
import type { Metadata } from 'next'
|
||||
import { notFound } from 'next/navigation'
|
||||
import Balancer from 'react-wrap-balancer'
|
||||
import { ScrollArea, Separator } from 'ui'
|
||||
|
||||
interface DocPageProps {
|
||||
params: {
|
||||
slug: string[]
|
||||
}
|
||||
}
|
||||
|
||||
async function getDocFromParams({ params }: DocPageProps) {
|
||||
const slug = params.slug?.join('/') || ''
|
||||
const doc = allDocs.find((doc) => doc.slugAsParams === slug)
|
||||
|
||||
if (!doc) {
|
||||
return null
|
||||
}
|
||||
|
||||
return doc
|
||||
}
|
||||
|
||||
export async function generateMetadata({ params }: DocPageProps): Promise<Metadata> {
|
||||
const doc = await getDocFromParams({ params })
|
||||
|
||||
if (!doc) {
|
||||
return {}
|
||||
}
|
||||
|
||||
return {
|
||||
title: doc.title,
|
||||
description: doc.description,
|
||||
openGraph: {
|
||||
title: doc.title,
|
||||
description: doc.description,
|
||||
type: 'article',
|
||||
url: absoluteUrl(doc.slug),
|
||||
images: [
|
||||
{
|
||||
url: siteConfig.ogImage,
|
||||
width: 1200,
|
||||
height: 630,
|
||||
alt: siteConfig.name,
|
||||
},
|
||||
],
|
||||
},
|
||||
twitter: {
|
||||
card: 'summary_large_image',
|
||||
title: doc.title,
|
||||
description: doc.description,
|
||||
images: [siteConfig.ogImage],
|
||||
creator: '@shadcn',
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export async function generateStaticParams(): Promise<DocPageProps['params'][]> {
|
||||
return allDocs.map((doc) => ({
|
||||
slug: doc.slugAsParams.split('/'),
|
||||
}))
|
||||
}
|
||||
|
||||
export default async function DocPage({ params }: DocPageProps) {
|
||||
const doc = await getDocFromParams({ params })
|
||||
|
||||
if (!doc) {
|
||||
notFound()
|
||||
}
|
||||
|
||||
const toc = await getTableOfContents(doc.body.raw)
|
||||
|
||||
return (
|
||||
<main className="relative lg:gap-10 xl:grid xl:grid-cols-[1fr_200px] pr-6 lg:py-8">
|
||||
<div className="mx-auto w-full min-w-0 max-w-4xl">
|
||||
<div className="mb-4 flex items-center space-x-1 text-sm text-foreground-muted">
|
||||
<div className="overflow-hidden text-ellipsis whitespace-nowrap">Docs</div>
|
||||
<ChevronRight className="h-4 w-4 text-foreground-muted" />
|
||||
<div className="text-foreground-lighter">{doc.title}</div>
|
||||
</div>
|
||||
<div className="space-y-2 mb-5">
|
||||
<h1 className={cn('scroll-m-20 text-4xl tracking-tight')}>{doc.title}</h1>
|
||||
{doc.description && (
|
||||
<p className="text-lg text-foreground-light">
|
||||
<Balancer>{doc.description}</Balancer>
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<Separator className="mb-6" />
|
||||
<SourcePanel doc={doc} />
|
||||
<div className="pb-12">
|
||||
<Mdx code={doc.body.code} />
|
||||
</div>
|
||||
<DocsPager doc={doc} />
|
||||
</div>
|
||||
{doc.toc && (
|
||||
<div className="hidden text-sm xl:block">
|
||||
<div className="sticky top-16 -mt-10 pt-4">
|
||||
<ScrollArea className="pb-10">
|
||||
<div className="sticky top-16 -mt-10 h-[calc(100vh-3.5rem)] py-12">
|
||||
<DashboardTableOfContents toc={toc} />
|
||||
</div>
|
||||
</ScrollArea>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</main>
|
||||
)
|
||||
}
|
||||
35
apps/design-system/app/(app)/layout.tsx
Normal file
35
apps/design-system/app/(app)/layout.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import SideNavigation from '@/components/side-navigation'
|
||||
import { SiteFooter } from '@/components/site-footer'
|
||||
// import ThemeSettings from '@/components/theme-settings'
|
||||
import TopNavigation from '@/components/top-navigation'
|
||||
import { ScrollArea } from 'ui'
|
||||
|
||||
interface AppLayoutProps {
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
export default function AppLayout({ children }: AppLayoutProps) {
|
||||
return (
|
||||
<>
|
||||
<TopNavigation />
|
||||
{/* main container */}
|
||||
<div className="">
|
||||
{/* main content */}
|
||||
<main className="flex-1 max-w-site mx-auto w-full border-l border-r">
|
||||
{/* {children} */}
|
||||
<div className="border-b">
|
||||
<div className="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-10 z-30 hidden h-[calc(100vh-3rem)] w-full shrink-0 md:sticky md:block border-r">
|
||||
<ScrollArea className="h-full py-6 lg:py-8">
|
||||
<SideNavigation />
|
||||
</ScrollArea>
|
||||
</aside>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
<SiteFooter />
|
||||
</>
|
||||
)
|
||||
}
|
||||
113
apps/design-system/app/(app)/page.tsx
Normal file
113
apps/design-system/app/(app)/page.tsx
Normal file
@@ -0,0 +1,113 @@
|
||||
import { DesignSystemMarks } from '@/components/design-system-marks'
|
||||
import { HomepageSvgHandler } from '@/components/homepage-svg-handler'
|
||||
import Image from 'next/image'
|
||||
import { Separator } from 'ui'
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<main className="relative lg:gap-10 pr-6 lg:py-20">
|
||||
<div className="mx-auto w-full min-w-0 max-w-6xl flex flex-col gap-20">
|
||||
<div className="flex flex-col gap-8 justify-start">
|
||||
{/* <div>
|
||||
<DesignSystemMarks />
|
||||
</div> */}
|
||||
<div>
|
||||
<h1 className="text-4xl text-foreground mb-3">Supabase Design System</h1>
|
||||
<h2 className="text-xl text-foreground-light font-light">
|
||||
Design resources for building consistent user experiences
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Homepage items */}
|
||||
|
||||
<div className="grid grid-cols-2 gap-10">
|
||||
<div className="px-10 py-8 min-h-[18rem] flex flex-col justify-between bg-surface-75/50 hover:bg-overlay/50 hover:border-foreground-muted cursor-pointer transition-all border rounded-md">
|
||||
<HomepageSvgHandler name="atoms-illustration" />
|
||||
<div>
|
||||
<h3 className="font-medium text-foreground">Atom components</h3>
|
||||
<p className="text-sm text-foreground-light">Building blocks of User interfaces</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-10 py-8 min-h-[18rem] flex flex-col justify-between bg-surface-75/50 hover:bg-overlay/50 hover:border-foreground-muted cursor-pointer transition-all border rounded-md">
|
||||
<HomepageSvgHandler name="fragments-illustration" />
|
||||
<div>
|
||||
<h3 className="font-medium text-foreground">Fragment components</h3>
|
||||
<p className="text-sm text-foreground-light">Components assembled from Atoms</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="px-10 py-8 min-h-[18rem] flex flex-col justify-between bg-surface-75/50 hover:bg-overlay/50 hover:border-foreground-muted cursor-pointer transition-all border rounded-md">
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="flex flex-row gap-0.5">
|
||||
<div className="w-3 h-16 rounded border bg-foreground"></div>
|
||||
<div className="w-3 h-16 rounded border bg-foreground-light"></div>
|
||||
<div className="w-3 h-16 rounded border bg-foreground-lighter"></div>
|
||||
<div className="w-3 h-16 rounded border bg-foreground-muted"></div>
|
||||
<div className="w-3 h-16 rounded border"></div>
|
||||
|
||||
<div className="w-3 h-16 rounded border bg"></div>
|
||||
<div className="w-3 h-16 rounded border bg-200"></div>
|
||||
<div className="w-3 h-16 rounded border bg-surface-75"></div>
|
||||
<div className="w-3 h-16 rounded border bg-surface-100"></div>
|
||||
<div className="w-3 h-16 rounded border bg-surface-200"></div>
|
||||
<div className="w-3 h-16 rounded border bg-surface-300"></div>
|
||||
<div className="w-3 h-16 rounded border"></div>
|
||||
|
||||
<div className="w-3 h-16 rounded border bg-brand-200"></div>
|
||||
<div className="w-3 h-16 rounded border bg-brand-300"></div>
|
||||
<div className="w-3 h-16 rounded border bg-brand-400"></div>
|
||||
<div className="w-3 h-16 rounded border bg-brand-500"></div>
|
||||
<div className="w-3 h-16 rounded border bg-brand"></div>
|
||||
<div className="w-3 h-16 rounded border bg-brand-600"></div>
|
||||
<div className="w-3 h-16 rounded border"></div>
|
||||
|
||||
<div className="w-3 h-16 rounded border bg-warning-200"></div>
|
||||
<div className="w-3 h-16 rounded border bg-warning-300"></div>
|
||||
<div className="w-3 h-16 rounded border bg-warning-400"></div>
|
||||
<div className="w-3 h-16 rounded border bg-warning-500"></div>
|
||||
<div className="w-3 h-16 rounded border bg-warning"></div>
|
||||
<div className="w-3 h-16 rounded border bg-warning-600"></div>
|
||||
<div className="w-3 h-16 rounded border"></div>
|
||||
|
||||
<div className="w-3 h-16 rounded border bg-destructive-200"></div>
|
||||
<div className="w-3 h-16 rounded border bg-destructive-300"></div>
|
||||
<div className="w-3 h-16 rounded border bg-destructive-400"></div>
|
||||
<div className="w-3 h-16 rounded border bg-destructive-500"></div>
|
||||
<div className="w-3 h-16 rounded border bg-destructive"></div>
|
||||
<div className="w-3 h-16 rounded border bg-destructive-600"></div>
|
||||
<div className="w-3 h-16 rounded border"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-medium text-foreground">Colors</h3>
|
||||
<p className="text-sm text-foreground-light">Building blocks of User interfaces</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-10 py-8 min-h-[18rem] flex flex-col justify-between bg-surface-75/50 hover:bg-overlay/50 hover:border-foreground-muted cursor-pointer transition-all border rounded-md">
|
||||
<HomepageSvgHandler name="fragments-illustration" />
|
||||
<div>
|
||||
<h3 className="font-medium text-foreground">Theming</h3>
|
||||
<p className="text-sm text-foreground-light">Components assembled from Atoms</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="px-10 py-8 min-h-[18rem] flex flex-col justify-between bg-surface-75/50 hover:bg-overlay/50 hover:border-foreground-muted cursor-pointer transition-all border rounded-md">
|
||||
<HomepageSvgHandler name="atoms-illustration" />
|
||||
<div>
|
||||
<h3 className="font-medium text-foreground">Background / Surfaces</h3>
|
||||
<p className="text-sm text-foreground-light">Building blocks of User interfaces</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-10 py-8 min-h-[18rem] flex flex-col justify-between bg-surface-75/50 hover:bg-overlay/50 hover:border-foreground-muted cursor-pointer transition-all border rounded-md">
|
||||
<HomepageSvgHandler name="fragments-illustration" />
|
||||
<div>
|
||||
<h3 className="font-medium text-foreground">Icons</h3>
|
||||
<p className="text-sm text-foreground-light">Components assembled from Atoms</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
18
apps/design-system/app/Providers.tsx
Normal file
18
apps/design-system/app/Providers.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
'use client'
|
||||
|
||||
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_Shadcn_ } from 'ui'
|
||||
|
||||
export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
|
||||
return (
|
||||
<JotaiProvider>
|
||||
<NextThemesProvider {...props}>
|
||||
<TooltipProvider_Shadcn_ delayDuration={0}>{children}</TooltipProvider_Shadcn_>
|
||||
</NextThemesProvider>
|
||||
</JotaiProvider>
|
||||
)
|
||||
}
|
||||
BIN
apps/design-system/app/favicon.ico
Normal file
BIN
apps/design-system/app/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
29
apps/design-system/app/globals.css
Normal file
29
apps/design-system/app/globals.css
Normal file
@@ -0,0 +1,29 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
/* :root {
|
||||
--foreground-rgb: 0, 0, 0;
|
||||
--background-start-rgb: 214, 219, 220;
|
||||
--background-end-rgb: 255, 255, 255;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--foreground-rgb: 255, 255, 255;
|
||||
--background-start-rgb: 0, 0, 0;
|
||||
--background-end-rgb: 0, 0, 0;
|
||||
}
|
||||
} */
|
||||
|
||||
body {
|
||||
/* color: rgb(var(--foreground-rgb));
|
||||
background: linear-gradient(to bottom, transparent, rgb(var(--background-end-rgb)))
|
||||
rgb(var(--background-start-rgb)); */
|
||||
}
|
||||
/*
|
||||
@layer utilities {
|
||||
.text-balance {
|
||||
text-wrap: balance;
|
||||
}
|
||||
} */
|
||||
38
apps/design-system/app/layout.tsx
Normal file
38
apps/design-system/app/layout.tsx
Normal file
@@ -0,0 +1,38 @@
|
||||
import '@/styles/globals.css'
|
||||
import type { Metadata } from 'next'
|
||||
import { Inter } from 'next/font/google'
|
||||
import { ThemeProvider } from './Providers'
|
||||
|
||||
const inter = Inter({ subsets: ['latin'] })
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Create Next App',
|
||||
description: 'Generated by create next app',
|
||||
}
|
||||
|
||||
interface RootLayoutProps {
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
export default async function Layout({ children }: RootLayoutProps) {
|
||||
// console.log('Root Layout mounted or re-rendered')
|
||||
|
||||
return (
|
||||
<html
|
||||
// className="dark"
|
||||
lang="en"
|
||||
// data-theme="dark"
|
||||
// style={{ colorScheme: 'dark' }}
|
||||
suppressHydrationWarning
|
||||
>
|
||||
<head />
|
||||
<body className={inter.className}>
|
||||
<ThemeProvider themes={['dark', 'light', 'deep-dark']} defaultTheme="system" enableSystem>
|
||||
<div vaul-drawer-wrapper="">
|
||||
<div className="relative flex min-h-screen flex-col bg-background">{children}</div>
|
||||
</div>
|
||||
</ThemeProvider>
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
}
|
||||
17
apps/design-system/components/callout.tsx
Normal file
17
apps/design-system/components/callout.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import { Alert_Shadcn_, AlertDescription_Shadcn_, AlertTitle_Shadcn_ } from 'ui'
|
||||
|
||||
interface CalloutProps {
|
||||
icon?: string
|
||||
title?: string
|
||||
children?: React.ReactNode
|
||||
}
|
||||
|
||||
export function Callout({ title, children, icon, ...props }: CalloutProps) {
|
||||
return (
|
||||
<Alert_Shadcn_ {...props}>
|
||||
{icon && <span className="mr-4 text-2xl">{icon}</span>}
|
||||
{title && <AlertTitle_Shadcn_>{title}</AlertTitle_Shadcn_>}
|
||||
<AlertDescription_Shadcn_>{children}</AlertDescription_Shadcn_>
|
||||
</Alert_Shadcn_>
|
||||
)
|
||||
}
|
||||
18
apps/design-system/components/class-label.tsx
Normal file
18
apps/design-system/components/class-label.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import * as React from 'react'
|
||||
|
||||
const ClassLabel = React.forwardRef<HTMLSpanElement, { children: React.ReactNode }>(
|
||||
({ children }, ref) => {
|
||||
return (
|
||||
<span
|
||||
ref={ref}
|
||||
className="bg-surface-100 rounded-full border px-2 font-mono text-xs text-foreground-lighter group-data-[state=open]:text-foreground"
|
||||
>
|
||||
{children}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
ClassLabel.displayName = 'ClassLabel'
|
||||
|
||||
export { ClassLabel }
|
||||
16
apps/design-system/components/click-counter.tsx
Normal file
16
apps/design-system/components/click-counter.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
|
||||
export function ClickCounter() {
|
||||
const [count, setCount] = useState(0)
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={() => setCount(count + 1)}
|
||||
className="whitespace-nowrap rounded-lg bg-gray-700 px-3 py-1 text-sm font-medium tabular-nums text-gray-100 hover:bg-gray-500 hover:text-white"
|
||||
>
|
||||
{count} Clicks
|
||||
</button>
|
||||
)
|
||||
}
|
||||
53
apps/design-system/components/code-block-wrapper.tsx
Normal file
53
apps/design-system/components/code-block-wrapper.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
'use client'
|
||||
|
||||
import * as React from 'react'
|
||||
|
||||
import { cn } from 'ui'
|
||||
import { Button } from 'ui'
|
||||
import {
|
||||
Collapsible_Shadcn_ as Collapsible,
|
||||
CollapsibleContent_Shadcn_ as CollapsibleContent,
|
||||
CollapsibleTrigger_Shadcn_ as CollapsibleTrigger,
|
||||
} from 'ui'
|
||||
|
||||
interface CodeBlockProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
expandButtonTitle?: string
|
||||
}
|
||||
|
||||
export function CodeBlockWrapper({
|
||||
expandButtonTitle = 'View Code',
|
||||
className,
|
||||
children,
|
||||
...props
|
||||
}: CodeBlockProps) {
|
||||
const [isOpened, setIsOpened] = React.useState(false)
|
||||
|
||||
return (
|
||||
<Collapsible open={isOpened} onOpenChange={setIsOpened}>
|
||||
<div className={cn('relative overflow-hidden', className)} {...props}>
|
||||
<CollapsibleContent forceMount className={cn('overflow-hidden', !isOpened && 'max-h-32')}>
|
||||
<div
|
||||
className={cn(
|
||||
'[&_pre]:my-0 [&_pre]:max-h-[650px] [&_pre]:pb-[100px]',
|
||||
!isOpened ? '[&_pre]:overflow-hidden' : '[&_pre]:overflow-auto]'
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</CollapsibleContent>
|
||||
<div
|
||||
className={cn(
|
||||
'absolute flex items-center justify-center bg-gradient-to-b from-zinc-700/30 to-zinc-950/90 p-2',
|
||||
isOpened ? 'inset-x-0 bottom-0 h-12' : 'inset-0'
|
||||
)}
|
||||
>
|
||||
<CollapsibleTrigger asChild>
|
||||
<Button type="secondary" className="h-8 text-xs">
|
||||
{isOpened ? 'Collapse' : expandButtonTitle}
|
||||
</Button>
|
||||
</CollapsibleTrigger>
|
||||
</div>
|
||||
</div>
|
||||
</Collapsible>
|
||||
)
|
||||
}
|
||||
107
apps/design-system/components/code-fragment.tsx
Normal file
107
apps/design-system/components/code-fragment.tsx
Normal file
@@ -0,0 +1,107 @@
|
||||
'use client'
|
||||
|
||||
import { Index } from '@/__registry__'
|
||||
import * as React from 'react'
|
||||
|
||||
import { useConfig } from '@/hooks/use-config'
|
||||
import { cn } from 'ui'
|
||||
|
||||
import { styles } from '@/registry/styles'
|
||||
|
||||
interface ComponentPreviewProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
name: string
|
||||
extractClassname?: boolean
|
||||
extractedClassNames?: string
|
||||
align?: 'center' | 'start' | 'end'
|
||||
peekCode?: boolean
|
||||
showGrid?: boolean
|
||||
showDottedGrid?: boolean
|
||||
wide?: boolean
|
||||
}
|
||||
|
||||
export function CodeFragment({
|
||||
name,
|
||||
children,
|
||||
className,
|
||||
extractClassname,
|
||||
extractedClassNames,
|
||||
align = 'center',
|
||||
peekCode = false,
|
||||
showGrid = false,
|
||||
showDottedGrid = true,
|
||||
wide = false,
|
||||
...props
|
||||
}: ComponentPreviewProps) {
|
||||
const [config] = useConfig()
|
||||
const index = styles.findIndex((style) => style.name === config.style)
|
||||
|
||||
const Codes = React.Children.toArray(children) as React.ReactElement[]
|
||||
const Code = Codes[index]
|
||||
|
||||
const [expand, setExpandState] = React.useState(false)
|
||||
|
||||
const Preview = React.useMemo(() => {
|
||||
// console.log('Index', Index)
|
||||
// console.log('name', name)
|
||||
// console.log('config.style', config.style)
|
||||
|
||||
const Component = Index[config.style][name]?.component
|
||||
// const Component = Index[name]?.component
|
||||
|
||||
if (!Component) {
|
||||
return (
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Code fragment{' '}
|
||||
<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])
|
||||
|
||||
const ComponentPreview = React.useMemo(() => {
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={cn('preview flex min-h-[350px] w-full justify-center p-10', {
|
||||
'items-center': align === 'center',
|
||||
'items-start': align === 'start',
|
||||
'items-end': align === 'end',
|
||||
})}
|
||||
>
|
||||
<React.Suspense
|
||||
fallback={
|
||||
<div className="flex items-center text-sm text-muted-foreground">Loading...</div>
|
||||
}
|
||||
>
|
||||
{Preview}
|
||||
</React.Suspense>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}, [Preview, align])
|
||||
|
||||
const wideClasses = wide ? '2xl:-ml-12 2xl:-mr-12' : ''
|
||||
|
||||
return (
|
||||
<div className={cn('mt-4 mb-12', wideClasses)}>
|
||||
<div
|
||||
className={cn(
|
||||
'relative rounded-md border-t border-l border-r border-b bg-studio overflow-hidden'
|
||||
)}
|
||||
>
|
||||
{showGrid && (
|
||||
<div className="pointer-events-none absolute h-full w-full bg-[linear-gradient(to_right,hsla(var(--foreground-default)/0.02)_1px,transparent_1px),linear-gradient(to_bottom,#80808012_1px,transparent_1px)] bg-[size:24px_24px]"></div>
|
||||
)}
|
||||
{showDottedGrid && (
|
||||
<div className="z-0 pointer-events-none absolute h-full w-full bg-[radial-gradient(hsla(var(--foreground-default)/0.02)_1px,transparent_1px)] [background-size:16px_16px] [mask-image:radial-gradient(ellipse_50%_50%_at_50%_50%,#000_70%,transparent_100%)]"></div>
|
||||
)}
|
||||
<div className="z-10 relative">{ComponentPreview}</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
155
apps/design-system/components/colors.tsx
Normal file
155
apps/design-system/components/colors.tsx
Normal file
@@ -0,0 +1,155 @@
|
||||
import { cn } from 'ui/src/lib/utils/cn'
|
||||
// import { Card } from '@ui/components/shadcn/ui/card'
|
||||
import color from 'ui/src/lib/tailwind-demo-classes'
|
||||
import { Grid, GridItem } from './grid'
|
||||
|
||||
const Colors = ({
|
||||
definition,
|
||||
}: {
|
||||
definition: 'background' | 'border' | 'text' | 'colors' | 'palletes'
|
||||
}) => {
|
||||
const Example = ({ x }: { x: string }) => {
|
||||
switch (definition) {
|
||||
case 'background':
|
||||
return (
|
||||
<div className={cn(x, 'relative w-full h-12 border border-overlay rounded-full')}></div>
|
||||
)
|
||||
break
|
||||
|
||||
case 'border':
|
||||
return <div className={cn(x, 'relative w-full h-12 border-4 rounded-full')}></div>
|
||||
break
|
||||
|
||||
case 'text':
|
||||
return (
|
||||
<span className={cn(x, 'relative w-full h-12 flex items-center justify-center')}>
|
||||
Postgres
|
||||
</span>
|
||||
)
|
||||
break
|
||||
|
||||
case 'colors':
|
||||
return (
|
||||
<div className={cn(x, 'relative w-full h-12 border border-overlay rounded-full')}></div>
|
||||
)
|
||||
break
|
||||
|
||||
case 'palletes':
|
||||
return (
|
||||
<div className={cn(x, 'relative w-full h-12 border border-overlay rounded-full')}></div>
|
||||
)
|
||||
break
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Grid>
|
||||
{color[definition].map((x: string, i) => {
|
||||
return (
|
||||
<GridItem
|
||||
key={i}
|
||||
className={cn(x.includes('contrast') && 'bg-foreground hover:bg-foreground-light')}
|
||||
>
|
||||
<Example x={x} />
|
||||
<span className="bg-surface-100 rounded-full border px-2 font-mono text-xs text-foreground-lighter group-data-[state=open]:text-foreground">
|
||||
{x}
|
||||
</span>
|
||||
</GridItem>
|
||||
)
|
||||
})}
|
||||
</Grid>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
/* <div className="flex flex-col gap-12 py-20">
|
||||
<div className="flex flex-row gap-12">
|
||||
<div>
|
||||
<h5 className="mb-3">Background</h5>
|
||||
<div className="flex flex-col gap-3">
|
||||
{color.background.map((x: string, i) => {
|
||||
return (
|
||||
<div className="flex gap-3 items-center" key={i}>
|
||||
<div className={cn(x, 'relative w-12 h-12 border border-overlay shadow')}></div>
|
||||
<div className="font-mono text-sm bg-surface-100 px-2 py-0.5 rounded-full">
|
||||
{x}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h5 className="mb-3">Border</h5>
|
||||
<div className="flex flex-col gap-3">
|
||||
{color.border.map((x, i) => {
|
||||
return (
|
||||
<div className="flex gap-3 items-center" key={i}>
|
||||
<div className={cn(x, 'relative w-12 h-12 border-4')}></div>
|
||||
<div className="font-mono text-sm bg-surface-100 px-2 py-0.5 rounded-full">
|
||||
{x}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h5 className="mb-3">texts</h5>
|
||||
<div className="flex flex-col gap-3">
|
||||
{color.text.map((x, i) => {
|
||||
return (
|
||||
<div className="flex gap-3 items-center" key={i}>
|
||||
<span className={cn(x, 'relative w-12 h-12 flex items-center justify-center')}>
|
||||
###
|
||||
</span>
|
||||
<div className="font-mono text-sm bg-surface-100 px-2 py-0.5 rounded-full">
|
||||
{x}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-row gap-12">
|
||||
<div>
|
||||
<h5 className="mb-3">Colors</h5>
|
||||
<div className="flex flex-col gap-3">
|
||||
{color.colors.map((x: string, i) => {
|
||||
return (
|
||||
<div className="flex gap-3 items-center" key={i}>
|
||||
<div className={cn(x, 'relative w-12 h-12 border border-overlay shadow')}></div>
|
||||
<div className="font-mono text-sm bg-surface-100 px-2 py-0.5 rounded-full">
|
||||
{x}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h5 className="mb-3">Palletes</h5>
|
||||
<div className="flex flex-col gap-3">
|
||||
{color.palletes.map((x, i) => {
|
||||
return (
|
||||
<div className="flex gap-3 items-center" key={i}>
|
||||
<div className={cn(x, 'relative w-12 h-12 border border-overlay shadow')}></div>
|
||||
<div className="font-mono text-sm bg-surface-100 px-2 py-0.5 rounded-full">
|
||||
{x}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div> */
|
||||
}
|
||||
|
||||
export { Colors }
|
||||
132
apps/design-system/components/command-menu.tsx
Normal file
132
apps/design-system/components/command-menu.tsx
Normal file
@@ -0,0 +1,132 @@
|
||||
'use client'
|
||||
|
||||
import * as React from 'react'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { DialogProps } from '@radix-ui/react-alert-dialog'
|
||||
import { CircleIcon, FileIcon, LaptopIcon, MoonIcon, SunIcon } from 'lucide-react'
|
||||
import { useTheme } from 'next-themes'
|
||||
|
||||
import { docsConfig } from '@/config/docs'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { Button } from 'ui'
|
||||
import {
|
||||
CommandDialog,
|
||||
CommandEmpty_Shadcn_,
|
||||
CommandGroup_Shadcn_,
|
||||
CommandInput_Shadcn_,
|
||||
CommandItem_Shadcn_,
|
||||
CommandList_Shadcn_,
|
||||
CommandSeparator_Shadcn_,
|
||||
} from 'ui'
|
||||
|
||||
export function CommandMenu({ ...props }: DialogProps) {
|
||||
const router = useRouter()
|
||||
const [open, setOpen] = React.useState(false)
|
||||
const { setTheme } = useTheme()
|
||||
|
||||
React.useEffect(() => {
|
||||
const down = (e: KeyboardEvent) => {
|
||||
if ((e.key === 'k' && (e.metaKey || e.ctrlKey)) || e.key === '/') {
|
||||
if (
|
||||
(e.target instanceof HTMLElement && e.target.isContentEditable) ||
|
||||
e.target instanceof HTMLInputElement ||
|
||||
e.target instanceof HTMLTextAreaElement ||
|
||||
e.target instanceof HTMLSelectElement
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
e.preventDefault()
|
||||
setOpen((open) => !open)
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('keydown', down)
|
||||
return () => document.removeEventListener('keydown', down)
|
||||
}, [])
|
||||
|
||||
const runCommand = React.useCallback((command: () => unknown) => {
|
||||
setOpen(false)
|
||||
command()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
type="outline"
|
||||
className={cn(
|
||||
`relative h-8 w-full justify-start rounded-[0.5rem] bg-background text-sm font-normal text-foreground-muted shadow-none sm:pr-12 md:w-40 lg:w-64
|
||||
hover:border-foreground-muted hover:bg-surface-100 hover:text-foreground-lighter
|
||||
`
|
||||
)}
|
||||
onClick={() => setOpen(true)}
|
||||
{...props}
|
||||
>
|
||||
<span className="hidden lg:inline-flex">Search Design System...</span>
|
||||
<span className="inline-flex lg:hidden">Search...</span>
|
||||
<kbd className="pointer-events-none absolute right-[0.3rem] top-[0.3rem] hidden h-5 select-none items-center gap-1 rounded border bg-surface-200 px-1.5 font-mono text-[10px] font-medium opacity-100 sm:flex text-foreground-light">
|
||||
<span className="text-sm">⌘</span>K
|
||||
</kbd>
|
||||
</Button>
|
||||
<CommandDialog open={open} onOpenChange={setOpen}>
|
||||
<CommandInput_Shadcn_ placeholder="Type a command or search..." />
|
||||
<CommandList_Shadcn_>
|
||||
<CommandEmpty_Shadcn_>No results found.</CommandEmpty_Shadcn_>
|
||||
{/* <CommandGroup_Shadcn_ heading="Links">
|
||||
{docsConfig.mainNav
|
||||
.filter((navitem) => !navitem.external)
|
||||
.map((navItem) => (
|
||||
<CommandItem_Shadcn_
|
||||
key={navItem.href}
|
||||
value={navItem.title}
|
||||
onSelect={() => {
|
||||
runCommand(() => router.push(navItem.href as string))
|
||||
}}
|
||||
>
|
||||
<FileIcon className="mr-2 h-4 w-4" strokeWidth={1} />
|
||||
{navItem.title}
|
||||
</CommandItem_Shadcn_>
|
||||
))}
|
||||
</CommandGroup_Shadcn_> */}
|
||||
{docsConfig.sidebarNav.map((group) => (
|
||||
<CommandGroup_Shadcn_ key={group.title} heading={group.title}>
|
||||
{group.items.map((navItem) => (
|
||||
<CommandItem_Shadcn_
|
||||
key={navItem.href}
|
||||
value={navItem.title}
|
||||
onSelect={() => {
|
||||
runCommand(() => router.push(navItem.href as string))
|
||||
}}
|
||||
>
|
||||
<div className="mr-2 flex h-4 w-4 items-center justify-center">
|
||||
<CircleIcon className="h-3 w-3" strokeWidth={1} />
|
||||
</div>
|
||||
{navItem.title}
|
||||
</CommandItem_Shadcn_>
|
||||
))}
|
||||
</CommandGroup_Shadcn_>
|
||||
))}
|
||||
<CommandSeparator_Shadcn_ />
|
||||
<CommandGroup_Shadcn_ heading="Theme">
|
||||
<CommandItem_Shadcn_ onSelect={() => runCommand(() => setTheme('light'))}>
|
||||
<SunIcon className="mr-2 h-4 w-4" strokeWidth={1} />
|
||||
Light
|
||||
</CommandItem_Shadcn_>
|
||||
<CommandItem_Shadcn_ onSelect={() => runCommand(() => setTheme('dark'))}>
|
||||
<MoonIcon className="mr-2 h-4 w-4" strokeWidth={1} />
|
||||
Dark
|
||||
</CommandItem_Shadcn_>
|
||||
<CommandItem_Shadcn_ onSelect={() => runCommand(() => setTheme('deep-dark'))}>
|
||||
<MoonIcon className="mr-2 h-4 w-4" strokeWidth={1} />
|
||||
Deep Dark
|
||||
</CommandItem_Shadcn_>
|
||||
<CommandItem_Shadcn_ onSelect={() => runCommand(() => setTheme('system'))}>
|
||||
<LaptopIcon className="mr-2 h-4 w-4" strokeWidth={1} />
|
||||
System
|
||||
</CommandItem_Shadcn_>
|
||||
</CommandGroup_Shadcn_>
|
||||
</CommandList_Shadcn_>
|
||||
</CommandDialog>
|
||||
</>
|
||||
)
|
||||
}
|
||||
97
apps/design-system/components/component-example.tsx
Normal file
97
apps/design-system/components/component-example.tsx
Normal file
@@ -0,0 +1,97 @@
|
||||
'use client'
|
||||
|
||||
import * as React from 'react'
|
||||
|
||||
import { cn } from 'ui'
|
||||
import {
|
||||
// CopyButton,
|
||||
CopyWithClassNames,
|
||||
} from '@/components/copy-button'
|
||||
import {
|
||||
Tabs_Shadcn_ as Tabs,
|
||||
TabsContent_Shadcn_ as TabsContent,
|
||||
TabsList_Shadcn_ as TabsList,
|
||||
TabsTrigger_Shadcn_ as TabsTrigger,
|
||||
} from 'ui'
|
||||
|
||||
interface ComponentExampleProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
extractClassname?: boolean
|
||||
extractedClassNames?: string
|
||||
align?: 'center' | 'start' | 'end'
|
||||
src?: string
|
||||
}
|
||||
|
||||
export function ComponentExample({
|
||||
children,
|
||||
className,
|
||||
extractClassname,
|
||||
extractedClassNames,
|
||||
align = 'center',
|
||||
src: _,
|
||||
...props
|
||||
}: ComponentExampleProps) {
|
||||
const [Example, Code, ...Children] = React.Children.toArray(children) as React.ReactElement[]
|
||||
|
||||
const codeString = React.useMemo(() => {
|
||||
if (typeof Code?.props['data-rehype-pretty-code-fragment'] !== 'undefined') {
|
||||
const [, Button] = React.Children.toArray(Code.props.children) as React.ReactElement[]
|
||||
return Button?.props?.value || Button?.props?.__rawString__ || null
|
||||
}
|
||||
}, [Code])
|
||||
|
||||
return (
|
||||
<div className={cn('group relative my-4 flex flex-col gap-2', className)} {...props}>
|
||||
<Tabs defaultValue="preview" className="relative mr-auto w-full">
|
||||
<div className="flex items-center justify-between pb-3">
|
||||
<TabsList className="w-full justify-start rounded-none border-b bg-transparent p-0">
|
||||
<TabsTrigger
|
||||
value="preview"
|
||||
className="relative rounded-none border-b-2 border-b-transparent bg-transparent px-4 pb-3 pt-2 font-semibold text-muted-foreground shadow-none transition-none data-[state=active]:border-b-primary data-[state=active]:text-foreground data-[state=active]:shadow-none"
|
||||
>
|
||||
Preview
|
||||
</TabsTrigger>
|
||||
<TabsTrigger
|
||||
value="code"
|
||||
className="relative rounded-none border-b-2 border-b-transparent bg-transparent px-4 pb-3 pt-2 font-semibold text-muted-foreground shadow-none transition-none data-[state=active]:border-b-primary data-[state=active]:text-foreground data-[state=active]:shadow-none"
|
||||
>
|
||||
Code
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
{
|
||||
extractedClassNames ? (
|
||||
<CopyWithClassNames
|
||||
value={codeString}
|
||||
classNames={extractedClassNames}
|
||||
className="absolute right-4 top-20"
|
||||
/>
|
||||
) : undefined
|
||||
// codeString && <CopyButton value={codeString} className="absolute right-4 top-20" />
|
||||
}
|
||||
</div>
|
||||
<TabsContent value="preview" className="rounded-md border">
|
||||
<div
|
||||
className={cn('flex min-h-[350px] justify-center p-10', {
|
||||
'items-center': align === 'center',
|
||||
'items-start': align === 'start',
|
||||
'items-end': align === 'end',
|
||||
})}
|
||||
>
|
||||
{Example}
|
||||
</div>
|
||||
</TabsContent>
|
||||
<TabsContent value="code">
|
||||
<div className="flex flex-col space-y-4">
|
||||
<div className="w-full rounded-md [&_button]:hidden [&_pre]:my-0 [&_pre]:max-h-[350px] [&_pre]:overflow-auto">
|
||||
{Code}
|
||||
</div>
|
||||
{Children?.length ? (
|
||||
<div className="rounded-md [&_button]:hidden [&_pre]:my-0 [&_pre]:max-h-[350px] [&_pre]:overflow-auto">
|
||||
{Children}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
260
apps/design-system/components/component-preview.tsx
Normal file
260
apps/design-system/components/component-preview.tsx
Normal file
@@ -0,0 +1,260 @@
|
||||
'use client'
|
||||
|
||||
import * as React from 'react'
|
||||
import { Index } from '@/__registry__'
|
||||
|
||||
import {
|
||||
Button,
|
||||
CollapsibleContent_Shadcn_,
|
||||
CollapsibleTrigger_Shadcn_,
|
||||
Collapsible_Shadcn_,
|
||||
cn,
|
||||
} from 'ui'
|
||||
import { useConfig } from '@/hooks/use-config'
|
||||
import {
|
||||
// CopyButton,
|
||||
CopyWithClassNames,
|
||||
} from '@/components/copy-button'
|
||||
// import { Icons } from '@/components/icons'
|
||||
// import { StyleSwitcher } from '@/components/style-switcher'
|
||||
// import { ThemeWrapper } from '@/components/theme-wrapper'
|
||||
import {
|
||||
Tabs_Shadcn_ as Tabs,
|
||||
TabsContent_Shadcn_ as TabsContent,
|
||||
TabsList_Shadcn_ as TabsList,
|
||||
TabsTrigger_Shadcn_ as TabsTrigger,
|
||||
} from 'ui'
|
||||
|
||||
// import { LoaderCircle } from 'lucide-react'
|
||||
|
||||
import { styles } from '@/registry/styles'
|
||||
import { ChevronRight, Expand } from 'lucide-react'
|
||||
|
||||
interface ComponentPreviewProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
name: string
|
||||
extractClassname?: boolean
|
||||
extractedClassNames?: string
|
||||
align?: 'center' | 'start' | 'end'
|
||||
peekCode?: boolean
|
||||
showGrid?: boolean
|
||||
showDottedGrid?: boolean
|
||||
wide?: boolean
|
||||
}
|
||||
|
||||
export function ComponentPreview({
|
||||
name,
|
||||
children,
|
||||
className,
|
||||
extractClassname,
|
||||
extractedClassNames,
|
||||
align = 'center',
|
||||
peekCode = false,
|
||||
showGrid = false,
|
||||
showDottedGrid = true,
|
||||
wide = false,
|
||||
...props
|
||||
}: ComponentPreviewProps) {
|
||||
const [config] = useConfig()
|
||||
const index = styles.findIndex((style) => style.name === config.style)
|
||||
|
||||
const Codes = React.Children.toArray(children) as React.ReactElement[]
|
||||
const Code = Codes[index]
|
||||
|
||||
const [expand, setExpandState] = React.useState(false)
|
||||
|
||||
const Preview = React.useMemo(() => {
|
||||
// console.log('Index', Index)
|
||||
// console.log('name', name)
|
||||
// console.log('config.style', config.style)
|
||||
|
||||
const Component = Index[config.style][name]?.component
|
||||
// const Component = Index[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])
|
||||
|
||||
const codeString = React.useMemo(() => {
|
||||
if (typeof Code?.props['data-rehype-pretty-code-fragment'] !== 'undefined') {
|
||||
const [, Button] = React.Children.toArray(Code.props.children) as React.ReactElement[]
|
||||
return Button?.props?.value || Button?.props?.__rawString__ || null
|
||||
}
|
||||
}, [Code])
|
||||
|
||||
const ComponentPreview = React.useMemo(() => {
|
||||
return (
|
||||
<>
|
||||
{/* <ThemeWrapper defaultTheme="zinc"> */}
|
||||
<div
|
||||
className={cn('preview flex min-h-[350px] w-full justify-center p-10', {
|
||||
'items-center': align === 'center',
|
||||
'items-start': align === 'start',
|
||||
'items-end': align === 'end',
|
||||
})}
|
||||
>
|
||||
<React.Suspense
|
||||
fallback={
|
||||
<div className="flex items-center text-sm text-muted-foreground">
|
||||
{/* <Icons.spinner className="mr-2 h-4 w-4 animate-spin" /> */}
|
||||
{/* <LoaderCircle className="mr-2 h-4 w-4 animate-spin" /> */}
|
||||
Loading...
|
||||
</div>
|
||||
}
|
||||
>
|
||||
{Preview}
|
||||
</React.Suspense>
|
||||
</div>
|
||||
{/* </ThemeWrapper> */}
|
||||
</>
|
||||
)
|
||||
}, [Preview, align])
|
||||
|
||||
const wideClasses = wide ? '2xl:-ml-12 2xl:-mr-12' : ''
|
||||
|
||||
if (peekCode) {
|
||||
return (
|
||||
<div className={cn('mt-4 mb-12', wideClasses)}>
|
||||
<div
|
||||
className={cn(
|
||||
'relative rounded-tl-md rounded-tr-md border-t border-l border-r bg-studio overflow-hidden'
|
||||
)}
|
||||
>
|
||||
{showGrid && (
|
||||
<div className="pointer-events-none absolute h-full w-full bg-[linear-gradient(to_right,hsla(var(--foreground-default)/0.02)_1px,transparent_1px),linear-gradient(to_bottom,#80808012_1px,transparent_1px)] bg-[size:24px_24px]"></div>
|
||||
)}
|
||||
{showDottedGrid && (
|
||||
<div className="z-0 pointer-events-none absolute h-full w-full bg-[radial-gradient(hsla(var(--foreground-default)/0.02)_1px,transparent_1px)] [background-size:16px_16px] [mask-image:radial-gradient(ellipse_50%_50%_at_50%_50%,#000_70%,transparent_100%)]"></div>
|
||||
)}
|
||||
<div className="z-10 relative">{ComponentPreview}</div>
|
||||
{/* <div className="preview-grid-background"></div> */}
|
||||
</div>
|
||||
<div className="flex flex-col space-y-4">
|
||||
<div
|
||||
className={cn(
|
||||
'relative',
|
||||
'w-full rounded-md [&_pre]:my-0',
|
||||
expand
|
||||
? '[&_pre]:overflow-auto'
|
||||
: 'inset-0 [&_pre]:max-h-[196px] [&_pre]:overflow-hidden',
|
||||
'[&_pre]:rounded-tr-none [&_pre]:rounded-tl-none'
|
||||
)}
|
||||
>
|
||||
{Code}
|
||||
<div className="absolute bottom-0 w-full flex justify-center mb-4">
|
||||
<Button
|
||||
className="rounded-full"
|
||||
onClick={() => setExpandState(!expand)}
|
||||
type="default"
|
||||
icon={<Expand className="text-foreground-lighter" />}
|
||||
>
|
||||
{expand ? 'Collapse code' : 'Expand code'}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={cn('mt-4 mb-12', wideClasses)}>
|
||||
<div
|
||||
className={cn(
|
||||
'relative rounded-tl-md rounded-tr-md border-t border-l border-r bg-studio overflow-hidden'
|
||||
)}
|
||||
>
|
||||
{showGrid && (
|
||||
<div className="pointer-events-none absolute h-full w-full bg-[linear-gradient(to_right,hsla(var(--foreground-default)/0.02)_1px,transparent_1px),linear-gradient(to_bottom,#80808012_1px,transparent_1px)] bg-[size:24px_24px]"></div>
|
||||
)}
|
||||
{showDottedGrid && (
|
||||
<div className="z-0 pointer-events-none absolute h-full w-full bg-[radial-gradient(hsla(var(--foreground-default)/0.02)_1px,transparent_1px)] [background-size:16px_16px] [mask-image:radial-gradient(ellipse_50%_50%_at_50%_50%,#000_70%,transparent_100%)]"></div>
|
||||
)}
|
||||
<div className="z-10 relative">{ComponentPreview}</div>
|
||||
{/* <div className="preview-grid-background"></div> */}
|
||||
</div>
|
||||
<Collapsible_Shadcn_>
|
||||
<CollapsibleTrigger_Shadcn_
|
||||
className={`
|
||||
flex
|
||||
gap-3 items-center
|
||||
w-full
|
||||
font-mono
|
||||
text-xs
|
||||
text-foreground-light
|
||||
px-4 py-4
|
||||
border border-r
|
||||
group
|
||||
data-[state=closed]:rounded-bl-md data-[state=closed]:rounded-br-md
|
||||
|
||||
`}
|
||||
>
|
||||
<ChevronRight
|
||||
className="transition-all group-data-[state=open]:rotate-90 text-foreground-lighter"
|
||||
size={14}
|
||||
/>
|
||||
View code
|
||||
</CollapsibleTrigger_Shadcn_>
|
||||
<CollapsibleContent_Shadcn_ className="transition-all">
|
||||
<div
|
||||
className={cn(
|
||||
'relative',
|
||||
'w-full rounded-md [&_pre]:my-0',
|
||||
'[&_pre]:overflow-auto',
|
||||
'[&_pre]:max-h-[320px]',
|
||||
'[&_pre]:rounded-tr-none [&_pre]:rounded-tl-none [&_pre]:border-t-transparent'
|
||||
)}
|
||||
>
|
||||
{Code}
|
||||
</div>
|
||||
</CollapsibleContent_Shadcn_>
|
||||
</Collapsible_Shadcn_>
|
||||
</div>
|
||||
)
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn('group relative my-4 flex flex-col gap-2', wideClasses, className)}
|
||||
{...props}
|
||||
>
|
||||
<Tabs defaultValue="preview" className="relative mr-auto w-full">
|
||||
<div className="flex items-center justify-between pb-3">
|
||||
<TabsList className="w-full justify-start rounded-none border-b bg-transparent p-0">
|
||||
<TabsTrigger
|
||||
value="preview"
|
||||
className="relative h-9 rounded-none border-b-2 border-b-transparent bg-transparent px-4 pb-3 pt-2 font-semibold text-muted-foreground shadow-none transition-none data-[state=active]:border-b-primary data-[state=active]:text-foreground data-[state=active]:shadow-none"
|
||||
>
|
||||
Preview
|
||||
</TabsTrigger>
|
||||
<TabsTrigger
|
||||
value="code"
|
||||
className="relative h-9 rounded-none border-b-2 border-b-transparent bg-transparent px-4 pb-3 pt-2 font-semibold text-muted-foreground shadow-none transition-none data-[state=active]:border-b-primary data-[state=active]:text-foreground data-[state=active]:shadow-none"
|
||||
>
|
||||
Code
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
</div>
|
||||
<TabsContent value="preview" className="relative rounded-md border bg-studio">
|
||||
{ComponentPreview}
|
||||
</TabsContent>
|
||||
<TabsContent value="code">
|
||||
<div className="flex flex-col space-y-4">
|
||||
<div className="w-full rounded-md [&_pre]:my-0 [&_pre]:max-h-[350px] [&_pre]:overflow-auto">
|
||||
{Code}
|
||||
</div>
|
||||
</div>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
58
apps/design-system/components/component-props.tsx
Normal file
58
apps/design-system/components/component-props.tsx
Normal file
@@ -0,0 +1,58 @@
|
||||
import fs from 'fs'
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from 'ui'
|
||||
// import { parse } from 'react-docgen'
|
||||
|
||||
export function ComponentProps(props: any) {
|
||||
// map through all props types for this component DropdownMenu
|
||||
// return a table with the prop name, type, default value, and description
|
||||
|
||||
// const code = `
|
||||
// /** My first component */
|
||||
// export default ({ name }: { name: string }) => <div>{{name}}</div>;
|
||||
// `
|
||||
|
||||
// const documentation = parse(code)
|
||||
//
|
||||
// console.log(documentation)
|
||||
|
||||
// console.log('from the component props', JSON.parse(props.docs))
|
||||
|
||||
const docs = JSON.parse(props.docs)
|
||||
|
||||
// console.log(docs.props)
|
||||
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
<p className="font-medium text-foreground-light">{props.children}</p>
|
||||
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead className="font-mono uppercase text-xs font-normal">Prop Name</TableHead>
|
||||
<TableHead className="font-mono uppercase text-xs font-normal">Required</TableHead>
|
||||
<TableHead className="font-mono uppercase text-xs font-normal">Type</TableHead>
|
||||
<TableHead className="font-mono uppercase text-xs text-right font-normal">
|
||||
Description
|
||||
</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{Object.entries(docs.props).map(([propName, prop], index) => (
|
||||
<TableRow key={index}>
|
||||
<TableCell>{propName}</TableCell>
|
||||
{/*
|
||||
// @ts-ignore */}
|
||||
<TableCell>{prop.required ? 'Yes' : 'No'}</TableCell>
|
||||
{/*
|
||||
// @ts-ignore */}
|
||||
<TableCell>{prop.flowType.name}</TableCell>
|
||||
{/*
|
||||
// @ts-ignore */}
|
||||
<TableCell className="text-right">{prop.description}</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
21
apps/design-system/components/component-source.tsx
Normal file
21
apps/design-system/components/component-source.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
'use client'
|
||||
|
||||
import * as React from 'react'
|
||||
|
||||
import { cn } from 'ui'
|
||||
import { CodeBlockWrapper } from '@/components/code-block-wrapper'
|
||||
|
||||
interface ComponentSourceProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
src: string
|
||||
}
|
||||
|
||||
export function ComponentSource({ children, className, ...props }: ComponentSourceProps) {
|
||||
return (
|
||||
<CodeBlockWrapper
|
||||
expandButtonTitle="Expand"
|
||||
className={cn('my-6 overflow-hidden rounded-md', className)}
|
||||
>
|
||||
{children}
|
||||
</CodeBlockWrapper>
|
||||
)
|
||||
}
|
||||
179
apps/design-system/components/copy-button.tsx
Normal file
179
apps/design-system/components/copy-button.tsx
Normal file
@@ -0,0 +1,179 @@
|
||||
'use client'
|
||||
|
||||
import * as React from 'react'
|
||||
import { DropdownMenuTriggerProps } from '@radix-ui/react-dropdown-menu'
|
||||
|
||||
// import { NpmCommands } from 'types/unist'
|
||||
|
||||
// import { Event, trackEvent } from '@/lib/events'
|
||||
import { cn } from 'ui'
|
||||
import { Button } from 'ui'
|
||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from 'ui'
|
||||
import { Check, Copy } from 'lucide-react'
|
||||
|
||||
interface CopyButtonProps extends React.HTMLAttributes<HTMLButtonElement> {
|
||||
value: string
|
||||
src?: string
|
||||
// event?: Event['name']
|
||||
}
|
||||
|
||||
export async function copyToClipboardWithMeta(
|
||||
value: string
|
||||
// event?: Event
|
||||
) {
|
||||
navigator.clipboard.writeText(value)
|
||||
if (event) {
|
||||
// trackEvent(event)
|
||||
}
|
||||
}
|
||||
|
||||
export function CopyButton({
|
||||
value,
|
||||
className,
|
||||
src,
|
||||
// event,
|
||||
...props
|
||||
}: CopyButtonProps) {
|
||||
const [hasCopied, setHasCopied] = React.useState(false)
|
||||
|
||||
React.useEffect(() => {
|
||||
setTimeout(() => {
|
||||
setHasCopied(false)
|
||||
}, 2000)
|
||||
}, [hasCopied])
|
||||
|
||||
return (
|
||||
<Button
|
||||
size="small"
|
||||
type="outline"
|
||||
className={cn(
|
||||
'relative z-10 h-6 w-6 text-foreground-muted hover:bg-surface-100 hover:text-foreground p-0',
|
||||
className
|
||||
)}
|
||||
onClick={() => {
|
||||
copyToClipboardWithMeta(
|
||||
value
|
||||
// event
|
||||
// ? {
|
||||
// name: event,
|
||||
// properties: {
|
||||
// code: value,
|
||||
// },
|
||||
// }
|
||||
// : undefined
|
||||
)
|
||||
setHasCopied(true)
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
<span className="sr-only">Copy</span>
|
||||
{hasCopied ? <Check className="h-3 w-3 text-brand-600" /> : <Copy className="h-3 w-3" />}
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
interface CopyWithClassNamesProps extends DropdownMenuTriggerProps {
|
||||
value: string
|
||||
classNames: string
|
||||
className?: string
|
||||
}
|
||||
|
||||
export function CopyWithClassNames({
|
||||
value,
|
||||
classNames,
|
||||
className,
|
||||
...props
|
||||
}: CopyWithClassNamesProps) {
|
||||
const [hasCopied, setHasCopied] = React.useState(false)
|
||||
|
||||
React.useEffect(() => {
|
||||
setTimeout(() => {
|
||||
setHasCopied(false)
|
||||
}, 2000)
|
||||
}, [hasCopied])
|
||||
|
||||
const copyToClipboard = React.useCallback((value: string) => {
|
||||
copyToClipboardWithMeta(value)
|
||||
setHasCopied(true)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
size="small"
|
||||
type="outline"
|
||||
className={cn(
|
||||
'relative z-10 h-6 w-6 text-zinc-50 hover:bg-zinc-700 hover:text-zinc-50',
|
||||
className
|
||||
)}
|
||||
>
|
||||
{hasCopied ? <Check className="h-3 w-3" /> : <Copy className="h-3 w-3" />}
|
||||
<span className="sr-only">Copy</span>
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem onClick={() => copyToClipboard(value)}>Component</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => copyToClipboard(classNames)}>Classname</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)
|
||||
}
|
||||
|
||||
interface CopyNpmCommandButtonProps extends DropdownMenuTriggerProps {
|
||||
// commands: Required<NpmCommands>
|
||||
}
|
||||
|
||||
// export function CopyNpmCommandButton({ commands, className, ...props }: CopyNpmCommandButtonProps) {
|
||||
// const [hasCopied, setHasCopied] = React.useState(false)
|
||||
|
||||
// React.useEffect(() => {
|
||||
// setTimeout(() => {
|
||||
// setHasCopied(false)
|
||||
// }, 2000)
|
||||
// }, [hasCopied])
|
||||
|
||||
// // const copyCommand = React.useCallback((value: string, pm: 'npm' | 'pnpm' | 'yarn' | 'bun') => {
|
||||
// // copyToClipboardWithMeta(value,
|
||||
// // // {
|
||||
// // // name: 'copy_npm_command',
|
||||
// // // properties: {
|
||||
// // // command: value,
|
||||
// // // pm,
|
||||
// // // },
|
||||
// // })
|
||||
// // setHasCopied(true)
|
||||
// // }, [])
|
||||
|
||||
// return (
|
||||
// <DropdownMenu>
|
||||
// <DropdownMenuTrigger asChild>
|
||||
// <Button
|
||||
// // size="icon"
|
||||
// type="outline"
|
||||
// className={cn(
|
||||
// 'relative z-10 h-6 w-6 text-zinc-50 hover:bg-zinc-700 hover:text-zinc-50',
|
||||
// className
|
||||
// )}
|
||||
// >
|
||||
// {hasCopied ? <CheckIcon className="h-3 w-3" /> : <CopyIcon className="h-3 w-3" />}
|
||||
// <span className="sr-only">Copy</span>
|
||||
// </Button>
|
||||
// </DropdownMenuTrigger>
|
||||
// <DropdownMenuContent align="end">
|
||||
// <DropdownMenuItem onClick={() => copyCommand(commands.__npmCommand__, 'npm')}>
|
||||
// npm
|
||||
// </DropdownMenuItem>
|
||||
// <DropdownMenuItem onClick={() => copyCommand(commands.__yarnCommand__, 'yarn')}>
|
||||
// yarn
|
||||
// </DropdownMenuItem>
|
||||
// <DropdownMenuItem onClick={() => copyCommand(commands.__pnpmCommand__, 'pnpm')}>
|
||||
// pnpm
|
||||
// </DropdownMenuItem>
|
||||
// <DropdownMenuItem onClick={() => copyCommand(commands.__bunCommand__, 'bun')}>
|
||||
// bun
|
||||
// </DropdownMenuItem>
|
||||
// </DropdownMenuContent>
|
||||
// </DropdownMenu>
|
||||
// )
|
||||
// }
|
||||
19
apps/design-system/components/design-system-marks.tsx
Normal file
19
apps/design-system/components/design-system-marks.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
'use client'
|
||||
|
||||
import SVG from 'react-inlinesvg'
|
||||
import { useTheme } from 'next-themes'
|
||||
|
||||
function DesignSystemMarks() {
|
||||
const { resolvedTheme } = useTheme()
|
||||
|
||||
const isDark = resolvedTheme?.includes('dark')
|
||||
|
||||
return (
|
||||
<SVG
|
||||
className="h-4 w-auto"
|
||||
src={`${process.env.NEXT_PUBLIC_BASE_PATH}/img/design-system-marks/design-system-marks--${isDark ? 'dark' : 'light'}.svg`}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { DesignSystemMarks }
|
||||
15
apps/design-system/components/example-label.tsx
Normal file
15
apps/design-system/components/example-label.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import * as React from 'react'
|
||||
|
||||
const ExampleLabel = React.forwardRef<HTMLSpanElement, { children: React.ReactNode }>(
|
||||
({ children }, ref) => {
|
||||
return (
|
||||
<span ref={ref} className="text-xs flex gap-3 items-center text-foreground-muted">
|
||||
{children}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
ExampleLabel.displayName = 'ExampleLabel'
|
||||
|
||||
export { ExampleLabel }
|
||||
62
apps/design-system/components/grid.tsx
Normal file
62
apps/design-system/components/grid.tsx
Normal file
@@ -0,0 +1,62 @@
|
||||
import { forwardRef } from 'react'
|
||||
import { cn } from 'ui'
|
||||
|
||||
const Grid = forwardRef<HTMLDivElement, React.HTMLProps<HTMLDivElement>>(
|
||||
({ children, ...props }, ref) => {
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
{...props}
|
||||
className={cn(
|
||||
'grid grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 border-t border-l my-12',
|
||||
props.className
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
Grid.displayName = 'Grid'
|
||||
|
||||
const GridItem = forwardRef<HTMLDivElement, React.HTMLProps<HTMLDivElement>>(
|
||||
({ children, ...props }, ref) => {
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
{...props}
|
||||
className={cn(
|
||||
`
|
||||
relative
|
||||
min-h-32
|
||||
flex gap-4 flex-col items-center p-4
|
||||
border-b border-r
|
||||
bg-surface-75/50
|
||||
justify-center hover:bg-surface-100
|
||||
group
|
||||
cursor-pointer
|
||||
`,
|
||||
props.className
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className="
|
||||
absolute
|
||||
w-full h-full box-content
|
||||
transition
|
||||
group-hover:border
|
||||
group-hover:border-foreground-muted
|
||||
group-data-[state=open]:border
|
||||
group-data-[state=open]:border-foreground-muted
|
||||
"
|
||||
></div>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
GridItem.displayName = 'GridItem'
|
||||
|
||||
export { Grid, GridItem }
|
||||
19
apps/design-system/components/homepage-svg-handler.tsx
Normal file
19
apps/design-system/components/homepage-svg-handler.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
'use client'
|
||||
|
||||
import SVG from 'react-inlinesvg'
|
||||
import { useTheme } from 'next-themes'
|
||||
|
||||
const HomepageSvgHandler = ({ name }: { name: string }) => {
|
||||
const { resolvedTheme } = useTheme()
|
||||
|
||||
return (
|
||||
<div>
|
||||
<SVG
|
||||
className="h-32 w-auto"
|
||||
src={`${process.env.NEXT_PUBLIC_BASE_PATH}/img/design-system-marks/${name}--${resolvedTheme}.svg`}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export { HomepageSvgHandler }
|
||||
70
apps/design-system/components/icons.tsx
Normal file
70
apps/design-system/components/icons.tsx
Normal file
@@ -0,0 +1,70 @@
|
||||
import { Index } from 'icons/__registry__/index'
|
||||
import { Copy } from 'lucide-react'
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuGroup,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
IconCopy,
|
||||
} from 'ui'
|
||||
import { Grid, GridItem } from './grid'
|
||||
|
||||
function Icons() {
|
||||
return (
|
||||
<>
|
||||
<Grid>
|
||||
{Index.map((icon: any, i: number) => (
|
||||
<DropdownMenu key={i} modal={false}>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<GridItem>
|
||||
<icon.component
|
||||
strokeWidth={1.5}
|
||||
size={21}
|
||||
className="group-data-[state=open]:scale-125 transition-all"
|
||||
/>
|
||||
<span className="bg-surface-100 rounded-full border px-2 font-mono text-xs text-foreground-lighter group-data-[state=open]:text-foreground">
|
||||
{icon.name}
|
||||
</span>
|
||||
</GridItem>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="w-48">
|
||||
<DropdownMenuGroup>
|
||||
<DropdownMenuItem
|
||||
className="flex items-center gap-2"
|
||||
onSelect={() => navigator.clipboard.writeText(icon.name)}
|
||||
>
|
||||
<Copy size={14} strokeWidth={1} />
|
||||
Copy name
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
className="flex items-center gap-2"
|
||||
onSelect={() => navigator.clipboard.writeText(icon.jsx)}
|
||||
>
|
||||
<Copy size={14} strokeWidth={1} />
|
||||
Copy JSX
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
className="flex items-center gap-2"
|
||||
onSelect={() => navigator.clipboard.writeText(icon.import)}
|
||||
>
|
||||
<Copy size={14} strokeWidth={1} />
|
||||
Copy import path
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
className="flex items-center gap-2"
|
||||
onSelect={() => navigator.clipboard.writeText(icon.svg)}
|
||||
>
|
||||
<Copy size={14} strokeWidth={1} />
|
||||
Copy SVG
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuGroup>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
))}
|
||||
</Grid>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export { Icons }
|
||||
291
apps/design-system/components/mdx-components.tsx
Normal file
291
apps/design-system/components/mdx-components.tsx
Normal file
@@ -0,0 +1,291 @@
|
||||
'use client'
|
||||
|
||||
import * as React from 'react'
|
||||
import Image from 'next/image'
|
||||
import Link from 'next/link'
|
||||
import { useMDXComponent } from 'next-contentlayer2/hooks'
|
||||
// import { NpmCommands } from 'types/unist'
|
||||
// import { Event } from '@/lib/events'
|
||||
import { cn } from 'ui'
|
||||
import { useConfig } from '@/hooks/use-config'
|
||||
import { Callout } from '@/components/callout'
|
||||
import { CodeBlockWrapper } from '@/components/code-block-wrapper'
|
||||
import { ComponentExample } from '@/components/component-example'
|
||||
import { ComponentPreview } from '@/components/component-preview'
|
||||
import { ComponentSource } from '@/components/component-source'
|
||||
import {
|
||||
CopyButton,
|
||||
// CopyNpmCommandButton
|
||||
} from '@/components/copy-button'
|
||||
// import { FrameworkDocs } from '@/components/framework-docs'
|
||||
import { StyleWrapper } from './style-wrapper'
|
||||
import {
|
||||
Accordion_Shadcn_ as Accordion,
|
||||
AccordionContent_Shadcn_ as AccordionContent,
|
||||
AccordionItem_Shadcn_ as AccordionItem,
|
||||
AccordionTrigger_Shadcn_ as AccordionTrigger,
|
||||
} from 'ui'
|
||||
import {
|
||||
Alert_Shadcn_ as Alert,
|
||||
AlertDescription_Shadcn_ as AlertDescription,
|
||||
AlertTitle_Shadcn_ as AlertTitle,
|
||||
} from 'ui'
|
||||
import { AspectRatio } from 'ui'
|
||||
import {
|
||||
Tabs_Shadcn_ as Tabs,
|
||||
TabsContent_Shadcn_ as TabsContent,
|
||||
TabsList_Shadcn_ as TabsList,
|
||||
TabsTrigger_Shadcn_ as TabsTrigger,
|
||||
} from 'ui'
|
||||
import { ComponentProps } from './component-props'
|
||||
import { Style } from '@/registry/styles'
|
||||
import { Colors } from '@/components/colors'
|
||||
import { Icons } from '@/components/icons'
|
||||
import { ThemeSettings } from '@/components/theme-settings'
|
||||
import { CodeFragment } from '@/components/code-fragment'
|
||||
|
||||
const components = {
|
||||
Accordion,
|
||||
AccordionContent,
|
||||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
Alert,
|
||||
AlertTitle,
|
||||
AlertDescription,
|
||||
h1: ({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) => (
|
||||
<h1 className={cn('font-heading mt-2 scroll-m-20 text-4xl font-bold', className)} {...props} />
|
||||
),
|
||||
h2: ({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) => (
|
||||
<h2
|
||||
className={cn(
|
||||
'font-heading mt-12 scroll-m-20 border-b pb-2 text-2xl tracking-tight first:mt-0',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
h3: ({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) => (
|
||||
<h3
|
||||
className={cn('font-heading mt-8 scroll-m-20 text-xl tracking-tight', className)}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
h4: ({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) => (
|
||||
<h4
|
||||
className={cn('font-heading mt-8 scroll-m-20 text-lg tracking-tight', className)}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
h5: ({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) => (
|
||||
<h5 className={cn('mt-8 scroll-m-20 text-lg tracking-tight', className)} {...props} />
|
||||
),
|
||||
h6: ({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) => (
|
||||
<h6 className={cn('mt-8 scroll-m-20 text-base tracking-tight', className)} {...props} />
|
||||
),
|
||||
a: ({ className, ...props }: React.HTMLAttributes<HTMLAnchorElement>) => (
|
||||
<a
|
||||
className={cn(
|
||||
'text-foreground underline decoration-1 decoration-foreground-muted underline-offset-4 transition-colors hover:decoration-brand hover:decoration-2',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
p: ({ className, ...props }: React.HTMLAttributes<HTMLParagraphElement>) => (
|
||||
<p
|
||||
className={cn('leading-7 [&:not(:first-child)]:mt-6 text-foreground-light', className)}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
ul: ({ className, ...props }: React.HTMLAttributes<HTMLUListElement>) => (
|
||||
<ul className={cn('my-6 ml-6 list-disc', className)} {...props} />
|
||||
),
|
||||
ol: ({ className, ...props }: React.HTMLAttributes<HTMLOListElement>) => (
|
||||
<ol className={cn('my-6 ml-6 list-decimal', className)} {...props} />
|
||||
),
|
||||
li: ({ className, ...props }: React.HTMLAttributes<HTMLElement>) => (
|
||||
<li className={cn('mt-2', className)} {...props} />
|
||||
),
|
||||
blockquote: ({ className, ...props }: React.HTMLAttributes<HTMLElement>) => (
|
||||
<blockquote className={cn('mt-6 border-l-2 pl-6 italic', className)} {...props} />
|
||||
),
|
||||
img: ({ className, alt, ...props }: React.ImgHTMLAttributes<HTMLImageElement>) => (
|
||||
// eslint-disable-next-line @next/next/no-img-element
|
||||
<img className={cn('rounded-md', className)} alt={alt} {...props} />
|
||||
),
|
||||
hr: ({ ...props }: React.HTMLAttributes<HTMLHRElement>) => (
|
||||
<hr className="my-4 md:my-8" {...props} />
|
||||
),
|
||||
table: ({ className, ...props }: React.HTMLAttributes<HTMLTableElement>) => (
|
||||
<div className="my-6 w-full overflow-y-auto">
|
||||
<table className={cn('w-full', className)} {...props} />
|
||||
</div>
|
||||
),
|
||||
tr: ({ className, ...props }: React.HTMLAttributes<HTMLTableRowElement>) => (
|
||||
<tr className={cn('m-0 border-t p-0 even:bg-surface-75/75', className)} {...props} />
|
||||
),
|
||||
th: ({ className, ...props }: React.HTMLAttributes<HTMLTableCellElement>) => (
|
||||
<th
|
||||
className={cn(
|
||||
'border px-4 py-2 text-left font-normal [&[align=center]]:text-center [&[align=right]]:text-right',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
td: ({ className, ...props }: React.HTMLAttributes<HTMLTableCellElement>) => (
|
||||
<td
|
||||
className={cn(
|
||||
'border text-foreground-light px-4 py-2 text-left [&[align=center]]:text-center [&[align=right]]:text-right',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
pre: (
|
||||
{
|
||||
className,
|
||||
__rawString__,
|
||||
// __npmCommand__,
|
||||
// __yarnCommand__,
|
||||
// __pnpmCommand__,
|
||||
// __bunCommand__,
|
||||
__withMeta__,
|
||||
__src__,
|
||||
// __event__,
|
||||
__style__,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLPreElement> & {
|
||||
__style__?: Style['name']
|
||||
__rawString__?: string
|
||||
__withMeta__?: boolean
|
||||
__src__?: string
|
||||
// __event__?: Event['name']
|
||||
}
|
||||
// & NpmCommands
|
||||
) => {
|
||||
return (
|
||||
<StyleWrapper styleName={__style__}>
|
||||
<pre
|
||||
className={cn(
|
||||
'mb-4 mt-6 max-h-[650px] overflow-x-auto rounded-lg border bg-surface-75/75 py-4',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
{__rawString__ && (
|
||||
// !__npmCommand__ &&
|
||||
<CopyButton
|
||||
value={__rawString__}
|
||||
src={__src__}
|
||||
// event={__event__}
|
||||
className={cn('absolute right-4 top-4', __withMeta__ && 'top-16')}
|
||||
/>
|
||||
)}
|
||||
{/* {__npmCommand__ && __yarnCommand__ && __pnpmCommand__ && __bunCommand__ && (
|
||||
<CopyNpmCommandButton
|
||||
commands={{
|
||||
__npmCommand__,
|
||||
__yarnCommand__,
|
||||
__pnpmCommand__,
|
||||
__bunCommand__,
|
||||
}}
|
||||
className={cn('absolute right-4 top-4', __withMeta__ && 'top-16')}
|
||||
/>
|
||||
)} */}
|
||||
</StyleWrapper>
|
||||
)
|
||||
},
|
||||
code: ({ className, ...props }: React.HTMLAttributes<HTMLElement>) => (
|
||||
<code
|
||||
className={cn(
|
||||
'relative rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-sm',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
Image,
|
||||
Callout,
|
||||
ComponentPreview,
|
||||
ComponentExample,
|
||||
ComponentSource,
|
||||
ComponentProps,
|
||||
AspectRatio,
|
||||
CodeBlockWrapper: ({ ...props }) => <CodeBlockWrapper className="rounded-md border" {...props} />,
|
||||
Step: ({ className, ...props }: React.ComponentProps<'h3'>) => (
|
||||
<h3
|
||||
className={cn(
|
||||
'font-heading mt-8 scroll-m-20 text-xl font-semibold tracking-tight',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
Steps: ({ ...props }) => (
|
||||
<div className="[&>h3]:step steps mb-12 ml-4 border-l pl-8 [counter-reset:step]" {...props} />
|
||||
),
|
||||
Tabs: ({ className, ...props }: React.ComponentProps<typeof Tabs>) => (
|
||||
<Tabs className={cn('relative mt-6 w-full', className)} {...props} />
|
||||
),
|
||||
TabsList: ({ className, ...props }: React.ComponentProps<typeof TabsList>) => (
|
||||
<TabsList
|
||||
className={cn('w-full justify-start rounded-none border-b bg-transparent p-0', className)}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
TabsTrigger: ({ className, ...props }: React.ComponentProps<typeof TabsTrigger>) => (
|
||||
<TabsTrigger
|
||||
className={cn(
|
||||
'relative h-9 rounded-none border-b-2 border-b-transparent bg-transparent px-4 pb-3 pt-2 font-semibold text-muted-foreground shadow-none transition-none data-[state=active]:border-b-primary data-[state=active]:text-foreground data-[state=active]:shadow-none',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
TabsContent: ({ className, ...props }: React.ComponentProps<typeof TabsContent>) => (
|
||||
<TabsContent
|
||||
className={cn(
|
||||
'relative [&_h3.font-heading]:text-base [&_h3.font-heading]:font-semibold',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
// FrameworkDocs: ({ className, ...props }: React.ComponentProps<typeof FrameworkDocs>) => (
|
||||
// <FrameworkDocs className={cn(className)} {...props} />
|
||||
// ),
|
||||
Link: ({ className, ...props }: React.ComponentProps<typeof Link>) => (
|
||||
<Link className={cn('font-medium underline underline-offset-4', className)} {...props} />
|
||||
),
|
||||
LinkedCard: ({ className, ...props }: React.ComponentProps<typeof Link>) => (
|
||||
<Link
|
||||
className={cn(
|
||||
'flex w-full flex-col items-center rounded-xl border bg-card p-6 text-card-foreground shadow transition-colors hover:bg-muted/50 sm:p-10',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
Colors,
|
||||
Icons,
|
||||
ThemeSettings,
|
||||
CodeFragment,
|
||||
}
|
||||
|
||||
interface MdxProps {
|
||||
code: string
|
||||
}
|
||||
|
||||
export function Mdx({ code }: MdxProps) {
|
||||
const [config] = useConfig()
|
||||
const Component = useMDXComponent(code, {
|
||||
style: config.style,
|
||||
})
|
||||
|
||||
return (
|
||||
<div className="mdx">
|
||||
<Component components={components} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
72
apps/design-system/components/pager.tsx
Normal file
72
apps/design-system/components/pager.tsx
Normal file
@@ -0,0 +1,72 @@
|
||||
import Link from 'next/link'
|
||||
import { Doc } from 'contentlayer/generated'
|
||||
import { NavItem, NavItemWithChildren } from '@/types/nav'
|
||||
|
||||
import { docsConfig } from '@/config/docs'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { buttonVariants } from 'ui'
|
||||
import { ChevronLeft, ChevronRight } from 'lucide-react'
|
||||
|
||||
interface DocsPagerProps {
|
||||
doc: Doc
|
||||
}
|
||||
|
||||
export function DocsPager({ doc }: DocsPagerProps) {
|
||||
const pager = getPagerForDoc(doc)
|
||||
|
||||
if (!pager) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-row items-center justify-between">
|
||||
{pager?.prev?.href && (
|
||||
<Link
|
||||
href={pager.prev.href}
|
||||
className="flex gap-3 text-foreground-light hover:text-foreground group"
|
||||
>
|
||||
<ChevronLeft className="mr-2 h-4 w-4 self-end mb-1 text-foreground-muted group-hover:text-foreground-lighter" />
|
||||
<div className="flex flex-col gap-1">
|
||||
<span className="text-xs font-mono uppercase tracking-wider text-foreground-muted group-hover:text-foreground-lighter">
|
||||
Previous
|
||||
</span>
|
||||
{pager.prev.title}
|
||||
</div>
|
||||
</Link>
|
||||
)}
|
||||
{pager?.next?.href && (
|
||||
<Link
|
||||
href={pager.next.href}
|
||||
className="flex gap-3 text-foreground-light hover:text-foreground group"
|
||||
>
|
||||
<div className="flex flex-col gap-1">
|
||||
<span className="text-xs font-mono uppercase tracking-wider text-foreground-muted group-hover:text-foreground-lighter">
|
||||
Next
|
||||
</span>
|
||||
{pager.next.title}
|
||||
</div>
|
||||
<ChevronRight className="ml-2 h-4 w-4 self-end mb-1 text-foreground-muted group-hover:text-foreground-lighter" />
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function getPagerForDoc(doc: Doc) {
|
||||
const flattenedLinks = [null, ...flatten(docsConfig.sidebarNav), null]
|
||||
const activeIndex = flattenedLinks.findIndex((link) => doc.slug === link?.href)
|
||||
const prev = activeIndex !== 0 ? flattenedLinks[activeIndex - 1] : null
|
||||
const next = activeIndex !== flattenedLinks.length - 1 ? flattenedLinks[activeIndex + 1] : null
|
||||
return {
|
||||
prev,
|
||||
next,
|
||||
}
|
||||
}
|
||||
|
||||
export function flatten(links: NavItemWithChildren[]): NavItem[] {
|
||||
return links
|
||||
.reduce<NavItem[]>((flat, link) => {
|
||||
return flat.concat(link.items?.length ? flatten(link.items) : link)
|
||||
}, [])
|
||||
.filter((link) => !link?.disabled)
|
||||
}
|
||||
43
apps/design-system/components/side-navigation-item.tsx
Normal file
43
apps/design-system/components/side-navigation-item.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
'use client'
|
||||
|
||||
import { SidebarNavItem } from '@/types/nav'
|
||||
import { cn } from 'ui/src/lib/utils/cn'
|
||||
import { usePathname } from 'next/navigation'
|
||||
import Link from 'next/link'
|
||||
import React from 'react'
|
||||
|
||||
const NavigationItem: React.FC<{ item: SidebarNavItem }> = React.memo(({ item }) => {
|
||||
const pathname = usePathname()
|
||||
|
||||
const isActive = pathname === item.href
|
||||
|
||||
return (
|
||||
<Link
|
||||
href={`${item.href}`}
|
||||
className={cn(
|
||||
'relative',
|
||||
'flex',
|
||||
'items-center',
|
||||
'h-8',
|
||||
'text-foreground-light px-6',
|
||||
!isActive && 'hover:bg-surface-100 hover:text-foreground',
|
||||
isActive && 'bg-surface-200 text-foreground',
|
||||
'transition-all'
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
'transition',
|
||||
'absolute left-0 w-1 h-full bg-foreground',
|
||||
|
||||
isActive ? 'opacity-100' : 'opacity-0'
|
||||
)}
|
||||
></div>
|
||||
{item.title}
|
||||
</Link>
|
||||
)
|
||||
})
|
||||
|
||||
NavigationItem.displayName = 'NavigationItem'
|
||||
|
||||
export default NavigationItem
|
||||
21
apps/design-system/components/side-navigation.tsx
Normal file
21
apps/design-system/components/side-navigation.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import { docsConfig } from '@/config/docs'
|
||||
import NavigationItem from '@/components/side-navigation-item'
|
||||
|
||||
function SideNavigation() {
|
||||
return (
|
||||
<nav className="min-w-[220px]">
|
||||
{docsConfig.sidebarNav.map((section, i) => (
|
||||
<div key={`${section.title}-${i}`} className="pb-10 space-y-0.5">
|
||||
<div className="font-mono uppercase text-xs text-foreground-lighter/75 mb-2 px-6 tracking-wider">
|
||||
{section.title}
|
||||
</div>
|
||||
{section.items.map((item, i) => (
|
||||
<NavigationItem item={item} key={`${item.href}-${i}`} />
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
|
||||
export default SideNavigation
|
||||
61
apps/design-system/components/site-footer.tsx
Normal file
61
apps/design-system/components/site-footer.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
import { siteConfig } from '@/config/site'
|
||||
|
||||
export function SiteFooter() {
|
||||
return (
|
||||
<footer className="py-6 md:px-8 md:py-0 mx-auto border-r border-l border-b w-full max-w-site">
|
||||
<div className="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-foreground-muted md:text-left">
|
||||
Built by{' '}
|
||||
<a
|
||||
href={siteConfig.links.twitter}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="font-medium underline underline-offset-4 hover:text-foreground-lighter"
|
||||
>
|
||||
Supabase
|
||||
</a>
|
||||
. The source code is available on{' '}
|
||||
<a
|
||||
href={siteConfig.links.github}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="font-medium underline underline-offset-4 hover:text-foreground-lighter"
|
||||
>
|
||||
GitHub
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
<p className="text-balance text-center text-sm leading-loose text-foreground-muted">
|
||||
Site inspired by{' '}
|
||||
<a
|
||||
href={siteConfig.links.credits.radix}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="font-medium underline underline-offset-4 hover:text-foreground-lighter"
|
||||
>
|
||||
Radix
|
||||
</a>
|
||||
,{' '}
|
||||
<a
|
||||
href={siteConfig.links.credits.shadcn}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="font-medium underline underline-offset-4 hover:text-foreground-lighter"
|
||||
>
|
||||
shadcn/ui
|
||||
</a>{' '}
|
||||
and{' '}
|
||||
<a
|
||||
href={siteConfig.links.credits.geist}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="font-medium underline underline-offset-4 hover:text-foreground-lighter"
|
||||
>
|
||||
Geist
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
</div>
|
||||
</footer>
|
||||
)
|
||||
}
|
||||
206
apps/design-system/components/source-panel.tsx
Normal file
206
apps/design-system/components/source-panel.tsx
Normal file
@@ -0,0 +1,206 @@
|
||||
import { Doc } from '@/.contentlayer/generated'
|
||||
import Link from 'next/link'
|
||||
import { forwardRef } from 'react'
|
||||
|
||||
import { ExternalLink } from 'lucide-react'
|
||||
import { Button } from 'ui'
|
||||
import { cn } from 'ui/src/lib/utils/cn'
|
||||
import Image from 'next/image'
|
||||
|
||||
const SourcePanel = forwardRef<HTMLDivElement, React.HTMLProps<HTMLDivElement> & { doc: Doc }>(
|
||||
({ doc, children, ...props }, ref) => {
|
||||
const label = {
|
||||
radix: 'Radix UI',
|
||||
vaul: 'Vaul',
|
||||
shadcn: 'ui.shadcn',
|
||||
}
|
||||
|
||||
const ShadcnPanel = () => {
|
||||
if (doc.source?.shadcn) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'bg-surface-75/50 border flex items-center p-3 px-5 gap-6 first:rounded-t-md last:rounded-b-md',
|
||||
props.className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" className="h-6 w-6">
|
||||
<rect width="256" height="256" fill="none"></rect>
|
||||
<line
|
||||
x1="208"
|
||||
y1="128"
|
||||
x2="128"
|
||||
y2="208"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="16"
|
||||
></line>
|
||||
<line
|
||||
x1="192"
|
||||
y1="40"
|
||||
x2="40"
|
||||
y2="192"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="16"
|
||||
></line>
|
||||
</svg>
|
||||
<span className="hidden font-bold sm:inline-block">shadcn/ui</span>
|
||||
</div>
|
||||
<span className="text-foreground-light text-sm">
|
||||
This component is based on ui.shadcn
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const VaulPanel = () => {
|
||||
if (doc.source?.vaul) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'bg-surface-75/50 border flex items-center p-3 px-5 gap-6 first:rounded-t-md last:rounded-b-md',
|
||||
props.className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<Image
|
||||
width={24}
|
||||
height={24}
|
||||
src="https://avatars.githubusercontent.com/u/36730035?s=48&v=4"
|
||||
alt="Vaul"
|
||||
className="h-6 w-6 rounded-full"
|
||||
/>
|
||||
<span className="hidden font-bold sm:inline-block">vaul</span>
|
||||
</div>
|
||||
<span className="text-foreground-light text-sm">
|
||||
This component is based on vaul by emilkowalski
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const InputOtp = () => {
|
||||
if (doc.source?.inputOtp) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'bg-surface-75/50 border flex items-center p-3 px-5 gap-6 first:rounded-t-md last:rounded-b-md',
|
||||
props.className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<Image
|
||||
width={24}
|
||||
height={24}
|
||||
src="https://avatars.githubusercontent.com/u/10366880?s=48&v=4"
|
||||
alt="inputOtp"
|
||||
className="h-6 w-6 rounded-full"
|
||||
/>
|
||||
<span className="hidden font-bold sm:inline-block">input-otp</span>
|
||||
</div>
|
||||
<span className="text-foreground-light text-sm">
|
||||
This component is based on input-otp by guilhermerodz
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const RadixPanel = () => {
|
||||
if (doc.source?.radix) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'bg-surface-75/50 border flex items-center p-3 px-5 gap-6 first:rounded-t-md last:rounded-b-md',
|
||||
props.className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<svg
|
||||
width="76"
|
||||
height="24"
|
||||
viewBox="0 0 76 24"
|
||||
fill="currentcolor"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path d="M43.9022 20.0061H46.4499C46.2647 19.0375 46.17 18.1161 46.17 17.0058V12.3753C46.17 9.25687 44.3893 7.72127 41.1943 7.72127C38.3003 7.72127 36.3324 9.23324 36.0777 11.8083H38.9254C39.0181 10.698 39.8052 9.96561 41.1017 9.96561C42.4446 9.96561 43.3243 10.6743 43.3243 12.1391V12.7061L39.8052 13.1077C37.4206 13.3912 35.5684 14.3834 35.5684 16.7931C35.5684 18.9666 37.2353 20.2659 39.5274 20.2659C41.4027 20.2659 42.9845 19.4863 43.6401 18.1161C43.6689 18.937 43.9022 20.0061 43.9022 20.0061ZM40.3377 18.1634C39.157 18.1634 38.5087 17.5727 38.5087 16.6278C38.5087 15.3757 39.4579 15.0922 40.7082 14.9268L43.3243 14.6197V15.352C43.3243 17.242 41.8658 18.1634 40.3377 18.1634ZM56.2588 20.0061H59.176V3H56.2125V9.96561C55.6569 8.76075 54.3141 7.72127 52.4851 7.72127C49.3058 7.72127 47.099 10.2963 47.099 14.0054C47.099 17.7381 49.3058 20.2896 52.4851 20.2896C54.2678 20.2896 55.68 19.2973 56.2588 18.0925V20.0061ZM56.282 14.218C56.282 16.5569 55.1938 18.0689 53.3185 18.0689C51.3969 18.0689 50.1856 16.486 50.1856 14.0054C50.1856 11.5485 51.3969 9.94198 53.3185 9.94198C55.1938 9.94198 56.282 11.454 56.282 13.7928V14.218ZM60.9066 5.97304H64.0553V3.01996H60.9066V5.97304ZM60.9992 20.0061H63.9627V8.00476H60.9992V20.0061ZM67.6638 20.0061L70.6041 15.8954L73.5212 20.0061H76.9246L72.3636 13.7219L76.5542 8.00476H73.3823L70.7661 11.7138L68.1731 8.00476H64.7697L69.0066 13.8637L64.4919 20.0061H67.6638Z"></path>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M24.9132 20V14.0168H28.7986L32.4513 20H35.7006L31.6894 13.5686C33.5045 12.986 35.0955 11.507 35.0955 9.01961C35.0955 5.7479 32.7994 4 28.9571 4H22V20H24.9132ZM24.9132 6.35294V11.6863H28.821C31.0395 11.6863 32.1599 10.7675 32.1599 9.01961C32.1599 7.27171 30.9395 6.35294 28.621 6.35294H24.9132Z"
|
||||
></path>
|
||||
<path d="M7 23C3.13401 23 0 19.6422 0 15.5C0 11.3578 3.13401 8 7 8V23Z"></path>
|
||||
<path d="M7 0H0V7H7V0Z"></path>
|
||||
<path d="M11.5 7C13.433 7 15 5.433 15 3.5C15 1.567 13.433 0 11.5 0C9.56704 0 8 1.567 8 3.5C8 5.433 9.56704 7 11.5 7Z"></path>
|
||||
</svg>
|
||||
<div className="flex flex-row items-center justify-between text-sm w-full">
|
||||
<span className="text-foreground-light text-sm">This component uses Radix UI</span>
|
||||
{doc.links ? (
|
||||
<div className="flex items-center gap-2 justify-end">
|
||||
{doc.links?.doc && (
|
||||
<Button
|
||||
type="outline"
|
||||
className="rounded-full"
|
||||
icon={<ExternalLink className="text-foreground-muted" strokeWidth={1} />}
|
||||
>
|
||||
<Link
|
||||
href={doc.links.doc}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
|
||||
// className={cn(buttonVariants({ variant: 'default' }), 'gap-1')}
|
||||
>
|
||||
Docs
|
||||
</Link>
|
||||
</Button>
|
||||
)}
|
||||
{doc.links?.api && (
|
||||
<Button
|
||||
type="outline"
|
||||
className="rounded-full"
|
||||
icon={<ExternalLink className="text-foreground-muted" strokeWidth={1} />}
|
||||
>
|
||||
<Link
|
||||
href={doc.links.api}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
|
||||
// className={cn(badgeVariants({ variant: 'default' }), 'gap-1')}
|
||||
>
|
||||
API Reference
|
||||
</Link>
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col -space-y-px">
|
||||
<RadixPanel />
|
||||
<VaulPanel />
|
||||
<InputOtp />
|
||||
{/* <ShadcnPanel /> */}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
SourcePanel.displayName = 'SourcePanel'
|
||||
|
||||
export { SourcePanel }
|
||||
20
apps/design-system/components/style-wrapper.tsx
Normal file
20
apps/design-system/components/style-wrapper.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
'use client'
|
||||
|
||||
import * as React from 'react'
|
||||
|
||||
import { useConfig } from '@/hooks/use-config'
|
||||
import { Style } from '@/registry/styles'
|
||||
|
||||
interface StyleWrapperProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
styleName?: Style['name']
|
||||
}
|
||||
|
||||
export function StyleWrapper({ styleName, children }: StyleWrapperProps) {
|
||||
const [config] = useConfig()
|
||||
|
||||
if (!styleName || config.style === styleName) {
|
||||
return <>{children}</>
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
54
apps/design-system/components/theme-settings.tsx
Normal file
54
apps/design-system/components/theme-settings.tsx
Normal file
@@ -0,0 +1,54 @@
|
||||
'use client'
|
||||
|
||||
import { useTheme } from 'next-themes'
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
import SVG from 'react-inlinesvg'
|
||||
import { RadioGroupLargeItem_Shadcn_, RadioGroup_Shadcn_, Theme, singleThemes } from 'ui'
|
||||
|
||||
const ThemeSettings = () => {
|
||||
const [mounted, setMounted] = useState(false)
|
||||
const { theme, setTheme } = useTheme()
|
||||
|
||||
/**
|
||||
* Avoid Hydration Mismatch
|
||||
* https://github.com/pacocoursey/next-themes?tab=readme-ov-file#avoid-hydration-mismatch
|
||||
*/
|
||||
// useEffect only runs on the client, so now we can safely show the UI
|
||||
useEffect(() => {
|
||||
setMounted(true)
|
||||
}, [])
|
||||
|
||||
if (!mounted) {
|
||||
return null
|
||||
}
|
||||
|
||||
function SingleThemeSelection() {
|
||||
return (
|
||||
<form className="py-8">
|
||||
<RadioGroup_Shadcn_
|
||||
name="theme"
|
||||
onValueChange={setTheme}
|
||||
aria-label="Choose a theme"
|
||||
defaultValue={theme}
|
||||
value={theme}
|
||||
className="flex flex-wrap gap-3"
|
||||
>
|
||||
{singleThemes.map((theme: Theme) => (
|
||||
<RadioGroupLargeItem_Shadcn_ key={theme.value} value={theme.value} label={theme.name}>
|
||||
<SVG src={`${process.env.NEXT_PUBLIC_BASE_PATH}/img/themes/${theme.value}.svg`} />
|
||||
</RadioGroupLargeItem_Shadcn_>
|
||||
))}
|
||||
</RadioGroup_Shadcn_>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<SingleThemeSelection />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export { ThemeSettings }
|
||||
92
apps/design-system/components/theme-switcher-dropdown.tsx
Normal file
92
apps/design-system/components/theme-switcher-dropdown.tsx
Normal file
@@ -0,0 +1,92 @@
|
||||
'use client'
|
||||
|
||||
import { Moon, Sun } from 'lucide-react'
|
||||
import { useTheme } from 'next-themes'
|
||||
import { useEffect, useState } from 'react'
|
||||
import SVG from 'react-inlinesvg'
|
||||
import {
|
||||
Button,
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuRadioGroup,
|
||||
DropdownMenuRadioItem,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
RadioGroupLargeItem_Shadcn_,
|
||||
RadioGroup_Shadcn_,
|
||||
Theme,
|
||||
singleThemes,
|
||||
} from 'ui'
|
||||
|
||||
const ThemeSwitcherDropdown = () => {
|
||||
const [mounted, setMounted] = useState(false)
|
||||
const { theme, setTheme, resolvedTheme } = useTheme()
|
||||
|
||||
/**
|
||||
* Avoid Hydration Mismatch
|
||||
* https://github.com/pacocoursey/next-themes?tab=readme-ov-file#avoid-hydration-mismatch
|
||||
*/
|
||||
// useEffect only runs on the client, so now we can safely show the UI
|
||||
useEffect(() => {
|
||||
setMounted(true)
|
||||
}, [])
|
||||
|
||||
if (!mounted) {
|
||||
return null
|
||||
}
|
||||
|
||||
function SingleThemeSelection() {
|
||||
return (
|
||||
<form>
|
||||
<RadioGroup_Shadcn_
|
||||
name="theme"
|
||||
onValueChange={setTheme}
|
||||
aria-label="Choose a theme"
|
||||
defaultValue={theme}
|
||||
value={theme}
|
||||
className="flex flex-wrap gap-3"
|
||||
></RadioGroup_Shadcn_>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
|
||||
const iconClasses = 'text-foreground-light group-data-[state=open]:text-foreground'
|
||||
|
||||
return (
|
||||
<>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
type="text"
|
||||
size="tiny"
|
||||
className="px-1 group"
|
||||
icon={
|
||||
resolvedTheme?.includes('light') ? (
|
||||
<Sun className={iconClasses} />
|
||||
) : (
|
||||
<Moon className={iconClasses} />
|
||||
)
|
||||
}
|
||||
></Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="w-56" align="end">
|
||||
<DropdownMenuLabel>Theme</DropdownMenuLabel>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuRadioGroup
|
||||
value={theme}
|
||||
onValueChange={(themeValue) => setTheme(themeValue)}
|
||||
>
|
||||
{singleThemes.map((theme: Theme) => (
|
||||
<DropdownMenuRadioItem key={theme.value} value={theme.value}>
|
||||
{theme.name}
|
||||
</DropdownMenuRadioItem>
|
||||
))}
|
||||
</DropdownMenuRadioGroup>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export { ThemeSwitcherDropdown }
|
||||
107
apps/design-system/components/toc.tsx
Normal file
107
apps/design-system/components/toc.tsx
Normal file
@@ -0,0 +1,107 @@
|
||||
// @ts-nocheck
|
||||
'use client'
|
||||
|
||||
import * as React from 'react'
|
||||
|
||||
import { TableOfContents } from '@/lib/toc'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { useMounted } from '@/hooks/use-mounted'
|
||||
|
||||
interface TocProps {
|
||||
toc: TableOfContents
|
||||
}
|
||||
|
||||
export function DashboardTableOfContents({ toc }: TocProps) {
|
||||
const itemIds = React.useMemo(
|
||||
() =>
|
||||
toc.items
|
||||
? toc.items
|
||||
.flatMap((item) => [item.url, item?.items?.map((item) => item.url)])
|
||||
.flat()
|
||||
.filter(Boolean)
|
||||
.map((id) => id?.split('#')[1])
|
||||
: [],
|
||||
[toc]
|
||||
)
|
||||
const activeHeading = useActiveItem(itemIds)
|
||||
const mounted = useMounted()
|
||||
|
||||
if (!toc?.items || !mounted) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
<p className="font-medium text-foreground-light">On This Page</p>
|
||||
<Tree tree={toc} activeItem={activeHeading} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function useActiveItem(itemIds: string[]) {
|
||||
const [activeId, setActiveId] = React.useState(null)
|
||||
|
||||
React.useEffect(() => {
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting) {
|
||||
setActiveId(entry.target.id)
|
||||
}
|
||||
})
|
||||
},
|
||||
{ rootMargin: `0% 0% -80% 0%` }
|
||||
)
|
||||
|
||||
itemIds?.forEach((id) => {
|
||||
const element = document.getElementById(id)
|
||||
if (element) {
|
||||
observer.observe(element)
|
||||
}
|
||||
})
|
||||
|
||||
return () => {
|
||||
itemIds?.forEach((id) => {
|
||||
const element = document.getElementById(id)
|
||||
if (element) {
|
||||
observer.unobserve(element)
|
||||
}
|
||||
})
|
||||
}
|
||||
}, [itemIds])
|
||||
|
||||
return activeId
|
||||
}
|
||||
|
||||
interface TreeProps {
|
||||
tree: TableOfContents
|
||||
level?: number
|
||||
activeItem?: string
|
||||
}
|
||||
|
||||
function Tree({ tree, level = 1, activeItem }: TreeProps) {
|
||||
return tree?.items?.length && level < 3 ? (
|
||||
<ul className={cn('m-0 list-none', { 'pl-4': level !== 1 })}>
|
||||
{tree.items.map((item, index) => {
|
||||
return (
|
||||
<li key={index} className={cn('mt-0 pt-2')}>
|
||||
<a
|
||||
href={item.url}
|
||||
className={cn(
|
||||
'inline-block no-underline transition-colors hover:text-foreground',
|
||||
item.url === `#${activeItem}`
|
||||
? 'font-medium text-foreground'
|
||||
: 'text-foreground-muted'
|
||||
)}
|
||||
>
|
||||
{item.title}
|
||||
</a>
|
||||
{item.items?.length ? (
|
||||
<Tree tree={item} level={level + 1} activeItem={activeItem} />
|
||||
) : null}
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
) : null
|
||||
}
|
||||
17
apps/design-system/components/top-navigation-search.tsx
Normal file
17
apps/design-system/components/top-navigation-search.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
'use client'
|
||||
|
||||
import { Search } from 'lucide-react'
|
||||
import { Input } from 'ui-patterns/DataInputs/Input'
|
||||
|
||||
function TopNavigationSearch() {
|
||||
return (
|
||||
<Input
|
||||
size="small"
|
||||
className="transition w-64 rounded-full hover:bg-surface-200 hover:border-foreground-muted cursor-pointer"
|
||||
icon={<Search size={14} />}
|
||||
placeholder="Search Design System..."
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { TopNavigationSearch }
|
||||
38
apps/design-system/components/top-navigation.tsx
Normal file
38
apps/design-system/components/top-navigation.tsx
Normal file
@@ -0,0 +1,38 @@
|
||||
// import { docsConfig } from '@/config/docs'
|
||||
import Link from 'next/link'
|
||||
import { DesignSystemMarks } from './design-system-marks'
|
||||
import { ThemeSwitcherDropdown } from './theme-switcher-dropdown'
|
||||
import { TopNavigationSearch } from './top-navigation-search'
|
||||
import { CommandMenu } from './command-menu'
|
||||
|
||||
function TopNavigation() {
|
||||
return (
|
||||
<header className="sticky top-0 z-50 w-full border-t bg-studio/95 backdrop-blur supports-[backdrop-filter]:bg-studio/60">
|
||||
<div className="absolute border-b border-dashed w-full top-[3.4rem] -z-10"></div>
|
||||
<nav className="h-14 w-full flex">
|
||||
<div className="max-w-site border-b w-full flex flex-row items-center gap-6 mx-auto px-6 border-r border-l justify-between">
|
||||
<div className="flex items-center gap-8">
|
||||
<Link href="/">
|
||||
<h1>Supabase Design System</h1>
|
||||
</Link>
|
||||
<DesignSystemMarks />
|
||||
</div>
|
||||
{/* {docsConfig.mainNav.map((section) => (
|
||||
<>
|
||||
<div className="font-mono uppercase text-xs text-foreground-lighter">
|
||||
{section.title}
|
||||
</div>
|
||||
</>
|
||||
))} */}
|
||||
<div className="flex items-center gap-8">
|
||||
{/* <TopNavigationSearch /> */}
|
||||
<CommandMenu />
|
||||
<ThemeSwitcherDropdown />
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
)
|
||||
}
|
||||
|
||||
export default TopNavigation
|
||||
335
apps/design-system/config/docs.ts
Normal file
335
apps/design-system/config/docs.ts
Normal file
@@ -0,0 +1,335 @@
|
||||
import { MainNavItem, SidebarNavItem } from 'types/nav'
|
||||
|
||||
interface DocsConfig {
|
||||
mainNav?: MainNavItem[]
|
||||
sidebarNav: SidebarNavItem[]
|
||||
}
|
||||
|
||||
export const docsConfig: DocsConfig = {
|
||||
// mainNav: [
|
||||
// {
|
||||
// title: 'Documentation',
|
||||
// href: '/docs',
|
||||
// },
|
||||
// {
|
||||
// title: 'Components',
|
||||
// href: '/docs/components/accordion',
|
||||
// },
|
||||
// {
|
||||
// title: 'Themes',
|
||||
// href: '/themes',
|
||||
// },
|
||||
// {
|
||||
// title: 'Examples',
|
||||
// href: '/examples',
|
||||
// },
|
||||
// {
|
||||
// title: 'Blocks',
|
||||
// href: '/blocks',
|
||||
// },
|
||||
// ],
|
||||
sidebarNav: [
|
||||
{
|
||||
title: 'Getting Started',
|
||||
items: [
|
||||
{
|
||||
title: 'Introduction',
|
||||
href: '/docs',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Color usage',
|
||||
href: '/docs/color-usage',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Tailwind classes',
|
||||
href: '/docs/tailwind-classes',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Theming',
|
||||
href: '/docs/theming',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Icons',
|
||||
href: '/docs/icons',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Figma',
|
||||
href: '/docs/figma',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Changelog',
|
||||
href: '/docs/changelog',
|
||||
items: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Fragment Components',
|
||||
items: [
|
||||
{
|
||||
title: 'Text Confirm Dialog',
|
||||
href: '/docs/fragments/text-confirm-dialog',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Form Item Layout',
|
||||
href: '/docs/fragments/form-item-layout',
|
||||
items: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Atom Components',
|
||||
items: [
|
||||
{
|
||||
title: 'Accordion',
|
||||
href: '/docs/components/accordion',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Alert',
|
||||
href: '/docs/components/alert',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Alert Dialog',
|
||||
href: '/docs/components/alert-dialog',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Aspect Ratio',
|
||||
href: '/docs/components/aspect-ratio',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Avatar',
|
||||
href: '/docs/components/avatar',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Badge',
|
||||
href: '/docs/components/badge',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Breadcrumb',
|
||||
href: '/docs/components/breadcrumb',
|
||||
items: [],
|
||||
label: 'New',
|
||||
},
|
||||
{
|
||||
title: 'Button',
|
||||
href: '/docs/components/button',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Calendar',
|
||||
href: '/docs/components/calendar',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Card',
|
||||
href: '/docs/components/card',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Carousel',
|
||||
href: '/docs/components/carousel',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Checkbox',
|
||||
href: '/docs/components/checkbox',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Collapsible',
|
||||
href: '/docs/components/collapsible',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Combobox',
|
||||
href: '/docs/components/combobox',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Command',
|
||||
href: '/docs/components/command',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Context Menu',
|
||||
href: '/docs/components/context-menu',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Data Table',
|
||||
href: '/docs/components/data-table',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Date Picker',
|
||||
href: '/docs/components/date-picker',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Dialog',
|
||||
href: '/docs/components/dialog',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Drawer',
|
||||
href: '/docs/components/drawer',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Dropdown Menu',
|
||||
href: '/docs/components/dropdown-menu',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Form',
|
||||
href: '/docs/components/form',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Hover Card',
|
||||
href: '/docs/components/hover-card',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Input',
|
||||
href: '/docs/components/input',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Input OTP',
|
||||
href: '/docs/components/input-otp',
|
||||
items: [],
|
||||
label: 'New',
|
||||
},
|
||||
{
|
||||
title: 'Label',
|
||||
href: '/docs/components/label',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Menubar',
|
||||
href: '/docs/components/menubar',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Navigation Menu',
|
||||
href: '/docs/components/navigation-menu',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Pagination',
|
||||
href: '/docs/components/pagination',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Popover',
|
||||
href: '/docs/components/popover',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Progress',
|
||||
href: '/docs/components/progress',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Radio Group',
|
||||
href: '/docs/components/radio-group',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Resizable',
|
||||
href: '/docs/components/resizable',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Scroll Area',
|
||||
href: '/docs/components/scroll-area',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Select',
|
||||
href: '/docs/components/select',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Separator',
|
||||
href: '/docs/components/separator',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Sheet',
|
||||
href: '/docs/components/sheet',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Skeleton',
|
||||
href: '/docs/components/skeleton',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Slider',
|
||||
href: '/docs/components/slider',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Sonner',
|
||||
href: '/docs/components/sonner',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Switch',
|
||||
href: '/docs/components/switch',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Table',
|
||||
href: '/docs/components/table',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Tabs',
|
||||
href: '/docs/components/tabs',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Textarea',
|
||||
href: '/docs/components/textarea',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Toast',
|
||||
href: '/docs/components/toast',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Toggle',
|
||||
href: '/docs/components/toggle',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Toggle Group',
|
||||
href: '/docs/components/toggle-group',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Tooltip',
|
||||
href: '/docs/components/tooltip',
|
||||
items: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
17
apps/design-system/config/site.ts
Normal file
17
apps/design-system/config/site.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
export const siteConfig = {
|
||||
name: 'Supabase Design System',
|
||||
url: 'https://supabase.com/design-system',
|
||||
ogImage: 'https://supabase.com/design-system/og.jpg',
|
||||
description: 'Design System of Supabase',
|
||||
links: {
|
||||
twitter: 'https://twitter.com/supabase',
|
||||
github: 'https://github.com/supabase/supabase/apps/design-system',
|
||||
credits: {
|
||||
radix: 'https://www.radix-ui.com/themes/docs/overview/getting-started',
|
||||
shadcn: 'https://ui.shadcn.com/',
|
||||
geist: 'https://vercel.com/geist/introduction',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export type SiteConfig = typeof siteConfig
|
||||
31
apps/design-system/content/docs/changelog.mdx
Normal file
31
apps/design-system/content/docs/changelog.mdx
Normal file
@@ -0,0 +1,31 @@
|
||||
---
|
||||
title: Changelog
|
||||
description: Latest updates and announcements.
|
||||
toc: false
|
||||
---
|
||||
|
||||
## May 2024 - Introducing Design System
|
||||
|
||||
We're introducing **Design System**, a home for Design related resources for Supabase
|
||||
|
||||
<a href="/blocks">
|
||||
<Image
|
||||
src="/images/lift-mode-light.png"
|
||||
width="1432"
|
||||
height="1050"
|
||||
alt="Lift Mode"
|
||||
className="border dark:hidden shadow-sm rounded-lg overflow-hidden mt-6 w-full"
|
||||
/>
|
||||
<Image
|
||||
src="/images/lift-mode-dark.png"
|
||||
width="1432"
|
||||
height="1069"
|
||||
alt="Lift Mode"
|
||||
className="border hidden dark:block shadow-sm rounded-lg overflow-hidden mt-6 w-full"
|
||||
/>
|
||||
<span class="sr-only">View the blocks library</span>
|
||||
</a>
|
||||
|
||||
With Lift Mode, you'll be able to copy the smaller components that make up a block template, like cards, buttons, and forms, and paste them directly into your project.
|
||||
|
||||
Visit the [Blocks](/blocks) page to try it out.
|
||||
74
apps/design-system/content/docs/color-usage.mdx
Normal file
74
apps/design-system/content/docs/color-usage.mdx
Normal file
@@ -0,0 +1,74 @@
|
||||
---
|
||||
title: Color usage
|
||||
description: Colors system breakdown with best practise
|
||||
---
|
||||
|
||||
Colors available in the Supabase Design System
|
||||
|
||||
These are examples of using colors with shorthands.
|
||||
|
||||
## Background
|
||||
|
||||
<Colors definition={'background'} />
|
||||
|
||||
### App backgrounds
|
||||
|
||||
We use backgrounds in 2 different ways. In the ./www and ./docs sites, we use a darker background, so we have an extra background color we can use
|
||||
|
||||
```jsx
|
||||
/**
|
||||
* ./www background color
|
||||
* ./docs background color
|
||||
*/
|
||||
<body className="bg">{children}</body>
|
||||
|
||||
/**
|
||||
* ./studio background color
|
||||
*/
|
||||
<body className="bg-studio">{children}</body>
|
||||
```
|
||||
|
||||
### Backgrounds and Surfaces
|
||||
|
||||
#### `./apps/www` + `./apps/docs`
|
||||
|
||||
We use surfaces in 2 different ways. In the ./www and ./docs sites, we use a darker background, so we have an extra surface color we can use
|
||||
|
||||
<CodeFragment name="color-usage-surface-www-and-docs" />
|
||||
|
||||
#### `./apps/studio`
|
||||
|
||||
For the studio (dashbaord) we can use `bg-surface-100`, `bg-surface-200`, `bg-surface-300`
|
||||
|
||||
<CodeFragment name="color-usage-surface-studio" />
|
||||
|
||||
#### Data grid and frame space
|
||||
|
||||
Data grids use an alternative background color for empty space to add depth to the layout.
|
||||
The background of the empty space is the same background as used in `./apps/docs` and `./apps/www` - although; the color has been mapped to `bg-alternative` so it works well across different themes.
|
||||
|
||||
<CodeFragment name="color-usage-surface-studio-frame" />
|
||||
|
||||
Dealing with large areas of emmpty space in data display should also be catered for. You can use the `bg-200` or `bg` class to fill the space.
|
||||
|
||||
### Overlays
|
||||
|
||||
We use the `./bg-overlay` background color for overlays.
|
||||
This is not to be confused with `Dialogs`, they require to use the same app background color as the site.
|
||||
|
||||
## Border
|
||||
|
||||
<Colors definition={'border'} />
|
||||
|
||||
## Text
|
||||
|
||||
These can also be accessed with `foreground`. Like `text-foreground-light`.
|
||||
|
||||
<Colors definition={'text'} />
|
||||
|
||||
## Other Colors
|
||||
|
||||
These can also be accessed with `foreground`. Like `text-foreground-light`.
|
||||
|
||||
<Colors definition={'colors'} />
|
||||
```
|
||||
139
apps/design-system/content/docs/components/accordion.mdx
Normal file
139
apps/design-system/content/docs/components/accordion.mdx
Normal file
@@ -0,0 +1,139 @@
|
||||
---
|
||||
title: Accordion
|
||||
description: A vertically stacked set of interactive headings that each reveal a section of content.
|
||||
component: true
|
||||
links:
|
||||
doc: https://www.radix-ui.com/docs/primitives/components/accordion
|
||||
api: https://www.radix-ui.com/docs/primitives/components/accordion#api-reference
|
||||
source:
|
||||
radix: true
|
||||
shadcn: true
|
||||
---
|
||||
|
||||
<ComponentPreview
|
||||
name="accordion-demo"
|
||||
className="[&_.preview>[data-orientation=vertical]]:sm:max-w-[70%]"
|
||||
peekCode
|
||||
wide
|
||||
/>
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs defaultValue="cli">
|
||||
|
||||
<TabsList>
|
||||
<TabsTrigger value="cli">CLI</TabsTrigger>
|
||||
<TabsTrigger value="manual">Manual</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="cli">
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step>Run the following command:</Step>
|
||||
|
||||
```bash
|
||||
npx shadcn-ui@latest add accordion
|
||||
```
|
||||
|
||||
<Step>Update `tailwind.config.js`</Step>
|
||||
|
||||
Add the following animations to your `tailwind.config.js` file:
|
||||
|
||||
```js title="tailwind.config.js" {5-18}
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
theme: {
|
||||
extend: {
|
||||
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',
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="manual">
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step>Install the following dependencies:</Step>
|
||||
|
||||
```bash
|
||||
npm install @radix-ui/react-accordion
|
||||
```
|
||||
|
||||
<Step>Copy and paste the following code into your project.</Step>
|
||||
|
||||
{/* <ComponentSource name="accordion" /> */}
|
||||
|
||||
<Step>Update the import paths to match your project setup.</Step>
|
||||
|
||||
<Step>Update `tailwind.config.js`</Step>
|
||||
|
||||
Add the following animations to your `tailwind.config.js` file:
|
||||
|
||||
```js title="tailwind.config.js" {5-18}
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
theme: {
|
||||
extend: {
|
||||
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',
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
</Tabs>
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import {
|
||||
Accordion,
|
||||
AccordionContent,
|
||||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
} from '@/components/ui/accordion'
|
||||
```
|
||||
|
||||
```tsx
|
||||
<Accordion type="single" collapsible>
|
||||
<AccordionItem value="item-1">
|
||||
<AccordionTrigger>Is it accessible?</AccordionTrigger>
|
||||
<AccordionContent>Yes. It adheres to the WAI-ARIA design pattern.</AccordionContent>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
```
|
||||
65
apps/design-system/content/docs/components/alert copy.mdx
Normal file
65
apps/design-system/content/docs/components/alert copy.mdx
Normal file
@@ -0,0 +1,65 @@
|
||||
---
|
||||
title: Alert
|
||||
description: Displays a callout for user attention.
|
||||
component: true
|
||||
---
|
||||
|
||||
<ComponentPreview name="alert-demo" peekCode wide />
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs defaultValue="cli">
|
||||
|
||||
<TabsList>
|
||||
<TabsTrigger value="cli">CLI</TabsTrigger>
|
||||
<TabsTrigger value="manual">Manual</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="cli">
|
||||
|
||||
```bash
|
||||
npx shadcn-ui@latest add alert
|
||||
```
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="manual">
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step>Copy and paste the following code into your project.</Step>
|
||||
|
||||
<ComponentSource name="alert" />
|
||||
|
||||
<Step>Update the import paths to match your project setup.</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
</Tabs>
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'
|
||||
```
|
||||
|
||||
```tsx
|
||||
<Alert>
|
||||
<Terminal className="h-4 w-4" />
|
||||
<AlertTitle>Heads up!</AlertTitle>
|
||||
<AlertDescription>
|
||||
You can add components and dependencies to your app using the cli.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Default
|
||||
|
||||
<ComponentPreview name="alert-demo" peekCode wide />
|
||||
|
||||
### Destructive
|
||||
|
||||
<ComponentPreview name="alert-destructive" />
|
||||
87
apps/design-system/content/docs/components/alert-dialog.mdx
Normal file
87
apps/design-system/content/docs/components/alert-dialog.mdx
Normal file
@@ -0,0 +1,87 @@
|
||||
---
|
||||
title: Alert Dialog
|
||||
description: A modal dialog that interrupts the user with important content and expects a response.
|
||||
featured: true
|
||||
component: true
|
||||
links:
|
||||
doc: https://www.radix-ui.com/docs/primitives/components/alert-dialog
|
||||
api: https://www.radix-ui.com/docs/primitives/components/alert-dialog#api-reference
|
||||
source:
|
||||
radix: true
|
||||
shadcn: true
|
||||
---
|
||||
|
||||
<ComponentPreview name="alert-dialog-demo" peekCode wide />
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs defaultValue="cli">
|
||||
|
||||
<TabsList>
|
||||
<TabsTrigger value="cli">CLI</TabsTrigger>
|
||||
<TabsTrigger value="manual">Manual</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="cli">
|
||||
|
||||
```bash
|
||||
npx shadcn-ui@latest add alert-dialog
|
||||
```
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="manual">
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step>Install the following dependencies:</Step>
|
||||
|
||||
```bash
|
||||
npm install @radix-ui/react-alert-dialog
|
||||
```
|
||||
|
||||
<Step>Copy and paste the following code into your project.</Step>
|
||||
|
||||
<ComponentSource name="alert-dialog" />
|
||||
|
||||
<Step>Update the import paths to match your project setup.</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
</Tabs>
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
AlertDialogCancel,
|
||||
AlertDialogContent,
|
||||
AlertDialogDescription,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
AlertDialogTrigger,
|
||||
} from '@/components/ui/alert-dialog'
|
||||
```
|
||||
|
||||
```tsx
|
||||
<AlertDialog>
|
||||
<AlertDialogTrigger>Open</AlertDialogTrigger>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
This action cannot be undone. This will permanently delete your account and remove your data
|
||||
from our servers.
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
||||
<AlertDialogAction>Continue</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
```
|
||||
79
apps/design-system/content/docs/components/alert.mdx
Normal file
79
apps/design-system/content/docs/components/alert.mdx
Normal file
@@ -0,0 +1,79 @@
|
||||
---
|
||||
title: Alert
|
||||
description: Displays a callout for user attention.
|
||||
component: true
|
||||
source:
|
||||
radix: true
|
||||
shadcn: true
|
||||
---
|
||||
|
||||
<ComponentPreview
|
||||
name="alert-demo"
|
||||
description="An alert with an icon, title and description. The title says 'Heads up!' and the description is 'You can add components to your app using the cli.'."
|
||||
peekCode
|
||||
wide
|
||||
/>
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs defaultValue="cli">
|
||||
|
||||
<TabsList>
|
||||
<TabsTrigger value="cli">CLI</TabsTrigger>
|
||||
<TabsTrigger value="manual">Manual</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="cli">
|
||||
|
||||
```bash
|
||||
npx shadcn-ui@latest add alert
|
||||
```
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="manual">
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step>Copy and paste the following code into your project.</Step>
|
||||
|
||||
<ComponentSource name="alert" />
|
||||
|
||||
<Step>Update the import paths to match your project setup.</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
</Tabs>
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'
|
||||
```
|
||||
|
||||
```tsx
|
||||
<Alert>
|
||||
<Terminal className="h-4 w-4" />
|
||||
<AlertTitle>Heads up!</AlertTitle>
|
||||
<AlertDescription>
|
||||
You can add components and dependencies to your app using the cli.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Default
|
||||
|
||||
<ComponentPreview
|
||||
name="alert-demo"
|
||||
description="An alert with an icon, title and description. The title says 'Heads up!' and the description is 'You can add components to your app using the cli.'."
|
||||
/>
|
||||
|
||||
### Destructive
|
||||
|
||||
<ComponentPreview
|
||||
name="alert-destructive"
|
||||
description="An alert with a destructive variant. The title says 'Delete this item?' and the description is 'This action cannot be undone.'."
|
||||
/>
|
||||
67
apps/design-system/content/docs/components/aspect-ratio.mdx
Normal file
67
apps/design-system/content/docs/components/aspect-ratio.mdx
Normal file
@@ -0,0 +1,67 @@
|
||||
---
|
||||
title: Aspect Ratio
|
||||
description: Displays content within a desired ratio.
|
||||
component: true
|
||||
links:
|
||||
doc: https://www.radix-ui.com/docs/primitives/components/aspect-ratio
|
||||
api: https://www.radix-ui.com/docs/primitives/components/aspect-ratio#api-reference
|
||||
source:
|
||||
radix: true
|
||||
shadcn: true
|
||||
---
|
||||
|
||||
<ComponentPreview name="aspect-ratio-demo" peekCode wide />
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs defaultValue="cli">
|
||||
|
||||
<TabsList>
|
||||
<TabsTrigger value="cli">CLI</TabsTrigger>
|
||||
<TabsTrigger value="manual">Manual</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="cli">
|
||||
|
||||
```bash
|
||||
npx shadcn-ui@latest add aspect-ratio
|
||||
```
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="manual">
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step>Install the following dependencies:</Step>
|
||||
|
||||
```bash
|
||||
npm install @radix-ui/react-aspect-ratio
|
||||
```
|
||||
|
||||
<Step>Copy and paste the following code into your project.</Step>
|
||||
|
||||
<ComponentSource name="aspect-ratio" />
|
||||
|
||||
<Step>Update the import paths to match your project setup.</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
</Tabs>
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import Image from 'next/image'
|
||||
|
||||
import { AspectRatio } from '@/components/ui/aspect-ratio'
|
||||
```
|
||||
|
||||
```tsx
|
||||
<div className="w-[450px]">
|
||||
<AspectRatio ratio={16 / 9}>
|
||||
<Image src="..." alt="Image" className="rounded-md object-cover" />
|
||||
</AspectRatio>
|
||||
</div>
|
||||
```
|
||||
64
apps/design-system/content/docs/components/avatar.mdx
Normal file
64
apps/design-system/content/docs/components/avatar.mdx
Normal file
@@ -0,0 +1,64 @@
|
||||
---
|
||||
title: Avatar
|
||||
description: An image element with a fallback for representing the user.
|
||||
component: true
|
||||
links:
|
||||
doc: https://www.radix-ui.com/docs/primitives/components/avatar
|
||||
api: https://www.radix-ui.com/docs/primitives/components/avatar#api-reference
|
||||
source:
|
||||
radix: true
|
||||
shadcn: true
|
||||
---
|
||||
|
||||
<ComponentPreview name="avatar-demo" peekCode wide />
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs defaultValue="cli">
|
||||
|
||||
<TabsList>
|
||||
<TabsTrigger value="cli">CLI</TabsTrigger>
|
||||
<TabsTrigger value="manual">Manual</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="cli">
|
||||
|
||||
```bash
|
||||
npx shadcn-ui@latest add avatar
|
||||
```
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="manual">
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step>Install the following dependencies:</Step>
|
||||
|
||||
```bash
|
||||
npm install @radix-ui/react-avatar
|
||||
```
|
||||
|
||||
<Step>Copy and paste the following code into your project.</Step>
|
||||
|
||||
<ComponentSource name="avatar" />
|
||||
|
||||
<Step>Update the import paths to match your project setup.</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
</Tabs>
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
|
||||
```
|
||||
|
||||
```tsx
|
||||
<Avatar>
|
||||
<AvatarImage src="https://github.com/mildtomato.png" />
|
||||
<AvatarFallback>CN</AvatarFallback>
|
||||
</Avatar>
|
||||
```
|
||||
87
apps/design-system/content/docs/components/badge.mdx
Normal file
87
apps/design-system/content/docs/components/badge.mdx
Normal file
@@ -0,0 +1,87 @@
|
||||
---
|
||||
title: Badge
|
||||
description: Displays a badge or a component that looks like a badge.
|
||||
component: true
|
||||
source:
|
||||
shadcn: true
|
||||
---
|
||||
|
||||
<ComponentPreview name="badge-demo" peekCode wide />
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs defaultValue="cli">
|
||||
|
||||
<TabsList>
|
||||
<TabsTrigger value="cli">CLI</TabsTrigger>
|
||||
<TabsTrigger value="manual">Manual</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="cli">
|
||||
|
||||
```bash
|
||||
npx shadcn-ui@latest add badge
|
||||
```
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="manual">
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step>Copy and paste the following code into your project.</Step>
|
||||
|
||||
<ComponentSource name="badge" />
|
||||
|
||||
<Step>Update the import paths to match your project setup.</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
</Tabs>
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
```
|
||||
|
||||
```tsx
|
||||
<Badge variant="outline">Badge</Badge>
|
||||
```
|
||||
|
||||
### Link
|
||||
|
||||
You can use the `badgeVariants` helper to create a link that looks like a badge.
|
||||
|
||||
```tsx
|
||||
import { badgeVariants } from '@/components/ui/badge'
|
||||
```
|
||||
|
||||
```tsx
|
||||
<Link className={badgeVariants({ variant: 'outline' })}>Badge</Link>
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Default
|
||||
|
||||
<ComponentPreview name="badge-demo" peekCode wide />
|
||||
|
||||
---
|
||||
|
||||
### Secondary
|
||||
|
||||
<ComponentPreview name="badge-secondary" />
|
||||
|
||||
---
|
||||
|
||||
### Outline
|
||||
|
||||
<ComponentPreview name="badge-outline" />
|
||||
|
||||
---
|
||||
|
||||
### Destructive
|
||||
|
||||
<ComponentPreview name="badge-destructive" />
|
||||
190
apps/design-system/content/docs/components/breadcrumb.mdx
Normal file
190
apps/design-system/content/docs/components/breadcrumb.mdx
Normal file
@@ -0,0 +1,190 @@
|
||||
---
|
||||
title: Breadcrumb
|
||||
description: Displays the path to the current resource using a hierarchy of links.
|
||||
component: true
|
||||
---
|
||||
|
||||
<ComponentPreview name="breadcrumb-demo" className="[&_.preview]:p-2" peekCode wide />
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs defaultValue="cli">
|
||||
|
||||
<TabsList>
|
||||
<TabsTrigger value="cli">CLI</TabsTrigger>
|
||||
<TabsTrigger value="manual">Manual</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="cli">
|
||||
|
||||
```bash
|
||||
npx shadcn-ui@latest add breadcrumb
|
||||
```
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="manual">
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step>Copy and paste the following code into your project.</Step>
|
||||
|
||||
<ComponentSource name="breadcrumb" />
|
||||
|
||||
<Step>Update the import paths to match your project setup.</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
</Tabs>
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import {
|
||||
Breadcrumb,
|
||||
BreadcrumbItem,
|
||||
BreadcrumbLink,
|
||||
BreadcrumbList,
|
||||
BreadcrumbPage,
|
||||
BreadcrumbSeparator,
|
||||
} from '@/components/ui/breadcrumb'
|
||||
```
|
||||
|
||||
```tsx
|
||||
<Breadcrumb>
|
||||
<BreadcrumbList>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink href="/">Home</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink href="/components">Components</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbPage>Breadcrumb</BreadcrumbPage>
|
||||
</BreadcrumbItem>
|
||||
</BreadcrumbList>
|
||||
</Breadcrumb>
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Custom separator
|
||||
|
||||
Use a custom component as `children` for `<BreadcrumbSeparator />` to create a custom separator.
|
||||
|
||||
<ComponentPreview name="breadcrumb-separator" />
|
||||
|
||||
```tsx showLineNumbers {1,10-12}
|
||||
import { Slash } from "lucide-react"
|
||||
|
||||
...
|
||||
|
||||
<Breadcrumb>
|
||||
<BreadcrumbList>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink href="/">Home</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator>
|
||||
<Slash />
|
||||
</BreadcrumbSeparator>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink href="/components">Components</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
</BreadcrumbList>
|
||||
</Breadcrumb>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Dropdown
|
||||
|
||||
You can compose `<BreadcrumbItem />` with a `<DropdownMenu />` to create a dropdown in the breadcrumb.
|
||||
|
||||
<ComponentPreview name="breadcrumb-dropdown" className="[&_.preview]:p-2" />
|
||||
|
||||
```tsx showLineNumbers {1-6,11-21}
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu"
|
||||
|
||||
...
|
||||
|
||||
<BreadcrumbItem>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger className="flex items-center gap-1">
|
||||
Components
|
||||
<ChevronDownIcon />
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="start">
|
||||
<DropdownMenuItem>Documentation</DropdownMenuItem>
|
||||
<DropdownMenuItem>Themes</DropdownMenuItem>
|
||||
<DropdownMenuItem>GitHub</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</BreadcrumbItem>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Collapsed
|
||||
|
||||
We provide a `<BreadcrumbEllipsis />` component to show a collapsed state when the breadcrumb is too long.
|
||||
|
||||
<ComponentPreview name="breadcrumb-ellipsis" className="[&_.preview]:p-2" />
|
||||
|
||||
```tsx showLineNumbers {1,9}
|
||||
import { BreadcrumbEllipsis } from "@/components/ui/breadcrumb"
|
||||
|
||||
...
|
||||
|
||||
<Breadcrumb>
|
||||
<BreadcrumbList>
|
||||
{/* ... */}
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbEllipsis />
|
||||
</BreadcrumbItem>
|
||||
{/* ... */}
|
||||
</BreadcrumbList>
|
||||
</Breadcrumb>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Link component
|
||||
|
||||
To use a custom link component from your routing library, you can use the `asChild` prop on `<BreadcrumbLink />`.
|
||||
|
||||
<ComponentPreview name="breadcrumb-link" />
|
||||
|
||||
```tsx showLineNumbers {1,8-10}
|
||||
import { Link } from "next/link"
|
||||
|
||||
...
|
||||
|
||||
<Breadcrumb>
|
||||
<BreadcrumbList>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink asChild>
|
||||
<Link href="/">Home</Link>
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
{/* ... */}
|
||||
</BreadcrumbList>
|
||||
</Breadcrumb>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Responsive
|
||||
|
||||
Here's an example of a responsive breadcrumb that composes `<BreadcrumbItem />` with `<BreadcrumbEllipsis />`, `<DropdownMenu />`, and `<Drawer />`.
|
||||
|
||||
It displays a dropdown on desktop and a drawer on mobile.
|
||||
|
||||
<ComponentPreview name="breadcrumb-responsive" className="[&_.preview]:p-2" />
|
||||
118
apps/design-system/content/docs/components/button.mdx
Normal file
118
apps/design-system/content/docs/components/button.mdx
Normal file
@@ -0,0 +1,118 @@
|
||||
---
|
||||
title: Button
|
||||
description: Displays a button or a component that looks like a button.
|
||||
featured: true
|
||||
component: true
|
||||
---
|
||||
|
||||
<ComponentPreview name="button-demo" peekCode wide />
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs defaultValue="cli">
|
||||
|
||||
<TabsList>
|
||||
<TabsTrigger value="cli">CLI</TabsTrigger>
|
||||
<TabsTrigger value="manual">Manual</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="cli">
|
||||
|
||||
```bash
|
||||
npx shadcn-ui@latest add button
|
||||
```
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="manual">
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step>Install the following dependencies:</Step>
|
||||
|
||||
```bash
|
||||
npm install @radix-ui/react-slot
|
||||
```
|
||||
|
||||
<Step>Copy and paste the following code into your project.</Step>
|
||||
|
||||
<ComponentSource name="button" />
|
||||
|
||||
<Step>Update the import paths to match your project setup.</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
</Tabs>
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import { Button } from '@/components/ui/button'
|
||||
```
|
||||
|
||||
```tsx
|
||||
<Button variant="outline">Button</Button>
|
||||
```
|
||||
|
||||
## Link
|
||||
|
||||
You can use the `buttonVariants` helper to create a link that looks like a button.
|
||||
|
||||
```tsx
|
||||
import { buttonVariants } from '@/components/ui/button'
|
||||
```
|
||||
|
||||
```tsx
|
||||
<Link className={buttonVariants({ variant: 'outline' })}>Click here</Link>
|
||||
```
|
||||
|
||||
Alternatively, you can set the `asChild` parameter and nest the link component.
|
||||
|
||||
```tsx
|
||||
<Button asChild>
|
||||
<Link href="/login">Login</Link>
|
||||
</Button>
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Primary
|
||||
|
||||
<ComponentPreview name="button-demo" peekCode wide />
|
||||
|
||||
### Secondary
|
||||
|
||||
<ComponentPreview name="button-secondary" />
|
||||
|
||||
### Destructive
|
||||
|
||||
<ComponentPreview name="button-destructive" />
|
||||
|
||||
### Outline
|
||||
|
||||
<ComponentPreview name="button-outline" />
|
||||
|
||||
### Ghost
|
||||
|
||||
<ComponentPreview name="button-ghost" />
|
||||
|
||||
### Link
|
||||
|
||||
<ComponentPreview name="button-link" />
|
||||
|
||||
### Icon
|
||||
|
||||
<ComponentPreview name="button-icon" />
|
||||
|
||||
### With Icon
|
||||
|
||||
<ComponentPreview name="button-with-icon" />
|
||||
|
||||
### Loading
|
||||
|
||||
<ComponentPreview name="button-loading" />
|
||||
|
||||
### As Child
|
||||
|
||||
<ComponentPreview name="button-as-child" />
|
||||
81
apps/design-system/content/docs/components/calendar.mdx
Normal file
81
apps/design-system/content/docs/components/calendar.mdx
Normal file
@@ -0,0 +1,81 @@
|
||||
---
|
||||
title: Calendar
|
||||
description: A date field component that allows users to enter and edit date.
|
||||
component: true
|
||||
links:
|
||||
doc: https://react-day-picker.js.org
|
||||
source:
|
||||
shadcn: true
|
||||
---
|
||||
|
||||
<ComponentPreview name="calendar-demo" peekCode wide />
|
||||
|
||||
## About
|
||||
|
||||
The `Calendar` component is built on top of [React DayPicker](https://react-day-picker.js.org).
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs defaultValue="cli">
|
||||
|
||||
<TabsList>
|
||||
<TabsTrigger value="cli">CLI</TabsTrigger>
|
||||
<TabsTrigger value="manual">Manual</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="cli">
|
||||
|
||||
```bash
|
||||
npx shadcn-ui@latest add calendar
|
||||
```
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="manual">
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step>Install the following dependencies:</Step>
|
||||
|
||||
```bash
|
||||
npm install react-day-picker date-fns
|
||||
```
|
||||
|
||||
<Step>Add the `Button` component to your project.</Step>
|
||||
|
||||
The `Calendar` component uses the `Button` component. Make sure you have it installed in your project.
|
||||
|
||||
<Step>Copy and paste the following code into your project.</Step>
|
||||
|
||||
<ComponentSource name="calendar" />
|
||||
|
||||
<Step>Update the import paths to match your project setup.</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
</Tabs>
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import { Calendar } from '@/components/ui/calendar'
|
||||
```
|
||||
|
||||
```tsx
|
||||
const [date, setDate] = React.useState<Date | undefined>(new Date())
|
||||
|
||||
return <Calendar mode="single" selected={date} onSelect={setDate} className="rounded-md border" />
|
||||
```
|
||||
|
||||
See the [React DayPicker](https://react-day-picker.js.org) documentation for more information.
|
||||
|
||||
## Date Picker
|
||||
|
||||
You can use the `<Calendar>` component to build a date picker. See the [Date Picker](/docs/components/date-picker) page for more information.
|
||||
|
||||
## Examples
|
||||
|
||||
### Form
|
||||
|
||||
<ComponentPreview name="calendar-form" />
|
||||
71
apps/design-system/content/docs/components/card.mdx
Normal file
71
apps/design-system/content/docs/components/card.mdx
Normal file
@@ -0,0 +1,71 @@
|
||||
---
|
||||
title: Card
|
||||
description: Displays a card with header, content, and footer.
|
||||
component: true
|
||||
---
|
||||
|
||||
<ComponentPreview name="card-with-form" />
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs defaultValue="cli">
|
||||
|
||||
<TabsList>
|
||||
<TabsTrigger value="cli">CLI</TabsTrigger>
|
||||
<TabsTrigger value="manual">Manual</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="cli">
|
||||
|
||||
```bash
|
||||
npx shadcn-ui@latest add card
|
||||
```
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="manual">
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step>Copy and paste the following code into your project.</Step>
|
||||
|
||||
<ComponentSource name="card" />
|
||||
|
||||
<Step>Update the import paths to match your project setup.</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
</Tabs>
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from '@/components/ui/card'
|
||||
```
|
||||
|
||||
```tsx
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Card Title</CardTitle>
|
||||
<CardDescription>Card Description</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p>Card Content</p>
|
||||
</CardContent>
|
||||
<CardFooter>
|
||||
<p>Card Footer</p>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
<ComponentPreview name="card-demo" peekCode wide />
|
||||
277
apps/design-system/content/docs/components/carousel.mdx
Normal file
277
apps/design-system/content/docs/components/carousel.mdx
Normal file
@@ -0,0 +1,277 @@
|
||||
---
|
||||
title: Carousel
|
||||
description: A carousel with motion and swipe built using Embla.
|
||||
component: true
|
||||
links:
|
||||
doc: https://www.embla-carousel.com/get-started/react
|
||||
api: https://www.embla-carousel.com/api
|
||||
---
|
||||
|
||||
<ComponentPreview name="carousel-demo" peekCode wide />
|
||||
|
||||
## About
|
||||
|
||||
The carousel component is built using the [Embla Carousel](https://www.embla-carousel.com/) library.
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs defaultValue="cli">
|
||||
|
||||
<TabsList>
|
||||
<TabsTrigger value="cli">CLI</TabsTrigger>
|
||||
<TabsTrigger value="manual">Manual</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="cli">
|
||||
|
||||
```bash
|
||||
npx shadcn-ui@latest add carousel
|
||||
```
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="manual">
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step>Install the following dependencies:</Step>
|
||||
|
||||
```bash
|
||||
npm install embla-carousel-react
|
||||
```
|
||||
|
||||
<Step>Copy and paste the following code into your project.</Step>
|
||||
|
||||
<ComponentSource name="carousel" />
|
||||
|
||||
<Step>Update the import paths to match your project setup.</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
</Tabs>
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import {
|
||||
Carousel,
|
||||
CarouselContent,
|
||||
CarouselItem,
|
||||
CarouselNext,
|
||||
CarouselPrevious,
|
||||
} from '@/components/ui/carousel'
|
||||
```
|
||||
|
||||
```tsx
|
||||
<Carousel>
|
||||
<CarouselContent>
|
||||
<CarouselItem>...</CarouselItem>
|
||||
<CarouselItem>...</CarouselItem>
|
||||
<CarouselItem>...</CarouselItem>
|
||||
</CarouselContent>
|
||||
<CarouselPrevious />
|
||||
<CarouselNext />
|
||||
</Carousel>
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Sizes
|
||||
|
||||
To set the size of the items, you can use the `basis` utility class on the `<CarouselItem />`.
|
||||
|
||||
<ComponentPreview name="carousel-size" />
|
||||
|
||||
```tsx title="Example" showLineNumbers {4-6}
|
||||
// 33% of the carousel width.
|
||||
<Carousel>
|
||||
<CarouselContent>
|
||||
<CarouselItem className="basis-1/3">...</CarouselItem>
|
||||
<CarouselItem className="basis-1/3">...</CarouselItem>
|
||||
<CarouselItem className="basis-1/3">...</CarouselItem>
|
||||
</CarouselContent>
|
||||
</Carousel>
|
||||
```
|
||||
|
||||
```tsx title="Responsive" showLineNumbers {4-6}
|
||||
// 50% on small screens and 33% on larger screens.
|
||||
<Carousel>
|
||||
<CarouselContent>
|
||||
<CarouselItem className="md:basis-1/2 lg:basis-1/3">...</CarouselItem>
|
||||
<CarouselItem className="md:basis-1/2 lg:basis-1/3">...</CarouselItem>
|
||||
<CarouselItem className="md:basis-1/2 lg:basis-1/3">...</CarouselItem>
|
||||
</CarouselContent>
|
||||
</Carousel>
|
||||
```
|
||||
|
||||
### Spacing
|
||||
|
||||
To set the spacing between the items, we use a `pl-[VALUE]` utility on the `<CarouselItem />` and a negative `-ml-[VALUE]` on the `<CarouselContent />`.
|
||||
|
||||
<Callout className="mt-6">
|
||||
**Why:** I tried to use the `gap` property or a `grid` layout on the `
|
||||
<CarouselContent />` but it required a lot of math and mental effort to get the
|
||||
spacing right. I found `pl-[VALUE]` and `-ml-[VALUE]` utilities much easier to
|
||||
use.
|
||||
|
||||
You can always adjust this in your own project if you need to.
|
||||
|
||||
</Callout>
|
||||
|
||||
<ComponentPreview name="carousel-spacing" />
|
||||
|
||||
```tsx title="Example" showLineNumbers /-ml-4/ /pl-4/
|
||||
<Carousel>
|
||||
<CarouselContent className="-ml-4">
|
||||
<CarouselItem className="pl-4">...</CarouselItem>
|
||||
<CarouselItem className="pl-4">...</CarouselItem>
|
||||
<CarouselItem className="pl-4">...</CarouselItem>
|
||||
</CarouselContent>
|
||||
</Carousel>
|
||||
```
|
||||
|
||||
```tsx title="Responsive" showLineNumbers /-ml-2/ /pl-2/ /md:-ml-4/ /md:pl-4/
|
||||
<Carousel>
|
||||
<CarouselContent className="-ml-2 md:-ml-4">
|
||||
<CarouselItem className="pl-2 md:pl-4">...</CarouselItem>
|
||||
<CarouselItem className="pl-2 md:pl-4">...</CarouselItem>
|
||||
<CarouselItem className="pl-2 md:pl-4">...</CarouselItem>
|
||||
</CarouselContent>
|
||||
</Carousel>
|
||||
```
|
||||
|
||||
### Orientation
|
||||
|
||||
Use the `orientation` prop to set the orientation of the carousel.
|
||||
|
||||
<ComponentPreview name="carousel-orientation" />
|
||||
|
||||
```tsx showLineNumbers /vertical | horizontal/
|
||||
<Carousel orientation="vertical | horizontal">
|
||||
<CarouselContent>
|
||||
<CarouselItem>...</CarouselItem>
|
||||
<CarouselItem>...</CarouselItem>
|
||||
<CarouselItem>...</CarouselItem>
|
||||
</CarouselContent>
|
||||
</Carousel>
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
You can pass options to the carousel using the `opts` prop. See the [Embla Carousel docs](https://www.embla-carousel.com/api/options/) for more information.
|
||||
|
||||
```tsx showLineNumbers {2-5}
|
||||
<Carousel
|
||||
opts={{
|
||||
align: 'start',
|
||||
loop: true,
|
||||
}}
|
||||
>
|
||||
<CarouselContent>
|
||||
<CarouselItem>...</CarouselItem>
|
||||
<CarouselItem>...</CarouselItem>
|
||||
<CarouselItem>...</CarouselItem>
|
||||
</CarouselContent>
|
||||
</Carousel>
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
Use a state and the `setApi` props to get an instance of the carousel API.
|
||||
|
||||
<ComponentPreview name="carousel-api" />
|
||||
|
||||
```tsx showLineNumbers {1,4,22}
|
||||
import { type CarouselApi } from '@/components/ui/carousel'
|
||||
|
||||
export function Example() {
|
||||
const [api, setApi] = React.useState<CarouselApi>()
|
||||
const [current, setCurrent] = React.useState(0)
|
||||
const [count, setCount] = React.useState(0)
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!api) {
|
||||
return
|
||||
}
|
||||
|
||||
setCount(api.scrollSnapList().length)
|
||||
setCurrent(api.selectedScrollSnap() + 1)
|
||||
|
||||
api.on('select', () => {
|
||||
setCurrent(api.selectedScrollSnap() + 1)
|
||||
})
|
||||
}, [api])
|
||||
|
||||
return (
|
||||
<Carousel setApi={setApi}>
|
||||
<CarouselContent>
|
||||
<CarouselItem>...</CarouselItem>
|
||||
<CarouselItem>...</CarouselItem>
|
||||
<CarouselItem>...</CarouselItem>
|
||||
</CarouselContent>
|
||||
</Carousel>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## Events
|
||||
|
||||
You can listen to events using the api instance from `setApi`.
|
||||
|
||||
```tsx showLineNumbers {1,4-14,16}
|
||||
import { type CarouselApi } from '@/components/ui/carousel'
|
||||
|
||||
export function Example() {
|
||||
const [api, setApi] = React.useState<CarouselApi>()
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!api) {
|
||||
return
|
||||
}
|
||||
|
||||
api.on('select', () => {
|
||||
// Do something on select.
|
||||
})
|
||||
}, [api])
|
||||
|
||||
return (
|
||||
<Carousel setApi={setApi}>
|
||||
<CarouselContent>
|
||||
<CarouselItem>...</CarouselItem>
|
||||
<CarouselItem>...</CarouselItem>
|
||||
<CarouselItem>...</CarouselItem>
|
||||
</CarouselContent>
|
||||
</Carousel>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
See the [Embla Carousel docs](https://www.embla-carousel.com/api/events/) for more information on using events.
|
||||
|
||||
## Plugins
|
||||
|
||||
You can use the `plugins` prop to add plugins to the carousel.
|
||||
|
||||
```ts showLineNumbers {1,6-10}
|
||||
import Autoplay from "embla-carousel-autoplay"
|
||||
|
||||
export function Example() {
|
||||
return (
|
||||
<Carousel
|
||||
plugins={[
|
||||
Autoplay({
|
||||
delay: 2000,
|
||||
}),
|
||||
]}
|
||||
>
|
||||
// ...
|
||||
</Carousel>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
<ComponentPreview name="carousel-plugin" />
|
||||
|
||||
See the [Embla Carousel docs](https://www.embla-carousel.com/api/plugins/) for more information on using plugins.
|
||||
77
apps/design-system/content/docs/components/checkbox.mdx
Normal file
77
apps/design-system/content/docs/components/checkbox.mdx
Normal file
@@ -0,0 +1,77 @@
|
||||
---
|
||||
title: Checkbox
|
||||
description: A control that allows the user to toggle between checked and not checked.
|
||||
component: true
|
||||
links:
|
||||
doc: https://www.radix-ui.com/docs/primitives/components/checkbox
|
||||
api: https://www.radix-ui.com/docs/primitives/components/checkbox#api-reference
|
||||
source:
|
||||
radix: true
|
||||
shadcn: true
|
||||
---
|
||||
|
||||
<ComponentPreview name="checkbox-demo" peekCode wide />
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs defaultValue="cli">
|
||||
|
||||
<TabsList>
|
||||
<TabsTrigger value="cli">CLI</TabsTrigger>
|
||||
<TabsTrigger value="manual">Manual</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="cli">
|
||||
|
||||
```bash
|
||||
npx shadcn-ui@latest add checkbox
|
||||
```
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="manual">
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step>Install the following dependencies:</Step>
|
||||
|
||||
```bash
|
||||
npm install @radix-ui/react-checkbox
|
||||
```
|
||||
|
||||
<Step>Copy and paste the following code into your project.</Step>
|
||||
|
||||
<ComponentSource name="checkbox" />
|
||||
|
||||
<Step>Update the import paths to match your project setup.</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
</Tabs>
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import { Checkbox } from '@/components/ui/checkbox'
|
||||
```
|
||||
|
||||
```tsx
|
||||
<Checkbox />
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### With text
|
||||
|
||||
<ComponentPreview name="checkbox-with-text" />
|
||||
|
||||
### Disabled
|
||||
|
||||
<ComponentPreview name="checkbox-disabled" />
|
||||
|
||||
### Form
|
||||
|
||||
<ComponentPreview name="checkbox-form-single" />
|
||||
|
||||
<ComponentPreview name="checkbox-form-multiple" />
|
||||
67
apps/design-system/content/docs/components/collapsible.mdx
Normal file
67
apps/design-system/content/docs/components/collapsible.mdx
Normal file
@@ -0,0 +1,67 @@
|
||||
---
|
||||
title: Collapsible
|
||||
description: An interactive component which expands/collapses a panel.
|
||||
component: true
|
||||
featured: true
|
||||
links:
|
||||
doc: https://www.radix-ui.com/docs/primitives/components/collapsible
|
||||
api: https://www.radix-ui.com/docs/primitives/components/collapsible#api-reference
|
||||
source:
|
||||
radix: true
|
||||
shadcn: true
|
||||
---
|
||||
|
||||
<ComponentPreview name="collapsible-demo" peekCode wide />
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs defaultValue="cli">
|
||||
|
||||
<TabsList>
|
||||
<TabsTrigger value="cli">CLI</TabsTrigger>
|
||||
<TabsTrigger value="manual">Manual</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="cli">
|
||||
|
||||
```bash
|
||||
npx shadcn-ui@latest add collapsible
|
||||
```
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="manual">
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step>Install the following dependencies:</Step>
|
||||
|
||||
```bash
|
||||
npm install @radix-ui/react-collapsible
|
||||
```
|
||||
|
||||
<Step>Copy and paste the following code into your project.</Step>
|
||||
|
||||
<ComponentSource name="collapsible" />
|
||||
|
||||
<Step>Update the import paths to match your project setup.</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
</Tabs>
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible'
|
||||
```
|
||||
|
||||
```tsx
|
||||
<Collapsible>
|
||||
<CollapsibleTrigger>Can I use this in my project?</CollapsibleTrigger>
|
||||
<CollapsibleContent>
|
||||
Yes. Free to use for personal and commercial projects. No attribution required.
|
||||
</CollapsibleContent>
|
||||
</Collapsible>
|
||||
```
|
||||
131
apps/design-system/content/docs/components/combobox.mdx
Normal file
131
apps/design-system/content/docs/components/combobox.mdx
Normal file
@@ -0,0 +1,131 @@
|
||||
---
|
||||
title: Combobox
|
||||
description: Autocomplete input and command palette with a list of suggestions.
|
||||
component: true
|
||||
source:
|
||||
shadcn: true
|
||||
---
|
||||
|
||||
<ComponentPreview name="combobox-demo" peekCode />
|
||||
|
||||
## Installation
|
||||
|
||||
The Combobox is built using a composition of the `<Popover />` and the `<Command />` components.
|
||||
|
||||
See installation instructions for the [Popover](/docs/components/popover#installation) and the [Command](/docs/components/command#installation) components.
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
'use client'
|
||||
|
||||
import * as React from 'react'
|
||||
import { Check, ChevronsUpDown } from 'lucide-react'
|
||||
|
||||
import { cn } from '@/lib/utils'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import {
|
||||
Command,
|
||||
CommandEmpty,
|
||||
CommandGroup,
|
||||
CommandInput,
|
||||
CommandItem,
|
||||
} from '@/components/ui/command'
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
|
||||
|
||||
const frameworks = [
|
||||
{
|
||||
value: 'next.js',
|
||||
label: 'Next.js',
|
||||
},
|
||||
{
|
||||
value: 'sveltekit',
|
||||
label: 'SvelteKit',
|
||||
},
|
||||
{
|
||||
value: 'nuxt.js',
|
||||
label: 'Nuxt.js',
|
||||
},
|
||||
{
|
||||
value: 'remix',
|
||||
label: 'Remix',
|
||||
},
|
||||
{
|
||||
value: 'astro',
|
||||
label: 'Astro',
|
||||
},
|
||||
]
|
||||
|
||||
export function ComboboxDemo() {
|
||||
const [open, setOpen] = React.useState(false)
|
||||
const [value, setValue] = React.useState('')
|
||||
|
||||
return (
|
||||
<Popover open={open} onOpenChange={setOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
role="combobox"
|
||||
aria-expanded={open}
|
||||
className="w-[200px] justify-between"
|
||||
>
|
||||
{value
|
||||
? frameworks.find((framework) => framework.value === value)?.label
|
||||
: 'Select framework...'}
|
||||
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-[200px] p-0">
|
||||
<Command>
|
||||
<CommandInput placeholder="Search framework..." />
|
||||
<CommandEmpty>No framework found.</CommandEmpty>
|
||||
<CommandGroup>
|
||||
{frameworks.map((framework) => (
|
||||
<CommandItem
|
||||
key={framework.value}
|
||||
value={framework.value}
|
||||
onSelect={(currentValue) => {
|
||||
setValue(currentValue === value ? '' : currentValue)
|
||||
setOpen(false)
|
||||
}}
|
||||
>
|
||||
<Check
|
||||
className={cn(
|
||||
'mr-2 h-4 w-4',
|
||||
value === framework.value ? 'opacity-100' : 'opacity-0'
|
||||
)}
|
||||
/>
|
||||
{framework.label}
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
</Command>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Combobox
|
||||
|
||||
<ComponentPreview name="combobox-demo" peekCode wide />
|
||||
|
||||
### Popover
|
||||
|
||||
<ComponentPreview name="combobox-popover" />
|
||||
|
||||
### Dropdown menu
|
||||
|
||||
<ComponentPreview name="combobox-dropdown-menu" />
|
||||
|
||||
### Responsive
|
||||
|
||||
You can create a responsive combobox by using the `<Popover />` on desktop and the `<Drawer />` components on mobile.
|
||||
|
||||
<ComponentPreview name="combobox-responsive" />
|
||||
|
||||
### Form
|
||||
|
||||
<ComponentPreview name="combobox-form" />
|
||||
138
apps/design-system/content/docs/components/command.mdx
Normal file
138
apps/design-system/content/docs/components/command.mdx
Normal file
@@ -0,0 +1,138 @@
|
||||
---
|
||||
title: Command
|
||||
description: Fast, composable, unstyled command menu for React.
|
||||
component: true
|
||||
links:
|
||||
doc: https://cmdk.paco.me
|
||||
source:
|
||||
shadcn: true
|
||||
---
|
||||
|
||||
<ComponentPreview
|
||||
name="command-demo"
|
||||
align="start"
|
||||
className="[&_.preview>div]:max-w-[450px]"
|
||||
peekCode
|
||||
wide
|
||||
/>
|
||||
|
||||
## About
|
||||
|
||||
The `<Command />` component uses the [`cmdk`](https://cmdk.paco.me) component by [pacocoursey](https://twitter.com/pacocoursey).
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs defaultValue="cli">
|
||||
|
||||
<TabsList>
|
||||
<TabsTrigger value="cli">CLI</TabsTrigger>
|
||||
<TabsTrigger value="manual">Manual</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="cli">
|
||||
|
||||
```bash
|
||||
npx shadcn-ui@latest add command
|
||||
```
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="manual">
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step>Install the following dependencies:</Step>
|
||||
|
||||
```bash
|
||||
npm install cmdk
|
||||
```
|
||||
|
||||
<Step>Copy and paste the following code into your project.</Step>
|
||||
|
||||
<ComponentSource name="command" />
|
||||
|
||||
<Step>Update the import paths to match your project setup.</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
</Tabs>
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import {
|
||||
Command,
|
||||
CommandDialog,
|
||||
CommandEmpty,
|
||||
CommandGroup,
|
||||
CommandInput,
|
||||
CommandItem,
|
||||
CommandList,
|
||||
CommandSeparator,
|
||||
CommandShortcut,
|
||||
} from '@/components/ui/command'
|
||||
```
|
||||
|
||||
```tsx
|
||||
<Command>
|
||||
<CommandInput placeholder="Type a command or search..." />
|
||||
<CommandList>
|
||||
<CommandEmpty>No results found.</CommandEmpty>
|
||||
<CommandGroup heading="Suggestions">
|
||||
<CommandItem>Calendar</CommandItem>
|
||||
<CommandItem>Search Emoji</CommandItem>
|
||||
<CommandItem>Calculator</CommandItem>
|
||||
</CommandGroup>
|
||||
<CommandSeparator />
|
||||
<CommandGroup heading="Settings">
|
||||
<CommandItem>Profile</CommandItem>
|
||||
<CommandItem>Billing</CommandItem>
|
||||
<CommandItem>Settings</CommandItem>
|
||||
</CommandGroup>
|
||||
</CommandList>
|
||||
</Command>
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Dialog
|
||||
|
||||
<ComponentPreview name="command-dialog" />
|
||||
|
||||
To show the command menu in a dialog, use the `<CommandDialog />` component.
|
||||
|
||||
```tsx
|
||||
export function CommandMenu() {
|
||||
const [open, setOpen] = React.useState(false)
|
||||
|
||||
React.useEffect(() => {
|
||||
const down = (e: KeyboardEvent) => {
|
||||
if (e.key === 'k' && (e.metaKey || e.ctrlKey)) {
|
||||
e.preventDefault()
|
||||
setOpen((open) => !open)
|
||||
}
|
||||
}
|
||||
document.addEventListener('keydown', down)
|
||||
return () => document.removeEventListener('keydown', down)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<CommandDialog open={open} onOpenChange={setOpen}>
|
||||
<CommandInput placeholder="Type a command or search..." />
|
||||
<CommandList>
|
||||
<CommandEmpty>No results found.</CommandEmpty>
|
||||
<CommandGroup heading="Suggestions">
|
||||
<CommandItem>Calendar</CommandItem>
|
||||
<CommandItem>Search Emoji</CommandItem>
|
||||
<CommandItem>Calculator</CommandItem>
|
||||
</CommandGroup>
|
||||
</CommandList>
|
||||
</CommandDialog>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### Combobox
|
||||
|
||||
You can use the `<Command />` component as a combobox. See the [Combobox](/docs/components/combobox) page for more information.
|
||||
74
apps/design-system/content/docs/components/context-menu.mdx
Normal file
74
apps/design-system/content/docs/components/context-menu.mdx
Normal file
@@ -0,0 +1,74 @@
|
||||
---
|
||||
title: Context Menu
|
||||
description: Displays a menu to the user — such as a set of actions or functions — triggered by a button.
|
||||
component: true
|
||||
links:
|
||||
doc: https://www.radix-ui.com/docs/primitives/components/context-menu
|
||||
api: https://www.radix-ui.com/docs/primitives/components/context-menu#api-reference
|
||||
source:
|
||||
radix: true
|
||||
shadcn: true
|
||||
---
|
||||
|
||||
<ComponentPreview name="context-menu-demo" peekCode wide />
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs defaultValue="cli">
|
||||
|
||||
<TabsList>
|
||||
<TabsTrigger value="cli">CLI</TabsTrigger>
|
||||
<TabsTrigger value="manual">Manual</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="cli">
|
||||
|
||||
```bash
|
||||
npx shadcn-ui@latest add context-menu
|
||||
```
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="manual">
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step>Install the following dependencies:</Step>
|
||||
|
||||
```bash
|
||||
npm install @radix-ui/react-context-menu
|
||||
```
|
||||
|
||||
<Step>Copy and paste the following code into your project.</Step>
|
||||
|
||||
<ComponentSource name="context-menu" />
|
||||
|
||||
<Step>Update the import paths to match your project setup.</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
</Tabs>
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import {
|
||||
ContextMenu,
|
||||
ContextMenuContent,
|
||||
ContextMenuItem,
|
||||
ContextMenuTrigger,
|
||||
} from '@/components/ui/context-menu'
|
||||
```
|
||||
|
||||
```tsx
|
||||
<ContextMenu>
|
||||
<ContextMenuTrigger>Right click</ContextMenuTrigger>
|
||||
<ContextMenuContent>
|
||||
<ContextMenuItem>Profile</ContextMenuItem>
|
||||
<ContextMenuItem>Billing</ContextMenuItem>
|
||||
<ContextMenuItem>Team</ContextMenuItem>
|
||||
<ContextMenuItem>Subscription</ContextMenuItem>
|
||||
</ContextMenuContent>
|
||||
</ContextMenu>
|
||||
```
|
||||
842
apps/design-system/content/docs/components/data-table.mdx
Normal file
842
apps/design-system/content/docs/components/data-table.mdx
Normal file
@@ -0,0 +1,842 @@
|
||||
---
|
||||
title: Data Table
|
||||
description: Powerful table and datagrids built using TanStack Table.
|
||||
component: true
|
||||
links:
|
||||
doc: https://tanstack.com/table/v8/docs/guide/introduction
|
||||
---
|
||||
|
||||
<ComponentPreview name="data-table-demo" peekCode wide />
|
||||
|
||||
## Introduction
|
||||
|
||||
Every data table or datagrid I've created has been unique. They all behave differently, have specific sorting and filtering requirements, and work with different data sources.
|
||||
|
||||
It doesn't make sense to combine all of these variations into a single component. If we do that, we'll lose the flexibility that [headless UI](https://tanstack.com/table/v8/docs/guide/introduction#what-is-headless-ui) provides.
|
||||
|
||||
So instead of a data-table component, I thought it would be more helpful to provide a guide on how to build your own.
|
||||
|
||||
We'll start with the basic `<Table />` component and build a complex data table from scratch.
|
||||
|
||||
<Callout className="mt-4">
|
||||
|
||||
**Tip:** If you find yourself using the same table in multiple places in your app, you can always extract it into a reusable component.
|
||||
|
||||
</Callout>
|
||||
|
||||
## Table of Contents
|
||||
|
||||
This guide will show you how to use [TanStack Table](https://tanstack.com/table) and the `<Table />` component to build your own custom data table. We'll cover the following topics:
|
||||
|
||||
- [Basic Table](#basic-table)
|
||||
- [Row Actions](#row-actions)
|
||||
- [Pagination](#pagination)
|
||||
- [Sorting](#sorting)
|
||||
- [Filtering](#filtering)
|
||||
- [Visibility](#visibility)
|
||||
- [Row Selection](#row-selection)
|
||||
- [Reusable Components](#reusable-components)
|
||||
|
||||
## Installation
|
||||
|
||||
1. Add the `<Table />` component to your project:
|
||||
|
||||
```bash
|
||||
npx shadcn-ui@latest add table
|
||||
```
|
||||
|
||||
2. Add `tanstack/react-table` dependency:
|
||||
|
||||
```bash
|
||||
npm install @tanstack/react-table
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
We are going to build a table to show recent payments. Here's what our data looks like:
|
||||
|
||||
```tsx showLineNumbers
|
||||
type Payment = {
|
||||
id: string
|
||||
amount: number
|
||||
status: 'pending' | 'processing' | 'success' | 'failed'
|
||||
email: string
|
||||
}
|
||||
|
||||
export const payments: Payment[] = [
|
||||
{
|
||||
id: '728ed52f',
|
||||
amount: 100,
|
||||
status: 'pending',
|
||||
email: 'm@example.com',
|
||||
},
|
||||
{
|
||||
id: '489e1d42',
|
||||
amount: 125,
|
||||
status: 'processing',
|
||||
email: 'example@gmail.com',
|
||||
},
|
||||
// ...
|
||||
]
|
||||
```
|
||||
|
||||
## Project Structure
|
||||
|
||||
Start by creating the following file structure:
|
||||
|
||||
```txt
|
||||
app
|
||||
└── payments
|
||||
├── columns.tsx
|
||||
├── data-table.tsx
|
||||
└── page.tsx
|
||||
```
|
||||
|
||||
I'm using a Next.js example here but this works for any other React framework.
|
||||
|
||||
- `columns.tsx` (client component) will contain our column definitions.
|
||||
- `data-table.tsx` (client component) will contain our `<DataTable />` component.
|
||||
- `page.tsx` (server component) is where we'll fetch data and render our table.
|
||||
|
||||
## Basic Table
|
||||
|
||||
Let's start by building a basic table.
|
||||
|
||||
<Steps>
|
||||
|
||||
### Column Definitions
|
||||
|
||||
First, we'll define our columns.
|
||||
|
||||
```tsx showLineNumbers title="app/payments/columns.tsx" {3,14-27}
|
||||
'use client'
|
||||
|
||||
import { ColumnDef } from '@tanstack/react-table'
|
||||
|
||||
// This type is used to define the shape of our data.
|
||||
// You can use a Zod schema here if you want.
|
||||
export type Payment = {
|
||||
id: string
|
||||
amount: number
|
||||
status: 'pending' | 'processing' | 'success' | 'failed'
|
||||
email: string
|
||||
}
|
||||
|
||||
export const columns: ColumnDef<Payment>[] = [
|
||||
{
|
||||
accessorKey: 'status',
|
||||
header: 'Status',
|
||||
},
|
||||
{
|
||||
accessorKey: 'email',
|
||||
header: 'Email',
|
||||
},
|
||||
{
|
||||
accessorKey: 'amount',
|
||||
header: 'Amount',
|
||||
},
|
||||
]
|
||||
```
|
||||
|
||||
<Callout className="mt-4">
|
||||
|
||||
**Note:** Columns are where you define the core of what your table
|
||||
will look like. They define the data that will be displayed, how it will be
|
||||
formatted, sorted and filtered.
|
||||
|
||||
</Callout>
|
||||
|
||||
### `<DataTable />` component
|
||||
|
||||
Next, we'll create a `<DataTable />` component to render our table.
|
||||
|
||||
```tsx showLineNumbers title="app/payments/data-table.tsx"
|
||||
'use client'
|
||||
|
||||
import { ColumnDef, flexRender, getCoreRowModel, useReactTable } from '@tanstack/react-table'
|
||||
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from '@/components/ui/table'
|
||||
|
||||
interface DataTableProps<TData, TValue> {
|
||||
columns: ColumnDef<TData, TValue>[]
|
||||
data: TData[]
|
||||
}
|
||||
|
||||
export function DataTable<TData, TValue>({ columns, data }: DataTableProps<TData, TValue>) {
|
||||
const table = useReactTable({
|
||||
data,
|
||||
columns,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
})
|
||||
|
||||
return (
|
||||
<div className="rounded-md border">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<TableRow key={headerGroup.id}>
|
||||
{headerGroup.headers.map((header) => {
|
||||
return (
|
||||
<TableHead key={header.id}>
|
||||
{header.isPlaceholder
|
||||
? null
|
||||
: flexRender(header.column.columnDef.header, header.getContext())}
|
||||
</TableHead>
|
||||
)
|
||||
})}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{table.getRowModel().rows?.length ? (
|
||||
table.getRowModel().rows.map((row) => (
|
||||
<TableRow key={row.id} data-state={row.getIsSelected() && 'selected'}>
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
<TableCell key={cell.id}>
|
||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
))
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell colSpan={columns.length} className="h-24 text-center">
|
||||
No results.
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
<Callout>
|
||||
|
||||
**Tip**: If you find yourself using `<DataTable />` in multiple places, this is the component you could make reusable by extracting it to `components/ui/data-table.tsx`.
|
||||
|
||||
`<DataTable columns={columns} data={data} />`
|
||||
|
||||
</Callout>
|
||||
|
||||
### Render the table
|
||||
|
||||
Finally, we'll render our table in our page component.
|
||||
|
||||
```tsx showLineNumbers title="app/payments/page.tsx" {22}
|
||||
import { Payment, columns } from './columns'
|
||||
import { DataTable } from './data-table'
|
||||
|
||||
async function getData(): Promise<Payment[]> {
|
||||
// Fetch data from your API here.
|
||||
return [
|
||||
{
|
||||
id: '728ed52f',
|
||||
amount: 100,
|
||||
status: 'pending',
|
||||
email: 'm@example.com',
|
||||
},
|
||||
// ...
|
||||
]
|
||||
}
|
||||
|
||||
export default async function DemoPage() {
|
||||
const data = await getData()
|
||||
|
||||
return (
|
||||
<div className="container mx-auto py-10">
|
||||
<DataTable columns={columns} data={data} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
</Steps>
|
||||
|
||||
## Cell Formatting
|
||||
|
||||
Let's format the amount cell to display the dollar amount. We'll also align the cell to the right.
|
||||
|
||||
<Steps>
|
||||
|
||||
### Update columns definition
|
||||
|
||||
Update the `header` and `cell` definitions for amount as follows:
|
||||
|
||||
```tsx showLineNumbers title="app/payments/columns.tsx" {4-15}
|
||||
export const columns: ColumnDef<Payment>[] = [
|
||||
{
|
||||
accessorKey: 'amount',
|
||||
header: () => <div className="text-right">Amount</div>,
|
||||
cell: ({ row }) => {
|
||||
const amount = parseFloat(row.getValue('amount'))
|
||||
const formatted = new Intl.NumberFormat('en-US', {
|
||||
style: 'currency',
|
||||
currency: 'USD',
|
||||
}).format(amount)
|
||||
|
||||
return <div className="text-right font-medium">{formatted}</div>
|
||||
},
|
||||
},
|
||||
]
|
||||
```
|
||||
|
||||
You can use the same approach to format other cells and headers.
|
||||
|
||||
</Steps>
|
||||
|
||||
## Row Actions
|
||||
|
||||
Let's add row actions to our table. We'll use a `<Dropdown />` component for this.
|
||||
|
||||
<Steps>
|
||||
|
||||
### Update columns definition
|
||||
|
||||
Update our columns definition to add a new `actions` column. The `actions` cell returns a `<Dropdown />` component.
|
||||
|
||||
```tsx showLineNumbers title="app/payments/columns.tsx" {4,6-14,18-45}
|
||||
'use client'
|
||||
|
||||
import { ColumnDef } from '@tanstack/react-table'
|
||||
import { MoreHorizontal } from 'lucide-react'
|
||||
|
||||
import { Button } from '@/components/ui/button'
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu'
|
||||
|
||||
export const columns: ColumnDef<Payment>[] = [
|
||||
// ...
|
||||
{
|
||||
id: 'actions',
|
||||
cell: ({ row }) => {
|
||||
const payment = row.original
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="ghost" className="h-8 w-8 p-0">
|
||||
<span className="sr-only">Open menu</span>
|
||||
<MoreHorizontal className="h-4 w-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuLabel>Actions</DropdownMenuLabel>
|
||||
<DropdownMenuItem onClick={() => navigator.clipboard.writeText(payment.id)}>
|
||||
Copy payment ID
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem>View customer</DropdownMenuItem>
|
||||
<DropdownMenuItem>View payment details</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)
|
||||
},
|
||||
},
|
||||
// ...
|
||||
]
|
||||
```
|
||||
|
||||
You can access the row data using `row.original` in the `cell` function. Use this to handle actions for your row eg. use the `id` to make a DELETE call to your API.
|
||||
|
||||
</Steps>
|
||||
|
||||
## Pagination
|
||||
|
||||
Next, we'll add pagination to our table.
|
||||
|
||||
<Steps>
|
||||
|
||||
### Update `<DataTable>`
|
||||
|
||||
```tsx showLineNumbers title="app/payments/data-table.tsx" {5,17}
|
||||
import {
|
||||
ColumnDef,
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
getPaginationRowModel,
|
||||
useReactTable,
|
||||
} from '@tanstack/react-table'
|
||||
|
||||
export function DataTable<TData, TValue>({ columns, data }: DataTableProps<TData, TValue>) {
|
||||
const table = useReactTable({
|
||||
data,
|
||||
columns,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
})
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
This will automatically paginate your rows into pages of 10. See the [pagination docs](https://tanstack.com/table/v8/docs/api/features/pagination) for more information on customizing page size and implementing manual pagination.
|
||||
|
||||
### Add pagination controls
|
||||
|
||||
We can add pagination controls to our table using the `<Button />` component and the `table.previousPage()`, `table.nextPage()` API methods.
|
||||
|
||||
```tsx showLineNumbers title="app/payments/data-table.tsx" {1,15,21-39}
|
||||
import { Button } from "@/components/ui/button"
|
||||
|
||||
export function DataTable<TData, TValue>({
|
||||
columns,
|
||||
data,
|
||||
}: DataTableProps<TData, TValue>) {
|
||||
const table = useReactTable({
|
||||
data,
|
||||
columns,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
})
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="rounded-md border">
|
||||
<Table>
|
||||
{ // .... }
|
||||
</Table>
|
||||
</div>
|
||||
<div className="flex items-center justify-end space-x-2 py-4">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => table.previousPage()}
|
||||
disabled={!table.getCanPreviousPage()}
|
||||
>
|
||||
Previous
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => table.nextPage()}
|
||||
disabled={!table.getCanNextPage()}
|
||||
>
|
||||
Next
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
See [Reusable Components](#reusable-components) section for a more advanced pagination component.
|
||||
|
||||
</Steps>
|
||||
|
||||
## Sorting
|
||||
|
||||
Let's make the email column sortable.
|
||||
|
||||
<Steps>
|
||||
|
||||
### Update `<DataTable>`
|
||||
|
||||
```tsx showLineNumbers title="app/payments/data-table.tsx" showLineNumbers {3,6,10,18,25-28}
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import {
|
||||
ColumnDef,
|
||||
SortingState,
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
getPaginationRowModel,
|
||||
getSortedRowModel,
|
||||
useReactTable,
|
||||
} from "@tanstack/react-table"
|
||||
|
||||
export function DataTable<TData, TValue>({
|
||||
columns,
|
||||
data,
|
||||
}: DataTableProps<TData, TValue>) {
|
||||
const [sorting, setSorting] = React.useState<SortingState>([])
|
||||
|
||||
const table = useReactTable({
|
||||
data,
|
||||
columns,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
onSortingChange: setSorting,
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
state: {
|
||||
sorting,
|
||||
},
|
||||
})
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="rounded-md border">
|
||||
<Table>{ ... }</Table>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### Make header cell sortable
|
||||
|
||||
We can now update the `email` header cell to add sorting controls.
|
||||
|
||||
```tsx showLineNumbers title="app/payments/columns.tsx" {4,9-19}
|
||||
'use client'
|
||||
|
||||
import { ColumnDef } from '@tanstack/react-table'
|
||||
import { ArrowUpDown, MoreHorizontal } from 'lucide-react'
|
||||
|
||||
export const columns: ColumnDef<Payment>[] = [
|
||||
{
|
||||
accessorKey: 'email',
|
||||
header: ({ column }) => {
|
||||
return (
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
|
||||
>
|
||||
Email
|
||||
<ArrowUpDown className="ml-2 h-4 w-4" />
|
||||
</Button>
|
||||
)
|
||||
},
|
||||
},
|
||||
]
|
||||
```
|
||||
|
||||
This will automatically sort the table (asc and desc) when the user toggles on the header cell.
|
||||
|
||||
</Steps>
|
||||
|
||||
## Filtering
|
||||
|
||||
Let's add a search input to filter emails in our table.
|
||||
|
||||
<Steps>
|
||||
|
||||
### Update `<DataTable>`
|
||||
|
||||
```tsx showLineNumbers title="app/payments/data-table.tsx" {6,10,17,24-26,35-36,39,45-54}
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import {
|
||||
ColumnDef,
|
||||
ColumnFiltersState,
|
||||
SortingState,
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
getFilteredRowModel,
|
||||
getPaginationRowModel,
|
||||
getSortedRowModel,
|
||||
useReactTable,
|
||||
} from "@tanstack/react-table"
|
||||
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Input } from "@/components/ui/input"
|
||||
|
||||
export function DataTable<TData, TValue>({
|
||||
columns,
|
||||
data,
|
||||
}: DataTableProps<TData, TValue>) {
|
||||
const [sorting, setSorting] = React.useState<SortingState>([])
|
||||
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
||||
[]
|
||||
)
|
||||
|
||||
const table = useReactTable({
|
||||
data,
|
||||
columns,
|
||||
onSortingChange: setSorting,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
onColumnFiltersChange: setColumnFilters,
|
||||
getFilteredRowModel: getFilteredRowModel(),
|
||||
state: {
|
||||
sorting,
|
||||
columnFilters,
|
||||
},
|
||||
})
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="flex items-center py-4">
|
||||
<Input
|
||||
placeholder="Filter emails..."
|
||||
value={(table.getColumn("email")?.getFilterValue() as string) ?? ""}
|
||||
onChange={(event) =>
|
||||
table.getColumn("email")?.setFilterValue(event.target.value)
|
||||
}
|
||||
className="max-w-sm"
|
||||
/>
|
||||
</div>
|
||||
<div className="rounded-md border">
|
||||
<Table>{ ... }</Table>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
Filtering is now enabled for the `email` column. You can add filters to other columns as well. See the [filtering docs](https://tanstack.com/table/v8/docs/guide/filters) for more information on customizing filters.
|
||||
|
||||
</Steps>
|
||||
|
||||
## Visibility
|
||||
|
||||
Adding column visibility is fairly simple using `@tanstack/react-table` visibility API.
|
||||
|
||||
<Steps>
|
||||
|
||||
### Update `<DataTable>`
|
||||
|
||||
```tsx showLineNumbers title="app/payments/data-table.tsx" {8,18-23,33-34,45,49,64-91}
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import {
|
||||
ColumnDef,
|
||||
ColumnFiltersState,
|
||||
SortingState,
|
||||
VisibilityState,
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
getFilteredRowModel,
|
||||
getPaginationRowModel,
|
||||
getSortedRowModel,
|
||||
useReactTable,
|
||||
} from "@tanstack/react-table"
|
||||
|
||||
import { Button } from "@/components/ui/button"
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuCheckboxItem,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu"
|
||||
|
||||
export function DataTable<TData, TValue>({
|
||||
columns,
|
||||
data,
|
||||
}: DataTableProps<TData, TValue>) {
|
||||
const [sorting, setSorting] = React.useState<SortingState>([])
|
||||
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
||||
[]
|
||||
)
|
||||
const [columnVisibility, setColumnVisibility] =
|
||||
React.useState<VisibilityState>({})
|
||||
|
||||
const table = useReactTable({
|
||||
data,
|
||||
columns,
|
||||
onSortingChange: setSorting,
|
||||
onColumnFiltersChange: setColumnFilters,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
getFilteredRowModel: getFilteredRowModel(),
|
||||
onColumnVisibilityChange: setColumnVisibility,
|
||||
state: {
|
||||
sorting,
|
||||
columnFilters,
|
||||
columnVisibility,
|
||||
},
|
||||
})
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="flex items-center py-4">
|
||||
<Input
|
||||
placeholder="Filter emails..."
|
||||
value={table.getColumn("email")?.getFilterValue() as string}
|
||||
onChange={(event) =>
|
||||
table.getColumn("email")?.setFilterValue(event.target.value)
|
||||
}
|
||||
className="max-w-sm"
|
||||
/>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="outline" className="ml-auto">
|
||||
Columns
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
{table
|
||||
.getAllColumns()
|
||||
.filter(
|
||||
(column) => column.getCanHide()
|
||||
)
|
||||
.map((column) => {
|
||||
return (
|
||||
<DropdownMenuCheckboxItem
|
||||
key={column.id}
|
||||
className="capitalize"
|
||||
checked={column.getIsVisible()}
|
||||
onCheckedChange={(value) =>
|
||||
column.toggleVisibility(!!value)
|
||||
}
|
||||
>
|
||||
{column.id}
|
||||
</DropdownMenuCheckboxItem>
|
||||
)
|
||||
})}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
<div className="rounded-md border">
|
||||
<Table>{ ... }</Table>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
This adds a dropdown menu that you can use to toggle column visibility.
|
||||
|
||||
</Steps>
|
||||
|
||||
## Row Selection
|
||||
|
||||
Next, we're going to add row selection to our table.
|
||||
|
||||
<Steps>
|
||||
|
||||
### Update column definitions
|
||||
|
||||
```tsx showLineNumbers title="app/payments/columns.tsx" {6,9-27}
|
||||
'use client'
|
||||
|
||||
import { ColumnDef } from '@tanstack/react-table'
|
||||
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Checkbox } from '@/components/ui/checkbox'
|
||||
|
||||
export const columns: ColumnDef<Payment>[] = [
|
||||
{
|
||||
id: 'select',
|
||||
header: ({ table }) => (
|
||||
<Checkbox
|
||||
checked={
|
||||
table.getIsAllPageRowsSelected() || (table.getIsSomePageRowsSelected() && 'indeterminate')
|
||||
}
|
||||
onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
|
||||
aria-label="Select all"
|
||||
/>
|
||||
),
|
||||
cell: ({ row }) => (
|
||||
<Checkbox
|
||||
checked={row.getIsSelected()}
|
||||
onCheckedChange={(value) => row.toggleSelected(!!value)}
|
||||
aria-label="Select row"
|
||||
/>
|
||||
),
|
||||
enableSorting: false,
|
||||
enableHiding: false,
|
||||
},
|
||||
]
|
||||
```
|
||||
|
||||
### Update `<DataTable>`
|
||||
|
||||
```tsx showLineNumbers title="app/payments/data-table.tsx" {11,23,28}
|
||||
export function DataTable<TData, TValue>({ columns, data }: DataTableProps<TData, TValue>) {
|
||||
const [sorting, setSorting] = React.useState<SortingState>([])
|
||||
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>([])
|
||||
const [columnVisibility, setColumnVisibility] = React.useState<VisibilityState>({})
|
||||
const [rowSelection, setRowSelection] = React.useState({})
|
||||
|
||||
const table = useReactTable({
|
||||
data,
|
||||
columns,
|
||||
onSortingChange: setSorting,
|
||||
onColumnFiltersChange: setColumnFilters,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
getFilteredRowModel: getFilteredRowModel(),
|
||||
onColumnVisibilityChange: setColumnVisibility,
|
||||
onRowSelectionChange: setRowSelection,
|
||||
state: {
|
||||
sorting,
|
||||
columnFilters,
|
||||
columnVisibility,
|
||||
rowSelection,
|
||||
},
|
||||
})
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="rounded-md border">
|
||||
<Table />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
This adds a checkbox to each row and a checkbox in the header to select all rows.
|
||||
|
||||
### Show selected rows
|
||||
|
||||
You can show the number of selected rows using the `table.getFilteredSelectedRowModel()` API.
|
||||
|
||||
```tsx
|
||||
<div className="flex-1 text-sm text-muted-foreground">
|
||||
{table.getFilteredSelectedRowModel().rows.length} of {table.getFilteredRowModel().rows.length}{' '}
|
||||
row(s) selected.
|
||||
</div>
|
||||
```
|
||||
|
||||
</Steps>
|
||||
|
||||
## Reusable Components
|
||||
|
||||
Here are some components you can use to build your data tables. This is from the [Tasks](/examples/tasks) demo.
|
||||
|
||||
### Column header
|
||||
|
||||
Make any column header sortable and hideable.
|
||||
|
||||
<ComponentSource src="/app/(app)/examples/tasks/components/data-table-column-header.tsx" />
|
||||
|
||||
```tsx {5}
|
||||
export const columns = [
|
||||
{
|
||||
accessorKey: 'email',
|
||||
header: ({ column }) => <DataTableColumnHeader column={column} title="Email" />,
|
||||
},
|
||||
]
|
||||
```
|
||||
|
||||
### Pagination
|
||||
|
||||
Add pagination controls to your table including page size and selection count.
|
||||
|
||||
<ComponentSource src="/app/(app)/examples/tasks/components/data-table-pagination.tsx" />
|
||||
|
||||
```tsx
|
||||
<DataTablePagination table={table} />
|
||||
```
|
||||
|
||||
### Column toggle
|
||||
|
||||
A component to toggle column visibility.
|
||||
|
||||
<ComponentSource src="/app/(app)/examples/tasks/components/data-table-view-options.tsx" />
|
||||
|
||||
```tsx
|
||||
<DataTableViewOptions table={table} />
|
||||
```
|
||||
74
apps/design-system/content/docs/components/date-picker.mdx
Normal file
74
apps/design-system/content/docs/components/date-picker.mdx
Normal file
@@ -0,0 +1,74 @@
|
||||
---
|
||||
title: Date Picker
|
||||
description: A date picker component with range and presets.
|
||||
component: true
|
||||
source:
|
||||
shadcn: true
|
||||
---
|
||||
|
||||
<ComponentPreview name="date-picker-demo" peekCode wide />
|
||||
|
||||
## Installation
|
||||
|
||||
The Date Picker is built using a composition of the `<Popover />` and the `<Calendar />` components.
|
||||
|
||||
See installation instructions for the [Popover](/docs/components/popover#installation) and the [Calendar](/docs/components/calendar#installation) components.
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
'use client'
|
||||
|
||||
import * as React from 'react'
|
||||
import { format } from 'date-fns'
|
||||
import { Calendar as CalendarIcon } from 'lucide-react'
|
||||
|
||||
import { cn } from '@/lib/utils'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Calendar } from '@/components/ui/calendar'
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
|
||||
|
||||
export function DatePickerDemo() {
|
||||
const [date, setDate] = React.useState<Date>()
|
||||
|
||||
return (
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant={'outline'}
|
||||
className={cn(
|
||||
'w-[280px] justify-start text-left font-normal',
|
||||
!date && 'text-muted-foreground'
|
||||
)}
|
||||
>
|
||||
<CalendarIcon className="mr-2 h-4 w-4" />
|
||||
{date ? format(date, 'PPP') : <span>Pick a date</span>}
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-auto p-0">
|
||||
<Calendar mode="single" selected={date} onSelect={setDate} initialFocus />
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
See the [React DayPicker](https://react-day-picker.js.org) documentation for more information.
|
||||
|
||||
## Examples
|
||||
|
||||
### Date Picker
|
||||
|
||||
<ComponentPreview name="date-picker-demo" peekCode wide />
|
||||
|
||||
### Date Range Picker
|
||||
|
||||
<ComponentPreview name="date-picker-with-range" />
|
||||
|
||||
### With Presets
|
||||
|
||||
<ComponentPreview name="date-picker-with-presets" />
|
||||
|
||||
### Form
|
||||
|
||||
<ComponentPreview name="date-picker-form" />
|
||||
120
apps/design-system/content/docs/components/dialog.mdx
Normal file
120
apps/design-system/content/docs/components/dialog.mdx
Normal file
@@ -0,0 +1,120 @@
|
||||
---
|
||||
title: Dialog
|
||||
description: A window overlaid on either the primary window or another dialog window, rendering the content underneath inert.
|
||||
featured: true
|
||||
component: true
|
||||
links:
|
||||
doc: https://www.radix-ui.com/docs/primitives/components/dialog
|
||||
api: https://www.radix-ui.com/docs/primitives/components/dialog#api-reference
|
||||
source:
|
||||
radix: true
|
||||
shadcn: true
|
||||
---
|
||||
|
||||
<ComponentPreview name="dialog-demo" peekCode wide />
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs defaultValue="cli">
|
||||
|
||||
<TabsList>
|
||||
<TabsTrigger value="cli">CLI</TabsTrigger>
|
||||
<TabsTrigger value="manual">Manual</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="cli">
|
||||
|
||||
```bash
|
||||
npx shadcn-ui@latest add dialog
|
||||
```
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="manual">
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step>Install the following dependencies:</Step>
|
||||
|
||||
```bash
|
||||
npm install @radix-ui/react-dialog
|
||||
```
|
||||
|
||||
<Step>Copy and paste the following code into your project.</Step>
|
||||
|
||||
<ComponentSource name="dialog" />
|
||||
|
||||
<Step>Update the import paths to match your project setup.</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
</Tabs>
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from '@/components/ui/dialog'
|
||||
```
|
||||
|
||||
```tsx
|
||||
<Dialog>
|
||||
<DialogTrigger>Open</DialogTrigger>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Are you absolutely sure?</DialogTitle>
|
||||
<DialogDescription>
|
||||
This action cannot be undone. This will permanently delete your account and remove your data
|
||||
from our servers.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Custom close button
|
||||
|
||||
<ComponentPreview name="dialog-close-button" />
|
||||
|
||||
## Notes
|
||||
|
||||
To activate the `Dialog` component from within a `Context Menu` or `Dropdown Menu`, you must encase the `Context Menu` or
|
||||
`Dropdown Menu` component in the `Dialog` component. For more information, refer to the linked issue [here](https://github.com/radix-ui/primitives/issues/1836).
|
||||
|
||||
```tsx {14-25}
|
||||
<Dialog>
|
||||
<ContextMenu>
|
||||
<ContextMenuTrigger>Right click</ContextMenuTrigger>
|
||||
<ContextMenuContent>
|
||||
<ContextMenuItem>Open</ContextMenuItem>
|
||||
<ContextMenuItem>Download</ContextMenuItem>
|
||||
<DialogTrigger asChild>
|
||||
<ContextMenuItem>
|
||||
<span>Delete</span>
|
||||
</ContextMenuItem>
|
||||
</DialogTrigger>
|
||||
</ContextMenuContent>
|
||||
</ContextMenu>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Are you absolutely sure?</DialogTitle>
|
||||
<DialogDescription>
|
||||
This action cannot be undone. Are you sure you want to permanently delete this file from our
|
||||
servers?
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<DialogFooter>
|
||||
<Button type="submit">Confirm</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
```
|
||||
95
apps/design-system/content/docs/components/drawer.mdx
Normal file
95
apps/design-system/content/docs/components/drawer.mdx
Normal file
@@ -0,0 +1,95 @@
|
||||
---
|
||||
title: Drawer
|
||||
description: A drawer component for React.
|
||||
component: true
|
||||
links:
|
||||
doc: https://github.com/emilkowalski/vaul
|
||||
source:
|
||||
vaul: true
|
||||
shadcn: true
|
||||
---
|
||||
|
||||
<ComponentPreview name="drawer-demo" peekCode wide />
|
||||
|
||||
## About
|
||||
|
||||
Drawer is built on top of [Vaul](https://github.com/emilkowalski/vaul) by [emilkowalski\_](https://twitter.com/emilkowalski_).
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs defaultValue="cli">
|
||||
|
||||
<TabsList>
|
||||
<TabsTrigger value="cli">CLI</TabsTrigger>
|
||||
<TabsTrigger value="manual">Manual</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="cli">
|
||||
|
||||
```bash
|
||||
npx shadcn-ui@latest add drawer
|
||||
```
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="manual">
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step>Install the following dependencies:</Step>
|
||||
|
||||
```bash
|
||||
npm install vaul
|
||||
```
|
||||
|
||||
<Step>Copy and paste the following code into your project.</Step>
|
||||
|
||||
<ComponentSource name="drawer" />
|
||||
|
||||
<Step>Update the import paths to match your project setup.</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
</Tabs>
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx showLineNumbers
|
||||
import {
|
||||
Drawer,
|
||||
DrawerClose,
|
||||
DrawerContent,
|
||||
DrawerDescription,
|
||||
DrawerFooter,
|
||||
DrawerHeader,
|
||||
DrawerTitle,
|
||||
DrawerTrigger,
|
||||
} from '@/components/ui/drawer'
|
||||
```
|
||||
|
||||
```tsx showLineNumbers
|
||||
<Drawer>
|
||||
<DrawerTrigger>Open</DrawerTrigger>
|
||||
<DrawerContent>
|
||||
<DrawerHeader>
|
||||
<DrawerTitle>Are you absolutely sure?</DrawerTitle>
|
||||
<DrawerDescription>This action cannot be undone.</DrawerDescription>
|
||||
</DrawerHeader>
|
||||
<DrawerFooter>
|
||||
<Button>Submit</Button>
|
||||
<DrawerClose>
|
||||
<Button variant="outline">Cancel</Button>
|
||||
</DrawerClose>
|
||||
</DrawerFooter>
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Responsive Dialog
|
||||
|
||||
You can combine the `Dialog` and `Drawer` components to create a responsive dialog. This renders a `Dialog` component on desktop and a `Drawer` on mobile.
|
||||
|
||||
<ComponentPreview name="drawer-dialog" />
|
||||
95
apps/design-system/content/docs/components/dropdown-menu.mdx
Normal file
95
apps/design-system/content/docs/components/dropdown-menu.mdx
Normal file
@@ -0,0 +1,95 @@
|
||||
---
|
||||
title: Dropdown Menu
|
||||
description: Displays a menu to the user — such as a set of actions or functions — triggered by a button.
|
||||
featured: true
|
||||
component: true
|
||||
links:
|
||||
doc: https://www.radix-ui.com/docs/primitives/components/dropdown-menu
|
||||
api: https://www.radix-ui.com/docs/primitives/components/dropdown-menu#api-reference
|
||||
source:
|
||||
radix: true
|
||||
shadcn: true
|
||||
---
|
||||
|
||||
<ComponentPreview name="dropdown-menu-demo" peekCode wide />
|
||||
|
||||
## Installation
|
||||
|
||||
So I guess I can write anything i want in here.
|
||||
|
||||
## Props
|
||||
|
||||
{/* <ComponentProps component={'/Dropdown'} foo="world" /> */}
|
||||
|
||||
<Tabs defaultValue="cli">
|
||||
|
||||
<TabsList>
|
||||
<TabsTrigger value="cli">CLI</TabsTrigger>
|
||||
<TabsTrigger value="manual">Manual</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="cli">
|
||||
|
||||
```bash
|
||||
npx shadcn-ui@latest add dropdown-menu
|
||||
```
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="manual">
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step>Install the following dependencies:</Step>
|
||||
|
||||
```bash
|
||||
npm install @radix-ui/react-dropdown-menu
|
||||
```
|
||||
|
||||
<Step>Copy and paste the following code into your project.</Step>
|
||||
|
||||
{/* <ComponentSource name="dropdown-menu" /> */}
|
||||
|
||||
<Step>Update the import paths to match your project setup.</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
</Tabs>
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu'
|
||||
```
|
||||
|
||||
```tsx
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger>Open</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuLabel>My Account</DropdownMenuLabel>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem>Profile</DropdownMenuItem>
|
||||
<DropdownMenuItem>Billing</DropdownMenuItem>
|
||||
<DropdownMenuItem>Team</DropdownMenuItem>
|
||||
<DropdownMenuItem>Subscription</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Checkboxes
|
||||
|
||||
<ComponentPreview name="dropdown-menu-checkboxes" />
|
||||
|
||||
### Radio Group
|
||||
|
||||
<ComponentPreview name="dropdown-menu-radio-group" />
|
||||
255
apps/design-system/content/docs/components/form.mdx
Normal file
255
apps/design-system/content/docs/components/form.mdx
Normal file
@@ -0,0 +1,255 @@
|
||||
---
|
||||
title: React Hook Form
|
||||
description: Building forms with React Hook Form and Zod.
|
||||
links:
|
||||
doc: https://react-hook-form.com
|
||||
source:
|
||||
shadcn: true
|
||||
---
|
||||
|
||||
Forms are tricky. They are one of the most common things you'll build in a web application, but also one of the most complex.
|
||||
|
||||
Well-designed HTML forms are:
|
||||
|
||||
- Well-structured and semantically correct.
|
||||
- Easy to use and navigate (keyboard).
|
||||
- Accessible with ARIA attributes and proper labels.
|
||||
- Has support for client and server side validation.
|
||||
- Well-styled and consistent with the rest of the application.
|
||||
|
||||
In this guide, we will take a look at building forms with [`react-hook-form`](https://react-hook-form.com/) and [`zod`](https://zod.dev). We're going to use a `<FormField>` component to compose accessible forms using Radix UI components.
|
||||
|
||||
## Features
|
||||
|
||||
The `<Form />` component is a wrapper around the `react-hook-form` library. It provides a few things:
|
||||
|
||||
- Composable components for building forms.
|
||||
- A `<FormField />` component for building controlled form fields.
|
||||
- Form validation using `zod`.
|
||||
- Handles accessibility and error messages.
|
||||
- Uses `React.useId()` for generating unique IDs.
|
||||
- Applies the correct `aria` attributes to form fields based on states.
|
||||
- Built to work with all Radix UI components.
|
||||
- Bring your own schema library. We use `zod` but you can use anything you want.
|
||||
- **You have full control over the markup and styling.**
|
||||
|
||||
## Anatomy
|
||||
|
||||
```tsx
|
||||
<Form>
|
||||
<FormField
|
||||
control={...}
|
||||
name="..."
|
||||
render={() => (
|
||||
<FormItem>
|
||||
<FormLabel />
|
||||
<FormControl>
|
||||
{ /* Your form field */}
|
||||
</FormControl>
|
||||
<FormDescription />
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</Form>
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
```tsx
|
||||
const form = useForm()
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="username"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Username</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder="shadcn" {...field} />
|
||||
</FormControl>
|
||||
<FormDescription>This is your public display name.</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs defaultValue="cli">
|
||||
|
||||
<TabsList>
|
||||
<TabsTrigger value="cli">CLI</TabsTrigger>
|
||||
<TabsTrigger value="manual">Manual</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="cli">
|
||||
|
||||
<Steps>
|
||||
|
||||
### Command
|
||||
|
||||
```bash
|
||||
npx shadcn-ui@latest add form
|
||||
```
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="manual">
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step>Install the following dependencies:</Step>
|
||||
|
||||
```bash
|
||||
npm install @radix-ui/react-label @radix-ui/react-slot react-hook-form @hookform/resolvers zod
|
||||
```
|
||||
|
||||
<Step>Copy and paste the following code into your project.</Step>
|
||||
|
||||
<ComponentSource name="form" />
|
||||
|
||||
<Step>Update the import paths to match your project setup.</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
</Tabs>
|
||||
|
||||
## Usage
|
||||
|
||||
<Steps>
|
||||
|
||||
### Create a form schema
|
||||
|
||||
Define the shape of your form using a Zod schema. You can read more about using Zod in the [Zod documentation](https://zod.dev).
|
||||
|
||||
```tsx showLineNumbers {3,5-7}
|
||||
'use client'
|
||||
|
||||
import { z } from 'zod'
|
||||
|
||||
const formSchema = z.object({
|
||||
username: z.string().min(2).max(50),
|
||||
})
|
||||
```
|
||||
|
||||
### Define a form
|
||||
|
||||
Use the `useForm` hook from `react-hook-form` to create a form.
|
||||
|
||||
```tsx showLineNumbers {3-4,14-20,22-27}
|
||||
'use client'
|
||||
|
||||
import { zodResolver } from '@hookform/resolvers/zod'
|
||||
import { useForm } from 'react-hook-form'
|
||||
import { z } from 'zod'
|
||||
|
||||
const formSchema = z.object({
|
||||
username: z.string().min(2, {
|
||||
message: 'Username must be at least 2 characters.',
|
||||
}),
|
||||
})
|
||||
|
||||
export function ProfileForm() {
|
||||
// 1. Define your form.
|
||||
const form = useForm<z.infer<typeof formSchema>>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
username: '',
|
||||
},
|
||||
})
|
||||
|
||||
// 2. Define a submit handler.
|
||||
function onSubmit(values: z.infer<typeof formSchema>) {
|
||||
// Do something with the form values.
|
||||
// ✅ This will be type-safe and validated.
|
||||
console.log(values)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Since `FormField` is using a controlled component, you need to provide a default value for the field. See the [React Hook Form docs](https://react-hook-form.com/docs/usecontroller) to learn more about controlled components.
|
||||
|
||||
### Build your form
|
||||
|
||||
We can now use the `<Form />` components to build our form.
|
||||
|
||||
```tsx showLineNumbers {7-17,28-50}
|
||||
'use client'
|
||||
|
||||
import { zodResolver } from '@hookform/resolvers/zod'
|
||||
import { useForm } from 'react-hook-form'
|
||||
import { z } from 'zod'
|
||||
|
||||
import { Button } from '@/components/ui/button'
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormDescription,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from '@/components/ui/form'
|
||||
import { Input } from '@/components/ui/input'
|
||||
|
||||
const formSchema = z.object({
|
||||
username: z.string().min(2, {
|
||||
message: 'Username must be at least 2 characters.',
|
||||
}),
|
||||
})
|
||||
|
||||
export function ProfileForm() {
|
||||
// ...
|
||||
|
||||
return (
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="username"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Username</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder="shadcn" {...field} />
|
||||
</FormControl>
|
||||
<FormDescription>This is your public display name.</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<Button type="submit">Submit</Button>
|
||||
</form>
|
||||
</Form>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### Done
|
||||
|
||||
That's it. You now have a fully accessible form that is type-safe with client-side validation.
|
||||
|
||||
<ComponentPreview
|
||||
name="input-form"
|
||||
className="[&_[role=tablist]]:hidden [&>div>div:first-child]:hidden"
|
||||
/>
|
||||
|
||||
</Steps>
|
||||
|
||||
## Examples
|
||||
|
||||
See the following links for more examples on how to use the `<Form />` component with other components:
|
||||
|
||||
- [Checkbox](/docs/components/checkbox#form)
|
||||
- [Date Picker](/docs/components/date-picker#form)
|
||||
- [Input](/docs/components/input#form)
|
||||
- [Radio Group](/docs/components/radio-group#form)
|
||||
- [Select](/docs/components/select#form)
|
||||
- [Switch](/docs/components/switch#form)
|
||||
- [Textarea](/docs/components/textarea#form)
|
||||
- [Combobox](/docs/components/combobox#form)
|
||||
64
apps/design-system/content/docs/components/hover-card.mdx
Normal file
64
apps/design-system/content/docs/components/hover-card.mdx
Normal file
@@ -0,0 +1,64 @@
|
||||
---
|
||||
title: Hover Card
|
||||
description: For sighted users to preview content available behind a link.
|
||||
component: true
|
||||
links:
|
||||
doc: https://www.radix-ui.com/docs/primitives/components/hover-card
|
||||
api: https://www.radix-ui.com/docs/primitives/components/hover-card#api-reference
|
||||
source:
|
||||
radix: true
|
||||
shadcn: true
|
||||
---
|
||||
|
||||
<ComponentPreview name="hover-card-demo" peekCode wide />
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs defaultValue="cli">
|
||||
|
||||
<TabsList>
|
||||
<TabsTrigger value="cli">CLI</TabsTrigger>
|
||||
<TabsTrigger value="manual">Manual</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="cli">
|
||||
|
||||
```bash
|
||||
npx shadcn-ui@latest add hover-card
|
||||
```
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="manual">
|
||||
|
||||
<Step>Install the following dependencies:</Step>
|
||||
|
||||
```bash
|
||||
npm install @radix-ui/react-hover-card
|
||||
```
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step>Copy and paste the following code into your project.</Step>
|
||||
|
||||
<ComponentSource name="hover-card" />
|
||||
|
||||
<Step>Update the import paths to match your project setup.</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
</Tabs>
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import { HoverCard, HoverCardContent, HoverCardTrigger } from '@/components/ui/hover-card'
|
||||
```
|
||||
|
||||
```tsx
|
||||
<HoverCard>
|
||||
<HoverCardTrigger>Hover</HoverCardTrigger>
|
||||
<HoverCardContent>The React Framework – created and maintained by @vercel.</HoverCardContent>
|
||||
</HoverCard>
|
||||
```
|
||||
191
apps/design-system/content/docs/components/input-otp.mdx
Normal file
191
apps/design-system/content/docs/components/input-otp.mdx
Normal file
@@ -0,0 +1,191 @@
|
||||
---
|
||||
title: Input OTP
|
||||
description: Accessible one-time password component with copy paste functionality.
|
||||
component: true
|
||||
links:
|
||||
doc: https://input-otp.rodz.dev
|
||||
source:
|
||||
shadcn: true
|
||||
inputOtp: true
|
||||
---
|
||||
|
||||
<ComponentPreview name="input-otp-demo" peekCode wide />
|
||||
|
||||
## About
|
||||
|
||||
Input OTP is built on top of [input-otp](https://github.com/guilhermerodz/input-otp) by [@guilherme_rodz](https://twitter.com/guilherme_rodz).
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs defaultValue="cli">
|
||||
|
||||
<TabsList>
|
||||
<TabsTrigger value="cli">CLI</TabsTrigger>
|
||||
<TabsTrigger value="manual">Manual</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="cli">
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step>Run the following command:</Step>
|
||||
|
||||
```bash
|
||||
npx shadcn-ui@latest add input-otp
|
||||
```
|
||||
|
||||
<Step>Update `tailwind.config.js`</Step>
|
||||
|
||||
Add the following animations to your `tailwind.config.js` file:
|
||||
|
||||
```js showLineNumbers title="tailwind.config.js" {6-9,12}
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
theme: {
|
||||
extend: {
|
||||
keyframes: {
|
||||
'caret-blink': {
|
||||
'0%,70%,100%': { opacity: '1' },
|
||||
'20%,50%': { opacity: '0' },
|
||||
},
|
||||
},
|
||||
animation: {
|
||||
'caret-blink': 'caret-blink 1.25s ease-out infinite',
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="manual">
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step>Install the following dependencies:</Step>
|
||||
|
||||
```bash
|
||||
npm install input-otp
|
||||
```
|
||||
|
||||
<Step>Copy and paste the following code into your project.</Step>
|
||||
|
||||
<ComponentSource name="input-otp" />
|
||||
|
||||
<Step>Update the import paths to match your project setup.</Step>
|
||||
|
||||
<Step>Update `tailwind.config.js`</Step>
|
||||
|
||||
Add the following animations to your `tailwind.config.js` file:
|
||||
|
||||
```js showLineNumbers title="tailwind.config.js" {6-9,12}
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
theme: {
|
||||
extend: {
|
||||
keyframes: {
|
||||
'caret-blink': {
|
||||
'0%,70%,100%': { opacity: '1' },
|
||||
'20%,50%': { opacity: '0' },
|
||||
},
|
||||
},
|
||||
animation: {
|
||||
'caret-blink': 'caret-blink 1.25s ease-out infinite',
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
</Tabs>
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import { InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot } from '@/components/ui/input-otp'
|
||||
```
|
||||
|
||||
```tsx
|
||||
<InputOTP maxLength={6}>
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={0} />
|
||||
<InputOTPSlot index={1} />
|
||||
<InputOTPSlot index={2} />
|
||||
</InputOTPGroup>
|
||||
<InputOTPSeparator />
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={3} />
|
||||
<InputOTPSlot index={4} />
|
||||
<InputOTPSlot index={5} />
|
||||
</InputOTPGroup>
|
||||
</InputOTP>
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Pattern
|
||||
|
||||
Use the `pattern` prop to define a custom pattern for the OTP input.
|
||||
|
||||
<ComponentPreview name="input-otp-pattern" />
|
||||
|
||||
```tsx showLineNumbers {1,7}
|
||||
import { REGEXP_ONLY_DIGITS_AND_CHARS } from "input-otp"
|
||||
|
||||
...
|
||||
|
||||
<InputOTP
|
||||
maxLength={6}
|
||||
pattern={REGEXP_ONLY_DIGITS_AND_CHARS}
|
||||
>
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={0} />
|
||||
{/* ... */}
|
||||
</InputOTPGroup>
|
||||
</InputOTP>
|
||||
```
|
||||
|
||||
### Separator
|
||||
|
||||
You can use the `<InputOTPSeparator />` component to add a separator between the input groups.
|
||||
|
||||
<ComponentPreview name="input-otp-separator" />
|
||||
|
||||
```tsx showLineNumbers {4,15}
|
||||
import {
|
||||
InputOTP,
|
||||
InputOTPGroup,
|
||||
InputOTPSeparator,
|
||||
InputOTPSlot,
|
||||
} from "@/components/ui/input-otp"
|
||||
|
||||
...
|
||||
|
||||
<InputOTP maxLength={4}>
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={0} />
|
||||
<InputOTPSlot index={1} />
|
||||
</InputOTPGroup>
|
||||
<InputOTPSeparator />
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={2} />
|
||||
<InputOTPSlot index={3} />
|
||||
</InputOTPGroup>
|
||||
</InputOTP>
|
||||
```
|
||||
|
||||
### Controlled
|
||||
|
||||
You can use the `value` and `onChange` props to control the input value.
|
||||
|
||||
<ComponentPreview name="input-otp-controlled" />
|
||||
|
||||
### Form
|
||||
|
||||
<ComponentPreview name="input-otp-form" />
|
||||
77
apps/design-system/content/docs/components/input.mdx
Normal file
77
apps/design-system/content/docs/components/input.mdx
Normal file
@@ -0,0 +1,77 @@
|
||||
---
|
||||
title: Input
|
||||
description: Displays a form input field or a component that looks like an input field.
|
||||
component: true
|
||||
source:
|
||||
shadcn: true
|
||||
---
|
||||
|
||||
<ComponentPreview name="input-demo" className="[&_input]:max-w-xs" peekCode wide />
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs defaultValue="cli">
|
||||
|
||||
<TabsList>
|
||||
<TabsTrigger value="cli">CLI</TabsTrigger>
|
||||
<TabsTrigger value="manual">Manual</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="cli">
|
||||
|
||||
```bash
|
||||
npx shadcn-ui@latest add input
|
||||
```
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="manual">
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step>Copy and paste the following code into your project.</Step>
|
||||
|
||||
<ComponentSource name="input" />
|
||||
|
||||
<Step>Update the import paths to match your project setup.</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
</Tabs>
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import { Input } from '@/components/ui/input'
|
||||
```
|
||||
|
||||
```tsx
|
||||
<Input />
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Default
|
||||
|
||||
<ComponentPreview name="input-demo" className="[&_input]:max-w-xs" />
|
||||
|
||||
### File
|
||||
|
||||
<ComponentPreview name="input-file" className="[&_input]:max-w-xs" />
|
||||
|
||||
### Disabled
|
||||
|
||||
<ComponentPreview name="input-disabled" className="[&_input]:max-w-xs" />
|
||||
|
||||
### With Label
|
||||
|
||||
<ComponentPreview name="input-with-label" className="[&_input]:max-w-xs" />
|
||||
|
||||
### With Button
|
||||
|
||||
<ComponentPreview name="input-with-button" className="[&_input]:max-w-xs" />
|
||||
|
||||
### Form
|
||||
|
||||
<ComponentPreview name="input-form" />
|
||||
61
apps/design-system/content/docs/components/label.mdx
Normal file
61
apps/design-system/content/docs/components/label.mdx
Normal file
@@ -0,0 +1,61 @@
|
||||
---
|
||||
title: Label
|
||||
description: Renders an accessible label associated with controls.
|
||||
component: true
|
||||
links:
|
||||
doc: https://www.radix-ui.com/docs/primitives/components/label
|
||||
api: https://www.radix-ui.com/docs/primitives/components/label#api-reference
|
||||
source:
|
||||
radix: true
|
||||
shadcn: true
|
||||
---
|
||||
|
||||
<ComponentPreview name="label-demo" peekCode wide />
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs defaultValue="cli">
|
||||
|
||||
<TabsList>
|
||||
<TabsTrigger value="cli">CLI</TabsTrigger>
|
||||
<TabsTrigger value="manual">Manual</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="cli">
|
||||
|
||||
```bash
|
||||
npx shadcn-ui@latest add label
|
||||
```
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="manual">
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step>Install the following dependencies:</Step>
|
||||
|
||||
```bash
|
||||
npm install @radix-ui/react-label
|
||||
```
|
||||
|
||||
<Step>Copy and paste the following code into your project.</Step>
|
||||
|
||||
<ComponentSource name="label" />
|
||||
|
||||
<Step>Update the import paths to match your project setup.</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
</Tabs>
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import { Label } from '@/components/ui/label'
|
||||
```
|
||||
|
||||
```tsx
|
||||
<Label htmlFor="email">Your email address</Label>
|
||||
```
|
||||
83
apps/design-system/content/docs/components/menubar.mdx
Normal file
83
apps/design-system/content/docs/components/menubar.mdx
Normal file
@@ -0,0 +1,83 @@
|
||||
---
|
||||
title: Menubar
|
||||
description: A visually persistent menu common in desktop applications that provides quick access to a consistent set of commands.
|
||||
component: true
|
||||
links:
|
||||
doc: https://www.radix-ui.com/docs/primitives/components/menubar
|
||||
api: https://www.radix-ui.com/docs/primitives/components/menubar#api-reference
|
||||
source:
|
||||
radix: true
|
||||
shadcn: true
|
||||
---
|
||||
|
||||
<ComponentPreview name="menubar-demo" peekCode wide />
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs defaultValue="cli">
|
||||
|
||||
<TabsList>
|
||||
<TabsTrigger value="cli">CLI</TabsTrigger>
|
||||
<TabsTrigger value="manual">Manual</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="cli">
|
||||
|
||||
```bash
|
||||
npx shadcn-ui@latest add menubar
|
||||
```
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="manual">
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step>Install the following dependencies:</Step>
|
||||
|
||||
```bash
|
||||
npm install @radix-ui/react-menubar
|
||||
```
|
||||
|
||||
<Step>Copy and paste the following code into your project.</Step>
|
||||
|
||||
<ComponentSource name="menubar" />
|
||||
|
||||
<Step>Update the import paths to match your project setup.</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
</Tabs>
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import {
|
||||
Menubar,
|
||||
MenubarContent,
|
||||
MenubarItem,
|
||||
MenubarMenu,
|
||||
MenubarSeparator,
|
||||
MenubarShortcut,
|
||||
MenubarTrigger,
|
||||
} from '@/components/ui/menubar'
|
||||
```
|
||||
|
||||
```tsx
|
||||
<Menubar>
|
||||
<MenubarMenu>
|
||||
<MenubarTrigger>File</MenubarTrigger>
|
||||
<MenubarContent>
|
||||
<MenubarItem>
|
||||
New Tab <MenubarShortcut>⌘T</MenubarShortcut>
|
||||
</MenubarItem>
|
||||
<MenubarItem>New Window</MenubarItem>
|
||||
<MenubarSeparator />
|
||||
<MenubarItem>Share</MenubarItem>
|
||||
<MenubarSeparator />
|
||||
<MenubarItem>Print</MenubarItem>
|
||||
</MenubarContent>
|
||||
</MenubarMenu>
|
||||
</Menubar>
|
||||
```
|
||||
@@ -0,0 +1,99 @@
|
||||
---
|
||||
title: Navigation Menu
|
||||
description: A collection of links for navigating websites.
|
||||
component: true
|
||||
links:
|
||||
doc: https://www.radix-ui.com/docs/primitives/components/navigation-menu
|
||||
api: https://www.radix-ui.com/docs/primitives/components/navigation-menu#api-reference
|
||||
source:
|
||||
radix: true
|
||||
shadcn: true
|
||||
---
|
||||
|
||||
<ComponentPreview name="navigation-menu-demo" peekCode wide />
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs defaultValue="cli">
|
||||
|
||||
<TabsList>
|
||||
<TabsTrigger value="cli">CLI</TabsTrigger>
|
||||
<TabsTrigger value="manual">Manual</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="cli">
|
||||
|
||||
```bash
|
||||
npx shadcn-ui@latest add navigation-menu
|
||||
```
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="manual">
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step>Install the following dependencies:</Step>
|
||||
|
||||
```bash
|
||||
npm install @radix-ui/react-navigation-menu
|
||||
```
|
||||
|
||||
<Step>Copy and paste the following code into your project.</Step>
|
||||
|
||||
<ComponentSource name="navigation-menu" />
|
||||
|
||||
<Step>Update the import paths to match your project setup.</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
</Tabs>
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import {
|
||||
NavigationMenu,
|
||||
NavigationMenuContent,
|
||||
NavigationMenuIndicator,
|
||||
NavigationMenuItem,
|
||||
NavigationMenuLink,
|
||||
NavigationMenuList,
|
||||
NavigationMenuTrigger,
|
||||
NavigationMenuViewport,
|
||||
} from '@/components/ui/navigation-menu'
|
||||
```
|
||||
|
||||
```tsx
|
||||
<NavigationMenu>
|
||||
<NavigationMenuList>
|
||||
<NavigationMenuItem>
|
||||
<NavigationMenuTrigger>Item One</NavigationMenuTrigger>
|
||||
<NavigationMenuContent>
|
||||
<NavigationMenuLink>Link</NavigationMenuLink>
|
||||
</NavigationMenuContent>
|
||||
</NavigationMenuItem>
|
||||
</NavigationMenuList>
|
||||
</NavigationMenu>
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Link Component
|
||||
|
||||
When using the Next.js `<Link />` component, you can use `navigationMenuTriggerStyle()` to apply the correct styles to the trigger.
|
||||
|
||||
```tsx
|
||||
import { navigationMenuTriggerStyle } from '@/components/ui/navigation-menu'
|
||||
```
|
||||
|
||||
```tsx {3-5}
|
||||
<NavigationMenuItem>
|
||||
<Link href="/docs" legacyBehavior passHref>
|
||||
<NavigationMenuLink className={navigationMenuTriggerStyle()}>Documentation</NavigationMenuLink>
|
||||
</Link>
|
||||
</NavigationMenuItem>
|
||||
```
|
||||
|
||||
See also the [Radix UI documentation](https://www.radix-ui.com/docs/primitives/components/navigation-menu#with-client-side-routing) for handling client side routing.
|
||||
104
apps/design-system/content/docs/components/pagination.mdx
Normal file
104
apps/design-system/content/docs/components/pagination.mdx
Normal file
@@ -0,0 +1,104 @@
|
||||
---
|
||||
title: Pagination
|
||||
description: Pagination with page navigation, next and previous links.
|
||||
component: true
|
||||
source:
|
||||
shadcn: true
|
||||
---
|
||||
|
||||
<ComponentPreview name="pagination-demo" peekCode wide />
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs defaultValue="cli">
|
||||
|
||||
<TabsList>
|
||||
<TabsTrigger value="cli">CLI</TabsTrigger>
|
||||
<TabsTrigger value="manual">Manual</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="cli">
|
||||
|
||||
```bash
|
||||
npx shadcn-ui@latest add pagination
|
||||
```
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="manual">
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step>Copy and paste the following code into your project.</Step>
|
||||
|
||||
<ComponentSource name="pagination" />
|
||||
|
||||
<Step>Update the import paths to match your project setup.</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
</Tabs>
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import {
|
||||
Pagination,
|
||||
PaginationContent,
|
||||
PaginationEllipsis,
|
||||
PaginationItem,
|
||||
PaginationLink,
|
||||
PaginationNext,
|
||||
PaginationPrevious,
|
||||
} from '@/components/ui/pagination'
|
||||
```
|
||||
|
||||
```tsx
|
||||
<Pagination>
|
||||
<PaginationContent>
|
||||
<PaginationItem>
|
||||
<PaginationPrevious href="#" />
|
||||
</PaginationItem>
|
||||
<PaginationItem>
|
||||
<PaginationLink href="#">1</PaginationLink>
|
||||
</PaginationItem>
|
||||
<PaginationItem>
|
||||
<PaginationEllipsis />
|
||||
</PaginationItem>
|
||||
<PaginationItem>
|
||||
<PaginationNext href="#" />
|
||||
</PaginationItem>
|
||||
</PaginationContent>
|
||||
</Pagination>
|
||||
```
|
||||
|
||||
### Next.js
|
||||
|
||||
By default the `<PaginationLink />` component will render an `<a />` tag.
|
||||
|
||||
To use the Next.js `<Link />` component, make the following updates to `pagination.tsx`.
|
||||
|
||||
```diff showLineNumbers /typeof Link/ {1}
|
||||
+ import Link from "next/link"
|
||||
|
||||
- type PaginationLinkProps = ... & React.ComponentProps<"a">
|
||||
+ type PaginationLinkProps = ... & React.ComponentProps<typeof Link>
|
||||
|
||||
const PaginationLink = ({...props }: ) => (
|
||||
<PaginationItem>
|
||||
- <a>
|
||||
+ <Link>
|
||||
// ...
|
||||
- </a>
|
||||
+ </Link>
|
||||
</PaginationItem>
|
||||
)
|
||||
|
||||
```
|
||||
|
||||
<Callout className="mt-6">
|
||||
|
||||
**Note:** We are making updates to the cli to automatically do this for you.
|
||||
|
||||
</Callout>
|
||||
64
apps/design-system/content/docs/components/popover.mdx
Normal file
64
apps/design-system/content/docs/components/popover.mdx
Normal file
@@ -0,0 +1,64 @@
|
||||
---
|
||||
title: Popover
|
||||
description: Displays rich content in a portal, triggered by a button.
|
||||
component: true
|
||||
links:
|
||||
doc: https://www.radix-ui.com/docs/primitives/components/popover
|
||||
api: https://www.radix-ui.com/docs/primitives/components/popover#api-reference
|
||||
source:
|
||||
radix: true
|
||||
shadcn: true
|
||||
---
|
||||
|
||||
<ComponentPreview name="popover-demo" peekCode wide />
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs defaultValue="cli">
|
||||
|
||||
<TabsList>
|
||||
<TabsTrigger value="cli">CLI</TabsTrigger>
|
||||
<TabsTrigger value="manual">Manual</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="cli">
|
||||
|
||||
```bash
|
||||
npx shadcn-ui@latest add popover
|
||||
```
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="manual">
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step>Install the following dependencies:</Step>
|
||||
|
||||
```bash
|
||||
npm install @radix-ui/react-popover
|
||||
```
|
||||
|
||||
<Step>Copy and paste the following code into your project.</Step>
|
||||
|
||||
<ComponentSource name="popover" />
|
||||
|
||||
<Step>Update the import paths to match your project setup.</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
</Tabs>
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
|
||||
```
|
||||
|
||||
```tsx
|
||||
<Popover>
|
||||
<PopoverTrigger>Open</PopoverTrigger>
|
||||
<PopoverContent>Place content for the popover here.</PopoverContent>
|
||||
</Popover>
|
||||
```
|
||||
61
apps/design-system/content/docs/components/progress.mdx
Normal file
61
apps/design-system/content/docs/components/progress.mdx
Normal file
@@ -0,0 +1,61 @@
|
||||
---
|
||||
title: Progress
|
||||
description: Displays an indicator showing the completion progress of a task, typically displayed as a progress bar.
|
||||
component: true
|
||||
links:
|
||||
doc: https://www.radix-ui.com/docs/primitives/components/progress
|
||||
api: https://www.radix-ui.com/docs/primitives/components/progress#api-reference
|
||||
source:
|
||||
radix: true
|
||||
shadcn: true
|
||||
---
|
||||
|
||||
<ComponentPreview name="progress-demo" peekCode wide />
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs defaultValue="cli">
|
||||
|
||||
<TabsList>
|
||||
<TabsTrigger value="cli">CLI</TabsTrigger>
|
||||
<TabsTrigger value="manual">Manual</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="cli">
|
||||
|
||||
```bash
|
||||
npx shadcn-ui@latest add progress
|
||||
```
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="manual">
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step>Install the following dependencies:</Step>
|
||||
|
||||
```bash
|
||||
npm install @radix-ui/react-progress
|
||||
```
|
||||
|
||||
<Step>Copy and paste the following code into your project.</Step>
|
||||
|
||||
<ComponentSource name="progress" />
|
||||
|
||||
<Step>Update the import paths to match your project setup.</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
</Tabs>
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import { Progress } from '@/components/ui/progress'
|
||||
```
|
||||
|
||||
```tsx
|
||||
<Progress value={33} />
|
||||
```
|
||||
77
apps/design-system/content/docs/components/radio-group.mdx
Normal file
77
apps/design-system/content/docs/components/radio-group.mdx
Normal file
@@ -0,0 +1,77 @@
|
||||
---
|
||||
title: Radio Group
|
||||
description: A set of checkable buttons—known as radio buttons—where no more than one of the buttons can be checked at a time.
|
||||
component: true
|
||||
links:
|
||||
doc: https://www.radix-ui.com/docs/primitives/components/radio-group
|
||||
api: https://www.radix-ui.com/docs/primitives/components/radio-group#api-reference
|
||||
source:
|
||||
radix: true
|
||||
shadcn: true
|
||||
---
|
||||
|
||||
<ComponentPreview name="radio-group-demo" peekCode wide />
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs defaultValue="cli">
|
||||
|
||||
<TabsList>
|
||||
<TabsTrigger value="cli">CLI</TabsTrigger>
|
||||
<TabsTrigger value="manual">Manual</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="cli">
|
||||
|
||||
```bash
|
||||
npx shadcn-ui@latest add radio-group
|
||||
```
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="manual">
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step>Install the following dependencies:</Step>
|
||||
|
||||
```bash
|
||||
npm install @radix-ui/react-radio-group
|
||||
```
|
||||
|
||||
<Step>Copy and paste the following code into your project.</Step>
|
||||
|
||||
<ComponentSource name="radio-group" />
|
||||
|
||||
<Step>Update the import paths to match your project setup.</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
</Tabs>
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'
|
||||
```
|
||||
|
||||
```tsx
|
||||
<RadioGroup defaultValue="option-one">
|
||||
<div className="flex items-center space-x-2">
|
||||
<RadioGroupItem value="option-one" id="option-one" />
|
||||
<Label htmlFor="option-one">Option One</Label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<RadioGroupItem value="option-two" id="option-two" />
|
||||
<Label htmlFor="option-two">Option Two</Label>
|
||||
</div>
|
||||
</RadioGroup>
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Form
|
||||
|
||||
<ComponentPreview name="radio-group-form" />
|
||||
110
apps/design-system/content/docs/components/resizable.mdx
Normal file
110
apps/design-system/content/docs/components/resizable.mdx
Normal file
@@ -0,0 +1,110 @@
|
||||
---
|
||||
title: Resizable
|
||||
description: Accessible resizable panel groups and layouts with keyboard support.
|
||||
component: true
|
||||
links:
|
||||
doc: https://github.com/bvaughn/react-resizable-panels
|
||||
api: https://github.com/bvaughn/react-resizable-panels/tree/main/packages/react-resizable-panels
|
||||
source:
|
||||
shadcn: true
|
||||
---
|
||||
|
||||
<ComponentPreview name="resizable-demo" peekCode wide />
|
||||
|
||||
## About
|
||||
|
||||
The `Resizable` component is built on top of [react-resizable-panels](https://github.com/bvaughn/react-resizable-panels) by [bvaughn](https://github.com/bvaughn).
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs defaultValue="cli">
|
||||
|
||||
<TabsList>
|
||||
<TabsTrigger value="cli">CLI</TabsTrigger>
|
||||
<TabsTrigger value="manual">Manual</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="cli">
|
||||
|
||||
```bash
|
||||
npx shadcn-ui@latest add resizable
|
||||
```
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="manual">
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step>Install the following dependencies:</Step>
|
||||
|
||||
```bash
|
||||
npm install react-resizable-panels
|
||||
```
|
||||
|
||||
<Step>Copy and paste the following code into your project.</Step>
|
||||
|
||||
<ComponentSource name="resizable" />
|
||||
|
||||
<Step>Update the import paths to match your project setup.</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
</Tabs>
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from '@/components/ui/resizable'
|
||||
```
|
||||
|
||||
```tsx
|
||||
<ResizablePanelGroup direction="horizontal">
|
||||
<ResizablePanel>One</ResizablePanel>
|
||||
<ResizableHandle />
|
||||
<ResizablePanel>Two</ResizablePanel>
|
||||
</ResizablePanelGroup>
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Vertical
|
||||
|
||||
Use the `direction` prop to set the direction of the resizable panels.
|
||||
|
||||
<ComponentPreview name="resizable-vertical" />
|
||||
|
||||
```tsx showLineNumbers {9}
|
||||
import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from '@/components/ui/resizable'
|
||||
|
||||
export default function Example() {
|
||||
return (
|
||||
<ResizablePanelGroup direction="vertical">
|
||||
<ResizablePanel>One</ResizablePanel>
|
||||
<ResizableHandle />
|
||||
<ResizablePanel>Two</ResizablePanel>
|
||||
</ResizablePanelGroup>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### Handle
|
||||
|
||||
You can set or hide the handle by using the `withHandle` prop on the `ResizableHandle` component.
|
||||
|
||||
<ComponentPreview name="resizable-handle" />
|
||||
|
||||
```tsx showLineNumbers {11}
|
||||
import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from '@/components/ui/resizable'
|
||||
|
||||
export default function Example() {
|
||||
return (
|
||||
<ResizablePanelGroup direction="horizontal">
|
||||
<ResizablePanel>One</ResizablePanel>
|
||||
<ResizableHandle withHandle />
|
||||
<ResizablePanel>Two</ResizablePanel>
|
||||
</ResizablePanelGroup>
|
||||
)
|
||||
}
|
||||
```
|
||||
73
apps/design-system/content/docs/components/scroll-area.mdx
Normal file
73
apps/design-system/content/docs/components/scroll-area.mdx
Normal file
@@ -0,0 +1,73 @@
|
||||
---
|
||||
title: Scroll-area
|
||||
description: Augments native scroll functionality for custom, cross-browser styling.
|
||||
component: true
|
||||
links:
|
||||
doc: https://www.radix-ui.com/docs/primitives/components/scroll-area
|
||||
api: https://www.radix-ui.com/docs/primitives/components/scroll-area#api-reference
|
||||
source:
|
||||
radix: true
|
||||
shadcn: true
|
||||
---
|
||||
|
||||
<ComponentPreview name="scroll-area-demo" peekCode wide />
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs defaultValue="cli">
|
||||
|
||||
<TabsList>
|
||||
<TabsTrigger value="cli">CLI</TabsTrigger>
|
||||
<TabsTrigger value="manual">Manual</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="cli">
|
||||
|
||||
```bash
|
||||
npx shadcn-ui@latest add scroll-area
|
||||
```
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="manual">
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step>Install the following dependencies:</Step>
|
||||
|
||||
```bash
|
||||
npm install @radix-ui/react-scroll-area
|
||||
```
|
||||
|
||||
<Step>Copy and paste the following code into your project.</Step>
|
||||
|
||||
<ComponentSource name="scroll-area" />
|
||||
|
||||
<Step>Update the import paths to match your project setup.</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
</Tabs>
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import { ScrollArea } from '@/components/ui/scroll-area'
|
||||
```
|
||||
|
||||
```tsx
|
||||
<ScrollArea className="h-[200px] w-[350px] rounded-md border p-4">
|
||||
Jokester began sneaking into the castle in the middle of the night and leaving jokes all over the
|
||||
place: under the king's pillow, in his soup, even in the royal toilet. The king was furious, but
|
||||
he couldn't seem to stop Jokester. And then, one day, the people of the kingdom discovered that
|
||||
the jokes left by Jokester were so funny that they couldn't help but laugh. And once they started
|
||||
laughing, they couldn't stop.
|
||||
</ScrollArea>
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Horizontal Scrolling
|
||||
|
||||
<ComponentPreview name="scroll-area-horizontal-demo" peekCode wide />
|
||||
87
apps/design-system/content/docs/components/select.mdx
Normal file
87
apps/design-system/content/docs/components/select.mdx
Normal file
@@ -0,0 +1,87 @@
|
||||
---
|
||||
title: Select
|
||||
description: Displays a list of options for the user to pick from—triggered by a button.
|
||||
component: true
|
||||
featured: true
|
||||
links:
|
||||
doc: https://www.radix-ui.com/docs/primitives/components/select
|
||||
api: https://www.radix-ui.com/docs/primitives/components/select#api-reference
|
||||
source:
|
||||
radix: true
|
||||
shadcn: true
|
||||
---
|
||||
|
||||
<ComponentPreview name="select-demo" peekCode wide />
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs defaultValue="cli">
|
||||
|
||||
<TabsList>
|
||||
<TabsTrigger value="cli">CLI</TabsTrigger>
|
||||
<TabsTrigger value="manual">Manual</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="cli">
|
||||
|
||||
```bash
|
||||
npx shadcn-ui@latest add select
|
||||
```
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="manual">
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step>Install the following dependencies:</Step>
|
||||
|
||||
```bash
|
||||
npm install @radix-ui/react-select
|
||||
```
|
||||
|
||||
<Step>Copy and paste the following code into your project.</Step>
|
||||
|
||||
<ComponentSource name="select" />
|
||||
|
||||
<Step>Update the import paths to match your project setup.</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
</Tabs>
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@/components/ui/select'
|
||||
```
|
||||
|
||||
```tsx
|
||||
<Select>
|
||||
<SelectTrigger className="w-[180px]">
|
||||
<SelectValue placeholder="Theme" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="light">Light</SelectItem>
|
||||
<SelectItem value="dark">Dark</SelectItem>
|
||||
<SelectItem value="system">System</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Scrollable
|
||||
|
||||
<ComponentPreview name="select-scrollable" />
|
||||
|
||||
### Form
|
||||
|
||||
<ComponentPreview name="select-form" />
|
||||
59
apps/design-system/content/docs/components/separator.mdx
Normal file
59
apps/design-system/content/docs/components/separator.mdx
Normal file
@@ -0,0 +1,59 @@
|
||||
---
|
||||
title: Separator
|
||||
description: Visually or semantically separates content.
|
||||
component: true
|
||||
links:
|
||||
doc: https://www.radix-ui.com/docs/primitives/components/separator
|
||||
api: https://www.radix-ui.com/docs/primitives/components/separator#api-reference
|
||||
source:
|
||||
shadcn: true
|
||||
---
|
||||
|
||||
<ComponentPreview name="separator-demo" peekCode wide />
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs defaultValue="cli">
|
||||
|
||||
<TabsList>
|
||||
<TabsTrigger value="cli">CLI</TabsTrigger>
|
||||
<TabsTrigger value="manual">Manual</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="cli">
|
||||
|
||||
```bash
|
||||
npx shadcn-ui@latest add separator
|
||||
```
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="manual">
|
||||
|
||||
<Steps>
|
||||
<Step>Install the following dependencies:</Step>
|
||||
|
||||
```bash
|
||||
npm install @radix-ui/react-separator
|
||||
```
|
||||
|
||||
<Step>Copy and paste the following code into your project.</Step>
|
||||
|
||||
<ComponentSource name="separator" />
|
||||
|
||||
<Step>Update the import paths to match your project setup.</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
</Tabs>
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import { Separator } from '@/components/ui/separator'
|
||||
```
|
||||
|
||||
```tsx
|
||||
<Separator />
|
||||
```
|
||||
106
apps/design-system/content/docs/components/sheet.mdx
Normal file
106
apps/design-system/content/docs/components/sheet.mdx
Normal file
@@ -0,0 +1,106 @@
|
||||
---
|
||||
title: Sheet
|
||||
description: Extends the Dialog component to display content that complements the main content of the screen.
|
||||
component: true
|
||||
links:
|
||||
doc: https://www.radix-ui.com/docs/primitives/components/dialog
|
||||
api: https://www.radix-ui.com/docs/primitives/components/dialog#api-reference
|
||||
source:
|
||||
radix: true
|
||||
shadcn: true
|
||||
---
|
||||
|
||||
<ComponentPreview name="sheet-demo" peekCode wide />
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs defaultValue="cli">
|
||||
|
||||
<TabsList>
|
||||
<TabsTrigger value="cli">CLI</TabsTrigger>
|
||||
<TabsTrigger value="manual">Manual</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="cli">
|
||||
|
||||
```bash
|
||||
npx shadcn-ui@latest add sheet
|
||||
```
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="manual">
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step>Install the following dependencies:</Step>
|
||||
|
||||
```bash
|
||||
npm install @radix-ui/react-dialog
|
||||
```
|
||||
|
||||
<Step>Copy and paste the following code into your project.</Step>
|
||||
|
||||
<ComponentSource name="sheet" />
|
||||
|
||||
<Step>Update the import paths to match your project setup.</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
</Tabs>
|
||||
|
||||
### Usage
|
||||
|
||||
```tsx
|
||||
import {
|
||||
Sheet,
|
||||
SheetContent,
|
||||
SheetDescription,
|
||||
SheetHeader,
|
||||
SheetTitle,
|
||||
SheetTrigger,
|
||||
} from '@/components/ui/sheet'
|
||||
```
|
||||
|
||||
```tsx
|
||||
<Sheet>
|
||||
<SheetTrigger>Open</SheetTrigger>
|
||||
<SheetContent>
|
||||
<SheetHeader>
|
||||
<SheetTitle>Are you absolutely sure?</SheetTitle>
|
||||
<SheetDescription>
|
||||
This action cannot be undone. This will permanently delete your account and remove your data
|
||||
from our servers.
|
||||
</SheetDescription>
|
||||
</SheetHeader>
|
||||
</SheetContent>
|
||||
</Sheet>
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Side
|
||||
|
||||
Use the `side` property to `<SheetContent />` to indicate the edge of the screen where the component will appear. The values can be `top`, `right`, `bottom` or `left`.
|
||||
|
||||
<ComponentPreview name="sheet-side" />
|
||||
|
||||
### Size
|
||||
|
||||
You can adjust the size of the sheet using CSS classes:
|
||||
|
||||
```tsx {3}
|
||||
<Sheet>
|
||||
<SheetTrigger>Open</SheetTrigger>
|
||||
<SheetContent className="w-[400px] sm:w-[540px]">
|
||||
<SheetHeader>
|
||||
<SheetTitle>Are you absolutely sure?</SheetTitle>
|
||||
<SheetDescription>
|
||||
This action cannot be undone. This will permanently delete your account and remove your data
|
||||
from our servers.
|
||||
</SheetDescription>
|
||||
</SheetHeader>
|
||||
</SheetContent>
|
||||
</Sheet>
|
||||
```
|
||||
57
apps/design-system/content/docs/components/skeleton.mdx
Normal file
57
apps/design-system/content/docs/components/skeleton.mdx
Normal file
@@ -0,0 +1,57 @@
|
||||
---
|
||||
title: Skeleton
|
||||
description: Use to show a placeholder while content is loading.
|
||||
component: true
|
||||
source:
|
||||
shadcn: true
|
||||
---
|
||||
|
||||
<ComponentPreview name="skeleton-demo" peekCode wide />
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs defaultValue="cli">
|
||||
|
||||
<TabsList>
|
||||
<TabsTrigger value="cli">CLI</TabsTrigger>
|
||||
<TabsTrigger value="manual">Manual</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="cli">
|
||||
|
||||
```bash
|
||||
npx shadcn-ui@latest add skeleton
|
||||
```
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="manual">
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step>Copy and paste the following code into your project.</Step>
|
||||
|
||||
<ComponentSource name="skeleton" />
|
||||
|
||||
<Step>Update the import paths to match your project setup.</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
</Tabs>
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import { Skeleton } from '@/components/ui/skeleton'
|
||||
```
|
||||
|
||||
```tsx
|
||||
<Skeleton className="w-[100px] h-[20px] rounded-full" />
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Card
|
||||
|
||||
<ComponentPreview name="skeleton-card" />
|
||||
61
apps/design-system/content/docs/components/slider.mdx
Normal file
61
apps/design-system/content/docs/components/slider.mdx
Normal file
@@ -0,0 +1,61 @@
|
||||
---
|
||||
title: Slider
|
||||
description: An input where the user selects a value from within a given range.
|
||||
component: true
|
||||
links:
|
||||
doc: https://www.radix-ui.com/docs/primitives/components/slider
|
||||
api: https://www.radix-ui.com/docs/primitives/components/slider#api-reference
|
||||
source:
|
||||
radix: true
|
||||
shadcn: true
|
||||
---
|
||||
|
||||
<ComponentPreview name="slider-demo" peekCode wide />
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs defaultValue="cli">
|
||||
|
||||
<TabsList>
|
||||
<TabsTrigger value="cli">CLI</TabsTrigger>
|
||||
<TabsTrigger value="manual">Manual</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="cli">
|
||||
|
||||
```bash
|
||||
npx shadcn-ui@latest add slider
|
||||
```
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="manual">
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step>Install the following dependencies:</Step>
|
||||
|
||||
```bash
|
||||
npm install @radix-ui/react-slider
|
||||
```
|
||||
|
||||
<Step>Copy and paste the following code into your project.</Step>
|
||||
|
||||
<ComponentSource name="slider" />
|
||||
|
||||
<Step>Update the import paths to match your project setup.</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
</Tabs>
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import { Slider } from '@/components/ui/slider'
|
||||
```
|
||||
|
||||
```tsx
|
||||
<Slider defaultValue={[33]} max={100} step={1} />
|
||||
```
|
||||
103
apps/design-system/content/docs/components/sonner.mdx
Normal file
103
apps/design-system/content/docs/components/sonner.mdx
Normal file
@@ -0,0 +1,103 @@
|
||||
---
|
||||
title: Sonner
|
||||
description: An opinionated toast component for React.
|
||||
component: true
|
||||
links:
|
||||
doc: https://sonner.emilkowal.ski
|
||||
source:
|
||||
shadcn: true
|
||||
---
|
||||
|
||||
<ComponentPreview name="sonner-demo" peekCode wide />
|
||||
|
||||
## About
|
||||
|
||||
Sonner is built and maintained by [emilkowalski\_](https://twitter.com/emilkowalski_).
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs defaultValue="cli">
|
||||
|
||||
<TabsList>
|
||||
<TabsTrigger value="cli">CLI</TabsTrigger>
|
||||
<TabsTrigger value="manual">Manual</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="cli">
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step>Run the following command:</Step>
|
||||
|
||||
```bash
|
||||
npx shadcn-ui@latest add sonner
|
||||
```
|
||||
|
||||
<Step>Add the Toaster component</Step>
|
||||
|
||||
```tsx title="app/layout.tsx" {1,9}
|
||||
import { Toaster } from '@/components/ui/sonner'
|
||||
|
||||
export default function RootLayout({ children }) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<head />
|
||||
<body>
|
||||
<main>{children}</main>
|
||||
<Toaster />
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="manual">
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step>Install the following dependencies:</Step>
|
||||
|
||||
```bash
|
||||
npm install sonner next-themes
|
||||
```
|
||||
|
||||
<Step>Copy and paste the following code into your project.</Step>
|
||||
|
||||
<ComponentSource name="sonner" />
|
||||
|
||||
<Step>Add the Toaster component</Step>
|
||||
|
||||
```tsx title="app/layout.tsx" {1,9}
|
||||
import { Toaster } from '@/components/ui/sonner'
|
||||
|
||||
export default function RootLayout({ children }) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<head />
|
||||
<body>
|
||||
<main>{children}</main>
|
||||
<Toaster />
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
</Tabs>
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import { toast } from 'sonner'
|
||||
```
|
||||
|
||||
```tsx
|
||||
toast('Event has been created.')
|
||||
```
|
||||
67
apps/design-system/content/docs/components/switch.mdx
Normal file
67
apps/design-system/content/docs/components/switch.mdx
Normal file
@@ -0,0 +1,67 @@
|
||||
---
|
||||
title: Switch
|
||||
description: A control that allows the user to toggle between checked and not checked.
|
||||
component: true
|
||||
links:
|
||||
doc: https://www.radix-ui.com/docs/primitives/components/switch
|
||||
api: https://www.radix-ui.com/docs/primitives/components/switch#api-reference
|
||||
source:
|
||||
radix: true
|
||||
shadcn: true
|
||||
---
|
||||
|
||||
<ComponentPreview name="switch-demo" peekCode wide />
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs defaultValue="cli">
|
||||
|
||||
<TabsList>
|
||||
<TabsTrigger value="cli">CLI</TabsTrigger>
|
||||
<TabsTrigger value="manual">Manual</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="cli">
|
||||
|
||||
```bash
|
||||
npx shadcn-ui@latest add switch
|
||||
```
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="manual">
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step>Install the following dependencies:</Step>
|
||||
|
||||
```bash
|
||||
npm install @radix-ui/react-switch
|
||||
```
|
||||
|
||||
<Step>Copy and paste the following code into your project.</Step>
|
||||
|
||||
<ComponentSource name="switch" />
|
||||
|
||||
<Step>Update the import paths to match your project setup.</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
</Tabs>
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import { Switch } from '@/components/ui/switch'
|
||||
```
|
||||
|
||||
```tsx
|
||||
<Switch />
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Form
|
||||
|
||||
<ComponentPreview name="switch-form" />
|
||||
85
apps/design-system/content/docs/components/table.mdx
Normal file
85
apps/design-system/content/docs/components/table.mdx
Normal file
@@ -0,0 +1,85 @@
|
||||
---
|
||||
title: Table
|
||||
description: A responsive table component.
|
||||
component: true
|
||||
source:
|
||||
shadcn: true
|
||||
---
|
||||
|
||||
<ComponentPreview name="table-demo" peekCode wide />
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs defaultValue="cli">
|
||||
|
||||
<TabsList>
|
||||
<TabsTrigger value="cli">CLI</TabsTrigger>
|
||||
<TabsTrigger value="manual">Manual</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="cli">
|
||||
|
||||
```bash
|
||||
npx shadcn-ui@latest add table
|
||||
```
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="manual">
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step>Copy and paste the following code into your project.</Step>
|
||||
|
||||
<ComponentSource name="table" />
|
||||
|
||||
<Step>Update the import paths to match your project setup.</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
</Tabs>
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCaption,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from '@/components/ui/table'
|
||||
```
|
||||
|
||||
```tsx
|
||||
<Table>
|
||||
<TableCaption>A list of your recent invoices.</TableCaption>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead className="w-[100px]">Invoice</TableHead>
|
||||
<TableHead>Status</TableHead>
|
||||
<TableHead>Method</TableHead>
|
||||
<TableHead className="text-right">Amount</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableCell className="font-medium">INV001</TableCell>
|
||||
<TableCell>Paid</TableCell>
|
||||
<TableCell>Credit Card</TableCell>
|
||||
<TableCell className="text-right">$250.00</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
```
|
||||
|
||||
## Data Table
|
||||
|
||||
You can use the `<Table />` component to build more complex data tables. Combine it with [@tanstack/react-table](https://tanstack.com/table/v8) to create tables with sorting, filtering and pagination.
|
||||
|
||||
See the [Data Table](/docs/components/data-table) documentation for more information.
|
||||
|
||||
You can also see an example of a data table in the [Tasks](/examples/tasks) demo.
|
||||
68
apps/design-system/content/docs/components/tabs.mdx
Normal file
68
apps/design-system/content/docs/components/tabs.mdx
Normal file
@@ -0,0 +1,68 @@
|
||||
---
|
||||
title: Tabs
|
||||
description: A set of layered sections of content—known as tab panels—that are displayed one at a time.
|
||||
component: true
|
||||
links:
|
||||
doc: https://www.radix-ui.com/docs/primitives/components/tabs
|
||||
api: https://www.radix-ui.com/docs/primitives/components/tabs#api-reference
|
||||
source:
|
||||
radix: true
|
||||
shadcn: true
|
||||
---
|
||||
|
||||
<ComponentPreview name="tabs-demo" peekCode wide />
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs defaultValue="cli">
|
||||
|
||||
<TabsList>
|
||||
<TabsTrigger value="cli">CLI</TabsTrigger>
|
||||
<TabsTrigger value="manual">Manual</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="cli">
|
||||
|
||||
```bash
|
||||
npx shadcn-ui@latest add tabs
|
||||
```
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="manual">
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step>Install the following dependencies:</Step>
|
||||
|
||||
```bash
|
||||
npm install @radix-ui/react-tabs
|
||||
```
|
||||
|
||||
<Step>Copy and paste the following code into your project.</Step>
|
||||
|
||||
<ComponentSource name="tabs" />
|
||||
|
||||
<Step>Update the import paths to match your project setup.</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
</Tabs>
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
||||
```
|
||||
|
||||
```tsx
|
||||
<Tabs defaultValue="account" className="w-[400px]">
|
||||
<TabsList>
|
||||
<TabsTrigger value="account">Account</TabsTrigger>
|
||||
<TabsTrigger value="password">Password</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="account">Make changes to your account here.</TabsContent>
|
||||
<TabsContent value="password">Change your password here.</TabsContent>
|
||||
</Tabs>
|
||||
```
|
||||
77
apps/design-system/content/docs/components/textarea.mdx
Normal file
77
apps/design-system/content/docs/components/textarea.mdx
Normal file
@@ -0,0 +1,77 @@
|
||||
---
|
||||
title: Textarea
|
||||
description: Displays a form textarea or a component that looks like a textarea.
|
||||
component: true
|
||||
source:
|
||||
shadcn: true
|
||||
---
|
||||
|
||||
<ComponentPreview name="textarea-demo" peekCode wide />
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs defaultValue="cli">
|
||||
|
||||
<TabsList>
|
||||
<TabsTrigger value="cli">CLI</TabsTrigger>
|
||||
<TabsTrigger value="manual">Manual</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="cli">
|
||||
|
||||
```bash
|
||||
npx shadcn-ui@latest add textarea
|
||||
```
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="manual">
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step>Copy and paste the following code into your project.</Step>
|
||||
|
||||
<ComponentSource name="textarea" />
|
||||
|
||||
<Step>Update the import paths to match your project setup.</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
</Tabs>
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import { Textarea } from '@/components/ui/textarea'
|
||||
```
|
||||
|
||||
```tsx
|
||||
<Textarea />
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Default
|
||||
|
||||
<ComponentPreview name="textarea-demo" peekCode wide />
|
||||
|
||||
### Disabled
|
||||
|
||||
<ComponentPreview name="textarea-disabled" />
|
||||
|
||||
### With Label
|
||||
|
||||
<ComponentPreview name="textarea-with-label" className="[&_div.grid]:w-full" />
|
||||
|
||||
### With Text
|
||||
|
||||
<ComponentPreview name="textarea-with-text" />
|
||||
|
||||
### With Button
|
||||
|
||||
<ComponentPreview name="textarea-with-button" />
|
||||
|
||||
### Form
|
||||
|
||||
<ComponentPreview name="textarea-form" />
|
||||
155
apps/design-system/content/docs/components/toast.mdx
Normal file
155
apps/design-system/content/docs/components/toast.mdx
Normal file
@@ -0,0 +1,155 @@
|
||||
---
|
||||
title: Toast
|
||||
description: A succinct message that is displayed temporarily.
|
||||
component: true
|
||||
links:
|
||||
doc: https://www.radix-ui.com/docs/primitives/components/toast
|
||||
api: https://www.radix-ui.com/docs/primitives/components/toast#api-reference
|
||||
source:
|
||||
shadcn: true
|
||||
---
|
||||
|
||||
<ComponentPreview name="toast-demo" peekCode wide />
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs defaultValue="cli">
|
||||
|
||||
<TabsList>
|
||||
<TabsTrigger value="cli">CLI</TabsTrigger>
|
||||
<TabsTrigger value="manual">Manual</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="cli">
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step>Run the following command:</Step>
|
||||
|
||||
```bash
|
||||
npx shadcn-ui@latest add toast
|
||||
```
|
||||
|
||||
<Step>Add the Toaster component</Step>
|
||||
|
||||
```tsx title="app/layout.tsx" {1,9}
|
||||
import { Toaster } from '@/components/ui/toaster'
|
||||
|
||||
export default function RootLayout({ children }) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<head />
|
||||
<body>
|
||||
<main>{children}</main>
|
||||
<Toaster />
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="manual">
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step>Install the following dependencies:</Step>
|
||||
|
||||
```bash
|
||||
npm install @radix-ui/react-toast
|
||||
```
|
||||
|
||||
<Step>Copy and paste the following code into your project.</Step>
|
||||
|
||||
`toast.tsx`
|
||||
|
||||
<ComponentSource name="toast" />
|
||||
|
||||
`toaster.tsx`
|
||||
|
||||
<ComponentSource name="toast" fileName="toaster" />
|
||||
|
||||
`use-toast.tsx`
|
||||
|
||||
<ComponentSource name="toast" fileName="use-toast" />
|
||||
|
||||
<Step>Update the import paths to match your project setup.</Step>
|
||||
|
||||
<Step>Add the Toaster component</Step>
|
||||
|
||||
```tsx title="app/layout.tsx" {1,9}
|
||||
import { Toaster } from '@/components/ui/toaster'
|
||||
|
||||
export default function RootLayout({ children }) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<head />
|
||||
<body>
|
||||
<main>{children}</main>
|
||||
<Toaster />
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
</Tabs>
|
||||
|
||||
## Usage
|
||||
|
||||
The `useToast` hook returns a `toast` function that you can use to display a toast.
|
||||
|
||||
```tsx
|
||||
import { useToast } from '@/components/ui/use-toast'
|
||||
```
|
||||
|
||||
```tsx {2,7-10}
|
||||
export const ToastDemo = () => {
|
||||
const { toast } = useToast()
|
||||
|
||||
return (
|
||||
<Button
|
||||
onClick={() => {
|
||||
toast({
|
||||
title: 'Scheduled: Catch up',
|
||||
description: 'Friday, February 10, 2023 at 5:57 PM',
|
||||
})
|
||||
}}
|
||||
>
|
||||
Show Toast
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
<Callout>
|
||||
|
||||
To display multiple toasts at the same time, you can update the `TOAST_LIMIT` in `use-toast.tsx`.
|
||||
|
||||
</Callout>
|
||||
|
||||
## Examples
|
||||
|
||||
### Simple
|
||||
|
||||
<ComponentPreview name="toast-simple" />
|
||||
|
||||
### With title
|
||||
|
||||
<ComponentPreview name="toast-with-title" />
|
||||
|
||||
### With Action
|
||||
|
||||
<ComponentPreview name="toast-with-action" />
|
||||
|
||||
### Destructive
|
||||
|
||||
Use `toast({ variant: "destructive" })` to display a destructive toast.
|
||||
|
||||
<ComponentPreview name="toast-destructive" />
|
||||
91
apps/design-system/content/docs/components/toggle-group.mdx
Normal file
91
apps/design-system/content/docs/components/toggle-group.mdx
Normal file
@@ -0,0 +1,91 @@
|
||||
---
|
||||
title: Toggle Group
|
||||
description: A set of two-state buttons that can be toggled on or off.
|
||||
component: true
|
||||
links:
|
||||
doc: https://www.radix-ui.com/docs/primitives/components/toggle-group
|
||||
api: https://www.radix-ui.com/docs/primitives/components/toggle-group#api-reference
|
||||
source:
|
||||
radix: true
|
||||
shadcn: true
|
||||
---
|
||||
|
||||
<ComponentPreview name="toggle-group-demo" peekCode wide />
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs defaultValue="cli">
|
||||
|
||||
<TabsList>
|
||||
<TabsTrigger value="cli">CLI</TabsTrigger>
|
||||
<TabsTrigger value="manual">Manual</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="cli">
|
||||
|
||||
```bash
|
||||
npx shadcn-ui@latest add toggle-group
|
||||
```
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="manual">
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step>Install the following dependencies:</Step>
|
||||
|
||||
```bash
|
||||
npm install @radix-ui/react-toggle-group
|
||||
```
|
||||
|
||||
<Step>Copy and paste the following code into your project.</Step>
|
||||
|
||||
<ComponentSource name="toggle-group" />
|
||||
|
||||
<Step>Update the import paths to match your project setup.</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
</Tabs>
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group'
|
||||
```
|
||||
|
||||
```tsx
|
||||
<ToggleGroup type="single">
|
||||
<ToggleGroupItem value="a">A</ToggleGroupItem>
|
||||
<ToggleGroupItem value="b">B</ToggleGroupItem>
|
||||
<ToggleGroupItem value="c">C</ToggleGroupItem>
|
||||
</ToggleGroup>
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Default
|
||||
|
||||
<ComponentPreview name="toggle-group-demo" peekCode wide />
|
||||
|
||||
### Outline
|
||||
|
||||
<ComponentPreview name="toggle-group-outline" />
|
||||
|
||||
### Single
|
||||
|
||||
<ComponentPreview name="toggle-group-single" />
|
||||
|
||||
### Small
|
||||
|
||||
<ComponentPreview name="toggle-group-sm" />
|
||||
|
||||
### Large
|
||||
|
||||
<ComponentPreview name="toggle-group-lg" />
|
||||
|
||||
### Disabled
|
||||
|
||||
<ComponentPreview name="toggle-group-disabled" />
|
||||
87
apps/design-system/content/docs/components/toggle.mdx
Normal file
87
apps/design-system/content/docs/components/toggle.mdx
Normal file
@@ -0,0 +1,87 @@
|
||||
---
|
||||
title: Toggle
|
||||
description: A two-state button that can be either on or off.
|
||||
component: true
|
||||
links:
|
||||
doc: https://www.radix-ui.com/docs/primitives/components/toggle
|
||||
api: https://www.radix-ui.com/docs/primitives/components/toggle#api-reference
|
||||
source:
|
||||
radix: true
|
||||
shadcn: true
|
||||
---
|
||||
|
||||
<ComponentPreview name="toggle-demo" peekCode wide />
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs defaultValue="cli">
|
||||
|
||||
<TabsList>
|
||||
<TabsTrigger value="cli">CLI</TabsTrigger>
|
||||
<TabsTrigger value="manual">Manual</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="cli">
|
||||
|
||||
```bash
|
||||
npx shadcn-ui@latest add toggle
|
||||
```
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="manual">
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step>Install the following dependencies:</Step>
|
||||
|
||||
```bash
|
||||
npm install @radix-ui/react-toggle
|
||||
```
|
||||
|
||||
<Step>Copy and paste the following code into your project.</Step>
|
||||
|
||||
<ComponentSource name="toggle" />
|
||||
|
||||
<Step>Update the import paths to match your project setup.</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
</Tabs>
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import { Toggle } from '@/components/ui/toggle'
|
||||
```
|
||||
|
||||
```tsx
|
||||
<Toggle>Toggle</Toggle>
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Default
|
||||
|
||||
<ComponentPreview name="toggle-demo" peekCode wide />
|
||||
|
||||
### Outline
|
||||
|
||||
<ComponentPreview name="toggle-outline" />
|
||||
|
||||
### With Text
|
||||
|
||||
<ComponentPreview name="toggle-with-text" />
|
||||
|
||||
### Small
|
||||
|
||||
<ComponentPreview name="toggle-sm" />
|
||||
|
||||
### Large
|
||||
|
||||
<ComponentPreview name="toggle-lg" />
|
||||
|
||||
### Disabled
|
||||
|
||||
<ComponentPreview name="toggle-disabled" />
|
||||
68
apps/design-system/content/docs/components/tooltip.mdx
Normal file
68
apps/design-system/content/docs/components/tooltip.mdx
Normal file
@@ -0,0 +1,68 @@
|
||||
---
|
||||
title: Tooltip
|
||||
description: A popup that displays information related to an element when the element receives keyboard focus or the mouse hovers over it.
|
||||
component: true
|
||||
links:
|
||||
doc: https://www.radix-ui.com/docs/primitives/components/tooltip
|
||||
api: https://www.radix-ui.com/docs/primitives/components/tooltip#api-reference
|
||||
source:
|
||||
radix: true
|
||||
shadcn: true
|
||||
---
|
||||
|
||||
<ComponentPreview name="tooltip-demo" peekCode wide />
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs defaultValue="cli">
|
||||
|
||||
<TabsList>
|
||||
<TabsTrigger value="cli">CLI</TabsTrigger>
|
||||
<TabsTrigger value="manual">Manual</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="cli">
|
||||
|
||||
```bash
|
||||
npx shadcn-ui@latest add tooltip
|
||||
```
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="manual">
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step>Install the following dependencies:</Step>
|
||||
|
||||
```bash
|
||||
npm install @radix-ui/react-tooltip
|
||||
```
|
||||
|
||||
<Step>Copy and paste the following code into your project.</Step>
|
||||
|
||||
<ComponentSource name="tooltip" />
|
||||
|
||||
<Step>Update the import paths to match your project setup.</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
</Tabs>
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'
|
||||
```
|
||||
|
||||
```tsx
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger>Hover</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>Add to library</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
```
|
||||
61
apps/design-system/content/docs/components/typography.mdx
Normal file
61
apps/design-system/content/docs/components/typography.mdx
Normal file
@@ -0,0 +1,61 @@
|
||||
---
|
||||
title: Typography
|
||||
description: Styles for headings, paragraphs, lists...etc
|
||||
component: true
|
||||
source:
|
||||
shadcn: true
|
||||
---
|
||||
|
||||
<ComponentPreview name="typography-demo" className="[&>div.min-h-[350px]]:p-6" peekCode wide />
|
||||
|
||||
## h1
|
||||
|
||||
<ComponentPreview name="typography-h1" extractClassName />
|
||||
|
||||
## h2
|
||||
|
||||
<ComponentPreview name="typography-h2" />
|
||||
|
||||
## h3
|
||||
|
||||
<ComponentPreview name="typography-h3" />
|
||||
|
||||
## h4
|
||||
|
||||
<ComponentPreview name="typography-h4" />
|
||||
|
||||
## p
|
||||
|
||||
<ComponentPreview name="typography-p" />
|
||||
|
||||
## blockquote
|
||||
|
||||
<ComponentPreview name="typography-blockquote" />
|
||||
|
||||
## table
|
||||
|
||||
<ComponentPreview name="typography-table" />
|
||||
|
||||
## list
|
||||
|
||||
<ComponentPreview name="typography-list" />
|
||||
|
||||
## Inline code
|
||||
|
||||
<ComponentPreview name="typography-inline-code" />
|
||||
|
||||
## Lead
|
||||
|
||||
<ComponentPreview name="typography-lead" />
|
||||
|
||||
## Large
|
||||
|
||||
<ComponentPreview name="typography-large" />
|
||||
|
||||
## Small
|
||||
|
||||
<ComponentPreview name="typography-small" />
|
||||
|
||||
## Muted
|
||||
|
||||
<ComponentPreview name="typography-muted" />
|
||||
28
apps/design-system/content/docs/figma.mdx
Normal file
28
apps/design-system/content/docs/figma.mdx
Normal file
@@ -0,0 +1,28 @@
|
||||
---
|
||||
title: Figma
|
||||
description: Every component recreated in Figma. With customizable props, typography and icons.
|
||||
---
|
||||
|
||||
The Figma UI Kit is open sourced by [Pietro Schirano](https://twitter.com/skirano).
|
||||
|
||||
<AspectRatio ratio={12 / 9} className="w-full mt-4">
|
||||
<iframe
|
||||
src="https://www.figma.com/embed?embed_host=share&url=https%3A%2F%2Fwww.figma.com%2Fdesign%2FWCja3lpEj1DeunV1d5zu5D%2FDesign-System%3Fnode-id%3D520%253A1715%26t%3Dz8Quu7PADvSqD0Ss-1"
|
||||
className="h-full w-full overflow-hidden rounded-lg border bg-surface-100"
|
||||
></iframe>
|
||||
</AspectRatio>
|
||||
|
||||
## Additional resources
|
||||
|
||||
### Figma Diagram Kit
|
||||
|
||||
<AspectRatio ratio={12 / 9} className="w-full mt-4">
|
||||
<iframe
|
||||
src="https://www.figma.com/embed?embed_host=share&url=https%3A%2F%2Fwww.figma.com%2Fdesign%2Fd6EV8a9G2yLht01AayAkmJ%2FDiagram-Kit-(Variables)%3Fnode-id%3D47%253A7265%26t%3D3JsjisJgJqD08wGG-1"
|
||||
className="h-full w-full overflow-hidden rounded-lg border bg-surface-100"
|
||||
></iframe>
|
||||
</AspectRatio>
|
||||
|
||||
## Grab a copy
|
||||
|
||||
https://www.figma.com/community/file/1203061493325953101
|
||||
@@ -0,0 +1,88 @@
|
||||
---
|
||||
title: Form Item Layout
|
||||
description: A helper component that provides a layout for form items.
|
||||
component: true
|
||||
fragment: true
|
||||
---
|
||||
|
||||
<ComponentPreview
|
||||
name="form-item-layout-demo"
|
||||
description="A helper component that provides a layout for form items."
|
||||
peekCode
|
||||
showDottedGrid
|
||||
wide
|
||||
/>
|
||||
|
||||
## Usage
|
||||
|
||||
This component is to help with the layout of form items. It automatically provides `FormItem`, `FormLabel`, `FormMessage` and `FormDescription`.
|
||||
The styling and layout of these components can be customized by passing in the components as props.
|
||||
|
||||
The components that can be replaced in `react-hook-form` atoms are highlighted below:
|
||||
|
||||
```tsx showLineNumbers {1-2,6-8}
|
||||
<FormItem_Shadcn_>
|
||||
<FormLabel_Shadcn_>Username</FormLabel_Shadcn_>
|
||||
<FormControl_Shadcn_>
|
||||
<Input placeholder="shadcn" {...field} />
|
||||
</FormControl_Shadcn_>
|
||||
<FormDescription_Shadcn_>This is your public display name.</FormDescription_Shadcn_>
|
||||
<FormMessage_Shadcn_ />
|
||||
</FormItem_Shadcn_>
|
||||
```
|
||||
|
||||
Using `FormItemLayout` it can look like this:
|
||||
|
||||
```tsx
|
||||
<FormItemLayout label="Username" description="This is your public display name">
|
||||
<FormControl_Shadcn_>
|
||||
<Input placeholder="mildtomato" {...field} />
|
||||
</FormControl_Shadcn_>
|
||||
</FormItemLayout>
|
||||
```
|
||||
|
||||
Please note that you must still use `FormControl_Shadcn_` to wrap input fields.
|
||||
|
||||
## Examples
|
||||
|
||||
## With Select component
|
||||
|
||||
<ComponentPreview
|
||||
name="form-item-layout-with-select"
|
||||
description="A helper component that provides a layout for form items."
|
||||
showDottedGrid
|
||||
/>
|
||||
|
||||
## Horizontal layout
|
||||
|
||||
This is useful for forms inside a `<SidePanel/>` / `<Sheet/>` component.
|
||||
|
||||
<ComponentPreview
|
||||
name="form-item-layout-with-horizontal"
|
||||
description="A helper component that provides a layout for form items."
|
||||
showDottedGrid
|
||||
/>
|
||||
|
||||
## With Switch component
|
||||
|
||||
<ComponentPreview
|
||||
name="form-item-layout-with-switch"
|
||||
description="A helper component that provides a layout for form items."
|
||||
showDottedGrid
|
||||
/>
|
||||
|
||||
## With Checkbox component
|
||||
|
||||
<ComponentPreview
|
||||
name="form-item-layout-with-checkbox"
|
||||
description="A helper component that provides a layout for form items."
|
||||
showDottedGrid
|
||||
/>
|
||||
|
||||
## List of items as Checkboxes
|
||||
|
||||
<ComponentPreview
|
||||
name="form-item-layout-with-checkbox-list"
|
||||
description="A helper component that provides a layout for form items."
|
||||
showDottedGrid
|
||||
/>
|
||||
@@ -0,0 +1,57 @@
|
||||
---
|
||||
title: Text Confirm Dialog
|
||||
description: A modal dialog that interrupts the user with important content and expects a response.
|
||||
component: true
|
||||
---
|
||||
|
||||
<ComponentPreview
|
||||
name="text-confirm-dialog-demo"
|
||||
description="An alert with an icon, title and description. The title says 'Heads up!' and the description is 'You can add components to your app using the cli.'."
|
||||
peekCode
|
||||
showDottedGrid
|
||||
wide
|
||||
/>
|
||||
|
||||
## Examples
|
||||
|
||||
### With Info Alert
|
||||
|
||||
<ComponentPreview
|
||||
name="text-confirm-dialog-with-info-alert"
|
||||
description="An alert with an icon, title and description. The title says 'Heads up!' and the description is 'You can add components to your app using the cli.'."
|
||||
/>
|
||||
|
||||
### With warning Alert
|
||||
|
||||
<ComponentPreview
|
||||
name="text-confirm-dialog-with-warning-alert"
|
||||
description="An alert with a destructive variant. The title says 'Delete this item?' and the description is 'This action cannot be undone.'."
|
||||
/>
|
||||
|
||||
### With destructive Alert
|
||||
|
||||
<ComponentPreview
|
||||
name="text-confirm-dialog-with-destructive-alert"
|
||||
description="An alert with a destructive variant. The title says 'Delete this item?' and the description is 'This action cannot be undone.'."
|
||||
/>
|
||||
|
||||
### With cancel button
|
||||
|
||||
<ComponentPreview
|
||||
name="text-confirm-dialog-with-cancel-button"
|
||||
description="An alert with a destructive variant. The title says 'Delete this item?' and the description is 'This action cannot be undone.'."
|
||||
/>
|
||||
|
||||
### With children
|
||||
|
||||
<ComponentPreview
|
||||
name="text-confirm-dialog-with-children"
|
||||
description="An alert with a destructive variant. The title says 'Delete this item?' and the description is 'This action cannot be undone.'."
|
||||
/>
|
||||
|
||||
### With size
|
||||
|
||||
<ComponentPreview
|
||||
name="text-confirm-dialog-with-size"
|
||||
description="An alert with a destructive variant. The title says 'Delete this item?' and the description is 'This action cannot be undone.'."
|
||||
/>
|
||||
6
apps/design-system/content/docs/icons.mdx
Normal file
6
apps/design-system/content/docs/icons.mdx
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
title: Icons
|
||||
description: Icons system breakdown. Copy values of Icons.
|
||||
---
|
||||
|
||||
<Icons />
|
||||
24
apps/design-system/content/docs/index.mdx
Normal file
24
apps/design-system/content/docs/index.mdx
Normal file
@@ -0,0 +1,24 @@
|
||||
---
|
||||
title: Introduction
|
||||
description: Components and patterns that you can copy and paste into Supabase apps. Accessible. Customizable. Open Source.
|
||||
---
|
||||
|
||||
## FAQ
|
||||
|
||||
<Accordion type="multiple">
|
||||
|
||||
<AccordionItem value="faq-1">
|
||||
<AccordionTrigger>
|
||||
Why copy/paste and not packaged as a dependency?
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
The idea behind this is to give you ownership and control over the code, allowing you to decide how the components are built and styled.
|
||||
|
||||
Start with some sensible defaults, then customize the components to your needs.
|
||||
|
||||
One of the drawback of packaging the components in an npm package is that the style is coupled with the implementation. _The design of your components should be separate from their implementation._
|
||||
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
|
||||
</Accordion>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user