mirror of
https://github.com/shadcn-ui/ui.git
synced 2026-06-14 11:21:36 +00:00
Compare commits
154 Commits
shadcn-ui@
...
shadcn-ui@
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
59b2cc8142 | ||
|
|
0e721be8dd | ||
|
|
7640ef7bbc | ||
|
|
8f3b28f50f | ||
|
|
73be841162 | ||
|
|
ad32fdeb7d | ||
|
|
2dd7864007 | ||
|
|
5d37bae1b8 | ||
|
|
4b59cb812e | ||
|
|
4b200ebf59 | ||
|
|
33795426dd | ||
|
|
fb614ac292 | ||
|
|
be580dbf76 | ||
|
|
98859e7b1c | ||
|
|
6b523b60db | ||
|
|
e3d5377a3e | ||
|
|
f60945c252 | ||
|
|
5eb33f7830 | ||
|
|
f6fef4a2ed | ||
|
|
f6f64ce773 | ||
|
|
7ce4414445 | ||
|
|
319c7c55cc | ||
|
|
57d404b5d3 | ||
|
|
6145dd8118 | ||
|
|
4fb98d520f | ||
|
|
1cf5fad881 | ||
|
|
6f3050248c | ||
|
|
1903eb94a8 | ||
|
|
9f3ae7746f | ||
|
|
c579e9232c | ||
|
|
08018ed623 | ||
|
|
1db90baaf2 | ||
|
|
3dc6207e97 | ||
|
|
5d2373fb7a | ||
|
|
e67c0d4507 | ||
|
|
147206c168 | ||
|
|
e6e9a6772b | ||
|
|
1ae9ffcf58 | ||
|
|
8fad64a854 | ||
|
|
7527ff490a | ||
|
|
3a279a2766 | ||
|
|
51c8c3d798 | ||
|
|
53f211b043 | ||
|
|
a2ed2883ac | ||
|
|
66c7f6d73b | ||
|
|
fc3d8288f7 | ||
|
|
6e399abdb4 | ||
|
|
3c22784a98 | ||
|
|
c82a6fab5f | ||
|
|
3fccfeb301 | ||
|
|
42e8eaf7cb | ||
|
|
d250109cc4 | ||
|
|
ef73e591c8 | ||
|
|
c6917799ce | ||
|
|
b4efc8aa4d | ||
|
|
35f776d38c | ||
|
|
5cadc5e983 | ||
|
|
e0782b328b | ||
|
|
cf0dadafce | ||
|
|
5877dcd21a | ||
|
|
95be4835b1 | ||
|
|
5a13def46d | ||
|
|
b8810caac7 | ||
|
|
24ec36ee7b | ||
|
|
dd94aa936f | ||
|
|
44f35d55b0 | ||
|
|
958a0fdb18 | ||
|
|
6b660033fb | ||
|
|
dac5a0bd2c | ||
|
|
648ddde3a2 | ||
|
|
4ec8a67dab | ||
|
|
9091dcdc1b | ||
|
|
33f89e9654 | ||
|
|
545423c93b | ||
|
|
82528a62a0 | ||
|
|
14abbd94b5 | ||
|
|
cf54b6fa71 | ||
|
|
46f247c47f | ||
|
|
beb0281ca2 | ||
|
|
a54ade1b98 | ||
|
|
11c1bc2cb9 | ||
|
|
4083876e80 | ||
|
|
0176754ff2 | ||
|
|
1be434bc64 | ||
|
|
2a346ede51 | ||
|
|
82c56f9503 | ||
|
|
f68798e50b | ||
|
|
b37fc17f04 | ||
|
|
d6063c5769 | ||
|
|
ef9fa600a5 | ||
|
|
43c4023ed8 | ||
|
|
c765635e13 | ||
|
|
95a9673b1e | ||
|
|
617cdd0e77 | ||
|
|
1536b7824e | ||
|
|
524e4b8b95 | ||
|
|
1f16cf4728 | ||
|
|
4f8d768e59 | ||
|
|
c0deeac0d0 | ||
|
|
897376329b | ||
|
|
d3d52fc687 | ||
|
|
4d0864a5c2 | ||
|
|
e8f58932bd | ||
|
|
2f0dbca221 | ||
|
|
58d012e342 | ||
|
|
963114e118 | ||
|
|
0a4286500e | ||
|
|
ae845788f6 | ||
|
|
ccb2d695a7 | ||
|
|
b838ffe8cc | ||
|
|
7ce6c495bd | ||
|
|
c9ca64d2b9 | ||
|
|
4bb9e9de53 | ||
|
|
c21ecfb665 | ||
|
|
613ec3583f | ||
|
|
170d3c087c | ||
|
|
4b0fbe27fa | ||
|
|
c34193cd34 | ||
|
|
88fdc989e9 | ||
|
|
4506822389 | ||
|
|
33a5fc7966 | ||
|
|
33b77e2f31 | ||
|
|
e3769277d8 | ||
|
|
3992a7b19c | ||
|
|
52c23746bc | ||
|
|
f68976b667 | ||
|
|
7a1f80af2c | ||
|
|
646f715388 | ||
|
|
9441130f05 | ||
|
|
48e3a4a326 | ||
|
|
98078fbe01 | ||
|
|
8be9e5d966 | ||
|
|
a8b1ea7e55 | ||
|
|
3c9f7ca0e2 | ||
|
|
c598f19845 | ||
|
|
7962cee384 | ||
|
|
de3c34845b | ||
|
|
6a1354e52d | ||
|
|
1532a15894 | ||
|
|
8e5d080900 | ||
|
|
cf95943446 | ||
|
|
3210bed755 | ||
|
|
eaa91d43df | ||
|
|
8cf0c7f3ba | ||
|
|
da7729644c | ||
|
|
aca3ef97e3 | ||
|
|
c9fecd4cdf | ||
|
|
6cf598d47f | ||
|
|
91727ec460 | ||
|
|
5e172fc34f | ||
|
|
f461ab0910 | ||
|
|
26c8d0f662 | ||
|
|
ac5c727fc9 | ||
|
|
54b1f5b661 |
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://unpkg.com/@changesets/config@2.3.0/schema.json",
|
||||
"changelog": ["@changesets/changelog-github", { "repo": "shadcn/ui" }],
|
||||
"changelog": ["@changesets/changelog-github", { "repo": "shadcn-ui/ui" }],
|
||||
"commit": false,
|
||||
"fixed": [],
|
||||
"linked": [],
|
||||
|
||||
12
.github/workflows/code-check.yml
vendored
12
.github/workflows/code-check.yml
vendored
@@ -7,7 +7,7 @@ on:
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
name: Lint
|
||||
name: pnpm lint
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
node-version: 18
|
||||
|
||||
- uses: pnpm/action-setup@v2.2.4
|
||||
name: Install pnpm
|
||||
@@ -43,7 +43,7 @@ jobs:
|
||||
|
||||
format:
|
||||
runs-on: ubuntu-latest
|
||||
name: Format
|
||||
name: pnpm format:check
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
@@ -52,7 +52,7 @@ jobs:
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
node-version: 18
|
||||
|
||||
- uses: pnpm/action-setup@v2.2.4
|
||||
name: Install pnpm
|
||||
@@ -81,7 +81,7 @@ jobs:
|
||||
|
||||
tsc:
|
||||
runs-on: ubuntu-latest
|
||||
name: TypeScript
|
||||
name: pnpm typecheck
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
@@ -90,7 +90,7 @@ jobs:
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
node-version: 18
|
||||
|
||||
- uses: pnpm/action-setup@v2.2.4
|
||||
name: Install pnpm
|
||||
|
||||
2
.github/workflows/prerelease-comment.yml
vendored
2
.github/workflows/prerelease-comment.yml
vendored
@@ -10,7 +10,7 @@ on:
|
||||
jobs:
|
||||
comment:
|
||||
if: |
|
||||
github.repository_owner == 'shadcn' &&
|
||||
github.repository_owner == 'shadcn-ui' &&
|
||||
${{ github.event.workflow_run.conclusion == 'success' }}
|
||||
runs-on: ubuntu-latest
|
||||
name: Write comment to the PR
|
||||
|
||||
2
.github/workflows/prerelease.yml
vendored
2
.github/workflows/prerelease.yml
vendored
@@ -10,7 +10,7 @@ on:
|
||||
jobs:
|
||||
prerelease:
|
||||
if: |
|
||||
github.repository_owner == 'shadcn' &&
|
||||
github.repository_owner == 'shadcn-ui' &&
|
||||
contains(github.event.pull_request.labels.*.name, '🚀 autorelease')
|
||||
name: Build & Publish a beta release to NPM
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -9,7 +9,7 @@ on:
|
||||
|
||||
jobs:
|
||||
release:
|
||||
if: ${{ github.repository_owner == 'shadcn' }}
|
||||
if: ${{ github.repository_owner == 'shadcn-ui' }}
|
||||
name: Create a PR for release workflow
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
4
.github/workflows/test.yml
vendored
4
.github/workflows/test.yml
vendored
@@ -7,7 +7,7 @@ on:
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
name: Test
|
||||
name: pnpm test
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
node-version: 18
|
||||
|
||||
- uses: pnpm/action-setup@v2.2.4
|
||||
name: Install pnpm
|
||||
|
||||
18
.kodiak.toml
Normal file
18
.kodiak.toml
Normal file
@@ -0,0 +1,18 @@
|
||||
# .kodiak.toml
|
||||
version = 1
|
||||
|
||||
[merge]
|
||||
automerge_label = "automerge"
|
||||
require_automerge_label = true
|
||||
method = "squash"
|
||||
delete_branch_on_merge = true
|
||||
optimistic_updates = false
|
||||
prioritize_ready_to_merge = true
|
||||
notify_on_conflict = true
|
||||
|
||||
[merge.message]
|
||||
title = "pull_request_title"
|
||||
body = "pull_request_body"
|
||||
include_pr_number = true
|
||||
body_type = "markdown"
|
||||
strip_html_comments = true
|
||||
175
CONTRIBUTING.md
Normal file
175
CONTRIBUTING.md
Normal file
@@ -0,0 +1,175 @@
|
||||
# Contributing
|
||||
|
||||
Thanks for your interest in contributing to ui.shadcn.com. We're happy to have you here.
|
||||
|
||||
Please take a moment to review this document before submitting your first pull request. We also strongly recommend that you check for open issues and pull requests to see if someone else is working on something similar.
|
||||
|
||||
If you need any help, feel free to reach out to [@shadcn](https://twitter.com/shadcn).
|
||||
|
||||
## About this repository
|
||||
|
||||
This repository is a monorepo.
|
||||
|
||||
- We use [pnpm](https://pnpm.io) and [`workspaces`](https://pnpm.io/workspaces) for development.
|
||||
- We use [Turborepo](https://turbo.build/repo) as our build system.
|
||||
- We use [changesets](https://github.com/changesets/changesets) for managing releases.
|
||||
|
||||
## Structure
|
||||
|
||||
This repository is structured as follows:
|
||||
|
||||
```
|
||||
apps
|
||||
└── www
|
||||
├── app
|
||||
├── components
|
||||
├── content
|
||||
└── registry
|
||||
├── default
|
||||
│ ├── example
|
||||
│ └── ui
|
||||
└── new-york
|
||||
├── example
|
||||
└── ui
|
||||
packages
|
||||
└── cli
|
||||
```
|
||||
|
||||
| Path | Description |
|
||||
| --------------------- | ---------------------------------------- |
|
||||
| `apps/www/app` | The Next.js application for the website. |
|
||||
| `apps/www/components` | The React components for the website. |
|
||||
| `apps/www/content` | The content for the website. |
|
||||
| `apps/www/registry` | The registry for the components. |
|
||||
| `packages/cli` | The `shadcn-ui` package. |
|
||||
|
||||
## Development
|
||||
|
||||
### Fork this repo
|
||||
|
||||
You can fork this repo by clicking the fork button in the top right corner of this page.
|
||||
|
||||
### Clone on your local machine
|
||||
|
||||
```bash
|
||||
git clone https://github.com/your-username/ui.git
|
||||
```
|
||||
|
||||
### Navigate to project directory
|
||||
|
||||
```bash
|
||||
cd ui
|
||||
```
|
||||
|
||||
### Create a new Branch
|
||||
|
||||
```bash
|
||||
git checkout -b my-new-branch
|
||||
```
|
||||
|
||||
### Install dependencies
|
||||
|
||||
```bash
|
||||
pnpm install
|
||||
```
|
||||
|
||||
### Run a workspace
|
||||
|
||||
You can use the `pnpm --filter=[WORKSPACE]` command to start the development process for a workspace.
|
||||
|
||||
#### Examples
|
||||
|
||||
1. To run the `ui.shadcn.com` website:
|
||||
|
||||
```bash
|
||||
pnpm --filter=www dev
|
||||
```
|
||||
|
||||
2. To run the `shadcn-ui` package:
|
||||
|
||||
```bash
|
||||
pnpm --filter=shadcn-ui dev
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
The documentation for this project is located in the `www` workspace. You can run the documentation locally by running the following command:
|
||||
|
||||
```bash
|
||||
pnpm --filter=www dev
|
||||
```
|
||||
|
||||
Documentation is written using [MDX](https://mdxjs.com). You can find the documentation files in the `apps/www/content/docs` directory.
|
||||
|
||||
## Components
|
||||
|
||||
We use a registry system for developing components. You can find the source code for the components under `apps/www/registry`. The components are organized by styles.
|
||||
|
||||
```bash
|
||||
apps
|
||||
└── www
|
||||
└── registry
|
||||
├── default
|
||||
│ ├── example
|
||||
│ └── ui
|
||||
└── new-york
|
||||
├── example
|
||||
└── ui
|
||||
```
|
||||
|
||||
When adding or modifying components, please ensure that:
|
||||
|
||||
1. You make the changes for every style.
|
||||
2. You update the documentation.
|
||||
3. You run `pnpm build:registry` to update the registry.
|
||||
|
||||
## Commit Convention
|
||||
|
||||
Before you create a Pull Request, please check whether your commits comply with
|
||||
the commit conventions used in this repository.
|
||||
|
||||
When you create a commit we kindly ask you to follow the convention
|
||||
`category(scope or module): message` in your commit message while using one of
|
||||
the following categories:
|
||||
|
||||
- `feat / feature`: all changes that introduce completely new code or new
|
||||
features
|
||||
- `fix`: changes that fix a bug (ideally you will additionally reference an
|
||||
issue if present)
|
||||
- `refactor`: any code related change that is not a fix nor a feature
|
||||
- `docs`: changing existing or creating new documentation (i.e. README, docs for
|
||||
usage of a lib or cli usage)
|
||||
- `build`: all changes regarding the build of the software, changes to
|
||||
dependencies or the addition of new dependencies
|
||||
- `test`: all changes regarding tests (adding new tests or changing existing
|
||||
ones)
|
||||
- `ci`: all changes regarding the configuration of continuous integration (i.e.
|
||||
github actions, ci system)
|
||||
- `chore`: all changes to the repository that do not fit into any of the above
|
||||
categories
|
||||
|
||||
e.g. `feat(components): add new prop to the avatar component`
|
||||
|
||||
If you are interested in the detailed specification you can visit
|
||||
https://www.conventionalcommits.org/ or check out the
|
||||
[Angular Commit Message Guidelines](https://github.com/angular/angular/blob/22b96b9/CONTRIBUTING.md#-commit-message-guidelines).
|
||||
|
||||
## Requests for new components
|
||||
|
||||
If you have a request for a new component, please open a discussion on GitHub. We'll be happy to help you out.
|
||||
|
||||
## CLI
|
||||
|
||||
The `shadcn-ui` package is a CLI for adding components to your project. You can find the documentation for the CLI [here](https://ui.shadcn.com/docs/cli).
|
||||
|
||||
Any changes to the CLI should be made in the `packages/cli` directory. If you can, it would be great if you could add tests for your changes.
|
||||
|
||||
## Testing
|
||||
|
||||
Tests are written using [Vitest](https://vitest.dev). You can run all the tests from the root of the repository.
|
||||
|
||||
```bash
|
||||
pnpm test
|
||||
```
|
||||
|
||||
Please ensure that the tests are passing when submitting a pull request. If you're adding new features, please include tests.
|
||||
@@ -8,6 +8,10 @@ Accessible and customizable components that you can copy and paste into your app
|
||||
|
||||
Visit http://ui.shadcn.com/docs to view the documentation.
|
||||
|
||||
## Contributing
|
||||
|
||||
Please read the [contributing guide](/CONTRIBUTING.md).
|
||||
|
||||
## License
|
||||
|
||||
Licensed under the [MIT license](https://github.com/shadcn/ui/blob/main/LICENSE.md).
|
||||
|
||||
@@ -3,4 +3,5 @@ node_modules
|
||||
.next
|
||||
build
|
||||
.contentlayer
|
||||
__registry__/index.tsx
|
||||
__registry__/index.tsx
|
||||
app/examples/mail/components/mail.tsx
|
||||
|
||||
@@ -68,6 +68,13 @@ export const Index: Record<string, any> = {
|
||||
component: React.lazy(() => import("@/registry/default/ui/card")),
|
||||
files: ["registry/default/ui/card.tsx"],
|
||||
},
|
||||
"carousel": {
|
||||
name: "carousel",
|
||||
type: "components:ui",
|
||||
registryDependencies: ["button"],
|
||||
component: React.lazy(() => import("@/registry/default/ui/carousel")),
|
||||
files: ["registry/default/ui/carousel.tsx"],
|
||||
},
|
||||
"checkbox": {
|
||||
name: "checkbox",
|
||||
type: "components:ui",
|
||||
@@ -103,6 +110,13 @@ export const Index: Record<string, any> = {
|
||||
component: React.lazy(() => import("@/registry/default/ui/dialog")),
|
||||
files: ["registry/default/ui/dialog.tsx"],
|
||||
},
|
||||
"drawer": {
|
||||
name: "drawer",
|
||||
type: "components:ui",
|
||||
registryDependencies: undefined,
|
||||
component: React.lazy(() => import("@/registry/default/ui/drawer")),
|
||||
files: ["registry/default/ui/drawer.tsx"],
|
||||
},
|
||||
"dropdown-menu": {
|
||||
name: "dropdown-menu",
|
||||
type: "components:ui",
|
||||
@@ -152,6 +166,13 @@ export const Index: Record<string, any> = {
|
||||
component: React.lazy(() => import("@/registry/default/ui/navigation-menu")),
|
||||
files: ["registry/default/ui/navigation-menu.tsx"],
|
||||
},
|
||||
"pagination": {
|
||||
name: "pagination",
|
||||
type: "components:ui",
|
||||
registryDependencies: ["button"],
|
||||
component: React.lazy(() => import("@/registry/default/ui/pagination")),
|
||||
files: ["registry/default/ui/pagination.tsx"],
|
||||
},
|
||||
"popover": {
|
||||
name: "popover",
|
||||
type: "components:ui",
|
||||
@@ -173,6 +194,13 @@ export const Index: Record<string, any> = {
|
||||
component: React.lazy(() => import("@/registry/default/ui/radio-group")),
|
||||
files: ["registry/default/ui/radio-group.tsx"],
|
||||
},
|
||||
"resizable": {
|
||||
name: "resizable",
|
||||
type: "components:ui",
|
||||
registryDependencies: undefined,
|
||||
component: React.lazy(() => import("@/registry/default/ui/resizable")),
|
||||
files: ["registry/default/ui/resizable.tsx"],
|
||||
},
|
||||
"scroll-area": {
|
||||
name: "scroll-area",
|
||||
type: "components:ui",
|
||||
@@ -215,6 +243,13 @@ export const Index: Record<string, any> = {
|
||||
component: React.lazy(() => import("@/registry/default/ui/slider")),
|
||||
files: ["registry/default/ui/slider.tsx"],
|
||||
},
|
||||
"sonner": {
|
||||
name: "sonner",
|
||||
type: "components:ui",
|
||||
registryDependencies: undefined,
|
||||
component: React.lazy(() => import("@/registry/default/ui/sonner")),
|
||||
files: ["registry/default/ui/sonner.tsx"],
|
||||
},
|
||||
"switch": {
|
||||
name: "switch",
|
||||
type: "components:ui",
|
||||
@@ -257,6 +292,13 @@ export const Index: Record<string, any> = {
|
||||
component: React.lazy(() => import("@/registry/default/ui/toggle")),
|
||||
files: ["registry/default/ui/toggle.tsx"],
|
||||
},
|
||||
"toggle-group": {
|
||||
name: "toggle-group",
|
||||
type: "components:ui",
|
||||
registryDependencies: ["toggle"],
|
||||
component: React.lazy(() => import("@/registry/default/ui/toggle-group")),
|
||||
files: ["registry/default/ui/toggle-group.tsx"],
|
||||
},
|
||||
"tooltip": {
|
||||
name: "tooltip",
|
||||
type: "components:ui",
|
||||
@@ -432,6 +474,48 @@ export const Index: Record<string, any> = {
|
||||
component: React.lazy(() => import("@/registry/default/example/card-with-form")),
|
||||
files: ["registry/default/example/card-with-form.tsx"],
|
||||
},
|
||||
"carousel-demo": {
|
||||
name: "carousel-demo",
|
||||
type: "components:example",
|
||||
registryDependencies: ["carousel"],
|
||||
component: React.lazy(() => import("@/registry/default/example/carousel-demo")),
|
||||
files: ["registry/default/example/carousel-demo.tsx"],
|
||||
},
|
||||
"carousel-size": {
|
||||
name: "carousel-size",
|
||||
type: "components:example",
|
||||
registryDependencies: ["carousel"],
|
||||
component: React.lazy(() => import("@/registry/default/example/carousel-size")),
|
||||
files: ["registry/default/example/carousel-size.tsx"],
|
||||
},
|
||||
"carousel-spacing": {
|
||||
name: "carousel-spacing",
|
||||
type: "components:example",
|
||||
registryDependencies: ["carousel"],
|
||||
component: React.lazy(() => import("@/registry/default/example/carousel-spacing")),
|
||||
files: ["registry/default/example/carousel-spacing.tsx"],
|
||||
},
|
||||
"carousel-orientation": {
|
||||
name: "carousel-orientation",
|
||||
type: "components:example",
|
||||
registryDependencies: ["carousel"],
|
||||
component: React.lazy(() => import("@/registry/default/example/carousel-orientation")),
|
||||
files: ["registry/default/example/carousel-orientation.tsx"],
|
||||
},
|
||||
"carousel-api": {
|
||||
name: "carousel-api",
|
||||
type: "components:example",
|
||||
registryDependencies: ["carousel"],
|
||||
component: React.lazy(() => import("@/registry/default/example/carousel-api")),
|
||||
files: ["registry/default/example/carousel-api.tsx"],
|
||||
},
|
||||
"carousel-plugin": {
|
||||
name: "carousel-plugin",
|
||||
type: "components:example",
|
||||
registryDependencies: ["carousel"],
|
||||
component: React.lazy(() => import("@/registry/default/example/carousel-plugin")),
|
||||
files: ["registry/default/example/carousel-plugin.tsx"],
|
||||
},
|
||||
"checkbox-demo": {
|
||||
name: "checkbox-demo",
|
||||
type: "components:example",
|
||||
@@ -502,6 +586,13 @@ export const Index: Record<string, any> = {
|
||||
component: React.lazy(() => import("@/registry/default/example/combobox-popover")),
|
||||
files: ["registry/default/example/combobox-popover.tsx"],
|
||||
},
|
||||
"combobox-responsive": {
|
||||
name: "combobox-responsive",
|
||||
type: "components:example",
|
||||
registryDependencies: ["combobox","popover","drawer"],
|
||||
component: React.lazy(() => import("@/registry/default/example/combobox-responsive")),
|
||||
files: ["registry/default/example/combobox-responsive.tsx"],
|
||||
},
|
||||
"command-demo": {
|
||||
name: "command-demo",
|
||||
type: "components:example",
|
||||
@@ -565,6 +656,27 @@ export const Index: Record<string, any> = {
|
||||
component: React.lazy(() => import("@/registry/default/example/dialog-demo")),
|
||||
files: ["registry/default/example/dialog-demo.tsx"],
|
||||
},
|
||||
"dialog-close-button": {
|
||||
name: "dialog-close-button",
|
||||
type: "components:example",
|
||||
registryDependencies: ["dialog","button"],
|
||||
component: React.lazy(() => import("@/registry/default/example/dialog-close-button")),
|
||||
files: ["registry/default/example/dialog-close-button.tsx"],
|
||||
},
|
||||
"drawer-demo": {
|
||||
name: "drawer-demo",
|
||||
type: "components:example",
|
||||
registryDependencies: ["drawer"],
|
||||
component: React.lazy(() => import("@/registry/default/example/drawer-demo")),
|
||||
files: ["registry/default/example/drawer-demo.tsx"],
|
||||
},
|
||||
"drawer-dialog": {
|
||||
name: "drawer-dialog",
|
||||
type: "components:example",
|
||||
registryDependencies: ["drawer","dialog"],
|
||||
component: React.lazy(() => import("@/registry/default/example/drawer-dialog")),
|
||||
files: ["registry/default/example/drawer-dialog.tsx"],
|
||||
},
|
||||
"dropdown-menu-demo": {
|
||||
name: "dropdown-menu-demo",
|
||||
type: "components:example",
|
||||
@@ -663,6 +775,13 @@ export const Index: Record<string, any> = {
|
||||
component: React.lazy(() => import("@/registry/default/example/navigation-menu-demo")),
|
||||
files: ["registry/default/example/navigation-menu-demo.tsx"],
|
||||
},
|
||||
"pagination-demo": {
|
||||
name: "pagination-demo",
|
||||
type: "components:example",
|
||||
registryDependencies: ["pagination"],
|
||||
component: React.lazy(() => import("@/registry/default/example/pagination-demo")),
|
||||
files: ["registry/default/example/pagination-demo.tsx"],
|
||||
},
|
||||
"popover-demo": {
|
||||
name: "popover-demo",
|
||||
type: "components:example",
|
||||
@@ -691,6 +810,34 @@ export const Index: Record<string, any> = {
|
||||
component: React.lazy(() => import("@/registry/default/example/radio-group-form")),
|
||||
files: ["registry/default/example/radio-group-form.tsx"],
|
||||
},
|
||||
"resizable-demo": {
|
||||
name: "resizable-demo",
|
||||
type: "components:example",
|
||||
registryDependencies: ["resizable"],
|
||||
component: React.lazy(() => import("@/registry/default/example/resizable-demo")),
|
||||
files: ["registry/default/example/resizable-demo.tsx"],
|
||||
},
|
||||
"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")),
|
||||
files: ["registry/default/example/resizable-demo-with-handle.tsx"],
|
||||
},
|
||||
"resizable-vertical": {
|
||||
name: "resizable-vertical",
|
||||
type: "components:example",
|
||||
registryDependencies: ["resizable"],
|
||||
component: React.lazy(() => import("@/registry/default/example/resizable-vertical")),
|
||||
files: ["registry/default/example/resizable-vertical.tsx"],
|
||||
},
|
||||
"resizable-handle": {
|
||||
name: "resizable-handle",
|
||||
type: "components:example",
|
||||
registryDependencies: ["resizable"],
|
||||
component: React.lazy(() => import("@/registry/default/example/resizable-handle")),
|
||||
files: ["registry/default/example/resizable-handle.tsx"],
|
||||
},
|
||||
"scroll-area-demo": {
|
||||
name: "scroll-area-demo",
|
||||
type: "components:example",
|
||||
@@ -698,6 +845,13 @@ export const Index: Record<string, any> = {
|
||||
component: React.lazy(() => import("@/registry/default/example/scroll-area-demo")),
|
||||
files: ["registry/default/example/scroll-area-demo.tsx"],
|
||||
},
|
||||
"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")),
|
||||
files: ["registry/default/example/scroll-area-horizontal-demo.tsx"],
|
||||
},
|
||||
"select-demo": {
|
||||
name: "select-demo",
|
||||
type: "components:example",
|
||||
@@ -705,6 +859,13 @@ export const Index: Record<string, any> = {
|
||||
component: React.lazy(() => import("@/registry/default/example/select-demo")),
|
||||
files: ["registry/default/example/select-demo.tsx"],
|
||||
},
|
||||
"select-scrollable": {
|
||||
name: "select-scrollable",
|
||||
type: "components:example",
|
||||
registryDependencies: ["select"],
|
||||
component: React.lazy(() => import("@/registry/default/example/select-scrollable")),
|
||||
files: ["registry/default/example/select-scrollable.tsx"],
|
||||
},
|
||||
"select-form": {
|
||||
name: "select-form",
|
||||
type: "components:example",
|
||||
@@ -747,6 +908,13 @@ export const Index: Record<string, any> = {
|
||||
component: React.lazy(() => import("@/registry/default/example/slider-demo")),
|
||||
files: ["registry/default/example/slider-demo.tsx"],
|
||||
},
|
||||
"sonner-demo": {
|
||||
name: "sonner-demo",
|
||||
type: "components:example",
|
||||
registryDependencies: ["sonner"],
|
||||
component: React.lazy(() => import("@/registry/default/example/sonner-demo")),
|
||||
files: ["registry/default/example/sonner-demo.tsx"],
|
||||
},
|
||||
"switch-demo": {
|
||||
name: "switch-demo",
|
||||
type: "components:example",
|
||||
@@ -852,6 +1020,48 @@ export const Index: Record<string, any> = {
|
||||
component: React.lazy(() => import("@/registry/default/example/toast-with-title")),
|
||||
files: ["registry/default/example/toast-with-title.tsx"],
|
||||
},
|
||||
"toggle-group-demo": {
|
||||
name: "toggle-group-demo",
|
||||
type: "components:example",
|
||||
registryDependencies: ["toggle-group"],
|
||||
component: React.lazy(() => import("@/registry/default/example/toggle-group-demo")),
|
||||
files: ["registry/default/example/toggle-group-demo.tsx"],
|
||||
},
|
||||
"toggle-group-disabled": {
|
||||
name: "toggle-group-disabled",
|
||||
type: "components:example",
|
||||
registryDependencies: ["toggle-group"],
|
||||
component: React.lazy(() => import("@/registry/default/example/toggle-group-disabled")),
|
||||
files: ["registry/default/example/toggle-group-disabled.tsx"],
|
||||
},
|
||||
"toggle-group-lg": {
|
||||
name: "toggle-group-lg",
|
||||
type: "components:example",
|
||||
registryDependencies: ["toggle-group"],
|
||||
component: React.lazy(() => import("@/registry/default/example/toggle-group-lg")),
|
||||
files: ["registry/default/example/toggle-group-lg.tsx"],
|
||||
},
|
||||
"toggle-group-outline": {
|
||||
name: "toggle-group-outline",
|
||||
type: "components:example",
|
||||
registryDependencies: ["toggle-group"],
|
||||
component: React.lazy(() => import("@/registry/default/example/toggle-group-outline")),
|
||||
files: ["registry/default/example/toggle-group-outline.tsx"],
|
||||
},
|
||||
"toggle-group-sm": {
|
||||
name: "toggle-group-sm",
|
||||
type: "components:example",
|
||||
registryDependencies: ["toggle-group"],
|
||||
component: React.lazy(() => import("@/registry/default/example/toggle-group-sm")),
|
||||
files: ["registry/default/example/toggle-group-sm.tsx"],
|
||||
},
|
||||
"toggle-group-single": {
|
||||
name: "toggle-group-single",
|
||||
type: "components:example",
|
||||
registryDependencies: ["toggle-group"],
|
||||
component: React.lazy(() => import("@/registry/default/example/toggle-group-single")),
|
||||
files: ["registry/default/example/toggle-group-single.tsx"],
|
||||
},
|
||||
"toggle-demo": {
|
||||
name: "toggle-demo",
|
||||
type: "components:example",
|
||||
@@ -1006,6 +1216,13 @@ export const Index: Record<string, any> = {
|
||||
component: React.lazy(() => import("@/registry/default/example/mode-toggle")),
|
||||
files: ["registry/default/example/mode-toggle.tsx"],
|
||||
},
|
||||
"cards": {
|
||||
name: "cards",
|
||||
type: "components:example",
|
||||
registryDependencies: undefined,
|
||||
component: React.lazy(() => import("@/registry/default/example/cards")),
|
||||
files: ["registry/default/example/cards/cards.tsx"],
|
||||
},
|
||||
}, "new-york": {
|
||||
"accordion": {
|
||||
name: "accordion",
|
||||
@@ -1070,6 +1287,13 @@ export const Index: Record<string, any> = {
|
||||
component: React.lazy(() => import("@/registry/new-york/ui/card")),
|
||||
files: ["registry/new-york/ui/card.tsx"],
|
||||
},
|
||||
"carousel": {
|
||||
name: "carousel",
|
||||
type: "components:ui",
|
||||
registryDependencies: ["button"],
|
||||
component: React.lazy(() => import("@/registry/new-york/ui/carousel")),
|
||||
files: ["registry/new-york/ui/carousel.tsx"],
|
||||
},
|
||||
"checkbox": {
|
||||
name: "checkbox",
|
||||
type: "components:ui",
|
||||
@@ -1105,6 +1329,13 @@ export const Index: Record<string, any> = {
|
||||
component: React.lazy(() => import("@/registry/new-york/ui/dialog")),
|
||||
files: ["registry/new-york/ui/dialog.tsx"],
|
||||
},
|
||||
"drawer": {
|
||||
name: "drawer",
|
||||
type: "components:ui",
|
||||
registryDependencies: undefined,
|
||||
component: React.lazy(() => import("@/registry/new-york/ui/drawer")),
|
||||
files: ["registry/new-york/ui/drawer.tsx"],
|
||||
},
|
||||
"dropdown-menu": {
|
||||
name: "dropdown-menu",
|
||||
type: "components:ui",
|
||||
@@ -1154,6 +1385,13 @@ export const Index: Record<string, any> = {
|
||||
component: React.lazy(() => import("@/registry/new-york/ui/navigation-menu")),
|
||||
files: ["registry/new-york/ui/navigation-menu.tsx"],
|
||||
},
|
||||
"pagination": {
|
||||
name: "pagination",
|
||||
type: "components:ui",
|
||||
registryDependencies: ["button"],
|
||||
component: React.lazy(() => import("@/registry/new-york/ui/pagination")),
|
||||
files: ["registry/new-york/ui/pagination.tsx"],
|
||||
},
|
||||
"popover": {
|
||||
name: "popover",
|
||||
type: "components:ui",
|
||||
@@ -1175,6 +1413,13 @@ export const Index: Record<string, any> = {
|
||||
component: React.lazy(() => import("@/registry/new-york/ui/radio-group")),
|
||||
files: ["registry/new-york/ui/radio-group.tsx"],
|
||||
},
|
||||
"resizable": {
|
||||
name: "resizable",
|
||||
type: "components:ui",
|
||||
registryDependencies: undefined,
|
||||
component: React.lazy(() => import("@/registry/new-york/ui/resizable")),
|
||||
files: ["registry/new-york/ui/resizable.tsx"],
|
||||
},
|
||||
"scroll-area": {
|
||||
name: "scroll-area",
|
||||
type: "components:ui",
|
||||
@@ -1217,6 +1462,13 @@ export const Index: Record<string, any> = {
|
||||
component: React.lazy(() => import("@/registry/new-york/ui/slider")),
|
||||
files: ["registry/new-york/ui/slider.tsx"],
|
||||
},
|
||||
"sonner": {
|
||||
name: "sonner",
|
||||
type: "components:ui",
|
||||
registryDependencies: undefined,
|
||||
component: React.lazy(() => import("@/registry/new-york/ui/sonner")),
|
||||
files: ["registry/new-york/ui/sonner.tsx"],
|
||||
},
|
||||
"switch": {
|
||||
name: "switch",
|
||||
type: "components:ui",
|
||||
@@ -1259,6 +1511,13 @@ export const Index: Record<string, any> = {
|
||||
component: React.lazy(() => import("@/registry/new-york/ui/toggle")),
|
||||
files: ["registry/new-york/ui/toggle.tsx"],
|
||||
},
|
||||
"toggle-group": {
|
||||
name: "toggle-group",
|
||||
type: "components:ui",
|
||||
registryDependencies: ["toggle"],
|
||||
component: React.lazy(() => import("@/registry/new-york/ui/toggle-group")),
|
||||
files: ["registry/new-york/ui/toggle-group.tsx"],
|
||||
},
|
||||
"tooltip": {
|
||||
name: "tooltip",
|
||||
type: "components:ui",
|
||||
@@ -1434,6 +1693,48 @@ export const Index: Record<string, any> = {
|
||||
component: React.lazy(() => import("@/registry/new-york/example/card-with-form")),
|
||||
files: ["registry/new-york/example/card-with-form.tsx"],
|
||||
},
|
||||
"carousel-demo": {
|
||||
name: "carousel-demo",
|
||||
type: "components:example",
|
||||
registryDependencies: ["carousel"],
|
||||
component: React.lazy(() => import("@/registry/new-york/example/carousel-demo")),
|
||||
files: ["registry/new-york/example/carousel-demo.tsx"],
|
||||
},
|
||||
"carousel-size": {
|
||||
name: "carousel-size",
|
||||
type: "components:example",
|
||||
registryDependencies: ["carousel"],
|
||||
component: React.lazy(() => import("@/registry/new-york/example/carousel-size")),
|
||||
files: ["registry/new-york/example/carousel-size.tsx"],
|
||||
},
|
||||
"carousel-spacing": {
|
||||
name: "carousel-spacing",
|
||||
type: "components:example",
|
||||
registryDependencies: ["carousel"],
|
||||
component: React.lazy(() => import("@/registry/new-york/example/carousel-spacing")),
|
||||
files: ["registry/new-york/example/carousel-spacing.tsx"],
|
||||
},
|
||||
"carousel-orientation": {
|
||||
name: "carousel-orientation",
|
||||
type: "components:example",
|
||||
registryDependencies: ["carousel"],
|
||||
component: React.lazy(() => import("@/registry/new-york/example/carousel-orientation")),
|
||||
files: ["registry/new-york/example/carousel-orientation.tsx"],
|
||||
},
|
||||
"carousel-api": {
|
||||
name: "carousel-api",
|
||||
type: "components:example",
|
||||
registryDependencies: ["carousel"],
|
||||
component: React.lazy(() => import("@/registry/new-york/example/carousel-api")),
|
||||
files: ["registry/new-york/example/carousel-api.tsx"],
|
||||
},
|
||||
"carousel-plugin": {
|
||||
name: "carousel-plugin",
|
||||
type: "components:example",
|
||||
registryDependencies: ["carousel"],
|
||||
component: React.lazy(() => import("@/registry/new-york/example/carousel-plugin")),
|
||||
files: ["registry/new-york/example/carousel-plugin.tsx"],
|
||||
},
|
||||
"checkbox-demo": {
|
||||
name: "checkbox-demo",
|
||||
type: "components:example",
|
||||
@@ -1504,6 +1805,13 @@ export const Index: Record<string, any> = {
|
||||
component: React.lazy(() => import("@/registry/new-york/example/combobox-popover")),
|
||||
files: ["registry/new-york/example/combobox-popover.tsx"],
|
||||
},
|
||||
"combobox-responsive": {
|
||||
name: "combobox-responsive",
|
||||
type: "components:example",
|
||||
registryDependencies: ["combobox","popover","drawer"],
|
||||
component: React.lazy(() => import("@/registry/new-york/example/combobox-responsive")),
|
||||
files: ["registry/new-york/example/combobox-responsive.tsx"],
|
||||
},
|
||||
"command-demo": {
|
||||
name: "command-demo",
|
||||
type: "components:example",
|
||||
@@ -1567,6 +1875,27 @@ export const Index: Record<string, any> = {
|
||||
component: React.lazy(() => import("@/registry/new-york/example/dialog-demo")),
|
||||
files: ["registry/new-york/example/dialog-demo.tsx"],
|
||||
},
|
||||
"dialog-close-button": {
|
||||
name: "dialog-close-button",
|
||||
type: "components:example",
|
||||
registryDependencies: ["dialog","button"],
|
||||
component: React.lazy(() => import("@/registry/new-york/example/dialog-close-button")),
|
||||
files: ["registry/new-york/example/dialog-close-button.tsx"],
|
||||
},
|
||||
"drawer-demo": {
|
||||
name: "drawer-demo",
|
||||
type: "components:example",
|
||||
registryDependencies: ["drawer"],
|
||||
component: React.lazy(() => import("@/registry/new-york/example/drawer-demo")),
|
||||
files: ["registry/new-york/example/drawer-demo.tsx"],
|
||||
},
|
||||
"drawer-dialog": {
|
||||
name: "drawer-dialog",
|
||||
type: "components:example",
|
||||
registryDependencies: ["drawer","dialog"],
|
||||
component: React.lazy(() => import("@/registry/new-york/example/drawer-dialog")),
|
||||
files: ["registry/new-york/example/drawer-dialog.tsx"],
|
||||
},
|
||||
"dropdown-menu-demo": {
|
||||
name: "dropdown-menu-demo",
|
||||
type: "components:example",
|
||||
@@ -1665,6 +1994,13 @@ export const Index: Record<string, any> = {
|
||||
component: React.lazy(() => import("@/registry/new-york/example/navigation-menu-demo")),
|
||||
files: ["registry/new-york/example/navigation-menu-demo.tsx"],
|
||||
},
|
||||
"pagination-demo": {
|
||||
name: "pagination-demo",
|
||||
type: "components:example",
|
||||
registryDependencies: ["pagination"],
|
||||
component: React.lazy(() => import("@/registry/new-york/example/pagination-demo")),
|
||||
files: ["registry/new-york/example/pagination-demo.tsx"],
|
||||
},
|
||||
"popover-demo": {
|
||||
name: "popover-demo",
|
||||
type: "components:example",
|
||||
@@ -1693,6 +2029,34 @@ export const Index: Record<string, any> = {
|
||||
component: React.lazy(() => import("@/registry/new-york/example/radio-group-form")),
|
||||
files: ["registry/new-york/example/radio-group-form.tsx"],
|
||||
},
|
||||
"resizable-demo": {
|
||||
name: "resizable-demo",
|
||||
type: "components:example",
|
||||
registryDependencies: ["resizable"],
|
||||
component: React.lazy(() => import("@/registry/new-york/example/resizable-demo")),
|
||||
files: ["registry/new-york/example/resizable-demo.tsx"],
|
||||
},
|
||||
"resizable-demo-with-handle": {
|
||||
name: "resizable-demo-with-handle",
|
||||
type: "components:example",
|
||||
registryDependencies: ["resizable"],
|
||||
component: React.lazy(() => import("@/registry/new-york/example/resizable-demo-with-handle")),
|
||||
files: ["registry/new-york/example/resizable-demo-with-handle.tsx"],
|
||||
},
|
||||
"resizable-vertical": {
|
||||
name: "resizable-vertical",
|
||||
type: "components:example",
|
||||
registryDependencies: ["resizable"],
|
||||
component: React.lazy(() => import("@/registry/new-york/example/resizable-vertical")),
|
||||
files: ["registry/new-york/example/resizable-vertical.tsx"],
|
||||
},
|
||||
"resizable-handle": {
|
||||
name: "resizable-handle",
|
||||
type: "components:example",
|
||||
registryDependencies: ["resizable"],
|
||||
component: React.lazy(() => import("@/registry/new-york/example/resizable-handle")),
|
||||
files: ["registry/new-york/example/resizable-handle.tsx"],
|
||||
},
|
||||
"scroll-area-demo": {
|
||||
name: "scroll-area-demo",
|
||||
type: "components:example",
|
||||
@@ -1700,6 +2064,13 @@ export const Index: Record<string, any> = {
|
||||
component: React.lazy(() => import("@/registry/new-york/example/scroll-area-demo")),
|
||||
files: ["registry/new-york/example/scroll-area-demo.tsx"],
|
||||
},
|
||||
"scroll-area-horizontal-demo": {
|
||||
name: "scroll-area-horizontal-demo",
|
||||
type: "components:example",
|
||||
registryDependencies: ["scroll-area"],
|
||||
component: React.lazy(() => import("@/registry/new-york/example/scroll-area-horizontal-demo")),
|
||||
files: ["registry/new-york/example/scroll-area-horizontal-demo.tsx"],
|
||||
},
|
||||
"select-demo": {
|
||||
name: "select-demo",
|
||||
type: "components:example",
|
||||
@@ -1707,6 +2078,13 @@ export const Index: Record<string, any> = {
|
||||
component: React.lazy(() => import("@/registry/new-york/example/select-demo")),
|
||||
files: ["registry/new-york/example/select-demo.tsx"],
|
||||
},
|
||||
"select-scrollable": {
|
||||
name: "select-scrollable",
|
||||
type: "components:example",
|
||||
registryDependencies: ["select"],
|
||||
component: React.lazy(() => import("@/registry/new-york/example/select-scrollable")),
|
||||
files: ["registry/new-york/example/select-scrollable.tsx"],
|
||||
},
|
||||
"select-form": {
|
||||
name: "select-form",
|
||||
type: "components:example",
|
||||
@@ -1749,6 +2127,13 @@ export const Index: Record<string, any> = {
|
||||
component: React.lazy(() => import("@/registry/new-york/example/slider-demo")),
|
||||
files: ["registry/new-york/example/slider-demo.tsx"],
|
||||
},
|
||||
"sonner-demo": {
|
||||
name: "sonner-demo",
|
||||
type: "components:example",
|
||||
registryDependencies: ["sonner"],
|
||||
component: React.lazy(() => import("@/registry/new-york/example/sonner-demo")),
|
||||
files: ["registry/new-york/example/sonner-demo.tsx"],
|
||||
},
|
||||
"switch-demo": {
|
||||
name: "switch-demo",
|
||||
type: "components:example",
|
||||
@@ -1854,6 +2239,48 @@ export const Index: Record<string, any> = {
|
||||
component: React.lazy(() => import("@/registry/new-york/example/toast-with-title")),
|
||||
files: ["registry/new-york/example/toast-with-title.tsx"],
|
||||
},
|
||||
"toggle-group-demo": {
|
||||
name: "toggle-group-demo",
|
||||
type: "components:example",
|
||||
registryDependencies: ["toggle-group"],
|
||||
component: React.lazy(() => import("@/registry/new-york/example/toggle-group-demo")),
|
||||
files: ["registry/new-york/example/toggle-group-demo.tsx"],
|
||||
},
|
||||
"toggle-group-disabled": {
|
||||
name: "toggle-group-disabled",
|
||||
type: "components:example",
|
||||
registryDependencies: ["toggle-group"],
|
||||
component: React.lazy(() => import("@/registry/new-york/example/toggle-group-disabled")),
|
||||
files: ["registry/new-york/example/toggle-group-disabled.tsx"],
|
||||
},
|
||||
"toggle-group-lg": {
|
||||
name: "toggle-group-lg",
|
||||
type: "components:example",
|
||||
registryDependencies: ["toggle-group"],
|
||||
component: React.lazy(() => import("@/registry/new-york/example/toggle-group-lg")),
|
||||
files: ["registry/new-york/example/toggle-group-lg.tsx"],
|
||||
},
|
||||
"toggle-group-outline": {
|
||||
name: "toggle-group-outline",
|
||||
type: "components:example",
|
||||
registryDependencies: ["toggle-group"],
|
||||
component: React.lazy(() => import("@/registry/new-york/example/toggle-group-outline")),
|
||||
files: ["registry/new-york/example/toggle-group-outline.tsx"],
|
||||
},
|
||||
"toggle-group-sm": {
|
||||
name: "toggle-group-sm",
|
||||
type: "components:example",
|
||||
registryDependencies: ["toggle-group"],
|
||||
component: React.lazy(() => import("@/registry/new-york/example/toggle-group-sm")),
|
||||
files: ["registry/new-york/example/toggle-group-sm.tsx"],
|
||||
},
|
||||
"toggle-group-single": {
|
||||
name: "toggle-group-single",
|
||||
type: "components:example",
|
||||
registryDependencies: ["toggle-group"],
|
||||
component: React.lazy(() => import("@/registry/new-york/example/toggle-group-single")),
|
||||
files: ["registry/new-york/example/toggle-group-single.tsx"],
|
||||
},
|
||||
"toggle-demo": {
|
||||
name: "toggle-demo",
|
||||
type: "components:example",
|
||||
@@ -2008,5 +2435,12 @@ export const Index: Record<string, any> = {
|
||||
component: React.lazy(() => import("@/registry/new-york/example/mode-toggle")),
|
||||
files: ["registry/new-york/example/mode-toggle.tsx"],
|
||||
},
|
||||
"cards": {
|
||||
name: "cards",
|
||||
type: "components:example",
|
||||
registryDependencies: undefined,
|
||||
component: React.lazy(() => import("@/registry/new-york/example/cards")),
|
||||
files: ["registry/new-york/example/cards/cards.tsx"],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -4,13 +4,12 @@ import { allDocs } from "contentlayer/generated"
|
||||
import "@/styles/mdx.css"
|
||||
import type { Metadata } from "next"
|
||||
import Link from "next/link"
|
||||
import { ChevronRightIcon } from "@radix-ui/react-icons"
|
||||
import { ChevronRightIcon, ExternalLinkIcon } from "@radix-ui/react-icons"
|
||||
import Balancer from "react-wrap-balancer"
|
||||
|
||||
import { siteConfig } from "@/config/site"
|
||||
import { getTableOfContents } from "@/lib/toc"
|
||||
import { absoluteUrl, cn } from "@/lib/utils"
|
||||
import { Icons } from "@/components/icons"
|
||||
import { Mdx } from "@/components/mdx-components"
|
||||
import { DocsPager } from "@/components/pager"
|
||||
import { DashboardTableOfContents } from "@/components/toc"
|
||||
@@ -28,7 +27,7 @@ async function getDocFromParams({ params }: DocPageProps) {
|
||||
const doc = allDocs.find((doc) => doc.slugAsParams === slug)
|
||||
|
||||
if (!doc) {
|
||||
null
|
||||
return null
|
||||
}
|
||||
|
||||
return doc
|
||||
@@ -107,27 +106,28 @@ export default async function DocPage({ params }: DocPageProps) {
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
{doc.radix ? (
|
||||
{doc.links ? (
|
||||
<div className="flex items-center space-x-2 pt-4">
|
||||
{doc.radix?.link && (
|
||||
{doc.links?.doc && (
|
||||
<Link
|
||||
href={doc.radix.link}
|
||||
href={doc.links.doc}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className={cn(badgeVariants({ variant: "secondary" }))}
|
||||
className={cn(badgeVariants({ variant: "secondary" }), "gap-1")}
|
||||
>
|
||||
<Icons.radix className="mr-1 h-3 w-3" />
|
||||
Radix UI
|
||||
Docs
|
||||
<ExternalLinkIcon className="h-3 w-3" />
|
||||
</Link>
|
||||
)}
|
||||
{doc.radix?.api && (
|
||||
{doc.links?.api && (
|
||||
<Link
|
||||
href={doc.radix.api}
|
||||
href={doc.links.api}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className={cn(badgeVariants({ variant: "secondary" }))}
|
||||
className={cn(badgeVariants({ variant: "secondary" }), "gap-1")}
|
||||
>
|
||||
API Reference
|
||||
<ExternalLinkIcon className="h-3 w-3" />
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
@@ -139,9 +139,11 @@ export default async function DocPage({ params }: DocPageProps) {
|
||||
</div>
|
||||
{doc.toc && (
|
||||
<div className="hidden text-sm xl:block">
|
||||
<div className="sticky top-16 -mt-10 h-[calc(100vh-3.5rem)] overflow-hidden pt-6">
|
||||
<div className="sticky top-16 -mt-10 pt-4">
|
||||
<ScrollArea className="pb-10">
|
||||
<DashboardTableOfContents toc={toc} />
|
||||
<div className="sticky top-16 -mt-10 h-[calc(100vh-3.5rem)] py-12">
|
||||
<DashboardTableOfContents toc={toc} />
|
||||
</div>
|
||||
</ScrollArea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -11,7 +11,7 @@ export default function DocsLayout({ children }: DocsLayoutProps) {
|
||||
<div className="border-b">
|
||||
<div className="container flex-1 items-start md:grid md:grid-cols-[220px_minmax(0,1fr)] md:gap-6 lg:grid-cols-[240px_minmax(0,1fr)] lg:gap-10">
|
||||
<aside className="fixed top-14 z-30 -ml-2 hidden h-[calc(100vh-3.5rem)] w-full shrink-0 md:sticky md:block">
|
||||
<ScrollArea className="h-full py-6 pl-8 pr-6 lg:py-8">
|
||||
<ScrollArea className="h-full py-6 pr-6 lg:py-8">
|
||||
<DocsSidebarNav items={docsConfig.sidebarNav} />
|
||||
</ScrollArea>
|
||||
</aside>
|
||||
|
||||
@@ -64,7 +64,7 @@ export function UserAuthForm({ className, ...props }: UserAuthFormProps) {
|
||||
) : (
|
||||
<Icons.gitHub className="mr-2 h-4 w-4" />
|
||||
)}{" "}
|
||||
Github
|
||||
GitHub
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -40,7 +40,7 @@ export default function AuthenticationPage() {
|
||||
>
|
||||
Login
|
||||
</Link>
|
||||
<div className="relative hidden h-full flex-col bg-muted p-10 text-white dark:border-r lg:flex">
|
||||
<div className="relative hidden h-full flex-col bg-muted p-10 text-white lg:flex dark:border-r">
|
||||
<div className="absolute inset-0 bg-zinc-900" />
|
||||
<div className="relative z-20 flex items-center text-lg font-medium">
|
||||
<svg
|
||||
|
||||
@@ -31,8 +31,8 @@ export function DemoGithub() {
|
||||
<div className="space-y-1">
|
||||
<CardTitle>shadcn/ui</CardTitle>
|
||||
<CardDescription>
|
||||
Beautifully designed components built with Radix UI and Tailwind
|
||||
CSS.
|
||||
Beautifully designed components that you can copy and paste into
|
||||
your apps. Accessible. Customizable. Open Source.
|
||||
</CardDescription>
|
||||
</div>
|
||||
<div className="flex items-center space-x-1 rounded-md bg-secondary text-secondary-foreground">
|
||||
|
||||
@@ -30,42 +30,52 @@ export function DemoPaymentMethod() {
|
||||
</CardHeader>
|
||||
<CardContent className="grid gap-6">
|
||||
<RadioGroup defaultValue="card" className="grid grid-cols-3 gap-4">
|
||||
<Label
|
||||
htmlFor="card"
|
||||
className="flex flex-col items-center justify-between rounded-md border-2 border-muted bg-popover p-4 hover:bg-accent hover:text-accent-foreground [&:has([data-state=checked])]:border-primary"
|
||||
>
|
||||
<RadioGroupItem value="card" id="card" className="sr-only" />
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
className="mb-3 h-6 w-6"
|
||||
<div>
|
||||
<RadioGroupItem value="card" id="card" className="peer sr-only" />
|
||||
<Label
|
||||
htmlFor="card"
|
||||
className="flex flex-col items-center justify-between rounded-md border-2 border-muted bg-popover p-4 hover:bg-accent hover:text-accent-foreground peer-data-[state=checked]:border-primary [&:has([data-state=checked])]:border-primary"
|
||||
>
|
||||
<rect width="20" height="14" x="2" y="5" rx="2" />
|
||||
<path d="M2 10h20" />
|
||||
</svg>
|
||||
Card
|
||||
</Label>
|
||||
<Label
|
||||
htmlFor="paypal"
|
||||
className="flex flex-col items-center justify-between rounded-md border-2 border-muted bg-popover p-4 hover:bg-accent hover:text-accent-foreground [&:has([data-state=checked])]:border-primary"
|
||||
>
|
||||
<RadioGroupItem value="paypal" id="paypal" className="sr-only" />
|
||||
<Icons.paypal className="mb-3 h-6 w-6" />
|
||||
Paypal
|
||||
</Label>
|
||||
<Label
|
||||
htmlFor="apple"
|
||||
className="flex flex-col items-center justify-between rounded-md border-2 border-muted bg-popover p-4 hover:bg-accent hover:text-accent-foreground [&:has([data-state=checked])]:border-primary"
|
||||
>
|
||||
<RadioGroupItem value="apple" id="apple" className="sr-only" />
|
||||
<Icons.apple className="mb-3 h-6 w-6" />
|
||||
Apple
|
||||
</Label>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
className="mb-3 h-6 w-6"
|
||||
>
|
||||
<rect width="20" height="14" x="2" y="5" rx="2" />
|
||||
<path d="M2 10h20" />
|
||||
</svg>
|
||||
Card
|
||||
</Label>
|
||||
</div>
|
||||
<div>
|
||||
<RadioGroupItem
|
||||
value="paypal"
|
||||
id="paypal"
|
||||
className="peer sr-only"
|
||||
/>
|
||||
<Label
|
||||
htmlFor="paypal"
|
||||
className="flex flex-col items-center justify-between rounded-md border-2 border-muted bg-popover p-4 hover:bg-accent hover:text-accent-foreground peer-data-[state=checked]:border-primary [&:has([data-state=checked])]:border-primary"
|
||||
>
|
||||
<Icons.paypal className="mb-3 h-6 w-6" />
|
||||
Paypal
|
||||
</Label>
|
||||
</div>
|
||||
<div>
|
||||
<RadioGroupItem value="apple" id="apple" className="peer sr-only" />
|
||||
<Label
|
||||
htmlFor="apple"
|
||||
className="flex flex-col items-center justify-between rounded-md border-2 border-muted bg-popover p-4 hover:bg-accent hover:text-accent-foreground peer-data-[state=checked]:border-primary [&:has([data-state=checked])]:border-primary"
|
||||
>
|
||||
<Icons.apple className="mb-3 h-6 w-6" />
|
||||
Apple
|
||||
</Label>
|
||||
</div>
|
||||
</RadioGroup>
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor="name">Name</Label>
|
||||
|
||||
@@ -75,7 +75,7 @@ export default function CardsPage() {
|
||||
<DemoNotifications />
|
||||
</DemoContainer>
|
||||
</div>
|
||||
<div className="col-span-2 grid items-start gap-6 lg:col-span-1 lg:grid-cols-2 xl:grid-cols-1">
|
||||
<div className="col-span-2 grid items-start gap-6 lg:col-span-2 lg:grid-cols-2 xl:col-span-1 xl:grid-cols-1">
|
||||
<DemoContainer>
|
||||
<DemoReportAnIssue />
|
||||
</DemoContainer>
|
||||
|
||||
@@ -71,7 +71,12 @@ export function Overview() {
|
||||
axisLine={false}
|
||||
tickFormatter={(value) => `$${value}`}
|
||||
/>
|
||||
<Bar dataKey="total" fill="#adfa1d" radius={[4, 4, 0, 0]} />
|
||||
<Bar
|
||||
dataKey="total"
|
||||
fill="currentColor"
|
||||
radius={[4, 4, 0, 0]}
|
||||
className="fill-primary"
|
||||
/>
|
||||
</BarChart>
|
||||
</ResponsiveContainer>
|
||||
)
|
||||
|
||||
@@ -100,6 +100,7 @@ export default function TeamSwitcher({ className }: TeamSwitcherProps) {
|
||||
<AvatarImage
|
||||
src={`https://avatar.vercel.sh/${selectedTeam.value}.png`}
|
||||
alt={selectedTeam.label}
|
||||
className="grayscale"
|
||||
/>
|
||||
<AvatarFallback>SC</AvatarFallback>
|
||||
</Avatar>
|
||||
|
||||
@@ -25,7 +25,7 @@ import { UserNav } from "@/app/examples/dashboard/components/user-nav"
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Dashboard",
|
||||
description: "Example dashboard app using the components.",
|
||||
description: "Example dashboard app built using the components.",
|
||||
}
|
||||
|
||||
export default function DashboardPage() {
|
||||
|
||||
@@ -184,10 +184,10 @@ export function AccountForm() {
|
||||
<CommandGroup>
|
||||
{languages.map((language) => (
|
||||
<CommandItem
|
||||
value={language.value}
|
||||
value={language.label}
|
||||
key={language.value}
|
||||
onSelect={(value) => {
|
||||
form.setValue("language", value)
|
||||
onSelect={() => {
|
||||
form.setValue("language", language.value)
|
||||
}}
|
||||
>
|
||||
<CheckIcon
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { Metadata } from "next"
|
||||
import Link from "next/link"
|
||||
import { ArrowRightIcon } from "@radix-ui/react-icons"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { Announcement } from "@/components/announcement"
|
||||
import { ExamplesNav } from "@/components/examples-nav"
|
||||
import {
|
||||
PageActions,
|
||||
PageHeader,
|
||||
PageHeaderDescription,
|
||||
PageHeaderHeading,
|
||||
} from "@/components/page-header"
|
||||
import { buttonVariants } from "@/registry/new-york/ui/button"
|
||||
import { Separator } from "@/registry/new-york/ui/separator"
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Examples",
|
||||
@@ -25,27 +25,17 @@ export default function ExamplesLayout({ children }: ExamplesLayoutProps) {
|
||||
return (
|
||||
<>
|
||||
<div className="container relative">
|
||||
<PageHeader className="page-header pb-8">
|
||||
<Link
|
||||
href="/docs/changelog"
|
||||
className="inline-flex items-center rounded-lg bg-muted px-3 py-1 text-sm font-medium"
|
||||
>
|
||||
🎉 <Separator className="mx-2 h-4" orientation="vertical" />{" "}
|
||||
<span className="sm:hidden">Style, a new CLI and more.</span>
|
||||
<span className="hidden sm:inline">
|
||||
Introducing Style, a new CLI and more.
|
||||
</span>
|
||||
<ArrowRightIcon className="ml-1 h-4 w-4" />
|
||||
</Link>
|
||||
<PageHeader>
|
||||
<Announcement />
|
||||
<PageHeaderHeading className="hidden md:block">
|
||||
Check out some examples.
|
||||
Check out some examples
|
||||
</PageHeaderHeading>
|
||||
<PageHeaderHeading className="md:hidden">Examples</PageHeaderHeading>
|
||||
<PageHeaderDescription>
|
||||
Dashboard, cards, authentication. Some examples built using the
|
||||
components. Use this as a guide to build your own.
|
||||
</PageHeaderDescription>
|
||||
<section className="flex w-full items-center space-x-4 pb-8 pt-4 md:pb-10">
|
||||
<PageActions>
|
||||
<Link
|
||||
href="/docs"
|
||||
className={cn(buttonVariants(), "rounded-[6px]")}
|
||||
@@ -61,11 +51,11 @@ export default function ExamplesLayout({ children }: ExamplesLayoutProps) {
|
||||
>
|
||||
Components
|
||||
</Link>
|
||||
</section>
|
||||
</PageActions>
|
||||
</PageHeader>
|
||||
<section>
|
||||
<ExamplesNav />
|
||||
<div className="overflow-hidden rounded-[0.5rem] border bg-background shadow">
|
||||
<div className="overflow-hidden rounded-[0.5rem] border bg-background shadow-md md:shadow-xl">
|
||||
{children}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
63
apps/www/app/examples/mail/components/account-switcher.tsx
Normal file
63
apps/www/app/examples/mail/components/account-switcher.tsx
Normal file
@@ -0,0 +1,63 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/registry/new-york/ui/select"
|
||||
|
||||
interface AccountSwitcherProps {
|
||||
isCollapsed: boolean
|
||||
accounts: {
|
||||
label: string
|
||||
email: string
|
||||
icon: React.ReactNode
|
||||
}[]
|
||||
}
|
||||
|
||||
export function AccountSwitcher({
|
||||
isCollapsed,
|
||||
accounts,
|
||||
}: AccountSwitcherProps) {
|
||||
const [selectedAccount, setSelectedAccount] = React.useState<string>(
|
||||
accounts[0].email
|
||||
)
|
||||
|
||||
return (
|
||||
<Select defaultValue={selectedAccount} onValueChange={setSelectedAccount}>
|
||||
<SelectTrigger
|
||||
className={cn(
|
||||
"flex items-center gap-2 [&>span]:line-clamp-1 [&>span]:flex [&>span]:w-full [&>span]:items-center [&>span]:gap-1 [&>span]:truncate [&_svg]:h-4 [&_svg]:w-4 [&_svg]:shrink-0",
|
||||
isCollapsed &&
|
||||
"flex h-9 w-9 shrink-0 items-center justify-center p-0 [&>span]:w-auto [&>svg]:hidden"
|
||||
)}
|
||||
aria-label="Select account"
|
||||
>
|
||||
<SelectValue placeholder="Select an account">
|
||||
{accounts.find((account) => account.email === selectedAccount)?.icon}
|
||||
<span className={cn("ml-2", isCollapsed && "hidden")}>
|
||||
{
|
||||
accounts.find((account) => account.email === selectedAccount)
|
||||
?.label
|
||||
}
|
||||
</span>
|
||||
</SelectValue>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{accounts.map((account) => (
|
||||
<SelectItem key={account.email} value={account.email}>
|
||||
<div className="flex items-center gap-3 [&_svg]:h-4 [&_svg]:w-4 [&_svg]:shrink-0 [&_svg]:text-foreground">
|
||||
{account.icon}
|
||||
{account.email}
|
||||
</div>
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
)
|
||||
}
|
||||
258
apps/www/app/examples/mail/components/mail-display.tsx
Normal file
258
apps/www/app/examples/mail/components/mail-display.tsx
Normal file
@@ -0,0 +1,258 @@
|
||||
import addDays from "date-fns/addDays"
|
||||
import addHours from "date-fns/addHours"
|
||||
import format from "date-fns/format"
|
||||
import nextSaturday from "date-fns/nextSaturday"
|
||||
import {
|
||||
Archive,
|
||||
ArchiveX,
|
||||
Clock,
|
||||
Forward,
|
||||
MoreVertical,
|
||||
Reply,
|
||||
ReplyAll,
|
||||
Trash2,
|
||||
} from "lucide-react"
|
||||
|
||||
import {
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
} from "@/registry/default/ui/dropdown-menu"
|
||||
import {
|
||||
Avatar,
|
||||
AvatarFallback,
|
||||
AvatarImage,
|
||||
} from "@/registry/new-york/ui/avatar"
|
||||
import { Button } from "@/registry/new-york/ui/button"
|
||||
import { Calendar } from "@/registry/new-york/ui/calendar"
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/registry/new-york/ui/dropdown-menu"
|
||||
import { Label } from "@/registry/new-york/ui/label"
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/registry/new-york/ui/popover"
|
||||
import { Separator } from "@/registry/new-york/ui/separator"
|
||||
import { Switch } from "@/registry/new-york/ui/switch"
|
||||
import { Textarea } from "@/registry/new-york/ui/textarea"
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipTrigger,
|
||||
} from "@/registry/new-york/ui/tooltip"
|
||||
import { Mail } from "@/app/examples/mail/data"
|
||||
|
||||
interface MailDisplayProps {
|
||||
mail: Mail | null
|
||||
}
|
||||
|
||||
export function MailDisplay({ mail }: MailDisplayProps) {
|
||||
const today = new Date()
|
||||
|
||||
return (
|
||||
<div className="flex h-full flex-col">
|
||||
<div className="flex items-center p-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button variant="ghost" size="icon" disabled={!mail}>
|
||||
<Archive className="h-4 w-4" />
|
||||
<span className="sr-only">Archive</span>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Archive</TooltipContent>
|
||||
</Tooltip>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button variant="ghost" size="icon" disabled={!mail}>
|
||||
<ArchiveX className="h-4 w-4" />
|
||||
<span className="sr-only">Move to junk</span>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Move to junk</TooltipContent>
|
||||
</Tooltip>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button variant="ghost" size="icon" disabled={!mail}>
|
||||
<Trash2 className="h-4 w-4" />
|
||||
<span className="sr-only">Move to trash</span>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Move to trash</TooltipContent>
|
||||
</Tooltip>
|
||||
<Separator orientation="vertical" className="mx-1 h-6" />
|
||||
<Tooltip>
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<TooltipTrigger asChild>
|
||||
<Button variant="ghost" size="icon" disabled={!mail}>
|
||||
<Clock className="h-4 w-4" />
|
||||
<span className="sr-only">Snooze</span>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="flex w-[535px] p-0">
|
||||
<div className="flex flex-col gap-2 border-r px-2 py-4">
|
||||
<div className="px-4 text-sm font-medium">Snooze until</div>
|
||||
<div className="grid min-w-[250px] gap-1">
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="justify-start font-normal"
|
||||
>
|
||||
Later today{" "}
|
||||
<span className="ml-auto text-muted-foreground">
|
||||
{format(addHours(today, 4), "E, h:m b")}
|
||||
</span>
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="justify-start font-normal"
|
||||
>
|
||||
Tomorrow
|
||||
<span className="ml-auto text-muted-foreground">
|
||||
{format(addDays(today, 1), "E, h:m b")}
|
||||
</span>
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="justify-start font-normal"
|
||||
>
|
||||
This weekend
|
||||
<span className="ml-auto text-muted-foreground">
|
||||
{format(nextSaturday(today), "E, h:m b")}
|
||||
</span>
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="justify-start font-normal"
|
||||
>
|
||||
Next week
|
||||
<span className="ml-auto text-muted-foreground">
|
||||
{format(addDays(today, 7), "E, h:m b")}
|
||||
</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-2">
|
||||
<Calendar />
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
<TooltipContent>Snooze</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div className="ml-auto flex items-center gap-2">
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button variant="ghost" size="icon" disabled={!mail}>
|
||||
<Reply className="h-4 w-4" />
|
||||
<span className="sr-only">Reply</span>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Reply</TooltipContent>
|
||||
</Tooltip>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button variant="ghost" size="icon" disabled={!mail}>
|
||||
<ReplyAll className="h-4 w-4" />
|
||||
<span className="sr-only">Reply all</span>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Reply all</TooltipContent>
|
||||
</Tooltip>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button variant="ghost" size="icon" disabled={!mail}>
|
||||
<Forward className="h-4 w-4" />
|
||||
<span className="sr-only">Forward</span>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Forward</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<Separator orientation="vertical" className="mx-2 h-6" />
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="ghost" size="icon" disabled={!mail}>
|
||||
<MoreVertical className="h-4 w-4" />
|
||||
<span className="sr-only">More</span>
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem>Mark as unread</DropdownMenuItem>
|
||||
<DropdownMenuItem>Star thread</DropdownMenuItem>
|
||||
<DropdownMenuItem>Add label</DropdownMenuItem>
|
||||
<DropdownMenuItem>Mute thread</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
<Separator />
|
||||
{mail ? (
|
||||
<div className="flex flex-1 flex-col">
|
||||
<div className="flex items-start p-4">
|
||||
<div className="flex items-start gap-4 text-sm">
|
||||
<Avatar>
|
||||
<AvatarImage alt={mail.name} />
|
||||
<AvatarFallback>
|
||||
{mail.name
|
||||
.split(" ")
|
||||
.map((chunk) => chunk[0])
|
||||
.join("")}
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
<div className="grid gap-1">
|
||||
<div className="font-semibold">{mail.name}</div>
|
||||
<div className="line-clamp-1 text-xs">{mail.subject}</div>
|
||||
<div className="line-clamp-1 text-xs">
|
||||
<span className="font-medium">Reply-To:</span> {mail.email}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{mail.date && (
|
||||
<div className="ml-auto text-xs text-muted-foreground">
|
||||
{format(new Date(mail.date), "PPpp")}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<Separator />
|
||||
<div className="flex-1 whitespace-pre-wrap p-4 text-sm">
|
||||
{mail.text}
|
||||
</div>
|
||||
<Separator className="mt-auto" />
|
||||
<div className="p-4">
|
||||
<form>
|
||||
<div className="grid gap-4">
|
||||
<Textarea
|
||||
className="p-4"
|
||||
placeholder={`Reply ${mail.name}...`}
|
||||
/>
|
||||
<div className="flex items-center">
|
||||
<Label
|
||||
htmlFor="mute"
|
||||
className="flex items-center gap-2 text-xs font-normal"
|
||||
>
|
||||
<Switch id="mute" aria-label="Mute thread" /> Mute this
|
||||
thread
|
||||
</Label>
|
||||
<Button
|
||||
onClick={(e) => e.preventDefault()}
|
||||
size="sm"
|
||||
className="ml-auto"
|
||||
>
|
||||
Send
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="p-8 text-center text-muted-foreground">
|
||||
No message selected
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
89
apps/www/app/examples/mail/components/mail-list.tsx
Normal file
89
apps/www/app/examples/mail/components/mail-list.tsx
Normal file
@@ -0,0 +1,89 @@
|
||||
import { ComponentProps } from "react"
|
||||
import formatDistanceToNow from "date-fns/formatDistanceToNow"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { Badge } from "@/registry/new-york/ui/badge"
|
||||
import { ScrollArea } from "@/registry/new-york/ui/scroll-area"
|
||||
import { Separator } from "@/registry/new-york/ui/separator"
|
||||
import { Mail } from "@/app/examples/mail/data"
|
||||
import { useMail } from "@/app/examples/mail/use-mail"
|
||||
|
||||
interface MailListProps {
|
||||
items: Mail[]
|
||||
}
|
||||
|
||||
export function MailList({ items }: MailListProps) {
|
||||
const [mail, setMail] = useMail()
|
||||
|
||||
return (
|
||||
<ScrollArea className="h-screen">
|
||||
<div className="flex flex-col gap-2 p-4 pt-0">
|
||||
{items.map((item) => (
|
||||
<button
|
||||
key={item.id}
|
||||
className={cn(
|
||||
"flex flex-col items-start gap-2 rounded-lg border p-3 text-left text-sm transition-all hover:bg-accent",
|
||||
mail.selected === item.id && "bg-muted"
|
||||
)}
|
||||
onClick={() =>
|
||||
setMail({
|
||||
...mail,
|
||||
selected: item.id,
|
||||
})
|
||||
}
|
||||
>
|
||||
<div className="flex w-full flex-col gap-1">
|
||||
<div className="flex items-center">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="font-semibold">{item.name}</div>
|
||||
{!item.read && (
|
||||
<span className="flex h-2 w-2 rounded-full bg-blue-600" />
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
className={cn(
|
||||
"ml-auto text-xs",
|
||||
mail.selected === item.id
|
||||
? "text-foreground"
|
||||
: "text-muted-foreground"
|
||||
)}
|
||||
>
|
||||
{formatDistanceToNow(new Date(item.date), {
|
||||
addSuffix: true,
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-xs font-medium">{item.subject}</div>
|
||||
</div>
|
||||
<div className="line-clamp-2 text-xs text-muted-foreground">
|
||||
{item.text.substring(0, 300)}
|
||||
</div>
|
||||
{item.labels.length ? (
|
||||
<div className="flex items-center gap-2">
|
||||
{item.labels.map((label) => (
|
||||
<Badge key={label} variant={getBadgeVariantFromLabel(label)}>
|
||||
{label}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
) : null}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</ScrollArea>
|
||||
)
|
||||
}
|
||||
|
||||
function getBadgeVariantFromLabel(
|
||||
label: string
|
||||
): ComponentProps<typeof Badge>["variant"] {
|
||||
if (["work"].includes(label.toLowerCase())) {
|
||||
return "default"
|
||||
}
|
||||
|
||||
if (["personal"].includes(label.toLowerCase())) {
|
||||
return "outline"
|
||||
}
|
||||
|
||||
return "secondary"
|
||||
}
|
||||
201
apps/www/app/examples/mail/components/mail.tsx
Normal file
201
apps/www/app/examples/mail/components/mail.tsx
Normal file
@@ -0,0 +1,201 @@
|
||||
"use client"
|
||||
import * as React from "react"
|
||||
import {
|
||||
AlertCircle,
|
||||
Archive,
|
||||
ArchiveX,
|
||||
File,
|
||||
Inbox,
|
||||
MessagesSquare,
|
||||
PenBox,
|
||||
Search,
|
||||
Send,
|
||||
ShoppingCart,
|
||||
Trash2,
|
||||
Users2,
|
||||
} from "lucide-react"
|
||||
|
||||
import { AccountSwitcher } from "@/app/examples/mail/components/account-switcher"
|
||||
import { MailDisplay } from "@/app/examples/mail/components/mail-display"
|
||||
import { MailList } from "@/app/examples/mail/components/mail-list"
|
||||
import { Nav } from "@/app/examples/mail/components/nav"
|
||||
import { Mail } from "@/app/examples/mail/data"
|
||||
import { useMail } from "@/app/examples/mail/use-mail"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { Separator } from "@/registry/new-york/ui/separator"
|
||||
import { Input } from "@/registry/new-york/ui/input"
|
||||
import {
|
||||
Tabs,
|
||||
TabsContent,
|
||||
TabsList,
|
||||
TabsTrigger,
|
||||
} from "@/registry/new-york/ui/tabs"
|
||||
import { TooltipProvider } from "@/registry/new-york/ui/tooltip"
|
||||
import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from "@/registry/new-york/ui/resizable"
|
||||
|
||||
interface MailProps {
|
||||
accounts: {
|
||||
label: string
|
||||
email: string
|
||||
icon: React.ReactNode
|
||||
}[]
|
||||
mails: Mail[]
|
||||
defaultLayout: number[] | undefined
|
||||
defaultCollapsed?: boolean
|
||||
navCollapsedSize: number
|
||||
}
|
||||
|
||||
export function Mail({
|
||||
accounts,
|
||||
mails,
|
||||
defaultLayout = [265, 440, 655],
|
||||
defaultCollapsed = false,
|
||||
navCollapsedSize,
|
||||
}: MailProps) {
|
||||
const [isCollapsed, setIsCollapsed] = React.useState(defaultCollapsed)
|
||||
const [mail] = useMail()
|
||||
|
||||
return (
|
||||
<TooltipProvider delayDuration={0}>
|
||||
<ResizablePanelGroup
|
||||
direction="horizontal"
|
||||
onLayout={(sizes: number[]) => {
|
||||
document.cookie = `react-resizable-panels:layout=${JSON.stringify(
|
||||
sizes
|
||||
)}`
|
||||
}}
|
||||
className="h-full max-h-[800px] items-stretch"
|
||||
>
|
||||
<ResizablePanel
|
||||
defaultSize={defaultLayout[0]}
|
||||
collapsedSize={navCollapsedSize}
|
||||
collapsible={true}
|
||||
minSize={15}
|
||||
maxSize={20}
|
||||
onCollapse={(collapsed) => {
|
||||
setIsCollapsed(collapsed)
|
||||
document.cookie = `react-resizable-panels:collapsed=${JSON.stringify(
|
||||
collapsed
|
||||
)}`
|
||||
}}
|
||||
className={cn(isCollapsed && "min-w-[50px] transition-all duration-300 ease-in-out")}
|
||||
>
|
||||
<div className={cn("flex h-[52px] items-center justify-center", isCollapsed ? 'h-[52px]': 'px-2')}>
|
||||
<AccountSwitcher isCollapsed={isCollapsed} accounts={accounts} />
|
||||
</div>
|
||||
<Separator />
|
||||
<Nav
|
||||
isCollapsed={isCollapsed}
|
||||
links={[
|
||||
{
|
||||
title: "Inbox",
|
||||
label: "128",
|
||||
icon: Inbox,
|
||||
variant: "default",
|
||||
},
|
||||
{
|
||||
title: "Drafts",
|
||||
label: "9",
|
||||
icon: File,
|
||||
variant: "ghost",
|
||||
},
|
||||
{
|
||||
title: "Sent",
|
||||
label: "",
|
||||
icon: Send,
|
||||
variant: "ghost",
|
||||
},
|
||||
{
|
||||
title: "Junk",
|
||||
label: "23",
|
||||
icon: ArchiveX,
|
||||
variant: "ghost",
|
||||
},
|
||||
{
|
||||
title: "Trash",
|
||||
label: "",
|
||||
icon: Trash2,
|
||||
variant: "ghost",
|
||||
},
|
||||
{
|
||||
title: "Archive",
|
||||
label: "",
|
||||
icon: Archive,
|
||||
variant: "ghost",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<Separator />
|
||||
<Nav
|
||||
isCollapsed={isCollapsed}
|
||||
links={[
|
||||
{
|
||||
title: "Social",
|
||||
label: "972",
|
||||
icon: Users2,
|
||||
variant: "ghost",
|
||||
},
|
||||
{
|
||||
title: "Updates",
|
||||
label: "342",
|
||||
icon: AlertCircle,
|
||||
variant: "ghost",
|
||||
},
|
||||
{
|
||||
title: "Forums",
|
||||
label: "128",
|
||||
icon: MessagesSquare,
|
||||
variant: "ghost",
|
||||
},
|
||||
{
|
||||
title: "Shopping",
|
||||
label: "8",
|
||||
icon: ShoppingCart,
|
||||
variant: "ghost",
|
||||
},
|
||||
{
|
||||
title: "Promotions",
|
||||
label: "21",
|
||||
icon: Archive,
|
||||
variant: "ghost",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</ResizablePanel>
|
||||
<ResizableHandle withHandle />
|
||||
<ResizablePanel defaultSize={defaultLayout[1]} minSize={30}>
|
||||
<Tabs defaultValue="all">
|
||||
<div className="flex items-center px-4 py-2">
|
||||
<h1 className="text-xl font-bold">Inbox</h1>
|
||||
<TabsList className="ml-auto">
|
||||
<TabsTrigger value="all" className="text-zinc-600 dark:text-zinc-200">All mail</TabsTrigger>
|
||||
<TabsTrigger value="unread" className="text-zinc-600 dark:text-zinc-200">Unread</TabsTrigger>
|
||||
</TabsList>
|
||||
</div>
|
||||
<Separator />
|
||||
<div className="bg-background/95 p-4 backdrop-blur supports-[backdrop-filter]:bg-background/60">
|
||||
<form>
|
||||
<div className="relative">
|
||||
<Search className="absolute left-2 top-2.5 h-4 w-4 text-muted-foreground" />
|
||||
<Input placeholder="Search" className="pl-8" />
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<TabsContent value="all" className="m-0">
|
||||
<MailList items={mails} />
|
||||
</TabsContent>
|
||||
<TabsContent value="unread" className="m-0">
|
||||
<MailList items={mails.filter((item) => !item.read)} />
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</ResizablePanel>
|
||||
<ResizableHandle withHandle />
|
||||
<ResizablePanel defaultSize={defaultLayout[2]}>
|
||||
<MailDisplay
|
||||
mail={mails.find((item) => item.id === mail.selected) || null}
|
||||
/>
|
||||
</ResizablePanel>
|
||||
</ResizablePanelGroup>
|
||||
</TooltipProvider>
|
||||
)
|
||||
}
|
||||
87
apps/www/app/examples/mail/components/nav.tsx
Normal file
87
apps/www/app/examples/mail/components/nav.tsx
Normal file
@@ -0,0 +1,87 @@
|
||||
"use client"
|
||||
|
||||
import Link from "next/link"
|
||||
import { LucideIcon } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { buttonVariants } from "@/registry/default/ui/button"
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipTrigger,
|
||||
} from "@/registry/new-york/ui/tooltip"
|
||||
|
||||
interface NavProps {
|
||||
isCollapsed: boolean
|
||||
links: {
|
||||
title: string
|
||||
label?: string
|
||||
icon: LucideIcon
|
||||
variant: "default" | "ghost"
|
||||
}[]
|
||||
}
|
||||
|
||||
export function Nav({ links, isCollapsed }: NavProps) {
|
||||
return (
|
||||
<div
|
||||
data-collapsed={isCollapsed}
|
||||
className="group flex flex-col gap-4 py-2 data-[collapsed=true]:py-2"
|
||||
>
|
||||
<nav className="grid gap-1 px-2 group-[[data-collapsed=true]]:justify-center group-[[data-collapsed=true]]:px-2">
|
||||
{links.map((link, index) =>
|
||||
isCollapsed ? (
|
||||
<Tooltip key={index} delayDuration={0}>
|
||||
<TooltipTrigger asChild>
|
||||
<Link
|
||||
href="#"
|
||||
className={cn(
|
||||
buttonVariants({ variant: link.variant, size: "icon" }),
|
||||
"h-9 w-9",
|
||||
link.variant === "default" &&
|
||||
"dark:bg-muted dark:text-muted-foreground dark:hover:bg-muted dark:hover:text-white"
|
||||
)}
|
||||
>
|
||||
<link.icon className="h-4 w-4" />
|
||||
<span className="sr-only">{link.title}</span>
|
||||
</Link>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="right" className="flex items-center gap-4">
|
||||
{link.title}
|
||||
{link.label && (
|
||||
<span className="ml-auto text-muted-foreground">
|
||||
{link.label}
|
||||
</span>
|
||||
)}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<Link
|
||||
key={index}
|
||||
href="#"
|
||||
className={cn(
|
||||
buttonVariants({ variant: link.variant, size: "sm" }),
|
||||
link.variant === "default" &&
|
||||
"dark:bg-muted dark:text-white dark:hover:bg-muted dark:hover:text-white",
|
||||
"justify-start"
|
||||
)}
|
||||
>
|
||||
<link.icon className="mr-2 h-4 w-4" />
|
||||
{link.title}
|
||||
{link.label && (
|
||||
<span
|
||||
className={cn(
|
||||
"ml-auto",
|
||||
link.variant === "default" &&
|
||||
"text-background dark:text-white"
|
||||
)}
|
||||
>
|
||||
{link.label}
|
||||
</span>
|
||||
)}
|
||||
</Link>
|
||||
)
|
||||
)}
|
||||
</nav>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
300
apps/www/app/examples/mail/data.tsx
Normal file
300
apps/www/app/examples/mail/data.tsx
Normal file
@@ -0,0 +1,300 @@
|
||||
export const mails = [
|
||||
{
|
||||
id: "6c84fb90-12c4-11e1-840d-7b25c5ee775a",
|
||||
name: "William Smith",
|
||||
email: "williamsmith@example.com",
|
||||
subject: "Meeting Tomorrow",
|
||||
text: "Hi, let's have a meeting tomorrow to discuss the project. I've been reviewing the project details and have some ideas I'd like to share. It's crucial that we align on our next steps to ensure the project's success.\n\nPlease come prepared with any questions or insights you may have. Looking forward to our meeting!\n\nBest regards, William",
|
||||
date: "2023-10-22T09:00:00",
|
||||
read: true,
|
||||
labels: ["meeting", "work", "important"],
|
||||
},
|
||||
{
|
||||
id: "110e8400-e29b-11d4-a716-446655440000",
|
||||
name: "Alice Smith",
|
||||
email: "alicesmith@example.com",
|
||||
subject: "Re: Project Update",
|
||||
text: "Thank you for the project update. It looks great! I've gone through the report, and the progress is impressive. The team has done a fantastic job, and I appreciate the hard work everyone has put in.\n\nI have a few minor suggestions that I'll include in the attached document.\n\nLet's discuss these during our next meeting. Keep up the excellent work!\n\nBest regards, Alice",
|
||||
date: "2023-10-22T10:30:00",
|
||||
read: true,
|
||||
labels: ["work", "important"],
|
||||
},
|
||||
{
|
||||
id: "3e7c3f6d-bdf5-46ae-8d90-171300f27ae2",
|
||||
name: "Bob Johnson",
|
||||
email: "bobjohnson@example.com",
|
||||
subject: "Weekend Plans",
|
||||
text: "Any plans for the weekend? I was thinking of going hiking in the nearby mountains. It's been a while since we had some outdoor fun.\n\nIf you're interested, let me know, and we can plan the details. It'll be a great way to unwind and enjoy nature.\n\nLooking forward to your response!\n\nBest, Bob",
|
||||
date: "2023-04-10T11:45:00",
|
||||
read: true,
|
||||
labels: ["personal"],
|
||||
},
|
||||
{
|
||||
id: "61c35085-72d7-42b4-8d62-738f700d4b92",
|
||||
name: "Emily Davis",
|
||||
email: "emilydavis@example.com",
|
||||
subject: "Re: Question about Budget",
|
||||
text: "I have a question about the budget for the upcoming project. It seems like there's a discrepancy in the allocation of resources.\n\nI've reviewed the budget report and identified a few areas where we might be able to optimize our spending without compromising the project's quality.\n\nI've attached a detailed analysis for your reference. Let's discuss this further in our next meeting.\n\nThanks, Emily",
|
||||
date: "2023-03-25T13:15:00",
|
||||
read: false,
|
||||
labels: ["work", "budget"],
|
||||
},
|
||||
{
|
||||
id: "8f7b5db9-d935-4e42-8e05-1f1d0a3dfb97",
|
||||
name: "Michael Wilson",
|
||||
email: "michaelwilson@example.com",
|
||||
subject: "Important Announcement",
|
||||
text: "I have an important announcement to make during our team meeting. It pertains to a strategic shift in our approach to the upcoming product launch. We've received valuable feedback from our beta testers, and I believe it's time to make some adjustments to better meet our customers' needs.\n\nThis change is crucial to our success, and I look forward to discussing it with the team. Please be prepared to share your insights during the meeting.\n\nRegards, Michael",
|
||||
date: "2023-03-10T15:00:00",
|
||||
read: false,
|
||||
labels: ["meeting", "work", "important"],
|
||||
},
|
||||
{
|
||||
id: "1f0f2c02-e299-40de-9b1d-86ef9e42126b",
|
||||
name: "Sarah Brown",
|
||||
email: "sarahbrown@example.com",
|
||||
subject: "Re: Feedback on Proposal",
|
||||
text: "Thank you for your feedback on the proposal. It looks great! I'm pleased to hear that you found it promising. The team worked diligently to address all the key points you raised, and I believe we now have a strong foundation for the project.\n\nI've attached the revised proposal for your review.\n\nPlease let me know if you have any further comments or suggestions. Looking forward to your response.\n\nBest regards, Sarah",
|
||||
date: "2023-02-15T16:30:00",
|
||||
read: true,
|
||||
labels: ["work"],
|
||||
},
|
||||
{
|
||||
id: "17c0a96d-4415-42b1-8b4f-764efab57f66",
|
||||
name: "David Lee",
|
||||
email: "davidlee@example.com",
|
||||
subject: "New Project Idea",
|
||||
text: "I have an exciting new project idea to discuss with you. It involves expanding our services to target a niche market that has shown considerable growth in recent months.\n\nI've prepared a detailed proposal outlining the potential benefits and the strategy for execution.\n\nThis project has the potential to significantly impact our business positively. Let's set up a meeting to dive into the details and determine if it aligns with our current goals.\n\nBest regards, David",
|
||||
date: "2023-01-28T17:45:00",
|
||||
read: false,
|
||||
labels: ["meeting", "work", "important"],
|
||||
},
|
||||
{
|
||||
id: "2f0130cb-39fc-44c4-bb3c-0a4337edaaab",
|
||||
name: "Olivia Wilson",
|
||||
email: "oliviawilson@example.com",
|
||||
subject: "Vacation Plans",
|
||||
text: "Let's plan our vacation for next month. What do you think? I've been thinking of visiting a tropical paradise, and I've put together some destination options.\n\nI believe it's time for us to unwind and recharge. Please take a look at the options and let me know your preferences.\n\nWe can start making arrangements to ensure a smooth and enjoyable trip.\n\nExcited to hear your thoughts! Olivia",
|
||||
date: "2022-12-20T18:30:00",
|
||||
read: true,
|
||||
labels: ["personal"],
|
||||
},
|
||||
{
|
||||
id: "de305d54-75b4-431b-adb2-eb6b9e546014",
|
||||
name: "James Martin",
|
||||
email: "jamesmartin@example.com",
|
||||
subject: "Re: Conference Registration",
|
||||
text: "I've completed the registration for the conference next month. The event promises to be a great networking opportunity, and I'm looking forward to attending the various sessions and connecting with industry experts.\n\nI've also attached the conference schedule for your reference.\n\nIf there are any specific topics or sessions you'd like me to explore, please let me know. It's an exciting event, and I'll make the most of it.\n\nBest regards, James",
|
||||
date: "2022-11-30T19:15:00",
|
||||
read: true,
|
||||
labels: ["work", "conference"],
|
||||
},
|
||||
{
|
||||
id: "7dd90c63-00f6-40f3-bd87-5060a24e8ee7",
|
||||
name: "Sophia White",
|
||||
email: "sophiawhite@example.com",
|
||||
subject: "Team Dinner",
|
||||
text: "Let's have a team dinner next week to celebrate our success. We've achieved some significant milestones, and it's time to acknowledge our hard work and dedication.\n\nI've made reservations at a lovely restaurant, and I'm sure it'll be an enjoyable evening.\n\nPlease confirm your availability and any dietary preferences. Looking forward to a fun and memorable dinner with the team!\n\nBest, Sophia",
|
||||
date: "2022-11-05T20:30:00",
|
||||
read: false,
|
||||
labels: ["meeting", "work"],
|
||||
},
|
||||
{
|
||||
id: "99a88f78-3eb4-4d87-87b7-7b15a49a0a05",
|
||||
name: "Daniel Johnson",
|
||||
email: "danieljohnson@example.com",
|
||||
subject: "Feedback Request",
|
||||
text: "I'd like your feedback on the latest project deliverables. We've made significant progress, and I value your input to ensure we're on the right track.\n\nI've attached the deliverables for your review, and I'm particularly interested in any areas where you think we can further enhance the quality or efficiency.\n\nYour feedback is invaluable, and I appreciate your time and expertise. Let's work together to make this project a success.\n\nRegards, Daniel",
|
||||
date: "2022-10-22T09:30:00",
|
||||
read: false,
|
||||
labels: ["work"],
|
||||
},
|
||||
{
|
||||
id: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
|
||||
name: "Ava Taylor",
|
||||
email: "avataylor@example.com",
|
||||
subject: "Re: Meeting Agenda",
|
||||
text: "Here's the agenda for our meeting next week. I've included all the topics we need to cover, as well as time allocations for each.\n\nIf you have any additional items to discuss or any specific points to address, please let me know, and we can integrate them into the agenda.\n\nIt's essential that our meeting is productive and addresses all relevant matters.\n\nLooking forward to our meeting! Ava",
|
||||
date: "2022-10-10T10:45:00",
|
||||
read: true,
|
||||
labels: ["meeting", "work"],
|
||||
},
|
||||
{
|
||||
id: "c1a0ecb4-2540-49c5-86f8-21e5ce79e4e6",
|
||||
name: "William Anderson",
|
||||
email: "williamanderson@example.com",
|
||||
subject: "Product Launch Update",
|
||||
text: "The product launch is on track. I'll provide an update during our call. We've made substantial progress in the development and marketing of our new product.\n\nI'm excited to share the latest updates with you during our upcoming call. It's crucial that we coordinate our efforts to ensure a successful launch. Please come prepared with any questions or insights you may have.\n\nLet's make this product launch a resounding success!\n\nBest regards, William",
|
||||
date: "2022-09-20T12:00:00",
|
||||
read: false,
|
||||
labels: ["meeting", "work", "important"],
|
||||
},
|
||||
{
|
||||
id: "ba54eefd-4097-4949-99f2-2a9ae4d1a836",
|
||||
name: "Mia Harris",
|
||||
email: "miaharris@example.com",
|
||||
subject: "Re: Travel Itinerary",
|
||||
text: "I've received the travel itinerary. It looks great! Thank you for your prompt assistance in arranging the details. I've reviewed the schedule and the accommodations, and everything seems to be in order. I'm looking forward to the trip, and I'm confident it'll be a smooth and enjoyable experience.\n\nIf there are any specific activities or attractions you recommend at our destination, please feel free to share your suggestions.\n\nExcited for the trip! Mia",
|
||||
date: "2022-09-10T13:15:00",
|
||||
read: true,
|
||||
labels: ["personal", "travel"],
|
||||
},
|
||||
{
|
||||
id: "df09b6ed-28bd-4e0c-85a9-9320ec5179aa",
|
||||
name: "Ethan Clark",
|
||||
email: "ethanclark@example.com",
|
||||
subject: "Team Building Event",
|
||||
text: "Let's plan a team-building event for our department. Team cohesion and morale are vital to our success, and I believe a well-organized team-building event can be incredibly beneficial. I've done some research and have a few ideas for fun and engaging activities.\n\nPlease let me know your thoughts and availability. We want this event to be both enjoyable and productive.\n\nTogether, we'll strengthen our team and boost our performance.\n\nRegards, Ethan",
|
||||
date: "2022-08-25T15:30:00",
|
||||
read: false,
|
||||
labels: ["meeting", "work"],
|
||||
},
|
||||
{
|
||||
id: "d67c1842-7f8b-4b4b-9be1-1b3b1ab4611d",
|
||||
name: "Chloe Hall",
|
||||
email: "chloehall@example.com",
|
||||
subject: "Re: Budget Approval",
|
||||
text: "The budget has been approved. We can proceed with the project. I'm delighted to inform you that our budget proposal has received the green light from the finance department. This is a significant milestone, and it means we can move forward with the project as planned.\n\nI've attached the finalized budget for your reference. Let's ensure that we stay on track and deliver the project on time and within budget.\n\nIt's an exciting time for us! Chloe",
|
||||
date: "2022-08-10T16:45:00",
|
||||
read: true,
|
||||
labels: ["work", "budget"],
|
||||
},
|
||||
{
|
||||
id: "6c9a7f94-8329-4d70-95d3-51f68c186ae1",
|
||||
name: "Samuel Turner",
|
||||
email: "samuelturner@example.com",
|
||||
subject: "Weekend Hike",
|
||||
text: "Who's up for a weekend hike in the mountains? I've been craving some outdoor adventure, and a hike in the mountains sounds like the perfect escape. If you're up for the challenge, we can explore some scenic trails and enjoy the beauty of nature.\n\nI've done some research and have a few routes in mind.\n\nLet me know if you're interested, and we can plan the details.\n\nIt's sure to be a memorable experience! Samuel",
|
||||
date: "2022-07-28T17:30:00",
|
||||
read: false,
|
||||
labels: ["personal"],
|
||||
},
|
||||
]
|
||||
|
||||
export type Mail = (typeof mails)[number]
|
||||
|
||||
export const accounts = [
|
||||
{
|
||||
label: "Alicia Koch",
|
||||
email: "alicia@example.com",
|
||||
icon: (
|
||||
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<title>Vercel</title>
|
||||
<path d="M24 22.525H0l12-21.05 12 21.05z" fill="currentColor" />
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
{
|
||||
label: "Alicia Koch",
|
||||
email: "alicia@gmail.com",
|
||||
icon: (
|
||||
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<title>Gmail</title>
|
||||
<path
|
||||
d="M24 5.457v13.909c0 .904-.732 1.636-1.636 1.636h-3.819V11.73L12 16.64l-6.545-4.91v9.273H1.636A1.636 1.636 0 0 1 0 19.366V5.457c0-2.023 2.309-3.178 3.927-1.964L5.455 4.64 12 9.548l6.545-4.91 1.528-1.145C21.69 2.28 24 3.434 24 5.457z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
{
|
||||
label: "Alicia Koch",
|
||||
email: "alicia@me.com",
|
||||
icon: (
|
||||
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<title>iCloud</title>
|
||||
<path
|
||||
d="M13.762 4.29a6.51 6.51 0 0 0-5.669 3.332 3.571 3.571 0 0 0-1.558-.36 3.571 3.571 0 0 0-3.516 3A4.918 4.918 0 0 0 0 14.796a4.918 4.918 0 0 0 4.92 4.914 4.93 4.93 0 0 0 .617-.045h14.42c2.305-.272 4.041-2.258 4.043-4.589v-.009a4.594 4.594 0 0 0-3.727-4.508 6.51 6.51 0 0 0-6.511-6.27z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
]
|
||||
|
||||
export type Account = (typeof accounts)[number]
|
||||
|
||||
export const contacts = [
|
||||
{
|
||||
name: "Emma Johnson",
|
||||
email: "emma.johnson@example.com",
|
||||
},
|
||||
{
|
||||
name: "Liam Wilson",
|
||||
email: "liam.wilson@example.com",
|
||||
},
|
||||
{
|
||||
name: "Olivia Davis",
|
||||
email: "olivia.davis@example.com",
|
||||
},
|
||||
{
|
||||
name: "Noah Martinez",
|
||||
email: "noah.martinez@example.com",
|
||||
},
|
||||
{
|
||||
name: "Ava Taylor",
|
||||
email: "ava.taylor@example.com",
|
||||
},
|
||||
{
|
||||
name: "Lucas Brown",
|
||||
email: "lucas.brown@example.com",
|
||||
},
|
||||
{
|
||||
name: "Sophia Smith",
|
||||
email: "sophia.smith@example.com",
|
||||
},
|
||||
{
|
||||
name: "Ethan Wilson",
|
||||
email: "ethan.wilson@example.com",
|
||||
},
|
||||
{
|
||||
name: "Isabella Jackson",
|
||||
email: "isabella.jackson@example.com",
|
||||
},
|
||||
{
|
||||
name: "Mia Clark",
|
||||
email: "mia.clark@example.com",
|
||||
},
|
||||
{
|
||||
name: "Mason Lee",
|
||||
email: "mason.lee@example.com",
|
||||
},
|
||||
{
|
||||
name: "Layla Harris",
|
||||
email: "layla.harris@example.com",
|
||||
},
|
||||
{
|
||||
name: "William Anderson",
|
||||
email: "william.anderson@example.com",
|
||||
},
|
||||
{
|
||||
name: "Ella White",
|
||||
email: "ella.white@example.com",
|
||||
},
|
||||
{
|
||||
name: "James Thomas",
|
||||
email: "james.thomas@example.com",
|
||||
},
|
||||
{
|
||||
name: "Harper Lewis",
|
||||
email: "harper.lewis@example.com",
|
||||
},
|
||||
{
|
||||
name: "Benjamin Moore",
|
||||
email: "benjamin.moore@example.com",
|
||||
},
|
||||
{
|
||||
name: "Aria Hall",
|
||||
email: "aria.hall@example.com",
|
||||
},
|
||||
{
|
||||
name: "Henry Turner",
|
||||
email: "henry.turner@example.com",
|
||||
},
|
||||
{
|
||||
name: "Scarlett Adams",
|
||||
email: "scarlett.adams@example.com",
|
||||
},
|
||||
]
|
||||
|
||||
export type Contact = (typeof contacts)[number]
|
||||
43
apps/www/app/examples/mail/page.tsx
Normal file
43
apps/www/app/examples/mail/page.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import { cookies } from "next/headers"
|
||||
import Image from "next/image"
|
||||
|
||||
import { Mail } from "@/app/examples/mail/components/mail"
|
||||
import { accounts, mails } from "@/app/examples/mail/data"
|
||||
|
||||
export default function MailPage() {
|
||||
const layout = cookies().get("react-resizable-panels:layout")
|
||||
const collapsed = cookies().get("react-resizable-panels:collapsed")
|
||||
|
||||
const defaultLayout = layout ? JSON.parse(layout.value) : undefined
|
||||
const defaultCollapsed = collapsed ? JSON.parse(collapsed.value) : undefined
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="md:hidden">
|
||||
<Image
|
||||
src="/examples/mail-dark.png"
|
||||
width={1280}
|
||||
height={727}
|
||||
alt="Mail"
|
||||
className="hidden dark:block"
|
||||
/>
|
||||
<Image
|
||||
src="/examples/mail-light.png"
|
||||
width={1280}
|
||||
height={727}
|
||||
alt="Mail"
|
||||
className="block dark:hidden"
|
||||
/>
|
||||
</div>
|
||||
<div className="hidden flex-col md:flex">
|
||||
<Mail
|
||||
accounts={accounts}
|
||||
mails={mails}
|
||||
defaultLayout={defaultLayout}
|
||||
defaultCollapsed={defaultCollapsed}
|
||||
navCollapsedSize={4}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
15
apps/www/app/examples/mail/use-mail.ts
Normal file
15
apps/www/app/examples/mail/use-mail.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { atom, useAtom } from "jotai"
|
||||
|
||||
import { Mail, mails } from "@/app/examples/mail/data"
|
||||
|
||||
type Config = {
|
||||
selected: Mail["id"] | null
|
||||
}
|
||||
|
||||
const configAtom = atom<Config>({
|
||||
selected: mails[0].id,
|
||||
})
|
||||
|
||||
export function useMail() {
|
||||
return useAtom(configAtom)
|
||||
}
|
||||
@@ -190,7 +190,7 @@ export function Menu() {
|
||||
<MenubarRadioItem value="Luis">Luis</MenubarRadioItem>
|
||||
</MenubarRadioGroup>
|
||||
<MenubarSeparator />
|
||||
<MenubarItem inset>Manage Famliy...</MenubarItem>
|
||||
<MenubarItem inset>Manage Family...</MenubarItem>
|
||||
<MenubarSeparator />
|
||||
<MenubarItem inset>Add Account...</MenubarItem>
|
||||
</MenubarContent>
|
||||
|
||||
@@ -35,7 +35,7 @@ export function PodcastEmptyPlaceholder() {
|
||||
You have not added any podcasts. Add one below.
|
||||
</p>
|
||||
<Dialog>
|
||||
<DialogTrigger>
|
||||
<DialogTrigger asChild>
|
||||
<Button size="sm" className="relative">
|
||||
Add Podcast
|
||||
</Button>
|
||||
|
||||
@@ -95,7 +95,7 @@ export function PresetActions() {
|
||||
<AlertDialog open={showDeleteDialog} onOpenChange={setShowDeleteDialog}>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>Are you sure absolutely sure?</AlertDialogTitle>
|
||||
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
This action cannot be undone. This preset will no longer be
|
||||
accessible by you or others you've shared it with.
|
||||
|
||||
@@ -36,7 +36,7 @@ export const models: Model<ModelType>[] = [
|
||||
strengths: "Moderate classification, semantic search",
|
||||
},
|
||||
{
|
||||
id: " be638fb1-973b-4471-a49c-290325085802",
|
||||
id: "be638fb1-973b-4471-a49c-290325085802",
|
||||
name: "text-ada-001",
|
||||
description:
|
||||
"Capable of very simple tasks, usually the fastest model in the GPT-3 series, and lowest cost.",
|
||||
|
||||
@@ -15,7 +15,10 @@ export const columns: ColumnDef<Task>[] = [
|
||||
id: "select",
|
||||
header: ({ table }) => (
|
||||
<Checkbox
|
||||
checked={table.getIsAllPageRowsSelected()}
|
||||
checked={
|
||||
table.getIsAllPageRowsSelected() ||
|
||||
(table.getIsSomePageRowsSelected() && "indeterminate")
|
||||
}
|
||||
onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
|
||||
aria-label="Select all"
|
||||
className="translate-y-[2px]"
|
||||
|
||||
@@ -21,7 +21,7 @@ import {
|
||||
} from "@/registry/new-york/ui/popover"
|
||||
import { Separator } from "@/registry/new-york/ui/separator"
|
||||
|
||||
interface DataTableFacetedFilter<TData, TValue> {
|
||||
interface DataTableFacetedFilterProps<TData, TValue> {
|
||||
column?: Column<TData, TValue>
|
||||
title?: string
|
||||
options: {
|
||||
@@ -35,7 +35,7 @@ export function DataTableFacetedFilter<TData, TValue>({
|
||||
column,
|
||||
title,
|
||||
options,
|
||||
}: DataTableFacetedFilter<TData, TValue>) {
|
||||
}: DataTableFacetedFilterProps<TData, TValue>) {
|
||||
const facets = column?.getFacetedUniqueValues()
|
||||
const selectedValues = new Set(column?.getFilterValue() as string[])
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ export function DataTable<TData, TValue>({
|
||||
<TableRow key={headerGroup.id}>
|
||||
{headerGroup.headers.map((header) => {
|
||||
return (
|
||||
<TableHead key={header.id}>
|
||||
<TableHead key={header.id} colSpan={header.colSpan}>
|
||||
{header.isPlaceholder
|
||||
? null
|
||||
: flexRender(
|
||||
|
||||
@@ -5,7 +5,7 @@ import { faker } from "@faker-js/faker"
|
||||
import { labels, priorities, statuses } from "./data"
|
||||
|
||||
const tasks = Array.from({ length: 100 }, () => ({
|
||||
id: `TASK-${faker.datatype.number({ min: 1000, max: 9999 })}`,
|
||||
id: `TASK-${faker.number.int({ min: 1000, max: 9999 })}`,
|
||||
title: faker.hacker.phrase().replace(/^./, (letter) => letter.toUpperCase()),
|
||||
status: faker.helpers.arrayElement(statuses).value,
|
||||
label: faker.helpers.arrayElement(labels).value,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import "@/styles/globals.css"
|
||||
import { Metadata } from "next"
|
||||
import { Metadata, Viewport } from "next"
|
||||
|
||||
import { siteConfig } from "@/config/site"
|
||||
import { fontSans } from "@/lib/fonts"
|
||||
@@ -9,7 +9,9 @@ import { ThemeProvider } from "@/components/providers"
|
||||
import { SiteFooter } from "@/components/site-footer"
|
||||
import { SiteHeader } from "@/components/site-header"
|
||||
import { TailwindIndicator } from "@/components/tailwind-indicator"
|
||||
import { ThemeSwitcher } from "@/components/theme-switcher"
|
||||
import { Toaster as DefaultToaster } from "@/registry/default/ui/toaster"
|
||||
import { Toaster as NewYorkSonner } from "@/registry/new-york/ui/sonner"
|
||||
import { Toaster as NewYorkToaster } from "@/registry/new-york/ui/toaster"
|
||||
|
||||
export const metadata: Metadata = {
|
||||
@@ -17,6 +19,7 @@ export const metadata: Metadata = {
|
||||
default: siteConfig.name,
|
||||
template: `%s - ${siteConfig.name}`,
|
||||
},
|
||||
metadataBase: new URL(siteConfig.url),
|
||||
description: siteConfig.description,
|
||||
keywords: [
|
||||
"Next.js",
|
||||
@@ -32,10 +35,6 @@ export const metadata: Metadata = {
|
||||
},
|
||||
],
|
||||
creator: "shadcn",
|
||||
themeColor: [
|
||||
{ media: "(prefers-color-scheme: light)", color: "white" },
|
||||
{ media: "(prefers-color-scheme: dark)", color: "black" },
|
||||
],
|
||||
openGraph: {
|
||||
type: "website",
|
||||
locale: "en_US",
|
||||
@@ -67,6 +66,13 @@ export const metadata: Metadata = {
|
||||
manifest: `${siteConfig.url}/site.webmanifest`,
|
||||
}
|
||||
|
||||
export const viewport: Viewport = {
|
||||
themeColor: [
|
||||
{ media: "(prefers-color-scheme: light)", color: "white" },
|
||||
{ media: "(prefers-color-scheme: dark)", color: "black" },
|
||||
],
|
||||
}
|
||||
|
||||
interface RootLayoutProps {
|
||||
children: React.ReactNode
|
||||
}
|
||||
@@ -79,20 +85,29 @@ export default function RootLayout({ children }: RootLayoutProps) {
|
||||
<body
|
||||
className={cn(
|
||||
"min-h-screen bg-background font-sans antialiased",
|
||||
fontSans.variable
|
||||
fontSans.className
|
||||
)}
|
||||
>
|
||||
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
|
||||
<div className="relative flex min-h-screen flex-col">
|
||||
<SiteHeader />
|
||||
<div className="flex-1">{children}</div>
|
||||
<SiteFooter />
|
||||
<ThemeProvider
|
||||
attribute="class"
|
||||
defaultTheme="system"
|
||||
enableSystem
|
||||
disableTransitionOnChange
|
||||
>
|
||||
<div vaul-drawer-wrapper="">
|
||||
<div className="relative flex min-h-screen flex-col bg-background">
|
||||
<SiteHeader />
|
||||
<main className="flex-1">{children}</main>
|
||||
<SiteFooter />
|
||||
</div>
|
||||
</div>
|
||||
<TailwindIndicator />
|
||||
<ThemeSwitcher />
|
||||
<Analytics />
|
||||
<NewYorkToaster />
|
||||
<DefaultToaster />
|
||||
<NewYorkSonner />
|
||||
</ThemeProvider>
|
||||
<Analytics />
|
||||
<NewYorkToaster />
|
||||
<DefaultToaster />
|
||||
</body>
|
||||
</html>
|
||||
</>
|
||||
|
||||
@@ -1,41 +1,31 @@
|
||||
import Image from "next/image"
|
||||
import Link from "next/link"
|
||||
import { ArrowRightIcon } from "@radix-ui/react-icons"
|
||||
|
||||
import { siteConfig } from "@/config/site"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { Announcement } from "@/components/announcement"
|
||||
import { ExamplesNav } from "@/components/examples-nav"
|
||||
import { Icons } from "@/components/icons"
|
||||
import {
|
||||
PageActions,
|
||||
PageHeader,
|
||||
PageHeaderDescription,
|
||||
PageHeaderHeading,
|
||||
} from "@/components/page-header"
|
||||
import { buttonVariants } from "@/registry/new-york/ui/button"
|
||||
import { Separator } from "@/registry/new-york/ui/separator"
|
||||
import DashboardPage from "@/app/examples/dashboard/page"
|
||||
import MailPage from "@/app/examples/mail/page"
|
||||
|
||||
export default function IndexPage() {
|
||||
return (
|
||||
<div className="container relative">
|
||||
<PageHeader className="pb-8">
|
||||
<Link
|
||||
href="/docs/changelog"
|
||||
className="inline-flex items-center rounded-lg bg-muted px-3 py-1 text-sm font-medium"
|
||||
>
|
||||
🎉 <Separator className="mx-2 h-4" orientation="vertical" />{" "}
|
||||
<span className="sm:hidden">Style, a new CLI and more.</span>
|
||||
<span className="hidden sm:inline">
|
||||
Introducing Style, a new CLI and more.
|
||||
</span>
|
||||
<ArrowRightIcon className="ml-1 h-4 w-4" />
|
||||
</Link>
|
||||
<PageHeaderHeading>Build your component library.</PageHeaderHeading>
|
||||
<PageHeader>
|
||||
<Announcement />
|
||||
<PageHeaderHeading>Build your component library</PageHeaderHeading>
|
||||
<PageHeaderDescription>
|
||||
Beautifully designed components that you can copy and paste into your
|
||||
apps. Accessible. Customizable. Open Source.
|
||||
</PageHeaderDescription>
|
||||
<div className="flex w-full items-center space-x-4 pb-8 pt-4 md:pb-10">
|
||||
<PageActions>
|
||||
<Link href="/docs" className={cn(buttonVariants())}>
|
||||
Get Started
|
||||
</Link>
|
||||
@@ -48,28 +38,28 @@ export default function IndexPage() {
|
||||
<Icons.gitHub className="mr-2 h-4 w-4" />
|
||||
GitHub
|
||||
</Link>
|
||||
</div>
|
||||
</PageActions>
|
||||
</PageHeader>
|
||||
<ExamplesNav className="[&>a:first-child]:text-primary" />
|
||||
<section className="space-y-8 overflow-hidden rounded-lg border-2 border-primary dark:border-muted md:hidden">
|
||||
<section className="overflow-hidden rounded-lg border bg-background shadow-md md:hidden md:shadow-xl">
|
||||
<Image
|
||||
src="/examples/dashboard-light.png"
|
||||
src="/examples/mail-dark.png"
|
||||
width={1280}
|
||||
height={866}
|
||||
alt="Dashboard"
|
||||
className="block dark:hidden"
|
||||
height={727}
|
||||
alt="Mail"
|
||||
className="hidden dark:block"
|
||||
/>
|
||||
<Image
|
||||
src="/examples/dashboard-dark.png"
|
||||
src="/examples/mail-light.png"
|
||||
width={1280}
|
||||
height={866}
|
||||
alt="Dashboard"
|
||||
className="hidden dark:block"
|
||||
height={727}
|
||||
alt="Mail"
|
||||
className="block dark:hidden"
|
||||
/>
|
||||
</section>
|
||||
<section className="hidden md:block">
|
||||
<div className="overflow-hidden rounded-lg border bg-background shadow">
|
||||
<DashboardPage />
|
||||
<div className="overflow-hidden rounded-lg border bg-background shadow-lg">
|
||||
<MailPage />
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
@@ -43,6 +43,7 @@ import TabsDemo from "@/registry/default/example/tabs-demo"
|
||||
import ToastDemo from "@/registry/default/example/toast-demo"
|
||||
import ToggleDemo from "@/registry/default/example/toggle-demo"
|
||||
import ToggleDisabled from "@/registry/default/example/toggle-disabled"
|
||||
import ToggleGroupDemo from "@/registry/default/example/toggle-group-demo"
|
||||
import ToggleOutline from "@/registry/default/example/toggle-outline"
|
||||
import ToggleWithText from "@/registry/default/example/toggle-with-text"
|
||||
import TooltipDemo from "@/registry/default/example/tooltip-demo"
|
||||
@@ -126,6 +127,9 @@ export default function KitchenSinkPage() {
|
||||
<SwitchDemo />
|
||||
<SelectDemo />
|
||||
</ComponentWrapper>
|
||||
<ComponentWrapper>
|
||||
<ToggleGroupDemo />
|
||||
</ComponentWrapper>
|
||||
<ComponentWrapper>
|
||||
<SeparatorDemo />
|
||||
</ComponentWrapper>
|
||||
|
||||
46
apps/www/app/themes/page.tsx
Normal file
46
apps/www/app/themes/page.tsx
Normal file
@@ -0,0 +1,46 @@
|
||||
import { Metadata } from "next"
|
||||
|
||||
import "public/registry/themes.css"
|
||||
import { Announcement } from "@/components/announcement"
|
||||
import {
|
||||
PageActions,
|
||||
PageHeader,
|
||||
PageHeaderDescription,
|
||||
PageHeaderHeading,
|
||||
} from "@/components/page-header"
|
||||
import { ThemeCustomizer } from "@/components/theme-customizer"
|
||||
import { ThemeWrapper } from "@/components/theme-wrapper"
|
||||
import { ThemesTabs } from "@/app/themes/tabs"
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Themes",
|
||||
description: "Hand-picked themes that you can copy and paste into your apps.",
|
||||
}
|
||||
|
||||
export default function ThemesPage() {
|
||||
return (
|
||||
<div className="container">
|
||||
<ThemeWrapper
|
||||
defaultTheme="zinc"
|
||||
className="relative flex flex-col items-start md:flex-row md:items-center"
|
||||
>
|
||||
<PageHeader>
|
||||
<Announcement />
|
||||
<PageHeaderHeading className="hidden md:block">
|
||||
Add colors. Make it yours.
|
||||
</PageHeaderHeading>
|
||||
<PageHeaderHeading className="md:hidden">
|
||||
Make it yours
|
||||
</PageHeaderHeading>
|
||||
<PageHeaderDescription>
|
||||
Hand-picked themes that you can copy and paste into your apps.
|
||||
</PageHeaderDescription>
|
||||
<PageActions>
|
||||
<ThemeCustomizer />
|
||||
</PageActions>
|
||||
</PageHeader>
|
||||
</ThemeWrapper>
|
||||
<ThemesTabs />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
73
apps/www/app/themes/tabs.tsx
Normal file
73
apps/www/app/themes/tabs.tsx
Normal file
@@ -0,0 +1,73 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
|
||||
import { useConfig } from "@/hooks/use-config"
|
||||
import { ThemeWrapper } from "@/components/theme-wrapper"
|
||||
import CardsDefault from "@/registry/default/example/cards"
|
||||
import { Skeleton } from "@/registry/default/ui/skeleton"
|
||||
import CardsNewYork from "@/registry/new-york/example/cards"
|
||||
|
||||
export function ThemesTabs() {
|
||||
const [mounted, setMounted] = React.useState(false)
|
||||
const [config] = useConfig()
|
||||
|
||||
React.useEffect(() => {
|
||||
setMounted(true)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
{!mounted ? (
|
||||
<div className="md:grids-col-2 grid md:gap-4 lg:grid-cols-10 xl:gap-6">
|
||||
<div className="space-y-4 lg:col-span-4 xl:col-span-6 xl:space-y-6">
|
||||
<Skeleton className="h-[218px] w-full" />
|
||||
<div className="grid gap-1 sm:grid-cols-[260px_1fr] md:hidden">
|
||||
<Skeleton className="h-[218px] w-full" />
|
||||
<div className="pt-3 sm:pl-2 sm:pt-0 xl:pl-4">
|
||||
<Skeleton className="h-[218px] w-full" />
|
||||
</div>
|
||||
<div className="pt-3 sm:col-span-2 xl:pt-4">
|
||||
<Skeleton className="h-[218px] w-full" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-1 xl:grid-cols-2">
|
||||
<div className="space-y-4 xl:space-y-6">
|
||||
<Skeleton className="h-[218px] w-full" />
|
||||
<Skeleton className="h-[218px] w-full" />
|
||||
<Skeleton className="h-[218px] w-full" />
|
||||
</div>
|
||||
<div className="space-y-4 xl:space-y-6">
|
||||
<Skeleton className="h-[218px] w-full" />
|
||||
<Skeleton className="h-[218px] w-full" />
|
||||
<div className="hidden xl:block">
|
||||
<Skeleton className="h-[218px] w-full" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-4 lg:col-span-6 xl:col-span-4 xl:space-y-6">
|
||||
<div className="hidden gap-1 sm:grid-cols-[260px_1fr] md:grid">
|
||||
<Skeleton className="h-[218px] w-full" />
|
||||
<div className="pt-3 sm:pl-2 sm:pt-0 xl:pl-4">
|
||||
<Skeleton className="h-[218px] w-full" />
|
||||
</div>
|
||||
<div className="pt-3 sm:col-span-2 xl:pt-4">
|
||||
<Skeleton className="h-[218px] w-full" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="hidden md:block">
|
||||
<Skeleton className="h-[218px] w-full" />
|
||||
</div>
|
||||
<Skeleton className="h-[218px] w-full" />
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<ThemeWrapper>
|
||||
{config.style === "new-york" && <CardsNewYork />}
|
||||
{config.style === "default" && <CardsDefault />}
|
||||
</ThemeWrapper>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
20
apps/www/components/announcement.tsx
Normal file
20
apps/www/components/announcement.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import Link from "next/link"
|
||||
import { ArrowRightIcon } from "@radix-ui/react-icons"
|
||||
|
||||
import { Separator } from "@/registry/new-york/ui/separator"
|
||||
|
||||
export function Announcement() {
|
||||
return (
|
||||
<Link
|
||||
href="/docs/changelog"
|
||||
className="inline-flex items-center rounded-lg bg-muted px-3 py-1 text-sm font-medium"
|
||||
>
|
||||
🎉 <Separator className="mx-2 h-4" orientation="vertical" />{" "}
|
||||
<span className="sm:hidden">New components and more.</span>
|
||||
<span className="hidden sm:inline">
|
||||
New components, cli updates and more.
|
||||
</span>
|
||||
<ArrowRightIcon className="ml-1 h-4 w-4" />
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
@@ -32,7 +32,16 @@ export function CommandMenu({ ...props }: DialogProps) {
|
||||
|
||||
React.useEffect(() => {
|
||||
const down = (e: KeyboardEvent) => {
|
||||
if (e.key === "k" && (e.metaKey || e.ctrlKey)) {
|
||||
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)
|
||||
}
|
||||
@@ -52,14 +61,14 @@ export function CommandMenu({ ...props }: DialogProps) {
|
||||
<Button
|
||||
variant="outline"
|
||||
className={cn(
|
||||
"relative w-full justify-start text-sm text-muted-foreground sm:pr-12 md:w-40 lg:w-64"
|
||||
"relative h-8 w-full justify-start rounded-[0.5rem] bg-background text-sm font-normal text-muted-foreground shadow-none sm:pr-12 md:w-40 lg:w-64"
|
||||
)}
|
||||
onClick={() => setOpen(true)}
|
||||
{...props}
|
||||
>
|
||||
<span className="hidden lg:inline-flex">Search documentation...</span>
|
||||
<span className="inline-flex lg:hidden">Search...</span>
|
||||
<kbd className="pointer-events-none absolute right-1.5 top-1.5 hidden h-5 select-none items-center gap-1 rounded border bg-muted px-1.5 font-mono text-[10px] font-medium opacity-100 sm:flex">
|
||||
<kbd className="pointer-events-none absolute right-[0.3rem] top-[0.3rem] hidden h-5 select-none items-center gap-1 rounded border bg-muted px-1.5 font-mono text-[10px] font-medium opacity-100 sm:flex">
|
||||
<span className="text-xs">⌘</span>K
|
||||
</kbd>
|
||||
</Button>
|
||||
|
||||
@@ -102,7 +102,7 @@ export function ComponentPreview({
|
||||
codeString && <CopyButton value={codeString} />
|
||||
)}
|
||||
</div>
|
||||
<ThemeWrapper>
|
||||
<ThemeWrapper defaultTheme="zinc">
|
||||
<div
|
||||
className={cn(
|
||||
"preview flex min-h-[350px] w-full justify-center p-10",
|
||||
|
||||
@@ -21,7 +21,7 @@ interface CopyButtonProps extends React.HTMLAttributes<HTMLButtonElement> {
|
||||
event?: Event["name"]
|
||||
}
|
||||
|
||||
async function copyToClipboardWithMeta(value: string, event?: Event) {
|
||||
export async function copyToClipboardWithMeta(value: string, event?: Event) {
|
||||
navigator.clipboard.writeText(value)
|
||||
if (event) {
|
||||
trackEvent(event)
|
||||
@@ -151,7 +151,7 @@ export function CopyNpmCommandButton({
|
||||
}, [hasCopied])
|
||||
|
||||
const copyCommand = React.useCallback(
|
||||
(value: string, pm: "npm" | "pnpm" | "yarn") => {
|
||||
(value: string, pm: "npm" | "pnpm" | "yarn" | "bun") => {
|
||||
copyToClipboardWithMeta(value, {
|
||||
name: "copy_npm_command",
|
||||
properties: {
|
||||
@@ -199,6 +199,11 @@ export function CopyNpmCommandButton({
|
||||
>
|
||||
pnpm
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
onClick={() => copyCommand(commands.__bunCommand__, "bun")}
|
||||
>
|
||||
bun
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)
|
||||
|
||||
31
apps/www/components/drawer.tsx
Normal file
31
apps/www/components/drawer.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
"use client"
|
||||
|
||||
import { forwardRef } from "react"
|
||||
import { Drawer as DrawerPrimitive } from "vaul"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const DrawerTrigger = DrawerPrimitive.Trigger
|
||||
|
||||
const DrawerContent = forwardRef<
|
||||
React.ElementRef<typeof DrawerPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Content>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<DrawerPrimitive.Portal>
|
||||
<DrawerPrimitive.Overlay className="fixed inset-0 z-50 bg-black/80" />
|
||||
<DrawerPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"fixed inset-x-0 bottom-0 z-50 mt-24 h-[96%] rounded-t-[10px] bg-background",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<div className="absolute left-1/2 top-3 h-2 w-[100px] translate-x-[-50%] rounded-full bg-muted" />
|
||||
{children}
|
||||
</DrawerPrimitive.Content>
|
||||
</DrawerPrimitive.Portal>
|
||||
))
|
||||
DrawerContent.displayName = "DrawerContent"
|
||||
|
||||
export { DrawerTrigger, DrawerContent }
|
||||
@@ -8,6 +8,11 @@ import { cn } from "@/lib/utils"
|
||||
import { ScrollArea, ScrollBar } from "@/registry/new-york/ui/scroll-area"
|
||||
|
||||
const examples = [
|
||||
{
|
||||
name: "Mail",
|
||||
href: "/examples/mail",
|
||||
code: "https://github.com/shadcn/ui/tree/main/apps/www/app/examples/mail",
|
||||
},
|
||||
{
|
||||
name: "Dashboard",
|
||||
href: "/examples/dashboard",
|
||||
@@ -54,15 +59,16 @@ export function ExamplesNav({ className, ...props }: ExamplesNavProps) {
|
||||
<div className="relative">
|
||||
<ScrollArea className="max-w-[600px] lg:max-w-none">
|
||||
<div className={cn("mb-4 flex items-center", className)} {...props}>
|
||||
{examples.map((example) => (
|
||||
{examples.map((example, index) => (
|
||||
<Link
|
||||
href={example.href}
|
||||
key={example.href}
|
||||
className={cn(
|
||||
"flex items-center px-4",
|
||||
pathname?.startsWith(example.href)
|
||||
? "font-bold text-primary"
|
||||
: "font-medium text-muted-foreground"
|
||||
"flex h-7 items-center justify-center rounded-full px-4 text-center text-sm transition-colors hover:text-primary",
|
||||
pathname?.startsWith(example.href) ||
|
||||
(index === 0 && pathname === "/")
|
||||
? "bg-muted font-medium text-primary"
|
||||
: "text-muted-foreground"
|
||||
)}
|
||||
>
|
||||
{example.name}
|
||||
@@ -71,9 +77,6 @@ export function ExamplesNav({ className, ...props }: ExamplesNavProps) {
|
||||
</div>
|
||||
<ScrollBar orientation="horizontal" className="invisible" />
|
||||
</ScrollArea>
|
||||
<ExampleCodeLink
|
||||
pathname={pathname === "/" ? "/examples/dashboard" : pathname}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -29,8 +29,14 @@ export const Icons = {
|
||||
</svg>
|
||||
),
|
||||
twitter: (props: IconProps) => (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" {...props}>
|
||||
<path d="M21.543 7.104c.015.211.015.423.015.636 0 6.507-4.954 14.01-14.01 14.01v-.003A13.94 13.94 0 0 1 0 19.539a9.88 9.88 0 0 0 7.287-2.041 4.93 4.93 0 0 1-4.6-3.42 4.916 4.916 0 0 0 2.223-.084A4.926 4.926 0 0 1 .96 9.167v-.062a4.887 4.887 0 0 0 2.235.616A4.928 4.928 0 0 1 1.67 3.148a13.98 13.98 0 0 0 10.15 5.144 4.929 4.929 0 0 1 8.39-4.49 9.868 9.868 0 0 0 3.128-1.196 4.941 4.941 0 0 1-2.165 2.724A9.828 9.828 0 0 0 24 4.555a10.019 10.019 0 0 1-2.457 2.549z" />
|
||||
<svg
|
||||
{...props}
|
||||
height="23"
|
||||
viewBox="0 0 1200 1227"
|
||||
width="23"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path d="M714.163 519.284L1160.89 0H1055.03L667.137 450.887L357.328 0H0L468.492 681.821L0 1226.37H105.866L515.491 750.218L842.672 1226.37H1200L714.137 519.284H714.163ZM569.165 687.828L521.697 619.934L144.011 79.6944H306.615L611.412 515.685L658.88 583.579L1055.08 1150.3H892.476L569.165 687.854V687.828Z" />
|
||||
</svg>
|
||||
),
|
||||
gitHub: (props: IconProps) => (
|
||||
|
||||
@@ -7,6 +7,7 @@ import { usePathname } from "next/navigation"
|
||||
import { siteConfig } from "@/config/site"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { Icons } from "@/components/icons"
|
||||
import { Badge } from "@/registry/new-york/ui/badge"
|
||||
|
||||
export function MainNav() {
|
||||
const pathname = usePathname()
|
||||
@@ -19,7 +20,7 @@ export function MainNav() {
|
||||
{siteConfig.name}
|
||||
</span>
|
||||
</Link>
|
||||
<nav className="flex items-center space-x-6 text-sm font-medium">
|
||||
<nav className="flex items-center gap-6 text-sm">
|
||||
<Link
|
||||
href="/docs"
|
||||
className={cn(
|
||||
@@ -27,7 +28,7 @@ export function MainNav() {
|
||||
pathname === "/docs" ? "text-foreground" : "text-foreground/60"
|
||||
)}
|
||||
>
|
||||
Documentation
|
||||
Docs
|
||||
</Link>
|
||||
<Link
|
||||
href="/docs/components"
|
||||
@@ -40,6 +41,17 @@ export function MainNav() {
|
||||
>
|
||||
Components
|
||||
</Link>
|
||||
<Link
|
||||
href="/themes"
|
||||
className={cn(
|
||||
"transition-colors hover:text-foreground/80",
|
||||
pathname?.startsWith("/themes")
|
||||
? "text-foreground"
|
||||
: "text-foreground/60"
|
||||
)}
|
||||
>
|
||||
Themes
|
||||
</Link>
|
||||
<Link
|
||||
href="/examples"
|
||||
className={cn(
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import * as React from "react"
|
||||
import Image from "next/image"
|
||||
import Link, { LinkProps } from "next/link"
|
||||
import Link from "next/link"
|
||||
import { useMDXComponent } from "next-contentlayer/hooks"
|
||||
import { NpmCommands } from "types/unist"
|
||||
|
||||
@@ -170,8 +170,9 @@ const components = {
|
||||
className,
|
||||
__rawString__,
|
||||
__npmCommand__,
|
||||
__pnpmCommand__,
|
||||
__yarnCommand__,
|
||||
__pnpmCommand__,
|
||||
__bunCommand__,
|
||||
__withMeta__,
|
||||
__src__,
|
||||
__event__,
|
||||
@@ -201,16 +202,20 @@ const components = {
|
||||
className={cn("absolute right-4 top-4", __withMeta__ && "top-16")}
|
||||
/>
|
||||
)}
|
||||
{__npmCommand__ && __yarnCommand__ && __pnpmCommand__ && (
|
||||
<CopyNpmCommandButton
|
||||
commands={{
|
||||
__npmCommand__,
|
||||
__pnpmCommand__,
|
||||
__yarnCommand__,
|
||||
}}
|
||||
className={cn("absolute right-4 top-4", __withMeta__ && "top-16")}
|
||||
/>
|
||||
)}
|
||||
{__npmCommand__ &&
|
||||
__yarnCommand__ &&
|
||||
__pnpmCommand__ &&
|
||||
__bunCommand__ && (
|
||||
<CopyNpmCommandButton
|
||||
commands={{
|
||||
__npmCommand__,
|
||||
__yarnCommand__,
|
||||
__pnpmCommand__,
|
||||
__bunCommand__,
|
||||
}}
|
||||
className={cn("absolute right-4 top-4", __withMeta__ && "top-16")}
|
||||
/>
|
||||
)}
|
||||
</StyleWrapper>
|
||||
)
|
||||
},
|
||||
@@ -292,6 +297,12 @@ const components = {
|
||||
}: React.ComponentProps<typeof FrameworkDocs>) => (
|
||||
<FrameworkDocs className={cn(className)} {...props} />
|
||||
),
|
||||
Link: ({ className, ...props }: React.ComponentProps<typeof Link>) => (
|
||||
<Link
|
||||
className={cn("font-medium underline underline-offset-4", className)}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
LinkedCard: ({ className, ...props }: React.ComponentProps<typeof Link>) => (
|
||||
<Link
|
||||
className={cn(
|
||||
|
||||
@@ -23,7 +23,35 @@ export function MobileNav() {
|
||||
variant="ghost"
|
||||
className="mr-2 px-0 text-base hover:bg-transparent focus-visible:bg-transparent focus-visible:ring-0 focus-visible:ring-offset-0 md:hidden"
|
||||
>
|
||||
<ViewVerticalIcon className="h-5 w-5" />
|
||||
<svg
|
||||
strokeWidth="1.5"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="h-5 w-5"
|
||||
>
|
||||
<path
|
||||
d="M3 5H11"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
></path>
|
||||
<path
|
||||
d="M3 12H16"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
></path>
|
||||
<path
|
||||
d="M3 19H21"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
></path>
|
||||
</svg>
|
||||
<span className="sr-only">Toggle Menu</span>
|
||||
</Button>
|
||||
</SheetTrigger>
|
||||
@@ -66,6 +94,11 @@ export function MobileNav() {
|
||||
className="text-muted-foreground"
|
||||
>
|
||||
{item.title}
|
||||
{item.label && (
|
||||
<span className="ml-2 rounded-md bg-[#adfa1d] px-1.5 py-0.5 text-xs leading-none text-[#000000] no-underline group-hover:no-underline">
|
||||
{item.label}
|
||||
</span>
|
||||
)}
|
||||
</MobileLink>
|
||||
) : (
|
||||
item.title
|
||||
|
||||
@@ -10,7 +10,7 @@ function PageHeader({
|
||||
return (
|
||||
<section
|
||||
className={cn(
|
||||
"flex max-w-[980px] flex-col items-start gap-2 px-4 pt-8 md:pt-12",
|
||||
"mx-auto flex max-w-[980px] flex-col items-center gap-2 py-8 md:py-12 md:pb-8 lg:py-24 lg:pb-20",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -27,7 +27,7 @@ function PageHeaderHeading({
|
||||
return (
|
||||
<h1
|
||||
className={cn(
|
||||
"text-3xl font-bold leading-tight tracking-tighter md:text-5xl lg:leading-[1.1]",
|
||||
"text-center text-3xl font-bold leading-tight tracking-tighter md:text-6xl lg:leading-[1.1]",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -42,7 +42,7 @@ function PageHeaderDescription({
|
||||
return (
|
||||
<Balance
|
||||
className={cn(
|
||||
"max-w-[750px] text-lg text-muted-foreground sm:text-xl",
|
||||
"max-w-[750px] text-center text-lg text-muted-foreground sm:text-xl",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -50,4 +50,19 @@ function PageHeaderDescription({
|
||||
)
|
||||
}
|
||||
|
||||
export { PageHeader, PageHeaderHeading, PageHeaderDescription }
|
||||
function PageActions({
|
||||
className,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLDivElement>) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"flex w-full items-center justify-center space-x-4 py-4 md:pb-10",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { PageHeader, PageHeaderHeading, PageHeaderDescription, PageActions }
|
||||
|
||||
@@ -4,7 +4,7 @@ export function SiteFooter() {
|
||||
return (
|
||||
<footer className="py-6 md:px-8 md:py-0">
|
||||
<div className="container flex flex-col items-center justify-between gap-4 md:h-24 md:flex-row">
|
||||
<p className="text-center text-sm leading-loose text-muted-foreground md:text-left">
|
||||
<p className="text-balance text-center text-sm leading-loose text-muted-foreground md:text-left">
|
||||
Built by{" "}
|
||||
<a
|
||||
href={siteConfig.links.twitter}
|
||||
|
||||
@@ -11,8 +11,8 @@ import { buttonVariants } from "@/registry/new-york/ui/button"
|
||||
|
||||
export function SiteHeader() {
|
||||
return (
|
||||
<header className="supports-backdrop-blur:bg-background/60 sticky top-0 z-40 w-full border-b bg-background/95 backdrop-blur">
|
||||
<div className="container flex h-14 items-center">
|
||||
<header className="sticky top-0 z-50 w-full border-b border-border/40 bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
|
||||
<div className="container flex h-14 max-w-screen-2xl items-center">
|
||||
<MainNav />
|
||||
<MobileNav />
|
||||
<div className="flex flex-1 items-center justify-between space-x-2 md:justify-end">
|
||||
@@ -50,7 +50,7 @@ export function SiteHeader() {
|
||||
"w-9 px-0"
|
||||
)}
|
||||
>
|
||||
<Icons.twitter className="h-4 w-4 fill-current" />
|
||||
<Icons.twitter className="h-3 w-3 fill-current" />
|
||||
<span className="sr-only">Twitter</span>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
52
apps/www/components/theme-component.tsx
Normal file
52
apps/www/components/theme-component.tsx
Normal file
@@ -0,0 +1,52 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import { Index } from "@/__registry__"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { useConfig } from "@/hooks/use-config"
|
||||
import { Icons } from "@/components/icons"
|
||||
|
||||
interface ThemeComponentProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
name: string
|
||||
extractClassname?: boolean
|
||||
extractedClassNames?: string
|
||||
align?: "center" | "start" | "end"
|
||||
}
|
||||
|
||||
export function ThemeComponent({ name, ...props }: ThemeComponentProps) {
|
||||
const [config] = useConfig()
|
||||
|
||||
const Preview = React.useMemo(() => {
|
||||
const Component = Index[config.style][name]?.component
|
||||
|
||||
if (!Component) {
|
||||
return (
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Component{" "}
|
||||
<code className="relative rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-sm">
|
||||
{name}
|
||||
</code>{" "}
|
||||
not found in registry.
|
||||
</p>
|
||||
)
|
||||
}
|
||||
|
||||
return <Component />
|
||||
}, [name, config.style])
|
||||
|
||||
return (
|
||||
<div className={cn("relative")} {...props}>
|
||||
<React.Suspense
|
||||
fallback={
|
||||
<div className="flex items-center text-sm text-muted-foreground">
|
||||
<Icons.spinner className="mr-2 h-4 w-4 animate-spin" />
|
||||
Loading...
|
||||
</div>
|
||||
}
|
||||
>
|
||||
{Preview}
|
||||
</React.Suspense>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
631
apps/www/components/theme-customizer.tsx
Normal file
631
apps/www/components/theme-customizer.tsx
Normal file
@@ -0,0 +1,631 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import {
|
||||
CheckIcon,
|
||||
CopyIcon,
|
||||
InfoCircledIcon,
|
||||
MoonIcon,
|
||||
ResetIcon,
|
||||
SunIcon,
|
||||
} from "@radix-ui/react-icons"
|
||||
import template from "lodash.template"
|
||||
import { Paintbrush } from "lucide-react"
|
||||
import { useTheme } from "next-themes"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { useConfig } from "@/hooks/use-config"
|
||||
import { copyToClipboardWithMeta } from "@/components/copy-button"
|
||||
import { ThemeWrapper } from "@/components/theme-wrapper"
|
||||
import { Button } from "@/registry/new-york/ui/button"
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "@/registry/new-york/ui/dialog"
|
||||
import {
|
||||
Drawer,
|
||||
DrawerContent,
|
||||
DrawerTrigger,
|
||||
} from "@/registry/new-york/ui/drawer"
|
||||
import { Label } from "@/registry/new-york/ui/label"
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/registry/new-york/ui/popover"
|
||||
import { Skeleton } from "@/registry/new-york/ui/skeleton"
|
||||
import { Theme, themes } from "@/registry/themes"
|
||||
|
||||
import "@/styles/mdx.css"
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipTrigger,
|
||||
} from "@/registry/new-york/ui/tooltip"
|
||||
|
||||
export function ThemeCustomizer() {
|
||||
const [config, setConfig] = useConfig()
|
||||
const { resolvedTheme: mode } = useTheme()
|
||||
const [mounted, setMounted] = React.useState(false)
|
||||
|
||||
React.useEffect(() => {
|
||||
setMounted(true)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="flex items-center space-x-2">
|
||||
<Drawer>
|
||||
<DrawerTrigger asChild>
|
||||
<Button variant="outline" className="md:hidden">
|
||||
<Paintbrush className="mr-2 h-4 w-4" />
|
||||
Customize
|
||||
</Button>
|
||||
</DrawerTrigger>
|
||||
<DrawerContent className="p-6 pt-0">
|
||||
<Customizer />
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
<div className="hidden md:flex">
|
||||
<div className="mr-2 hidden items-center space-x-0.5 lg:flex">
|
||||
{mounted ? (
|
||||
<>
|
||||
{["zinc", "rose", "blue", "green", "orange"].map((color) => {
|
||||
const theme = themes.find((theme) => theme.name === color)
|
||||
const isActive = config.theme === color
|
||||
|
||||
if (!theme) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<Tooltip key={theme.name}>
|
||||
<TooltipTrigger asChild>
|
||||
<button
|
||||
onClick={() =>
|
||||
setConfig({
|
||||
...config,
|
||||
theme: theme.name,
|
||||
})
|
||||
}
|
||||
className={cn(
|
||||
"flex h-9 w-9 items-center justify-center rounded-full border-2 text-xs",
|
||||
isActive
|
||||
? "border-[--theme-primary]"
|
||||
: "border-transparent"
|
||||
)}
|
||||
style={
|
||||
{
|
||||
"--theme-primary": `hsl(${
|
||||
theme?.activeColor[
|
||||
mode === "dark" ? "dark" : "light"
|
||||
]
|
||||
})`,
|
||||
} as React.CSSProperties
|
||||
}
|
||||
>
|
||||
<span
|
||||
className={cn(
|
||||
"flex h-6 w-6 items-center justify-center rounded-full bg-[--theme-primary]"
|
||||
)}
|
||||
>
|
||||
{isActive && (
|
||||
<CheckIcon className="h-4 w-4 text-white" />
|
||||
)}
|
||||
</span>
|
||||
<span className="sr-only">{theme.label}</span>
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent
|
||||
align="center"
|
||||
className="rounded-[0.5rem] bg-zinc-900 text-zinc-50"
|
||||
>
|
||||
{theme.label}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
)
|
||||
})}
|
||||
</>
|
||||
) : (
|
||||
<div className="mr-1 flex items-center gap-4">
|
||||
<Skeleton className="h-6 w-6 rounded-full" />
|
||||
<Skeleton className="h-6 w-6 rounded-full" />
|
||||
<Skeleton className="h-6 w-6 rounded-full" />
|
||||
<Skeleton className="h-6 w-6 rounded-full" />
|
||||
<Skeleton className="h-6 w-6 rounded-full" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button variant="outline">
|
||||
<Paintbrush className="mr-2 h-4 w-4" />
|
||||
Customize
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent
|
||||
align="center"
|
||||
className="z-40 w-[340px] rounded-[0.5rem] bg-white p-6 dark:bg-zinc-950"
|
||||
>
|
||||
<Customizer />
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
<CopyCodeButton />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function Customizer() {
|
||||
const [mounted, setMounted] = React.useState(false)
|
||||
const { setTheme: setMode, resolvedTheme: mode } = useTheme()
|
||||
const [config, setConfig] = useConfig()
|
||||
|
||||
React.useEffect(() => {
|
||||
setMounted(true)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<ThemeWrapper
|
||||
defaultTheme="zinc"
|
||||
className="flex flex-col space-y-4 md:space-y-6"
|
||||
>
|
||||
<div className="flex items-start pt-4 md:pt-0">
|
||||
<div className="space-y-1 pr-2">
|
||||
<div className="font-semibold leading-none tracking-tight">
|
||||
Customize
|
||||
</div>
|
||||
<div className="text-xs text-muted-foreground">
|
||||
Pick a style and color for your components.
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="ml-auto rounded-[0.5rem]"
|
||||
onClick={() => {
|
||||
setConfig({
|
||||
...config,
|
||||
theme: "zinc",
|
||||
radius: 0.5,
|
||||
})
|
||||
}}
|
||||
>
|
||||
<ResetIcon />
|
||||
<span className="sr-only">Reset</span>
|
||||
</Button>
|
||||
</div>
|
||||
<div className="flex flex-1 flex-col space-y-4 md:space-y-6">
|
||||
<div className="space-y-1.5">
|
||||
<div className="flex w-full items-center">
|
||||
<Label className="text-xs">Style</Label>
|
||||
<Popover>
|
||||
<PopoverTrigger>
|
||||
<InfoCircledIcon className="ml-1 h-3 w-3" />
|
||||
<span className="sr-only">About styles</span>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent
|
||||
className="space-y-3 rounded-[0.5rem] text-sm"
|
||||
side="right"
|
||||
align="start"
|
||||
alignOffset={-20}
|
||||
>
|
||||
<p className="font-medium">
|
||||
What is the difference between the New York and Default style?
|
||||
</p>
|
||||
<p>
|
||||
A style comes with its own set of components, animations,
|
||||
icons and more.
|
||||
</p>
|
||||
<p>
|
||||
The <span className="font-medium">Default</span> style has
|
||||
larger inputs, uses lucide-react for icons and
|
||||
tailwindcss-animate for animations.
|
||||
</p>
|
||||
<p>
|
||||
The <span className="font-medium">New York</span> style ships
|
||||
with smaller buttons and cards with shadows. It uses icons
|
||||
from Radix Icons.
|
||||
</p>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
<div className="grid grid-cols-3 gap-2">
|
||||
<Button
|
||||
variant={"outline"}
|
||||
size="sm"
|
||||
onClick={() => setConfig({ ...config, style: "default" })}
|
||||
className={cn(
|
||||
config.style === "default" && "border-2 border-primary"
|
||||
)}
|
||||
>
|
||||
Default
|
||||
</Button>
|
||||
<Button
|
||||
variant={"outline"}
|
||||
size="sm"
|
||||
onClick={() => setConfig({ ...config, style: "new-york" })}
|
||||
className={cn(
|
||||
config.style === "new-york" && "border-2 border-primary"
|
||||
)}
|
||||
>
|
||||
New York
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-xs">Color</Label>
|
||||
<div className="grid grid-cols-3 gap-2">
|
||||
{themes.map((theme) => {
|
||||
const isActive = config.theme === theme.name
|
||||
|
||||
return mounted ? (
|
||||
<Button
|
||||
variant={"outline"}
|
||||
size="sm"
|
||||
key={theme.name}
|
||||
onClick={() => {
|
||||
setConfig({
|
||||
...config,
|
||||
theme: theme.name,
|
||||
})
|
||||
}}
|
||||
className={cn(
|
||||
"justify-start",
|
||||
isActive && "border-2 border-primary"
|
||||
)}
|
||||
style={
|
||||
{
|
||||
"--theme-primary": `hsl(${
|
||||
theme?.activeColor[mode === "dark" ? "dark" : "light"]
|
||||
})`,
|
||||
} as React.CSSProperties
|
||||
}
|
||||
>
|
||||
<span
|
||||
className={cn(
|
||||
"mr-1 flex h-5 w-5 shrink-0 -translate-x-1 items-center justify-center rounded-full bg-[--theme-primary]"
|
||||
)}
|
||||
>
|
||||
{isActive && <CheckIcon className="h-4 w-4 text-white" />}
|
||||
</span>
|
||||
{theme.label}
|
||||
</Button>
|
||||
) : (
|
||||
<Skeleton className="h-8 w-full" key={theme.name} />
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-xs">Radius</Label>
|
||||
<div className="grid grid-cols-5 gap-2">
|
||||
{["0", "0.3", "0.5", "0.75", "1.0"].map((value) => {
|
||||
return (
|
||||
<Button
|
||||
variant={"outline"}
|
||||
size="sm"
|
||||
key={value}
|
||||
onClick={() => {
|
||||
setConfig({
|
||||
...config,
|
||||
radius: parseFloat(value),
|
||||
})
|
||||
}}
|
||||
className={cn(
|
||||
config.radius === parseFloat(value) &&
|
||||
"border-2 border-primary"
|
||||
)}
|
||||
>
|
||||
{value}
|
||||
</Button>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-xs">Mode</Label>
|
||||
<div className="grid grid-cols-3 gap-2">
|
||||
{mounted ? (
|
||||
<>
|
||||
<Button
|
||||
variant={"outline"}
|
||||
size="sm"
|
||||
onClick={() => setMode("light")}
|
||||
className={cn(mode === "light" && "border-2 border-primary")}
|
||||
>
|
||||
<SunIcon className="mr-1 -translate-x-1" />
|
||||
Light
|
||||
</Button>
|
||||
<Button
|
||||
variant={"outline"}
|
||||
size="sm"
|
||||
onClick={() => setMode("dark")}
|
||||
className={cn(mode === "dark" && "border-2 border-primary")}
|
||||
>
|
||||
<MoonIcon className="mr-1 -translate-x-1" />
|
||||
Dark
|
||||
</Button>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Skeleton className="h-8 w-full" />
|
||||
<Skeleton className="h-8 w-full" />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ThemeWrapper>
|
||||
)
|
||||
}
|
||||
|
||||
function CopyCodeButton() {
|
||||
const [config] = useConfig()
|
||||
const activeTheme = themes.find((theme) => theme.name === config.theme)
|
||||
const [hasCopied, setHasCopied] = React.useState(false)
|
||||
|
||||
React.useEffect(() => {
|
||||
setTimeout(() => {
|
||||
setHasCopied(false)
|
||||
}, 2000)
|
||||
}, [hasCopied])
|
||||
|
||||
return (
|
||||
<>
|
||||
{activeTheme && (
|
||||
<Button
|
||||
onClick={() => {
|
||||
copyToClipboardWithMeta(getThemeCode(activeTheme, config.radius), {
|
||||
name: "copy_theme_code",
|
||||
properties: {
|
||||
theme: activeTheme.name,
|
||||
radius: config.radius,
|
||||
},
|
||||
})
|
||||
setHasCopied(true)
|
||||
}}
|
||||
className="md:hidden"
|
||||
>
|
||||
{hasCopied ? (
|
||||
<CheckIcon className="mr-2 h-4 w-4" />
|
||||
) : (
|
||||
<CopyIcon className="mr-2 h-4 w-4" />
|
||||
)}
|
||||
Copy
|
||||
</Button>
|
||||
)}
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<Button className="hidden md:flex">Copy code</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="max-w-2xl outline-none">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Theme</DialogTitle>
|
||||
<DialogDescription>
|
||||
Copy and paste the following code into your CSS file.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<ThemeWrapper defaultTheme="zinc" className="relative">
|
||||
<CustomizerCode />
|
||||
{activeTheme && (
|
||||
<Button
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
copyToClipboardWithMeta(
|
||||
getThemeCode(activeTheme, config.radius),
|
||||
{
|
||||
name: "copy_theme_code",
|
||||
properties: {
|
||||
theme: activeTheme.name,
|
||||
radius: config.radius,
|
||||
},
|
||||
}
|
||||
)
|
||||
setHasCopied(true)
|
||||
}}
|
||||
className="absolute right-4 top-4 bg-muted text-muted-foreground hover:bg-muted hover:text-muted-foreground"
|
||||
>
|
||||
{hasCopied ? (
|
||||
<CheckIcon className="mr-2 h-4 w-4" />
|
||||
) : (
|
||||
<CopyIcon className="mr-2 h-4 w-4" />
|
||||
)}
|
||||
Copy
|
||||
</Button>
|
||||
)}
|
||||
</ThemeWrapper>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function CustomizerCode() {
|
||||
const [config] = useConfig()
|
||||
const activeTheme = themes.find((theme) => theme.name === config.theme)
|
||||
|
||||
return (
|
||||
<ThemeWrapper defaultTheme="zinc" className="relative space-y-4">
|
||||
<div data-rehype-pretty-code-fragment="">
|
||||
<pre className="max-h-[450px] overflow-x-auto rounded-lg border bg-zinc-950 py-4 dark:bg-zinc-900">
|
||||
<code className="relative rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-sm">
|
||||
<span className="line text-white">@layer base {</span>
|
||||
<span className="line text-white"> :root {</span>
|
||||
<span className="line text-white">
|
||||
--background:{" "}
|
||||
{activeTheme?.cssVars.light["background"]};
|
||||
</span>
|
||||
<span className="line text-white">
|
||||
--foreground:{" "}
|
||||
{activeTheme?.cssVars.light["foreground"]};
|
||||
</span>
|
||||
{[
|
||||
"card",
|
||||
"popover",
|
||||
"primary",
|
||||
"secondary",
|
||||
"muted",
|
||||
"accent",
|
||||
"destructive",
|
||||
].map((prefix) => (
|
||||
<>
|
||||
<span className="line text-white">
|
||||
--{prefix}:{" "}
|
||||
{
|
||||
activeTheme?.cssVars.light[
|
||||
prefix as keyof typeof activeTheme.cssVars.light
|
||||
]
|
||||
}
|
||||
;
|
||||
</span>
|
||||
<span className="line text-white">
|
||||
--{prefix}-foreground:{" "}
|
||||
{
|
||||
activeTheme?.cssVars.light[
|
||||
`${prefix}-foreground` as keyof typeof activeTheme.cssVars.light
|
||||
]
|
||||
}
|
||||
;
|
||||
</span>
|
||||
</>
|
||||
))}
|
||||
<span className="line text-white">
|
||||
--border:{" "}
|
||||
{activeTheme?.cssVars.light["border"]};
|
||||
</span>
|
||||
<span className="line text-white">
|
||||
--input:{" "}
|
||||
{activeTheme?.cssVars.light["input"]};
|
||||
</span>
|
||||
<span className="line text-white">
|
||||
--ring:{" "}
|
||||
{activeTheme?.cssVars.light["ring"]};
|
||||
</span>
|
||||
<span className="line text-white">
|
||||
--radius: {config.radius}rem;
|
||||
</span>
|
||||
<span className="line text-white"> }</span>
|
||||
<span className="line text-white"> </span>
|
||||
<span className="line text-white"> .dark {</span>
|
||||
<span className="line text-white">
|
||||
--background:{" "}
|
||||
{activeTheme?.cssVars.dark["background"]};
|
||||
</span>
|
||||
<span className="line text-white">
|
||||
--foreground:{" "}
|
||||
{activeTheme?.cssVars.dark["foreground"]};
|
||||
</span>
|
||||
{[
|
||||
"card",
|
||||
"popover",
|
||||
"primary",
|
||||
"secondary",
|
||||
"muted",
|
||||
"accent",
|
||||
"destructive",
|
||||
].map((prefix) => (
|
||||
<>
|
||||
<span className="line text-white">
|
||||
--{prefix}:{" "}
|
||||
{
|
||||
activeTheme?.cssVars.dark[
|
||||
prefix as keyof typeof activeTheme.cssVars.dark
|
||||
]
|
||||
}
|
||||
;
|
||||
</span>
|
||||
<span className="line text-white">
|
||||
--{prefix}-foreground:{" "}
|
||||
{
|
||||
activeTheme?.cssVars.dark[
|
||||
`${prefix}-foreground` as keyof typeof activeTheme.cssVars.dark
|
||||
]
|
||||
}
|
||||
;
|
||||
</span>
|
||||
</>
|
||||
))}
|
||||
<span className="line text-white">
|
||||
--border:{" "}
|
||||
{activeTheme?.cssVars.dark["border"]};
|
||||
</span>
|
||||
<span className="line text-white">
|
||||
--input:{" "}
|
||||
{activeTheme?.cssVars.dark["input"]};
|
||||
</span>
|
||||
<span className="line text-white">
|
||||
--ring:{" "}
|
||||
{activeTheme?.cssVars.dark["ring"]};
|
||||
</span>
|
||||
<span className="line text-white"> }</span>
|
||||
<span className="line text-white">}</span>
|
||||
</code>
|
||||
</pre>
|
||||
</div>
|
||||
</ThemeWrapper>
|
||||
)
|
||||
}
|
||||
|
||||
function getThemeCode(theme: Theme, radius: number) {
|
||||
if (!theme) {
|
||||
return ""
|
||||
}
|
||||
|
||||
return template(BASE_STYLES_WITH_VARIABLES)({
|
||||
colors: theme.cssVars,
|
||||
radius,
|
||||
})
|
||||
}
|
||||
|
||||
const BASE_STYLES_WITH_VARIABLES = `
|
||||
@layer base {
|
||||
:root {
|
||||
--background: <%- colors.light["background"] %>;
|
||||
--foreground: <%- colors.light["foreground"] %>;
|
||||
--card: <%- colors.light["card"] %>;
|
||||
--card-foreground: <%- colors.light["card-foreground"] %>;
|
||||
--popover: <%- colors.light["popover"] %>;
|
||||
--popover-foreground: <%- colors.light["popover-foreground"] %>;
|
||||
--primary: <%- colors.light["primary"] %>;
|
||||
--primary-foreground: <%- colors.light["primary-foreground"] %>;
|
||||
--secondary: <%- colors.light["secondary"] %>;
|
||||
--secondary-foreground: <%- colors.light["secondary-foreground"] %>;
|
||||
--muted: <%- colors.light["muted"] %>;
|
||||
--muted-foreground: <%- colors.light["muted-foreground"] %>;
|
||||
--accent: <%- colors.light["accent"] %>;
|
||||
--accent-foreground: <%- colors.light["accent-foreground"] %>;
|
||||
--destructive: <%- colors.light["destructive"] %>;
|
||||
--destructive-foreground: <%- colors.light["destructive-foreground"] %>;
|
||||
--border: <%- colors.light["border"] %>;
|
||||
--input: <%- colors.light["input"] %>;
|
||||
--ring: <%- colors.light["ring"] %>;
|
||||
--radius: <%- radius %>rem;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: <%- colors.dark["background"] %>;
|
||||
--foreground: <%- colors.dark["foreground"] %>;
|
||||
--card: <%- colors.dark["card"] %>;
|
||||
--card-foreground: <%- colors.dark["card-foreground"] %>;
|
||||
--popover: <%- colors.dark["popover"] %>;
|
||||
--popover-foreground: <%- colors.dark["popover-foreground"] %>;
|
||||
--primary: <%- colors.dark["primary"] %>;
|
||||
--primary-foreground: <%- colors.dark["primary-foreground"] %>;
|
||||
--secondary: <%- colors.dark["secondary"] %>;
|
||||
--secondary-foreground: <%- colors.dark["secondary-foreground"] %>;
|
||||
--muted: <%- colors.dark["muted"] %>;
|
||||
--muted-foreground: <%- colors.dark["muted-foreground"] %>;
|
||||
--accent: <%- colors.dark["accent"] %>;
|
||||
--accent-foreground: <%- colors.dark["accent-foreground"] %>;
|
||||
--destructive: <%- colors.dark["destructive"] %>;
|
||||
--destructive-foreground: <%- colors.dark["destructive-foreground"] %>;
|
||||
--border: <%- colors.dark["border"] %>;
|
||||
--input: <%- colors.dark["input"] %>;
|
||||
--ring: <%- colors.dark["ring"] %>;
|
||||
}
|
||||
}
|
||||
`
|
||||
26
apps/www/components/theme-switcher.tsx
Normal file
26
apps/www/components/theme-switcher.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import { useSelectedLayoutSegment } from "next/navigation"
|
||||
|
||||
import { useConfig } from "@/hooks/use-config"
|
||||
|
||||
export function ThemeSwitcher() {
|
||||
const [config] = useConfig()
|
||||
const segment = useSelectedLayoutSegment()
|
||||
|
||||
React.useEffect(() => {
|
||||
document.body.classList.forEach((className) => {
|
||||
if (className.match(/^theme.*/)) {
|
||||
document.body.classList.remove(className)
|
||||
}
|
||||
})
|
||||
|
||||
const theme = segment === "themes" ? config.theme : null
|
||||
if (theme) {
|
||||
return document.body.classList.add(`theme-${theme}`)
|
||||
}
|
||||
}, [segment, config])
|
||||
|
||||
return null
|
||||
}
|
||||
@@ -3,8 +3,31 @@
|
||||
import { cn } from "@/lib/utils"
|
||||
import { useConfig } from "@/hooks/use-config"
|
||||
|
||||
export function ThemeWrapper({ children }: React.ComponentProps<"div">) {
|
||||
interface ThemeWrapperProps extends React.ComponentProps<"div"> {
|
||||
defaultTheme?: string
|
||||
}
|
||||
|
||||
export function ThemeWrapper({
|
||||
defaultTheme,
|
||||
children,
|
||||
className,
|
||||
}: ThemeWrapperProps) {
|
||||
const [config] = useConfig()
|
||||
|
||||
return <div className={cn(`theme-${config.theme}`, "w-full")}>{children}</div>
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
`theme-${defaultTheme || config.theme}`,
|
||||
"w-full",
|
||||
className
|
||||
)}
|
||||
style={
|
||||
{
|
||||
"--radius": `${defaultTheme ? 0.5 : config.radius}rem`,
|
||||
} as React.CSSProperties
|
||||
}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -15,6 +15,10 @@ export const docsConfig: DocsConfig = {
|
||||
title: "Components",
|
||||
href: "/docs/components/accordion",
|
||||
},
|
||||
{
|
||||
title: "Themes",
|
||||
href: "/themes",
|
||||
},
|
||||
{
|
||||
title: "Examples",
|
||||
href: "/examples",
|
||||
@@ -48,6 +52,11 @@ export const docsConfig: DocsConfig = {
|
||||
href: "/docs/installation",
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: "components.json",
|
||||
href: "/docs/components-json",
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: "Theming",
|
||||
href: "/docs/theming",
|
||||
@@ -78,46 +87,6 @@ export const docsConfig: DocsConfig = {
|
||||
href: "/docs/changelog",
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: "About",
|
||||
href: "/docs/about",
|
||||
items: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Installation",
|
||||
items: [
|
||||
{
|
||||
title: "Next.js",
|
||||
href: "/docs/installation/next",
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: "Vite",
|
||||
href: "/docs/installation/vite",
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: "Remix",
|
||||
href: "/docs/installation/remix",
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: "Gatsby",
|
||||
href: "/docs/installation/gatsby",
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: "Astro",
|
||||
href: "/docs/installation/astro",
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: "Manual",
|
||||
href: "/docs/installation/manual",
|
||||
items: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -168,6 +137,12 @@ export const docsConfig: DocsConfig = {
|
||||
href: "/docs/components/card",
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: "Carousel",
|
||||
href: "/docs/components/carousel",
|
||||
items: [],
|
||||
label: "New",
|
||||
},
|
||||
{
|
||||
title: "Checkbox",
|
||||
href: "/docs/components/checkbox",
|
||||
@@ -208,6 +183,12 @@ export const docsConfig: DocsConfig = {
|
||||
href: "/docs/components/dialog",
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: "Drawer",
|
||||
href: "/docs/components/drawer",
|
||||
items: [],
|
||||
label: "New",
|
||||
},
|
||||
{
|
||||
title: "Dropdown Menu",
|
||||
href: "/docs/components/dropdown-menu",
|
||||
@@ -243,6 +224,12 @@ export const docsConfig: DocsConfig = {
|
||||
href: "/docs/components/navigation-menu",
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: "Pagination",
|
||||
href: "/docs/components/pagination",
|
||||
items: [],
|
||||
label: "New",
|
||||
},
|
||||
{
|
||||
title: "Popover",
|
||||
href: "/docs/components/popover",
|
||||
@@ -258,6 +245,12 @@ export const docsConfig: DocsConfig = {
|
||||
href: "/docs/components/radio-group",
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: "Resizable",
|
||||
href: "/docs/components/resizable",
|
||||
items: [],
|
||||
label: "New",
|
||||
},
|
||||
{
|
||||
title: "Scroll Area",
|
||||
href: "/docs/components/scroll-area",
|
||||
@@ -288,6 +281,12 @@ export const docsConfig: DocsConfig = {
|
||||
href: "/docs/components/slider",
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: "Sonner",
|
||||
href: "/docs/components/sonner",
|
||||
items: [],
|
||||
label: "New",
|
||||
},
|
||||
{
|
||||
title: "Switch",
|
||||
href: "/docs/components/switch",
|
||||
@@ -318,6 +317,11 @@ export const docsConfig: DocsConfig = {
|
||||
href: "/docs/components/toggle",
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: "Toggle Group",
|
||||
href: "/docs/components/toggle-group",
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: "Tooltip",
|
||||
href: "/docs/components/tooltip",
|
||||
|
||||
@@ -3,10 +3,10 @@ export const siteConfig = {
|
||||
url: "https://ui.shadcn.com",
|
||||
ogImage: "https://ui.shadcn.com/og.jpg",
|
||||
description:
|
||||
"Beautifully designed components built with Radix UI and Tailwind CSS.",
|
||||
"Beautifully designed components that you can copy and paste into your apps. Accessible. Customizable. Open Source.",
|
||||
links: {
|
||||
twitter: "https://twitter.com/shadcn",
|
||||
github: "https://github.com/shadcn/ui",
|
||||
github: "https://github.com/shadcn-ui/ui",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -4,11 +4,76 @@ description: Latest updates and announcements.
|
||||
toc: false
|
||||
---
|
||||
|
||||
## December 2023 - New components, CLI and more
|
||||
|
||||
We've added new components to shadcn/ui and made a lot of improvements to the CLI.
|
||||
|
||||
Here's a quick overview of what's new:
|
||||
|
||||
- [**Carousel**](#carousel) - A carousel component with motion, swipe gestures and keyboard support.
|
||||
- [**Drawer**](#drawer) - A drawer component that looks amazing on mobile.
|
||||
- [**Pagination**](#pagination) - A pagination component with page navigation, previous and next buttons.
|
||||
- [**Resizable**](#resizable) - A resizable component for building resizable panel groups and layouts.
|
||||
- [**Sonner**](#sonner) - The last toast component you'll ever need.
|
||||
- [**CLI updates**](#cli-updates) - Support for custom **Tailwind prefix** and `tailwind.config.ts`.
|
||||
|
||||
### Carousel
|
||||
|
||||
We've added a fully featured carousel component with motion, swipe gestures and keyboard support. Built on top of [Embla Carousel](https://www.embla-carousel.com).
|
||||
|
||||
It has support for infinite looping, autoplay, vertical orientation, and more.
|
||||
|
||||
<ComponentPreview name="carousel-demo" />
|
||||
|
||||
### Drawer
|
||||
|
||||
Oh the drawer component 😍. Built on top of [Vaul](https://github.com/emilkowalski/vaul) by [emilkowalski\_](https://twitter.com/emilkowalski_).
|
||||
|
||||
Try opening the following drawer on mobile. It looks amazing!
|
||||
|
||||
<ComponentPreview name="drawer-demo" />
|
||||
|
||||
### Pagination
|
||||
|
||||
We've added a pagination component with page navigation, previous and next buttons. Simple, flexible and works with your framework's `<Link />` component.
|
||||
|
||||
<ComponentPreview name="pagination-demo" />
|
||||
|
||||
### Resizable
|
||||
|
||||
Build resizable panel groups and layouts with this `<Resizable />` component.
|
||||
|
||||
<ComponentPreview name="resizable-demo-with-handle" />
|
||||
|
||||
`<Resizable />` is built using [react-resizable-panels](https://github.com/bvaughn/react-resizable-panels) by [bvaughn](https://github.com/bvaughn). It has support for mouse, touch and keyboard.
|
||||
|
||||
### Sonner
|
||||
|
||||
Another one by [emilkowalski\_](https://twitter.com/emilkowalski_). The last toast component you'll ever need. Sonner is now availabe in shadcn/ui.
|
||||
|
||||
<ComponentPreview name="sonner-demo" />
|
||||
|
||||
### CLI updates
|
||||
|
||||
This has been one of the most requested features. You can now configure a custom Tailwind prefix and the cli will automatically prefix your utility classes when adding components.
|
||||
|
||||
This means you can now easily add shadcn/ui components to existing projects like Docusaurus, Nextra...etc. A drop-in for your existing design system with no conflict. 🔥
|
||||
|
||||
```tsx /tw-/
|
||||
<AlertDialog className="tw-grid tw-gap-4 tw-border tw-bg-background tw-shadow-lg" />
|
||||
```
|
||||
|
||||
It works with `cn`, `cva` and CSS variables.
|
||||
|
||||
The cli can now also detect `tailwind.config.ts` and add the TypeScript version of the config for you.
|
||||
|
||||
That's it. Happy Holidays.
|
||||
|
||||
## July 2023 - JavaScript
|
||||
|
||||
This project and the components are written in TypeScript. We recommend using TypeScript for your project as well.
|
||||
This project and the components are written in TypeScript. **We recommend using TypeScript for your project as well**.
|
||||
|
||||
However we provide a JavaScript version of the components as well. The JavaScript version is available via the [cli](/docs/cli).
|
||||
However we provide a JavaScript version of the components, available via the [cli](/docs/cli).
|
||||
|
||||
```txt
|
||||
Would you like to use TypeScript (recommended)? no
|
||||
|
||||
@@ -16,6 +16,7 @@ npx shadcn-ui@latest init
|
||||
You will be asked a few questions to configure `components.json`:
|
||||
|
||||
```txt showLineNumbers
|
||||
Would you like to use TypeScript (recommended)? no/yes
|
||||
Which style would you like to use? › Default
|
||||
Which color would you like to use as base color? › Slate
|
||||
Where is your global CSS file? › › app/globals.css
|
||||
@@ -50,7 +51,8 @@ npx shadcn-ui@latest add [component]
|
||||
You will be presented with a list of components to choose from:
|
||||
|
||||
```txt
|
||||
Which components would you like to add? › Space to select. Return to submit.
|
||||
Which components would you like to add? › Space to select. A to toggle all.
|
||||
Enter to submit.
|
||||
|
||||
◯ accordion
|
||||
◯ alert
|
||||
|
||||
189
apps/www/content/docs/components-json.mdx
Normal file
189
apps/www/content/docs/components-json.mdx
Normal file
@@ -0,0 +1,189 @@
|
||||
---
|
||||
title: components.json
|
||||
description: Configuration for your project.
|
||||
---
|
||||
|
||||
The `components.json` file holds configuration for your project.
|
||||
|
||||
We use it to understand how your project is set up and how to generate components customized for your project.
|
||||
|
||||
<Callout className="mt-6">
|
||||
Note: The `components.json` file is optional and **only required if you're
|
||||
using the CLI** to add components to your project. If you're using the copy
|
||||
and paste method, you don't need this file.
|
||||
</Callout>
|
||||
|
||||
You can create a `components.json` file in your project by running the following command:
|
||||
|
||||
```bash
|
||||
npx shadcn-ui@latest init
|
||||
```
|
||||
|
||||
See the <Link href="/docs/cli">CLI section</Link> for more information.
|
||||
|
||||
## $schema
|
||||
|
||||
You can see the JSON Schema for `components.json` [here](https://ui.shadcn.com/schema.json).
|
||||
|
||||
```json title="components.json"
|
||||
{
|
||||
"$schema": "https://ui.shadcn.com/schema.json"
|
||||
}
|
||||
```
|
||||
|
||||
## style
|
||||
|
||||
The style for your components. **This cannot be changed after initialization.**
|
||||
|
||||
```json title="components.json"
|
||||
{
|
||||
"style": "default" | "new-york"
|
||||
}
|
||||
```
|
||||
|
||||
<ComponentPreview name="card-with-form" />
|
||||
|
||||
## tailwind
|
||||
|
||||
Configuration to help the CLI understand how Tailwind CSS is set up in your project.
|
||||
|
||||
See the <Link href="/docs/installation">installation section</Link> for how to set up Tailwind CSS.
|
||||
|
||||
### tailwind.config
|
||||
|
||||
Path to where your `tailwind.config.js` file is located.
|
||||
|
||||
```json title="components.json"
|
||||
{
|
||||
"tailwind": {
|
||||
"config": "tailwind.config.js" | "tailwind.config.ts"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### tailwind.css
|
||||
|
||||
Path to the CSS file that imports Tailwind CSS into your project.
|
||||
|
||||
```json title="components.json"
|
||||
{
|
||||
"tailwind": {
|
||||
"css": "styles/global.css"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### tailwind.baseColor
|
||||
|
||||
This is used to generate the default color palette for your components. **This cannot be changed after initialization.**
|
||||
|
||||
```json title="components.json"
|
||||
{
|
||||
"tailwind": {
|
||||
"baseColor": "gray" | "neutral" | "slate" | "stone" | "zinc"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### tailwind.cssVariables
|
||||
|
||||
You can choose between using CSS variables or Tailwind CSS utility classes for theming.
|
||||
|
||||
To use utility classes for theming set `tailwind.cssVariables` to `false`. For CSS variables, set `tailwind.cssVariables` to `true`.
|
||||
|
||||
```json title="components.json"
|
||||
{
|
||||
"tailwind": {
|
||||
"cssVariables": `true` | `false`
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
For more information, see the <Link href="/docs/theming">theming docs</Link>.
|
||||
|
||||
**This cannot be changed after initialization.** To switch between CSS variables and utility classes, you'll have to delete and re-install your components.
|
||||
|
||||
### tailwind.prefix
|
||||
|
||||
The prefix to use for your Tailwind CSS utility classes. Components will be added with this prefix.
|
||||
|
||||
```json title="components.json"
|
||||
{
|
||||
"tailwind": {
|
||||
"prefix": "tw-"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## rsc
|
||||
|
||||
Whether or not to enable support for React Server Components.
|
||||
|
||||
The CLI automatically adds a `use client` directive to client components when set to `true`.
|
||||
|
||||
```json title="components.json"
|
||||
{
|
||||
"rsc": `true` | `false`
|
||||
}
|
||||
```
|
||||
|
||||
## tsx
|
||||
|
||||
Choose between TypeScript or JavaScript components.
|
||||
|
||||
Setting this option to `false` allows components to be added as JavaScript with the `.jsx` file extension.
|
||||
|
||||
```json title="components.json"
|
||||
{
|
||||
"tsx": `true` | `false`
|
||||
}
|
||||
```
|
||||
|
||||
## aliases
|
||||
|
||||
The CLI uses these values and the `paths` config from your `tsconfig.json` or `jsconfig.json` file to place generated components in the correct location.
|
||||
|
||||
Path aliases have to be set up in your `tsconfig.json` or `jsconfig.json` file.
|
||||
|
||||
<Callout className="mt-6">
|
||||
**Important:** If you're using the `src` directory, make sure it is included
|
||||
under `paths` in your `tsconfig.json` or `jsconfig.json` file.
|
||||
</Callout>
|
||||
|
||||
### aliases.utils
|
||||
|
||||
Import alias for your utility functions.
|
||||
|
||||
```json title="components.json"
|
||||
{
|
||||
"aliases": {
|
||||
"utils": "@/lib/utils"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### aliases.components
|
||||
|
||||
Import alias for your components.
|
||||
|
||||
```json title="components.json"
|
||||
{
|
||||
"aliases": {
|
||||
"components": "@/components"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### aliases.ui
|
||||
|
||||
Import alias for `ui` components.
|
||||
|
||||
The CLI will use the `aliases.ui` value to determine where to place your `ui` components. Use this config if you want to customize the installation directory for your `ui` components.
|
||||
|
||||
```json title="components.json"
|
||||
{
|
||||
"aliases": {
|
||||
"ui": "@/app/ui"
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -2,8 +2,8 @@
|
||||
title: Accordion
|
||||
description: A vertically stacked set of interactive headings that each reveal a section of content.
|
||||
component: true
|
||||
radix:
|
||||
link: https://www.radix-ui.com/docs/primitives/components/accordion
|
||||
links:
|
||||
doc: https://www.radix-ui.com/docs/primitives/components/accordion
|
||||
api: https://www.radix-ui.com/docs/primitives/components/accordion#api-reference
|
||||
---
|
||||
|
||||
@@ -42,12 +42,12 @@ module.exports = {
|
||||
extend: {
|
||||
keyframes: {
|
||||
"accordion-down": {
|
||||
from: { height: 0 },
|
||||
from: { height: "0" },
|
||||
to: { height: "var(--radix-accordion-content-height)" },
|
||||
},
|
||||
"accordion-up": {
|
||||
from: { height: "var(--radix-accordion-content-height)" },
|
||||
to: { height: 0 },
|
||||
to: { height: "0" },
|
||||
},
|
||||
},
|
||||
animation: {
|
||||
@@ -90,12 +90,12 @@ module.exports = {
|
||||
extend: {
|
||||
keyframes: {
|
||||
"accordion-down": {
|
||||
from: { height: 0 },
|
||||
from: { height: "0" },
|
||||
to: { height: "var(--radix-accordion-content-height)" },
|
||||
},
|
||||
"accordion-up": {
|
||||
from: { height: "var(--radix-accordion-content-height)" },
|
||||
to: { height: 0 },
|
||||
to: { height: "0" },
|
||||
},
|
||||
},
|
||||
animation: {
|
||||
|
||||
@@ -3,8 +3,8 @@ title: Alert Dialog
|
||||
description: A modal dialog that interrupts the user with important content and expects a response.
|
||||
featured: true
|
||||
component: true
|
||||
radix:
|
||||
link: https://www.radix-ui.com/docs/primitives/components/alert-dialog
|
||||
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
|
||||
---
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
title: Aspect Ratio
|
||||
description: Displays content within a desired ratio.
|
||||
component: true
|
||||
radix:
|
||||
link: https://www.radix-ui.com/docs/primitives/components/aspect-ratio
|
||||
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
|
||||
---
|
||||
|
||||
@@ -58,11 +58,7 @@ import { AspectRatio } from "@/components/ui/aspect-ratio"
|
||||
```tsx
|
||||
<div className="w-[450px]">
|
||||
<AspectRatio ratio={16 / 9}>
|
||||
<Image
|
||||
src="..."
|
||||
alt=Image""
|
||||
className="rounded-md object-cover"
|
||||
/>
|
||||
<Image src="..." alt="Image" className="rounded-md object-cover" />
|
||||
</AspectRatio>
|
||||
</div>
|
||||
```
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
title: Avatar
|
||||
description: An image element with a fallback for representing the user.
|
||||
component: true
|
||||
radix:
|
||||
link: https://www.radix-ui.com/docs/primitives/components/avatar
|
||||
links:
|
||||
doc: https://www.radix-ui.com/docs/primitives/components/avatar
|
||||
api: https://www.radix-ui.com/docs/primitives/components/avatar#api-reference
|
||||
---
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
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
|
||||
---
|
||||
|
||||
<ComponentPreview name="calendar-demo" />
|
||||
|
||||
277
apps/www/content/docs/components/carousel.mdx
Normal file
277
apps/www/content/docs/components/carousel.mdx
Normal file
@@ -0,0 +1,277 @@
|
||||
---
|
||||
title: Carousel
|
||||
description: A carousel with motion and swipe built using Embla.
|
||||
component: true
|
||||
links:
|
||||
doc: https://www.embla-carousel.com/get-started/react
|
||||
api: https://www.embla-carousel.com/api
|
||||
---
|
||||
|
||||
<ComponentPreview name="carousel-demo" />
|
||||
|
||||
## About
|
||||
|
||||
The carousel component is built using the [Embla Carousel](https://www.embla-carousel.com/) library.
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs defaultValue="cli">
|
||||
|
||||
<TabsList>
|
||||
<TabsTrigger value="cli">CLI</TabsTrigger>
|
||||
<TabsTrigger value="manual">Manual</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="cli">
|
||||
|
||||
```bash
|
||||
npx shadcn-ui@latest add carousel
|
||||
```
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="manual">
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step>Install the following dependencies:</Step>
|
||||
|
||||
```bash
|
||||
npm install embla-carousel-react
|
||||
```
|
||||
|
||||
<Step>Copy and paste the following code into your project.</Step>
|
||||
|
||||
<ComponentSource name="carousel" />
|
||||
|
||||
<Step>Update the import paths to match your project setup.</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
</Tabs>
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import {
|
||||
Carousel,
|
||||
CarouselContent,
|
||||
CarouselItem,
|
||||
CarouselNext,
|
||||
CarouselPrevious,
|
||||
} from "@/components/ui/carousel"
|
||||
```
|
||||
|
||||
```tsx
|
||||
<Carousel>
|
||||
<CarouselContent>
|
||||
<CarouselItem>...</CarouselItem>
|
||||
<CarouselItem>...</CarouselItem>
|
||||
<CarouselItem>...</CarouselItem>
|
||||
</CarouselContent>
|
||||
<CarouselPrevious />
|
||||
<CarouselNext />
|
||||
</Carousel>
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Sizes
|
||||
|
||||
To set the size of the items, you can use the `basis` utility class on the `<CarouselItem />`.
|
||||
|
||||
<ComponentPreview name="carousel-size" />
|
||||
|
||||
```tsx title="Example" showLineNumbers {4-6}
|
||||
// 33% of the carousel width.
|
||||
<Carousel>
|
||||
<CarouselContent>
|
||||
<CarouselItem className="basis-1/3">...</CarouselItem>
|
||||
<CarouselItem className="basis-1/3">...</CarouselItem>
|
||||
<CarouselItem className="basis-1/3">...</CarouselItem>
|
||||
</CarouselContent>
|
||||
</Carousel>
|
||||
```
|
||||
|
||||
```tsx title="Responsive" showLineNumbers {4-6}
|
||||
// 50% on small screens and 33% on larger screens.
|
||||
<Carousel>
|
||||
<CarouselContent>
|
||||
<CarouselItem className="md:basis-1/2 lg:basis-1/3">...</CarouselItem>
|
||||
<CarouselItem className="md:basis-1/2 lg:basis-1/3">...</CarouselItem>
|
||||
<CarouselItem className="md:basis-1/2 lg:basis-1/3">...</CarouselItem>
|
||||
</CarouselContent>
|
||||
</Carousel>
|
||||
```
|
||||
|
||||
### Spacing
|
||||
|
||||
To set the spacing between the items, we use a `pl-[VALUE]` utility on the `<CarouselItem />` and a negative `-ml-[VALUE]` on the `<CarouselContent />`.
|
||||
|
||||
<Callout className="mt-6">
|
||||
**Why:** I tried to use the `gap` property or a `grid` layout on the `
|
||||
<CarouselContent />` but it required a lot of math and mental effort to get the
|
||||
spacing right. I found `pl-[VALUE]` and `-ml-[VALUE]` utilities much easier to
|
||||
use.
|
||||
|
||||
You can always adjust this in your own project if you need to.
|
||||
|
||||
</Callout>
|
||||
|
||||
<ComponentPreview name="carousel-spacing" />
|
||||
|
||||
```tsx title="Example" showLineNumbers /-ml-4/ /pl-4/
|
||||
<Carousel>
|
||||
<CarouselContent className="-ml-4">
|
||||
<CarouselItem className="pl-4">...</CarouselItem>
|
||||
<CarouselItem className="pl-4">...</CarouselItem>
|
||||
<CarouselItem className="pl-4">...</CarouselItem>
|
||||
</CarouselContent>
|
||||
</Carousel>
|
||||
```
|
||||
|
||||
```tsx title="Responsive" showLineNumbers /-ml-2/ /pl-2/ /md:-ml-4/ /md:pl-4/
|
||||
<Carousel>
|
||||
<CarouselContent className="-ml-2 md:-ml-4">
|
||||
<CarouselItem className="pl-2 md:pl-4">...</CarouselItem>
|
||||
<CarouselItem className="pl-2 md:pl-4">...</CarouselItem>
|
||||
<CarouselItem className="pl-2 md:pl-4">...</CarouselItem>
|
||||
</CarouselContent>
|
||||
</Carousel>
|
||||
```
|
||||
|
||||
### Orientation
|
||||
|
||||
Use the `orientation` prop to set the orientation of the carousel.
|
||||
|
||||
<ComponentPreview name="carousel-orientation" />
|
||||
|
||||
```tsx showLineNumbers /vertical | horizontal/
|
||||
<Carousel orientation="vertical | horizontal">
|
||||
<CarouselContent>
|
||||
<CarouselItem>...</CarouselItem>
|
||||
<CarouselItem>...</CarouselItem>
|
||||
<CarouselItem>...</CarouselItem>
|
||||
</CarouselContent>
|
||||
</Carousel>
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
You can pass options to the carousel using the `opts` prop. See the [Embla Carousel docs](https://www.embla-carousel.com/api/options/) for more information.
|
||||
|
||||
```tsx showLineNumbers {2-5}
|
||||
<Carousel
|
||||
opts={{
|
||||
align: "start",
|
||||
loop: true,
|
||||
}}
|
||||
>
|
||||
<CarouselContent>
|
||||
<CarouselItem>...</CarouselItem>
|
||||
<CarouselItem>...</CarouselItem>
|
||||
<CarouselItem>...</CarouselItem>
|
||||
</CarouselContent>
|
||||
</Carousel>
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
Use a state and the `setApi` props to get an instance of the carousel API.
|
||||
|
||||
<ComponentPreview name="carousel-api" />
|
||||
|
||||
```tsx showLineNumbers {1,4,22}
|
||||
import { type CarouselApi } from "@/components/ui/carousel"
|
||||
|
||||
export function Example() {
|
||||
const [api, setApi] = React.useState<CarouselApi>()
|
||||
const [current, setCurrent] = React.useState(0)
|
||||
const [count, setCount] = React.useState(0)
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!api) {
|
||||
return
|
||||
}
|
||||
|
||||
setCount(api.scrollSnapList().length)
|
||||
setCurrent(api.selectedScrollSnap() + 1)
|
||||
|
||||
api.on("select", () => {
|
||||
setCurrent(api.selectedScrollSnap() + 1)
|
||||
})
|
||||
}, [api])
|
||||
|
||||
return (
|
||||
<Carousel setApi={setApi}>
|
||||
<CarouselContent>
|
||||
<CarouselItem>...</CarouselItem>
|
||||
<CarouselItem>...</CarouselItem>
|
||||
<CarouselItem>...</CarouselItem>
|
||||
</CarouselContent>
|
||||
</Carousel>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## Events
|
||||
|
||||
You can listen to events using the api instance from `setApi`.
|
||||
|
||||
```tsx showLineNumbers {1,4-14,16}
|
||||
import { type CarouselApi } from "@/components/ui/carousel"
|
||||
|
||||
export function Example() {
|
||||
const [api, setApi] = React.useState<CarouselApi>()
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!api) {
|
||||
return
|
||||
}
|
||||
|
||||
api.on("select", () => {
|
||||
// Do something on select.
|
||||
})
|
||||
}, [api])
|
||||
|
||||
return (
|
||||
<Carousel setApi={setApi}>
|
||||
<CarouselContent>
|
||||
<CarouselItem>...</CarouselItem>
|
||||
<CarouselItem>...</CarouselItem>
|
||||
<CarouselItem>...</CarouselItem>
|
||||
</CarouselContent>
|
||||
</Carousel>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
See the [Embla Carousel docs](https://www.embla-carousel.com/api/events/) for more information on using events.
|
||||
|
||||
## Plugins
|
||||
|
||||
You can use the `plugins` prop to add plugins to the carousel.
|
||||
|
||||
```ts showLineNumbers {1,6-10}
|
||||
import Autoplay from "embla-carousel-autoplay"
|
||||
|
||||
export function Example() {
|
||||
return (
|
||||
<Carousel
|
||||
plugins={[
|
||||
Autoplay({
|
||||
delay: 2000,
|
||||
}),
|
||||
]}
|
||||
>
|
||||
// ...
|
||||
</Carousel>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
<ComponentPreview name="carousel-plugin" />
|
||||
|
||||
See the [Embla Carousel docs](https://www.embla-carousel.com/api/plugins/) for more information on using plugins.
|
||||
@@ -2,8 +2,8 @@
|
||||
title: Checkbox
|
||||
description: A control that allows the user to toggle between checked and not checked.
|
||||
component: true
|
||||
radix:
|
||||
link: https://www.radix-ui.com/docs/primitives/components/checkbox
|
||||
links:
|
||||
doc: https://www.radix-ui.com/docs/primitives/components/checkbox
|
||||
api: https://www.radix-ui.com/docs/primitives/components/checkbox#api-reference
|
||||
---
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@ title: Collapsible
|
||||
description: An interactive component which expands/collapses a panel.
|
||||
component: true
|
||||
featured: true
|
||||
radix:
|
||||
link: https://www.radix-ui.com/docs/primitives/components/collapsible
|
||||
links:
|
||||
doc: https://www.radix-ui.com/docs/primitives/components/collapsible
|
||||
api: https://www.radix-ui.com/docs/primitives/components/collapsible#api-reference
|
||||
---
|
||||
|
||||
|
||||
@@ -85,6 +85,7 @@ export function ComboboxDemo() {
|
||||
{frameworks.map((framework) => (
|
||||
<CommandItem
|
||||
key={framework.value}
|
||||
value={framework.value}
|
||||
onSelect={(currentValue) => {
|
||||
setValue(currentValue === value ? "" : currentValue)
|
||||
setOpen(false)
|
||||
@@ -121,6 +122,12 @@ export function ComboboxDemo() {
|
||||
|
||||
<ComponentPreview name="combobox-dropdown-menu" />
|
||||
|
||||
### Responsive
|
||||
|
||||
You can create a responsive combobox by using the `<Popover />` on desktop and the `<Drawer />` components on mobile.
|
||||
|
||||
<ComponentPreview name="combobox-responsive" />
|
||||
|
||||
### Form
|
||||
|
||||
<ComponentPreview name="combobox-form" />
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
title: Command
|
||||
description: Fast, composable, unstyled command menu for React.
|
||||
component: true
|
||||
links:
|
||||
doc: https://cmdk.paco.me
|
||||
---
|
||||
|
||||
<ComponentPreview
|
||||
@@ -56,6 +58,7 @@ npm install cmdk
|
||||
|
||||
```tsx
|
||||
import {
|
||||
Command,
|
||||
CommandDialog,
|
||||
CommandEmpty,
|
||||
CommandGroup,
|
||||
@@ -101,7 +104,8 @@ export function CommandMenu() {
|
||||
|
||||
React.useEffect(() => {
|
||||
const down = (e: KeyboardEvent) => {
|
||||
if (e.key === "k" && e.metaKey) {
|
||||
if (e.key === "k" && (e.metaKey || e.ctrlKey)) {
|
||||
e.preventDefault()
|
||||
setOpen((open) => !open)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
title: Context Menu
|
||||
description: Displays a menu to the user — such as a set of actions or functions — triggered by a button.
|
||||
component: true
|
||||
radix:
|
||||
link: https://www.radix-ui.com/docs/primitives/components/context-menu
|
||||
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
|
||||
---
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
title: Data Table
|
||||
description: Powerful table and datagrids built using TanStack Table.
|
||||
component: true
|
||||
links:
|
||||
doc: https://tanstack.com/table/v8/docs/guide/introduction
|
||||
---
|
||||
|
||||
<ComponentPreview name="data-table-demo" />
|
||||
@@ -746,7 +748,10 @@ export const columns: ColumnDef<Payment>[] = [
|
||||
id: "select",
|
||||
header: ({ table }) => (
|
||||
<Checkbox
|
||||
checked={table.getIsAllPageRowsSelected()}
|
||||
checked={
|
||||
table.getIsAllPageRowsSelected() ||
|
||||
(table.getIsSomePageRowsSelected() && "indeterminate")
|
||||
}
|
||||
onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
|
||||
aria-label="Select all"
|
||||
/>
|
||||
|
||||
@@ -3,8 +3,8 @@ title: Dialog
|
||||
description: A window overlaid on either the primary window or another dialog window, rendering the content underneath inert.
|
||||
featured: true
|
||||
component: true
|
||||
radix:
|
||||
link: https://www.radix-ui.com/docs/primitives/components/dialog
|
||||
links:
|
||||
doc: https://www.radix-ui.com/docs/primitives/components/dialog
|
||||
api: https://www.radix-ui.com/docs/primitives/components/dialog#api-reference
|
||||
---
|
||||
|
||||
@@ -66,7 +66,7 @@ import {
|
||||
<DialogTrigger>Open</DialogTrigger>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Are you sure absolutely sure?</DialogTitle>
|
||||
<DialogTitle>Are you absolutely sure?</DialogTitle>
|
||||
<DialogDescription>
|
||||
This action cannot be undone. This will permanently delete your account
|
||||
and remove your data from our servers.
|
||||
@@ -76,6 +76,12 @@ import {
|
||||
</Dialog>
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Custom close button
|
||||
|
||||
<ComponentPreview name="dialog-close-button" />
|
||||
|
||||
## Notes
|
||||
|
||||
To activate the `Dialog` component from within a `Context Menu` or `Dropdown Menu`, you must encase the `Context Menu` or
|
||||
@@ -97,7 +103,7 @@ To activate the `Dialog` component from within a `Context Menu` or `Dropdown Men
|
||||
</ContextMenu>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Are you sure absolutely sure?</DialogTitle>
|
||||
<DialogTitle>Are you absolutely sure?</DialogTitle>
|
||||
<DialogDescription>
|
||||
This action cannot be undone. Are you sure you want to permanently
|
||||
delete this file from our servers?
|
||||
|
||||
92
apps/www/content/docs/components/drawer.mdx
Normal file
92
apps/www/content/docs/components/drawer.mdx
Normal file
@@ -0,0 +1,92 @@
|
||||
---
|
||||
title: Drawer
|
||||
description: A drawer component for React.
|
||||
component: true
|
||||
links:
|
||||
doc: https://github.com/emilkowalski/vaul
|
||||
---
|
||||
|
||||
<ComponentPreview name="drawer-demo" />
|
||||
|
||||
## About
|
||||
|
||||
Drawer is built on top of [Vaul](https://github.com/emilkowalski/vaul) by [emilkowalski\_](https://twitter.com/emilkowalski_).
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs defaultValue="cli">
|
||||
|
||||
<TabsList>
|
||||
<TabsTrigger value="cli">CLI</TabsTrigger>
|
||||
<TabsTrigger value="manual">Manual</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="cli">
|
||||
|
||||
```bash
|
||||
npx shadcn-ui@latest add drawer
|
||||
```
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="manual">
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step>Install the following dependencies:</Step>
|
||||
|
||||
```bash
|
||||
npm install vaul
|
||||
```
|
||||
|
||||
<Step>Copy and paste the following code into your project.</Step>
|
||||
|
||||
<ComponentSource name="drawer" />
|
||||
|
||||
<Step>Update the import paths to match your project setup.</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
</Tabs>
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx showLineNumbers
|
||||
import {
|
||||
Drawer,
|
||||
DrawerClose,
|
||||
DrawerContent,
|
||||
DrawerDescription,
|
||||
DrawerFooter,
|
||||
DrawerHeader,
|
||||
DrawerTitle,
|
||||
DrawerTrigger,
|
||||
} from "@/components/ui/drawer"
|
||||
```
|
||||
|
||||
```tsx showLineNumbers
|
||||
<Drawer>
|
||||
<DrawerTrigger>Open</DrawerTrigger>
|
||||
<DrawerContent>
|
||||
<DrawerHeader>
|
||||
<DrawerTitle>Are you absolutely sure?</DrawerTitle>
|
||||
<DrawerDescription>This action cannot be undone.</DrawerDescription>
|
||||
</DrawerHeader>
|
||||
<DrawerFooter>
|
||||
<Button>Submit</Button>
|
||||
<DrawerClose>
|
||||
<Button variant="outline">Cancel</Button>
|
||||
</DrawerClose>
|
||||
</DrawerFooter>
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Responsive Dialog
|
||||
|
||||
You can combine the `Dialog` and `Drawer` components to create a responsive dialog. This renders a `Dialog` component on desktop and a `Drawer` on mobile.
|
||||
|
||||
<ComponentPreview name="drawer-dialog" />
|
||||
@@ -3,8 +3,8 @@ 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
|
||||
radix:
|
||||
link: https://www.radix-ui.com/docs/primitives/components/dropdown-menu
|
||||
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
|
||||
---
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
---
|
||||
title: React Hook Form
|
||||
description: Building forms with React Hook Form and Zod.
|
||||
links:
|
||||
doc: https://react-hook-form.com
|
||||
---
|
||||
|
||||
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.
|
||||
@@ -73,6 +75,14 @@ const form = useForm()
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs defaultValue="cli">
|
||||
|
||||
<TabsList>
|
||||
<TabsTrigger value="cli">CLI</TabsTrigger>
|
||||
<TabsTrigger value="manual">Manual</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="cli">
|
||||
|
||||
<Steps>
|
||||
|
||||
### Command
|
||||
@@ -83,6 +93,30 @@ npx shadcn-ui@latest add form
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="manual">
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step>Install the following dependencies:</Step>
|
||||
|
||||
```bash
|
||||
npm install @radix-ui/react-label @radix-ui/react-slot react-hook-form @hookform/resolvers zod
|
||||
```
|
||||
|
||||
<Step>Copy and paste the following code into your project.</Step>
|
||||
|
||||
<ComponentSource name="form" />
|
||||
|
||||
<Step>Update the import paths to match your project setup.</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
</Tabs>
|
||||
|
||||
## Usage
|
||||
|
||||
<Steps>
|
||||
@@ -91,10 +125,9 @@ npx shadcn-ui@latest add form
|
||||
|
||||
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 {4,6-8}
|
||||
```tsx showLineNumbers {3,5-7}
|
||||
"use client"
|
||||
|
||||
import Link from "next/link"
|
||||
import * as z from "zod"
|
||||
|
||||
const formSchema = z.object({
|
||||
@@ -106,11 +139,11 @@ const formSchema = z.object({
|
||||
|
||||
Use the `useForm` hook from `react-hook-form` to create a form.
|
||||
|
||||
```tsx showLineNumbers {4,14-20,22-27}
|
||||
```tsx showLineNumbers {3-4,14-20,22-27}
|
||||
"use client"
|
||||
|
||||
import Link from "next/link"
|
||||
import { zodResolver } from "@hookform/resolvers/zod"
|
||||
import { useForm } from "react-hook-form"
|
||||
import * as z from "zod"
|
||||
|
||||
const formSchema = z.object({
|
||||
@@ -146,8 +179,8 @@ We can now use the `<Form />` components to build our form.
|
||||
```tsx showLineNumbers {7-17,28-50}
|
||||
"use client"
|
||||
|
||||
import Link from "next/link"
|
||||
import { zodResolver } from "@hookform/resolvers/zod"
|
||||
import { useForm } from "react-hook-form"
|
||||
import * as z from "zod"
|
||||
|
||||
import { Button } from "@/components/ui/button"
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
title: Hover Card
|
||||
description: For sighted users to preview content available behind a link.
|
||||
component: true
|
||||
radix:
|
||||
link: https://www.radix-ui.com/docs/primitives/components/hover-card
|
||||
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
|
||||
---
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
title: Label
|
||||
description: Renders an accessible label associated with controls.
|
||||
component: true
|
||||
radix:
|
||||
link: https://www.radix-ui.com/docs/primitives/components/label
|
||||
links:
|
||||
doc: https://www.radix-ui.com/docs/primitives/components/label
|
||||
api: https://www.radix-ui.com/docs/primitives/components/label#api-reference
|
||||
---
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
title: Menubar
|
||||
description: A visually persistent menu common in desktop applications that provides quick access to a consistent set of commands.
|
||||
component: true
|
||||
radix:
|
||||
link: https://www.radix-ui.com/docs/primitives/components/menubar
|
||||
links:
|
||||
doc: https://www.radix-ui.com/docs/primitives/components/menubar
|
||||
api: https://www.radix-ui.com/docs/primitives/components/menubar#api-reference
|
||||
---
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
title: Navigation Menu
|
||||
description: A collection of links for navigating websites.
|
||||
component: true
|
||||
radix:
|
||||
link: https://www.radix-ui.com/docs/primitives/components/navigation-menu
|
||||
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
|
||||
---
|
||||
|
||||
|
||||
102
apps/www/content/docs/components/pagination.mdx
Normal file
102
apps/www/content/docs/components/pagination.mdx
Normal file
@@ -0,0 +1,102 @@
|
||||
---
|
||||
title: Pagination
|
||||
description: Pagination with page navigation, next and previous links.
|
||||
component: true
|
||||
---
|
||||
|
||||
<ComponentPreview name="pagination-demo" />
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs defaultValue="cli">
|
||||
|
||||
<TabsList>
|
||||
<TabsTrigger value="cli">CLI</TabsTrigger>
|
||||
<TabsTrigger value="manual">Manual</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="cli">
|
||||
|
||||
```bash
|
||||
npx shadcn-ui@latest add pagination
|
||||
```
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="manual">
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step>Copy and paste the following code into your project.</Step>
|
||||
|
||||
<ComponentSource name="pagination" />
|
||||
|
||||
<Step>Update the import paths to match your project setup.</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
</Tabs>
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import {
|
||||
Pagination,
|
||||
PaginationContent,
|
||||
PaginationEllipsis,
|
||||
PaginationItem,
|
||||
PaginationLink,
|
||||
PaginationNext,
|
||||
PaginationPrevious,
|
||||
} from "@/components/ui/pagination"
|
||||
```
|
||||
|
||||
```tsx
|
||||
<Pagination>
|
||||
<PaginationContent>
|
||||
<PaginationItem>
|
||||
<PaginationPrevious href="#" />
|
||||
</PaginationItem>
|
||||
<PaginationItem>
|
||||
<PaginationLink href="#">1</PaginationLink>
|
||||
</PaginationItem>
|
||||
<PaginationItem>
|
||||
<PaginationEllipsis />
|
||||
</PaginationItem>
|
||||
<PaginationItem>
|
||||
<PaginationNext href="#" />
|
||||
</PaginationItem>
|
||||
</PaginationContent>
|
||||
</Pagination>
|
||||
```
|
||||
|
||||
### Next.js
|
||||
|
||||
By default the `<PaginationLink />` component will render an `<a />` tag.
|
||||
|
||||
To use the Next.js `<Link />` component, make the following updates to `pagination.tsx`.
|
||||
|
||||
```diff showLineNumbers /typeof Link/ {1}
|
||||
+ import Link from "next/link"
|
||||
|
||||
- type PaginationLinkProps = ... & React.ComponentProps<"a">
|
||||
+ type PaginationLinkProps = ... & React.ComponentProps<typeof Link>
|
||||
|
||||
const PaginationLink = ({...props }: ) => (
|
||||
<PaginationItem>
|
||||
- <a>
|
||||
+ <Link>
|
||||
// ...
|
||||
- </a>
|
||||
+ </Link>
|
||||
</PaginationItem>
|
||||
)
|
||||
|
||||
```
|
||||
|
||||
<Callout className="mt-6">
|
||||
|
||||
**Note:** We are making updates to the cli to automatically do this for you.
|
||||
|
||||
</Callout>
|
||||
@@ -2,8 +2,8 @@
|
||||
title: Popover
|
||||
description: Displays rich content in a portal, triggered by a button.
|
||||
component: true
|
||||
radix:
|
||||
link: https://www.radix-ui.com/docs/primitives/components/popover
|
||||
links:
|
||||
doc: https://www.radix-ui.com/docs/primitives/components/popover
|
||||
api: https://www.radix-ui.com/docs/primitives/components/popover#api-reference
|
||||
---
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
title: Progress
|
||||
description: Displays an indicator showing the completion progress of a task, typically displayed as a progress bar.
|
||||
component: true
|
||||
radix:
|
||||
link: https://www.radix-ui.com/docs/primitives/components/progress
|
||||
links:
|
||||
doc: https://www.radix-ui.com/docs/primitives/components/progress
|
||||
api: https://www.radix-ui.com/docs/primitives/components/progress#api-reference
|
||||
---
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
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
|
||||
radix:
|
||||
link: https://www.radix-ui.com/docs/primitives/components/radio-group
|
||||
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
|
||||
---
|
||||
|
||||
|
||||
120
apps/www/content/docs/components/resizable.mdx
Normal file
120
apps/www/content/docs/components/resizable.mdx
Normal file
@@ -0,0 +1,120 @@
|
||||
---
|
||||
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
|
||||
---
|
||||
|
||||
<ComponentPreview name="resizable-demo" />
|
||||
|
||||
## About
|
||||
|
||||
The `Resizable` component is built on top of [react-resizable-panels](https://github.com/bvaughn/react-resizable-panels) by [bvaughn](https://github.com/bvaughn).
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs defaultValue="cli">
|
||||
|
||||
<TabsList>
|
||||
<TabsTrigger value="cli">CLI</TabsTrigger>
|
||||
<TabsTrigger value="manual">Manual</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="cli">
|
||||
|
||||
```bash
|
||||
npx shadcn-ui@latest add resizable
|
||||
```
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="manual">
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step>Install the following dependencies:</Step>
|
||||
|
||||
```bash
|
||||
npm install react-resizable-panels
|
||||
```
|
||||
|
||||
<Step>Copy and paste the following code into your project.</Step>
|
||||
|
||||
<ComponentSource name="resizable" />
|
||||
|
||||
<Step>Update the import paths to match your project setup.</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
</Tabs>
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import {
|
||||
ResizableHandle,
|
||||
ResizablePanel,
|
||||
ResizablePanelGroup,
|
||||
} from "@/components/ui/resizable"
|
||||
```
|
||||
|
||||
```tsx
|
||||
<ResizablePanelGroup direction="horizontal">
|
||||
<ResizablePanel>One</ResizablePanel>
|
||||
<ResizableHandle />
|
||||
<ResizablePanel>Two</ResizablePanel>
|
||||
</ResizablePanelGroup>
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Vertical
|
||||
|
||||
Use the `direction` prop to set the direction of the resizable panels.
|
||||
|
||||
<ComponentPreview name="resizable-vertical" />
|
||||
|
||||
```tsx showLineNumbers {9}
|
||||
import {
|
||||
ResizableHandle,
|
||||
ResizablePanel,
|
||||
ResizablePanelGroup,
|
||||
} from "@/components/ui/resizable"
|
||||
|
||||
export default function Example() {
|
||||
return (
|
||||
<ResizablePanelGroup direction="vertical">
|
||||
<ResizablePanel>One</ResizablePanel>
|
||||
<ResizableHandle />
|
||||
<ResizablePanel>Two</ResizablePanel>
|
||||
</ResizablePanelGroup>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### Handle
|
||||
|
||||
You can set or hide the handle by using the `withHandle` prop on the `ResizableHandle` component.
|
||||
|
||||
<ComponentPreview name="resizable-handle" />
|
||||
|
||||
```tsx showLineNumbers {11}
|
||||
import {
|
||||
ResizableHandle,
|
||||
ResizablePanel,
|
||||
ResizablePanelGroup,
|
||||
} from "@/components/ui/resizable"
|
||||
|
||||
export default function Example() {
|
||||
return (
|
||||
<ResizablePanelGroup direction="horizontal">
|
||||
<ResizablePanel>One</ResizablePanel>
|
||||
<ResizableHandle withHandle />
|
||||
<ResizablePanel>Two</ResizablePanel>
|
||||
</ResizablePanelGroup>
|
||||
)
|
||||
}
|
||||
```
|
||||
@@ -2,8 +2,8 @@
|
||||
title: Scroll-area
|
||||
description: Augments native scroll functionality for custom, cross-browser styling.
|
||||
component: true
|
||||
radix:
|
||||
link: https://www.radix-ui.com/docs/primitives/components/scroll-area
|
||||
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
|
||||
---
|
||||
|
||||
@@ -63,3 +63,9 @@ import { ScrollArea } from "@/components/ui/scroll-area"
|
||||
started laughing, they couldn't stop.
|
||||
</ScrollArea>
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Horizontal Scrolling
|
||||
|
||||
<ComponentPreview name="scroll-area-horizontal-demo" />
|
||||
|
||||
@@ -3,8 +3,8 @@ title: Select
|
||||
description: Displays a list of options for the user to pick from—triggered by a button.
|
||||
component: true
|
||||
featured: true
|
||||
radix:
|
||||
link: https://www.radix-ui.com/docs/primitives/components/select
|
||||
links:
|
||||
doc: https://www.radix-ui.com/docs/primitives/components/select
|
||||
api: https://www.radix-ui.com/docs/primitives/components/select#api-reference
|
||||
---
|
||||
|
||||
@@ -75,6 +75,10 @@ import {
|
||||
|
||||
## Examples
|
||||
|
||||
### Scrollable
|
||||
|
||||
<ComponentPreview name="select-scrollable" />
|
||||
|
||||
### Form
|
||||
|
||||
<ComponentPreview name="select-form" />
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
title: Separator
|
||||
description: Visually or semantically separates content.
|
||||
component: true
|
||||
radix:
|
||||
link: https://www.radix-ui.com/docs/primitives/components/separator
|
||||
links:
|
||||
doc: https://www.radix-ui.com/docs/primitives/components/separator
|
||||
api: https://www.radix-ui.com/docs/primitives/components/separator#api-reference
|
||||
---
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
title: Sheet
|
||||
description: Extends the Dialog component to display content that complements the main content of the screen.
|
||||
component: true
|
||||
radix:
|
||||
link: https://www.radix-ui.com/docs/primitives/components/dialog
|
||||
links:
|
||||
doc: https://www.radix-ui.com/docs/primitives/components/dialog
|
||||
api: https://www.radix-ui.com/docs/primitives/components/dialog#api-reference
|
||||
---
|
||||
|
||||
@@ -65,7 +65,7 @@ import {
|
||||
<SheetTrigger>Open</SheetTrigger>
|
||||
<SheetContent>
|
||||
<SheetHeader>
|
||||
<SheetTitle>Are you sure absolutely sure?</SheetTitle>
|
||||
<SheetTitle>Are you absolutely sure?</SheetTitle>
|
||||
<SheetDescription>
|
||||
This action cannot be undone. This will permanently delete your account
|
||||
and remove your data from our servers.
|
||||
@@ -92,7 +92,7 @@ You can adjust the size of the sheet using CSS classes:
|
||||
<SheetTrigger>Open</SheetTrigger>
|
||||
<SheetContent className="w-[400px] sm:w-[540px]">
|
||||
<SheetHeader>
|
||||
<SheetTitle>Are you sure absolutely sure?</SheetTitle>
|
||||
<SheetTitle>Are you absolutely sure?</SheetTitle>
|
||||
<SheetDescription>
|
||||
This action cannot be undone. This will permanently delete your account
|
||||
and remove your data from our servers.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
title: Slider
|
||||
description: An input where the user selects a value from within a given range.
|
||||
component: true
|
||||
radix:
|
||||
link: https://www.radix-ui.com/docs/primitives/components/slider
|
||||
links:
|
||||
doc: https://www.radix-ui.com/docs/primitives/components/slider
|
||||
api: https://www.radix-ui.com/docs/primitives/components/slider#api-reference
|
||||
---
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user