diff --git a/.prettierignore b/.prettierignore index bc42d43c6b..10d155ba31 100644 --- a/.prettierignore +++ b/.prettierignore @@ -16,3 +16,4 @@ apps/docs/pages/guides/integrations/*.mdx apps/studio/public apps/**/.turbo apps/docs/CONTRIBUTING.md +apps/design-system/__registry__ \ No newline at end of file diff --git a/apps/design-system/.eslintrc.json b/apps/design-system/.eslintrc.json new file mode 100644 index 0000000000..bffb357a71 --- /dev/null +++ b/apps/design-system/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "next/core-web-vitals" +} diff --git a/apps/design-system/.gitignore b/apps/design-system/.gitignore new file mode 100644 index 0000000000..57c2e5aba2 --- /dev/null +++ b/apps/design-system/.gitignore @@ -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 \ No newline at end of file diff --git a/apps/design-system/README.md b/apps/design-system/README.md new file mode 100644 index 0000000000..c4033664f8 --- /dev/null +++ b/apps/design-system/README.md @@ -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. diff --git a/apps/design-system/__registry__/index.tsx b/apps/design-system/__registry__/index.tsx new file mode 100644 index 0000000000..c5e960a978 --- /dev/null +++ b/apps/design-system/__registry__/index.tsx @@ -0,0 +1,1593 @@ +// @ts-nocheck +// This file is autogenerated by scripts/build-registry.ts +// Do not edit this file directly. +import * as React from "react" + +export const Index: Record = { + "default": { + "ConfirmationModal": { + name: "ConfirmationModal", + type: "components:fragment", + registryDependencies: undefined, + component: React.lazy(() => import("@/../../packages/ui-patterns/Dialogs/ConfirmationModal")), + source: "", + files: ["registry/default//Dialogs/ConfirmationModal.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "TextConfirmModal": { + name: "TextConfirmModal", + type: "components:fragment", + registryDependencies: undefined, + component: React.lazy(() => import("@/../../packages/ui-patterns/Dialogs/TextConfirmModal")), + source: "", + files: ["registry/default//Dialogs/TextConfirmModal.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "ConfirmDialog": { + name: "ConfirmDialog", + type: "components:fragment", + registryDependencies: undefined, + component: React.lazy(() => import("@/../../packages/ui-patterns/Dialogs/ConfirmDialog")), + source: "", + files: ["registry/default//Dialogs/ConfirmDialog.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "accordion-demo": { + name: "accordion-demo", + type: "components:example", + registryDependencies: ["accordion"], + component: React.lazy(() => import("@/registry/default/example/accordion-demo")), + source: "", + files: ["registry/default/example/accordion-demo.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "alert-demo": { + name: "alert-demo", + type: "components:example", + registryDependencies: ["alert"], + component: React.lazy(() => import("@/registry/default/example/alert-demo")), + source: "", + files: ["registry/default/example/alert-demo.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "alert-destructive": { + name: "alert-destructive", + type: "components:example", + registryDependencies: ["alert"], + component: React.lazy(() => import("@/registry/default/example/alert-destructive")), + source: "", + files: ["registry/default/example/alert-destructive.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "alert-dialog-demo": { + name: "alert-dialog-demo", + type: "components:example", + registryDependencies: ["alert-dialog","button"], + component: React.lazy(() => import("@/registry/default/example/alert-dialog-demo")), + source: "", + files: ["registry/default/example/alert-dialog-demo.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "aspect-ratio-demo": { + name: "aspect-ratio-demo", + type: "components:example", + registryDependencies: ["aspect-ratio"], + component: React.lazy(() => import("@/registry/default/example/aspect-ratio-demo")), + source: "", + files: ["registry/default/example/aspect-ratio-demo.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "avatar-demo": { + name: "avatar-demo", + type: "components:example", + registryDependencies: ["avatar"], + component: React.lazy(() => import("@/registry/default/example/avatar-demo")), + source: "", + files: ["registry/default/example/avatar-demo.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "badge-demo": { + name: "badge-demo", + type: "components:example", + registryDependencies: ["badge"], + component: React.lazy(() => import("@/registry/default/example/badge-demo")), + source: "", + files: ["registry/default/example/badge-demo.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "badge-destructive": { + name: "badge-destructive", + type: "components:example", + registryDependencies: ["badge"], + component: React.lazy(() => import("@/registry/default/example/badge-destructive")), + source: "", + files: ["registry/default/example/badge-destructive.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "badge-outline": { + name: "badge-outline", + type: "components:example", + registryDependencies: ["badge"], + component: React.lazy(() => import("@/registry/default/example/badge-outline")), + source: "", + files: ["registry/default/example/badge-outline.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "badge-secondary": { + name: "badge-secondary", + type: "components:example", + registryDependencies: ["badge"], + component: React.lazy(() => import("@/registry/default/example/badge-secondary")), + source: "", + files: ["registry/default/example/badge-secondary.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "button-demo": { + name: "button-demo", + type: "components:example", + registryDependencies: ["button"], + component: React.lazy(() => import("@/registry/default/example/button-demo")), + source: "", + files: ["registry/default/example/button-demo.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "button-secondary": { + name: "button-secondary", + type: "components:example", + registryDependencies: ["button"], + component: React.lazy(() => import("@/registry/default/example/button-secondary")), + source: "", + files: ["registry/default/example/button-secondary.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "button-destructive": { + name: "button-destructive", + type: "components:example", + registryDependencies: ["button"], + component: React.lazy(() => import("@/registry/default/example/button-destructive")), + source: "", + files: ["registry/default/example/button-destructive.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "button-outline": { + name: "button-outline", + type: "components:example", + registryDependencies: ["button"], + component: React.lazy(() => import("@/registry/default/example/button-outline")), + source: "", + files: ["registry/default/example/button-outline.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "button-ghost": { + name: "button-ghost", + type: "components:example", + registryDependencies: ["button"], + component: React.lazy(() => import("@/registry/default/example/button-ghost")), + source: "", + files: ["registry/default/example/button-ghost.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "button-link": { + name: "button-link", + type: "components:example", + registryDependencies: ["button"], + component: React.lazy(() => import("@/registry/default/example/button-link")), + source: "", + files: ["registry/default/example/button-link.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "button-with-icon": { + name: "button-with-icon", + type: "components:example", + registryDependencies: ["button"], + component: React.lazy(() => import("@/registry/default/example/button-with-icon")), + source: "", + files: ["registry/default/example/button-with-icon.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "button-loading": { + name: "button-loading", + type: "components:example", + registryDependencies: ["button"], + component: React.lazy(() => import("@/registry/default/example/button-loading")), + source: "", + files: ["registry/default/example/button-loading.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "button-icon": { + name: "button-icon", + type: "components:example", + registryDependencies: ["button"], + component: React.lazy(() => import("@/registry/default/example/button-icon")), + source: "", + files: ["registry/default/example/button-icon.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "button-as-child": { + name: "button-as-child", + type: "components:example", + registryDependencies: ["button"], + component: React.lazy(() => import("@/registry/default/example/button-as-child")), + source: "", + files: ["registry/default/example/button-as-child.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "calendar-demo": { + name: "calendar-demo", + type: "components:example", + registryDependencies: ["calendar"], + component: React.lazy(() => import("@/registry/default/example/calendar-demo")), + source: "", + files: ["registry/default/example/calendar-demo.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "calendar-form": { + name: "calendar-form", + type: "components:example", + registryDependencies: ["calendar","form","popover"], + component: React.lazy(() => import("@/registry/default/example/calendar-form")), + source: "", + files: ["registry/default/example/calendar-form.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "checkbox-demo": { + name: "checkbox-demo", + type: "components:example", + registryDependencies: ["checkbox"], + component: React.lazy(() => import("@/registry/default/example/checkbox-demo")), + source: "", + files: ["registry/default/example/checkbox-demo.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "checkbox-disabled": { + name: "checkbox-disabled", + type: "components:example", + registryDependencies: ["checkbox"], + component: React.lazy(() => import("@/registry/default/example/checkbox-disabled")), + source: "", + files: ["registry/default/example/checkbox-disabled.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "checkbox-form-multiple": { + name: "checkbox-form-multiple", + type: "components:example", + registryDependencies: ["checkbox","form"], + component: React.lazy(() => import("@/registry/default/example/checkbox-form-multiple")), + source: "", + files: ["registry/default/example/checkbox-form-multiple.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "checkbox-form-single": { + name: "checkbox-form-single", + type: "components:example", + registryDependencies: ["checkbox","form"], + component: React.lazy(() => import("@/registry/default/example/checkbox-form-single")), + source: "", + files: ["registry/default/example/checkbox-form-single.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "checkbox-with-text": { + name: "checkbox-with-text", + type: "components:example", + registryDependencies: ["checkbox"], + component: React.lazy(() => import("@/registry/default/example/checkbox-with-text")), + source: "", + files: ["registry/default/example/checkbox-with-text.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "collapsible-demo": { + name: "collapsible-demo", + type: "components:example", + registryDependencies: ["collapsible"], + component: React.lazy(() => import("@/registry/default/example/collapsible-demo")), + source: "", + files: ["registry/default/example/collapsible-demo.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "combobox-demo": { + name: "combobox-demo", + type: "components:example", + registryDependencies: ["command"], + component: React.lazy(() => import("@/registry/default/example/combobox-demo")), + source: "", + files: ["registry/default/example/combobox-demo.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "combobox-dropdown-menu": { + name: "combobox-dropdown-menu", + type: "components:example", + registryDependencies: ["command","dropdown-menu","button"], + component: React.lazy(() => import("@/registry/default/example/combobox-dropdown-menu")), + source: "", + files: ["registry/default/example/combobox-dropdown-menu.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "combobox-form": { + name: "combobox-form", + type: "components:example", + registryDependencies: ["command","form"], + component: React.lazy(() => import("@/registry/default/example/combobox-form")), + source: "", + files: ["registry/default/example/combobox-form.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "combobox-popover": { + name: "combobox-popover", + type: "components:example", + registryDependencies: ["combobox","popover"], + component: React.lazy(() => import("@/registry/default/example/combobox-popover")), + source: "", + files: ["registry/default/example/combobox-popover.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "combobox-responsive": { + name: "combobox-responsive", + type: "components:example", + registryDependencies: ["combobox","popover","drawer"], + component: React.lazy(() => import("@/registry/default/example/combobox-responsive")), + source: "", + files: ["registry/default/example/combobox-responsive.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "command-demo": { + name: "command-demo", + type: "components:example", + registryDependencies: ["command"], + component: React.lazy(() => import("@/registry/default/example/command-demo")), + source: "", + files: ["registry/default/example/command-demo.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "command-dialog": { + name: "command-dialog", + type: "components:example", + registryDependencies: ["command","dialog"], + component: React.lazy(() => import("@/registry/default/example/command-dialog")), + source: "", + files: ["registry/default/example/command-dialog.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "context-menu-demo": { + name: "context-menu-demo", + type: "components:example", + registryDependencies: ["context-menu"], + component: React.lazy(() => import("@/registry/default/example/context-menu-demo")), + source: "", + files: ["registry/default/example/context-menu-demo.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "date-picker-demo": { + name: "date-picker-demo", + type: "components:example", + registryDependencies: ["button","calendar","popover"], + component: React.lazy(() => import("@/registry/default/example/date-picker-demo")), + source: "", + files: ["registry/default/example/date-picker-demo.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "date-picker-form": { + name: "date-picker-form", + type: "components:example", + registryDependencies: ["button","calendar","form","popover"], + component: React.lazy(() => import("@/registry/default/example/date-picker-form")), + source: "", + files: ["registry/default/example/date-picker-form.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "date-picker-with-presets": { + name: "date-picker-with-presets", + type: "components:example", + registryDependencies: ["button","calendar","popover","select"], + component: React.lazy(() => import("@/registry/default/example/date-picker-with-presets")), + source: "", + files: ["registry/default/example/date-picker-with-presets.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "date-picker-with-range": { + name: "date-picker-with-range", + type: "components:example", + registryDependencies: ["button","calendar","popover"], + component: React.lazy(() => import("@/registry/default/example/date-picker-with-range")), + source: "", + files: ["registry/default/example/date-picker-with-range.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "dialog-demo": { + name: "dialog-demo", + type: "components:example", + registryDependencies: ["dialog"], + component: React.lazy(() => import("@/registry/default/example/dialog-demo")), + source: "", + files: ["registry/default/example/dialog-demo.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "dialog-close-button": { + name: "dialog-close-button", + type: "components:example", + registryDependencies: ["dialog","button"], + component: React.lazy(() => import("@/registry/default/example/dialog-close-button")), + source: "", + files: ["registry/default/example/dialog-close-button.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "drawer-demo": { + name: "drawer-demo", + type: "components:example", + registryDependencies: ["drawer"], + component: React.lazy(() => import("@/registry/default/example/drawer-demo")), + source: "", + files: ["registry/default/example/drawer-demo.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "drawer-dialog": { + name: "drawer-dialog", + type: "components:example", + registryDependencies: ["drawer","dialog"], + component: React.lazy(() => import("@/registry/default/example/drawer-dialog")), + source: "", + files: ["registry/default/example/drawer-dialog.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "dropdown-menu-demo": { + name: "dropdown-menu-demo", + type: "components:example", + registryDependencies: ["dropdown-menu"], + component: React.lazy(() => import("@/registry/default/example/dropdown-menu-demo")), + source: "", + files: ["registry/default/example/dropdown-menu-demo.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "dropdown-menu-checkboxes": { + name: "dropdown-menu-checkboxes", + type: "components:example", + registryDependencies: ["dropdown-menu","checkbox"], + component: React.lazy(() => import("@/registry/default/example/dropdown-menu-checkboxes")), + source: "", + files: ["registry/default/example/dropdown-menu-checkboxes.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "dropdown-menu-radio-group": { + name: "dropdown-menu-radio-group", + type: "components:example", + registryDependencies: ["dropdown-menu","radio-group"], + component: React.lazy(() => import("@/registry/default/example/dropdown-menu-radio-group")), + source: "", + files: ["registry/default/example/dropdown-menu-radio-group.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "hover-card-demo": { + name: "hover-card-demo", + type: "components:example", + registryDependencies: ["hover-card"], + component: React.lazy(() => import("@/registry/default/example/hover-card-demo")), + source: "", + files: ["registry/default/example/hover-card-demo.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "input-demo": { + name: "input-demo", + type: "components:example", + registryDependencies: ["input"], + component: React.lazy(() => import("@/registry/default/example/input-demo")), + source: "", + files: ["registry/default/example/input-demo.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "input-disabled": { + name: "input-disabled", + type: "components:example", + registryDependencies: ["input"], + component: React.lazy(() => import("@/registry/default/example/input-disabled")), + source: "", + files: ["registry/default/example/input-disabled.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "input-file": { + name: "input-file", + type: "components:example", + registryDependencies: ["input"], + component: React.lazy(() => import("@/registry/default/example/input-file")), + source: "", + files: ["registry/default/example/input-file.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "input-form": { + name: "input-form", + type: "components:example", + registryDependencies: ["input","button","form"], + component: React.lazy(() => import("@/registry/default/example/input-form")), + source: "", + files: ["registry/default/example/input-form.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "input-with-button": { + name: "input-with-button", + type: "components:example", + registryDependencies: ["input","button"], + component: React.lazy(() => import("@/registry/default/example/input-with-button")), + source: "", + files: ["registry/default/example/input-with-button.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "input-with-label": { + name: "input-with-label", + type: "components:example", + registryDependencies: ["input","button","label"], + component: React.lazy(() => import("@/registry/default/example/input-with-label")), + source: "", + files: ["registry/default/example/input-with-label.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "input-with-text": { + name: "input-with-text", + type: "components:example", + registryDependencies: ["input","button","label"], + component: React.lazy(() => import("@/registry/default/example/input-with-text")), + source: "", + files: ["registry/default/example/input-with-text.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "input-otp-demo": { + name: "input-otp-demo", + type: "components:example", + registryDependencies: ["input-otp"], + component: React.lazy(() => import("@/registry/default/example/input-otp-demo")), + source: "", + files: ["registry/default/example/input-otp-demo.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "input-otp-pattern": { + name: "input-otp-pattern", + type: "components:example", + registryDependencies: ["input-otp"], + component: React.lazy(() => import("@/registry/default/example/input-otp-pattern")), + source: "", + files: ["registry/default/example/input-otp-pattern.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "input-otp-separator": { + name: "input-otp-separator", + type: "components:example", + registryDependencies: ["input-otp"], + component: React.lazy(() => import("@/registry/default/example/input-otp-separator")), + source: "", + files: ["registry/default/example/input-otp-separator.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "input-otp-controlled": { + name: "input-otp-controlled", + type: "components:example", + registryDependencies: ["input-otp"], + component: React.lazy(() => import("@/registry/default/example/input-otp-controlled")), + source: "", + files: ["registry/default/example/input-otp-controlled.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "input-otp-form": { + name: "input-otp-form", + type: "components:example", + registryDependencies: ["input-otp","form"], + component: React.lazy(() => import("@/registry/default/example/input-otp-form")), + source: "", + files: ["registry/default/example/input-otp-form.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "label-demo": { + name: "label-demo", + type: "components:example", + registryDependencies: ["label"], + component: React.lazy(() => import("@/registry/default/example/label-demo")), + source: "", + files: ["registry/default/example/label-demo.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "menubar-demo": { + name: "menubar-demo", + type: "components:example", + registryDependencies: ["menubar"], + component: React.lazy(() => import("@/registry/default/example/menubar-demo")), + source: "", + files: ["registry/default/example/menubar-demo.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "navigation-menu-demo": { + name: "navigation-menu-demo", + type: "components:example", + registryDependencies: ["navigation-menu"], + component: React.lazy(() => import("@/registry/default/example/navigation-menu-demo")), + source: "", + files: ["registry/default/example/navigation-menu-demo.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "popover-demo": { + name: "popover-demo", + type: "components:example", + registryDependencies: ["popover"], + component: React.lazy(() => import("@/registry/default/example/popover-demo")), + source: "", + files: ["registry/default/example/popover-demo.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "progress-demo": { + name: "progress-demo", + type: "components:example", + registryDependencies: ["progress"], + component: React.lazy(() => import("@/registry/default/example/progress-demo")), + source: "", + files: ["registry/default/example/progress-demo.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "radio-group-demo": { + name: "radio-group-demo", + type: "components:example", + registryDependencies: ["radio-group"], + component: React.lazy(() => import("@/registry/default/example/radio-group-demo")), + source: "", + files: ["registry/default/example/radio-group-demo.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "radio-group-form": { + name: "radio-group-form", + type: "components:example", + registryDependencies: ["radio-group","form"], + component: React.lazy(() => import("@/registry/default/example/radio-group-form")), + source: "", + files: ["registry/default/example/radio-group-form.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "resizable-demo": { + name: "resizable-demo", + type: "components:example", + registryDependencies: ["resizable"], + component: React.lazy(() => import("@/registry/default/example/resizable-demo")), + source: "", + files: ["registry/default/example/resizable-demo.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "resizable-demo-with-handle": { + name: "resizable-demo-with-handle", + type: "components:example", + registryDependencies: ["resizable"], + component: React.lazy(() => import("@/registry/default/example/resizable-demo-with-handle")), + source: "", + files: ["registry/default/example/resizable-demo-with-handle.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "resizable-vertical": { + name: "resizable-vertical", + type: "components:example", + registryDependencies: ["resizable"], + component: React.lazy(() => import("@/registry/default/example/resizable-vertical")), + source: "", + files: ["registry/default/example/resizable-vertical.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "resizable-handle": { + name: "resizable-handle", + type: "components:example", + registryDependencies: ["resizable"], + component: React.lazy(() => import("@/registry/default/example/resizable-handle")), + source: "", + files: ["registry/default/example/resizable-handle.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "scroll-area-demo": { + name: "scroll-area-demo", + type: "components:example", + registryDependencies: ["scroll-area"], + component: React.lazy(() => import("@/registry/default/example/scroll-area-demo")), + source: "", + files: ["registry/default/example/scroll-area-demo.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "scroll-area-horizontal-demo": { + name: "scroll-area-horizontal-demo", + type: "components:example", + registryDependencies: ["scroll-area"], + component: React.lazy(() => import("@/registry/default/example/scroll-area-horizontal-demo")), + source: "", + files: ["registry/default/example/scroll-area-horizontal-demo.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "select-demo": { + name: "select-demo", + type: "components:example", + registryDependencies: ["select"], + component: React.lazy(() => import("@/registry/default/example/select-demo")), + source: "", + files: ["registry/default/example/select-demo.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "select-scrollable": { + name: "select-scrollable", + type: "components:example", + registryDependencies: ["select"], + component: React.lazy(() => import("@/registry/default/example/select-scrollable")), + source: "", + files: ["registry/default/example/select-scrollable.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "select-form": { + name: "select-form", + type: "components:example", + registryDependencies: ["select"], + component: React.lazy(() => import("@/registry/default/example/select-form")), + source: "", + files: ["registry/default/example/select-form.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "separator-demo": { + name: "separator-demo", + type: "components:example", + registryDependencies: ["separator"], + component: React.lazy(() => import("@/registry/default/example/separator-demo")), + source: "", + files: ["registry/default/example/separator-demo.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "sheet-demo": { + name: "sheet-demo", + type: "components:example", + registryDependencies: ["sheet"], + component: React.lazy(() => import("@/registry/default/example/sheet-demo")), + source: "", + files: ["registry/default/example/sheet-demo.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "sheet-side": { + name: "sheet-side", + type: "components:example", + registryDependencies: ["sheet"], + component: React.lazy(() => import("@/registry/default/example/sheet-side")), + source: "", + files: ["registry/default/example/sheet-side.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "skeleton-demo": { + name: "skeleton-demo", + type: "components:example", + registryDependencies: ["skeleton"], + component: React.lazy(() => import("@/registry/default/example/skeleton-demo")), + source: "", + files: ["registry/default/example/skeleton-demo.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "skeleton-card": { + name: "skeleton-card", + type: "components:example", + registryDependencies: ["skeleton"], + component: React.lazy(() => import("@/registry/default/example/skeleton-card")), + source: "", + files: ["registry/default/example/skeleton-card.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "slider-demo": { + name: "slider-demo", + type: "components:example", + registryDependencies: ["slider"], + component: React.lazy(() => import("@/registry/default/example/slider-demo")), + source: "", + files: ["registry/default/example/slider-demo.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "switch-demo": { + name: "switch-demo", + type: "components:example", + registryDependencies: ["switch"], + component: React.lazy(() => import("@/registry/default/example/switch-demo")), + source: "", + files: ["registry/default/example/switch-demo.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "switch-form": { + name: "switch-form", + type: "components:example", + registryDependencies: ["switch","form"], + component: React.lazy(() => import("@/registry/default/example/switch-form")), + source: "", + files: ["registry/default/example/switch-form.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "table-demo": { + name: "table-demo", + type: "components:example", + registryDependencies: ["table"], + component: React.lazy(() => import("@/registry/default/example/table-demo")), + source: "", + files: ["registry/default/example/table-demo.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "tabs-demo": { + name: "tabs-demo", + type: "components:example", + registryDependencies: ["tabs"], + component: React.lazy(() => import("@/registry/default/example/tabs-demo")), + source: "", + files: ["registry/default/example/tabs-demo.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "textarea-demo": { + name: "textarea-demo", + type: "components:example", + registryDependencies: ["textarea"], + component: React.lazy(() => import("@/registry/default/example/textarea-demo")), + source: "", + files: ["registry/default/example/textarea-demo.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "textarea-disabled": { + name: "textarea-disabled", + type: "components:example", + registryDependencies: ["textarea"], + component: React.lazy(() => import("@/registry/default/example/textarea-disabled")), + source: "", + files: ["registry/default/example/textarea-disabled.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "textarea-form": { + name: "textarea-form", + type: "components:example", + registryDependencies: ["textarea","form"], + component: React.lazy(() => import("@/registry/default/example/textarea-form")), + source: "", + files: ["registry/default/example/textarea-form.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "textarea-with-button": { + name: "textarea-with-button", + type: "components:example", + registryDependencies: ["textarea","button"], + component: React.lazy(() => import("@/registry/default/example/textarea-with-button")), + source: "", + files: ["registry/default/example/textarea-with-button.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "textarea-with-label": { + name: "textarea-with-label", + type: "components:example", + registryDependencies: ["textarea","label"], + component: React.lazy(() => import("@/registry/default/example/textarea-with-label")), + source: "", + files: ["registry/default/example/textarea-with-label.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "textarea-with-text": { + name: "textarea-with-text", + type: "components:example", + registryDependencies: ["textarea","label"], + component: React.lazy(() => import("@/registry/default/example/textarea-with-text")), + source: "", + files: ["registry/default/example/textarea-with-text.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "toast-demo": { + name: "toast-demo", + type: "components:example", + registryDependencies: ["toast"], + component: React.lazy(() => import("@/registry/default/example/toast-demo")), + source: "", + files: ["registry/default/example/toast-demo.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "toast-destructive": { + name: "toast-destructive", + type: "components:example", + registryDependencies: ["toast"], + component: React.lazy(() => import("@/registry/default/example/toast-destructive")), + source: "", + files: ["registry/default/example/toast-destructive.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "toast-simple": { + name: "toast-simple", + type: "components:example", + registryDependencies: ["toast"], + component: React.lazy(() => import("@/registry/default/example/toast-simple")), + source: "", + files: ["registry/default/example/toast-simple.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "toast-with-action": { + name: "toast-with-action", + type: "components:example", + registryDependencies: ["toast"], + component: React.lazy(() => import("@/registry/default/example/toast-with-action")), + source: "", + files: ["registry/default/example/toast-with-action.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "toast-with-title": { + name: "toast-with-title", + type: "components:example", + registryDependencies: ["toast"], + component: React.lazy(() => import("@/registry/default/example/toast-with-title")), + source: "", + files: ["registry/default/example/toast-with-title.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "toggle-group-demo": { + name: "toggle-group-demo", + type: "components:example", + registryDependencies: ["toggle-group"], + component: React.lazy(() => import("@/registry/default/example/toggle-group-demo")), + source: "", + files: ["registry/default/example/toggle-group-demo.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "toggle-group-disabled": { + name: "toggle-group-disabled", + type: "components:example", + registryDependencies: ["toggle-group"], + component: React.lazy(() => import("@/registry/default/example/toggle-group-disabled")), + source: "", + files: ["registry/default/example/toggle-group-disabled.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "toggle-group-lg": { + name: "toggle-group-lg", + type: "components:example", + registryDependencies: ["toggle-group"], + component: React.lazy(() => import("@/registry/default/example/toggle-group-lg")), + source: "", + files: ["registry/default/example/toggle-group-lg.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "toggle-group-outline": { + name: "toggle-group-outline", + type: "components:example", + registryDependencies: ["toggle-group"], + component: React.lazy(() => import("@/registry/default/example/toggle-group-outline")), + source: "", + files: ["registry/default/example/toggle-group-outline.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "toggle-group-sm": { + name: "toggle-group-sm", + type: "components:example", + registryDependencies: ["toggle-group"], + component: React.lazy(() => import("@/registry/default/example/toggle-group-sm")), + source: "", + files: ["registry/default/example/toggle-group-sm.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "toggle-group-single": { + name: "toggle-group-single", + type: "components:example", + registryDependencies: ["toggle-group"], + component: React.lazy(() => import("@/registry/default/example/toggle-group-single")), + source: "", + files: ["registry/default/example/toggle-group-single.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "toggle-demo": { + name: "toggle-demo", + type: "components:example", + registryDependencies: ["toggle"], + component: React.lazy(() => import("@/registry/default/example/toggle-demo")), + source: "", + files: ["registry/default/example/toggle-demo.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "toggle-disabled": { + name: "toggle-disabled", + type: "components:example", + registryDependencies: ["toggle"], + component: React.lazy(() => import("@/registry/default/example/toggle-disabled")), + source: "", + files: ["registry/default/example/toggle-disabled.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "toggle-lg": { + name: "toggle-lg", + type: "components:example", + registryDependencies: ["toggle"], + component: React.lazy(() => import("@/registry/default/example/toggle-lg")), + source: "", + files: ["registry/default/example/toggle-lg.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "toggle-outline": { + name: "toggle-outline", + type: "components:example", + registryDependencies: ["toggle"], + component: React.lazy(() => import("@/registry/default/example/toggle-outline")), + source: "", + files: ["registry/default/example/toggle-outline.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "toggle-sm": { + name: "toggle-sm", + type: "components:example", + registryDependencies: ["toggle"], + component: React.lazy(() => import("@/registry/default/example/toggle-sm")), + source: "", + files: ["registry/default/example/toggle-sm.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "toggle-with-text": { + name: "toggle-with-text", + type: "components:example", + registryDependencies: ["toggle"], + component: React.lazy(() => import("@/registry/default/example/toggle-with-text")), + source: "", + files: ["registry/default/example/toggle-with-text.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "tooltip-demo": { + name: "tooltip-demo", + type: "components:example", + registryDependencies: ["tooltip"], + component: React.lazy(() => import("@/registry/default/example/tooltip-demo")), + source: "", + files: ["registry/default/example/tooltip-demo.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "typography-blockquote": { + name: "typography-blockquote", + type: "components:example", + registryDependencies: undefined, + component: React.lazy(() => import("@/registry/default/example/typography-blockquote")), + source: "", + files: ["registry/default/example/typography-blockquote.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "typography-demo": { + name: "typography-demo", + type: "components:example", + registryDependencies: undefined, + component: React.lazy(() => import("@/registry/default/example/typography-demo")), + source: "", + files: ["registry/default/example/typography-demo.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "typography-h1": { + name: "typography-h1", + type: "components:example", + registryDependencies: undefined, + component: React.lazy(() => import("@/registry/default/example/typography-h1")), + source: "", + files: ["registry/default/example/typography-h1.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "typography-h2": { + name: "typography-h2", + type: "components:example", + registryDependencies: undefined, + component: React.lazy(() => import("@/registry/default/example/typography-h2")), + source: "", + files: ["registry/default/example/typography-h2.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "typography-h3": { + name: "typography-h3", + type: "components:example", + registryDependencies: undefined, + component: React.lazy(() => import("@/registry/default/example/typography-h3")), + source: "", + files: ["registry/default/example/typography-h3.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "typography-h4": { + name: "typography-h4", + type: "components:example", + registryDependencies: undefined, + component: React.lazy(() => import("@/registry/default/example/typography-h4")), + source: "", + files: ["registry/default/example/typography-h4.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "typography-inline-code": { + name: "typography-inline-code", + type: "components:example", + registryDependencies: undefined, + component: React.lazy(() => import("@/registry/default/example/typography-inline-code")), + source: "", + files: ["registry/default/example/typography-inline-code.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "typography-large": { + name: "typography-large", + type: "components:example", + registryDependencies: undefined, + component: React.lazy(() => import("@/registry/default/example/typography-large")), + source: "", + files: ["registry/default/example/typography-large.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "typography-lead": { + name: "typography-lead", + type: "components:example", + registryDependencies: undefined, + component: React.lazy(() => import("@/registry/default/example/typography-lead")), + source: "", + files: ["registry/default/example/typography-lead.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "typography-list": { + name: "typography-list", + type: "components:example", + registryDependencies: undefined, + component: React.lazy(() => import("@/registry/default/example/typography-list")), + source: "", + files: ["registry/default/example/typography-list.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "typography-muted": { + name: "typography-muted", + type: "components:example", + registryDependencies: undefined, + component: React.lazy(() => import("@/registry/default/example/typography-muted")), + source: "", + files: ["registry/default/example/typography-muted.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "typography-p": { + name: "typography-p", + type: "components:example", + registryDependencies: undefined, + component: React.lazy(() => import("@/registry/default/example/typography-p")), + source: "", + files: ["registry/default/example/typography-p.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "typography-small": { + name: "typography-small", + type: "components:example", + registryDependencies: undefined, + component: React.lazy(() => import("@/registry/default/example/typography-small")), + source: "", + files: ["registry/default/example/typography-small.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "typography-table": { + name: "typography-table", + type: "components:example", + registryDependencies: undefined, + component: React.lazy(() => import("@/registry/default/example/typography-table")), + source: "", + files: ["registry/default/example/typography-table.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "mode-toggle": { + name: "mode-toggle", + type: "components:example", + registryDependencies: undefined, + component: React.lazy(() => import("@/registry/default/example/mode-toggle")), + source: "", + files: ["registry/default/example/mode-toggle.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "text-confirm-dialog-demo": { + name: "text-confirm-dialog-demo", + type: "components:example", + registryDependencies: undefined, + component: React.lazy(() => import("@/registry/default/example/text-confirm-dialog-demo")), + source: "", + files: ["registry/default/example/text-confirm-dialog-demo.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "text-confirm-dialog-with-info-alert": { + name: "text-confirm-dialog-with-info-alert", + type: "components:example", + registryDependencies: undefined, + component: React.lazy(() => import("@/registry/default/example/text-confirm-dialog-with-info-alert")), + source: "", + files: ["registry/default/example/text-confirm-dialog-with-info-alert.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "text-confirm-dialog-with-warning-alert": { + name: "text-confirm-dialog-with-warning-alert", + type: "components:example", + registryDependencies: undefined, + component: React.lazy(() => import("@/registry/default/example/text-confirm-dialog-with-warning-alert")), + source: "", + files: ["registry/default/example/text-confirm-dialog-with-warning-alert.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "text-confirm-dialog-with-destructive-alert": { + name: "text-confirm-dialog-with-destructive-alert", + type: "components:example", + registryDependencies: undefined, + component: React.lazy(() => import("@/registry/default/example/text-confirm-dialog-with-destructive-alert")), + source: "", + files: ["registry/default/example/text-confirm-dialog-with-destructive-alert.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "text-confirm-dialog-with-size": { + name: "text-confirm-dialog-with-size", + type: "components:example", + registryDependencies: undefined, + component: React.lazy(() => import("@/registry/default/example/text-confirm-dialog-with-size")), + source: "", + files: ["registry/default/example/text-confirm-dialog-with-size.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "text-confirm-dialog-with-children": { + name: "text-confirm-dialog-with-children", + type: "components:example", + registryDependencies: undefined, + component: React.lazy(() => import("@/registry/default/example/text-confirm-dialog-with-children")), + source: "", + files: ["registry/default/example/text-confirm-dialog-with-children.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "text-confirm-dialog-with-cancel-button": { + name: "text-confirm-dialog-with-cancel-button", + type: "components:example", + registryDependencies: undefined, + component: React.lazy(() => import("@/registry/default/example/text-confirm-dialog-with-cancel-button")), + source: "", + files: ["registry/default/example/text-confirm-dialog-with-cancel-button.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "form-item-layout-demo": { + name: "form-item-layout-demo", + type: "components:example", + registryDependencies: undefined, + component: React.lazy(() => import("@/registry/default/example/form-item-layout-demo")), + source: "", + files: ["registry/default/example/form-item-layout-demo.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "form-item-layout-with-select": { + name: "form-item-layout-with-select", + type: "components:example", + registryDependencies: undefined, + component: React.lazy(() => import("@/registry/default/example/form-item-layout-with-select")), + source: "", + files: ["registry/default/example/form-item-layout-with-select.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "form-item-layout-with-horizontal": { + name: "form-item-layout-with-horizontal", + type: "components:example", + registryDependencies: undefined, + component: React.lazy(() => import("@/registry/default/example/form-item-layout-with-horizontal")), + source: "", + files: ["registry/default/example/form-item-layout-with-horizontal.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "form-item-layout-with-switch": { + name: "form-item-layout-with-switch", + type: "components:example", + registryDependencies: undefined, + component: React.lazy(() => import("@/registry/default/example/form-item-layout-with-switch")), + source: "", + files: ["registry/default/example/form-item-layout-with-switch.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "form-item-layout-with-checkbox": { + name: "form-item-layout-with-checkbox", + type: "components:example", + registryDependencies: undefined, + component: React.lazy(() => import("@/registry/default/example/form-item-layout-with-checkbox")), + source: "", + files: ["registry/default/example/form-item-layout-with-switch.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "form-item-layout-with-checkbox-list": { + name: "form-item-layout-with-checkbox-list", + type: "components:example", + registryDependencies: undefined, + component: React.lazy(() => import("@/registry/default/example/form-item-layout-with-checkbox-list")), + source: "", + files: ["registry/default/example/form-item-layout-with-switch.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "color-usage-surface-www-and-docs": { + name: "color-usage-surface-www-and-docs", + type: "docs:example", + registryDependencies: undefined, + component: React.lazy(() => import("@/registry/default/example/color-usage-surface-www-and-docs")), + source: "", + files: ["registry/default/example/color-usage-surface-www-and-docs.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "color-usage-surface-studio": { + name: "color-usage-surface-studio", + type: "docs:example", + registryDependencies: undefined, + component: React.lazy(() => import("@/registry/default/example/color-usage-surface-studio")), + source: "", + files: ["registry/default/example/color-usage-surface-studio.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + "color-usage-surface-studio-frame": { + name: "color-usage-surface-studio-frame", + type: "docs:example", + registryDependencies: undefined, + component: React.lazy(() => import("@/registry/default/example/color-usage-surface-studio-frame")), + source: "", + files: ["registry/default/example/color-usage-surface-studio-frame.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, + }, +} diff --git a/apps/design-system/app/(app)/docs/[[...slug]]/page.tsx b/apps/design-system/app/(app)/docs/[[...slug]]/page.tsx new file mode 100644 index 0000000000..efa2f74912 --- /dev/null +++ b/apps/design-system/app/(app)/docs/[[...slug]]/page.tsx @@ -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 { + 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 { + 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 ( +
+
+
+
Docs
+ +
{doc.title}
+
+
+

{doc.title}

+ {doc.description && ( +

+ {doc.description} +

+ )} +
+ + +
+ +
+ +
+ {doc.toc && ( +
+
+ +
+ +
+
+
+
+ )} +
+ ) +} diff --git a/apps/design-system/app/(app)/layout.tsx b/apps/design-system/app/(app)/layout.tsx new file mode 100644 index 0000000000..49b81a77a8 --- /dev/null +++ b/apps/design-system/app/(app)/layout.tsx @@ -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 ( + <> + + {/* main container */} +
+ {/* main content */} +
+ {/* {children} */} +
+
+ + {children} +
+
+
+
+ + + ) +} diff --git a/apps/design-system/app/(app)/page.tsx b/apps/design-system/app/(app)/page.tsx new file mode 100644 index 0000000000..7d22b66af8 --- /dev/null +++ b/apps/design-system/app/(app)/page.tsx @@ -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 ( +
+
+
+ {/*
+ +
*/} +
+

Supabase Design System

+

+ Design resources for building consistent user experiences +

+
+
+ + {/* Homepage items */} + +
+
+ +
+

Atom components

+

Building blocks of User interfaces

+
+
+
+ +
+

Fragment components

+

Components assembled from Atoms

+
+
+ +
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+

Colors

+

Building blocks of User interfaces

+
+
+
+ +
+

Theming

+

Components assembled from Atoms

+
+
+ +
+ +
+

Background / Surfaces

+

Building blocks of User interfaces

+
+
+
+ +
+

Icons

+

Components assembled from Atoms

+
+
+
+
+
+ ) +} diff --git a/apps/design-system/app/Providers.tsx b/apps/design-system/app/Providers.tsx new file mode 100644 index 0000000000..be3bdf7861 --- /dev/null +++ b/apps/design-system/app/Providers.tsx @@ -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 ( + + + {children} + + + ) +} diff --git a/apps/design-system/app/favicon.ico b/apps/design-system/app/favicon.ico new file mode 100644 index 0000000000..718d6fea48 Binary files /dev/null and b/apps/design-system/app/favicon.ico differ diff --git a/apps/design-system/app/globals.css b/apps/design-system/app/globals.css new file mode 100644 index 0000000000..f7f07cd643 --- /dev/null +++ b/apps/design-system/app/globals.css @@ -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; + } +} */ diff --git a/apps/design-system/app/layout.tsx b/apps/design-system/app/layout.tsx new file mode 100644 index 0000000000..7e8ff99a5a --- /dev/null +++ b/apps/design-system/app/layout.tsx @@ -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 ( + + + + +
+
{children}
+
+
+ + + ) +} diff --git a/apps/design-system/components/callout.tsx b/apps/design-system/components/callout.tsx new file mode 100644 index 0000000000..63ed3afc9b --- /dev/null +++ b/apps/design-system/components/callout.tsx @@ -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 ( + + {icon && {icon}} + {title && {title}} + {children} + + ) +} diff --git a/apps/design-system/components/class-label.tsx b/apps/design-system/components/class-label.tsx new file mode 100644 index 0000000000..6575c2f437 --- /dev/null +++ b/apps/design-system/components/class-label.tsx @@ -0,0 +1,18 @@ +import * as React from 'react' + +const ClassLabel = React.forwardRef( + ({ children }, ref) => { + return ( + + {children} + + ) + } +) + +ClassLabel.displayName = 'ClassLabel' + +export { ClassLabel } diff --git a/apps/design-system/components/click-counter.tsx b/apps/design-system/components/click-counter.tsx new file mode 100644 index 0000000000..df68cb14cd --- /dev/null +++ b/apps/design-system/components/click-counter.tsx @@ -0,0 +1,16 @@ +'use client' + +import { useState } from 'react' + +export function ClickCounter() { + const [count, setCount] = useState(0) + + return ( + + ) +} diff --git a/apps/design-system/components/code-block-wrapper.tsx b/apps/design-system/components/code-block-wrapper.tsx new file mode 100644 index 0000000000..c347400671 --- /dev/null +++ b/apps/design-system/components/code-block-wrapper.tsx @@ -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 { + expandButtonTitle?: string +} + +export function CodeBlockWrapper({ + expandButtonTitle = 'View Code', + className, + children, + ...props +}: CodeBlockProps) { + const [isOpened, setIsOpened] = React.useState(false) + + return ( + +
+ +
+ {children} +
+
+
+ + + +
+
+
+ ) +} diff --git a/apps/design-system/components/code-fragment.tsx b/apps/design-system/components/code-fragment.tsx new file mode 100644 index 0000000000..afe821331d --- /dev/null +++ b/apps/design-system/components/code-fragment.tsx @@ -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 { + 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 ( +

+ Code fragment{' '} + + {name} + {' '} + not found in registry. +

+ ) + } + + return + }, [name, config.style]) + + const ComponentPreview = React.useMemo(() => { + return ( + <> +
+ Loading...
+ } + > + {Preview} + + + + ) + }, [Preview, align]) + + const wideClasses = wide ? '2xl:-ml-12 2xl:-mr-12' : '' + + return ( +
+
+ {showGrid && ( +
+ )} + {showDottedGrid && ( +
+ )} +
{ComponentPreview}
+
+
+ ) +} diff --git a/apps/design-system/components/colors.tsx b/apps/design-system/components/colors.tsx new file mode 100644 index 0000000000..e1d63267d0 --- /dev/null +++ b/apps/design-system/components/colors.tsx @@ -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 ( +
+ ) + break + + case 'border': + return
+ break + + case 'text': + return ( + + Postgres + + ) + break + + case 'colors': + return ( +
+ ) + break + + case 'palletes': + return ( +
+ ) + break + + default: + break + } + } + + return ( + <> + + {color[definition].map((x: string, i) => { + return ( + + + + {x} + + + ) + })} + + + ) +} + +{ + /*
+
+
+
Background
+
+ {color.background.map((x: string, i) => { + return ( +
+
+
+ {x} +
+
+ ) + })} +
+
+
+
Border
+
+ {color.border.map((x, i) => { + return ( +
+
+
+ {x} +
+
+ ) + })} +
+
+
+
texts
+
+ {color.text.map((x, i) => { + return ( +
+ + ### + +
+ {x} +
+
+ ) + })} +
+
+
+
+
+
Colors
+
+ {color.colors.map((x: string, i) => { + return ( +
+
+
+ {x} +
+
+ ) + })} +
+
+
+
Palletes
+
+ {color.palletes.map((x, i) => { + return ( +
+
+
+ {x} +
+
+ ) + })} +
+
+
+
*/ +} + +export { Colors } diff --git a/apps/design-system/components/command-menu.tsx b/apps/design-system/components/command-menu.tsx new file mode 100644 index 0000000000..bc6aa3c9fa --- /dev/null +++ b/apps/design-system/components/command-menu.tsx @@ -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 ( + <> + + + + + No results found. + {/* + {docsConfig.mainNav + .filter((navitem) => !navitem.external) + .map((navItem) => ( + { + runCommand(() => router.push(navItem.href as string)) + }} + > + + {navItem.title} + + ))} + */} + {docsConfig.sidebarNav.map((group) => ( + + {group.items.map((navItem) => ( + { + runCommand(() => router.push(navItem.href as string)) + }} + > +
+ +
+ {navItem.title} +
+ ))} +
+ ))} + + + runCommand(() => setTheme('light'))}> + + Light + + runCommand(() => setTheme('dark'))}> + + Dark + + runCommand(() => setTheme('deep-dark'))}> + + Deep Dark + + runCommand(() => setTheme('system'))}> + + System + + +
+
+ + ) +} diff --git a/apps/design-system/components/component-example.tsx b/apps/design-system/components/component-example.tsx new file mode 100644 index 0000000000..4a4ae25ff3 --- /dev/null +++ b/apps/design-system/components/component-example.tsx @@ -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 { + 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 ( +
+ +
+ + + Preview + + + Code + + + { + extractedClassNames ? ( + + ) : undefined + // codeString && + } +
+ +
+ {Example} +
+
+ +
+
+ {Code} +
+ {Children?.length ? ( +
+ {Children} +
+ ) : null} +
+
+
+
+ ) +} diff --git a/apps/design-system/components/component-preview.tsx b/apps/design-system/components/component-preview.tsx new file mode 100644 index 0000000000..744f02e076 --- /dev/null +++ b/apps/design-system/components/component-preview.tsx @@ -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 { + 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 ( +

+ Component{' '} + + {name} + {' '} + not found in registry. +

+ ) + } + + return + }, [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 ( + <> + {/* */} +
+ + {/* */} + {/* */} + Loading... +
+ } + > + {Preview} + + + {/*
*/} + + ) + }, [Preview, align]) + + const wideClasses = wide ? '2xl:-ml-12 2xl:-mr-12' : '' + + if (peekCode) { + return ( +
+
+ {showGrid && ( +
+ )} + {showDottedGrid && ( +
+ )} +
{ComponentPreview}
+ {/*
*/} +
+
+
+ {Code} +
+ +
+
+
+
+ ) + } + + return ( +
+
+ {showGrid && ( +
+ )} + {showDottedGrid && ( +
+ )} +
{ComponentPreview}
+ {/*
*/} +
+ + + + View code + + +
+ {Code} +
+
+
+
+ ) + + return ( +
+ +
+ + + Preview + + + Code + + +
+ + {ComponentPreview} + + +
+
+ {Code} +
+
+
+
+
+ ) +} diff --git a/apps/design-system/components/component-props.tsx b/apps/design-system/components/component-props.tsx new file mode 100644 index 0000000000..de41a1ac42 --- /dev/null +++ b/apps/design-system/components/component-props.tsx @@ -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 }) =>
{{name}}
; + // ` + + // 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 ( +
+

{props.children}

+ + + + + Prop Name + Required + Type + + Description + + + + + {Object.entries(docs.props).map(([propName, prop], index) => ( + + {propName} + {/* + // @ts-ignore */} + {prop.required ? 'Yes' : 'No'} + {/* + // @ts-ignore */} + {prop.flowType.name} + {/* + // @ts-ignore */} + {prop.description} + + ))} + +
+
+ ) +} diff --git a/apps/design-system/components/component-source.tsx b/apps/design-system/components/component-source.tsx new file mode 100644 index 0000000000..c63d4e4931 --- /dev/null +++ b/apps/design-system/components/component-source.tsx @@ -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 { + src: string +} + +export function ComponentSource({ children, className, ...props }: ComponentSourceProps) { + return ( + + {children} + + ) +} diff --git a/apps/design-system/components/copy-button.tsx b/apps/design-system/components/copy-button.tsx new file mode 100644 index 0000000000..ed823d518c --- /dev/null +++ b/apps/design-system/components/copy-button.tsx @@ -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 { + 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 ( + + ) +} + +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 ( + + + + + + copyToClipboard(value)}>Component + copyToClipboard(classNames)}>Classname + + + ) +} + +interface CopyNpmCommandButtonProps extends DropdownMenuTriggerProps { + // commands: Required +} + +// 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 ( +// +// +// +// +// +// copyCommand(commands.__npmCommand__, 'npm')}> +// npm +// +// copyCommand(commands.__yarnCommand__, 'yarn')}> +// yarn +// +// copyCommand(commands.__pnpmCommand__, 'pnpm')}> +// pnpm +// +// copyCommand(commands.__bunCommand__, 'bun')}> +// bun +// +// +// +// ) +// } diff --git a/apps/design-system/components/design-system-marks.tsx b/apps/design-system/components/design-system-marks.tsx new file mode 100644 index 0000000000..fab36f9399 --- /dev/null +++ b/apps/design-system/components/design-system-marks.tsx @@ -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 ( + + ) +} + +export { DesignSystemMarks } diff --git a/apps/design-system/components/example-label.tsx b/apps/design-system/components/example-label.tsx new file mode 100644 index 0000000000..6774d8e1bb --- /dev/null +++ b/apps/design-system/components/example-label.tsx @@ -0,0 +1,15 @@ +import * as React from 'react' + +const ExampleLabel = React.forwardRef( + ({ children }, ref) => { + return ( + + {children} + + ) + } +) + +ExampleLabel.displayName = 'ExampleLabel' + +export { ExampleLabel } diff --git a/apps/design-system/components/grid.tsx b/apps/design-system/components/grid.tsx new file mode 100644 index 0000000000..659117a773 --- /dev/null +++ b/apps/design-system/components/grid.tsx @@ -0,0 +1,62 @@ +import { forwardRef } from 'react' +import { cn } from 'ui' + +const Grid = forwardRef>( + ({ children, ...props }, ref) => { + return ( +
+ {children} +
+ ) + } +) + +Grid.displayName = 'Grid' + +const GridItem = forwardRef>( + ({ children, ...props }, ref) => { + return ( +
+
+ {children} +
+ ) + } +) + +GridItem.displayName = 'GridItem' + +export { Grid, GridItem } diff --git a/apps/design-system/components/homepage-svg-handler.tsx b/apps/design-system/components/homepage-svg-handler.tsx new file mode 100644 index 0000000000..e359eb0e83 --- /dev/null +++ b/apps/design-system/components/homepage-svg-handler.tsx @@ -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 ( +
+ +
+ ) +} + +export { HomepageSvgHandler } diff --git a/apps/design-system/components/icons.tsx b/apps/design-system/components/icons.tsx new file mode 100644 index 0000000000..8642a9d17c --- /dev/null +++ b/apps/design-system/components/icons.tsx @@ -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 ( + <> + + {Index.map((icon: any, i: number) => ( + + + + + + {icon.name} + + + + + + navigator.clipboard.writeText(icon.name)} + > + + Copy name + + navigator.clipboard.writeText(icon.jsx)} + > + + Copy JSX + + navigator.clipboard.writeText(icon.import)} + > + + Copy import path + + navigator.clipboard.writeText(icon.svg)} + > + + Copy SVG + + + + + ))} + + + ) +} + +export { Icons } diff --git a/apps/design-system/components/mdx-components.tsx b/apps/design-system/components/mdx-components.tsx new file mode 100644 index 0000000000..691e2edbda --- /dev/null +++ b/apps/design-system/components/mdx-components.tsx @@ -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) => ( +

+ ), + h2: ({ className, ...props }: React.HTMLAttributes) => ( +

+ ), + h3: ({ className, ...props }: React.HTMLAttributes) => ( +

+ ), + h4: ({ className, ...props }: React.HTMLAttributes) => ( +

+ ), + h5: ({ className, ...props }: React.HTMLAttributes) => ( +
+ ), + h6: ({ className, ...props }: React.HTMLAttributes) => ( +
+ ), + a: ({ className, ...props }: React.HTMLAttributes) => ( + + ), + p: ({ className, ...props }: React.HTMLAttributes) => ( +

+ ), + ul: ({ className, ...props }: React.HTMLAttributes) => ( +

    + ), + ol: ({ className, ...props }: React.HTMLAttributes) => ( +
      + ), + li: ({ className, ...props }: React.HTMLAttributes) => ( +
    1. + ), + blockquote: ({ className, ...props }: React.HTMLAttributes) => ( +
      + ), + img: ({ className, alt, ...props }: React.ImgHTMLAttributes) => ( + // eslint-disable-next-line @next/next/no-img-element + {alt} + ), + hr: ({ ...props }: React.HTMLAttributes) => ( +
      + ), + table: ({ className, ...props }: React.HTMLAttributes) => ( +
      + + + ), + tr: ({ className, ...props }: React.HTMLAttributes) => ( + + ), + th: ({ className, ...props }: React.HTMLAttributes) => ( +
      + ), + td: ({ className, ...props }: React.HTMLAttributes) => ( + + ), + pre: ( + { + className, + __rawString__, + // __npmCommand__, + // __yarnCommand__, + // __pnpmCommand__, + // __bunCommand__, + __withMeta__, + __src__, + // __event__, + __style__, + ...props + }: React.HTMLAttributes & { + __style__?: Style['name'] + __rawString__?: string + __withMeta__?: boolean + __src__?: string + // __event__?: Event['name'] + } + // & NpmCommands + ) => { + return ( + +
      +        {__rawString__ && (
      +          // !__npmCommand__ &&
      +          
      +        )}
      +        {/* {__npmCommand__ && __yarnCommand__ && __pnpmCommand__ && __bunCommand__ && (
      +          
      +        )} */}
      +      
      +    )
      +  },
      +  code: ({ className, ...props }: React.HTMLAttributes) => (
      +    
      +  ),
      +  Image,
      +  Callout,
      +  ComponentPreview,
      +  ComponentExample,
      +  ComponentSource,
      +  ComponentProps,
      +  AspectRatio,
      +  CodeBlockWrapper: ({ ...props }) => ,
      +  Step: ({ className, ...props }: React.ComponentProps<'h3'>) => (
      +    

      + ), + Steps: ({ ...props }) => ( +
      + ), + Tabs: ({ className, ...props }: React.ComponentProps) => ( + + ), + TabsList: ({ className, ...props }: React.ComponentProps) => ( + + ), + TabsTrigger: ({ className, ...props }: React.ComponentProps) => ( + + ), + TabsContent: ({ className, ...props }: React.ComponentProps) => ( + + ), + // FrameworkDocs: ({ className, ...props }: React.ComponentProps) => ( + // + // ), + Link: ({ className, ...props }: React.ComponentProps) => ( + + ), + LinkedCard: ({ className, ...props }: React.ComponentProps) => ( + + ), + Colors, + Icons, + ThemeSettings, + CodeFragment, +} + +interface MdxProps { + code: string +} + +export function Mdx({ code }: MdxProps) { + const [config] = useConfig() + const Component = useMDXComponent(code, { + style: config.style, + }) + + return ( +
      + +
      + ) +} diff --git a/apps/design-system/components/pager.tsx b/apps/design-system/components/pager.tsx new file mode 100644 index 0000000000..9dd476b33d --- /dev/null +++ b/apps/design-system/components/pager.tsx @@ -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 ( +
      + {pager?.prev?.href && ( + + +
      + + Previous + + {pager.prev.title} +
      + + )} + {pager?.next?.href && ( + +
      + + Next + + {pager.next.title} +
      + + + )} +
      + ) +} + +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((flat, link) => { + return flat.concat(link.items?.length ? flatten(link.items) : link) + }, []) + .filter((link) => !link?.disabled) +} diff --git a/apps/design-system/components/side-navigation-item.tsx b/apps/design-system/components/side-navigation-item.tsx new file mode 100644 index 0000000000..c22e53aa7e --- /dev/null +++ b/apps/design-system/components/side-navigation-item.tsx @@ -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 ( + +
      + {item.title} + + ) +}) + +NavigationItem.displayName = 'NavigationItem' + +export default NavigationItem diff --git a/apps/design-system/components/side-navigation.tsx b/apps/design-system/components/side-navigation.tsx new file mode 100644 index 0000000000..3924337c37 --- /dev/null +++ b/apps/design-system/components/side-navigation.tsx @@ -0,0 +1,21 @@ +import { docsConfig } from '@/config/docs' +import NavigationItem from '@/components/side-navigation-item' + +function SideNavigation() { + return ( + + ) +} + +export default SideNavigation diff --git a/apps/design-system/components/site-footer.tsx b/apps/design-system/components/site-footer.tsx new file mode 100644 index 0000000000..d05d343575 --- /dev/null +++ b/apps/design-system/components/site-footer.tsx @@ -0,0 +1,61 @@ +import { siteConfig } from '@/config/site' + +export function SiteFooter() { + return ( + + ) +} diff --git a/apps/design-system/components/source-panel.tsx b/apps/design-system/components/source-panel.tsx new file mode 100644 index 0000000000..04dc897204 --- /dev/null +++ b/apps/design-system/components/source-panel.tsx @@ -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 & { doc: Doc }>( + ({ doc, children, ...props }, ref) => { + const label = { + radix: 'Radix UI', + vaul: 'Vaul', + shadcn: 'ui.shadcn', + } + + const ShadcnPanel = () => { + if (doc.source?.shadcn) { + return ( +
      +
      + + + + + + shadcn/ui +
      + + This component is based on ui.shadcn + +
      + ) + } + } + + const VaulPanel = () => { + if (doc.source?.vaul) { + return ( +
      +
      + Vaul + vaul +
      + + This component is based on vaul by emilkowalski + +
      + ) + } + } + + const InputOtp = () => { + if (doc.source?.inputOtp) { + return ( +
      +
      + inputOtp + input-otp +
      + + This component is based on input-otp by guilhermerodz + +
      + ) + } + } + + const RadixPanel = () => { + if (doc.source?.radix) { + return ( +
      + + + + + + + +
      + This component uses Radix UI + {doc.links ? ( +
      + {doc.links?.doc && ( + + )} + {doc.links?.api && ( + + )} +
      + ) : null} +
      +
      + ) + } + } + + return ( +
      + + + + {/* */} +
      + ) + } +) + +SourcePanel.displayName = 'SourcePanel' + +export { SourcePanel } diff --git a/apps/design-system/components/style-wrapper.tsx b/apps/design-system/components/style-wrapper.tsx new file mode 100644 index 0000000000..4937cad099 --- /dev/null +++ b/apps/design-system/components/style-wrapper.tsx @@ -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 { + styleName?: Style['name'] +} + +export function StyleWrapper({ styleName, children }: StyleWrapperProps) { + const [config] = useConfig() + + if (!styleName || config.style === styleName) { + return <>{children} + } + + return null +} diff --git a/apps/design-system/components/theme-settings.tsx b/apps/design-system/components/theme-settings.tsx new file mode 100644 index 0000000000..0d06e68b37 --- /dev/null +++ b/apps/design-system/components/theme-settings.tsx @@ -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 ( +
      + + {singleThemes.map((theme: Theme) => ( + + + + ))} + +
      + ) + } + + return ( + <> + + + ) +} + +export { ThemeSettings } diff --git a/apps/design-system/components/theme-switcher-dropdown.tsx b/apps/design-system/components/theme-switcher-dropdown.tsx new file mode 100644 index 0000000000..4796194c07 --- /dev/null +++ b/apps/design-system/components/theme-switcher-dropdown.tsx @@ -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 ( +
      + +
      + ) + } + + const iconClasses = 'text-foreground-light group-data-[state=open]:text-foreground' + + return ( + <> + + + + + + Theme + + setTheme(themeValue)} + > + {singleThemes.map((theme: Theme) => ( + + {theme.name} + + ))} + + + + + ) +} + +export { ThemeSwitcherDropdown } diff --git a/apps/design-system/components/toc.tsx b/apps/design-system/components/toc.tsx new file mode 100644 index 0000000000..d796b84518 --- /dev/null +++ b/apps/design-system/components/toc.tsx @@ -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 ( +
      +

      On This Page

      + +
      + ) +} + +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 ? ( +
        + {tree.items.map((item, index) => { + return ( +
      • + + {item.title} + + {item.items?.length ? ( + + ) : null} +
      • + ) + })} +
      + ) : null +} diff --git a/apps/design-system/components/top-navigation-search.tsx b/apps/design-system/components/top-navigation-search.tsx new file mode 100644 index 0000000000..3de4610238 --- /dev/null +++ b/apps/design-system/components/top-navigation-search.tsx @@ -0,0 +1,17 @@ +'use client' + +import { Search } from 'lucide-react' +import { Input } from 'ui-patterns/DataInputs/Input' + +function TopNavigationSearch() { + return ( + } + placeholder="Search Design System..." + /> + ) +} + +export { TopNavigationSearch } diff --git a/apps/design-system/components/top-navigation.tsx b/apps/design-system/components/top-navigation.tsx new file mode 100644 index 0000000000..7301be52fc --- /dev/null +++ b/apps/design-system/components/top-navigation.tsx @@ -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 ( +
      +
      + +
      + ) +} + +export default TopNavigation diff --git a/apps/design-system/config/docs.ts b/apps/design-system/config/docs.ts new file mode 100644 index 0000000000..5d56ad87a5 --- /dev/null +++ b/apps/design-system/config/docs.ts @@ -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: [], + }, + ], + }, + ], +} diff --git a/apps/design-system/config/site.ts b/apps/design-system/config/site.ts new file mode 100644 index 0000000000..c86298e0e3 --- /dev/null +++ b/apps/design-system/config/site.ts @@ -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 diff --git a/apps/design-system/content/docs/changelog.mdx b/apps/design-system/content/docs/changelog.mdx new file mode 100644 index 0000000000..58a251547a --- /dev/null +++ b/apps/design-system/content/docs/changelog.mdx @@ -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 + + + Lift Mode + Lift Mode + View the blocks library + + +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. diff --git a/apps/design-system/content/docs/color-usage.mdx b/apps/design-system/content/docs/color-usage.mdx new file mode 100644 index 0000000000..aed6907d2b --- /dev/null +++ b/apps/design-system/content/docs/color-usage.mdx @@ -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 + + + +### 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 + */ +{children} + +/** + * ./studio background color + */ +{children} +``` + +### 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 + + + +#### `./apps/studio` + +For the studio (dashbaord) we can use `bg-surface-100`, `bg-surface-200`, `bg-surface-300` + + + +#### 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. + + + +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 + + + +## Text + +These can also be accessed with `foreground`. Like `text-foreground-light`. + + + +## Other Colors + +These can also be accessed with `foreground`. Like `text-foreground-light`. + + +``` diff --git a/apps/design-system/content/docs/components/accordion.mdx b/apps/design-system/content/docs/components/accordion.mdx new file mode 100644 index 0000000000..9190c8c824 --- /dev/null +++ b/apps/design-system/content/docs/components/accordion.mdx @@ -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 +--- + + + +## Installation + + + + + CLI + Manual + + + + + + +Run the following command: + +```bash +npx shadcn-ui@latest add accordion +``` + +Update `tailwind.config.js` + +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', + }, + }, + }, +} +``` + + + + + + + + + +Install the following dependencies: + +```bash +npm install @radix-ui/react-accordion +``` + +Copy and paste the following code into your project. + +{/* */} + +Update the import paths to match your project setup. + +Update `tailwind.config.js` + +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', + }, + }, + }, +} +``` + + + + + + + +## Usage + +```tsx +import { + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger, +} from '@/components/ui/accordion' +``` + +```tsx + + + Is it accessible? + Yes. It adheres to the WAI-ARIA design pattern. + + +``` diff --git a/apps/design-system/content/docs/components/alert copy.mdx b/apps/design-system/content/docs/components/alert copy.mdx new file mode 100644 index 0000000000..1ada949fd9 --- /dev/null +++ b/apps/design-system/content/docs/components/alert copy.mdx @@ -0,0 +1,65 @@ +--- +title: Alert +description: Displays a callout for user attention. +component: true +--- + + + +## Installation + + + + + CLI + Manual + + + +```bash +npx shadcn-ui@latest add alert +``` + + + + + + + +Copy and paste the following code into your project. + + + +Update the import paths to match your project setup. + + + + + + + +## Usage + +```tsx +import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert' +``` + +```tsx + + + Heads up! + + You can add components and dependencies to your app using the cli. + + +``` + +## Examples + +### Default + + + +### Destructive + + diff --git a/apps/design-system/content/docs/components/alert-dialog.mdx b/apps/design-system/content/docs/components/alert-dialog.mdx new file mode 100644 index 0000000000..10c28d1f13 --- /dev/null +++ b/apps/design-system/content/docs/components/alert-dialog.mdx @@ -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 +--- + + + +## Installation + + + + + CLI + Manual + + + +```bash +npx shadcn-ui@latest add alert-dialog +``` + + + + + + + +Install the following dependencies: + +```bash +npm install @radix-ui/react-alert-dialog +``` + +Copy and paste the following code into your project. + + + +Update the import paths to match your project setup. + + + + + + + +## Usage + +```tsx +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, + AlertDialogTrigger, +} from '@/components/ui/alert-dialog' +``` + +```tsx + + Open + + + Are you absolutely sure? + + This action cannot be undone. This will permanently delete your account and remove your data + from our servers. + + + + Cancel + Continue + + + +``` diff --git a/apps/design-system/content/docs/components/alert.mdx b/apps/design-system/content/docs/components/alert.mdx new file mode 100644 index 0000000000..0f0a031a2a --- /dev/null +++ b/apps/design-system/content/docs/components/alert.mdx @@ -0,0 +1,79 @@ +--- +title: Alert +description: Displays a callout for user attention. +component: true +source: + radix: true + shadcn: true +--- + + + +## Installation + + + + + CLI + Manual + + + +```bash +npx shadcn-ui@latest add alert +``` + + + + + + + +Copy and paste the following code into your project. + + + +Update the import paths to match your project setup. + + + + + + + +## Usage + +```tsx +import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert' +``` + +```tsx + + + Heads up! + + You can add components and dependencies to your app using the cli. + + +``` + +## Examples + +### Default + + + +### Destructive + + diff --git a/apps/design-system/content/docs/components/aspect-ratio.mdx b/apps/design-system/content/docs/components/aspect-ratio.mdx new file mode 100644 index 0000000000..4ddf98dfdd --- /dev/null +++ b/apps/design-system/content/docs/components/aspect-ratio.mdx @@ -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 +--- + + + +## Installation + + + + + CLI + Manual + + + +```bash +npx shadcn-ui@latest add aspect-ratio +``` + + + + + + + +Install the following dependencies: + +```bash +npm install @radix-ui/react-aspect-ratio +``` + +Copy and paste the following code into your project. + + + +Update the import paths to match your project setup. + + + + + + + +## Usage + +```tsx +import Image from 'next/image' + +import { AspectRatio } from '@/components/ui/aspect-ratio' +``` + +```tsx +
      + + Image + +
      +``` diff --git a/apps/design-system/content/docs/components/avatar.mdx b/apps/design-system/content/docs/components/avatar.mdx new file mode 100644 index 0000000000..f6fda6a3d8 --- /dev/null +++ b/apps/design-system/content/docs/components/avatar.mdx @@ -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 +--- + + + +## Installation + + + + + CLI + Manual + + + +```bash +npx shadcn-ui@latest add avatar +``` + + + + + + + +Install the following dependencies: + +```bash +npm install @radix-ui/react-avatar +``` + +Copy and paste the following code into your project. + + + +Update the import paths to match your project setup. + + + + + + + +## Usage + +```tsx +import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar' +``` + +```tsx + + + CN + +``` diff --git a/apps/design-system/content/docs/components/badge.mdx b/apps/design-system/content/docs/components/badge.mdx new file mode 100644 index 0000000000..d5b22c47a7 --- /dev/null +++ b/apps/design-system/content/docs/components/badge.mdx @@ -0,0 +1,87 @@ +--- +title: Badge +description: Displays a badge or a component that looks like a badge. +component: true +source: + shadcn: true +--- + + + +## Installation + + + + + CLI + Manual + + + +```bash +npx shadcn-ui@latest add badge +``` + + + + + + + +Copy and paste the following code into your project. + + + +Update the import paths to match your project setup. + + + + + + + +## Usage + +```tsx +import { Badge } from '@/components/ui/badge' +``` + +```tsx +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 +Badge +``` + +## Examples + +### Default + + + +--- + +### Secondary + + + +--- + +### Outline + + + +--- + +### Destructive + + diff --git a/apps/design-system/content/docs/components/breadcrumb.mdx b/apps/design-system/content/docs/components/breadcrumb.mdx new file mode 100644 index 0000000000..4665d2938b --- /dev/null +++ b/apps/design-system/content/docs/components/breadcrumb.mdx @@ -0,0 +1,190 @@ +--- +title: Breadcrumb +description: Displays the path to the current resource using a hierarchy of links. +component: true +--- + + + +## Installation + + + + + CLI + Manual + + + +```bash +npx shadcn-ui@latest add breadcrumb +``` + + + + + + + +Copy and paste the following code into your project. + + + +Update the import paths to match your project setup. + + + + + + + +## Usage + +```tsx +import { + Breadcrumb, + BreadcrumbItem, + BreadcrumbLink, + BreadcrumbList, + BreadcrumbPage, + BreadcrumbSeparator, +} from '@/components/ui/breadcrumb' +``` + +```tsx + + + + Home + + + + Components + + + + Breadcrumb + + + +``` + +## Examples + +### Custom separator + +Use a custom component as `children` for `` to create a custom separator. + + + +```tsx showLineNumbers {1,10-12} +import { Slash } from "lucide-react" + +... + + + + + Home + + + + + + Components + + + +``` + +--- + +### Dropdown + +You can compose `` with a `` to create a dropdown in the breadcrumb. + + + +```tsx showLineNumbers {1-6,11-21} +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu" + +... + + + + + Components + + + + Documentation + Themes + GitHub + + + +``` + +--- + +### Collapsed + +We provide a `` component to show a collapsed state when the breadcrumb is too long. + + + +```tsx showLineNumbers {1,9} +import { BreadcrumbEllipsis } from "@/components/ui/breadcrumb" + +... + + + + {/* ... */} + + + + {/* ... */} + + +``` + +--- + +### Link component + +To use a custom link component from your routing library, you can use the `asChild` prop on ``. + + + +```tsx showLineNumbers {1,8-10} +import { Link } from "next/link" + +... + + + + + + Home + + + {/* ... */} + + +``` + +--- + +### Responsive + +Here's an example of a responsive breadcrumb that composes `` with ``, ``, and ``. + +It displays a dropdown on desktop and a drawer on mobile. + + diff --git a/apps/design-system/content/docs/components/button.mdx b/apps/design-system/content/docs/components/button.mdx new file mode 100644 index 0000000000..f2bb59be55 --- /dev/null +++ b/apps/design-system/content/docs/components/button.mdx @@ -0,0 +1,118 @@ +--- +title: Button +description: Displays a button or a component that looks like a button. +featured: true +component: true +--- + + + +## Installation + + + + + CLI + Manual + + + +```bash +npx shadcn-ui@latest add button +``` + + + + + + + +Install the following dependencies: + +```bash +npm install @radix-ui/react-slot +``` + +Copy and paste the following code into your project. + + + +Update the import paths to match your project setup. + + + + + + + +## Usage + +```tsx +import { Button } from '@/components/ui/button' +``` + +```tsx + +``` + +## Link + +You can use the `buttonVariants` helper to create a link that looks like a button. + +```tsx +import { buttonVariants } from '@/components/ui/button' +``` + +```tsx +Click here +``` + +Alternatively, you can set the `asChild` parameter and nest the link component. + +```tsx + +``` + +## Examples + +### Primary + + + +### Secondary + + + +### Destructive + + + +### Outline + + + +### Ghost + + + +### Link + + + +### Icon + + + +### With Icon + + + +### Loading + + + +### As Child + + diff --git a/apps/design-system/content/docs/components/calendar.mdx b/apps/design-system/content/docs/components/calendar.mdx new file mode 100644 index 0000000000..9b1d4187cb --- /dev/null +++ b/apps/design-system/content/docs/components/calendar.mdx @@ -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 +--- + + + +## About + +The `Calendar` component is built on top of [React DayPicker](https://react-day-picker.js.org). + +## Installation + + + + + CLI + Manual + + + +```bash +npx shadcn-ui@latest add calendar +``` + + + + + + + +Install the following dependencies: + +```bash +npm install react-day-picker date-fns +``` + +Add the `Button` component to your project. + +The `Calendar` component uses the `Button` component. Make sure you have it installed in your project. + +Copy and paste the following code into your project. + + + +Update the import paths to match your project setup. + + + + + + + +## Usage + +```tsx +import { Calendar } from '@/components/ui/calendar' +``` + +```tsx +const [date, setDate] = React.useState(new Date()) + +return +``` + +See the [React DayPicker](https://react-day-picker.js.org) documentation for more information. + +## Date Picker + +You can use the `` component to build a date picker. See the [Date Picker](/docs/components/date-picker) page for more information. + +## Examples + +### Form + + diff --git a/apps/design-system/content/docs/components/card.mdx b/apps/design-system/content/docs/components/card.mdx new file mode 100644 index 0000000000..32bfdc8ec0 --- /dev/null +++ b/apps/design-system/content/docs/components/card.mdx @@ -0,0 +1,71 @@ +--- +title: Card +description: Displays a card with header, content, and footer. +component: true +--- + + + +## Installation + + + + + CLI + Manual + + + +```bash +npx shadcn-ui@latest add card +``` + + + + + + + +Copy and paste the following code into your project. + + + +Update the import paths to match your project setup. + + + + + + + +## Usage + +```tsx +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from '@/components/ui/card' +``` + +```tsx + + + Card Title + Card Description + + +

      Card Content

      +
      + +

      Card Footer

      +
      +
      +``` + +## Examples + + diff --git a/apps/design-system/content/docs/components/carousel.mdx b/apps/design-system/content/docs/components/carousel.mdx new file mode 100644 index 0000000000..71f3806892 --- /dev/null +++ b/apps/design-system/content/docs/components/carousel.mdx @@ -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 +--- + + + +## About + +The carousel component is built using the [Embla Carousel](https://www.embla-carousel.com/) library. + +## Installation + + + + + CLI + Manual + + + + +```bash +npx shadcn-ui@latest add carousel +``` + + + + + + + +Install the following dependencies: + +```bash +npm install embla-carousel-react +``` + +Copy and paste the following code into your project. + + + +Update the import paths to match your project setup. + + + + + + + +## Usage + +```tsx +import { + Carousel, + CarouselContent, + CarouselItem, + CarouselNext, + CarouselPrevious, +} from '@/components/ui/carousel' +``` + +```tsx + + + ... + ... + ... + + + + +``` + +## Examples + +### Sizes + +To set the size of the items, you can use the `basis` utility class on the ``. + + + +```tsx title="Example" showLineNumbers {4-6} +// 33% of the carousel width. + + + ... + ... + ... + + +``` + +```tsx title="Responsive" showLineNumbers {4-6} +// 50% on small screens and 33% on larger screens. + + + ... + ... + ... + + +``` + +### Spacing + +To set the spacing between the items, we use a `pl-[VALUE]` utility on the `` and a negative `-ml-[VALUE]` on the ``. + + + **Why:** I tried to use the `gap` property or a `grid` layout on the ` + ` 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. + + + + + +```tsx title="Example" showLineNumbers /-ml-4/ /pl-4/ + + + ... + ... + ... + + +``` + +```tsx title="Responsive" showLineNumbers /-ml-2/ /pl-2/ /md:-ml-4/ /md:pl-4/ + + + ... + ... + ... + + +``` + +### Orientation + +Use the `orientation` prop to set the orientation of the carousel. + + + +```tsx showLineNumbers /vertical | horizontal/ + + + ... + ... + ... + + +``` + +## 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} + + + ... + ... + ... + + +``` + +## API + +Use a state and the `setApi` props to get an instance of the carousel API. + + + +```tsx showLineNumbers {1,4,22} +import { type CarouselApi } from '@/components/ui/carousel' + +export function Example() { + const [api, setApi] = React.useState() + 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 ( + + + ... + ... + ... + + + ) +} +``` + +## 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() + + React.useEffect(() => { + if (!api) { + return + } + + api.on('select', () => { + // Do something on select. + }) + }, [api]) + + return ( + + + ... + ... + ... + + + ) +} +``` + +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 ( + + // ... + + ) +} +``` + + + +See the [Embla Carousel docs](https://www.embla-carousel.com/api/plugins/) for more information on using plugins. diff --git a/apps/design-system/content/docs/components/checkbox.mdx b/apps/design-system/content/docs/components/checkbox.mdx new file mode 100644 index 0000000000..fc7b7180d4 --- /dev/null +++ b/apps/design-system/content/docs/components/checkbox.mdx @@ -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 +--- + + + +## Installation + + + + + CLI + Manual + + + +```bash +npx shadcn-ui@latest add checkbox +``` + + + + + + + +Install the following dependencies: + +```bash +npm install @radix-ui/react-checkbox +``` + +Copy and paste the following code into your project. + + + +Update the import paths to match your project setup. + + + + + + + +## Usage + +```tsx +import { Checkbox } from '@/components/ui/checkbox' +``` + +```tsx + +``` + +## Examples + +### With text + + + +### Disabled + + + +### Form + + + + diff --git a/apps/design-system/content/docs/components/collapsible.mdx b/apps/design-system/content/docs/components/collapsible.mdx new file mode 100644 index 0000000000..3f5be4bb44 --- /dev/null +++ b/apps/design-system/content/docs/components/collapsible.mdx @@ -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 +--- + + + +## Installation + + + + + CLI + Manual + + + +```bash +npx shadcn-ui@latest add collapsible +``` + + + + + + + +Install the following dependencies: + +```bash +npm install @radix-ui/react-collapsible +``` + +Copy and paste the following code into your project. + + + +Update the import paths to match your project setup. + + + + + + + +## Usage + +```tsx +import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible' +``` + +```tsx + + Can I use this in my project? + + Yes. Free to use for personal and commercial projects. No attribution required. + + +``` diff --git a/apps/design-system/content/docs/components/combobox.mdx b/apps/design-system/content/docs/components/combobox.mdx new file mode 100644 index 0000000000..209f3a5478 --- /dev/null +++ b/apps/design-system/content/docs/components/combobox.mdx @@ -0,0 +1,131 @@ +--- +title: Combobox +description: Autocomplete input and command palette with a list of suggestions. +component: true +source: + shadcn: true +--- + + + +## Installation + +The Combobox is built using a composition of the `` and the `` 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 ( + + + + + + + + No framework found. + + {frameworks.map((framework) => ( + { + setValue(currentValue === value ? '' : currentValue) + setOpen(false) + }} + > + + {framework.label} + + ))} + + + + + ) +} +``` + +## Examples + +### Combobox + + + +### Popover + + + +### Dropdown menu + + + +### Responsive + +You can create a responsive combobox by using the `` on desktop and the `` components on mobile. + + + +### Form + + diff --git a/apps/design-system/content/docs/components/command.mdx b/apps/design-system/content/docs/components/command.mdx new file mode 100644 index 0000000000..cae6977b6e --- /dev/null +++ b/apps/design-system/content/docs/components/command.mdx @@ -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 +--- + + + +## About + +The `` component uses the [`cmdk`](https://cmdk.paco.me) component by [pacocoursey](https://twitter.com/pacocoursey). + +## Installation + + + + + CLI + Manual + + + +```bash +npx shadcn-ui@latest add command +``` + + + + + + + +Install the following dependencies: + +```bash +npm install cmdk +``` + +Copy and paste the following code into your project. + + + +Update the import paths to match your project setup. + + + + + + + +## Usage + +```tsx +import { + Command, + CommandDialog, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList, + CommandSeparator, + CommandShortcut, +} from '@/components/ui/command' +``` + +```tsx + + + + No results found. + + Calendar + Search Emoji + Calculator + + + + Profile + Billing + Settings + + + +``` + +## Examples + +### Dialog + + + +To show the command menu in a dialog, use the `` 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 ( + + + + No results found. + + Calendar + Search Emoji + Calculator + + + + ) +} +``` + +### Combobox + +You can use the `` component as a combobox. See the [Combobox](/docs/components/combobox) page for more information. diff --git a/apps/design-system/content/docs/components/context-menu.mdx b/apps/design-system/content/docs/components/context-menu.mdx new file mode 100644 index 0000000000..4bd06b6baa --- /dev/null +++ b/apps/design-system/content/docs/components/context-menu.mdx @@ -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 +--- + + + +## Installation + + + + + CLI + Manual + + + +```bash +npx shadcn-ui@latest add context-menu +``` + + + + + + + +Install the following dependencies: + +```bash +npm install @radix-ui/react-context-menu +``` + +Copy and paste the following code into your project. + + + +Update the import paths to match your project setup. + + + + + + + +## Usage + +```tsx +import { + ContextMenu, + ContextMenuContent, + ContextMenuItem, + ContextMenuTrigger, +} from '@/components/ui/context-menu' +``` + +```tsx + + Right click + + Profile + Billing + Team + Subscription + + +``` diff --git a/apps/design-system/content/docs/components/data-table.mdx b/apps/design-system/content/docs/components/data-table.mdx new file mode 100644 index 0000000000..d2e6620145 --- /dev/null +++ b/apps/design-system/content/docs/components/data-table.mdx @@ -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 +--- + + + +## 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 `` component and build a complex data table from scratch. + + + +**Tip:** If you find yourself using the same table in multiple places in your app, you can always extract it into a reusable component. + + + +## Table of Contents + +This guide will show you how to use [TanStack Table](https://tanstack.com/table) and the `
      ` 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 `
      ` 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 `` 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. + + + +### 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[] = [ + { + accessorKey: 'status', + header: 'Status', + }, + { + accessorKey: 'email', + header: 'Email', + }, + { + accessorKey: 'amount', + header: 'Amount', + }, +] +``` + + + +**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. + + + +### `` component + +Next, we'll create a `` 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 { + columns: ColumnDef[] + data: TData[] +} + +export function DataTable({ columns, data }: DataTableProps) { + const table = useReactTable({ + data, + columns, + getCoreRowModel: getCoreRowModel(), + }) + + return ( +
      +
      + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + return ( + + {header.isPlaceholder + ? null + : flexRender(header.column.columnDef.header, header.getContext())} + + ) + })} + + ))} + + + {table.getRowModel().rows?.length ? ( + table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + + {flexRender(cell.column.columnDef.cell, cell.getContext())} + + ))} + + )) + ) : ( + + + No results. + + + )} + +
      +
      + ) +} +``` + + + +**Tip**: If you find yourself using `` in multiple places, this is the component you could make reusable by extracting it to `components/ui/data-table.tsx`. + +`` + + + +### 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 { + // 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 ( +
      + +
      + ) +} +``` + + + +## Cell Formatting + +Let's format the amount cell to display the dollar amount. We'll also align the cell to the right. + + + +### 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[] = [ + { + accessorKey: 'amount', + header: () =>
      Amount
      , + cell: ({ row }) => { + const amount = parseFloat(row.getValue('amount')) + const formatted = new Intl.NumberFormat('en-US', { + style: 'currency', + currency: 'USD', + }).format(amount) + + return
      {formatted}
      + }, + }, +] +``` + +You can use the same approach to format other cells and headers. + +
      + +## Row Actions + +Let's add row actions to our table. We'll use a `` component for this. + + + +### Update columns definition + +Update our columns definition to add a new `actions` column. The `actions` cell returns a `` 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[] = [ + // ... + { + id: 'actions', + cell: ({ row }) => { + const payment = row.original + + return ( + + + + + + Actions + navigator.clipboard.writeText(payment.id)}> + Copy payment ID + + + View customer + View payment details + + + ) + }, + }, + // ... +] +``` + +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. + + + +## Pagination + +Next, we'll add pagination to our table. + + + +### Update `` + +```tsx showLineNumbers title="app/payments/data-table.tsx" {5,17} +import { + ColumnDef, + flexRender, + getCoreRowModel, + getPaginationRowModel, + useReactTable, +} from '@tanstack/react-table' + +export function DataTable({ columns, data }: DataTableProps) { + 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 ` + + + + ) +} +``` + +See [Reusable Components](#reusable-components) section for a more advanced pagination component. + + + +## Sorting + +Let's make the email column sortable. + + + +### Update `` + +```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({ + columns, + data, +}: DataTableProps) { + const [sorting, setSorting] = React.useState([]) + + const table = useReactTable({ + data, + columns, + getCoreRowModel: getCoreRowModel(), + getPaginationRowModel: getPaginationRowModel(), + onSortingChange: setSorting, + getSortedRowModel: getSortedRowModel(), + state: { + sorting, + }, + }) + + return ( +
      +
      + { ... }
      +
      +
      + ) +} +``` + +### 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[] = [ + { + accessorKey: 'email', + header: ({ column }) => { + return ( + + ) + }, + }, +] +``` + +This will automatically sort the table (asc and desc) when the user toggles on the header cell. + +
      + +## Filtering + +Let's add a search input to filter emails in our table. + + + +### Update `` + +```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({ + columns, + data, +}: DataTableProps) { + const [sorting, setSorting] = React.useState([]) + const [columnFilters, setColumnFilters] = React.useState( + [] + ) + + const table = useReactTable({ + data, + columns, + onSortingChange: setSorting, + getCoreRowModel: getCoreRowModel(), + getPaginationRowModel: getPaginationRowModel(), + getSortedRowModel: getSortedRowModel(), + onColumnFiltersChange: setColumnFilters, + getFilteredRowModel: getFilteredRowModel(), + state: { + sorting, + columnFilters, + }, + }) + + return ( +
      +
      + + table.getColumn("email")?.setFilterValue(event.target.value) + } + className="max-w-sm" + /> +
      +
      + { ... }
      +
      +
      + ) +} +``` + +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. + +
      + +## Visibility + +Adding column visibility is fairly simple using `@tanstack/react-table` visibility API. + + + +### Update `` + +```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({ + columns, + data, +}: DataTableProps) { + const [sorting, setSorting] = React.useState([]) + const [columnFilters, setColumnFilters] = React.useState( + [] + ) + const [columnVisibility, setColumnVisibility] = + React.useState({}) + + const table = useReactTable({ + data, + columns, + onSortingChange: setSorting, + onColumnFiltersChange: setColumnFilters, + getCoreRowModel: getCoreRowModel(), + getPaginationRowModel: getPaginationRowModel(), + getSortedRowModel: getSortedRowModel(), + getFilteredRowModel: getFilteredRowModel(), + onColumnVisibilityChange: setColumnVisibility, + state: { + sorting, + columnFilters, + columnVisibility, + }, + }) + + return ( +
      +
      + + table.getColumn("email")?.setFilterValue(event.target.value) + } + className="max-w-sm" + /> + + + + + + {table + .getAllColumns() + .filter( + (column) => column.getCanHide() + ) + .map((column) => { + return ( + + column.toggleVisibility(!!value) + } + > + {column.id} + + ) + })} + + +
      +
      + { ... }
      +
      +
      + ) +} +``` + +This adds a dropdown menu that you can use to toggle column visibility. + +
      + +## Row Selection + +Next, we're going to add row selection to our table. + + + +### 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[] = [ + { + id: 'select', + header: ({ table }) => ( + table.toggleAllPageRowsSelected(!!value)} + aria-label="Select all" + /> + ), + cell: ({ row }) => ( + row.toggleSelected(!!value)} + aria-label="Select row" + /> + ), + enableSorting: false, + enableHiding: false, + }, +] +``` + +### Update `` + +```tsx showLineNumbers title="app/payments/data-table.tsx" {11,23,28} +export function DataTable({ columns, data }: DataTableProps) { + const [sorting, setSorting] = React.useState([]) + const [columnFilters, setColumnFilters] = React.useState([]) + const [columnVisibility, setColumnVisibility] = React.useState({}) + 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 ( +
      +
      + + + + ) +} +``` + +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 +
      + {table.getFilteredSelectedRowModel().rows.length} of {table.getFilteredRowModel().rows.length}{' '} + row(s) selected. +
      +``` + + + +## 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. + + + +```tsx {5} +export const columns = [ + { + accessorKey: 'email', + header: ({ column }) => , + }, +] +``` + +### Pagination + +Add pagination controls to your table including page size and selection count. + + + +```tsx + +``` + +### Column toggle + +A component to toggle column visibility. + + + +```tsx + +``` diff --git a/apps/design-system/content/docs/components/date-picker.mdx b/apps/design-system/content/docs/components/date-picker.mdx new file mode 100644 index 0000000000..0848ee3bdb --- /dev/null +++ b/apps/design-system/content/docs/components/date-picker.mdx @@ -0,0 +1,74 @@ +--- +title: Date Picker +description: A date picker component with range and presets. +component: true +source: + shadcn: true +--- + + + +## Installation + +The Date Picker is built using a composition of the `` and the `` 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() + + return ( + + + + + + + + + ) +} +``` + +See the [React DayPicker](https://react-day-picker.js.org) documentation for more information. + +## Examples + +### Date Picker + + + +### Date Range Picker + + + +### With Presets + + + +### Form + + diff --git a/apps/design-system/content/docs/components/dialog.mdx b/apps/design-system/content/docs/components/dialog.mdx new file mode 100644 index 0000000000..47d29e96eb --- /dev/null +++ b/apps/design-system/content/docs/components/dialog.mdx @@ -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 +--- + + + +## Installation + + + + + CLI + Manual + + + +```bash +npx shadcn-ui@latest add dialog +``` + + + + + + + +Install the following dependencies: + +```bash +npm install @radix-ui/react-dialog +``` + +Copy and paste the following code into your project. + + + +Update the import paths to match your project setup. + + + + + + + +## Usage + +```tsx +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, + DialogTrigger, +} from '@/components/ui/dialog' +``` + +```tsx + + Open + + + Are you absolutely sure? + + This action cannot be undone. This will permanently delete your account and remove your data + from our servers. + + + + +``` + +## Examples + +### Custom 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} + + + Right click + + Open + Download + + + Delete + + + + + + + Are you absolutely sure? + + This action cannot be undone. Are you sure you want to permanently delete this file from our + servers? + + + + + + + +``` diff --git a/apps/design-system/content/docs/components/drawer.mdx b/apps/design-system/content/docs/components/drawer.mdx new file mode 100644 index 0000000000..1767e43266 --- /dev/null +++ b/apps/design-system/content/docs/components/drawer.mdx @@ -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 +--- + + + +## About + +Drawer is built on top of [Vaul](https://github.com/emilkowalski/vaul) by [emilkowalski\_](https://twitter.com/emilkowalski_). + +## Installation + + + + + CLI + Manual + + + +```bash +npx shadcn-ui@latest add drawer +``` + + + + + + + +Install the following dependencies: + +```bash +npm install vaul +``` + +Copy and paste the following code into your project. + + + +Update the import paths to match your project setup. + + + + + + + +## Usage + +```tsx showLineNumbers +import { + Drawer, + DrawerClose, + DrawerContent, + DrawerDescription, + DrawerFooter, + DrawerHeader, + DrawerTitle, + DrawerTrigger, +} from '@/components/ui/drawer' +``` + +```tsx showLineNumbers + + Open + + + Are you absolutely sure? + This action cannot be undone. + + + + + + + + + +``` + +## 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. + + diff --git a/apps/design-system/content/docs/components/dropdown-menu.mdx b/apps/design-system/content/docs/components/dropdown-menu.mdx new file mode 100644 index 0000000000..daccc38aa8 --- /dev/null +++ b/apps/design-system/content/docs/components/dropdown-menu.mdx @@ -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 +--- + + + +## Installation + +So I guess I can write anything i want in here. + +## Props + +{/* */} + + + + + CLI + Manual + + + +```bash +npx shadcn-ui@latest add dropdown-menu +``` + + + + + + + +Install the following dependencies: + +```bash +npm install @radix-ui/react-dropdown-menu +``` + +Copy and paste the following code into your project. + +{/* */} + +Update the import paths to match your project setup. + + + + + + + +## Usage + +```tsx +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from '@/components/ui/dropdown-menu' +``` + +```tsx + + Open + + My Account + + Profile + Billing + Team + Subscription + + +``` + +## Examples + +### Checkboxes + + + +### Radio Group + + diff --git a/apps/design-system/content/docs/components/form.mdx b/apps/design-system/content/docs/components/form.mdx new file mode 100644 index 0000000000..f69cff2305 --- /dev/null +++ b/apps/design-system/content/docs/components/form.mdx @@ -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 `` component to compose accessible forms using Radix UI components. + +## Features + +The `
      ` component is a wrapper around the `react-hook-form` library. It provides a few things: + +- Composable components for building forms. +- A `` 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 + + ( + + + + { /* Your form field */} + + + + + )} + /> + +``` + +## Example + +```tsx +const form = useForm() + + ( + + Username + + + + This is your public display name. + + + )} +/> +``` + +## Installation + + + + + CLI + Manual + + + + + +### Command + +```bash +npx shadcn-ui@latest add form +``` + + + + + + + + + +Install the following dependencies: + +```bash +npm install @radix-ui/react-label @radix-ui/react-slot react-hook-form @hookform/resolvers zod +``` + +Copy and paste the following code into your project. + + + +Update the import paths to match your project setup. + + + + + + + +## Usage + + + +### 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>({ + resolver: zodResolver(formSchema), + defaultValues: { + username: '', + }, + }) + + // 2. Define a submit handler. + function onSubmit(values: z.infer) { + // 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 `
      ` 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 ( + + + ( + + Username + + + + This is your public display name. + + + )} + /> + + + + ) +} +``` + +### Done + +That's it. You now have a fully accessible form that is type-safe with client-side validation. + + + +
      + +## Examples + +See the following links for more examples on how to use the `
      ` 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) diff --git a/apps/design-system/content/docs/components/hover-card.mdx b/apps/design-system/content/docs/components/hover-card.mdx new file mode 100644 index 0000000000..7d666ede67 --- /dev/null +++ b/apps/design-system/content/docs/components/hover-card.mdx @@ -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 +--- + + + +## Installation + + + + + CLI + Manual + + + +```bash +npx shadcn-ui@latest add hover-card +``` + + + + + +Install the following dependencies: + +```bash +npm install @radix-ui/react-hover-card +``` + + + +Copy and paste the following code into your project. + + + +Update the import paths to match your project setup. + + + + + + + +## Usage + +```tsx +import { HoverCard, HoverCardContent, HoverCardTrigger } from '@/components/ui/hover-card' +``` + +```tsx + + Hover + The React Framework – created and maintained by @vercel. + +``` diff --git a/apps/design-system/content/docs/components/input-otp.mdx b/apps/design-system/content/docs/components/input-otp.mdx new file mode 100644 index 0000000000..6743207ab8 --- /dev/null +++ b/apps/design-system/content/docs/components/input-otp.mdx @@ -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 +--- + + + +## 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 + + + + + CLI + Manual + + + + + +Run the following command: + +```bash +npx shadcn-ui@latest add input-otp +``` + +Update `tailwind.config.js` + +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', + }, + }, + }, +} +``` + + + + + + + + + +Install the following dependencies: + +```bash +npm install input-otp +``` + +Copy and paste the following code into your project. + + + +Update the import paths to match your project setup. + +Update `tailwind.config.js` + +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', + }, + }, + }, +} +``` + + + + + + + +## Usage + +```tsx +import { InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot } from '@/components/ui/input-otp' +``` + +```tsx + + + + + + + + + + + + + +``` + +## Examples + +### Pattern + +Use the `pattern` prop to define a custom pattern for the OTP input. + + + +```tsx showLineNumbers {1,7} +import { REGEXP_ONLY_DIGITS_AND_CHARS } from "input-otp" + +... + + + + + {/* ... */} + + +``` + +### Separator + +You can use the `` component to add a separator between the input groups. + + + +```tsx showLineNumbers {4,15} +import { + InputOTP, + InputOTPGroup, + InputOTPSeparator, + InputOTPSlot, +} from "@/components/ui/input-otp" + +... + + + + + + + + + + + + +``` + +### Controlled + +You can use the `value` and `onChange` props to control the input value. + + + +### Form + + diff --git a/apps/design-system/content/docs/components/input.mdx b/apps/design-system/content/docs/components/input.mdx new file mode 100644 index 0000000000..fdbdd85a6a --- /dev/null +++ b/apps/design-system/content/docs/components/input.mdx @@ -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 +--- + + + +## Installation + + + + + CLI + Manual + + + +```bash +npx shadcn-ui@latest add input +``` + + + + + + + +Copy and paste the following code into your project. + + + +Update the import paths to match your project setup. + + + + + + + +## Usage + +```tsx +import { Input } from '@/components/ui/input' +``` + +```tsx + +``` + +## Examples + +### Default + + + +### File + + + +### Disabled + + + +### With Label + + + +### With Button + + + +### Form + + diff --git a/apps/design-system/content/docs/components/label.mdx b/apps/design-system/content/docs/components/label.mdx new file mode 100644 index 0000000000..c2d59c1a99 --- /dev/null +++ b/apps/design-system/content/docs/components/label.mdx @@ -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 +--- + + + +## Installation + + + + + CLI + Manual + + + +```bash +npx shadcn-ui@latest add label +``` + + + + + + + +Install the following dependencies: + +```bash +npm install @radix-ui/react-label +``` + +Copy and paste the following code into your project. + + + +Update the import paths to match your project setup. + + + + + + + +## Usage + +```tsx +import { Label } from '@/components/ui/label' +``` + +```tsx + +``` diff --git a/apps/design-system/content/docs/components/menubar.mdx b/apps/design-system/content/docs/components/menubar.mdx new file mode 100644 index 0000000000..ff88641610 --- /dev/null +++ b/apps/design-system/content/docs/components/menubar.mdx @@ -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 +--- + + + +## Installation + + + + + CLI + Manual + + + +```bash +npx shadcn-ui@latest add menubar +``` + + + + + + + +Install the following dependencies: + +```bash +npm install @radix-ui/react-menubar +``` + +Copy and paste the following code into your project. + + + +Update the import paths to match your project setup. + + + + + + + +## Usage + +```tsx +import { + Menubar, + MenubarContent, + MenubarItem, + MenubarMenu, + MenubarSeparator, + MenubarShortcut, + MenubarTrigger, +} from '@/components/ui/menubar' +``` + +```tsx + + + File + + + New Tab ⌘T + + New Window + + Share + + Print + + + +``` diff --git a/apps/design-system/content/docs/components/navigation-menu.mdx b/apps/design-system/content/docs/components/navigation-menu.mdx new file mode 100644 index 0000000000..042dc0945d --- /dev/null +++ b/apps/design-system/content/docs/components/navigation-menu.mdx @@ -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 +--- + + + +## Installation + + + + + CLI + Manual + + + +```bash +npx shadcn-ui@latest add navigation-menu +``` + + + + + + + +Install the following dependencies: + +```bash +npm install @radix-ui/react-navigation-menu +``` + +Copy and paste the following code into your project. + + + +Update the import paths to match your project setup. + + + + + + + +## Usage + +```tsx +import { + NavigationMenu, + NavigationMenuContent, + NavigationMenuIndicator, + NavigationMenuItem, + NavigationMenuLink, + NavigationMenuList, + NavigationMenuTrigger, + NavigationMenuViewport, +} from '@/components/ui/navigation-menu' +``` + +```tsx + + + + Item One + + Link + + + + +``` + +## Examples + +### Link Component + +When using the Next.js `` component, you can use `navigationMenuTriggerStyle()` to apply the correct styles to the trigger. + +```tsx +import { navigationMenuTriggerStyle } from '@/components/ui/navigation-menu' +``` + +```tsx {3-5} + + + Documentation + + +``` + +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. diff --git a/apps/design-system/content/docs/components/pagination.mdx b/apps/design-system/content/docs/components/pagination.mdx new file mode 100644 index 0000000000..e5fbf62620 --- /dev/null +++ b/apps/design-system/content/docs/components/pagination.mdx @@ -0,0 +1,104 @@ +--- +title: Pagination +description: Pagination with page navigation, next and previous links. +component: true +source: + shadcn: true +--- + + + +## Installation + + + + + CLI + Manual + + + +```bash +npx shadcn-ui@latest add pagination +``` + + + + + + + +Copy and paste the following code into your project. + + + +Update the import paths to match your project setup. + + + + + + + +## Usage + +```tsx +import { + Pagination, + PaginationContent, + PaginationEllipsis, + PaginationItem, + PaginationLink, + PaginationNext, + PaginationPrevious, +} from '@/components/ui/pagination' +``` + +```tsx + + + + + + + 1 + + + + + + + + + +``` + +### Next.js + +By default the `` component will render an `` tag. + +To use the Next.js `` 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 + +const PaginationLink = ({...props }: ) => ( + +- ++ + // ... +- ++ + +) + +``` + + + +**Note:** We are making updates to the cli to automatically do this for you. + + diff --git a/apps/design-system/content/docs/components/popover.mdx b/apps/design-system/content/docs/components/popover.mdx new file mode 100644 index 0000000000..f04fe012c8 --- /dev/null +++ b/apps/design-system/content/docs/components/popover.mdx @@ -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 +--- + + + +## Installation + + + + + CLI + Manual + + + +```bash +npx shadcn-ui@latest add popover +``` + + + + + + + +Install the following dependencies: + +```bash +npm install @radix-ui/react-popover +``` + +Copy and paste the following code into your project. + + + +Update the import paths to match your project setup. + + + + + + + +## Usage + +```tsx +import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover' +``` + +```tsx + + Open + Place content for the popover here. + +``` diff --git a/apps/design-system/content/docs/components/progress.mdx b/apps/design-system/content/docs/components/progress.mdx new file mode 100644 index 0000000000..a187c3421a --- /dev/null +++ b/apps/design-system/content/docs/components/progress.mdx @@ -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 +--- + + + +## Installation + + + + + CLI + Manual + + + +```bash +npx shadcn-ui@latest add progress +``` + + + + + + + +Install the following dependencies: + +```bash +npm install @radix-ui/react-progress +``` + +Copy and paste the following code into your project. + + + +Update the import paths to match your project setup. + + + + + + + +## Usage + +```tsx +import { Progress } from '@/components/ui/progress' +``` + +```tsx + +``` diff --git a/apps/design-system/content/docs/components/radio-group.mdx b/apps/design-system/content/docs/components/radio-group.mdx new file mode 100644 index 0000000000..8bd7e79ac5 --- /dev/null +++ b/apps/design-system/content/docs/components/radio-group.mdx @@ -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 +--- + + + +## Installation + + + + + CLI + Manual + + + +```bash +npx shadcn-ui@latest add radio-group +``` + + + + + + + +Install the following dependencies: + +```bash +npm install @radix-ui/react-radio-group +``` + +Copy and paste the following code into your project. + + + +Update the import paths to match your project setup. + + + + + + + +## Usage + +```tsx +import { Label } from '@/components/ui/label' +import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group' +``` + +```tsx + +
      + + +
      +
      + + +
      +
      +``` + +## Examples + +### Form + + diff --git a/apps/design-system/content/docs/components/resizable.mdx b/apps/design-system/content/docs/components/resizable.mdx new file mode 100644 index 0000000000..b06c1af8f2 --- /dev/null +++ b/apps/design-system/content/docs/components/resizable.mdx @@ -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 +--- + + + +## 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 + + + + + CLI + Manual + + + +```bash +npx shadcn-ui@latest add resizable +``` + + + + + + + +Install the following dependencies: + +```bash +npm install react-resizable-panels +``` + +Copy and paste the following code into your project. + + + +Update the import paths to match your project setup. + + + + + + + +## Usage + +```tsx +import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from '@/components/ui/resizable' +``` + +```tsx + + One + + Two + +``` + +## Examples + +### Vertical + +Use the `direction` prop to set the direction of the resizable panels. + + + +```tsx showLineNumbers {9} +import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from '@/components/ui/resizable' + +export default function Example() { + return ( + + One + + Two + + ) +} +``` + +### Handle + +You can set or hide the handle by using the `withHandle` prop on the `ResizableHandle` component. + + + +```tsx showLineNumbers {11} +import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from '@/components/ui/resizable' + +export default function Example() { + return ( + + One + + Two + + ) +} +``` diff --git a/apps/design-system/content/docs/components/scroll-area.mdx b/apps/design-system/content/docs/components/scroll-area.mdx new file mode 100644 index 0000000000..55d039eec5 --- /dev/null +++ b/apps/design-system/content/docs/components/scroll-area.mdx @@ -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 +--- + + + +## Installation + + + + + CLI + Manual + + + +```bash +npx shadcn-ui@latest add scroll-area +``` + + + + + + + +Install the following dependencies: + +```bash +npm install @radix-ui/react-scroll-area +``` + +Copy and paste the following code into your project. + + + +Update the import paths to match your project setup. + + + + + + + +## Usage + +```tsx +import { ScrollArea } from '@/components/ui/scroll-area' +``` + +```tsx + + 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. + +``` + +## Examples + +### Horizontal Scrolling + + diff --git a/apps/design-system/content/docs/components/select.mdx b/apps/design-system/content/docs/components/select.mdx new file mode 100644 index 0000000000..64a0fd5181 --- /dev/null +++ b/apps/design-system/content/docs/components/select.mdx @@ -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 +--- + + + +## Installation + + + + + CLI + Manual + + + +```bash +npx shadcn-ui@latest add select +``` + + + + + + + +Install the following dependencies: + +```bash +npm install @radix-ui/react-select +``` + +Copy and paste the following code into your project. + + + +Update the import paths to match your project setup. + + + + + + + +## Usage + +```tsx +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select' +``` + +```tsx + +``` + +## Examples + +### Scrollable + + + +### Form + + diff --git a/apps/design-system/content/docs/components/separator.mdx b/apps/design-system/content/docs/components/separator.mdx new file mode 100644 index 0000000000..92fa9bc2b4 --- /dev/null +++ b/apps/design-system/content/docs/components/separator.mdx @@ -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 +--- + + + +## Installation + + + + + CLI + Manual + + + +```bash +npx shadcn-ui@latest add separator +``` + + + + + + +Install the following dependencies: + +```bash +npm install @radix-ui/react-separator +``` + +Copy and paste the following code into your project. + + + +Update the import paths to match your project setup. + + + + + + + +## Usage + +```tsx +import { Separator } from '@/components/ui/separator' +``` + +```tsx + +``` diff --git a/apps/design-system/content/docs/components/sheet.mdx b/apps/design-system/content/docs/components/sheet.mdx new file mode 100644 index 0000000000..96220e63eb --- /dev/null +++ b/apps/design-system/content/docs/components/sheet.mdx @@ -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 +--- + + + +## Installation + + + + + CLI + Manual + + + +```bash +npx shadcn-ui@latest add sheet +``` + + + + + + + +Install the following dependencies: + +```bash +npm install @radix-ui/react-dialog +``` + +Copy and paste the following code into your project. + + + +Update the import paths to match your project setup. + + + + + + + +### Usage + +```tsx +import { + Sheet, + SheetContent, + SheetDescription, + SheetHeader, + SheetTitle, + SheetTrigger, +} from '@/components/ui/sheet' +``` + +```tsx + + Open + + + Are you absolutely sure? + + This action cannot be undone. This will permanently delete your account and remove your data + from our servers. + + + + +``` + +## Examples + +### Side + +Use the `side` property to `` to indicate the edge of the screen where the component will appear. The values can be `top`, `right`, `bottom` or `left`. + + + +### Size + +You can adjust the size of the sheet using CSS classes: + +```tsx {3} + + Open + + + Are you absolutely sure? + + This action cannot be undone. This will permanently delete your account and remove your data + from our servers. + + + + +``` diff --git a/apps/design-system/content/docs/components/skeleton.mdx b/apps/design-system/content/docs/components/skeleton.mdx new file mode 100644 index 0000000000..5ab50f0250 --- /dev/null +++ b/apps/design-system/content/docs/components/skeleton.mdx @@ -0,0 +1,57 @@ +--- +title: Skeleton +description: Use to show a placeholder while content is loading. +component: true +source: + shadcn: true +--- + + + +## Installation + + + + + CLI + Manual + + + +```bash +npx shadcn-ui@latest add skeleton +``` + + + + + + + +Copy and paste the following code into your project. + + + +Update the import paths to match your project setup. + + + + + + + +## Usage + +```tsx +import { Skeleton } from '@/components/ui/skeleton' +``` + +```tsx + +``` + +## Examples + +### Card + + diff --git a/apps/design-system/content/docs/components/slider.mdx b/apps/design-system/content/docs/components/slider.mdx new file mode 100644 index 0000000000..e2f1120559 --- /dev/null +++ b/apps/design-system/content/docs/components/slider.mdx @@ -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 +--- + + + +## Installation + + + + + CLI + Manual + + + +```bash +npx shadcn-ui@latest add slider +``` + + + + + + + +Install the following dependencies: + +```bash +npm install @radix-ui/react-slider +``` + +Copy and paste the following code into your project. + + + +Update the import paths to match your project setup. + + + + + + + +## Usage + +```tsx +import { Slider } from '@/components/ui/slider' +``` + +```tsx + +``` diff --git a/apps/design-system/content/docs/components/sonner.mdx b/apps/design-system/content/docs/components/sonner.mdx new file mode 100644 index 0000000000..3af18cfd4a --- /dev/null +++ b/apps/design-system/content/docs/components/sonner.mdx @@ -0,0 +1,103 @@ +--- +title: Sonner +description: An opinionated toast component for React. +component: true +links: + doc: https://sonner.emilkowal.ski +source: + shadcn: true +--- + + + +## About + +Sonner is built and maintained by [emilkowalski\_](https://twitter.com/emilkowalski_). + +## Installation + + + + + CLI + Manual + + + + + +Run the following command: + +```bash +npx shadcn-ui@latest add sonner +``` + +Add the Toaster component + +```tsx title="app/layout.tsx" {1,9} +import { Toaster } from '@/components/ui/sonner' + +export default function RootLayout({ children }) { + return ( + + + +
      {children}
      + + + + ) +} +``` + +
      + +
      + + + + + +Install the following dependencies: + +```bash +npm install sonner next-themes +``` + +Copy and paste the following code into your project. + + + +Add the Toaster component + +```tsx title="app/layout.tsx" {1,9} +import { Toaster } from '@/components/ui/sonner' + +export default function RootLayout({ children }) { + return ( + + + +
      {children}
      + + + + ) +} +``` + +
      + +
      + +
      + +## Usage + +```tsx +import { toast } from 'sonner' +``` + +```tsx +toast('Event has been created.') +``` diff --git a/apps/design-system/content/docs/components/switch.mdx b/apps/design-system/content/docs/components/switch.mdx new file mode 100644 index 0000000000..3dcf332821 --- /dev/null +++ b/apps/design-system/content/docs/components/switch.mdx @@ -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 +--- + + + +## Installation + + + + + CLI + Manual + + + +```bash +npx shadcn-ui@latest add switch +``` + + + + + + + +Install the following dependencies: + +```bash +npm install @radix-ui/react-switch +``` + +Copy and paste the following code into your project. + + + +Update the import paths to match your project setup. + + + + + + + +## Usage + +```tsx +import { Switch } from '@/components/ui/switch' +``` + +```tsx + +``` + +## Examples + +### Form + + diff --git a/apps/design-system/content/docs/components/table.mdx b/apps/design-system/content/docs/components/table.mdx new file mode 100644 index 0000000000..ff05bebe23 --- /dev/null +++ b/apps/design-system/content/docs/components/table.mdx @@ -0,0 +1,85 @@ +--- +title: Table +description: A responsive table component. +component: true +source: + shadcn: true +--- + + + +## Installation + + + + + CLI + Manual + + + +```bash +npx shadcn-ui@latest add table +``` + + + + + + + +Copy and paste the following code into your project. + + + +Update the import paths to match your project setup. + + + + + + + +## Usage + +```tsx +import { + Table, + TableBody, + TableCaption, + TableCell, + TableHead, + TableHeader, + TableRow, +} from '@/components/ui/table' +``` + +```tsx +
      + A list of your recent invoices. + + + Invoice + Status + Method + Amount + + + + + INV001 + Paid + Credit Card + $250.00 + + +
      +``` + +## Data Table + +You can use the `` 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. diff --git a/apps/design-system/content/docs/components/tabs.mdx b/apps/design-system/content/docs/components/tabs.mdx new file mode 100644 index 0000000000..8085115c24 --- /dev/null +++ b/apps/design-system/content/docs/components/tabs.mdx @@ -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 +--- + + + +## Installation + + + + + CLI + Manual + + + +```bash +npx shadcn-ui@latest add tabs +``` + + + + + + + +Install the following dependencies: + +```bash +npm install @radix-ui/react-tabs +``` + +Copy and paste the following code into your project. + + + +Update the import paths to match your project setup. + + + + + + + +## Usage + +```tsx +import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' +``` + +```tsx + + + Account + Password + + Make changes to your account here. + Change your password here. + +``` diff --git a/apps/design-system/content/docs/components/textarea.mdx b/apps/design-system/content/docs/components/textarea.mdx new file mode 100644 index 0000000000..07b91d5896 --- /dev/null +++ b/apps/design-system/content/docs/components/textarea.mdx @@ -0,0 +1,77 @@ +--- +title: Textarea +description: Displays a form textarea or a component that looks like a textarea. +component: true +source: + shadcn: true +--- + + + +## Installation + + + + + CLI + Manual + + + +```bash +npx shadcn-ui@latest add textarea +``` + + + + + + + +Copy and paste the following code into your project. + + + +Update the import paths to match your project setup. + + + + + + + +## Usage + +```tsx +import { Textarea } from '@/components/ui/textarea' +``` + +```tsx +