mirror of
https://github.com/shadcn-ui/ui.git
synced 2026-06-27 14:44:12 +00:00
Compare commits
133 Commits
shadcn-ui@
...
shadcn-ui@
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
25a41cfe2a | ||
|
|
edc653c01e | ||
|
|
f78a4aaa28 | ||
|
|
b4dda36cc9 | ||
|
|
cbe0f1959c | ||
|
|
dbd3b8f066 | ||
|
|
7abb4019c3 | ||
|
|
b14081631f | ||
|
|
0857bfe889 | ||
|
|
ea6699adbf | ||
|
|
1f004243d4 | ||
|
|
eee7ce6879 | ||
|
|
c3377530f4 | ||
|
|
a8bb2ef737 | ||
|
|
ab836d1ab3 | ||
|
|
77d6f5676e | ||
|
|
2846b2ea9a | ||
|
|
0f84973b4d | ||
|
|
f8348621f4 | ||
|
|
fbed50f4e8 | ||
|
|
1971fa7511 | ||
|
|
7b5582e5d0 | ||
|
|
2ca7476c9b | ||
|
|
aea12e9762 | ||
|
|
343acb3a51 | ||
|
|
cf139e5fa1 | ||
|
|
38fb9693d0 | ||
|
|
f1de3401a2 | ||
|
|
379d1560c3 | ||
|
|
d604b82fb3 | ||
|
|
658c710bce | ||
|
|
4ca9619efa | ||
|
|
eeb17545a1 | ||
|
|
3d717f992b | ||
|
|
042554ad07 | ||
|
|
71f496d41f | ||
|
|
0a5df3ac85 | ||
|
|
eb27529f50 | ||
|
|
dffbe89f7d | ||
|
|
22f23b7db3 | ||
|
|
d6d4017b95 | ||
|
|
00ecdfbb17 | ||
|
|
065ba02ae5 | ||
|
|
5dfc2020aa | ||
|
|
5aecccc586 | ||
|
|
97a444b210 | ||
|
|
060e896183 | ||
|
|
c4da22ffe9 | ||
|
|
f6b2d0c5dd | ||
|
|
3f01388389 | ||
|
|
c584f01163 | ||
|
|
588ebd79e4 | ||
|
|
36881682cf | ||
|
|
d1363515eb | ||
|
|
5afb8d530f | ||
|
|
6a5195498f | ||
|
|
7d8be94a01 | ||
|
|
a3c904dcc9 | ||
|
|
11447c9bff | ||
|
|
f9d399172c | ||
|
|
4ccff13f9c | ||
|
|
dbbdbe618f | ||
|
|
b09cff40ae | ||
|
|
df9369762a | ||
|
|
87ad14cb2a | ||
|
|
5a2ce61e2e | ||
|
|
4a7c07e754 | ||
|
|
8eb3e1e160 | ||
|
|
5bc68894b8 | ||
|
|
21890afa80 | ||
|
|
84b7200178 | ||
|
|
f8272baf07 | ||
|
|
9a6b934421 | ||
|
|
b3247d90a6 | ||
|
|
0c31f60bb0 | ||
|
|
be64c55901 | ||
|
|
b19199a35d | ||
|
|
6e67107170 |
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://unpkg.com/@changesets/config@2.3.0/schema.json",
|
"$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,
|
"commit": false,
|
||||||
"fixed": [],
|
"fixed": [],
|
||||||
"linked": [],
|
"linked": [],
|
||||||
|
|||||||
@@ -10,13 +10,12 @@
|
|||||||
"plugins": ["tailwindcss"],
|
"plugins": ["tailwindcss"],
|
||||||
"rules": {
|
"rules": {
|
||||||
"@next/next/no-html-link-for-pages": "off",
|
"@next/next/no-html-link-for-pages": "off",
|
||||||
"react/jsx-key": "off",
|
|
||||||
"tailwindcss/no-custom-classname": "off",
|
"tailwindcss/no-custom-classname": "off",
|
||||||
"tailwindcss/classnames-order": "error"
|
"tailwindcss/classnames-order": "error"
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"tailwindcss": {
|
"tailwindcss": {
|
||||||
"callees": ["cn"],
|
"callees": ["cn", "cva"],
|
||||||
"config": "tailwind.config.cjs"
|
"config": "tailwind.config.cjs"
|
||||||
},
|
},
|
||||||
"next": {
|
"next": {
|
||||||
|
|||||||
3
.github/workflows/code-check.yml
vendored
3
.github/workflows/code-check.yml
vendored
@@ -22,6 +22,7 @@ jobs:
|
|||||||
name: Install pnpm
|
name: Install pnpm
|
||||||
id: pnpm-install
|
id: pnpm-install
|
||||||
with:
|
with:
|
||||||
|
version: 8.6.1
|
||||||
run_install: false
|
run_install: false
|
||||||
|
|
||||||
- name: Get pnpm store directory
|
- name: Get pnpm store directory
|
||||||
@@ -57,6 +58,7 @@ jobs:
|
|||||||
name: Install pnpm
|
name: Install pnpm
|
||||||
id: pnpm-install
|
id: pnpm-install
|
||||||
with:
|
with:
|
||||||
|
version: 8.6.1
|
||||||
run_install: false
|
run_install: false
|
||||||
|
|
||||||
- name: Get pnpm store directory
|
- name: Get pnpm store directory
|
||||||
@@ -94,6 +96,7 @@ jobs:
|
|||||||
name: Install pnpm
|
name: Install pnpm
|
||||||
id: pnpm-install
|
id: pnpm-install
|
||||||
with:
|
with:
|
||||||
|
version: 8.6.1
|
||||||
run_install: false
|
run_install: false
|
||||||
|
|
||||||
- name: Get pnpm store directory
|
- name: Get pnpm store directory
|
||||||
|
|||||||
2
.github/workflows/prerelease-comment.yml
vendored
2
.github/workflows/prerelease-comment.yml
vendored
@@ -10,7 +10,7 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
comment:
|
comment:
|
||||||
if: |
|
if: |
|
||||||
github.repository_owner == 'shadcn' &&
|
github.repository_owner == 'shadcn-ui' &&
|
||||||
${{ github.event.workflow_run.conclusion == 'success' }}
|
${{ github.event.workflow_run.conclusion == 'success' }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
name: Write comment to the PR
|
name: Write comment to the PR
|
||||||
|
|||||||
4
.github/workflows/prerelease.yml
vendored
4
.github/workflows/prerelease.yml
vendored
@@ -10,7 +10,7 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
prerelease:
|
prerelease:
|
||||||
if: |
|
if: |
|
||||||
github.repository_owner == 'shadcn' &&
|
github.repository_owner == 'shadcn-ui' &&
|
||||||
contains(github.event.pull_request.labels.*.name, '🚀 autorelease')
|
contains(github.event.pull_request.labels.*.name, '🚀 autorelease')
|
||||||
name: Build & Publish a beta release to NPM
|
name: Build & Publish a beta release to NPM
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -24,6 +24,8 @@ jobs:
|
|||||||
|
|
||||||
- name: Use PNPM
|
- name: Use PNPM
|
||||||
uses: pnpm/action-setup@v2.2.4
|
uses: pnpm/action-setup@v2.2.4
|
||||||
|
with:
|
||||||
|
version: 8.6.1
|
||||||
|
|
||||||
- name: Use Node.js 18
|
- name: Use Node.js 18
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
|
|||||||
5
.github/workflows/release.yml
vendored
5
.github/workflows/release.yml
vendored
@@ -9,7 +9,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
release:
|
release:
|
||||||
if: ${{ github.repository_owner == 'shadcn' }}
|
if: ${{ github.repository_owner == 'shadcn-ui' }}
|
||||||
name: Create a PR for release workflow
|
name: Create a PR for release workflow
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
@@ -20,10 +20,13 @@ jobs:
|
|||||||
|
|
||||||
- name: Use PNPM
|
- name: Use PNPM
|
||||||
uses: pnpm/action-setup@v2.2.4
|
uses: pnpm/action-setup@v2.2.4
|
||||||
|
with:
|
||||||
|
version: 8.6.1
|
||||||
|
|
||||||
- name: Use Node.js 18
|
- name: Use Node.js 18
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
|
version: 8.6.1
|
||||||
node-version: 18
|
node-version: 18
|
||||||
cache: "pnpm"
|
cache: "pnpm"
|
||||||
|
|
||||||
|
|||||||
42
.github/workflows/test.yml
vendored
Normal file
42
.github/workflows/test.yml
vendored
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
name: Test
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches: ["*"]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: Test
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Install Node.js
|
||||||
|
uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: 16
|
||||||
|
|
||||||
|
- uses: pnpm/action-setup@v2.2.4
|
||||||
|
name: Install pnpm
|
||||||
|
id: pnpm-install
|
||||||
|
with:
|
||||||
|
version: 8.6.1
|
||||||
|
run_install: false
|
||||||
|
|
||||||
|
- name: Get pnpm store directory
|
||||||
|
id: pnpm-cache
|
||||||
|
run: |
|
||||||
|
echo "pnpm_cache_dir=$(pnpm store path)" >> $GITHUB_OUTPUT
|
||||||
|
- uses: actions/cache@v3
|
||||||
|
name: Setup pnpm cache
|
||||||
|
with:
|
||||||
|
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
|
||||||
|
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-pnpm-store-
|
||||||
|
- name: Install dependencies
|
||||||
|
run: pnpm install
|
||||||
|
|
||||||
|
- run: pnpm test
|
||||||
@@ -3,4 +3,4 @@ node_modules
|
|||||||
.next
|
.next
|
||||||
build
|
build
|
||||||
.contentlayer
|
.contentlayer
|
||||||
apps/www/pages/api/components.json
|
apps/www/pages/api/registry.json
|
||||||
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
@@ -2,5 +2,9 @@
|
|||||||
"eslint.workingDirectories": [
|
"eslint.workingDirectories": [
|
||||||
{ "pattern": "apps/*/" },
|
{ "pattern": "apps/*/" },
|
||||||
{ "pattern": "packages/*/" }
|
{ "pattern": "packages/*/" }
|
||||||
|
],
|
||||||
|
"tailwindCSS.experimental.classRegex": [
|
||||||
|
["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"],
|
||||||
|
["cn\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"]
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
162
CONTRIBUTING.md
Normal file
162
CONTRIBUTING.md
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
# 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
|
||||||
|
|
||||||
|
### Start by cloning the repository:
|
||||||
|
|
||||||
|
```
|
||||||
|
git clone git@github.com:shadcn-ui/ui.git
|
||||||
|
```
|
||||||
|
|
||||||
|
### Install dependencies
|
||||||
|
|
||||||
|
```
|
||||||
|
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:
|
||||||
|
|
||||||
|
```
|
||||||
|
pnpm --filter=www dev
|
||||||
|
```
|
||||||
|
|
||||||
|
2. To run the `shadcn-ui` package:
|
||||||
|
|
||||||
|
```
|
||||||
|
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.
|
Visit http://ui.shadcn.com/docs to view the documentation.
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
Please read the [contributing guide](/CONTRIBUTING.md).
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Licensed under the [MIT license](https://github.com/shadcn/ui/blob/main/LICENSE.md).
|
Licensed under the [MIT license](https://github.com/shadcn/ui/blob/main/LICENSE.md).
|
||||||
|
|||||||
@@ -2,4 +2,5 @@ dist
|
|||||||
node_modules
|
node_modules
|
||||||
.next
|
.next
|
||||||
build
|
build
|
||||||
.contentlayer
|
.contentlayer
|
||||||
|
__registry__/index.tsx
|
||||||
1
apps/www/__registry__/.autogenerated
Normal file
1
apps/www/__registry__/.autogenerated
Normal file
@@ -0,0 +1 @@
|
|||||||
|
// The content of this directory is autogenerated by the registry server.
|
||||||
0
apps/www/__registry__/.gitkeep
Normal file
0
apps/www/__registry__/.gitkeep
Normal file
1
apps/www/__registry__/README.md
Normal file
1
apps/www/__registry__/README.md
Normal file
@@ -0,0 +1 @@
|
|||||||
|
> Files inside this directory is autogenerated by `./scripts/build-registry.ts`. **Do not edit them manually.** - shadcn
|
||||||
2026
apps/www/__registry__/index.tsx
Normal file
2026
apps/www/__registry__/index.tsx
Normal file
File diff suppressed because it is too large
Load Diff
@@ -4,18 +4,18 @@ import { allDocs } from "contentlayer/generated"
|
|||||||
import "@/styles/mdx.css"
|
import "@/styles/mdx.css"
|
||||||
import type { Metadata } from "next"
|
import type { Metadata } from "next"
|
||||||
import Link from "next/link"
|
import Link from "next/link"
|
||||||
import { ChevronRight } from "lucide-react"
|
import { ChevronRightIcon } from "@radix-ui/react-icons"
|
||||||
import Balancer from "react-wrap-balancer"
|
import Balancer from "react-wrap-balancer"
|
||||||
|
|
||||||
import { siteConfig } from "@/config/site"
|
import { siteConfig } from "@/config/site"
|
||||||
import { getTableOfContents } from "@/lib/toc"
|
import { getTableOfContents } from "@/lib/toc"
|
||||||
import { absoluteUrl, cn } from "@/lib/utils"
|
import { absoluteUrl, cn } from "@/lib/utils"
|
||||||
import { badgeVariants } from "@/components/ui/badge"
|
|
||||||
import { Separator } from "@/components/ui/separator"
|
|
||||||
import { Icons } from "@/components/icons"
|
import { Icons } from "@/components/icons"
|
||||||
import { Mdx } from "@/components/mdx-components"
|
import { Mdx } from "@/components/mdx-components"
|
||||||
import { DocsPager } from "@/components/pager"
|
import { DocsPager } from "@/components/pager"
|
||||||
import { DashboardTableOfContents } from "@/components/toc"
|
import { DashboardTableOfContents } from "@/components/toc"
|
||||||
|
import { badgeVariants } from "@/registry/new-york/ui/badge"
|
||||||
|
import { ScrollArea } from "@/registry/new-york/ui/scroll-area"
|
||||||
|
|
||||||
interface DocPageProps {
|
interface DocPageProps {
|
||||||
params: {
|
params: {
|
||||||
@@ -94,7 +94,7 @@ export default async function DocPage({ params }: DocPageProps) {
|
|||||||
<div className="overflow-hidden text-ellipsis whitespace-nowrap">
|
<div className="overflow-hidden text-ellipsis whitespace-nowrap">
|
||||||
Docs
|
Docs
|
||||||
</div>
|
</div>
|
||||||
<ChevronRight className="h-4 w-4" />
|
<ChevronRightIcon className="h-4 w-4" />
|
||||||
<div className="font-medium text-foreground">{doc.title}</div>
|
<div className="font-medium text-foreground">{doc.title}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
@@ -132,16 +132,22 @@ export default async function DocPage({ params }: DocPageProps) {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
<Separator className="my-4 md:my-6" />
|
<div className="pb-12 pt-8">
|
||||||
<Mdx code={doc.body.code} />
|
<Mdx code={doc.body.code} />
|
||||||
<Separator className="my-4 md:my-6" />
|
</div>
|
||||||
<DocsPager doc={doc} />
|
<DocsPager doc={doc} />
|
||||||
</div>
|
</div>
|
||||||
<div className="hidden text-sm xl:block">
|
{doc.toc && (
|
||||||
<div className="sticky top-16 -mt-10 max-h-[calc(var(--vh)-4rem)] overflow-y-auto pt-6">
|
<div className="hidden text-sm xl:block">
|
||||||
<DashboardTableOfContents toc={toc} />
|
<div className="sticky top-16 -mt-10 pt-4">
|
||||||
|
<ScrollArea className="pb-10">
|
||||||
|
<div className="sticky top-16 -mt-10 h-[calc(100vh-3.5rem)] py-12">
|
||||||
|
<DashboardTableOfContents toc={toc} />
|
||||||
|
</div>
|
||||||
|
</ScrollArea>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
</main>
|
</main>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { docsConfig } from "@/config/docs"
|
import { docsConfig } from "@/config/docs"
|
||||||
import { ScrollArea } from "@/components/ui/scroll-area"
|
|
||||||
import { DocsSidebarNav } from "@/components/sidebar-nav"
|
import { DocsSidebarNav } from "@/components/sidebar-nav"
|
||||||
|
import { ScrollArea } from "@/registry/new-york/ui/scroll-area"
|
||||||
|
|
||||||
interface DocsLayoutProps {
|
interface DocsLayoutProps {
|
||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
@@ -8,13 +8,15 @@ interface DocsLayoutProps {
|
|||||||
|
|
||||||
export default function DocsLayout({ children }: DocsLayoutProps) {
|
export default function DocsLayout({ children }: DocsLayoutProps) {
|
||||||
return (
|
return (
|
||||||
<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">
|
<div className="border-b">
|
||||||
<aside className="fixed top-14 z-30 -ml-2 hidden h-[calc(100vh-3.5rem)] w-full shrink-0 overflow-y-auto border-r md:sticky md:block">
|
<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">
|
||||||
<ScrollArea className="py-6 pr-6 lg:py-8">
|
<aside className="fixed top-14 z-30 -ml-2 hidden h-[calc(100vh-3.5rem)] w-full shrink-0 md:sticky md:block">
|
||||||
<DocsSidebarNav items={docsConfig.sidebarNav} />
|
<ScrollArea className="h-full py-6 pl-8 pr-6 lg:py-8">
|
||||||
</ScrollArea>
|
<DocsSidebarNav items={docsConfig.sidebarNav} />
|
||||||
</aside>
|
</ScrollArea>
|
||||||
{children}
|
</aside>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,10 @@
|
|||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
import { Button } from "@/components/ui/button"
|
|
||||||
import { Input } from "@/components/ui/input"
|
|
||||||
import { Label } from "@/components/ui/label"
|
|
||||||
import { Icons } from "@/components/icons"
|
import { Icons } from "@/components/icons"
|
||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
|
import { Input } from "@/registry/new-york/ui/input"
|
||||||
|
import { Label } from "@/registry/new-york/ui/label"
|
||||||
|
|
||||||
interface UserAuthFormProps extends React.HTMLAttributes<HTMLDivElement> {}
|
interface UserAuthFormProps extends React.HTMLAttributes<HTMLDivElement> {}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
import { Metadata } from "next"
|
import { Metadata } from "next"
|
||||||
import Image from "next/image"
|
import Image from "next/image"
|
||||||
import Link from "next/link"
|
import Link from "next/link"
|
||||||
import { Command } from "lucide-react"
|
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
import { buttonVariants } from "@/components/ui/button"
|
import { buttonVariants } from "@/registry/new-york/ui/button"
|
||||||
import { UserAuthForm } from "@/app/examples/authentication/components/user-auth-form"
|
import { UserAuthForm } from "@/app/examples/authentication/components/user-auth-form"
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
@@ -35,29 +34,35 @@ export default function AuthenticationPage() {
|
|||||||
<Link
|
<Link
|
||||||
href="/examples/authentication"
|
href="/examples/authentication"
|
||||||
className={cn(
|
className={cn(
|
||||||
buttonVariants({ variant: "ghost", size: "sm" }),
|
buttonVariants({ variant: "ghost" }),
|
||||||
"absolute right-4 top-4 md:right-8 md:top-8"
|
"absolute right-4 top-4 md:right-8 md:top-8"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
Login
|
Login
|
||||||
</Link>
|
</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 dark:border-r lg:flex">
|
||||||
<div
|
<div className="absolute inset-0 bg-zinc-900" />
|
||||||
className="absolute inset-0 bg-cover"
|
|
||||||
style={{
|
|
||||||
backgroundImage:
|
|
||||||
"url(https://images.unsplash.com/photo-1590069261209-f8e9b8642343?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1376&q=80)",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<div className="relative z-20 flex items-center text-lg font-medium">
|
<div className="relative z-20 flex items-center text-lg font-medium">
|
||||||
<Command className="mr-2 h-6 w-6" /> Acme Inc
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
className="mr-2 h-6 w-6"
|
||||||
|
>
|
||||||
|
<path d="M15 6v12a3 3 0 1 0 3-3H6a3 3 0 1 0 3 3V6a3 3 0 1 0-3 3h12a3 3 0 1 0-3-3" />
|
||||||
|
</svg>
|
||||||
|
Acme Inc
|
||||||
</div>
|
</div>
|
||||||
<div className="relative z-20 mt-auto">
|
<div className="relative z-20 mt-auto">
|
||||||
<blockquote className="space-y-2">
|
<blockquote className="space-y-2">
|
||||||
<p className="text-lg">
|
<p className="text-lg">
|
||||||
“This library has saved me countless hours of work and
|
“This library has saved me countless hours of work and
|
||||||
helped me deliver stunning designs to my clients faster than
|
helped me deliver stunning designs to my clients faster than
|
||||||
ever before. Highly recommended!”
|
ever before.”
|
||||||
</p>
|
</p>
|
||||||
<footer className="text-sm">Sofia Davis</footer>
|
<footer className="text-sm">Sofia Davis</footer>
|
||||||
</blockquote>
|
</blockquote>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
CardContent,
|
CardContent,
|
||||||
@@ -8,9 +8,9 @@ import {
|
|||||||
CardFooter,
|
CardFooter,
|
||||||
CardHeader,
|
CardHeader,
|
||||||
CardTitle,
|
CardTitle,
|
||||||
} from "@/components/ui/card"
|
} from "@/registry/new-york/ui/card"
|
||||||
import { Label } from "@/components/ui/label"
|
import { Label } from "@/registry/new-york/ui/label"
|
||||||
import { Switch } from "@/components/ui/switch"
|
import { Switch } from "@/registry/new-york/ui/switch"
|
||||||
|
|
||||||
export function DemoCookieSettings() {
|
export function DemoCookieSettings() {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { Button } from "@/components/ui/button"
|
import { Icons } from "@/components/icons"
|
||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
CardContent,
|
CardContent,
|
||||||
@@ -8,10 +9,9 @@ import {
|
|||||||
CardFooter,
|
CardFooter,
|
||||||
CardHeader,
|
CardHeader,
|
||||||
CardTitle,
|
CardTitle,
|
||||||
} from "@/components/ui/card"
|
} from "@/registry/new-york/ui/card"
|
||||||
import { Input } from "@/components/ui/input"
|
import { Input } from "@/registry/new-york/ui/input"
|
||||||
import { Label } from "@/components/ui/label"
|
import { Label } from "@/registry/new-york/ui/label"
|
||||||
import { Icons } from "@/components/icons"
|
|
||||||
|
|
||||||
export function DemoCreateAccount() {
|
export function DemoCreateAccount() {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Card, CardContent } from "@/components/ui/card"
|
import DatePickerWithRange from "@/registry/default/example/date-picker-with-range"
|
||||||
import { Label } from "@/components/ui/label"
|
import { Card, CardContent } from "@/registry/new-york/ui/card"
|
||||||
import { CalendarDateRangePicker } from "@/components/examples/calendar/date-range-picker"
|
import { Label } from "@/registry/new-york/ui/label"
|
||||||
|
|
||||||
export function DemoDatePicker() {
|
export function DemoDatePicker() {
|
||||||
return (
|
return (
|
||||||
@@ -10,7 +10,7 @@ export function DemoDatePicker() {
|
|||||||
<Label htmlFor="date" className="shrink-0">
|
<Label htmlFor="date" className="shrink-0">
|
||||||
Pick a date
|
Pick a date
|
||||||
</Label>
|
</Label>
|
||||||
<CalendarDateRangePicker className="[&>button]:w-[260px]" />
|
<DatePickerWithRange className="[&>button]:w-[260px]" />
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -1,13 +1,18 @@
|
|||||||
import { ChevronDown, Circle, Plus, Star } from "lucide-react"
|
import {
|
||||||
|
ChevronDownIcon,
|
||||||
|
CircleIcon,
|
||||||
|
PlusIcon,
|
||||||
|
StarIcon,
|
||||||
|
} from "@radix-ui/react-icons"
|
||||||
|
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
CardContent,
|
CardContent,
|
||||||
CardDescription,
|
CardDescription,
|
||||||
CardHeader,
|
CardHeader,
|
||||||
CardTitle,
|
CardTitle,
|
||||||
} from "@/components/ui/card"
|
} from "@/registry/new-york/ui/card"
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuCheckboxItem,
|
DropdownMenuCheckboxItem,
|
||||||
@@ -16,8 +21,8 @@ import {
|
|||||||
DropdownMenuLabel,
|
DropdownMenuLabel,
|
||||||
DropdownMenuSeparator,
|
DropdownMenuSeparator,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu"
|
} from "@/registry/new-york/ui/dropdown-menu"
|
||||||
import { Separator } from "@/components/ui/separator"
|
import { Separator } from "@/registry/new-york/ui/separator"
|
||||||
|
|
||||||
export function DemoGithub() {
|
export function DemoGithub() {
|
||||||
return (
|
return (
|
||||||
@@ -31,15 +36,15 @@ export function DemoGithub() {
|
|||||||
</CardDescription>
|
</CardDescription>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center space-x-1 rounded-md bg-secondary text-secondary-foreground">
|
<div className="flex items-center space-x-1 rounded-md bg-secondary text-secondary-foreground">
|
||||||
<Button variant="secondary" className="px-3">
|
<Button variant="secondary" className="px-3 shadow-none">
|
||||||
<Star className="mr-2 h-4 w-4" />
|
<StarIcon className="mr-2 h-4 w-4" />
|
||||||
Star
|
Star
|
||||||
</Button>
|
</Button>
|
||||||
<Separator orientation="vertical" className="h-[20px]" />
|
<Separator orientation="vertical" className="h-[20px]" />
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
<Button variant="secondary" className="px-2">
|
<Button variant="secondary" className="px-2 shadow-none">
|
||||||
<ChevronDown className="h-4 w-4 text-secondary-foreground" />
|
<ChevronDownIcon className="h-4 w-4 text-secondary-foreground" />
|
||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent
|
<DropdownMenuContent
|
||||||
@@ -57,7 +62,7 @@ export function DemoGithub() {
|
|||||||
<DropdownMenuCheckboxItem>Inspiration</DropdownMenuCheckboxItem>
|
<DropdownMenuCheckboxItem>Inspiration</DropdownMenuCheckboxItem>
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
<DropdownMenuItem>
|
<DropdownMenuItem>
|
||||||
<Plus className="mr-2 h-4 w-4" /> Create List
|
<PlusIcon className="mr-2 h-4 w-4" /> Create List
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
@@ -66,12 +71,12 @@ export function DemoGithub() {
|
|||||||
<CardContent>
|
<CardContent>
|
||||||
<div className="flex space-x-4 text-sm text-muted-foreground">
|
<div className="flex space-x-4 text-sm text-muted-foreground">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<Circle className="mr-1 h-3 w-3 fill-sky-400 text-sky-400" />
|
<CircleIcon className="mr-1 h-3 w-3 fill-sky-400 text-sky-400" />
|
||||||
TypeScipt
|
TypeScript
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<Star className="mr-1 h-3 w-3" />
|
<StarIcon className="mr-1 h-3 w-3" />
|
||||||
10k
|
20k
|
||||||
</div>
|
</div>
|
||||||
<div>Updated April 2023</div>
|
<div>Updated April 2023</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { AtSign, Bell, BellOff } from "lucide-react"
|
import { BellIcon, EyeNoneIcon, PersonIcon } from "@radix-ui/react-icons"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
@@ -6,20 +6,20 @@ import {
|
|||||||
CardDescription,
|
CardDescription,
|
||||||
CardHeader,
|
CardHeader,
|
||||||
CardTitle,
|
CardTitle,
|
||||||
} from "@/components/ui/card"
|
} from "@/registry/new-york/ui/card"
|
||||||
|
|
||||||
export function DemoNotifications() {
|
export function DemoNotifications() {
|
||||||
return (
|
return (
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader>
|
<CardHeader className="pb-3">
|
||||||
<CardTitle>Notifications</CardTitle>
|
<CardTitle>Notifications</CardTitle>
|
||||||
<CardDescription>
|
<CardDescription>
|
||||||
Choose what you want to be notified about.
|
Choose what you want to be notified about.
|
||||||
</CardDescription>
|
</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="grid gap-1 p-1.5">
|
<CardContent className="grid gap-1">
|
||||||
<div className="flex items-center space-x-4 rounded-md p-2 hover:bg-accent hover:text-accent-foreground">
|
<div className="-mx-2 flex items-start space-x-4 rounded-md p-2 transition-all hover:bg-accent hover:text-accent-foreground">
|
||||||
<Bell className="h-5 w-5" />
|
<BellIcon className="mt-px h-5 w-5" />
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<p className="text-sm font-medium leading-none">Everything</p>
|
<p className="text-sm font-medium leading-none">Everything</p>
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground">
|
||||||
@@ -27,8 +27,8 @@ export function DemoNotifications() {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center space-x-4 rounded-md bg-accent p-2 text-accent-foreground">
|
<div className="-mx-2 flex items-start space-x-4 rounded-md bg-accent p-2 text-accent-foreground transition-all">
|
||||||
<AtSign className="h-5 w-5" />
|
<PersonIcon className="mt-px h-5 w-5" />
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<p className="text-sm font-medium leading-none">Available</p>
|
<p className="text-sm font-medium leading-none">Available</p>
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground">
|
||||||
@@ -36,8 +36,8 @@ export function DemoNotifications() {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center space-x-4 rounded-md p-2 hover:bg-accent hover:text-accent-foreground">
|
<div className="-mx-2 flex items-start space-x-4 rounded-md p-2 transition-all hover:bg-accent hover:text-accent-foreground">
|
||||||
<BellOff className="h-5 w-5" />
|
<EyeNoneIcon className="mt-px h-5 w-5" />
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<p className="text-sm font-medium leading-none">Ignoring</p>
|
<p className="text-sm font-medium leading-none">Ignoring</p>
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground">
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { CreditCard } from "lucide-react"
|
import { Icons } from "@/components/icons"
|
||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
import { Button } from "@/components/ui/button"
|
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
CardContent,
|
CardContent,
|
||||||
@@ -8,18 +7,17 @@ import {
|
|||||||
CardFooter,
|
CardFooter,
|
||||||
CardHeader,
|
CardHeader,
|
||||||
CardTitle,
|
CardTitle,
|
||||||
} from "@/components/ui/card"
|
} from "@/registry/new-york/ui/card"
|
||||||
import { Input } from "@/components/ui/input"
|
import { Input } from "@/registry/new-york/ui/input"
|
||||||
import { Label } from "@/components/ui/label"
|
import { Label } from "@/registry/new-york/ui/label"
|
||||||
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
|
import { RadioGroup, RadioGroupItem } from "@/registry/new-york/ui/radio-group"
|
||||||
import {
|
import {
|
||||||
Select,
|
Select,
|
||||||
SelectContent,
|
SelectContent,
|
||||||
SelectItem,
|
SelectItem,
|
||||||
SelectTrigger,
|
SelectTrigger,
|
||||||
SelectValue,
|
SelectValue,
|
||||||
} from "@/components/ui/select"
|
} from "@/registry/new-york/ui/select"
|
||||||
import { Icons } from "@/components/icons"
|
|
||||||
|
|
||||||
export function DemoPaymentMethod() {
|
export function DemoPaymentMethod() {
|
||||||
return (
|
return (
|
||||||
@@ -32,30 +30,52 @@ export function DemoPaymentMethod() {
|
|||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="grid gap-6">
|
<CardContent className="grid gap-6">
|
||||||
<RadioGroup defaultValue="card" className="grid grid-cols-3 gap-4">
|
<RadioGroup defaultValue="card" className="grid grid-cols-3 gap-4">
|
||||||
<Label
|
<div>
|
||||||
htmlFor="card"
|
<RadioGroupItem value="card" id="card" className="peer sr-only" />
|
||||||
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"
|
<Label
|
||||||
>
|
htmlFor="card"
|
||||||
<RadioGroupItem value="card" id="card" className="sr-only" />
|
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"
|
||||||
<CreditCard className="mb-3 h-6 w-6" />
|
>
|
||||||
Card
|
<svg
|
||||||
</Label>
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
<Label
|
viewBox="0 0 24 24"
|
||||||
htmlFor="paypal"
|
fill="none"
|
||||||
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"
|
stroke="currentColor"
|
||||||
>
|
strokeLinecap="round"
|
||||||
<RadioGroupItem value="paypal" id="paypal" className="sr-only" />
|
strokeLinejoin="round"
|
||||||
<Icons.paypal className="mb-3 h-6 w-6" />
|
strokeWidth="2"
|
||||||
Paypal
|
className="mb-3 h-6 w-6"
|
||||||
</Label>
|
>
|
||||||
<Label
|
<rect width="20" height="14" x="2" y="5" rx="2" />
|
||||||
htmlFor="apple"
|
<path d="M2 10h20" />
|
||||||
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"
|
</svg>
|
||||||
>
|
Card
|
||||||
<RadioGroupItem value="apple" id="apple" className="sr-only" />
|
</Label>
|
||||||
<Icons.apple className="mb-3 h-6 w-6" />
|
</div>
|
||||||
Apple
|
<div>
|
||||||
</Label>
|
<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>
|
</RadioGroup>
|
||||||
<div className="grid gap-2">
|
<div className="grid gap-2">
|
||||||
<Label htmlFor="name">Name</Label>
|
<Label htmlFor="name">Name</Label>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
CardContent,
|
CardContent,
|
||||||
@@ -8,17 +8,17 @@ import {
|
|||||||
CardFooter,
|
CardFooter,
|
||||||
CardHeader,
|
CardHeader,
|
||||||
CardTitle,
|
CardTitle,
|
||||||
} from "@/components/ui/card"
|
} from "@/registry/new-york/ui/card"
|
||||||
import { Input } from "@/components/ui/input"
|
import { Input } from "@/registry/new-york/ui/input"
|
||||||
import { Label } from "@/components/ui/label"
|
import { Label } from "@/registry/new-york/ui/label"
|
||||||
import {
|
import {
|
||||||
Select,
|
Select,
|
||||||
SelectContent,
|
SelectContent,
|
||||||
SelectItem,
|
SelectItem,
|
||||||
SelectTrigger,
|
SelectTrigger,
|
||||||
SelectValue,
|
SelectValue,
|
||||||
} from "@/components/ui/select"
|
} from "@/registry/new-york/ui/select"
|
||||||
import { Textarea } from "@/components/ui/textarea"
|
import { Textarea } from "@/registry/new-york/ui/textarea"
|
||||||
|
|
||||||
export function DemoReportAnIssue() {
|
export function DemoReportAnIssue() {
|
||||||
return (
|
return (
|
||||||
@@ -49,7 +49,10 @@ export function DemoReportAnIssue() {
|
|||||||
<div className="grid gap-2">
|
<div className="grid gap-2">
|
||||||
<Label htmlFor="security-level">Security Level</Label>
|
<Label htmlFor="security-level">Security Level</Label>
|
||||||
<Select defaultValue="2">
|
<Select defaultValue="2">
|
||||||
<SelectTrigger id="security-level">
|
<SelectTrigger
|
||||||
|
id="security-level"
|
||||||
|
className="line-clamp-1 w-[160px] truncate"
|
||||||
|
>
|
||||||
<SelectValue placeholder="Select level" />
|
<SelectValue placeholder="Select level" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
|
|||||||
@@ -1,23 +1,27 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
|
import {
|
||||||
import { Button } from "@/components/ui/button"
|
Avatar,
|
||||||
|
AvatarFallback,
|
||||||
|
AvatarImage,
|
||||||
|
} from "@/registry/new-york/ui/avatar"
|
||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
CardContent,
|
CardContent,
|
||||||
CardDescription,
|
CardDescription,
|
||||||
CardHeader,
|
CardHeader,
|
||||||
CardTitle,
|
CardTitle,
|
||||||
} from "@/components/ui/card"
|
} from "@/registry/new-york/ui/card"
|
||||||
import { Input } from "@/components/ui/input"
|
import { Input } from "@/registry/new-york/ui/input"
|
||||||
import {
|
import {
|
||||||
Select,
|
Select,
|
||||||
SelectContent,
|
SelectContent,
|
||||||
SelectItem,
|
SelectItem,
|
||||||
SelectTrigger,
|
SelectTrigger,
|
||||||
SelectValue,
|
SelectValue,
|
||||||
} from "@/components/ui/select"
|
} from "@/registry/new-york/ui/select"
|
||||||
import { Separator } from "@/components/ui/separator"
|
import { Separator } from "@/registry/new-york/ui/separator"
|
||||||
|
|
||||||
export function DemoShareDocument() {
|
export function DemoShareDocument() {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,14 +1,18 @@
|
|||||||
import { ChevronDown } from "lucide-react"
|
import { ChevronDownIcon } from "@radix-ui/react-icons"
|
||||||
|
|
||||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
|
import {
|
||||||
import { Button } from "@/components/ui/button"
|
Avatar,
|
||||||
|
AvatarFallback,
|
||||||
|
AvatarImage,
|
||||||
|
} from "@/registry/new-york/ui/avatar"
|
||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
CardContent,
|
CardContent,
|
||||||
CardDescription,
|
CardDescription,
|
||||||
CardHeader,
|
CardHeader,
|
||||||
CardTitle,
|
CardTitle,
|
||||||
} from "@/components/ui/card"
|
} from "@/registry/new-york/ui/card"
|
||||||
import {
|
import {
|
||||||
Command,
|
Command,
|
||||||
CommandEmpty,
|
CommandEmpty,
|
||||||
@@ -16,12 +20,12 @@ import {
|
|||||||
CommandInput,
|
CommandInput,
|
||||||
CommandItem,
|
CommandItem,
|
||||||
CommandList,
|
CommandList,
|
||||||
} from "@/components/ui/command"
|
} from "@/registry/new-york/ui/command"
|
||||||
import {
|
import {
|
||||||
Popover,
|
Popover,
|
||||||
PopoverContent,
|
PopoverContent,
|
||||||
PopoverTrigger,
|
PopoverTrigger,
|
||||||
} from "@/components/ui/popover"
|
} from "@/registry/new-york/ui/popover"
|
||||||
|
|
||||||
export function DemoTeamMembers() {
|
export function DemoTeamMembers() {
|
||||||
return (
|
return (
|
||||||
@@ -48,7 +52,7 @@ export function DemoTeamMembers() {
|
|||||||
<PopoverTrigger asChild>
|
<PopoverTrigger asChild>
|
||||||
<Button variant="outline" className="ml-auto">
|
<Button variant="outline" className="ml-auto">
|
||||||
Owner{" "}
|
Owner{" "}
|
||||||
<ChevronDown className="ml-2 h-4 w-4 text-muted-foreground" />
|
<ChevronDownIcon className="ml-2 h-4 w-4 text-muted-foreground" />
|
||||||
</Button>
|
</Button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent className="p-0" align="end">
|
<PopoverContent className="p-0" align="end">
|
||||||
@@ -102,7 +106,7 @@ export function DemoTeamMembers() {
|
|||||||
<PopoverTrigger asChild>
|
<PopoverTrigger asChild>
|
||||||
<Button variant="outline" className="ml-auto">
|
<Button variant="outline" className="ml-auto">
|
||||||
Member{" "}
|
Member{" "}
|
||||||
<ChevronDown className="ml-2 h-4 w-4 text-muted-foreground" />
|
<ChevronDownIcon className="ml-2 h-4 w-4 text-muted-foreground" />
|
||||||
</Button>
|
</Button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent className="p-0" align="end">
|
<PopoverContent className="p-0" align="end">
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Metadata } from "next"
|
import { Metadata } from "next"
|
||||||
|
import Image from "next/image"
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
@@ -11,8 +12,6 @@ import { DemoPaymentMethod } from "./components/payment-method"
|
|||||||
import { DemoReportAnIssue } from "./components/report-an-issue"
|
import { DemoReportAnIssue } from "./components/report-an-issue"
|
||||||
import { DemoShareDocument } from "./components/share-document"
|
import { DemoShareDocument } from "./components/share-document"
|
||||||
import { DemoTeamMembers } from "./components/team-members"
|
import { DemoTeamMembers } from "./components/team-members"
|
||||||
import "./styles.css"
|
|
||||||
import Image from "next/image"
|
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Cards",
|
title: "Cards",
|
||||||
@@ -76,7 +75,7 @@ export default function CardsPage() {
|
|||||||
<DemoNotifications />
|
<DemoNotifications />
|
||||||
</DemoContainer>
|
</DemoContainer>
|
||||||
</div>
|
</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>
|
<DemoContainer>
|
||||||
<DemoReportAnIssue />
|
<DemoReportAnIssue />
|
||||||
</DemoContainer>
|
</DemoContainer>
|
||||||
|
|||||||
@@ -1,63 +0,0 @@
|
|||||||
[data-section="cards"] {
|
|
||||||
--background: 0 0% 100%;
|
|
||||||
--foreground: 222.2 47.4% 11.2%;
|
|
||||||
|
|
||||||
--muted: 214 95% 93%;
|
|
||||||
--muted-foreground: 215.4 16.3% 46.9%;
|
|
||||||
|
|
||||||
--popover: 0 0% 100%;
|
|
||||||
--popover-foreground: 222.2 47.4% 11.2%;
|
|
||||||
|
|
||||||
--border: 214.3 31.8% 91.4%;
|
|
||||||
--input: 214.3 31.8% 91.4%;
|
|
||||||
|
|
||||||
--card: 0 0% 100%;
|
|
||||||
--card-foreground: 222.2 47.4% 11.2%;
|
|
||||||
|
|
||||||
--primary: 224 82% 56%;
|
|
||||||
--primary-foreground: 0 0% 100%;
|
|
||||||
|
|
||||||
--secondary: 210 40% 96.1%;
|
|
||||||
--secondary-foreground: 222.2 47.4% 11.2%;
|
|
||||||
|
|
||||||
--accent: 216 100% 97%;
|
|
||||||
--accent-foreground: 222.2 47.4% 11.2%;
|
|
||||||
|
|
||||||
--destructive: 0 100% 50%;
|
|
||||||
--destructive-foreground: 210 40% 98%;
|
|
||||||
|
|
||||||
--ring: 215 20.2% 65.1%;
|
|
||||||
|
|
||||||
--radius: 1.2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dark [data-section="cards"] {
|
|
||||||
--background: 224 71% 4%;
|
|
||||||
--foreground: 213 31% 91%;
|
|
||||||
|
|
||||||
--muted: 223 47% 11%;
|
|
||||||
--muted-foreground: 215.4 16.3% 56.9%;
|
|
||||||
|
|
||||||
--popover: 224 71% 4%;
|
|
||||||
--popover-foreground: 215 20.2% 65.1%;
|
|
||||||
|
|
||||||
--border: 216 34% 17%;
|
|
||||||
--input: 216 34% 17%;
|
|
||||||
|
|
||||||
--card: 224 71% 4%;
|
|
||||||
--card-foreground: 213 31% 91%;
|
|
||||||
|
|
||||||
--primary: 224 82% 56%;
|
|
||||||
--primary-foreground: 0 0% 100%;
|
|
||||||
|
|
||||||
--secondary: 210 40% 96.1%;
|
|
||||||
--secondary-foreground: 222.2 47.4% 11.2%;
|
|
||||||
|
|
||||||
--accent: 226 57% 21%;
|
|
||||||
--accent-foreground: 0 0% 100%;
|
|
||||||
|
|
||||||
--destructive: 0 63% 31%;
|
|
||||||
--destructive-foreground: 210 40% 98%;
|
|
||||||
|
|
||||||
--ring: 215 20.2% 65.1%;
|
|
||||||
}
|
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
|
import { CalendarIcon } from "@radix-ui/react-icons"
|
||||||
import { addDays, format } from "date-fns"
|
import { addDays, format } from "date-fns"
|
||||||
import { Calendar as CalendarIcon } from "lucide-react"
|
|
||||||
import { DateRange } from "react-day-picker"
|
import { DateRange } from "react-day-picker"
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
import { Calendar } from "@/components/ui/calendar"
|
import { Calendar } from "@/registry/new-york/ui/calendar"
|
||||||
import {
|
import {
|
||||||
Popover,
|
Popover,
|
||||||
PopoverContent,
|
PopoverContent,
|
||||||
PopoverTrigger,
|
PopoverTrigger,
|
||||||
} from "@/components/ui/popover"
|
} from "@/registry/new-york/ui/popover"
|
||||||
|
|
||||||
export function CalendarDateRangePicker({
|
export function CalendarDateRangePicker({
|
||||||
className,
|
className,
|
||||||
@@ -29,9 +29,8 @@ export function CalendarDateRangePicker({
|
|||||||
<Button
|
<Button
|
||||||
id="date"
|
id="date"
|
||||||
variant={"outline"}
|
variant={"outline"}
|
||||||
size="sm"
|
|
||||||
className={cn(
|
className={cn(
|
||||||
"w-[240px] justify-start text-left font-normal",
|
"w-[260px] justify-start text-left font-normal",
|
||||||
!date && "text-muted-foreground"
|
!date && "text-muted-foreground"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
|
import {
|
||||||
|
Avatar,
|
||||||
|
AvatarFallback,
|
||||||
|
AvatarImage,
|
||||||
|
} from "@/registry/new-york/ui/avatar"
|
||||||
|
|
||||||
export function RecentSales() {
|
export function RecentSales() {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Input } from "@/components/ui/input"
|
import { Input } from "@/registry/new-york/ui/input"
|
||||||
|
|
||||||
export function Search() {
|
export function Search() {
|
||||||
return (
|
return (
|
||||||
@@ -6,7 +6,7 @@ export function Search() {
|
|||||||
<Input
|
<Input
|
||||||
type="search"
|
type="search"
|
||||||
placeholder="Search..."
|
placeholder="Search..."
|
||||||
className="h-9 md:w-[100px] lg:w-[300px]"
|
className="md:w-[100px] lg:w-[300px]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,11 +1,19 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import { Check, ChevronsUpDown, PlusCircle } from "lucide-react"
|
import {
|
||||||
|
CaretSortIcon,
|
||||||
|
CheckIcon,
|
||||||
|
PlusCircledIcon,
|
||||||
|
} from "@radix-ui/react-icons"
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
|
import {
|
||||||
import { Button } from "@/components/ui/button"
|
Avatar,
|
||||||
|
AvatarFallback,
|
||||||
|
AvatarImage,
|
||||||
|
} from "@/registry/new-york/ui/avatar"
|
||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
import {
|
import {
|
||||||
Command,
|
Command,
|
||||||
CommandEmpty,
|
CommandEmpty,
|
||||||
@@ -14,7 +22,7 @@ import {
|
|||||||
CommandItem,
|
CommandItem,
|
||||||
CommandList,
|
CommandList,
|
||||||
CommandSeparator,
|
CommandSeparator,
|
||||||
} from "@/components/ui/command"
|
} from "@/registry/new-york/ui/command"
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
@@ -23,21 +31,21 @@ import {
|
|||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
DialogTrigger,
|
DialogTrigger,
|
||||||
} from "@/components/ui/dialog"
|
} from "@/registry/new-york/ui/dialog"
|
||||||
import { Input } from "@/components/ui/input"
|
import { Input } from "@/registry/new-york/ui/input"
|
||||||
import { Label } from "@/components/ui/label"
|
import { Label } from "@/registry/new-york/ui/label"
|
||||||
import {
|
import {
|
||||||
Popover,
|
Popover,
|
||||||
PopoverContent,
|
PopoverContent,
|
||||||
PopoverTrigger,
|
PopoverTrigger,
|
||||||
} from "@/components/ui/popover"
|
} from "@/registry/new-york/ui/popover"
|
||||||
import {
|
import {
|
||||||
Select,
|
Select,
|
||||||
SelectContent,
|
SelectContent,
|
||||||
SelectItem,
|
SelectItem,
|
||||||
SelectTrigger,
|
SelectTrigger,
|
||||||
SelectValue,
|
SelectValue,
|
||||||
} from "@/components/ui/select"
|
} from "@/registry/new-york/ui/select"
|
||||||
|
|
||||||
const groups = [
|
const groups = [
|
||||||
{
|
{
|
||||||
@@ -82,8 +90,7 @@ export default function TeamSwitcher({ className }: TeamSwitcherProps) {
|
|||||||
<Popover open={open} onOpenChange={setOpen}>
|
<Popover open={open} onOpenChange={setOpen}>
|
||||||
<PopoverTrigger asChild>
|
<PopoverTrigger asChild>
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="outline"
|
||||||
size="sm"
|
|
||||||
role="combobox"
|
role="combobox"
|
||||||
aria-expanded={open}
|
aria-expanded={open}
|
||||||
aria-label="Select a team"
|
aria-label="Select a team"
|
||||||
@@ -97,7 +104,7 @@ export default function TeamSwitcher({ className }: TeamSwitcherProps) {
|
|||||||
<AvatarFallback>SC</AvatarFallback>
|
<AvatarFallback>SC</AvatarFallback>
|
||||||
</Avatar>
|
</Avatar>
|
||||||
{selectedTeam.label}
|
{selectedTeam.label}
|
||||||
<ChevronsUpDown className="ml-auto h-4 w-4 shrink-0 opacity-50" />
|
<CaretSortIcon className="ml-auto h-4 w-4 shrink-0 opacity-50" />
|
||||||
</Button>
|
</Button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent className="w-[200px] p-0">
|
<PopoverContent className="w-[200px] p-0">
|
||||||
@@ -120,11 +127,12 @@ export default function TeamSwitcher({ className }: TeamSwitcherProps) {
|
|||||||
<AvatarImage
|
<AvatarImage
|
||||||
src={`https://avatar.vercel.sh/${team.value}.png`}
|
src={`https://avatar.vercel.sh/${team.value}.png`}
|
||||||
alt={team.label}
|
alt={team.label}
|
||||||
|
className="grayscale"
|
||||||
/>
|
/>
|
||||||
<AvatarFallback>SC</AvatarFallback>
|
<AvatarFallback>SC</AvatarFallback>
|
||||||
</Avatar>
|
</Avatar>
|
||||||
{team.label}
|
{team.label}
|
||||||
<Check
|
<CheckIcon
|
||||||
className={cn(
|
className={cn(
|
||||||
"ml-auto h-4 w-4",
|
"ml-auto h-4 w-4",
|
||||||
selectedTeam.value === team.value
|
selectedTeam.value === team.value
|
||||||
@@ -147,7 +155,7 @@ export default function TeamSwitcher({ className }: TeamSwitcherProps) {
|
|||||||
setShowNewTeamDialog(true)
|
setShowNewTeamDialog(true)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<PlusCircle className="mr-2 h-5 w-5" />
|
<PlusCircledIcon className="mr-2 h-5 w-5" />
|
||||||
Create Team
|
Create Team
|
||||||
</CommandItem>
|
</CommandItem>
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import { CreditCard, LogOut, PlusCircle, Settings, User } from "lucide-react"
|
import {
|
||||||
|
Avatar,
|
||||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
|
AvatarFallback,
|
||||||
import { Button } from "@/components/ui/button"
|
AvatarImage,
|
||||||
|
} from "@/registry/new-york/ui/avatar"
|
||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
@@ -11,7 +13,7 @@ import {
|
|||||||
DropdownMenuSeparator,
|
DropdownMenuSeparator,
|
||||||
DropdownMenuShortcut,
|
DropdownMenuShortcut,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu"
|
} from "@/registry/new-york/ui/dropdown-menu"
|
||||||
|
|
||||||
export function UserNav() {
|
export function UserNav() {
|
||||||
return (
|
return (
|
||||||
@@ -36,29 +38,22 @@ export function UserNav() {
|
|||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
<DropdownMenuGroup>
|
<DropdownMenuGroup>
|
||||||
<DropdownMenuItem>
|
<DropdownMenuItem>
|
||||||
<User className="mr-2 h-4 w-4" />
|
Profile
|
||||||
<span>Profile</span>
|
|
||||||
<DropdownMenuShortcut>⇧⌘P</DropdownMenuShortcut>
|
<DropdownMenuShortcut>⇧⌘P</DropdownMenuShortcut>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem>
|
<DropdownMenuItem>
|
||||||
<CreditCard className="mr-2 h-4 w-4" />
|
Billing
|
||||||
<span>Billing</span>
|
|
||||||
<DropdownMenuShortcut>⌘B</DropdownMenuShortcut>
|
<DropdownMenuShortcut>⌘B</DropdownMenuShortcut>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem>
|
<DropdownMenuItem>
|
||||||
<Settings className="mr-2 h-4 w-4" />
|
Settings
|
||||||
<span>Settings</span>
|
|
||||||
<DropdownMenuShortcut>⌘S</DropdownMenuShortcut>
|
<DropdownMenuShortcut>⌘S</DropdownMenuShortcut>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem>
|
<DropdownMenuItem>New Team</DropdownMenuItem>
|
||||||
<PlusCircle className="mr-2 h-4 w-4" />
|
|
||||||
<span>New Team</span>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</DropdownMenuGroup>
|
</DropdownMenuGroup>
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
<DropdownMenuItem>
|
<DropdownMenuItem>
|
||||||
<LogOut className="mr-2 h-4 w-4" />
|
Log out
|
||||||
<span>Log out</span>
|
|
||||||
<DropdownMenuShortcut>⇧⌘Q</DropdownMenuShortcut>
|
<DropdownMenuShortcut>⇧⌘Q</DropdownMenuShortcut>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
|
|||||||
@@ -1,16 +1,20 @@
|
|||||||
import { Metadata } from "next"
|
import { Metadata } from "next"
|
||||||
import Image from "next/image"
|
import Image from "next/image"
|
||||||
import { Activity, CreditCard, DollarSign, Download, Users } from "lucide-react"
|
|
||||||
|
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
CardContent,
|
CardContent,
|
||||||
CardDescription,
|
CardDescription,
|
||||||
CardHeader,
|
CardHeader,
|
||||||
CardTitle,
|
CardTitle,
|
||||||
} from "@/components/ui/card"
|
} from "@/registry/new-york/ui/card"
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
|
import {
|
||||||
|
Tabs,
|
||||||
|
TabsContent,
|
||||||
|
TabsList,
|
||||||
|
TabsTrigger,
|
||||||
|
} from "@/registry/new-york/ui/tabs"
|
||||||
import { CalendarDateRangePicker } from "@/app/examples/dashboard/components/date-range-picker"
|
import { CalendarDateRangePicker } from "@/app/examples/dashboard/components/date-range-picker"
|
||||||
import { MainNav } from "@/app/examples/dashboard/components/main-nav"
|
import { MainNav } from "@/app/examples/dashboard/components/main-nav"
|
||||||
import { Overview } from "@/app/examples/dashboard/components/overview"
|
import { Overview } from "@/app/examples/dashboard/components/overview"
|
||||||
@@ -21,7 +25,7 @@ import { UserNav } from "@/app/examples/dashboard/components/user-nav"
|
|||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Dashboard",
|
title: "Dashboard",
|
||||||
description: "Example dashboard app using the components.",
|
description: "Example dashboard app built using the components.",
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function DashboardPage() {
|
export default function DashboardPage() {
|
||||||
@@ -59,10 +63,7 @@ export default function DashboardPage() {
|
|||||||
<h2 className="text-3xl font-bold tracking-tight">Dashboard</h2>
|
<h2 className="text-3xl font-bold tracking-tight">Dashboard</h2>
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<CalendarDateRangePicker />
|
<CalendarDateRangePicker />
|
||||||
<Button size="sm">
|
<Button>Download</Button>
|
||||||
<Download className="mr-2 h-4 w-4" />
|
|
||||||
Download
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Tabs defaultValue="overview" className="space-y-4">
|
<Tabs defaultValue="overview" className="space-y-4">
|
||||||
@@ -85,7 +86,18 @@ export default function DashboardPage() {
|
|||||||
<CardTitle className="text-sm font-medium">
|
<CardTitle className="text-sm font-medium">
|
||||||
Total Revenue
|
Total Revenue
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
<DollarSign className="h-4 w-4 text-muted-foreground" />
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth="2"
|
||||||
|
className="h-4 w-4 text-muted-foreground"
|
||||||
|
>
|
||||||
|
<path d="M12 2v20M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6" />
|
||||||
|
</svg>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<div className="text-2xl font-bold">$45,231.89</div>
|
<div className="text-2xl font-bold">$45,231.89</div>
|
||||||
@@ -99,7 +111,20 @@ export default function DashboardPage() {
|
|||||||
<CardTitle className="text-sm font-medium">
|
<CardTitle className="text-sm font-medium">
|
||||||
Subscriptions
|
Subscriptions
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
<Users className="h-4 w-4 text-muted-foreground" />
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth="2"
|
||||||
|
className="h-4 w-4 text-muted-foreground"
|
||||||
|
>
|
||||||
|
<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" />
|
||||||
|
<circle cx="9" cy="7" r="4" />
|
||||||
|
<path d="M22 21v-2a4 4 0 0 0-3-3.87M16 3.13a4 4 0 0 1 0 7.75" />
|
||||||
|
</svg>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<div className="text-2xl font-bold">+2350</div>
|
<div className="text-2xl font-bold">+2350</div>
|
||||||
@@ -111,7 +136,19 @@ export default function DashboardPage() {
|
|||||||
<Card>
|
<Card>
|
||||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||||
<CardTitle className="text-sm font-medium">Sales</CardTitle>
|
<CardTitle className="text-sm font-medium">Sales</CardTitle>
|
||||||
<CreditCard className="h-4 w-4 text-muted-foreground" />
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth="2"
|
||||||
|
className="h-4 w-4 text-muted-foreground"
|
||||||
|
>
|
||||||
|
<rect width="20" height="14" x="2" y="5" rx="2" />
|
||||||
|
<path d="M2 10h20" />
|
||||||
|
</svg>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<div className="text-2xl font-bold">+12,234</div>
|
<div className="text-2xl font-bold">+12,234</div>
|
||||||
@@ -125,7 +162,18 @@ export default function DashboardPage() {
|
|||||||
<CardTitle className="text-sm font-medium">
|
<CardTitle className="text-sm font-medium">
|
||||||
Active Now
|
Active Now
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
<Activity className="h-4 w-4 text-muted-foreground" />
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth="2"
|
||||||
|
className="h-4 w-4 text-muted-foreground"
|
||||||
|
>
|
||||||
|
<path d="M22 12h-4l-3 9L9 3l-3 9H2" />
|
||||||
|
</svg>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<div className="text-2xl font-bold">+573</div>
|
<div className="text-2xl font-bold">+573</div>
|
||||||
|
|||||||
219
apps/www/app/examples/forms/account/account-form.tsx
Normal file
219
apps/www/app/examples/forms/account/account-form.tsx
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod"
|
||||||
|
import { CalendarIcon, CaretSortIcon, CheckIcon } from "@radix-ui/react-icons"
|
||||||
|
import { format } from "date-fns"
|
||||||
|
import { useForm } from "react-hook-form"
|
||||||
|
import * as z from "zod"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
|
import { Calendar } from "@/registry/new-york/ui/calendar"
|
||||||
|
import {
|
||||||
|
Command,
|
||||||
|
CommandEmpty,
|
||||||
|
CommandGroup,
|
||||||
|
CommandInput,
|
||||||
|
CommandItem,
|
||||||
|
} from "@/registry/new-york/ui/command"
|
||||||
|
import {
|
||||||
|
Form,
|
||||||
|
FormControl,
|
||||||
|
FormDescription,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
|
} from "@/registry/new-york/ui/form"
|
||||||
|
import { Input } from "@/registry/new-york/ui/input"
|
||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
PopoverContent,
|
||||||
|
PopoverTrigger,
|
||||||
|
} from "@/registry/new-york/ui/popover"
|
||||||
|
import { toast } from "@/registry/new-york/ui/use-toast"
|
||||||
|
|
||||||
|
const languages = [
|
||||||
|
{ label: "English", value: "en" },
|
||||||
|
{ label: "French", value: "fr" },
|
||||||
|
{ label: "German", value: "de" },
|
||||||
|
{ label: "Spanish", value: "es" },
|
||||||
|
{ label: "Portuguese", value: "pt" },
|
||||||
|
{ label: "Russian", value: "ru" },
|
||||||
|
{ label: "Japanese", value: "ja" },
|
||||||
|
{ label: "Korean", value: "ko" },
|
||||||
|
{ label: "Chinese", value: "zh" },
|
||||||
|
] as const
|
||||||
|
|
||||||
|
const accountFormSchema = z.object({
|
||||||
|
name: z
|
||||||
|
.string()
|
||||||
|
.min(2, {
|
||||||
|
message: "Name must be at least 2 characters.",
|
||||||
|
})
|
||||||
|
.max(30, {
|
||||||
|
message: "Name must not be longer than 30 characters.",
|
||||||
|
}),
|
||||||
|
dob: z.date({
|
||||||
|
required_error: "A date of birth is required.",
|
||||||
|
}),
|
||||||
|
language: z.string({
|
||||||
|
required_error: "Please select a language.",
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
type AccountFormValues = z.infer<typeof accountFormSchema>
|
||||||
|
|
||||||
|
// This can come from your database or API.
|
||||||
|
const defaultValues: Partial<AccountFormValues> = {
|
||||||
|
// name: "Your name",
|
||||||
|
// dob: new Date("2023-01-23"),
|
||||||
|
}
|
||||||
|
|
||||||
|
export function AccountForm() {
|
||||||
|
const form = useForm<AccountFormValues>({
|
||||||
|
resolver: zodResolver(accountFormSchema),
|
||||||
|
defaultValues,
|
||||||
|
})
|
||||||
|
|
||||||
|
function onSubmit(data: AccountFormValues) {
|
||||||
|
toast({
|
||||||
|
title: "You submitted the following values:",
|
||||||
|
description: (
|
||||||
|
<pre className="mt-2 w-[340px] rounded-md bg-slate-950 p-4">
|
||||||
|
<code className="text-white">{JSON.stringify(data, null, 2)}</code>
|
||||||
|
</pre>
|
||||||
|
),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form {...form}>
|
||||||
|
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="name"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Name</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input placeholder="Your name" {...field} />
|
||||||
|
</FormControl>
|
||||||
|
<FormDescription>
|
||||||
|
This is the name that will be displayed on your profile and in
|
||||||
|
emails.
|
||||||
|
</FormDescription>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="dob"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="flex flex-col">
|
||||||
|
<FormLabel>Date of birth</FormLabel>
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger asChild>
|
||||||
|
<FormControl>
|
||||||
|
<Button
|
||||||
|
variant={"outline"}
|
||||||
|
className={cn(
|
||||||
|
"w-[240px] pl-3 text-left font-normal",
|
||||||
|
!field.value && "text-muted-foreground"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{field.value ? (
|
||||||
|
format(field.value, "PPP")
|
||||||
|
) : (
|
||||||
|
<span>Pick a date</span>
|
||||||
|
)}
|
||||||
|
<CalendarIcon className="ml-auto h-4 w-4 opacity-50" />
|
||||||
|
</Button>
|
||||||
|
</FormControl>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent className="w-auto p-0" align="start">
|
||||||
|
<Calendar
|
||||||
|
mode="single"
|
||||||
|
selected={field.value}
|
||||||
|
onSelect={field.onChange}
|
||||||
|
disabled={(date) =>
|
||||||
|
date > new Date() || date < new Date("1900-01-01")
|
||||||
|
}
|
||||||
|
initialFocus
|
||||||
|
/>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
<FormDescription>
|
||||||
|
Your date of birth is used to calculate your age.
|
||||||
|
</FormDescription>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="language"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="flex flex-col">
|
||||||
|
<FormLabel>Language</FormLabel>
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger asChild>
|
||||||
|
<FormControl>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
role="combobox"
|
||||||
|
className={cn(
|
||||||
|
"w-[200px] justify-between",
|
||||||
|
!field.value && "text-muted-foreground"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{field.value
|
||||||
|
? languages.find(
|
||||||
|
(language) => language.value === field.value
|
||||||
|
)?.label
|
||||||
|
: "Select language"}
|
||||||
|
<CaretSortIcon className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||||
|
</Button>
|
||||||
|
</FormControl>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent className="w-[200px] p-0">
|
||||||
|
<Command>
|
||||||
|
<CommandInput placeholder="Search language..." />
|
||||||
|
<CommandEmpty>No language found.</CommandEmpty>
|
||||||
|
<CommandGroup>
|
||||||
|
{languages.map((language) => (
|
||||||
|
<CommandItem
|
||||||
|
value={language.label}
|
||||||
|
key={language.value}
|
||||||
|
onSelect={() => {
|
||||||
|
form.setValue("language", language.value)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CheckIcon
|
||||||
|
className={cn(
|
||||||
|
"mr-2 h-4 w-4",
|
||||||
|
language.value === field.value
|
||||||
|
? "opacity-100"
|
||||||
|
: "opacity-0"
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
{language.label}
|
||||||
|
</CommandItem>
|
||||||
|
))}
|
||||||
|
</CommandGroup>
|
||||||
|
</Command>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
<FormDescription>
|
||||||
|
This is the language that will be used in the dashboard.
|
||||||
|
</FormDescription>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Button type="submit">Update account</Button>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
)
|
||||||
|
}
|
||||||
18
apps/www/app/examples/forms/account/page.tsx
Normal file
18
apps/www/app/examples/forms/account/page.tsx
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { Separator } from "@/registry/new-york/ui/separator"
|
||||||
|
import { AccountForm } from "@/app/examples/forms/account/account-form"
|
||||||
|
|
||||||
|
export default function SettingsAccountPage() {
|
||||||
|
return (
|
||||||
|
<div className="space-y-6">
|
||||||
|
<div>
|
||||||
|
<h3 className="text-lg font-medium">Account</h3>
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
Update your account settings. Set your preferred language and
|
||||||
|
timezone.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<Separator />
|
||||||
|
<AccountForm />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
164
apps/www/app/examples/forms/appearance/appearance-form.tsx
Normal file
164
apps/www/app/examples/forms/appearance/appearance-form.tsx
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod"
|
||||||
|
import { ChevronDownIcon } from "@radix-ui/react-icons"
|
||||||
|
import { useForm } from "react-hook-form"
|
||||||
|
import * as z from "zod"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
import { Button, buttonVariants } from "@/registry/new-york/ui/button"
|
||||||
|
import {
|
||||||
|
Form,
|
||||||
|
FormControl,
|
||||||
|
FormDescription,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
|
} from "@/registry/new-york/ui/form"
|
||||||
|
import { RadioGroup, RadioGroupItem } from "@/registry/new-york/ui/radio-group"
|
||||||
|
import { toast } from "@/registry/new-york/ui/use-toast"
|
||||||
|
|
||||||
|
const appearanceFormSchema = z.object({
|
||||||
|
theme: z.enum(["light", "dark"], {
|
||||||
|
required_error: "Please select a theme.",
|
||||||
|
}),
|
||||||
|
font: z.enum(["inter", "manrope", "system"], {
|
||||||
|
invalid_type_error: "Select a font",
|
||||||
|
required_error: "Please select a font.",
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
type AppearanceFormValues = z.infer<typeof appearanceFormSchema>
|
||||||
|
|
||||||
|
// This can come from your database or API.
|
||||||
|
const defaultValues: Partial<AppearanceFormValues> = {
|
||||||
|
theme: "light",
|
||||||
|
}
|
||||||
|
|
||||||
|
export function AppearanceForm() {
|
||||||
|
const form = useForm<AppearanceFormValues>({
|
||||||
|
resolver: zodResolver(appearanceFormSchema),
|
||||||
|
defaultValues,
|
||||||
|
})
|
||||||
|
|
||||||
|
function onSubmit(data: AppearanceFormValues) {
|
||||||
|
toast({
|
||||||
|
title: "You submitted the following values:",
|
||||||
|
description: (
|
||||||
|
<pre className="mt-2 w-[340px] rounded-md bg-slate-950 p-4">
|
||||||
|
<code className="text-white">{JSON.stringify(data, null, 2)}</code>
|
||||||
|
</pre>
|
||||||
|
),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form {...form}>
|
||||||
|
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="font"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Font</FormLabel>
|
||||||
|
<div className="relative w-max">
|
||||||
|
<FormControl>
|
||||||
|
<select
|
||||||
|
className={cn(
|
||||||
|
buttonVariants({ variant: "outline" }),
|
||||||
|
"w-[200px] appearance-none bg-transparent font-normal"
|
||||||
|
)}
|
||||||
|
{...field}
|
||||||
|
>
|
||||||
|
<option value="inter">Inter</option>
|
||||||
|
<option value="manrope">Manrope</option>
|
||||||
|
<option value="system">System</option>
|
||||||
|
</select>
|
||||||
|
</FormControl>
|
||||||
|
<ChevronDownIcon className="absolute right-3 top-2.5 h-4 w-4 opacity-50" />
|
||||||
|
</div>
|
||||||
|
<FormDescription>
|
||||||
|
Set the font you want to use in the dashboard.
|
||||||
|
</FormDescription>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="theme"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="space-y-1">
|
||||||
|
<FormLabel>Theme</FormLabel>
|
||||||
|
<FormDescription>
|
||||||
|
Select the theme for the dashboard.
|
||||||
|
</FormDescription>
|
||||||
|
<FormMessage />
|
||||||
|
<RadioGroup
|
||||||
|
onValueChange={field.onChange}
|
||||||
|
defaultValue={field.value}
|
||||||
|
className="grid max-w-md grid-cols-2 gap-8 pt-2"
|
||||||
|
>
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel className="[&:has([data-state=checked])>div]:border-primary">
|
||||||
|
<FormControl>
|
||||||
|
<RadioGroupItem value="light" className="sr-only" />
|
||||||
|
</FormControl>
|
||||||
|
<div className="items-center rounded-md border-2 border-muted p-1 hover:border-accent">
|
||||||
|
<div className="space-y-2 rounded-sm bg-[#ecedef] p-2">
|
||||||
|
<div className="space-y-2 rounded-md bg-white p-2 shadow-sm">
|
||||||
|
<div className="h-2 w-[80px] rounded-lg bg-[#ecedef]" />
|
||||||
|
<div className="h-2 w-[100px] rounded-lg bg-[#ecedef]" />
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-2 rounded-md bg-white p-2 shadow-sm">
|
||||||
|
<div className="h-4 w-4 rounded-full bg-[#ecedef]" />
|
||||||
|
<div className="h-2 w-[100px] rounded-lg bg-[#ecedef]" />
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-2 rounded-md bg-white p-2 shadow-sm">
|
||||||
|
<div className="h-4 w-4 rounded-full bg-[#ecedef]" />
|
||||||
|
<div className="h-2 w-[100px] rounded-lg bg-[#ecedef]" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span className="block w-full p-2 text-center font-normal">
|
||||||
|
Light
|
||||||
|
</span>
|
||||||
|
</FormLabel>
|
||||||
|
</FormItem>
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel className="[&:has([data-state=checked])>div]:border-primary">
|
||||||
|
<FormControl>
|
||||||
|
<RadioGroupItem value="dark" className="sr-only" />
|
||||||
|
</FormControl>
|
||||||
|
<div className="items-center rounded-md border-2 border-muted bg-popover p-1 hover:bg-accent hover:text-accent-foreground">
|
||||||
|
<div className="space-y-2 rounded-sm bg-slate-950 p-2">
|
||||||
|
<div className="space-y-2 rounded-md bg-slate-800 p-2 shadow-sm">
|
||||||
|
<div className="h-2 w-[80px] rounded-lg bg-slate-400" />
|
||||||
|
<div className="h-2 w-[100px] rounded-lg bg-slate-400" />
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-2 rounded-md bg-slate-800 p-2 shadow-sm">
|
||||||
|
<div className="h-4 w-4 rounded-full bg-slate-400" />
|
||||||
|
<div className="h-2 w-[100px] rounded-lg bg-slate-400" />
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-2 rounded-md bg-slate-800 p-2 shadow-sm">
|
||||||
|
<div className="h-4 w-4 rounded-full bg-slate-400" />
|
||||||
|
<div className="h-2 w-[100px] rounded-lg bg-slate-400" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span className="block w-full p-2 text-center font-normal">
|
||||||
|
Dark
|
||||||
|
</span>
|
||||||
|
</FormLabel>
|
||||||
|
</FormItem>
|
||||||
|
</RadioGroup>
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Button type="submit">Update preferences</Button>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
)
|
||||||
|
}
|
||||||
18
apps/www/app/examples/forms/appearance/page.tsx
Normal file
18
apps/www/app/examples/forms/appearance/page.tsx
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { Separator } from "@/registry/new-york/ui/separator"
|
||||||
|
import { AppearanceForm } from "@/app/examples/forms/appearance/appearance-form"
|
||||||
|
|
||||||
|
export default function SettingsAppearancePage() {
|
||||||
|
return (
|
||||||
|
<div className="space-y-6">
|
||||||
|
<div>
|
||||||
|
<h3 className="text-lg font-medium">Appearance</h3>
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
Customize the appearance of the app. Automatically switch between day
|
||||||
|
and night themes.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<Separator />
|
||||||
|
<AppearanceForm />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
44
apps/www/app/examples/forms/components/sidebar-nav.tsx
Normal file
44
apps/www/app/examples/forms/components/sidebar-nav.tsx
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import Link from "next/link"
|
||||||
|
import { usePathname } from "next/navigation"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
import { buttonVariants } from "@/registry/new-york/ui/button"
|
||||||
|
|
||||||
|
interface SidebarNavProps extends React.HTMLAttributes<HTMLElement> {
|
||||||
|
items: {
|
||||||
|
href: string
|
||||||
|
title: string
|
||||||
|
}[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SidebarNav({ className, items, ...props }: SidebarNavProps) {
|
||||||
|
const pathname = usePathname()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<nav
|
||||||
|
className={cn(
|
||||||
|
"flex space-x-2 lg:flex-col lg:space-x-0 lg:space-y-1",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{items.map((item) => (
|
||||||
|
<Link
|
||||||
|
key={item.href}
|
||||||
|
href={item.href}
|
||||||
|
className={cn(
|
||||||
|
buttonVariants({ variant: "ghost" }),
|
||||||
|
pathname === item.href
|
||||||
|
? "bg-muted hover:bg-muted"
|
||||||
|
: "hover:bg-transparent hover:underline",
|
||||||
|
"justify-start"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{item.title}
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
</nav>
|
||||||
|
)
|
||||||
|
}
|
||||||
132
apps/www/app/examples/forms/display/display-form.tsx
Normal file
132
apps/www/app/examples/forms/display/display-form.tsx
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod"
|
||||||
|
import { useForm } from "react-hook-form"
|
||||||
|
import * as z from "zod"
|
||||||
|
|
||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
|
import { Checkbox } from "@/registry/new-york/ui/checkbox"
|
||||||
|
import {
|
||||||
|
Form,
|
||||||
|
FormControl,
|
||||||
|
FormDescription,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
|
} from "@/registry/new-york/ui/form"
|
||||||
|
import { toast } from "@/registry/new-york/ui/use-toast"
|
||||||
|
|
||||||
|
const items = [
|
||||||
|
{
|
||||||
|
id: "recents",
|
||||||
|
label: "Recents",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "home",
|
||||||
|
label: "Home",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "applications",
|
||||||
|
label: "Applications",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "desktop",
|
||||||
|
label: "Desktop",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "downloads",
|
||||||
|
label: "Downloads",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "documents",
|
||||||
|
label: "Documents",
|
||||||
|
},
|
||||||
|
] as const
|
||||||
|
|
||||||
|
const displayFormSchema = z.object({
|
||||||
|
items: z.array(z.string()).refine((value) => value.some((item) => item), {
|
||||||
|
message: "You have to select at least one item.",
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
type DisplayFormValues = z.infer<typeof displayFormSchema>
|
||||||
|
|
||||||
|
// This can come from your database or API.
|
||||||
|
const defaultValues: Partial<DisplayFormValues> = {
|
||||||
|
items: ["recents", "home"],
|
||||||
|
}
|
||||||
|
|
||||||
|
export function DisplayForm() {
|
||||||
|
const form = useForm<DisplayFormValues>({
|
||||||
|
resolver: zodResolver(displayFormSchema),
|
||||||
|
defaultValues,
|
||||||
|
})
|
||||||
|
|
||||||
|
function onSubmit(data: DisplayFormValues) {
|
||||||
|
toast({
|
||||||
|
title: "You submitted the following values:",
|
||||||
|
description: (
|
||||||
|
<pre className="mt-2 w-[340px] rounded-md bg-slate-950 p-4">
|
||||||
|
<code className="text-white">{JSON.stringify(data, null, 2)}</code>
|
||||||
|
</pre>
|
||||||
|
),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form {...form}>
|
||||||
|
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="items"
|
||||||
|
render={() => (
|
||||||
|
<FormItem>
|
||||||
|
<div className="mb-4">
|
||||||
|
<FormLabel className="text-base">Sidebar</FormLabel>
|
||||||
|
<FormDescription>
|
||||||
|
Select the items you want to display in the sidebar.
|
||||||
|
</FormDescription>
|
||||||
|
</div>
|
||||||
|
{items.map((item) => (
|
||||||
|
<FormField
|
||||||
|
key={item.id}
|
||||||
|
control={form.control}
|
||||||
|
name="items"
|
||||||
|
render={({ field }) => {
|
||||||
|
return (
|
||||||
|
<FormItem
|
||||||
|
key={item.id}
|
||||||
|
className="flex flex-row items-start space-x-3 space-y-0"
|
||||||
|
>
|
||||||
|
<FormControl>
|
||||||
|
<Checkbox
|
||||||
|
checked={field.value?.includes(item.id)}
|
||||||
|
onCheckedChange={(checked) => {
|
||||||
|
return checked
|
||||||
|
? field.onChange([...field.value, item.id])
|
||||||
|
: field.onChange(
|
||||||
|
field.value?.filter(
|
||||||
|
(value) => value !== item.id
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormLabel className="font-normal">
|
||||||
|
{item.label}
|
||||||
|
</FormLabel>
|
||||||
|
</FormItem>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Button type="submit">Update display</Button>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
)
|
||||||
|
}
|
||||||
17
apps/www/app/examples/forms/display/page.tsx
Normal file
17
apps/www/app/examples/forms/display/page.tsx
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { Separator } from "@/registry/new-york/ui/separator"
|
||||||
|
import { DisplayForm } from "@/app/examples/forms/display/display-form"
|
||||||
|
|
||||||
|
export default function SettingsDisplayPage() {
|
||||||
|
return (
|
||||||
|
<div className="space-y-6">
|
||||||
|
<div>
|
||||||
|
<h3 className="text-lg font-medium">Display</h3>
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
Turn items on or off to control what's displayed in the app.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<Separator />
|
||||||
|
<DisplayForm />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
75
apps/www/app/examples/forms/layout.tsx
Normal file
75
apps/www/app/examples/forms/layout.tsx
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
import { Metadata } from "next"
|
||||||
|
import Image from "next/image"
|
||||||
|
|
||||||
|
import { Separator } from "@/registry/new-york/ui/separator"
|
||||||
|
import { SidebarNav } from "@/app/examples/forms/components/sidebar-nav"
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "Forms",
|
||||||
|
description: "Advanced form example using react-hook-form and Zod.",
|
||||||
|
}
|
||||||
|
|
||||||
|
const sidebarNavItems = [
|
||||||
|
{
|
||||||
|
title: "Profile",
|
||||||
|
href: "/examples/forms",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Account",
|
||||||
|
href: "/examples/forms/account",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Appearance",
|
||||||
|
href: "/examples/forms/appearance",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Notifications",
|
||||||
|
href: "/examples/forms/notifications",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Display",
|
||||||
|
href: "/examples/forms/display",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
interface SettingsLayoutProps {
|
||||||
|
children: React.ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function SettingsLayout({ children }: SettingsLayoutProps) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="md:hidden">
|
||||||
|
<Image
|
||||||
|
src="/examples/forms-light.png"
|
||||||
|
width={1280}
|
||||||
|
height={791}
|
||||||
|
alt="Forms"
|
||||||
|
className="block dark:hidden"
|
||||||
|
/>
|
||||||
|
<Image
|
||||||
|
src="/examples/forms-dark.png"
|
||||||
|
width={1280}
|
||||||
|
height={791}
|
||||||
|
alt="Forms"
|
||||||
|
className="hidden dark:block"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="hidden space-y-6 p-10 pb-16 md:block">
|
||||||
|
<div className="space-y-0.5">
|
||||||
|
<h2 className="text-2xl font-bold tracking-tight">Settings</h2>
|
||||||
|
<p className="text-muted-foreground">
|
||||||
|
Manage your account settings and set e-mail preferences.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<Separator className="my-6" />
|
||||||
|
<div className="flex flex-col space-y-8 lg:flex-row lg:space-x-12 lg:space-y-0">
|
||||||
|
<aside className="-mx-4 lg:w-1/5">
|
||||||
|
<SidebarNav items={sidebarNavItems} />
|
||||||
|
</aside>
|
||||||
|
<div className="flex-1 lg:max-w-2xl">{children}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
222
apps/www/app/examples/forms/notifications/notifications-form.tsx
Normal file
222
apps/www/app/examples/forms/notifications/notifications-form.tsx
Normal file
@@ -0,0 +1,222 @@
|
|||||||
|
"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 "@/registry/new-york/ui/button"
|
||||||
|
import { Checkbox } from "@/registry/new-york/ui/checkbox"
|
||||||
|
import {
|
||||||
|
Form,
|
||||||
|
FormControl,
|
||||||
|
FormDescription,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
|
} from "@/registry/new-york/ui/form"
|
||||||
|
import { RadioGroup, RadioGroupItem } from "@/registry/new-york/ui/radio-group"
|
||||||
|
import { Switch } from "@/registry/new-york/ui/switch"
|
||||||
|
import { toast } from "@/registry/new-york/ui/use-toast"
|
||||||
|
|
||||||
|
const notificationsFormSchema = z.object({
|
||||||
|
type: z.enum(["all", "mentions", "none"], {
|
||||||
|
required_error: "You need to select a notification type.",
|
||||||
|
}),
|
||||||
|
mobile: z.boolean().default(false).optional(),
|
||||||
|
communication_emails: z.boolean().default(false).optional(),
|
||||||
|
social_emails: z.boolean().default(false).optional(),
|
||||||
|
marketing_emails: z.boolean().default(false).optional(),
|
||||||
|
security_emails: z.boolean(),
|
||||||
|
})
|
||||||
|
|
||||||
|
type NotificationsFormValues = z.infer<typeof notificationsFormSchema>
|
||||||
|
|
||||||
|
// This can come from your database or API.
|
||||||
|
const defaultValues: Partial<NotificationsFormValues> = {
|
||||||
|
communication_emails: false,
|
||||||
|
marketing_emails: false,
|
||||||
|
social_emails: true,
|
||||||
|
security_emails: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
export function NotificationsForm() {
|
||||||
|
const form = useForm<NotificationsFormValues>({
|
||||||
|
resolver: zodResolver(notificationsFormSchema),
|
||||||
|
defaultValues,
|
||||||
|
})
|
||||||
|
|
||||||
|
function onSubmit(data: NotificationsFormValues) {
|
||||||
|
toast({
|
||||||
|
title: "You submitted the following values:",
|
||||||
|
description: (
|
||||||
|
<pre className="mt-2 w-[340px] rounded-md bg-slate-950 p-4">
|
||||||
|
<code className="text-white">{JSON.stringify(data, null, 2)}</code>
|
||||||
|
</pre>
|
||||||
|
),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form {...form}>
|
||||||
|
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="type"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="space-y-3">
|
||||||
|
<FormLabel>Notify me about...</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<RadioGroup
|
||||||
|
onValueChange={field.onChange}
|
||||||
|
defaultValue={field.value}
|
||||||
|
className="flex flex-col space-y-1"
|
||||||
|
>
|
||||||
|
<FormItem className="flex items-center space-x-3 space-y-0">
|
||||||
|
<FormControl>
|
||||||
|
<RadioGroupItem value="all" />
|
||||||
|
</FormControl>
|
||||||
|
<FormLabel className="font-normal">
|
||||||
|
All new messages
|
||||||
|
</FormLabel>
|
||||||
|
</FormItem>
|
||||||
|
<FormItem className="flex items-center space-x-3 space-y-0">
|
||||||
|
<FormControl>
|
||||||
|
<RadioGroupItem value="mentions" />
|
||||||
|
</FormControl>
|
||||||
|
<FormLabel className="font-normal">
|
||||||
|
Direct messages and mentions
|
||||||
|
</FormLabel>
|
||||||
|
</FormItem>
|
||||||
|
<FormItem className="flex items-center space-x-3 space-y-0">
|
||||||
|
<FormControl>
|
||||||
|
<RadioGroupItem value="none" />
|
||||||
|
</FormControl>
|
||||||
|
<FormLabel className="font-normal">Nothing</FormLabel>
|
||||||
|
</FormItem>
|
||||||
|
</RadioGroup>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<div>
|
||||||
|
<h3 className="mb-4 text-lg font-medium">Email Notifications</h3>
|
||||||
|
<div className="space-y-4">
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="communication_emails"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="flex flex-row items-center justify-between rounded-lg border p-4">
|
||||||
|
<div className="space-y-0.5">
|
||||||
|
<FormLabel className="text-base">
|
||||||
|
Communication emails
|
||||||
|
</FormLabel>
|
||||||
|
<FormDescription>
|
||||||
|
Receive emails about your account activity.
|
||||||
|
</FormDescription>
|
||||||
|
</div>
|
||||||
|
<FormControl>
|
||||||
|
<Switch
|
||||||
|
checked={field.value}
|
||||||
|
onCheckedChange={field.onChange}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="marketing_emails"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="flex flex-row items-center justify-between rounded-lg border p-4">
|
||||||
|
<div className="space-y-0.5">
|
||||||
|
<FormLabel className="text-base">
|
||||||
|
Marketing emails
|
||||||
|
</FormLabel>
|
||||||
|
<FormDescription>
|
||||||
|
Receive emails about new products, features, and more.
|
||||||
|
</FormDescription>
|
||||||
|
</div>
|
||||||
|
<FormControl>
|
||||||
|
<Switch
|
||||||
|
checked={field.value}
|
||||||
|
onCheckedChange={field.onChange}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="social_emails"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="flex flex-row items-center justify-between rounded-lg border p-4">
|
||||||
|
<div className="space-y-0.5">
|
||||||
|
<FormLabel className="text-base">Social emails</FormLabel>
|
||||||
|
<FormDescription>
|
||||||
|
Receive emails for friend requests, follows, and more.
|
||||||
|
</FormDescription>
|
||||||
|
</div>
|
||||||
|
<FormControl>
|
||||||
|
<Switch
|
||||||
|
checked={field.value}
|
||||||
|
onCheckedChange={field.onChange}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="security_emails"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="flex flex-row items-center justify-between rounded-lg border p-4">
|
||||||
|
<div className="space-y-0.5">
|
||||||
|
<FormLabel className="text-base">Security emails</FormLabel>
|
||||||
|
<FormDescription>
|
||||||
|
Receive emails about your account activity and security.
|
||||||
|
</FormDescription>
|
||||||
|
</div>
|
||||||
|
<FormControl>
|
||||||
|
<Switch
|
||||||
|
checked={field.value}
|
||||||
|
onCheckedChange={field.onChange}
|
||||||
|
disabled
|
||||||
|
aria-readonly
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="mobile"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="flex flex-row items-start space-x-3 space-y-0">
|
||||||
|
<FormControl>
|
||||||
|
<Checkbox
|
||||||
|
checked={field.value}
|
||||||
|
onCheckedChange={field.onChange}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<div className="space-y-1 leading-none">
|
||||||
|
<FormLabel>
|
||||||
|
Use different settings for my mobile devices
|
||||||
|
</FormLabel>
|
||||||
|
<FormDescription>
|
||||||
|
You can manage your mobile notifications in the{" "}
|
||||||
|
<Link href="/examples/forms">mobile settings</Link> page.
|
||||||
|
</FormDescription>
|
||||||
|
</div>
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Button type="submit">Update notifications</Button>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
)
|
||||||
|
}
|
||||||
17
apps/www/app/examples/forms/notifications/page.tsx
Normal file
17
apps/www/app/examples/forms/notifications/page.tsx
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { Separator } from "@/registry/new-york/ui/separator"
|
||||||
|
import { NotificationsForm } from "@/app/examples/forms/notifications/notifications-form"
|
||||||
|
|
||||||
|
export default function SettingsNotificationsPage() {
|
||||||
|
return (
|
||||||
|
<div className="space-y-6">
|
||||||
|
<div>
|
||||||
|
<h3 className="text-lg font-medium">Notifications</h3>
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
Configure how you receive notifications.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<Separator />
|
||||||
|
<NotificationsForm />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
17
apps/www/app/examples/forms/page.tsx
Normal file
17
apps/www/app/examples/forms/page.tsx
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { Separator } from "@/registry/new-york/ui/separator"
|
||||||
|
import { ProfileForm } from "@/app/examples/forms/profile-form"
|
||||||
|
|
||||||
|
export default function SettingsProfilePage() {
|
||||||
|
return (
|
||||||
|
<div className="space-y-6">
|
||||||
|
<div>
|
||||||
|
<h3 className="text-lg font-medium">Profile</h3>
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
This is how others will see you on the site.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<Separator />
|
||||||
|
<ProfileForm />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
191
apps/www/app/examples/forms/profile-form.tsx
Normal file
191
apps/www/app/examples/forms/profile-form.tsx
Normal file
@@ -0,0 +1,191 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import Link from "next/link"
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod"
|
||||||
|
import { useFieldArray, useForm } from "react-hook-form"
|
||||||
|
import * as z from "zod"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
|
import {
|
||||||
|
Form,
|
||||||
|
FormControl,
|
||||||
|
FormDescription,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
|
} from "@/registry/new-york/ui/form"
|
||||||
|
import { Input } from "@/registry/new-york/ui/input"
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from "@/registry/new-york/ui/select"
|
||||||
|
import { Textarea } from "@/registry/new-york/ui/textarea"
|
||||||
|
import { toast } from "@/registry/new-york/ui/use-toast"
|
||||||
|
|
||||||
|
const profileFormSchema = z.object({
|
||||||
|
username: z
|
||||||
|
.string()
|
||||||
|
.min(2, {
|
||||||
|
message: "Username must be at least 2 characters.",
|
||||||
|
})
|
||||||
|
.max(30, {
|
||||||
|
message: "Username must not be longer than 30 characters.",
|
||||||
|
}),
|
||||||
|
email: z
|
||||||
|
.string({
|
||||||
|
required_error: "Please select an email to display.",
|
||||||
|
})
|
||||||
|
.email(),
|
||||||
|
bio: z.string().max(160).min(4),
|
||||||
|
urls: z
|
||||||
|
.array(
|
||||||
|
z.object({
|
||||||
|
value: z.string().url({ message: "Please enter a valid URL." }),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.optional(),
|
||||||
|
})
|
||||||
|
|
||||||
|
type ProfileFormValues = z.infer<typeof profileFormSchema>
|
||||||
|
|
||||||
|
// This can come from your database or API.
|
||||||
|
const defaultValues: Partial<ProfileFormValues> = {
|
||||||
|
bio: "I own a computer.",
|
||||||
|
urls: [
|
||||||
|
{ value: "https://shadcn.com" },
|
||||||
|
{ value: "http://twitter.com/shadcn" },
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ProfileForm() {
|
||||||
|
const form = useForm<ProfileFormValues>({
|
||||||
|
resolver: zodResolver(profileFormSchema),
|
||||||
|
defaultValues,
|
||||||
|
mode: "onChange",
|
||||||
|
})
|
||||||
|
|
||||||
|
const { fields, append } = useFieldArray({
|
||||||
|
name: "urls",
|
||||||
|
control: form.control,
|
||||||
|
})
|
||||||
|
|
||||||
|
function onSubmit(data: ProfileFormValues) {
|
||||||
|
toast({
|
||||||
|
title: "You submitted the following values:",
|
||||||
|
description: (
|
||||||
|
<pre className="mt-2 w-[340px] rounded-md bg-slate-950 p-4">
|
||||||
|
<code className="text-white">{JSON.stringify(data, null, 2)}</code>
|
||||||
|
</pre>
|
||||||
|
),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form {...form}>
|
||||||
|
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="username"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Username</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input placeholder="shadcn" {...field} />
|
||||||
|
</FormControl>
|
||||||
|
<FormDescription>
|
||||||
|
This is your public display name. It can be your real name or a
|
||||||
|
pseudonym. You can only change this once every 30 days.
|
||||||
|
</FormDescription>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="email"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Email</FormLabel>
|
||||||
|
<Select onValueChange={field.onChange} defaultValue={field.value}>
|
||||||
|
<FormControl>
|
||||||
|
<SelectTrigger>
|
||||||
|
<SelectValue placeholder="Select a verified email to display" />
|
||||||
|
</SelectTrigger>
|
||||||
|
</FormControl>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="m@example.com">m@example.com</SelectItem>
|
||||||
|
<SelectItem value="m@google.com">m@google.com</SelectItem>
|
||||||
|
<SelectItem value="m@support.com">m@support.com</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
<FormDescription>
|
||||||
|
You can manage verified email addresses in your{" "}
|
||||||
|
<Link href="/examples/forms">email settings</Link>.
|
||||||
|
</FormDescription>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="bio"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Bio</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Textarea
|
||||||
|
placeholder="Tell us a little bit about yourself"
|
||||||
|
className="resize-none"
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormDescription>
|
||||||
|
You can <span>@mention</span> other users and organizations to
|
||||||
|
link to them.
|
||||||
|
</FormDescription>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<div>
|
||||||
|
{fields.map((field, index) => (
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
key={field.id}
|
||||||
|
name={`urls.${index}.value`}
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel className={cn(index !== 0 && "sr-only")}>
|
||||||
|
URLs
|
||||||
|
</FormLabel>
|
||||||
|
<FormDescription className={cn(index !== 0 && "sr-only")}>
|
||||||
|
Add links to your website, blog, or social media profiles.
|
||||||
|
</FormDescription>
|
||||||
|
<FormControl>
|
||||||
|
<Input {...field} />
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
className="mt-2"
|
||||||
|
onClick={() => append({ value: "" })}
|
||||||
|
>
|
||||||
|
Add URL
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<Button type="submit">Update profile</Button>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,14 +1,16 @@
|
|||||||
import { Metadata } from "next"
|
import { Metadata } from "next"
|
||||||
import Link from "next/link"
|
import Link from "next/link"
|
||||||
|
import { ArrowRightIcon } from "@radix-ui/react-icons"
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
import { buttonVariants } from "@/components/ui/button"
|
|
||||||
import { ExamplesNav } from "@/components/examples-nav"
|
import { ExamplesNav } from "@/components/examples-nav"
|
||||||
import {
|
import {
|
||||||
PageHeader,
|
PageHeader,
|
||||||
PageHeaderDescription,
|
PageHeaderDescription,
|
||||||
PageHeaderHeading,
|
PageHeaderHeading,
|
||||||
} from "@/components/page-header"
|
} from "@/components/page-header"
|
||||||
|
import { buttonVariants } from "@/registry/new-york/ui/button"
|
||||||
|
import { Separator } from "@/registry/new-york/ui/separator"
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Examples",
|
title: "Examples",
|
||||||
@@ -22,8 +24,19 @@ interface ExamplesLayoutProps {
|
|||||||
export default function ExamplesLayout({ children }: ExamplesLayoutProps) {
|
export default function ExamplesLayout({ children }: ExamplesLayoutProps) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="container relative pb-10">
|
<div className="container relative">
|
||||||
<PageHeader className="page-header">
|
<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>
|
||||||
<PageHeaderHeading className="hidden md:block">
|
<PageHeaderHeading className="hidden md:block">
|
||||||
Check out some examples.
|
Check out some examples.
|
||||||
</PageHeaderHeading>
|
</PageHeaderHeading>
|
||||||
@@ -32,34 +45,27 @@ export default function ExamplesLayout({ children }: ExamplesLayoutProps) {
|
|||||||
Dashboard, cards, authentication. Some examples built using the
|
Dashboard, cards, authentication. Some examples built using the
|
||||||
components. Use this as a guide to build your own.
|
components. Use this as a guide to build your own.
|
||||||
</PageHeaderDescription>
|
</PageHeaderDescription>
|
||||||
|
<section className="flex w-full items-center space-x-4 pb-8 pt-4 md:pb-10">
|
||||||
|
<Link
|
||||||
|
href="/docs"
|
||||||
|
className={cn(buttonVariants(), "rounded-[6px]")}
|
||||||
|
>
|
||||||
|
Get Started
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
href="/components"
|
||||||
|
className={cn(
|
||||||
|
buttonVariants({ variant: "outline" }),
|
||||||
|
"rounded-[6px]"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
Components
|
||||||
|
</Link>
|
||||||
|
</section>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
<section className="pb-6 md:pb-10">
|
|
||||||
<div className="flex w-full items-center justify-between">
|
|
||||||
<div className="flex space-x-4">
|
|
||||||
<Link
|
|
||||||
href="/docs"
|
|
||||||
className={cn(
|
|
||||||
buttonVariants({ size: "lg" }),
|
|
||||||
"rounded-[0.5rem]"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
Get Started
|
|
||||||
</Link>
|
|
||||||
<Link
|
|
||||||
href="/components"
|
|
||||||
className={cn(
|
|
||||||
buttonVariants({ variant: "outline", size: "lg" }),
|
|
||||||
"rounded-[0.5rem] pl-6"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
Components
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
<section>
|
<section>
|
||||||
<ExamplesNav />
|
<ExamplesNav />
|
||||||
<div className="overflow-hidden rounded-[0.5rem] border bg-background shadow-xl">
|
<div className="overflow-hidden rounded-[0.5rem] border bg-background shadow">
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import Image from "next/image"
|
import Image from "next/image"
|
||||||
import { ListMusic, PlusCircle } from "lucide-react"
|
import { PlusCircledIcon } from "@radix-ui/react-icons"
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
import { AspectRatio } from "@/components/ui/aspect-ratio"
|
|
||||||
import {
|
import {
|
||||||
ContextMenu,
|
ContextMenu,
|
||||||
ContextMenuContent,
|
ContextMenuContent,
|
||||||
@@ -12,7 +11,7 @@ import {
|
|||||||
ContextMenuSubContent,
|
ContextMenuSubContent,
|
||||||
ContextMenuSubTrigger,
|
ContextMenuSubTrigger,
|
||||||
ContextMenuTrigger,
|
ContextMenuTrigger,
|
||||||
} from "@/components/ui/context-menu"
|
} from "@/registry/new-york/ui/context-menu"
|
||||||
|
|
||||||
import { Album } from "../data/albums"
|
import { Album } from "../data/albums"
|
||||||
import { playlists } from "../data/playlists"
|
import { playlists } from "../data/playlists"
|
||||||
@@ -55,13 +54,25 @@ export function AlbumArtwork({
|
|||||||
<ContextMenuSubTrigger>Add to Playlist</ContextMenuSubTrigger>
|
<ContextMenuSubTrigger>Add to Playlist</ContextMenuSubTrigger>
|
||||||
<ContextMenuSubContent className="w-48">
|
<ContextMenuSubContent className="w-48">
|
||||||
<ContextMenuItem>
|
<ContextMenuItem>
|
||||||
<PlusCircle className="mr-2 h-4 w-4" />
|
<PlusCircledIcon className="mr-2 h-4 w-4" />
|
||||||
New Playlist
|
New Playlist
|
||||||
</ContextMenuItem>
|
</ContextMenuItem>
|
||||||
<ContextMenuSeparator />
|
<ContextMenuSeparator />
|
||||||
{playlists.map((playlist) => (
|
{playlists.map((playlist) => (
|
||||||
<ContextMenuItem key={playlist}>
|
<ContextMenuItem key={playlist}>
|
||||||
<ListMusic className="mr-2 h-4 w-4" /> {playlist}
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth="2"
|
||||||
|
className="mr-2 h-4 w-4"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path d="M21 15V6M18.5 18a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5ZM12 12H3M16 6H3M12 18H3" />
|
||||||
|
</svg>
|
||||||
|
{playlist}
|
||||||
</ContextMenuItem>
|
</ContextMenuItem>
|
||||||
))}
|
))}
|
||||||
</ContextMenuSubContent>
|
</ContextMenuSubContent>
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import { Globe, Mic } from "lucide-react"
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Menubar,
|
Menubar,
|
||||||
MenubarCheckboxItem,
|
MenubarCheckboxItem,
|
||||||
@@ -15,7 +13,7 @@ import {
|
|||||||
MenubarSubContent,
|
MenubarSubContent,
|
||||||
MenubarSubTrigger,
|
MenubarSubTrigger,
|
||||||
MenubarTrigger,
|
MenubarTrigger,
|
||||||
} from "@/components/ui/menubar"
|
} from "@/registry/new-york/ui/menubar"
|
||||||
|
|
||||||
export function Menu() {
|
export function Menu() {
|
||||||
return (
|
return (
|
||||||
@@ -130,13 +128,37 @@ export function Menu() {
|
|||||||
<MenubarItem>
|
<MenubarItem>
|
||||||
Smart Dictation...{" "}
|
Smart Dictation...{" "}
|
||||||
<MenubarShortcut>
|
<MenubarShortcut>
|
||||||
<Mic className="h-4 w-4" />
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth="2"
|
||||||
|
className="h-4 w-4"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path d="m12 8-9.04 9.06a2.82 2.82 0 1 0 3.98 3.98L16 12" />
|
||||||
|
<circle cx="17" cy="7" r="5" />
|
||||||
|
</svg>
|
||||||
</MenubarShortcut>
|
</MenubarShortcut>
|
||||||
</MenubarItem>
|
</MenubarItem>
|
||||||
<MenubarItem>
|
<MenubarItem>
|
||||||
Emoji & Symbols{" "}
|
Emoji & Symbols{" "}
|
||||||
<MenubarShortcut>
|
<MenubarShortcut>
|
||||||
<Globe className="h-4 w-4" />
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth="2"
|
||||||
|
className="h-4 w-4"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<circle cx="12" cy="12" r="10" />
|
||||||
|
<path d="M2 12h20M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z" />
|
||||||
|
</svg>
|
||||||
</MenubarShortcut>
|
</MenubarShortcut>
|
||||||
</MenubarItem>
|
</MenubarItem>
|
||||||
</MenubarContent>
|
</MenubarContent>
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
import { Plus, Podcast } from "lucide-react"
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
|
|
||||||
import { Button } from "@/components/ui/button"
|
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
@@ -9,15 +7,29 @@ import {
|
|||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
DialogTrigger,
|
DialogTrigger,
|
||||||
} from "@/components/ui/dialog"
|
} from "@/registry/new-york/ui/dialog"
|
||||||
import { Input } from "@/components/ui/input"
|
import { Input } from "@/registry/new-york/ui/input"
|
||||||
import { Label } from "@/components/ui/label"
|
import { Label } from "@/registry/new-york/ui/label"
|
||||||
|
|
||||||
export function PodcastEmptyPlaceholder() {
|
export function PodcastEmptyPlaceholder() {
|
||||||
return (
|
return (
|
||||||
<div className="flex h-[450px] shrink-0 items-center justify-center rounded-md border border-dashed">
|
<div className="flex h-[450px] shrink-0 items-center justify-center rounded-md border border-dashed">
|
||||||
<div className="mx-auto flex max-w-[420px] flex-col items-center justify-center text-center">
|
<div className="mx-auto flex max-w-[420px] flex-col items-center justify-center text-center">
|
||||||
<Podcast className="h-10 w-10 text-muted-foreground" />
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth="2"
|
||||||
|
className="h-10 w-10 text-muted-foreground"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<circle cx="12" cy="11" r="1" />
|
||||||
|
<path d="M11 17a1 1 0 0 1 2 0c0 .5-.34 3-.5 4.5a.5.5 0 0 1-1 0c-.16-1.5-.5-4-.5-4.5ZM8 14a5 5 0 1 1 8 0" />
|
||||||
|
<path d="M17 18.5a9 9 0 1 0-10 0" />
|
||||||
|
</svg>
|
||||||
|
|
||||||
<h3 className="mt-4 text-lg font-semibold">No episodes added</h3>
|
<h3 className="mt-4 text-lg font-semibold">No episodes added</h3>
|
||||||
<p className="mb-4 mt-2 text-sm text-muted-foreground">
|
<p className="mb-4 mt-2 text-sm text-muted-foreground">
|
||||||
You have not added any podcasts. Add one below.
|
You have not added any podcasts. Add one below.
|
||||||
@@ -25,7 +37,6 @@ export function PodcastEmptyPlaceholder() {
|
|||||||
<Dialog>
|
<Dialog>
|
||||||
<DialogTrigger>
|
<DialogTrigger>
|
||||||
<Button size="sm" className="relative">
|
<Button size="sm" className="relative">
|
||||||
<Plus className="mr-2 h-4 w-4" />
|
|
||||||
Add Podcast
|
Add Podcast
|
||||||
</Button>
|
</Button>
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
|
|||||||
@@ -1,18 +1,6 @@
|
|||||||
import {
|
|
||||||
LayoutGrid,
|
|
||||||
Library,
|
|
||||||
ListMusic,
|
|
||||||
Mic2,
|
|
||||||
Music,
|
|
||||||
Music2,
|
|
||||||
PlayCircle,
|
|
||||||
Radio,
|
|
||||||
User,
|
|
||||||
} from "lucide-react"
|
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
import { ScrollArea } from "@/components/ui/scroll-area"
|
import { ScrollArea } from "@/registry/new-york/ui/scroll-area"
|
||||||
|
|
||||||
import { Playlist } from "../data/playlists"
|
import { Playlist } from "../data/playlists"
|
||||||
|
|
||||||
@@ -24,69 +12,186 @@ export function Sidebar({ className, playlists }: SidebarProps) {
|
|||||||
return (
|
return (
|
||||||
<div className={cn("pb-12", className)}>
|
<div className={cn("pb-12", className)}>
|
||||||
<div className="space-y-4 py-4">
|
<div className="space-y-4 py-4">
|
||||||
<div className="px-4 py-2">
|
<div className="px-3 py-2">
|
||||||
<h2 className="mb-2 px-2 text-lg font-semibold tracking-tight">
|
<h2 className="mb-2 px-4 text-lg font-semibold tracking-tight">
|
||||||
Discover
|
Discover
|
||||||
</h2>
|
</h2>
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<Button
|
<Button variant="secondary" className="w-full justify-start">
|
||||||
variant="secondary"
|
<svg
|
||||||
size="sm"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
className="w-full justify-start"
|
viewBox="0 0 24 24"
|
||||||
>
|
fill="none"
|
||||||
<PlayCircle className="mr-2 h-4 w-4" />
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
className="mr-2 h-4 w-4"
|
||||||
|
>
|
||||||
|
<circle cx="12" cy="12" r="10" />
|
||||||
|
<polygon points="10 8 16 12 10 16 10 8" />
|
||||||
|
</svg>
|
||||||
Listen Now
|
Listen Now
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="ghost" size="sm" className="w-full justify-start">
|
<Button variant="ghost" className="w-full justify-start">
|
||||||
<LayoutGrid className="mr-2 h-4 w-4" />
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
className="mr-2 h-4 w-4"
|
||||||
|
>
|
||||||
|
<rect width="7" height="7" x="3" y="3" rx="1" />
|
||||||
|
<rect width="7" height="7" x="14" y="3" rx="1" />
|
||||||
|
<rect width="7" height="7" x="14" y="14" rx="1" />
|
||||||
|
<rect width="7" height="7" x="3" y="14" rx="1" />
|
||||||
|
</svg>
|
||||||
Browse
|
Browse
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="ghost" size="sm" className="w-full justify-start">
|
<Button variant="ghost" className="w-full justify-start">
|
||||||
<Radio className="mr-2 h-4 w-4" />
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
className="mr-2 h-4 w-4"
|
||||||
|
>
|
||||||
|
<path d="M4.9 19.1C1 15.2 1 8.8 4.9 4.9" />
|
||||||
|
<path d="M7.8 16.2c-2.3-2.3-2.3-6.1 0-8.5" />
|
||||||
|
<circle cx="12" cy="12" r="2" />
|
||||||
|
<path d="M16.2 7.8c2.3 2.3 2.3 6.1 0 8.5" />
|
||||||
|
<path d="M19.1 4.9C23 8.8 23 15.1 19.1 19" />
|
||||||
|
</svg>
|
||||||
Radio
|
Radio
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="px-4 py-2">
|
<div className="px-3 py-2">
|
||||||
<h2 className="mb-2 px-2 text-lg font-semibold tracking-tight">
|
<h2 className="mb-2 px-4 text-lg font-semibold tracking-tight">
|
||||||
Library
|
Library
|
||||||
</h2>
|
</h2>
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<Button variant="ghost" size="sm" className="w-full justify-start">
|
<Button variant="ghost" className="w-full justify-start">
|
||||||
<ListMusic className="mr-2 h-4 w-4" />
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
className="mr-2 h-4 w-4"
|
||||||
|
>
|
||||||
|
<path d="M21 15V6" />
|
||||||
|
<path d="M18.5 18a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5Z" />
|
||||||
|
<path d="M12 12H3" />
|
||||||
|
<path d="M16 6H3" />
|
||||||
|
<path d="M12 18H3" />
|
||||||
|
</svg>
|
||||||
Playlists
|
Playlists
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="ghost" size="sm" className="w-full justify-start">
|
<Button variant="ghost" className="w-full justify-start">
|
||||||
<Music2 className="mr-2 h-4 w-4" />
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
className="mr-2 h-4 w-4"
|
||||||
|
>
|
||||||
|
<circle cx="8" cy="18" r="4" />
|
||||||
|
<path d="M12 18V2l7 4" />
|
||||||
|
</svg>
|
||||||
Songs
|
Songs
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="ghost" size="sm" className="w-full justify-start">
|
<Button variant="ghost" className="w-full justify-start">
|
||||||
<User className="mr-2 h-4 w-4" />
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
className="mr-2 h-4 w-4"
|
||||||
|
>
|
||||||
|
<path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2" />
|
||||||
|
<circle cx="12" cy="7" r="4" />
|
||||||
|
</svg>
|
||||||
Made for You
|
Made for You
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="ghost" size="sm" className="w-full justify-start">
|
<Button variant="ghost" className="w-full justify-start">
|
||||||
<Mic2 className="mr-2 h-4 w-4" />
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
className="mr-2 h-4 w-4"
|
||||||
|
>
|
||||||
|
<path d="m12 8-9.04 9.06a2.82 2.82 0 1 0 3.98 3.98L16 12" />
|
||||||
|
<circle cx="17" cy="7" r="5" />
|
||||||
|
</svg>
|
||||||
Artists
|
Artists
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="ghost" size="sm" className="w-full justify-start">
|
<Button variant="ghost" className="w-full justify-start">
|
||||||
<Library className="mr-2 h-4 w-4" />
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
className="mr-2 h-4 w-4"
|
||||||
|
>
|
||||||
|
<path d="m16 6 4 14" />
|
||||||
|
<path d="M12 6v14" />
|
||||||
|
<path d="M8 8v12" />
|
||||||
|
<path d="M4 4v16" />
|
||||||
|
</svg>
|
||||||
Albums
|
Albums
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="py-2">
|
<div className="py-2">
|
||||||
<h2 className="relative px-6 text-lg font-semibold tracking-tight">
|
<h2 className="relative px-7 text-lg font-semibold tracking-tight">
|
||||||
Playlists
|
Playlists
|
||||||
</h2>
|
</h2>
|
||||||
<ScrollArea className="h-[300px] px-2">
|
<ScrollArea className="h-[300px] px-1">
|
||||||
<div className="space-y-1 p-2">
|
<div className="space-y-1 p-2">
|
||||||
{playlists?.map((playlist) => (
|
{playlists?.map((playlist, i) => (
|
||||||
<Button
|
<Button
|
||||||
|
key={`${playlist}-${i}`}
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="sm"
|
|
||||||
className="w-full justify-start font-normal"
|
className="w-full justify-start font-normal"
|
||||||
>
|
>
|
||||||
<ListMusic className="mr-2 h-4 w-4" />
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
className="mr-2 h-4 w-4"
|
||||||
|
>
|
||||||
|
<path d="M21 15V6" />
|
||||||
|
<path d="M18.5 18a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5Z" />
|
||||||
|
<path d="M12 12H3" />
|
||||||
|
<path d="M16 6H3" />
|
||||||
|
<path d="M12 18H3" />
|
||||||
|
</svg>
|
||||||
{playlist}
|
{playlist}
|
||||||
</Button>
|
</Button>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -1,8 +1,16 @@
|
|||||||
import { Metadata } from "next"
|
import { Metadata } from "next"
|
||||||
|
import Image from "next/image"
|
||||||
|
import { PlusCircledIcon } from "@radix-ui/react-icons"
|
||||||
|
|
||||||
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area"
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
import { Separator } from "@/components/ui/separator"
|
import { ScrollArea, ScrollBar } from "@/registry/new-york/ui/scroll-area"
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
|
import { Separator } from "@/registry/new-york/ui/separator"
|
||||||
|
import {
|
||||||
|
Tabs,
|
||||||
|
TabsContent,
|
||||||
|
TabsList,
|
||||||
|
TabsTrigger,
|
||||||
|
} from "@/registry/new-york/ui/tabs"
|
||||||
|
|
||||||
import { AlbumArtwork } from "./components/album-artwork"
|
import { AlbumArtwork } from "./components/album-artwork"
|
||||||
import { Menu } from "./components/menu"
|
import { Menu } from "./components/menu"
|
||||||
@@ -10,11 +18,6 @@ import { PodcastEmptyPlaceholder } from "./components/podcast-empty-placeholder"
|
|||||||
import { Sidebar } from "./components/sidebar"
|
import { Sidebar } from "./components/sidebar"
|
||||||
import { listenNowAlbums, madeForYouAlbums } from "./data/albums"
|
import { listenNowAlbums, madeForYouAlbums } from "./data/albums"
|
||||||
import { playlists } from "./data/playlists"
|
import { playlists } from "./data/playlists"
|
||||||
import "./styles.css"
|
|
||||||
import Image from "next/image"
|
|
||||||
import { PlusCircle } from "lucide-react"
|
|
||||||
|
|
||||||
import { Button } from "@/components/ui/button"
|
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Music App",
|
title: "Music App",
|
||||||
@@ -61,7 +64,7 @@ export default function MusicPage() {
|
|||||||
</TabsList>
|
</TabsList>
|
||||||
<div className="ml-auto mr-4">
|
<div className="ml-auto mr-4">
|
||||||
<Button>
|
<Button>
|
||||||
<PlusCircle className="mr-2 h-4 w-4" />
|
<PlusCircledIcon className="mr-2 h-4 w-4" />
|
||||||
Add music
|
Add music
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,65 +0,0 @@
|
|||||||
[data-section="music"] {
|
|
||||||
--background: 0 0% 100%;
|
|
||||||
--foreground: 222.2 47.4% 11.2%;
|
|
||||||
|
|
||||||
--muted: 243 5% 96%;
|
|
||||||
--muted-foreground: 215.4 16.3% 46.9%;
|
|
||||||
|
|
||||||
--popover: 0 0% 100%;
|
|
||||||
--popover-foreground: 222.2 47.4% 11.2%;
|
|
||||||
|
|
||||||
--border: 214.3 31.8% 91.4%;
|
|
||||||
--input: 214.3 31.8% 91.4%;
|
|
||||||
|
|
||||||
--card: 0 0% 100%;
|
|
||||||
--card-foreground: 222.2 47.4% 11.2%;
|
|
||||||
|
|
||||||
--primary: 349 89% 60%;
|
|
||||||
--primary-foreground: 0 0% 100%;
|
|
||||||
|
|
||||||
--secondary: 243 11% 4%;
|
|
||||||
--secondary-foreground: 0 0% 98%;
|
|
||||||
|
|
||||||
--accent: 243 11% 4%;
|
|
||||||
--accent-foreground: 0 0% 100%;
|
|
||||||
|
|
||||||
--destructive: 0 100% 50%;
|
|
||||||
--destructive-foreground: 210 40% 98%;
|
|
||||||
|
|
||||||
--ring: 349 89% 60%;
|
|
||||||
|
|
||||||
--radius: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dark [data-section="music"] {
|
|
||||||
--background: 224 71% 4%;
|
|
||||||
--foreground: 213 31% 91%;
|
|
||||||
|
|
||||||
--muted: 223 47% 11%;
|
|
||||||
--muted-foreground: 215.4 16.3% 56.9%;
|
|
||||||
|
|
||||||
--accent: 216 34% 17%;
|
|
||||||
--accent-foreground: 210 40% 98%;
|
|
||||||
|
|
||||||
--popover: 224 71% 4%;
|
|
||||||
--popover-foreground: 215 20.2% 65.1%;
|
|
||||||
|
|
||||||
--border: 216 34% 17%;
|
|
||||||
--input: 216 34% 17%;
|
|
||||||
|
|
||||||
--card: 224 71% 4%;
|
|
||||||
--card-foreground: 213 31% 91%;
|
|
||||||
|
|
||||||
--primary: 349 89% 60%;
|
|
||||||
--primary-foreground: 0 0% 100%;
|
|
||||||
|
|
||||||
--secondary: 222.2 47.4% 11.2%;
|
|
||||||
--secondary-foreground: 210 40% 98%;
|
|
||||||
|
|
||||||
--destructive: 0 63% 31%;
|
|
||||||
--destructive-foreground: 210 40% 98%;
|
|
||||||
|
|
||||||
--ring: 216 34% 17%;
|
|
||||||
|
|
||||||
--radius: 0.5rem;
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
DialogTrigger,
|
DialogTrigger,
|
||||||
} from "@/components/ui/dialog"
|
} from "@/registry/new-york/ui/dialog"
|
||||||
|
|
||||||
export function CodeViewer() {
|
export function CodeViewer() {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,153 +0,0 @@
|
|||||||
import {
|
|
||||||
AlertTriangle,
|
|
||||||
ArrowRight,
|
|
||||||
Check,
|
|
||||||
ChevronLeft,
|
|
||||||
ChevronRight,
|
|
||||||
ClipboardCheck,
|
|
||||||
Copy,
|
|
||||||
CreditCard,
|
|
||||||
Fingerprint,
|
|
||||||
HelpCircle,
|
|
||||||
Laptop,
|
|
||||||
Loader2,
|
|
||||||
LucideProps,
|
|
||||||
Moon,
|
|
||||||
MoreVertical,
|
|
||||||
Plus,
|
|
||||||
Settings,
|
|
||||||
SunMedium,
|
|
||||||
Trash,
|
|
||||||
Twitter,
|
|
||||||
User,
|
|
||||||
X,
|
|
||||||
type Icon as LucideIcon,
|
|
||||||
} from "lucide-react"
|
|
||||||
|
|
||||||
export type Icon = LucideIcon
|
|
||||||
|
|
||||||
export const Icons = {
|
|
||||||
logo: Fingerprint,
|
|
||||||
close: X,
|
|
||||||
spinner: Loader2,
|
|
||||||
chevronLeft: ChevronLeft,
|
|
||||||
chevronRight: ChevronRight,
|
|
||||||
trash: Trash,
|
|
||||||
settings: Settings,
|
|
||||||
billing: CreditCard,
|
|
||||||
ellipsis: MoreVertical,
|
|
||||||
add: Plus,
|
|
||||||
warning: AlertTriangle,
|
|
||||||
user: User,
|
|
||||||
arrowRight: ArrowRight,
|
|
||||||
help: HelpCircle,
|
|
||||||
twitter: Twitter,
|
|
||||||
check: Check,
|
|
||||||
copy: Copy,
|
|
||||||
copyDone: ClipboardCheck,
|
|
||||||
sun: SunMedium,
|
|
||||||
moon: Moon,
|
|
||||||
laptop: Laptop,
|
|
||||||
gitHub: (props: LucideProps) => (
|
|
||||||
<svg viewBox="0 0 438.549 438.549" {...props}>
|
|
||||||
<path
|
|
||||||
fill="currentColor"
|
|
||||||
d="M409.132 114.573c-19.608-33.596-46.205-60.194-79.798-79.8-33.598-19.607-70.277-29.408-110.063-29.408-39.781 0-76.472 9.804-110.063 29.408-33.596 19.605-60.192 46.204-79.8 79.8C9.803 148.168 0 184.854 0 224.63c0 47.78 13.94 90.745 41.827 128.906 27.884 38.164 63.906 64.572 108.063 79.227 5.14.954 8.945.283 11.419-1.996 2.475-2.282 3.711-5.14 3.711-8.562 0-.571-.049-5.708-.144-15.417a2549.81 2549.81 0 01-.144-25.406l-6.567 1.136c-4.187.767-9.469 1.092-15.846 1-6.374-.089-12.991-.757-19.842-1.999-6.854-1.231-13.229-4.086-19.13-8.559-5.898-4.473-10.085-10.328-12.56-17.556l-2.855-6.57c-1.903-4.374-4.899-9.233-8.992-14.559-4.093-5.331-8.232-8.945-12.419-10.848l-1.999-1.431c-1.332-.951-2.568-2.098-3.711-3.429-1.142-1.331-1.997-2.663-2.568-3.997-.572-1.335-.098-2.43 1.427-3.289 1.525-.859 4.281-1.276 8.28-1.276l5.708.853c3.807.763 8.516 3.042 14.133 6.851 5.614 3.806 10.229 8.754 13.846 14.842 4.38 7.806 9.657 13.754 15.846 17.847 6.184 4.093 12.419 6.136 18.699 6.136 6.28 0 11.704-.476 16.274-1.423 4.565-.952 8.848-2.383 12.847-4.285 1.713-12.758 6.377-22.559 13.988-29.41-10.848-1.14-20.601-2.857-29.264-5.14-8.658-2.286-17.605-5.996-26.835-11.14-9.235-5.137-16.896-11.516-22.985-19.126-6.09-7.614-11.088-17.61-14.987-29.979-3.901-12.374-5.852-26.648-5.852-42.826 0-23.035 7.52-42.637 22.557-58.817-7.044-17.318-6.379-36.732 1.997-58.24 5.52-1.715 13.706-.428 24.554 3.853 10.85 4.283 18.794 7.952 23.84 10.994 5.046 3.041 9.089 5.618 12.135 7.708 17.705-4.947 35.976-7.421 54.818-7.421s37.117 2.474 54.823 7.421l10.849-6.849c7.419-4.57 16.18-8.758 26.262-12.565 10.088-3.805 17.802-4.853 23.134-3.138 8.562 21.509 9.325 40.922 2.279 58.24 15.036 16.18 22.559 35.787 22.559 58.817 0 16.178-1.958 30.497-5.853 42.966-3.9 12.471-8.941 22.457-15.125 29.979-6.191 7.521-13.901 13.85-23.131 18.986-9.232 5.14-18.182 8.85-26.84 11.136-8.662 2.286-18.415 4.004-29.263 5.146 9.894 8.562 14.842 22.077 14.842 40.539v60.237c0 3.422 1.19 6.279 3.572 8.562 2.379 2.279 6.136 2.95 11.276 1.995 44.163-14.653 80.185-41.062 108.068-79.226 27.88-38.161 41.825-81.126 41.825-128.906-.01-39.771-9.818-76.454-29.414-110.049z"
|
|
||||||
></path>
|
|
||||||
</svg>
|
|
||||||
),
|
|
||||||
completeMode: ({ ...props }: LucideProps) => (
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="none"
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
<rect x="4" y="3" width="12" height="2" rx="1" fill="currentColor"></rect>
|
|
||||||
<rect x="4" y="7" width="12" height="2" rx="1" fill="currentColor"></rect>
|
|
||||||
<rect x="4" y="11" width="3" height="2" rx="1" fill="currentColor"></rect>
|
|
||||||
<rect x="4" y="15" width="3" height="2" rx="1" fill="currentColor"></rect>
|
|
||||||
<rect
|
|
||||||
x="8.5"
|
|
||||||
y="11"
|
|
||||||
width="3"
|
|
||||||
height="2"
|
|
||||||
rx="1"
|
|
||||||
fill="currentColor"
|
|
||||||
></rect>
|
|
||||||
<rect
|
|
||||||
x="8.5"
|
|
||||||
y="15"
|
|
||||||
width="3"
|
|
||||||
height="2"
|
|
||||||
rx="1"
|
|
||||||
fill="currentColor"
|
|
||||||
></rect>
|
|
||||||
<rect
|
|
||||||
x="13"
|
|
||||||
y="11"
|
|
||||||
width="3"
|
|
||||||
height="2"
|
|
||||||
rx="1"
|
|
||||||
fill="currentColor"
|
|
||||||
></rect>
|
|
||||||
</svg>
|
|
||||||
),
|
|
||||||
insertMode: ({ ...props }: LucideProps) => (
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="none"
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fillRule="evenodd"
|
|
||||||
clipRule="evenodd"
|
|
||||||
d="M14.491 7.769a.888.888 0 0 1 .287.648.888.888 0 0 1-.287.648l-3.916 3.667a1.013 1.013 0 0 1-.692.268c-.26 0-.509-.097-.692-.268L5.275 9.065A.886.886 0 0 1 5 8.42a.889.889 0 0 1 .287-.64c.181-.17.427-.267.683-.269.257-.002.504.09.69.258L8.903 9.87V3.917c0-.243.103-.477.287-.649.183-.171.432-.268.692-.268.26 0 .509.097.692.268a.888.888 0 0 1 .287.649V9.87l2.245-2.102c.183-.172.432-.269.692-.269.26 0 .508.097.692.269Z"
|
|
||||||
fill="currentColor"
|
|
||||||
></path>
|
|
||||||
<rect x="4" y="15" width="3" height="2" rx="1" fill="currentColor"></rect>
|
|
||||||
<rect
|
|
||||||
x="8.5"
|
|
||||||
y="15"
|
|
||||||
width="3"
|
|
||||||
height="2"
|
|
||||||
rx="1"
|
|
||||||
fill="currentColor"
|
|
||||||
></rect>
|
|
||||||
<rect
|
|
||||||
x="13"
|
|
||||||
y="15"
|
|
||||||
width="3"
|
|
||||||
height="2"
|
|
||||||
rx="1"
|
|
||||||
fill="currentColor"
|
|
||||||
></rect>
|
|
||||||
</svg>
|
|
||||||
),
|
|
||||||
editMode: ({ ...props }: LucideProps) => (
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="none"
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
<rect x="4" y="3" width="12" height="2" rx="1" fill="currentColor"></rect>
|
|
||||||
<rect x="4" y="7" width="12" height="2" rx="1" fill="currentColor"></rect>
|
|
||||||
<rect x="4" y="11" width="3" height="2" rx="1" fill="currentColor"></rect>
|
|
||||||
<rect x="4" y="15" width="4" height="2" rx="1" fill="currentColor"></rect>
|
|
||||||
<rect
|
|
||||||
x="8.5"
|
|
||||||
y="11"
|
|
||||||
width="3"
|
|
||||||
height="2"
|
|
||||||
rx="1"
|
|
||||||
fill="currentColor"
|
|
||||||
></rect>
|
|
||||||
<path
|
|
||||||
d="M17.154 11.346a1.182 1.182 0 0 0-1.671 0L11 15.829V17.5h1.671l4.483-4.483a1.182 1.182 0 0 0 0-1.671Z"
|
|
||||||
fill="currentColor"
|
|
||||||
></path>
|
|
||||||
</svg>
|
|
||||||
),
|
|
||||||
}
|
|
||||||
@@ -7,9 +7,9 @@ import {
|
|||||||
HoverCard,
|
HoverCard,
|
||||||
HoverCardContent,
|
HoverCardContent,
|
||||||
HoverCardTrigger,
|
HoverCardTrigger,
|
||||||
} from "@/components/ui/hover-card"
|
} from "@/registry/new-york/ui/hover-card"
|
||||||
import { Label } from "@/components/ui/label"
|
import { Label } from "@/registry/new-york/ui/label"
|
||||||
import { Slider } from "@/components/ui/slider"
|
import { Slider } from "@/registry/new-york/ui/slider"
|
||||||
|
|
||||||
interface MaxLengthSelectorProps {
|
interface MaxLengthSelectorProps {
|
||||||
defaultValue: SliderProps["defaultValue"]
|
defaultValue: SliderProps["defaultValue"]
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
|
import { CaretSortIcon, CheckIcon } from "@radix-ui/react-icons"
|
||||||
import { PopoverProps } from "@radix-ui/react-popover"
|
import { PopoverProps } from "@radix-ui/react-popover"
|
||||||
import { Check, ChevronsUpDown } from "lucide-react"
|
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
import { useMutationObserver } from "@/hooks/use-mutation-observer"
|
import { useMutationObserver } from "@/hooks/use-mutation-observer"
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
import {
|
import {
|
||||||
Command,
|
Command,
|
||||||
CommandEmpty,
|
CommandEmpty,
|
||||||
@@ -14,18 +14,18 @@ import {
|
|||||||
CommandInput,
|
CommandInput,
|
||||||
CommandItem,
|
CommandItem,
|
||||||
CommandList,
|
CommandList,
|
||||||
} from "@/components/ui/command"
|
} from "@/registry/new-york/ui/command"
|
||||||
import {
|
import {
|
||||||
HoverCard,
|
HoverCard,
|
||||||
HoverCardContent,
|
HoverCardContent,
|
||||||
HoverCardTrigger,
|
HoverCardTrigger,
|
||||||
} from "@/components/ui/hover-card"
|
} from "@/registry/new-york/ui/hover-card"
|
||||||
import { Label } from "@/components/ui/label"
|
import { Label } from "@/registry/new-york/ui/label"
|
||||||
import {
|
import {
|
||||||
Popover,
|
Popover,
|
||||||
PopoverContent,
|
PopoverContent,
|
||||||
PopoverTrigger,
|
PopoverTrigger,
|
||||||
} from "@/components/ui/popover"
|
} from "@/registry/new-york/ui/popover"
|
||||||
|
|
||||||
import { Model, ModelType } from "../data/models"
|
import { Model, ModelType } from "../data/models"
|
||||||
|
|
||||||
@@ -64,7 +64,7 @@ export function ModelSelector({ models, types, ...props }: ModelSelectorProps) {
|
|||||||
className="w-full justify-between"
|
className="w-full justify-between"
|
||||||
>
|
>
|
||||||
{selectedModel ? selectedModel.name : "Select a model..."}
|
{selectedModel ? selectedModel.name : "Select a model..."}
|
||||||
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
<CaretSortIcon className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||||
</Button>
|
</Button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent align="end" className="w-[250px] p-0">
|
<PopoverContent align="end" className="w-[250px] p-0">
|
||||||
@@ -152,7 +152,7 @@ function ModelItem({ model, isSelected, onSelect, onPeek }: ModelItemProps) {
|
|||||||
className="aria-selected:bg-primary aria-selected:text-primary-foreground"
|
className="aria-selected:bg-primary aria-selected:text-primary-foreground"
|
||||||
>
|
>
|
||||||
{model.name}
|
{model.name}
|
||||||
<Check
|
<CheckIcon
|
||||||
className={cn(
|
className={cn(
|
||||||
"ml-auto h-4 w-4",
|
"ml-auto h-4 w-4",
|
||||||
isSelected ? "opacity-100" : "opacity-0"
|
isSelected ? "opacity-100" : "opacity-0"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import { Dialog } from "@radix-ui/react-dialog"
|
import { Dialog } from "@radix-ui/react-dialog"
|
||||||
import { Flag, MoreHorizontal, Trash } from "lucide-react"
|
import { DotsHorizontalIcon } from "@radix-ui/react-icons"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AlertDialog,
|
AlertDialog,
|
||||||
@@ -12,25 +12,25 @@ import {
|
|||||||
AlertDialogFooter,
|
AlertDialogFooter,
|
||||||
AlertDialogHeader,
|
AlertDialogHeader,
|
||||||
AlertDialogTitle,
|
AlertDialogTitle,
|
||||||
} from "@/components/ui/alert-dialog"
|
} from "@/registry/new-york/ui/alert-dialog"
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
import {
|
import {
|
||||||
DialogContent,
|
DialogContent,
|
||||||
DialogDescription,
|
DialogDescription,
|
||||||
DialogFooter,
|
DialogFooter,
|
||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from "@/components/ui/dialog"
|
} from "@/registry/new-york/ui/dialog"
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
DropdownMenuItem,
|
DropdownMenuItem,
|
||||||
DropdownMenuSeparator,
|
DropdownMenuSeparator,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu"
|
} from "@/registry/new-york/ui/dropdown-menu"
|
||||||
import { Label } from "@/components/ui/label"
|
import { Label } from "@/registry/new-york/ui/label"
|
||||||
import { Switch } from "@/components/ui/switch"
|
import { Switch } from "@/registry/new-york/ui/switch"
|
||||||
import { toast } from "@/components/ui/use-toast"
|
import { toast } from "@/registry/new-york/ui/use-toast"
|
||||||
|
|
||||||
export function PresetActions() {
|
export function PresetActions() {
|
||||||
const [open, setIsOpen] = React.useState(false)
|
const [open, setIsOpen] = React.useState(false)
|
||||||
@@ -42,12 +42,11 @@ export function PresetActions() {
|
|||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
<Button variant="secondary">
|
<Button variant="secondary">
|
||||||
<span className="sr-only">Actions</span>
|
<span className="sr-only">Actions</span>
|
||||||
<MoreHorizontal className="h-4 w-4" />
|
<DotsHorizontalIcon className="h-4 w-4" />
|
||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent align="end">
|
<DropdownMenuContent align="end">
|
||||||
<DropdownMenuItem onSelect={() => setIsOpen(true)}>
|
<DropdownMenuItem onSelect={() => setIsOpen(true)}>
|
||||||
<Flag className="mr-2 h-4 w-4" />
|
|
||||||
Content filter preferences
|
Content filter preferences
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
@@ -55,7 +54,6 @@ export function PresetActions() {
|
|||||||
onSelect={() => setShowDeleteDialog(true)}
|
onSelect={() => setShowDeleteDialog(true)}
|
||||||
className="text-red-600"
|
className="text-red-600"
|
||||||
>
|
>
|
||||||
<Trash className="mr-2 h-4 w-4" />
|
|
||||||
Delete preset
|
Delete preset
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
@@ -7,9 +7,9 @@ import {
|
|||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
DialogTrigger,
|
DialogTrigger,
|
||||||
} from "@/components/ui/dialog"
|
} from "@/registry/new-york/ui/dialog"
|
||||||
import { Input } from "@/components/ui/input"
|
import { Input } from "@/registry/new-york/ui/input"
|
||||||
import { Label } from "@/components/ui/label"
|
import { Label } from "@/registry/new-york/ui/label"
|
||||||
|
|
||||||
export function PresetSave() {
|
export function PresetSave() {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -2,23 +2,23 @@
|
|||||||
|
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import { useRouter } from "next/navigation"
|
import { useRouter } from "next/navigation"
|
||||||
|
import { CaretSortIcon, CheckIcon } from "@radix-ui/react-icons"
|
||||||
import { PopoverProps } from "@radix-ui/react-popover"
|
import { PopoverProps } from "@radix-ui/react-popover"
|
||||||
import { Check, ChevronsUpDown } from "lucide-react"
|
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
import {
|
import {
|
||||||
Command,
|
Command,
|
||||||
CommandEmpty,
|
CommandEmpty,
|
||||||
CommandGroup,
|
CommandGroup,
|
||||||
CommandInput,
|
CommandInput,
|
||||||
CommandItem,
|
CommandItem,
|
||||||
} from "@/components/ui/command"
|
} from "@/registry/new-york/ui/command"
|
||||||
import {
|
import {
|
||||||
Popover,
|
Popover,
|
||||||
PopoverContent,
|
PopoverContent,
|
||||||
PopoverTrigger,
|
PopoverTrigger,
|
||||||
} from "@/components/ui/popover"
|
} from "@/registry/new-york/ui/popover"
|
||||||
|
|
||||||
import { Preset } from "../data/presets"
|
import { Preset } from "../data/presets"
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@ export function PresetSelector({ presets, ...props }: PresetSelectorProps) {
|
|||||||
className="flex-1 justify-between md:max-w-[200px] lg:max-w-[300px]"
|
className="flex-1 justify-between md:max-w-[200px] lg:max-w-[300px]"
|
||||||
>
|
>
|
||||||
{selectedPreset ? selectedPreset.name : "Load a preset..."}
|
{selectedPreset ? selectedPreset.name : "Load a preset..."}
|
||||||
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
<CaretSortIcon className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||||
</Button>
|
</Button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent className="w-[300px] p-0">
|
<PopoverContent className="w-[300px] p-0">
|
||||||
@@ -59,7 +59,7 @@ export function PresetSelector({ presets, ...props }: PresetSelectorProps) {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{preset.name}
|
{preset.name}
|
||||||
<Check
|
<CheckIcon
|
||||||
className={cn(
|
className={cn(
|
||||||
"ml-auto h-4 w-4",
|
"ml-auto h-4 w-4",
|
||||||
selectedPreset?.id === preset.id
|
selectedPreset?.id === preset.id
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { Copy } from "lucide-react"
|
import { CopyIcon } from "@radix-ui/react-icons"
|
||||||
|
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
import { Input } from "@/components/ui/input"
|
import { Input } from "@/registry/new-york/ui/input"
|
||||||
import { Label } from "@/components/ui/label"
|
import { Label } from "@/registry/new-york/ui/label"
|
||||||
import {
|
import {
|
||||||
Popover,
|
Popover,
|
||||||
PopoverContent,
|
PopoverContent,
|
||||||
PopoverTrigger,
|
PopoverTrigger,
|
||||||
} from "@/components/ui/popover"
|
} from "@/registry/new-york/ui/popover"
|
||||||
|
|
||||||
export function PresetShare() {
|
export function PresetShare() {
|
||||||
return (
|
return (
|
||||||
@@ -37,7 +37,7 @@ export function PresetShare() {
|
|||||||
</div>
|
</div>
|
||||||
<Button type="submit" size="sm" className="px-3">
|
<Button type="submit" size="sm" className="px-3">
|
||||||
<span className="sr-only">Copy</span>
|
<span className="sr-only">Copy</span>
|
||||||
<Copy className="h-4 w-4" />
|
<CopyIcon className="h-4 w-4" />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</PopoverContent>
|
</PopoverContent>
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ import {
|
|||||||
HoverCard,
|
HoverCard,
|
||||||
HoverCardContent,
|
HoverCardContent,
|
||||||
HoverCardTrigger,
|
HoverCardTrigger,
|
||||||
} from "@/components/ui/hover-card"
|
} from "@/registry/new-york/ui/hover-card"
|
||||||
import { Label } from "@/components/ui/label"
|
import { Label } from "@/registry/new-york/ui/label"
|
||||||
import { Slider } from "@/components/ui/slider"
|
import { Slider } from "@/registry/new-york/ui/slider"
|
||||||
|
|
||||||
interface TemperatureSelectorProps {
|
interface TemperatureSelectorProps {
|
||||||
defaultValue: SliderProps["defaultValue"]
|
defaultValue: SliderProps["defaultValue"]
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ import {
|
|||||||
HoverCard,
|
HoverCard,
|
||||||
HoverCardContent,
|
HoverCardContent,
|
||||||
HoverCardTrigger,
|
HoverCardTrigger,
|
||||||
} from "@/components/ui/hover-card"
|
} from "@/registry/new-york/ui/hover-card"
|
||||||
import { Label } from "@/components/ui/label"
|
import { Label } from "@/registry/new-york/ui/label"
|
||||||
import { Slider } from "@/components/ui/slider"
|
import { Slider } from "@/registry/new-york/ui/slider"
|
||||||
|
|
||||||
interface TopPSelectorProps {
|
interface TopPSelectorProps {
|
||||||
defaultValue: SliderProps["defaultValue"]
|
defaultValue: SliderProps["defaultValue"]
|
||||||
|
|||||||
@@ -1,19 +1,24 @@
|
|||||||
import { Metadata } from "next"
|
import { Metadata } from "next"
|
||||||
import { History } from "lucide-react"
|
import Image from "next/image"
|
||||||
|
import { CounterClockwiseClockIcon } from "@radix-ui/react-icons"
|
||||||
|
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
import {
|
import {
|
||||||
HoverCard,
|
HoverCard,
|
||||||
HoverCardContent,
|
HoverCardContent,
|
||||||
HoverCardTrigger,
|
HoverCardTrigger,
|
||||||
} from "@/components/ui/hover-card"
|
} from "@/registry/new-york/ui/hover-card"
|
||||||
import { Label } from "@/components/ui/label"
|
import { Label } from "@/registry/new-york/ui/label"
|
||||||
import { Separator } from "@/components/ui/separator"
|
import { Separator } from "@/registry/new-york/ui/separator"
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
|
import {
|
||||||
import { Textarea } from "@/components/ui/textarea"
|
Tabs,
|
||||||
|
TabsContent,
|
||||||
|
TabsList,
|
||||||
|
TabsTrigger,
|
||||||
|
} from "@/registry/new-york/ui/tabs"
|
||||||
|
import { Textarea } from "@/registry/new-york/ui/textarea"
|
||||||
|
|
||||||
import { CodeViewer } from "./components/code-viewer"
|
import { CodeViewer } from "./components/code-viewer"
|
||||||
import { Icons } from "./components/icons"
|
|
||||||
import { MaxLengthSelector } from "./components/maxlength-selector"
|
import { MaxLengthSelector } from "./components/maxlength-selector"
|
||||||
import { ModelSelector } from "./components/model-selector"
|
import { ModelSelector } from "./components/model-selector"
|
||||||
import { PresetActions } from "./components/preset-actions"
|
import { PresetActions } from "./components/preset-actions"
|
||||||
@@ -24,8 +29,6 @@ import { TemperatureSelector } from "./components/temperature-selector"
|
|||||||
import { TopPSelector } from "./components/top-p-selector"
|
import { TopPSelector } from "./components/top-p-selector"
|
||||||
import { models, types } from "./data/models"
|
import { models, types } from "./data/models"
|
||||||
import { presets } from "./data/presets"
|
import { presets } from "./data/presets"
|
||||||
import "./styles.css"
|
|
||||||
import Image from "next/image"
|
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Playground",
|
title: "Playground",
|
||||||
@@ -86,15 +89,163 @@ export default function PlaygroundPage() {
|
|||||||
<TabsList className="grid grid-cols-3">
|
<TabsList className="grid grid-cols-3">
|
||||||
<TabsTrigger value="complete">
|
<TabsTrigger value="complete">
|
||||||
<span className="sr-only">Complete</span>
|
<span className="sr-only">Complete</span>
|
||||||
<Icons.completeMode className="h-5 w-5" />
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
fill="none"
|
||||||
|
className="h-5 w-5"
|
||||||
|
>
|
||||||
|
<rect
|
||||||
|
x="4"
|
||||||
|
y="3"
|
||||||
|
width="12"
|
||||||
|
height="2"
|
||||||
|
rx="1"
|
||||||
|
fill="currentColor"
|
||||||
|
></rect>
|
||||||
|
<rect
|
||||||
|
x="4"
|
||||||
|
y="7"
|
||||||
|
width="12"
|
||||||
|
height="2"
|
||||||
|
rx="1"
|
||||||
|
fill="currentColor"
|
||||||
|
></rect>
|
||||||
|
<rect
|
||||||
|
x="4"
|
||||||
|
y="11"
|
||||||
|
width="3"
|
||||||
|
height="2"
|
||||||
|
rx="1"
|
||||||
|
fill="currentColor"
|
||||||
|
></rect>
|
||||||
|
<rect
|
||||||
|
x="4"
|
||||||
|
y="15"
|
||||||
|
width="3"
|
||||||
|
height="2"
|
||||||
|
rx="1"
|
||||||
|
fill="currentColor"
|
||||||
|
></rect>
|
||||||
|
<rect
|
||||||
|
x="8.5"
|
||||||
|
y="11"
|
||||||
|
width="3"
|
||||||
|
height="2"
|
||||||
|
rx="1"
|
||||||
|
fill="currentColor"
|
||||||
|
></rect>
|
||||||
|
<rect
|
||||||
|
x="8.5"
|
||||||
|
y="15"
|
||||||
|
width="3"
|
||||||
|
height="2"
|
||||||
|
rx="1"
|
||||||
|
fill="currentColor"
|
||||||
|
></rect>
|
||||||
|
<rect
|
||||||
|
x="13"
|
||||||
|
y="11"
|
||||||
|
width="3"
|
||||||
|
height="2"
|
||||||
|
rx="1"
|
||||||
|
fill="currentColor"
|
||||||
|
></rect>
|
||||||
|
</svg>
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
<TabsTrigger value="insert">
|
<TabsTrigger value="insert">
|
||||||
<span className="sr-only">Insert</span>
|
<span className="sr-only">Insert</span>
|
||||||
<Icons.insertMode className="h-5 w-5" />
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
fill="none"
|
||||||
|
className="h-5 w-5"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M14.491 7.769a.888.888 0 0 1 .287.648.888.888 0 0 1-.287.648l-3.916 3.667a1.013 1.013 0 0 1-.692.268c-.26 0-.509-.097-.692-.268L5.275 9.065A.886.886 0 0 1 5 8.42a.889.889 0 0 1 .287-.64c.181-.17.427-.267.683-.269.257-.002.504.09.69.258L8.903 9.87V3.917c0-.243.103-.477.287-.649.183-.171.432-.268.692-.268.26 0 .509.097.692.268a.888.888 0 0 1 .287.649V9.87l2.245-2.102c.183-.172.432-.269.692-.269.26 0 .508.097.692.269Z"
|
||||||
|
fill="currentColor"
|
||||||
|
></path>
|
||||||
|
<rect
|
||||||
|
x="4"
|
||||||
|
y="15"
|
||||||
|
width="3"
|
||||||
|
height="2"
|
||||||
|
rx="1"
|
||||||
|
fill="currentColor"
|
||||||
|
></rect>
|
||||||
|
<rect
|
||||||
|
x="8.5"
|
||||||
|
y="15"
|
||||||
|
width="3"
|
||||||
|
height="2"
|
||||||
|
rx="1"
|
||||||
|
fill="currentColor"
|
||||||
|
></rect>
|
||||||
|
<rect
|
||||||
|
x="13"
|
||||||
|
y="15"
|
||||||
|
width="3"
|
||||||
|
height="2"
|
||||||
|
rx="1"
|
||||||
|
fill="currentColor"
|
||||||
|
></rect>
|
||||||
|
</svg>
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
<TabsTrigger value="edit">
|
<TabsTrigger value="edit">
|
||||||
<span className="sr-only">Edit</span>
|
<span className="sr-only">Edit</span>
|
||||||
<Icons.editMode className="h-5 w-5" />
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
fill="none"
|
||||||
|
className="h-5 w-5"
|
||||||
|
>
|
||||||
|
<rect
|
||||||
|
x="4"
|
||||||
|
y="3"
|
||||||
|
width="12"
|
||||||
|
height="2"
|
||||||
|
rx="1"
|
||||||
|
fill="currentColor"
|
||||||
|
></rect>
|
||||||
|
<rect
|
||||||
|
x="4"
|
||||||
|
y="7"
|
||||||
|
width="12"
|
||||||
|
height="2"
|
||||||
|
rx="1"
|
||||||
|
fill="currentColor"
|
||||||
|
></rect>
|
||||||
|
<rect
|
||||||
|
x="4"
|
||||||
|
y="11"
|
||||||
|
width="3"
|
||||||
|
height="2"
|
||||||
|
rx="1"
|
||||||
|
fill="currentColor"
|
||||||
|
></rect>
|
||||||
|
<rect
|
||||||
|
x="4"
|
||||||
|
y="15"
|
||||||
|
width="4"
|
||||||
|
height="2"
|
||||||
|
rx="1"
|
||||||
|
fill="currentColor"
|
||||||
|
></rect>
|
||||||
|
<rect
|
||||||
|
x="8.5"
|
||||||
|
y="11"
|
||||||
|
width="3"
|
||||||
|
height="2"
|
||||||
|
rx="1"
|
||||||
|
fill="currentColor"
|
||||||
|
></rect>
|
||||||
|
<path
|
||||||
|
d="M17.154 11.346a1.182 1.182 0 0 0-1.671 0L11 15.829V17.5h1.671l4.483-4.483a1.182 1.182 0 0 0 0-1.671Z"
|
||||||
|
fill="currentColor"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
</div>
|
</div>
|
||||||
@@ -114,7 +265,7 @@ export default function PlaygroundPage() {
|
|||||||
<Button>Submit</Button>
|
<Button>Submit</Button>
|
||||||
<Button variant="secondary">
|
<Button variant="secondary">
|
||||||
<span className="sr-only">Show history</span>
|
<span className="sr-only">Show history</span>
|
||||||
<History className="h-4 w-4" />
|
<CounterClockwiseClockIcon className="h-4 w-4" />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -132,7 +283,7 @@ export default function PlaygroundPage() {
|
|||||||
<Button>Submit</Button>
|
<Button>Submit</Button>
|
||||||
<Button variant="secondary">
|
<Button variant="secondary">
|
||||||
<span className="sr-only">Show history</span>
|
<span className="sr-only">Show history</span>
|
||||||
<History className="h-4 w-4" />
|
<CounterClockwiseClockIcon className="h-4 w-4" />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -163,7 +314,7 @@ export default function PlaygroundPage() {
|
|||||||
<Button>Submit</Button>
|
<Button>Submit</Button>
|
||||||
<Button variant="secondary">
|
<Button variant="secondary">
|
||||||
<span className="sr-only">Show history</span>
|
<span className="sr-only">Show history</span>
|
||||||
<History className="h-4 w-4" />
|
<CounterClockwiseClockIcon className="h-4 w-4" />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,65 +0,0 @@
|
|||||||
[data-section="playground"] {
|
|
||||||
--background: 0 0% 100%;
|
|
||||||
--foreground: 222.2 47.4% 11.2%;
|
|
||||||
|
|
||||||
--muted: 154 79% 96%;
|
|
||||||
--muted-foreground: 215.4 16.3% 46.9%;
|
|
||||||
|
|
||||||
--popover: 0 0% 100%;
|
|
||||||
--popover-foreground: 222.2 47.4% 11.2%;
|
|
||||||
|
|
||||||
--border: 214.3 31.8% 91.4%;
|
|
||||||
--input: 214.3 31.8% 91.4%;
|
|
||||||
|
|
||||||
--card: 0 0% 100%;
|
|
||||||
--card-foreground: 222.2 47.4% 11.2%;
|
|
||||||
|
|
||||||
--primary: 143 72% 29%;
|
|
||||||
--primary-foreground: 141 75% 97%;
|
|
||||||
|
|
||||||
--secondary: 145 80% 10%;
|
|
||||||
--secondary-foreground: 141 75% 97%;
|
|
||||||
|
|
||||||
--accent: 154 79% 96%;
|
|
||||||
--accent-foreground: 222.2 47.4% 11.2%;
|
|
||||||
|
|
||||||
--destructive: 0 100% 50%;
|
|
||||||
--destructive-foreground: 210 40% 98%;
|
|
||||||
|
|
||||||
--ring: 158 64% 52%;
|
|
||||||
|
|
||||||
--radius: 0rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dark [data-section="playground"] {
|
|
||||||
--background: 224 71% 4%;
|
|
||||||
--foreground: 213 31% 91%;
|
|
||||||
|
|
||||||
--muted: 223 47% 11%;
|
|
||||||
--muted-foreground: 215.4 16.3% 56.9%;
|
|
||||||
|
|
||||||
--popover: 224 71% 4%;
|
|
||||||
--popover-foreground: 215 20.2% 65.1%;
|
|
||||||
|
|
||||||
--border: 216 34% 17%;
|
|
||||||
--input: 216 34% 17%;
|
|
||||||
|
|
||||||
--card: 224 71% 4%;
|
|
||||||
--card-foreground: 213 31% 91%;
|
|
||||||
|
|
||||||
--primary: 143 72% 29%;
|
|
||||||
--primary-foreground: 141 75% 97%;
|
|
||||||
|
|
||||||
--secondary: 145 80% 10%;
|
|
||||||
--secondary-foreground: 141 75% 97%;
|
|
||||||
|
|
||||||
--accent: 154 79% 96%;
|
|
||||||
--accent-foreground: 222.2 47.4% 11.2%;
|
|
||||||
|
|
||||||
--destructive: 0 100% 50%;
|
|
||||||
--destructive-foreground: 210 40% 98%;
|
|
||||||
|
|
||||||
--ring: 158 64% 52%;
|
|
||||||
|
|
||||||
--radius: 0rem;
|
|
||||||
}
|
|
||||||
120
apps/www/app/examples/tasks/components/columns.tsx
Normal file
120
apps/www/app/examples/tasks/components/columns.tsx
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import { ColumnDef } from "@tanstack/react-table"
|
||||||
|
|
||||||
|
import { Badge } from "@/registry/new-york/ui/badge"
|
||||||
|
import { Checkbox } from "@/registry/new-york/ui/checkbox"
|
||||||
|
|
||||||
|
import { labels, priorities, statuses } from "../data/data"
|
||||||
|
import { Task } from "../data/schema"
|
||||||
|
import { DataTableColumnHeader } from "./data-table-column-header"
|
||||||
|
import { DataTableRowActions } from "./data-table-row-actions"
|
||||||
|
|
||||||
|
export const columns: ColumnDef<Task>[] = [
|
||||||
|
{
|
||||||
|
id: "select",
|
||||||
|
header: ({ table }) => (
|
||||||
|
<Checkbox
|
||||||
|
checked={table.getIsAllPageRowsSelected()}
|
||||||
|
onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
|
||||||
|
aria-label="Select all"
|
||||||
|
className="translate-y-[2px]"
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<Checkbox
|
||||||
|
checked={row.getIsSelected()}
|
||||||
|
onCheckedChange={(value) => row.toggleSelected(!!value)}
|
||||||
|
aria-label="Select row"
|
||||||
|
className="translate-y-[2px]"
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
enableSorting: false,
|
||||||
|
enableHiding: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "id",
|
||||||
|
header: ({ column }) => (
|
||||||
|
<DataTableColumnHeader column={column} title="Task" />
|
||||||
|
),
|
||||||
|
cell: ({ row }) => <div className="w-[80px]">{row.getValue("id")}</div>,
|
||||||
|
enableSorting: false,
|
||||||
|
enableHiding: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "title",
|
||||||
|
header: ({ column }) => (
|
||||||
|
<DataTableColumnHeader column={column} title="Title" />
|
||||||
|
),
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const label = labels.find((label) => label.value === row.original.label)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex space-x-2">
|
||||||
|
{label && <Badge variant="outline">{label.label}</Badge>}
|
||||||
|
<span className="max-w-[500px] truncate font-medium">
|
||||||
|
{row.getValue("title")}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "status",
|
||||||
|
header: ({ column }) => (
|
||||||
|
<DataTableColumnHeader column={column} title="Status" />
|
||||||
|
),
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const status = statuses.find(
|
||||||
|
(status) => status.value === row.getValue("status")
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!status) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex w-[100px] items-center">
|
||||||
|
{status.icon && (
|
||||||
|
<status.icon className="mr-2 h-4 w-4 text-muted-foreground" />
|
||||||
|
)}
|
||||||
|
<span>{status.label}</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
filterFn: (row, id, value) => {
|
||||||
|
return value.includes(row.getValue(id))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "priority",
|
||||||
|
header: ({ column }) => (
|
||||||
|
<DataTableColumnHeader column={column} title="Priority" />
|
||||||
|
),
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const priority = priorities.find(
|
||||||
|
(priority) => priority.value === row.getValue("priority")
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!priority) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex items-center">
|
||||||
|
{priority.icon && (
|
||||||
|
<priority.icon className="mr-2 h-4 w-4 text-muted-foreground" />
|
||||||
|
)}
|
||||||
|
<span>{priority.label}</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
filterFn: (row, id, value) => {
|
||||||
|
return value.includes(row.getValue(id))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "actions",
|
||||||
|
cell: ({ row }) => <DataTableRowActions row={row} />,
|
||||||
|
},
|
||||||
|
]
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
import {
|
||||||
|
ArrowDownIcon,
|
||||||
|
ArrowUpIcon,
|
||||||
|
CaretSortIcon,
|
||||||
|
EyeNoneIcon,
|
||||||
|
} from "@radix-ui/react-icons"
|
||||||
|
import { Column } from "@tanstack/react-table"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuSeparator,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from "@/registry/new-york/ui/dropdown-menu"
|
||||||
|
|
||||||
|
interface DataTableColumnHeaderProps<TData, TValue>
|
||||||
|
extends React.HTMLAttributes<HTMLDivElement> {
|
||||||
|
column: Column<TData, TValue>
|
||||||
|
title: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export function DataTableColumnHeader<TData, TValue>({
|
||||||
|
column,
|
||||||
|
title,
|
||||||
|
className,
|
||||||
|
}: DataTableColumnHeaderProps<TData, TValue>) {
|
||||||
|
if (!column.getCanSort()) {
|
||||||
|
return <div className={cn(className)}>{title}</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={cn("flex items-center space-x-2", className)}>
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
className="-ml-3 h-8 data-[state=open]:bg-accent"
|
||||||
|
>
|
||||||
|
<span>{title}</span>
|
||||||
|
{column.getIsSorted() === "desc" ? (
|
||||||
|
<ArrowDownIcon className="ml-2 h-4 w-4" />
|
||||||
|
) : column.getIsSorted() === "asc" ? (
|
||||||
|
<ArrowUpIcon className="ml-2 h-4 w-4" />
|
||||||
|
) : (
|
||||||
|
<CaretSortIcon className="ml-2 h-4 w-4" />
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent align="start">
|
||||||
|
<DropdownMenuItem onClick={() => column.toggleSorting(false)}>
|
||||||
|
<ArrowUpIcon className="mr-2 h-3.5 w-3.5 text-muted-foreground/70" />
|
||||||
|
Asc
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem onClick={() => column.toggleSorting(true)}>
|
||||||
|
<ArrowDownIcon className="mr-2 h-3.5 w-3.5 text-muted-foreground/70" />
|
||||||
|
Desc
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
<DropdownMenuItem onClick={() => column.toggleVisibility(false)}>
|
||||||
|
<EyeNoneIcon className="mr-2 h-3.5 w-3.5 text-muted-foreground/70" />
|
||||||
|
Hide
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,147 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
import { CheckIcon, PlusCircledIcon } from "@radix-ui/react-icons"
|
||||||
|
import { Column } from "@tanstack/react-table"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
import { Badge } from "@/registry/new-york/ui/badge"
|
||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
|
import {
|
||||||
|
Command,
|
||||||
|
CommandEmpty,
|
||||||
|
CommandGroup,
|
||||||
|
CommandInput,
|
||||||
|
CommandItem,
|
||||||
|
CommandList,
|
||||||
|
CommandSeparator,
|
||||||
|
} from "@/registry/new-york/ui/command"
|
||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
PopoverContent,
|
||||||
|
PopoverTrigger,
|
||||||
|
} from "@/registry/new-york/ui/popover"
|
||||||
|
import { Separator } from "@/registry/new-york/ui/separator"
|
||||||
|
|
||||||
|
interface DataTableFacetedFilterProps<TData, TValue> {
|
||||||
|
column?: Column<TData, TValue>
|
||||||
|
title?: string
|
||||||
|
options: {
|
||||||
|
label: string
|
||||||
|
value: string
|
||||||
|
icon?: React.ComponentType<{ className?: string }>
|
||||||
|
}[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export function DataTableFacetedFilter<TData, TValue>({
|
||||||
|
column,
|
||||||
|
title,
|
||||||
|
options,
|
||||||
|
}: DataTableFacetedFilterProps<TData, TValue>) {
|
||||||
|
const facets = column?.getFacetedUniqueValues()
|
||||||
|
const selectedValues = new Set(column?.getFilterValue() as string[])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger asChild>
|
||||||
|
<Button variant="outline" size="sm" className="h-8 border-dashed">
|
||||||
|
<PlusCircledIcon className="mr-2 h-4 w-4" />
|
||||||
|
{title}
|
||||||
|
{selectedValues?.size > 0 && (
|
||||||
|
<>
|
||||||
|
<Separator orientation="vertical" className="mx-2 h-4" />
|
||||||
|
<Badge
|
||||||
|
variant="secondary"
|
||||||
|
className="rounded-sm px-1 font-normal lg:hidden"
|
||||||
|
>
|
||||||
|
{selectedValues.size}
|
||||||
|
</Badge>
|
||||||
|
<div className="hidden space-x-1 lg:flex">
|
||||||
|
{selectedValues.size > 2 ? (
|
||||||
|
<Badge
|
||||||
|
variant="secondary"
|
||||||
|
className="rounded-sm px-1 font-normal"
|
||||||
|
>
|
||||||
|
{selectedValues.size} selected
|
||||||
|
</Badge>
|
||||||
|
) : (
|
||||||
|
options
|
||||||
|
.filter((option) => selectedValues.has(option.value))
|
||||||
|
.map((option) => (
|
||||||
|
<Badge
|
||||||
|
variant="secondary"
|
||||||
|
key={option.value}
|
||||||
|
className="rounded-sm px-1 font-normal"
|
||||||
|
>
|
||||||
|
{option.label}
|
||||||
|
</Badge>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent className="w-[200px] p-0" align="start">
|
||||||
|
<Command>
|
||||||
|
<CommandInput placeholder={title} />
|
||||||
|
<CommandList>
|
||||||
|
<CommandEmpty>No results found.</CommandEmpty>
|
||||||
|
<CommandGroup>
|
||||||
|
{options.map((option) => {
|
||||||
|
const isSelected = selectedValues.has(option.value)
|
||||||
|
return (
|
||||||
|
<CommandItem
|
||||||
|
key={option.value}
|
||||||
|
onSelect={() => {
|
||||||
|
if (isSelected) {
|
||||||
|
selectedValues.delete(option.value)
|
||||||
|
} else {
|
||||||
|
selectedValues.add(option.value)
|
||||||
|
}
|
||||||
|
const filterValues = Array.from(selectedValues)
|
||||||
|
column?.setFilterValue(
|
||||||
|
filterValues.length ? filterValues : undefined
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"mr-2 flex h-4 w-4 items-center justify-center rounded-sm border border-primary",
|
||||||
|
isSelected
|
||||||
|
? "bg-primary text-primary-foreground"
|
||||||
|
: "opacity-50 [&_svg]:invisible"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<CheckIcon className={cn("h-4 w-4")} />
|
||||||
|
</div>
|
||||||
|
{option.icon && (
|
||||||
|
<option.icon className="mr-2 h-4 w-4 text-muted-foreground" />
|
||||||
|
)}
|
||||||
|
<span>{option.label}</span>
|
||||||
|
{facets?.get(option.value) && (
|
||||||
|
<span className="ml-auto flex h-4 w-4 items-center justify-center font-mono text-xs">
|
||||||
|
{facets.get(option.value)}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</CommandItem>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</CommandGroup>
|
||||||
|
{selectedValues.size > 0 && (
|
||||||
|
<>
|
||||||
|
<CommandSeparator />
|
||||||
|
<CommandGroup>
|
||||||
|
<CommandItem
|
||||||
|
onSelect={() => column?.setFilterValue(undefined)}
|
||||||
|
className="justify-center text-center"
|
||||||
|
>
|
||||||
|
Clear filters
|
||||||
|
</CommandItem>
|
||||||
|
</CommandGroup>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</CommandList>
|
||||||
|
</Command>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
import {
|
||||||
|
ChevronLeftIcon,
|
||||||
|
ChevronRightIcon,
|
||||||
|
DoubleArrowLeftIcon,
|
||||||
|
DoubleArrowRightIcon,
|
||||||
|
} from "@radix-ui/react-icons"
|
||||||
|
import { Table } from "@tanstack/react-table"
|
||||||
|
|
||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from "@/registry/new-york/ui/select"
|
||||||
|
|
||||||
|
interface DataTablePaginationProps<TData> {
|
||||||
|
table: Table<TData>
|
||||||
|
}
|
||||||
|
|
||||||
|
export function DataTablePagination<TData>({
|
||||||
|
table,
|
||||||
|
}: DataTablePaginationProps<TData>) {
|
||||||
|
return (
|
||||||
|
<div className="flex items-center justify-between px-2">
|
||||||
|
<div className="flex-1 text-sm text-muted-foreground">
|
||||||
|
{table.getFilteredSelectedRowModel().rows.length} of{" "}
|
||||||
|
{table.getFilteredRowModel().rows.length} row(s) selected.
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-6 lg:space-x-8">
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<p className="text-sm font-medium">Rows per page</p>
|
||||||
|
<Select
|
||||||
|
value={`${table.getState().pagination.pageSize}`}
|
||||||
|
onValueChange={(value) => {
|
||||||
|
table.setPageSize(Number(value))
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SelectTrigger className="h-8 w-[70px]">
|
||||||
|
<SelectValue placeholder={table.getState().pagination.pageSize} />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent side="top">
|
||||||
|
{[10, 20, 30, 40, 50].map((pageSize) => (
|
||||||
|
<SelectItem key={pageSize} value={`${pageSize}`}>
|
||||||
|
{pageSize}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
<div className="flex w-[100px] items-center justify-center text-sm font-medium">
|
||||||
|
Page {table.getState().pagination.pageIndex + 1} of{" "}
|
||||||
|
{table.getPageCount()}
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
className="hidden h-8 w-8 p-0 lg:flex"
|
||||||
|
onClick={() => table.setPageIndex(0)}
|
||||||
|
disabled={!table.getCanPreviousPage()}
|
||||||
|
>
|
||||||
|
<span className="sr-only">Go to first page</span>
|
||||||
|
<DoubleArrowLeftIcon className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
className="h-8 w-8 p-0"
|
||||||
|
onClick={() => table.previousPage()}
|
||||||
|
disabled={!table.getCanPreviousPage()}
|
||||||
|
>
|
||||||
|
<span className="sr-only">Go to previous page</span>
|
||||||
|
<ChevronLeftIcon className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
className="h-8 w-8 p-0"
|
||||||
|
onClick={() => table.nextPage()}
|
||||||
|
disabled={!table.getCanNextPage()}
|
||||||
|
>
|
||||||
|
<span className="sr-only">Go to next page</span>
|
||||||
|
<ChevronRightIcon className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
className="hidden h-8 w-8 p-0 lg:flex"
|
||||||
|
onClick={() => table.setPageIndex(table.getPageCount() - 1)}
|
||||||
|
disabled={!table.getCanNextPage()}
|
||||||
|
>
|
||||||
|
<span className="sr-only">Go to last page</span>
|
||||||
|
<DoubleArrowRightIcon className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import { DotsHorizontalIcon } from "@radix-ui/react-icons"
|
||||||
|
import { Row } from "@tanstack/react-table"
|
||||||
|
|
||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuRadioGroup,
|
||||||
|
DropdownMenuRadioItem,
|
||||||
|
DropdownMenuSeparator,
|
||||||
|
DropdownMenuShortcut,
|
||||||
|
DropdownMenuSub,
|
||||||
|
DropdownMenuSubContent,
|
||||||
|
DropdownMenuSubTrigger,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from "@/registry/new-york/ui/dropdown-menu"
|
||||||
|
|
||||||
|
import { labels } from "../data/data"
|
||||||
|
import { taskSchema } from "../data/schema"
|
||||||
|
|
||||||
|
interface DataTableRowActionsProps<TData> {
|
||||||
|
row: Row<TData>
|
||||||
|
}
|
||||||
|
|
||||||
|
export function DataTableRowActions<TData>({
|
||||||
|
row,
|
||||||
|
}: DataTableRowActionsProps<TData>) {
|
||||||
|
const task = taskSchema.parse(row.original)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
className="flex h-8 w-8 p-0 data-[state=open]:bg-muted"
|
||||||
|
>
|
||||||
|
<DotsHorizontalIcon className="h-4 w-4" />
|
||||||
|
<span className="sr-only">Open menu</span>
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent align="end" className="w-[160px]">
|
||||||
|
<DropdownMenuItem>Edit</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem>Make a copy</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem>Favorite</DropdownMenuItem>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
<DropdownMenuSub>
|
||||||
|
<DropdownMenuSubTrigger>Labels</DropdownMenuSubTrigger>
|
||||||
|
<DropdownMenuSubContent>
|
||||||
|
<DropdownMenuRadioGroup value={task.label}>
|
||||||
|
{labels.map((label) => (
|
||||||
|
<DropdownMenuRadioItem key={label.value} value={label.value}>
|
||||||
|
{label.label}
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
))}
|
||||||
|
</DropdownMenuRadioGroup>
|
||||||
|
</DropdownMenuSubContent>
|
||||||
|
</DropdownMenuSub>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
<DropdownMenuItem>
|
||||||
|
Delete
|
||||||
|
<DropdownMenuShortcut>⌘⌫</DropdownMenuShortcut>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import { Cross2Icon } from "@radix-ui/react-icons"
|
||||||
|
import { Table } from "@tanstack/react-table"
|
||||||
|
|
||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
|
import { Input } from "@/registry/new-york/ui/input"
|
||||||
|
import { DataTableViewOptions } from "@/app/examples/tasks/components/data-table-view-options"
|
||||||
|
|
||||||
|
import { priorities, statuses } from "../data/data"
|
||||||
|
import { DataTableFacetedFilter } from "./data-table-faceted-filter"
|
||||||
|
|
||||||
|
interface DataTableToolbarProps<TData> {
|
||||||
|
table: Table<TData>
|
||||||
|
}
|
||||||
|
|
||||||
|
export function DataTableToolbar<TData>({
|
||||||
|
table,
|
||||||
|
}: DataTableToolbarProps<TData>) {
|
||||||
|
const isFiltered = table.getState().columnFilters.length > 0
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="flex flex-1 items-center space-x-2">
|
||||||
|
<Input
|
||||||
|
placeholder="Filter tasks..."
|
||||||
|
value={(table.getColumn("title")?.getFilterValue() as string) ?? ""}
|
||||||
|
onChange={(event) =>
|
||||||
|
table.getColumn("title")?.setFilterValue(event.target.value)
|
||||||
|
}
|
||||||
|
className="h-8 w-[150px] lg:w-[250px]"
|
||||||
|
/>
|
||||||
|
{table.getColumn("status") && (
|
||||||
|
<DataTableFacetedFilter
|
||||||
|
column={table.getColumn("status")}
|
||||||
|
title="Status"
|
||||||
|
options={statuses}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{table.getColumn("priority") && (
|
||||||
|
<DataTableFacetedFilter
|
||||||
|
column={table.getColumn("priority")}
|
||||||
|
title="Priority"
|
||||||
|
options={priorities}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{isFiltered && (
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
onClick={() => table.resetColumnFilters()}
|
||||||
|
className="h-8 px-2 lg:px-3"
|
||||||
|
>
|
||||||
|
Reset
|
||||||
|
<Cross2Icon className="ml-2 h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<DataTableViewOptions table={table} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import { DropdownMenuTrigger } from "@radix-ui/react-dropdown-menu"
|
||||||
|
import { MixerHorizontalIcon } from "@radix-ui/react-icons"
|
||||||
|
import { Table } from "@tanstack/react-table"
|
||||||
|
|
||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuCheckboxItem,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuLabel,
|
||||||
|
DropdownMenuSeparator,
|
||||||
|
} from "@/registry/new-york/ui/dropdown-menu"
|
||||||
|
|
||||||
|
interface DataTableViewOptionsProps<TData> {
|
||||||
|
table: Table<TData>
|
||||||
|
}
|
||||||
|
|
||||||
|
export function DataTableViewOptions<TData>({
|
||||||
|
table,
|
||||||
|
}: DataTableViewOptionsProps<TData>) {
|
||||||
|
return (
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
className="ml-auto hidden h-8 lg:flex"
|
||||||
|
>
|
||||||
|
<MixerHorizontalIcon className="mr-2 h-4 w-4" />
|
||||||
|
View
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent align="end" className="w-[150px]">
|
||||||
|
<DropdownMenuLabel>Toggle columns</DropdownMenuLabel>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
{table
|
||||||
|
.getAllColumns()
|
||||||
|
.filter(
|
||||||
|
(column) =>
|
||||||
|
typeof column.accessorFn !== "undefined" && column.getCanHide()
|
||||||
|
)
|
||||||
|
.map((column) => {
|
||||||
|
return (
|
||||||
|
<DropdownMenuCheckboxItem
|
||||||
|
key={column.id}
|
||||||
|
className="capitalize"
|
||||||
|
checked={column.getIsVisible()}
|
||||||
|
onCheckedChange={(value) => column.toggleVisibility(!!value)}
|
||||||
|
>
|
||||||
|
{column.id}
|
||||||
|
</DropdownMenuCheckboxItem>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
)
|
||||||
|
}
|
||||||
126
apps/www/app/examples/tasks/components/data-table.tsx
Normal file
126
apps/www/app/examples/tasks/components/data-table.tsx
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
|
import {
|
||||||
|
ColumnDef,
|
||||||
|
ColumnFiltersState,
|
||||||
|
SortingState,
|
||||||
|
VisibilityState,
|
||||||
|
flexRender,
|
||||||
|
getCoreRowModel,
|
||||||
|
getFacetedRowModel,
|
||||||
|
getFacetedUniqueValues,
|
||||||
|
getFilteredRowModel,
|
||||||
|
getPaginationRowModel,
|
||||||
|
getSortedRowModel,
|
||||||
|
useReactTable,
|
||||||
|
} from "@tanstack/react-table"
|
||||||
|
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableHead,
|
||||||
|
TableHeader,
|
||||||
|
TableRow,
|
||||||
|
} from "@/registry/new-york/ui/table"
|
||||||
|
|
||||||
|
import { DataTablePagination } from "../components/data-table-pagination"
|
||||||
|
import { DataTableToolbar } from "../components/data-table-toolbar"
|
||||||
|
|
||||||
|
interface DataTableProps<TData, TValue> {
|
||||||
|
columns: ColumnDef<TData, TValue>[]
|
||||||
|
data: TData[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export function DataTable<TData, TValue>({
|
||||||
|
columns,
|
||||||
|
data,
|
||||||
|
}: DataTableProps<TData, TValue>) {
|
||||||
|
const [rowSelection, setRowSelection] = React.useState({})
|
||||||
|
const [columnVisibility, setColumnVisibility] =
|
||||||
|
React.useState<VisibilityState>({})
|
||||||
|
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
const [sorting, setSorting] = React.useState<SortingState>([])
|
||||||
|
|
||||||
|
const table = useReactTable({
|
||||||
|
data,
|
||||||
|
columns,
|
||||||
|
state: {
|
||||||
|
sorting,
|
||||||
|
columnVisibility,
|
||||||
|
rowSelection,
|
||||||
|
columnFilters,
|
||||||
|
},
|
||||||
|
enableRowSelection: true,
|
||||||
|
onRowSelectionChange: setRowSelection,
|
||||||
|
onSortingChange: setSorting,
|
||||||
|
onColumnFiltersChange: setColumnFilters,
|
||||||
|
onColumnVisibilityChange: setColumnVisibility,
|
||||||
|
getCoreRowModel: getCoreRowModel(),
|
||||||
|
getFilteredRowModel: getFilteredRowModel(),
|
||||||
|
getPaginationRowModel: getPaginationRowModel(),
|
||||||
|
getSortedRowModel: getSortedRowModel(),
|
||||||
|
getFacetedRowModel: getFacetedRowModel(),
|
||||||
|
getFacetedUniqueValues: getFacetedUniqueValues(),
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="space-y-4">
|
||||||
|
<DataTableToolbar table={table} />
|
||||||
|
<div className="rounded-md border">
|
||||||
|
<Table>
|
||||||
|
<TableHeader>
|
||||||
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
|
<TableRow key={headerGroup.id}>
|
||||||
|
{headerGroup.headers.map((header) => {
|
||||||
|
return (
|
||||||
|
<TableHead key={header.id}>
|
||||||
|
{header.isPlaceholder
|
||||||
|
? null
|
||||||
|
: flexRender(
|
||||||
|
header.column.columnDef.header,
|
||||||
|
header.getContext()
|
||||||
|
)}
|
||||||
|
</TableHead>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{table.getRowModel().rows?.length ? (
|
||||||
|
table.getRowModel().rows.map((row) => (
|
||||||
|
<TableRow
|
||||||
|
key={row.id}
|
||||||
|
data-state={row.getIsSelected() && "selected"}
|
||||||
|
>
|
||||||
|
{row.getVisibleCells().map((cell) => (
|
||||||
|
<TableCell key={cell.id}>
|
||||||
|
{flexRender(
|
||||||
|
cell.column.columnDef.cell,
|
||||||
|
cell.getContext()
|
||||||
|
)}
|
||||||
|
</TableCell>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<TableRow>
|
||||||
|
<TableCell
|
||||||
|
colSpan={columns.length}
|
||||||
|
className="h-24 text-center"
|
||||||
|
>
|
||||||
|
No results.
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</div>
|
||||||
|
<DataTablePagination table={table} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
62
apps/www/app/examples/tasks/components/user-nav.tsx
Normal file
62
apps/www/app/examples/tasks/components/user-nav.tsx
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import {
|
||||||
|
Avatar,
|
||||||
|
AvatarFallback,
|
||||||
|
AvatarImage,
|
||||||
|
} from "@/registry/new-york/ui/avatar"
|
||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuGroup,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuLabel,
|
||||||
|
DropdownMenuSeparator,
|
||||||
|
DropdownMenuShortcut,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from "@/registry/new-york/ui/dropdown-menu"
|
||||||
|
|
||||||
|
export function UserNav() {
|
||||||
|
return (
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button variant="ghost" className="relative h-8 w-8 rounded-full">
|
||||||
|
<Avatar className="h-9 w-9">
|
||||||
|
<AvatarImage src="/avatars/03.png" alt="@shadcn" />
|
||||||
|
<AvatarFallback>SC</AvatarFallback>
|
||||||
|
</Avatar>
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent className="w-56" align="end" forceMount>
|
||||||
|
<DropdownMenuLabel className="font-normal">
|
||||||
|
<div className="flex flex-col space-y-1">
|
||||||
|
<p className="text-sm font-medium leading-none">shadcn</p>
|
||||||
|
<p className="text-xs leading-none text-muted-foreground">
|
||||||
|
m@example.com
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</DropdownMenuLabel>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
<DropdownMenuGroup>
|
||||||
|
<DropdownMenuItem>
|
||||||
|
Profile
|
||||||
|
<DropdownMenuShortcut>⇧⌘P</DropdownMenuShortcut>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem>
|
||||||
|
Billing
|
||||||
|
<DropdownMenuShortcut>⌘B</DropdownMenuShortcut>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem>
|
||||||
|
Settings
|
||||||
|
<DropdownMenuShortcut>⌘S</DropdownMenuShortcut>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem>New Team</DropdownMenuItem>
|
||||||
|
</DropdownMenuGroup>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
<DropdownMenuItem>
|
||||||
|
Log out
|
||||||
|
<DropdownMenuShortcut>⇧⌘Q</DropdownMenuShortcut>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
)
|
||||||
|
}
|
||||||
71
apps/www/app/examples/tasks/data/data.tsx
Normal file
71
apps/www/app/examples/tasks/data/data.tsx
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
import {
|
||||||
|
ArrowDownIcon,
|
||||||
|
ArrowRightIcon,
|
||||||
|
ArrowUpIcon,
|
||||||
|
CheckCircledIcon,
|
||||||
|
CircleIcon,
|
||||||
|
CrossCircledIcon,
|
||||||
|
QuestionMarkCircledIcon,
|
||||||
|
StopwatchIcon,
|
||||||
|
} from "@radix-ui/react-icons"
|
||||||
|
|
||||||
|
export const labels = [
|
||||||
|
{
|
||||||
|
value: "bug",
|
||||||
|
label: "Bug",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "feature",
|
||||||
|
label: "Feature",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "documentation",
|
||||||
|
label: "Documentation",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
export const statuses = [
|
||||||
|
{
|
||||||
|
value: "backlog",
|
||||||
|
label: "Backlog",
|
||||||
|
icon: QuestionMarkCircledIcon,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "todo",
|
||||||
|
label: "Todo",
|
||||||
|
icon: CircleIcon,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "in progress",
|
||||||
|
label: "In Progress",
|
||||||
|
icon: StopwatchIcon,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "done",
|
||||||
|
label: "Done",
|
||||||
|
icon: CheckCircledIcon,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "canceled",
|
||||||
|
label: "Canceled",
|
||||||
|
icon: CrossCircledIcon,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
export const priorities = [
|
||||||
|
{
|
||||||
|
label: "Low",
|
||||||
|
value: "low",
|
||||||
|
icon: ArrowDownIcon,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Medium",
|
||||||
|
value: "medium",
|
||||||
|
icon: ArrowRightIcon,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "High",
|
||||||
|
value: "high",
|
||||||
|
icon: ArrowUpIcon,
|
||||||
|
},
|
||||||
|
]
|
||||||
13
apps/www/app/examples/tasks/data/schema.ts
Normal file
13
apps/www/app/examples/tasks/data/schema.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { z } from "zod"
|
||||||
|
|
||||||
|
// We're keeping a simple non-relational schema here.
|
||||||
|
// IRL, you will have a schema for your data models.
|
||||||
|
export const taskSchema = z.object({
|
||||||
|
id: z.string(),
|
||||||
|
title: z.string(),
|
||||||
|
status: z.string(),
|
||||||
|
label: z.string(),
|
||||||
|
priority: z.string(),
|
||||||
|
})
|
||||||
|
|
||||||
|
export type Task = z.infer<typeof taskSchema>
|
||||||
20
apps/www/app/examples/tasks/data/seed.ts
Executable file
20
apps/www/app/examples/tasks/data/seed.ts
Executable file
@@ -0,0 +1,20 @@
|
|||||||
|
import fs from "fs"
|
||||||
|
import path from "path"
|
||||||
|
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 })}`,
|
||||||
|
title: faker.hacker.phrase().replace(/^./, (letter) => letter.toUpperCase()),
|
||||||
|
status: faker.helpers.arrayElement(statuses).value,
|
||||||
|
label: faker.helpers.arrayElement(labels).value,
|
||||||
|
priority: faker.helpers.arrayElement(priorities).value,
|
||||||
|
}))
|
||||||
|
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(__dirname, "tasks.json"),
|
||||||
|
JSON.stringify(tasks, null, 2)
|
||||||
|
)
|
||||||
|
|
||||||
|
console.log("✅ Tasks data generated.")
|
||||||
702
apps/www/app/examples/tasks/data/tasks.json
Normal file
702
apps/www/app/examples/tasks/data/tasks.json
Normal file
@@ -0,0 +1,702 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "TASK-8782",
|
||||||
|
"title": "You can't compress the program without quantifying the open-source SSD pixel!",
|
||||||
|
"status": "in progress",
|
||||||
|
"label": "documentation",
|
||||||
|
"priority": "medium"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-7878",
|
||||||
|
"title": "Try to calculate the EXE feed, maybe it will index the multi-byte pixel!",
|
||||||
|
"status": "backlog",
|
||||||
|
"label": "documentation",
|
||||||
|
"priority": "medium"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-7839",
|
||||||
|
"title": "We need to bypass the neural TCP card!",
|
||||||
|
"status": "todo",
|
||||||
|
"label": "bug",
|
||||||
|
"priority": "high"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-5562",
|
||||||
|
"title": "The SAS interface is down, bypass the open-source pixel so we can back up the PNG bandwidth!",
|
||||||
|
"status": "backlog",
|
||||||
|
"label": "feature",
|
||||||
|
"priority": "medium"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-8686",
|
||||||
|
"title": "I'll parse the wireless SSL protocol, that should driver the API panel!",
|
||||||
|
"status": "canceled",
|
||||||
|
"label": "feature",
|
||||||
|
"priority": "medium"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-1280",
|
||||||
|
"title": "Use the digital TLS panel, then you can transmit the haptic system!",
|
||||||
|
"status": "done",
|
||||||
|
"label": "bug",
|
||||||
|
"priority": "high"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-7262",
|
||||||
|
"title": "The UTF8 application is down, parse the neural bandwidth so we can back up the PNG firewall!",
|
||||||
|
"status": "done",
|
||||||
|
"label": "feature",
|
||||||
|
"priority": "high"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-1138",
|
||||||
|
"title": "Generating the driver won't do anything, we need to quantify the 1080p SMTP bandwidth!",
|
||||||
|
"status": "in progress",
|
||||||
|
"label": "feature",
|
||||||
|
"priority": "medium"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-7184",
|
||||||
|
"title": "We need to program the back-end THX pixel!",
|
||||||
|
"status": "todo",
|
||||||
|
"label": "feature",
|
||||||
|
"priority": "low"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-5160",
|
||||||
|
"title": "Calculating the bus won't do anything, we need to navigate the back-end JSON protocol!",
|
||||||
|
"status": "in progress",
|
||||||
|
"label": "documentation",
|
||||||
|
"priority": "high"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-5618",
|
||||||
|
"title": "Generating the driver won't do anything, we need to index the online SSL application!",
|
||||||
|
"status": "done",
|
||||||
|
"label": "documentation",
|
||||||
|
"priority": "medium"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-6699",
|
||||||
|
"title": "I'll transmit the wireless JBOD capacitor, that should hard drive the SSD feed!",
|
||||||
|
"status": "backlog",
|
||||||
|
"label": "documentation",
|
||||||
|
"priority": "medium"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-2858",
|
||||||
|
"title": "We need to override the online UDP bus!",
|
||||||
|
"status": "backlog",
|
||||||
|
"label": "bug",
|
||||||
|
"priority": "medium"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-9864",
|
||||||
|
"title": "I'll reboot the 1080p FTP panel, that should matrix the HEX hard drive!",
|
||||||
|
"status": "done",
|
||||||
|
"label": "bug",
|
||||||
|
"priority": "high"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-8404",
|
||||||
|
"title": "We need to generate the virtual HEX alarm!",
|
||||||
|
"status": "in progress",
|
||||||
|
"label": "bug",
|
||||||
|
"priority": "low"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-5365",
|
||||||
|
"title": "Backing up the pixel won't do anything, we need to transmit the primary IB array!",
|
||||||
|
"status": "in progress",
|
||||||
|
"label": "documentation",
|
||||||
|
"priority": "low"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-1780",
|
||||||
|
"title": "The CSS feed is down, index the bluetooth transmitter so we can compress the CLI protocol!",
|
||||||
|
"status": "todo",
|
||||||
|
"label": "documentation",
|
||||||
|
"priority": "high"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-6938",
|
||||||
|
"title": "Use the redundant SCSI application, then you can hack the optical alarm!",
|
||||||
|
"status": "todo",
|
||||||
|
"label": "documentation",
|
||||||
|
"priority": "high"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-9885",
|
||||||
|
"title": "We need to compress the auxiliary VGA driver!",
|
||||||
|
"status": "backlog",
|
||||||
|
"label": "bug",
|
||||||
|
"priority": "high"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-3216",
|
||||||
|
"title": "Transmitting the transmitter won't do anything, we need to compress the virtual HDD sensor!",
|
||||||
|
"status": "backlog",
|
||||||
|
"label": "documentation",
|
||||||
|
"priority": "medium"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-9285",
|
||||||
|
"title": "The IP monitor is down, copy the haptic alarm so we can generate the HTTP transmitter!",
|
||||||
|
"status": "todo",
|
||||||
|
"label": "bug",
|
||||||
|
"priority": "high"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-1024",
|
||||||
|
"title": "Overriding the microchip won't do anything, we need to transmit the digital OCR transmitter!",
|
||||||
|
"status": "in progress",
|
||||||
|
"label": "documentation",
|
||||||
|
"priority": "low"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-7068",
|
||||||
|
"title": "You can't generate the capacitor without indexing the wireless HEX pixel!",
|
||||||
|
"status": "canceled",
|
||||||
|
"label": "bug",
|
||||||
|
"priority": "low"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-6502",
|
||||||
|
"title": "Navigating the microchip won't do anything, we need to bypass the back-end SQL bus!",
|
||||||
|
"status": "todo",
|
||||||
|
"label": "bug",
|
||||||
|
"priority": "high"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-5326",
|
||||||
|
"title": "We need to hack the redundant UTF8 transmitter!",
|
||||||
|
"status": "todo",
|
||||||
|
"label": "bug",
|
||||||
|
"priority": "low"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-6274",
|
||||||
|
"title": "Use the virtual PCI circuit, then you can parse the bluetooth alarm!",
|
||||||
|
"status": "canceled",
|
||||||
|
"label": "documentation",
|
||||||
|
"priority": "low"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-1571",
|
||||||
|
"title": "I'll input the neural DRAM circuit, that should protocol the SMTP interface!",
|
||||||
|
"status": "in progress",
|
||||||
|
"label": "feature",
|
||||||
|
"priority": "medium"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-9518",
|
||||||
|
"title": "Compressing the interface won't do anything, we need to compress the online SDD matrix!",
|
||||||
|
"status": "canceled",
|
||||||
|
"label": "documentation",
|
||||||
|
"priority": "medium"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-5581",
|
||||||
|
"title": "I'll synthesize the digital COM pixel, that should transmitter the UTF8 protocol!",
|
||||||
|
"status": "backlog",
|
||||||
|
"label": "documentation",
|
||||||
|
"priority": "high"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-2197",
|
||||||
|
"title": "Parsing the feed won't do anything, we need to copy the bluetooth DRAM bus!",
|
||||||
|
"status": "todo",
|
||||||
|
"label": "documentation",
|
||||||
|
"priority": "low"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-8484",
|
||||||
|
"title": "We need to parse the solid state UDP firewall!",
|
||||||
|
"status": "in progress",
|
||||||
|
"label": "bug",
|
||||||
|
"priority": "low"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-9892",
|
||||||
|
"title": "If we back up the application, we can get to the UDP application through the multi-byte THX capacitor!",
|
||||||
|
"status": "done",
|
||||||
|
"label": "documentation",
|
||||||
|
"priority": "high"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-9616",
|
||||||
|
"title": "We need to synthesize the cross-platform ASCII pixel!",
|
||||||
|
"status": "in progress",
|
||||||
|
"label": "feature",
|
||||||
|
"priority": "medium"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-9744",
|
||||||
|
"title": "Use the back-end IP card, then you can input the solid state hard drive!",
|
||||||
|
"status": "done",
|
||||||
|
"label": "documentation",
|
||||||
|
"priority": "low"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-1376",
|
||||||
|
"title": "Generating the alarm won't do anything, we need to generate the mobile IP capacitor!",
|
||||||
|
"status": "backlog",
|
||||||
|
"label": "documentation",
|
||||||
|
"priority": "low"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-7382",
|
||||||
|
"title": "If we back up the firewall, we can get to the RAM alarm through the primary UTF8 pixel!",
|
||||||
|
"status": "todo",
|
||||||
|
"label": "feature",
|
||||||
|
"priority": "low"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-2290",
|
||||||
|
"title": "I'll compress the virtual JSON panel, that should application the UTF8 bus!",
|
||||||
|
"status": "canceled",
|
||||||
|
"label": "documentation",
|
||||||
|
"priority": "high"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-1533",
|
||||||
|
"title": "You can't input the firewall without overriding the wireless TCP firewall!",
|
||||||
|
"status": "done",
|
||||||
|
"label": "bug",
|
||||||
|
"priority": "high"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-4920",
|
||||||
|
"title": "Bypassing the hard drive won't do anything, we need to input the bluetooth JSON program!",
|
||||||
|
"status": "in progress",
|
||||||
|
"label": "bug",
|
||||||
|
"priority": "high"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-5168",
|
||||||
|
"title": "If we synthesize the bus, we can get to the IP panel through the virtual TLS array!",
|
||||||
|
"status": "in progress",
|
||||||
|
"label": "feature",
|
||||||
|
"priority": "low"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-7103",
|
||||||
|
"title": "We need to parse the multi-byte EXE bandwidth!",
|
||||||
|
"status": "canceled",
|
||||||
|
"label": "feature",
|
||||||
|
"priority": "low"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-4314",
|
||||||
|
"title": "If we compress the program, we can get to the XML alarm through the multi-byte COM matrix!",
|
||||||
|
"status": "in progress",
|
||||||
|
"label": "bug",
|
||||||
|
"priority": "high"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-3415",
|
||||||
|
"title": "Use the cross-platform XML application, then you can quantify the solid state feed!",
|
||||||
|
"status": "todo",
|
||||||
|
"label": "feature",
|
||||||
|
"priority": "high"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-8339",
|
||||||
|
"title": "Try to calculate the DNS interface, maybe it will input the bluetooth capacitor!",
|
||||||
|
"status": "in progress",
|
||||||
|
"label": "feature",
|
||||||
|
"priority": "low"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-6995",
|
||||||
|
"title": "Try to hack the XSS bandwidth, maybe it will override the bluetooth matrix!",
|
||||||
|
"status": "todo",
|
||||||
|
"label": "feature",
|
||||||
|
"priority": "high"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-8053",
|
||||||
|
"title": "If we connect the program, we can get to the UTF8 matrix through the digital UDP protocol!",
|
||||||
|
"status": "todo",
|
||||||
|
"label": "feature",
|
||||||
|
"priority": "medium"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-4336",
|
||||||
|
"title": "If we synthesize the microchip, we can get to the SAS sensor through the optical UDP program!",
|
||||||
|
"status": "todo",
|
||||||
|
"label": "documentation",
|
||||||
|
"priority": "low"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-8790",
|
||||||
|
"title": "I'll back up the optical COM alarm, that should alarm the RSS capacitor!",
|
||||||
|
"status": "done",
|
||||||
|
"label": "bug",
|
||||||
|
"priority": "medium"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-8980",
|
||||||
|
"title": "Try to navigate the SQL transmitter, maybe it will back up the virtual firewall!",
|
||||||
|
"status": "canceled",
|
||||||
|
"label": "bug",
|
||||||
|
"priority": "low"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-7342",
|
||||||
|
"title": "Use the neural CLI card, then you can parse the online port!",
|
||||||
|
"status": "backlog",
|
||||||
|
"label": "documentation",
|
||||||
|
"priority": "low"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-5608",
|
||||||
|
"title": "I'll hack the haptic SSL program, that should bus the UDP transmitter!",
|
||||||
|
"status": "canceled",
|
||||||
|
"label": "documentation",
|
||||||
|
"priority": "low"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-1606",
|
||||||
|
"title": "I'll generate the bluetooth PNG firewall, that should pixel the SSL driver!",
|
||||||
|
"status": "done",
|
||||||
|
"label": "feature",
|
||||||
|
"priority": "medium"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-7872",
|
||||||
|
"title": "Transmitting the circuit won't do anything, we need to reboot the 1080p RSS monitor!",
|
||||||
|
"status": "canceled",
|
||||||
|
"label": "feature",
|
||||||
|
"priority": "medium"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-4167",
|
||||||
|
"title": "Use the cross-platform SMS circuit, then you can synthesize the optical feed!",
|
||||||
|
"status": "canceled",
|
||||||
|
"label": "bug",
|
||||||
|
"priority": "medium"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-9581",
|
||||||
|
"title": "You can't index the port without hacking the cross-platform XSS monitor!",
|
||||||
|
"status": "backlog",
|
||||||
|
"label": "documentation",
|
||||||
|
"priority": "low"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-8806",
|
||||||
|
"title": "We need to bypass the back-end SSL panel!",
|
||||||
|
"status": "done",
|
||||||
|
"label": "bug",
|
||||||
|
"priority": "medium"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-6542",
|
||||||
|
"title": "Try to quantify the RSS firewall, maybe it will quantify the open-source system!",
|
||||||
|
"status": "done",
|
||||||
|
"label": "feature",
|
||||||
|
"priority": "low"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-6806",
|
||||||
|
"title": "The VGA protocol is down, reboot the back-end matrix so we can parse the CSS panel!",
|
||||||
|
"status": "canceled",
|
||||||
|
"label": "documentation",
|
||||||
|
"priority": "low"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-9549",
|
||||||
|
"title": "You can't bypass the bus without connecting the neural JBOD bus!",
|
||||||
|
"status": "todo",
|
||||||
|
"label": "feature",
|
||||||
|
"priority": "high"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-1075",
|
||||||
|
"title": "Backing up the driver won't do anything, we need to parse the redundant RAM pixel!",
|
||||||
|
"status": "done",
|
||||||
|
"label": "feature",
|
||||||
|
"priority": "medium"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-1427",
|
||||||
|
"title": "Use the auxiliary PCI circuit, then you can calculate the cross-platform interface!",
|
||||||
|
"status": "done",
|
||||||
|
"label": "documentation",
|
||||||
|
"priority": "high"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-1907",
|
||||||
|
"title": "Hacking the circuit won't do anything, we need to back up the online DRAM system!",
|
||||||
|
"status": "todo",
|
||||||
|
"label": "documentation",
|
||||||
|
"priority": "high"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-4309",
|
||||||
|
"title": "If we generate the system, we can get to the TCP sensor through the optical GB pixel!",
|
||||||
|
"status": "backlog",
|
||||||
|
"label": "bug",
|
||||||
|
"priority": "medium"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-3973",
|
||||||
|
"title": "I'll parse the back-end ADP array, that should bandwidth the RSS bandwidth!",
|
||||||
|
"status": "todo",
|
||||||
|
"label": "feature",
|
||||||
|
"priority": "medium"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-7962",
|
||||||
|
"title": "Use the wireless RAM program, then you can hack the cross-platform feed!",
|
||||||
|
"status": "canceled",
|
||||||
|
"label": "bug",
|
||||||
|
"priority": "low"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-3360",
|
||||||
|
"title": "You can't quantify the program without synthesizing the neural OCR interface!",
|
||||||
|
"status": "done",
|
||||||
|
"label": "feature",
|
||||||
|
"priority": "medium"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-9887",
|
||||||
|
"title": "Use the auxiliary ASCII sensor, then you can connect the solid state port!",
|
||||||
|
"status": "backlog",
|
||||||
|
"label": "bug",
|
||||||
|
"priority": "medium"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-3649",
|
||||||
|
"title": "I'll input the virtual USB system, that should circuit the DNS monitor!",
|
||||||
|
"status": "in progress",
|
||||||
|
"label": "feature",
|
||||||
|
"priority": "medium"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-3586",
|
||||||
|
"title": "If we quantify the circuit, we can get to the CLI feed through the mobile SMS hard drive!",
|
||||||
|
"status": "in progress",
|
||||||
|
"label": "bug",
|
||||||
|
"priority": "low"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-5150",
|
||||||
|
"title": "I'll hack the wireless XSS port, that should transmitter the IP interface!",
|
||||||
|
"status": "canceled",
|
||||||
|
"label": "feature",
|
||||||
|
"priority": "medium"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-3652",
|
||||||
|
"title": "The SQL interface is down, override the optical bus so we can program the ASCII interface!",
|
||||||
|
"status": "backlog",
|
||||||
|
"label": "feature",
|
||||||
|
"priority": "low"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-6884",
|
||||||
|
"title": "Use the digital PCI circuit, then you can synthesize the multi-byte microchip!",
|
||||||
|
"status": "canceled",
|
||||||
|
"label": "feature",
|
||||||
|
"priority": "high"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-1591",
|
||||||
|
"title": "We need to connect the mobile XSS driver!",
|
||||||
|
"status": "in progress",
|
||||||
|
"label": "feature",
|
||||||
|
"priority": "high"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-3802",
|
||||||
|
"title": "Try to override the ASCII protocol, maybe it will parse the virtual matrix!",
|
||||||
|
"status": "in progress",
|
||||||
|
"label": "feature",
|
||||||
|
"priority": "low"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-7253",
|
||||||
|
"title": "Programming the capacitor won't do anything, we need to bypass the neural IB hard drive!",
|
||||||
|
"status": "backlog",
|
||||||
|
"label": "bug",
|
||||||
|
"priority": "high"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-9739",
|
||||||
|
"title": "We need to hack the multi-byte HDD bus!",
|
||||||
|
"status": "done",
|
||||||
|
"label": "documentation",
|
||||||
|
"priority": "medium"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-4424",
|
||||||
|
"title": "Try to hack the HEX alarm, maybe it will connect the optical pixel!",
|
||||||
|
"status": "in progress",
|
||||||
|
"label": "documentation",
|
||||||
|
"priority": "medium"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-3922",
|
||||||
|
"title": "You can't back up the capacitor without generating the wireless PCI program!",
|
||||||
|
"status": "backlog",
|
||||||
|
"label": "bug",
|
||||||
|
"priority": "low"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-4921",
|
||||||
|
"title": "I'll index the open-source IP feed, that should system the GB application!",
|
||||||
|
"status": "canceled",
|
||||||
|
"label": "bug",
|
||||||
|
"priority": "low"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-5814",
|
||||||
|
"title": "We need to calculate the 1080p AGP feed!",
|
||||||
|
"status": "backlog",
|
||||||
|
"label": "bug",
|
||||||
|
"priority": "high"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-2645",
|
||||||
|
"title": "Synthesizing the system won't do anything, we need to navigate the multi-byte HDD firewall!",
|
||||||
|
"status": "todo",
|
||||||
|
"label": "documentation",
|
||||||
|
"priority": "medium"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-4535",
|
||||||
|
"title": "Try to copy the JSON circuit, maybe it will connect the wireless feed!",
|
||||||
|
"status": "in progress",
|
||||||
|
"label": "feature",
|
||||||
|
"priority": "low"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-4463",
|
||||||
|
"title": "We need to copy the solid state AGP monitor!",
|
||||||
|
"status": "done",
|
||||||
|
"label": "documentation",
|
||||||
|
"priority": "low"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-9745",
|
||||||
|
"title": "If we connect the protocol, we can get to the GB system through the bluetooth PCI microchip!",
|
||||||
|
"status": "canceled",
|
||||||
|
"label": "feature",
|
||||||
|
"priority": "high"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-2080",
|
||||||
|
"title": "If we input the bus, we can get to the RAM matrix through the auxiliary RAM card!",
|
||||||
|
"status": "todo",
|
||||||
|
"label": "bug",
|
||||||
|
"priority": "medium"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-3838",
|
||||||
|
"title": "I'll bypass the online TCP application, that should panel the AGP system!",
|
||||||
|
"status": "backlog",
|
||||||
|
"label": "bug",
|
||||||
|
"priority": "high"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-1340",
|
||||||
|
"title": "We need to navigate the virtual PNG circuit!",
|
||||||
|
"status": "todo",
|
||||||
|
"label": "bug",
|
||||||
|
"priority": "medium"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-6665",
|
||||||
|
"title": "If we parse the monitor, we can get to the SSD hard drive through the cross-platform AGP alarm!",
|
||||||
|
"status": "canceled",
|
||||||
|
"label": "feature",
|
||||||
|
"priority": "low"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-7585",
|
||||||
|
"title": "If we calculate the hard drive, we can get to the SSL program through the multi-byte CSS microchip!",
|
||||||
|
"status": "backlog",
|
||||||
|
"label": "feature",
|
||||||
|
"priority": "low"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-6319",
|
||||||
|
"title": "We need to copy the multi-byte SCSI program!",
|
||||||
|
"status": "backlog",
|
||||||
|
"label": "bug",
|
||||||
|
"priority": "high"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-4369",
|
||||||
|
"title": "Try to input the SCSI bus, maybe it will generate the 1080p pixel!",
|
||||||
|
"status": "backlog",
|
||||||
|
"label": "bug",
|
||||||
|
"priority": "high"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-9035",
|
||||||
|
"title": "We need to override the solid state PNG array!",
|
||||||
|
"status": "canceled",
|
||||||
|
"label": "documentation",
|
||||||
|
"priority": "low"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-3970",
|
||||||
|
"title": "You can't index the transmitter without quantifying the haptic ASCII card!",
|
||||||
|
"status": "todo",
|
||||||
|
"label": "documentation",
|
||||||
|
"priority": "medium"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-4473",
|
||||||
|
"title": "You can't bypass the protocol without overriding the neural RSS program!",
|
||||||
|
"status": "todo",
|
||||||
|
"label": "documentation",
|
||||||
|
"priority": "low"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-4136",
|
||||||
|
"title": "You can't hack the hard drive without hacking the primary JSON program!",
|
||||||
|
"status": "canceled",
|
||||||
|
"label": "bug",
|
||||||
|
"priority": "medium"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-3939",
|
||||||
|
"title": "Use the back-end SQL firewall, then you can connect the neural hard drive!",
|
||||||
|
"status": "done",
|
||||||
|
"label": "feature",
|
||||||
|
"priority": "low"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-2007",
|
||||||
|
"title": "I'll input the back-end USB protocol, that should bandwidth the PCI system!",
|
||||||
|
"status": "backlog",
|
||||||
|
"label": "bug",
|
||||||
|
"priority": "high"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-7516",
|
||||||
|
"title": "Use the primary SQL program, then you can generate the auxiliary transmitter!",
|
||||||
|
"status": "done",
|
||||||
|
"label": "documentation",
|
||||||
|
"priority": "medium"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-6906",
|
||||||
|
"title": "Try to back up the DRAM system, maybe it will reboot the online transmitter!",
|
||||||
|
"status": "done",
|
||||||
|
"label": "feature",
|
||||||
|
"priority": "high"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-5207",
|
||||||
|
"title": "The SMS interface is down, copy the bluetooth bus so we can quantify the VGA card!",
|
||||||
|
"status": "in progress",
|
||||||
|
"label": "bug",
|
||||||
|
"priority": "low"
|
||||||
|
}
|
||||||
|
]
|
||||||
65
apps/www/app/examples/tasks/page.tsx
Normal file
65
apps/www/app/examples/tasks/page.tsx
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
import { promises as fs } from "fs"
|
||||||
|
import path from "path"
|
||||||
|
import { Metadata } from "next"
|
||||||
|
import Image from "next/image"
|
||||||
|
import { z } from "zod"
|
||||||
|
|
||||||
|
import { columns } from "./components/columns"
|
||||||
|
import { DataTable } from "./components/data-table"
|
||||||
|
import { UserNav } from "./components/user-nav"
|
||||||
|
import { taskSchema } from "./data/schema"
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "Tasks",
|
||||||
|
description: "A task and issue tracker build using Tanstack Table.",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simulate a database read for tasks.
|
||||||
|
async function getTasks() {
|
||||||
|
const data = await fs.readFile(
|
||||||
|
path.join(process.cwd(), "app/examples/tasks/data/tasks.json")
|
||||||
|
)
|
||||||
|
|
||||||
|
const tasks = JSON.parse(data.toString())
|
||||||
|
|
||||||
|
return z.array(taskSchema).parse(tasks)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function TaskPage() {
|
||||||
|
const tasks = await getTasks()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="md:hidden">
|
||||||
|
<Image
|
||||||
|
src="/examples/tasks-light.png"
|
||||||
|
width={1280}
|
||||||
|
height={998}
|
||||||
|
alt="Playground"
|
||||||
|
className="block dark:hidden"
|
||||||
|
/>
|
||||||
|
<Image
|
||||||
|
src="/examples/tasks-dark.png"
|
||||||
|
width={1280}
|
||||||
|
height={998}
|
||||||
|
alt="Playground"
|
||||||
|
className="hidden dark:block"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="hidden h-full flex-1 flex-col space-y-8 p-8 md:flex">
|
||||||
|
<div className="flex items-center justify-between space-y-2">
|
||||||
|
<div>
|
||||||
|
<h2 className="text-2xl font-bold tracking-tight">Welcome back!</h2>
|
||||||
|
<p className="text-muted-foreground">
|
||||||
|
Here's a list of your tasks for this month!
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<UserNav />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<DataTable data={tasks} columns={columns} />
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
import { Metadata } from "next"
|
|
||||||
import Link from "next/link"
|
|
||||||
import { Heart } from "lucide-react"
|
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
|
||||||
import { AspectRatio } from "@/components/ui/aspect-ratio"
|
|
||||||
import { buttonVariants } from "@/components/ui/button"
|
|
||||||
import {
|
|
||||||
PageHeader,
|
|
||||||
PageHeaderDescription,
|
|
||||||
PageHeaderHeading,
|
|
||||||
} from "@/components/page-header"
|
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
|
||||||
title: "Figma",
|
|
||||||
description:
|
|
||||||
"Every component recreated in Figma. With customizable props, typography and icons.",
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function FigmaPage() {
|
|
||||||
return (
|
|
||||||
<div className="container pb-10">
|
|
||||||
<PageHeader>
|
|
||||||
<PageHeaderHeading>Grab the free Figma UI Kit.</PageHeaderHeading>
|
|
||||||
<PageHeaderDescription>
|
|
||||||
Every component recreated in Figma. With customizable props,
|
|
||||||
typography and icons. Open sourced by{" "}
|
|
||||||
<Link
|
|
||||||
href="https://twitter.com/skirano"
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
className="font-medium underline underline-offset-4"
|
|
||||||
>
|
|
||||||
Pietro Schirano
|
|
||||||
</Link>
|
|
||||||
.
|
|
||||||
</PageHeaderDescription>
|
|
||||||
</PageHeader>
|
|
||||||
<section className="mb-4 grid items-center gap-6">
|
|
||||||
<div className="flex flex-col space-y-4 sm:flex-row sm:space-x-4 sm:space-y-0 md:flex-row">
|
|
||||||
<Link
|
|
||||||
href="https://www.figma.com/community/file/1203061493325953101"
|
|
||||||
className={buttonVariants({ size: "lg" })}
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
>
|
|
||||||
Get a copy
|
|
||||||
</Link>
|
|
||||||
<Link
|
|
||||||
href="https://twitter.com/skirano"
|
|
||||||
className={cn(
|
|
||||||
buttonVariants({ size: "lg", variant: "outline" }),
|
|
||||||
"px-5"
|
|
||||||
)}
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
>
|
|
||||||
<Heart className="mr-2 h-4 w-4 fill-current" />
|
|
||||||
Follow Pietro
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
<AspectRatio ratio={16 / 9} className="w-full">
|
|
||||||
<iframe
|
|
||||||
src="https://embed.figma.com/file/1203061493325953101/hf_embed?community_viewer=true&embed_host=shadcn&hub_file_id=1203061493325953101&kind=&viewer=1"
|
|
||||||
className="h-full w-full overflow-hidden rounded-lg border bg-muted"
|
|
||||||
/>
|
|
||||||
</AspectRatio>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -4,13 +4,14 @@ import { Metadata } from "next"
|
|||||||
import { siteConfig } from "@/config/site"
|
import { siteConfig } from "@/config/site"
|
||||||
import { fontSans } from "@/lib/fonts"
|
import { fontSans } from "@/lib/fonts"
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
import { Toaster } from "@/components/ui/toaster"
|
|
||||||
import { Analytics } from "@/components/analytics"
|
import { Analytics } from "@/components/analytics"
|
||||||
|
import { ThemeProvider } from "@/components/providers"
|
||||||
import { SiteFooter } from "@/components/site-footer"
|
import { SiteFooter } from "@/components/site-footer"
|
||||||
import { SiteHeader } from "@/components/site-header"
|
import { SiteHeader } from "@/components/site-header"
|
||||||
import { StyleSwitcher } from "@/components/style-switcher"
|
|
||||||
import { TailwindIndicator } from "@/components/tailwind-indicator"
|
import { TailwindIndicator } from "@/components/tailwind-indicator"
|
||||||
import { ThemeProvider } from "@/components/theme-provider"
|
import { ThemeSwitcher } from "@/components/theme-switcher"
|
||||||
|
import { Toaster as DefaultToaster } from "@/registry/default/ui/toaster"
|
||||||
|
import { Toaster as NewYorkToaster } from "@/registry/new-york/ui/toaster"
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: {
|
title: {
|
||||||
@@ -82,7 +83,12 @@ export default function RootLayout({ children }: RootLayoutProps) {
|
|||||||
fontSans.variable
|
fontSans.variable
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
|
<ThemeProvider
|
||||||
|
attribute="class"
|
||||||
|
defaultTheme="system"
|
||||||
|
enableSystem
|
||||||
|
disableTransitionOnChange
|
||||||
|
>
|
||||||
<div className="relative flex min-h-screen flex-col">
|
<div className="relative flex min-h-screen flex-col">
|
||||||
<SiteHeader />
|
<SiteHeader />
|
||||||
<div className="flex-1">{children}</div>
|
<div className="flex-1">{children}</div>
|
||||||
@@ -90,9 +96,10 @@ export default function RootLayout({ children }: RootLayoutProps) {
|
|||||||
</div>
|
</div>
|
||||||
<TailwindIndicator />
|
<TailwindIndicator />
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
<StyleSwitcher />
|
<ThemeSwitcher />
|
||||||
<Analytics />
|
<Analytics />
|
||||||
<Toaster />
|
<NewYorkToaster />
|
||||||
|
<DefaultToaster />
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import Image from "next/image"
|
import Image from "next/image"
|
||||||
import Link from "next/link"
|
import Link from "next/link"
|
||||||
|
import { ArrowRightIcon } from "@radix-ui/react-icons"
|
||||||
|
|
||||||
import { siteConfig } from "@/config/site"
|
import { siteConfig } from "@/config/site"
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
import { buttonVariants } from "@/components/ui/button"
|
|
||||||
import { ExamplesNav } from "@/components/examples-nav"
|
import { ExamplesNav } from "@/components/examples-nav"
|
||||||
import { Icons } from "@/components/icons"
|
import { Icons } from "@/components/icons"
|
||||||
import {
|
import {
|
||||||
@@ -11,42 +11,45 @@ import {
|
|||||||
PageHeaderDescription,
|
PageHeaderDescription,
|
||||||
PageHeaderHeading,
|
PageHeaderHeading,
|
||||||
} from "@/components/page-header"
|
} from "@/components/page-header"
|
||||||
import { PromoVideo } from "@/components/promo-video"
|
import { buttonVariants } from "@/registry/new-york/ui/button"
|
||||||
import { StyleSwitcher } from "@/components/style-switcher"
|
import { Separator } from "@/registry/new-york/ui/separator"
|
||||||
import DashboardPage from "@/app/examples/dashboard/page"
|
import DashboardPage from "@/app/examples/dashboard/page"
|
||||||
|
|
||||||
export default function IndexPage() {
|
export default function IndexPage() {
|
||||||
return (
|
return (
|
||||||
<div className="container relative pb-10">
|
<div className="container relative">
|
||||||
<StyleSwitcher />
|
<PageHeader className="pb-8">
|
||||||
<PageHeader>
|
<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>
|
<PageHeaderHeading>Build your component library.</PageHeaderHeading>
|
||||||
<PageHeaderDescription>
|
<PageHeaderDescription>
|
||||||
Beautifully designed components that you can copy and paste into your
|
Beautifully designed components that you can copy and paste into your
|
||||||
apps. Accessible. Customizable. Open Source.
|
apps. Accessible. Customizable. Open Source.
|
||||||
</PageHeaderDescription>
|
</PageHeaderDescription>
|
||||||
</PageHeader>
|
<div className="flex w-full items-center space-x-4 pb-8 pt-4 md:pb-10">
|
||||||
<section className="pb-8 md:pb-10">
|
<Link href="/docs" className={cn(buttonVariants())}>
|
||||||
<div className="flex w-full items-center justify-between">
|
Get Started
|
||||||
<div className="flex space-x-4">
|
</Link>
|
||||||
<Link href="/docs" className={cn(buttonVariants({ size: "lg" }))}>
|
<Link
|
||||||
Get Started
|
target="_blank"
|
||||||
</Link>
|
rel="noreferrer"
|
||||||
<Link
|
href={siteConfig.links.github}
|
||||||
target="_blank"
|
className={cn(buttonVariants({ variant: "outline" }))}
|
||||||
rel="noreferrer"
|
>
|
||||||
href={siteConfig.links.github}
|
<Icons.gitHub className="mr-2 h-4 w-4" />
|
||||||
className={cn(
|
GitHub
|
||||||
buttonVariants({ variant: "outline", size: "lg" }),
|
</Link>
|
||||||
"pl-6"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<Icons.gitHub className="mr-2 h-4 w-4" />
|
|
||||||
GitHub
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</PageHeader>
|
||||||
<ExamplesNav className="[&>a:first-child]:text-primary" />
|
<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="space-y-8 overflow-hidden rounded-lg border-2 border-primary dark:border-muted md:hidden">
|
||||||
<Image
|
<Image
|
||||||
@@ -65,7 +68,7 @@ export default function IndexPage() {
|
|||||||
/>
|
/>
|
||||||
</section>
|
</section>
|
||||||
<section className="hidden md:block">
|
<section className="hidden md:block">
|
||||||
<div className="overflow-hidden rounded-lg border bg-background shadow-xl">
|
<div className="overflow-hidden rounded-lg border bg-background shadow">
|
||||||
<DashboardPage />
|
<DashboardPage />
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
27
apps/www/app/sink/layout.tsx
Normal file
27
apps/www/app/sink/layout.tsx
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import Link from "next/link"
|
||||||
|
|
||||||
|
import { ThemeWrapper } from "@/components/theme-wrapper"
|
||||||
|
import { styles } from "@/registry/styles"
|
||||||
|
|
||||||
|
interface SinkLayoutProps {
|
||||||
|
children: React.ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function SinkLayout({ children }: SinkLayoutProps) {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<div className="container">
|
||||||
|
<div className="flex space-x-2 px-2 py-4">
|
||||||
|
{styles.map((style) => (
|
||||||
|
<Link href={`/sink/${style.name}`} key={style.name}>
|
||||||
|
{style.label}
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex-1">
|
||||||
|
<ThemeWrapper>{children}</ThemeWrapper>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
206
apps/www/app/sink/new-york/page.tsx
Normal file
206
apps/www/app/sink/new-york/page.tsx
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
import Link from "next/link"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
import AccordionDemo from "@/registry/new-york/example/accordion-demo"
|
||||||
|
import AlertDialogDemo from "@/registry/new-york/example/alert-dialog-demo"
|
||||||
|
import AspectRatioDemo from "@/registry/new-york/example/aspect-ratio-demo"
|
||||||
|
import AvatarDemo from "@/registry/new-york/example/avatar-demo"
|
||||||
|
import BadgeDemo from "@/registry/new-york/example/badge-demo"
|
||||||
|
import BadgeDestructive from "@/registry/new-york/example/badge-destructive"
|
||||||
|
import BadgeOutline from "@/registry/new-york/example/badge-outline"
|
||||||
|
import BadgeSecondary from "@/registry/new-york/example/badge-secondary"
|
||||||
|
import ButtonDemo from "@/registry/new-york/example/button-demo"
|
||||||
|
import ButtonDestructive from "@/registry/new-york/example/button-destructive"
|
||||||
|
import ButtonGhost from "@/registry/new-york/example/button-ghost"
|
||||||
|
import ButtonLink from "@/registry/new-york/example/button-link"
|
||||||
|
import ButtonLoading from "@/registry/new-york/example/button-loading"
|
||||||
|
import ButtonOutline from "@/registry/new-york/example/button-outline"
|
||||||
|
import ButtonSecondary from "@/registry/new-york/example/button-secondary"
|
||||||
|
import ButtonWithIcon from "@/registry/new-york/example/button-with-icon"
|
||||||
|
import CardDemo from "@/registry/new-york/example/card-demo"
|
||||||
|
import CheckboxDemo from "@/registry/new-york/example/checkbox-demo"
|
||||||
|
import CollapsibleDemo from "@/registry/new-york/example/collapsible-demo"
|
||||||
|
import CommandDemo from "@/registry/new-york/example/command-demo"
|
||||||
|
import ContextMenuDemo from "@/registry/new-york/example/context-menu-demo"
|
||||||
|
import DatePickerDemo from "@/registry/new-york/example/date-picker-demo"
|
||||||
|
import DialogDemo from "@/registry/new-york/example/dialog-demo"
|
||||||
|
import DropdownMenuDemo from "@/registry/new-york/example/dropdown-menu-demo"
|
||||||
|
import HoverCardDemo from "@/registry/new-york/example/hover-card-demo"
|
||||||
|
import MenubarDemo from "@/registry/new-york/example/menubar-demo"
|
||||||
|
import NavigationMenuDemo from "@/registry/new-york/example/navigation-menu-demo"
|
||||||
|
import PopoverDemo from "@/registry/new-york/example/popover-demo"
|
||||||
|
import ProgressDemo from "@/registry/new-york/example/progress-demo"
|
||||||
|
import RadioGroupDemo from "@/registry/new-york/example/radio-group-demo"
|
||||||
|
import ScrollAreaDemo from "@/registry/new-york/example/scroll-area-demo"
|
||||||
|
import SelectDemo from "@/registry/new-york/example/select-demo"
|
||||||
|
import SeparatorDemo from "@/registry/new-york/example/separator-demo"
|
||||||
|
import SheetDemo from "@/registry/new-york/example/sheet-demo"
|
||||||
|
import SkeletonDemo from "@/registry/new-york/example/skeleton-demo"
|
||||||
|
import SliderDemo from "@/registry/new-york/example/slider-demo"
|
||||||
|
import SwitchDemo from "@/registry/new-york/example/switch-demo"
|
||||||
|
import TabsDemo from "@/registry/new-york/example/tabs-demo"
|
||||||
|
import ToastDemo from "@/registry/new-york/example/toast-demo"
|
||||||
|
import ToggleDemo from "@/registry/new-york/example/toggle-demo"
|
||||||
|
import ToggleDisabled from "@/registry/new-york/example/toggle-disabled"
|
||||||
|
import ToggleOutline from "@/registry/new-york/example/toggle-outline"
|
||||||
|
import ToggleWithText from "@/registry/new-york/example/toggle-with-text"
|
||||||
|
import TooltipDemo from "@/registry/new-york/example/tooltip-demo"
|
||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
|
|
||||||
|
export default function KitchenSinkPage() {
|
||||||
|
return (
|
||||||
|
<div className="container">
|
||||||
|
<div className="grid gap-4">
|
||||||
|
<div className="grid grid-cols-3 items-start gap-4">
|
||||||
|
<div className="grid gap-4">
|
||||||
|
<ComponentWrapper>
|
||||||
|
<CardDemo className="w-full" />
|
||||||
|
</ComponentWrapper>
|
||||||
|
<ComponentWrapper>
|
||||||
|
<SliderDemo className="w-full" />
|
||||||
|
</ComponentWrapper>
|
||||||
|
<ComponentWrapper
|
||||||
|
className="spa flex-col items-start space-x-0
|
||||||
|
space-y-2"
|
||||||
|
>
|
||||||
|
<p className="text-sm text-muted-foreground">Documentation</p>
|
||||||
|
<p className="text-sm font-medium leading-none">
|
||||||
|
You can customize the theme using{" "}
|
||||||
|
<code className="relative rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-sm font-semibold text-foreground">
|
||||||
|
CSS variables
|
||||||
|
</code>
|
||||||
|
.{" "}
|
||||||
|
<Link
|
||||||
|
href="#"
|
||||||
|
className="font-medium text-primary underline underline-offset-4"
|
||||||
|
>
|
||||||
|
Click here
|
||||||
|
</Link>{" "}
|
||||||
|
to learn more.
|
||||||
|
</p>
|
||||||
|
</ComponentWrapper>
|
||||||
|
<ComponentWrapper>
|
||||||
|
<CheckboxDemo />
|
||||||
|
<HoverCardDemo />
|
||||||
|
</ComponentWrapper>
|
||||||
|
<ComponentWrapper className="[&>div]:w-full">
|
||||||
|
<TabsDemo />
|
||||||
|
</ComponentWrapper>
|
||||||
|
</div>
|
||||||
|
<div className="grid gap-4">
|
||||||
|
<ComponentWrapper>
|
||||||
|
<MenubarDemo />
|
||||||
|
<AvatarDemo />
|
||||||
|
</ComponentWrapper>
|
||||||
|
<ComponentWrapper className="flex-col items-start space-x-0 space-y-2">
|
||||||
|
<div className="flex space-x-2">
|
||||||
|
<ButtonDemo />
|
||||||
|
<ButtonSecondary />
|
||||||
|
<ButtonDestructive />
|
||||||
|
</div>
|
||||||
|
<div className="flex space-x-2">
|
||||||
|
<ButtonOutline />
|
||||||
|
<ButtonLink />
|
||||||
|
<ButtonGhost />
|
||||||
|
</div>
|
||||||
|
<div className="flex space-x-2">
|
||||||
|
<ButtonWithIcon />
|
||||||
|
<ButtonLoading />
|
||||||
|
</div>
|
||||||
|
<div className="flex space-x-2">
|
||||||
|
<Button size="lg">Large</Button>
|
||||||
|
<Button size="sm">Small</Button>
|
||||||
|
</div>
|
||||||
|
</ComponentWrapper>
|
||||||
|
<ComponentWrapper>
|
||||||
|
<DatePickerDemo />
|
||||||
|
</ComponentWrapper>
|
||||||
|
<ComponentWrapper>
|
||||||
|
<AccordionDemo />
|
||||||
|
</ComponentWrapper>
|
||||||
|
<ComponentWrapper className="[&_ul>li:last-child]:hidden">
|
||||||
|
<NavigationMenuDemo />
|
||||||
|
</ComponentWrapper>
|
||||||
|
<ComponentWrapper className="justify-between">
|
||||||
|
<SwitchDemo />
|
||||||
|
<SelectDemo />
|
||||||
|
</ComponentWrapper>
|
||||||
|
<ComponentWrapper>
|
||||||
|
<SeparatorDemo />
|
||||||
|
</ComponentWrapper>
|
||||||
|
<ComponentWrapper>
|
||||||
|
<AspectRatioDemo />
|
||||||
|
</ComponentWrapper>
|
||||||
|
<ComponentWrapper>
|
||||||
|
<PopoverDemo />
|
||||||
|
<ToastDemo />
|
||||||
|
</ComponentWrapper>
|
||||||
|
</div>
|
||||||
|
<div className="grid gap-4">
|
||||||
|
<ComponentWrapper>
|
||||||
|
<TooltipDemo />
|
||||||
|
<SheetDemo />
|
||||||
|
<ProgressDemo />
|
||||||
|
</ComponentWrapper>
|
||||||
|
<ComponentWrapper>
|
||||||
|
<CommandDemo />
|
||||||
|
</ComponentWrapper>
|
||||||
|
<ComponentWrapper className="[&>span]:h-[80px] [&>span]:w-[200px]">
|
||||||
|
<RadioGroupDemo />
|
||||||
|
<ContextMenuDemo />
|
||||||
|
</ComponentWrapper>
|
||||||
|
<ComponentWrapper>
|
||||||
|
<div className="flex space-x-2">
|
||||||
|
<DropdownMenuDemo />
|
||||||
|
<AlertDialogDemo />
|
||||||
|
<DialogDemo />
|
||||||
|
</div>
|
||||||
|
</ComponentWrapper>
|
||||||
|
<ComponentWrapper>
|
||||||
|
<div className="flex space-x-2">
|
||||||
|
<BadgeDemo />
|
||||||
|
<BadgeSecondary />
|
||||||
|
<BadgeDestructive />
|
||||||
|
<BadgeOutline />
|
||||||
|
</div>
|
||||||
|
</ComponentWrapper>
|
||||||
|
<ComponentWrapper>
|
||||||
|
<SkeletonDemo />
|
||||||
|
</ComponentWrapper>
|
||||||
|
<ComponentWrapper className="[&>div]:w-full">
|
||||||
|
<CollapsibleDemo />
|
||||||
|
</ComponentWrapper>
|
||||||
|
<ComponentWrapper>
|
||||||
|
<div className="flex space-x-2">
|
||||||
|
<ToggleDemo />
|
||||||
|
<ToggleOutline />
|
||||||
|
<ToggleDisabled />
|
||||||
|
<ToggleWithText />
|
||||||
|
</div>
|
||||||
|
</ComponentWrapper>
|
||||||
|
<ComponentWrapper>
|
||||||
|
<ScrollAreaDemo />
|
||||||
|
</ComponentWrapper>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function ComponentWrapper({
|
||||||
|
className,
|
||||||
|
children,
|
||||||
|
}: React.HTMLAttributes<HTMLDivElement>) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"flex items-center justify-between space-x-4 rounded-md p-4",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -2,55 +2,56 @@ import * as React from "react"
|
|||||||
import Link from "next/link"
|
import Link from "next/link"
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
import { AccordionDemo } from "@/components/examples/accordion/demo"
|
import AccordionDemo from "@/registry/default/example/accordion-demo"
|
||||||
import { AlertDialogDemo } from "@/components/examples/alert-dialog/demo"
|
import AlertDialogDemo from "@/registry/default/example/alert-dialog-demo"
|
||||||
import { AspectRatioDemo } from "@/components/examples/aspect-ratio/demo"
|
import AspectRatioDemo from "@/registry/default/example/aspect-ratio-demo"
|
||||||
import { AvatarDemo } from "@/components/examples/avatar/demo"
|
import AvatarDemo from "@/registry/default/example/avatar-demo"
|
||||||
import { BadgeDemo } from "@/components/examples/badge/demo"
|
import BadgeDemo from "@/registry/default/example/badge-demo"
|
||||||
import { BadgeDestructive } from "@/components/examples/badge/destructive"
|
import BadgeDestructive from "@/registry/default/example/badge-destructive"
|
||||||
import { BadgeOutline } from "@/components/examples/badge/outline"
|
import BadgeOutline from "@/registry/default/example/badge-outline"
|
||||||
import { BadgeSecondary } from "@/components/examples/badge/secondary"
|
import BadgeSecondary from "@/registry/default/example/badge-secondary"
|
||||||
import { ButtonDemo } from "@/components/examples/button/demo"
|
import ButtonDemo from "@/registry/default/example/button-demo"
|
||||||
import { ButtonDestructive } from "@/components/examples/button/destructive"
|
import ButtonDestructive from "@/registry/default/example/button-destructive"
|
||||||
import { ButtonGhost } from "@/components/examples/button/ghost"
|
import ButtonGhost from "@/registry/default/example/button-ghost"
|
||||||
import { ButtonLink } from "@/components/examples/button/link"
|
import ButtonLink from "@/registry/default/example/button-link"
|
||||||
import { ButtonLoading } from "@/components/examples/button/loading"
|
import ButtonLoading from "@/registry/default/example/button-loading"
|
||||||
import { ButtonOutline } from "@/components/examples/button/outline"
|
import ButtonOutline from "@/registry/default/example/button-outline"
|
||||||
import { ButtonSecondary } from "@/components/examples/button/secondary"
|
import ButtonSecondary from "@/registry/default/example/button-secondary"
|
||||||
import { ButtonWithIcon } from "@/components/examples/button/with-icon"
|
import ButtonWithIcon from "@/registry/default/example/button-with-icon"
|
||||||
import { CalendarDatePicker } from "@/components/examples/calendar/date-picker"
|
import CardDemo from "@/registry/default/example/card-demo"
|
||||||
import { CardDemo } from "@/components/examples/card/demo"
|
import CheckboxDemo from "@/registry/default/example/checkbox-demo"
|
||||||
import { CheckboxDemo } from "@/components/examples/checkbox/demo"
|
import CollapsibleDemo from "@/registry/default/example/collapsible-demo"
|
||||||
import { CollapsibleDemo } from "@/components/examples/collapsible/demo"
|
import CommandDemo from "@/registry/default/example/command-demo"
|
||||||
import { CommandDemo } from "@/components/examples/command/demo"
|
import ContextMenuDemo from "@/registry/default/example/context-menu-demo"
|
||||||
import { ContextMenuDemo } from "@/components/examples/context-menu/demo"
|
import DatePickerDemo from "@/registry/default/example/date-picker-demo"
|
||||||
import { DialogDemo } from "@/components/examples/dialog/demo"
|
import DialogDemo from "@/registry/default/example/dialog-demo"
|
||||||
import { DropdownMenuDemo } from "@/components/examples/dropdown-menu/demo"
|
import DropdownMenuDemo from "@/registry/default/example/dropdown-menu-demo"
|
||||||
import { HoverCardDemo } from "@/components/examples/hover-card/demo"
|
import HoverCardDemo from "@/registry/default/example/hover-card-demo"
|
||||||
import { MenubarDemo } from "@/components/examples/menubar/demo"
|
import MenubarDemo from "@/registry/default/example/menubar-demo"
|
||||||
import { NavigationMenuDemo } from "@/components/examples/navigation-menu/demo"
|
import NavigationMenuDemo from "@/registry/default/example/navigation-menu-demo"
|
||||||
import { PopoverDemo } from "@/components/examples/popover/demo"
|
import PopoverDemo from "@/registry/default/example/popover-demo"
|
||||||
import { ProgressDemo } from "@/components/examples/progress/demo"
|
import ProgressDemo from "@/registry/default/example/progress-demo"
|
||||||
import { RadioGroupDemo } from "@/components/examples/radio-group/demo"
|
import RadioGroupDemo from "@/registry/default/example/radio-group-demo"
|
||||||
import { ScrollAreaDemo } from "@/components/examples/scroll-area/demo"
|
import ScrollAreaDemo from "@/registry/default/example/scroll-area-demo"
|
||||||
import { SelectDemo } from "@/components/examples/select/demo"
|
import SelectDemo from "@/registry/default/example/select-demo"
|
||||||
import { SeparatorDemo } from "@/components/examples/separator/demo"
|
import SeparatorDemo from "@/registry/default/example/separator-demo"
|
||||||
import { SheetDemo } from "@/components/examples/sheet/demo"
|
import SheetDemo from "@/registry/default/example/sheet-demo"
|
||||||
import { SkeletonDemo } from "@/components/examples/skeleton/demo"
|
import SkeletonDemo from "@/registry/default/example/skeleton-demo"
|
||||||
import { SliderDemo } from "@/components/examples/slider/demo"
|
import SliderDemo from "@/registry/default/example/slider-demo"
|
||||||
import { SwitchDemo } from "@/components/examples/switch/demo"
|
import SwitchDemo from "@/registry/default/example/switch-demo"
|
||||||
import { TabsDemo } from "@/components/examples/tabs/demo"
|
import TabsDemo from "@/registry/default/example/tabs-demo"
|
||||||
import { ToastDemo } from "@/components/examples/toast/demo"
|
import ToastDemo from "@/registry/default/example/toast-demo"
|
||||||
import { ToggleDemo } from "@/components/examples/toggle/demo"
|
import ToggleDemo from "@/registry/default/example/toggle-demo"
|
||||||
import { ToggleDisabled } from "@/components/examples/toggle/disabled"
|
import ToggleDisabled from "@/registry/default/example/toggle-disabled"
|
||||||
import { ToggleOutline } from "@/components/examples/toggle/outline"
|
import ToggleOutline from "@/registry/default/example/toggle-outline"
|
||||||
import { ToggleWithText } from "@/components/examples/toggle/with-text"
|
import ToggleWithText from "@/registry/default/example/toggle-with-text"
|
||||||
import { TooltipDemo } from "@/components/examples/tooltip/demo"
|
import TooltipDemo from "@/registry/default/example/tooltip-demo"
|
||||||
|
import { Button } from "@/registry/default/ui/button"
|
||||||
|
|
||||||
export default function KitchenSinkPage() {
|
export default function KitchenSinkPage() {
|
||||||
return (
|
return (
|
||||||
<div className="container">
|
<div className="container">
|
||||||
<div className="grid gap-4 py-10">
|
<div className="grid gap-4">
|
||||||
<div className="grid grid-cols-3 items-start gap-4">
|
<div className="grid grid-cols-3 items-start gap-4">
|
||||||
<div className="grid gap-4">
|
<div className="grid gap-4">
|
||||||
<ComponentWrapper>
|
<ComponentWrapper>
|
||||||
@@ -63,7 +64,7 @@ export default function KitchenSinkPage() {
|
|||||||
className="spa flex-col items-start space-x-0
|
className="spa flex-col items-start space-x-0
|
||||||
space-y-2"
|
space-y-2"
|
||||||
>
|
>
|
||||||
<p className="text-foreground-muted text-sm">Documentation</p>
|
<p className="text-sm text-muted-foreground">Documentation</p>
|
||||||
<p className="text-sm font-medium leading-none">
|
<p className="text-sm font-medium leading-none">
|
||||||
You can customize the theme using{" "}
|
You can customize the theme using{" "}
|
||||||
<code className="relative rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-sm font-semibold text-foreground">
|
<code className="relative rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-sm font-semibold text-foreground">
|
||||||
@@ -107,9 +108,13 @@ export default function KitchenSinkPage() {
|
|||||||
<ButtonWithIcon />
|
<ButtonWithIcon />
|
||||||
<ButtonLoading />
|
<ButtonLoading />
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex space-x-2">
|
||||||
|
<Button size="lg">Large</Button>
|
||||||
|
<Button size="sm">Small</Button>
|
||||||
|
</div>
|
||||||
</ComponentWrapper>
|
</ComponentWrapper>
|
||||||
<ComponentWrapper>
|
<ComponentWrapper>
|
||||||
<CalendarDatePicker />
|
<DatePickerDemo />
|
||||||
</ComponentWrapper>
|
</ComponentWrapper>
|
||||||
<ComponentWrapper>
|
<ComponentWrapper>
|
||||||
<AccordionDemo />
|
<AccordionDemo />
|
||||||
@@ -191,7 +196,7 @@ function ComponentWrapper({
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex items-center justify-between space-x-4 rounded-md border p-4",
|
"flex items-center justify-between space-x-4 rounded-md p-4",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|||||||
38
apps/www/app/themes/page.tsx
Normal file
38
apps/www/app/themes/page.tsx
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import { Metadata } from "next"
|
||||||
|
|
||||||
|
import "public/registry/themes.css"
|
||||||
|
import {
|
||||||
|
PageHeader,
|
||||||
|
PageHeaderDescription,
|
||||||
|
PageHeaderHeading,
|
||||||
|
} from "@/components/page-header"
|
||||||
|
import { ThemeCustomizer } from "@/components/theme-customizer"
|
||||||
|
import { ThemeWrapper } from "@/components/theme-wrapper"
|
||||||
|
import { ThemesTabs } from "@/app/themes/tabs"
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "Themes",
|
||||||
|
description: "Hand-picked themes that you can copy and paste into your apps.",
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function ThemesPage() {
|
||||||
|
return (
|
||||||
|
<div className="container">
|
||||||
|
<ThemeWrapper
|
||||||
|
defaultTheme="zinc"
|
||||||
|
className="relative flex flex-col items-start md:flex-row md:items-center"
|
||||||
|
>
|
||||||
|
<PageHeader className="relative pb-4 md:pb-8 lg:pb-12">
|
||||||
|
<PageHeaderHeading>Make it yours.</PageHeaderHeading>
|
||||||
|
<PageHeaderDescription>
|
||||||
|
Hand-picked themes that you can copy and paste into your apps.
|
||||||
|
</PageHeaderDescription>
|
||||||
|
</PageHeader>
|
||||||
|
<div className="px-4 pb-8 md:ml-auto md:pb-0">
|
||||||
|
<ThemeCustomizer />
|
||||||
|
</div>
|
||||||
|
</ThemeWrapper>
|
||||||
|
<ThemesTabs />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
73
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>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
import { cn } from "@/lib/utils"
|
import {
|
||||||
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"
|
Alert,
|
||||||
|
AlertDescription,
|
||||||
|
AlertTitle,
|
||||||
|
} from "@/registry/new-york/ui/alert"
|
||||||
|
|
||||||
interface CalloutProps {
|
interface CalloutProps {
|
||||||
icon?: string
|
icon?: string
|
||||||
|
|||||||
@@ -3,12 +3,12 @@
|
|||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
import {
|
import {
|
||||||
Collapsible,
|
Collapsible,
|
||||||
CollapsibleContent,
|
CollapsibleContent,
|
||||||
CollapsibleTrigger,
|
CollapsibleTrigger,
|
||||||
} from "@/components/ui/collapsible"
|
} from "@/registry/new-york/ui/collapsible"
|
||||||
|
|
||||||
interface CodeBlockProps extends React.HTMLAttributes<HTMLDivElement> {
|
interface CodeBlockProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||||
expandButtonTitle?: string
|
expandButtonTitle?: string
|
||||||
@@ -31,7 +31,7 @@ export function CodeBlockWrapper({
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"[&_pre]:max-h-[650px [&_pre]:my-0 [&_pre]:pb-[100px]",
|
"[&_pre]:my-0 [&_pre]:max-h-[650px] [&_pre]:pb-[100px]",
|
||||||
!isOpened ? "[&_pre]:overflow-hidden" : "[&_pre]:overflow-auto]"
|
!isOpened ? "[&_pre]:overflow-hidden" : "[&_pre]:overflow-auto]"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
@@ -40,7 +40,7 @@ export function CodeBlockWrapper({
|
|||||||
</CollapsibleContent>
|
</CollapsibleContent>
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"absolute flex items-center justify-center bg-gradient-to-b from-background/30 to-muted/90 p-2",
|
"absolute flex items-center justify-center bg-gradient-to-b from-zinc-700/30 to-zinc-950/90 p-2",
|
||||||
isOpened ? "inset-x-0 bottom-0 h-12" : "inset-0"
|
isOpened ? "inset-x-0 bottom-0 h-12" : "inset-0"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -3,13 +3,18 @@
|
|||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import { useRouter } from "next/navigation"
|
import { useRouter } from "next/navigation"
|
||||||
import { DialogProps } from "@radix-ui/react-alert-dialog"
|
import { DialogProps } from "@radix-ui/react-alert-dialog"
|
||||||
import { allDocs } from "contentlayer/generated"
|
import {
|
||||||
import { Circle, File, Laptop, Moon, SunMedium } from "lucide-react"
|
CircleIcon,
|
||||||
|
FileIcon,
|
||||||
|
LaptopIcon,
|
||||||
|
MoonIcon,
|
||||||
|
SunIcon,
|
||||||
|
} from "@radix-ui/react-icons"
|
||||||
import { useTheme } from "next-themes"
|
import { useTheme } from "next-themes"
|
||||||
|
|
||||||
import { docsConfig } from "@/config/docs"
|
import { docsConfig } from "@/config/docs"
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
import {
|
import {
|
||||||
CommandDialog,
|
CommandDialog,
|
||||||
CommandEmpty,
|
CommandEmpty,
|
||||||
@@ -18,7 +23,7 @@ import {
|
|||||||
CommandItem,
|
CommandItem,
|
||||||
CommandList,
|
CommandList,
|
||||||
CommandSeparator,
|
CommandSeparator,
|
||||||
} from "@/components/ui/command"
|
} from "@/registry/new-york/ui/command"
|
||||||
|
|
||||||
export function CommandMenu({ ...props }: DialogProps) {
|
export function CommandMenu({ ...props }: DialogProps) {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@@ -47,14 +52,14 @@ export function CommandMenu({ ...props }: DialogProps) {
|
|||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className={cn(
|
className={cn(
|
||||||
"relative h-9 w-full justify-start rounded-[0.5rem] text-sm text-muted-foreground sm:pr-12 md:w-40 lg:w-64"
|
"relative w-full justify-start text-sm text-muted-foreground sm:pr-12 md:w-40 lg:w-64"
|
||||||
)}
|
)}
|
||||||
onClick={() => setOpen(true)}
|
onClick={() => setOpen(true)}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<span className="hidden lg:inline-flex">Search documentation...</span>
|
<span className="hidden lg:inline-flex">Search documentation...</span>
|
||||||
<span className="inline-flex lg:hidden">Search...</span>
|
<span className="inline-flex lg:hidden">Search...</span>
|
||||||
<kbd className="pointer-events-none absolute right-1.5 top-2 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-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">
|
||||||
<span className="text-xs">⌘</span>K
|
<span className="text-xs">⌘</span>K
|
||||||
</kbd>
|
</kbd>
|
||||||
</Button>
|
</Button>
|
||||||
@@ -68,11 +73,12 @@ export function CommandMenu({ ...props }: DialogProps) {
|
|||||||
.map((navItem) => (
|
.map((navItem) => (
|
||||||
<CommandItem
|
<CommandItem
|
||||||
key={navItem.href}
|
key={navItem.href}
|
||||||
|
value={navItem.title}
|
||||||
onSelect={() => {
|
onSelect={() => {
|
||||||
runCommand(() => router.push(navItem.href as string))
|
runCommand(() => router.push(navItem.href as string))
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<File className="mr-2 h-4 w-4" />
|
<FileIcon className="mr-2 h-4 w-4" />
|
||||||
{navItem.title}
|
{navItem.title}
|
||||||
</CommandItem>
|
</CommandItem>
|
||||||
))}
|
))}
|
||||||
@@ -82,12 +88,13 @@ export function CommandMenu({ ...props }: DialogProps) {
|
|||||||
{group.items.map((navItem) => (
|
{group.items.map((navItem) => (
|
||||||
<CommandItem
|
<CommandItem
|
||||||
key={navItem.href}
|
key={navItem.href}
|
||||||
|
value={navItem.title}
|
||||||
onSelect={() => {
|
onSelect={() => {
|
||||||
runCommand(() => router.push(navItem.href as string))
|
runCommand(() => router.push(navItem.href as string))
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="mr-2 flex h-4 w-4 items-center justify-center">
|
<div className="mr-2 flex h-4 w-4 items-center justify-center">
|
||||||
<Circle className="h-3 w-3" />
|
<CircleIcon className="h-3 w-3" />
|
||||||
</div>
|
</div>
|
||||||
{navItem.title}
|
{navItem.title}
|
||||||
</CommandItem>
|
</CommandItem>
|
||||||
@@ -97,15 +104,15 @@ export function CommandMenu({ ...props }: DialogProps) {
|
|||||||
<CommandSeparator />
|
<CommandSeparator />
|
||||||
<CommandGroup heading="Theme">
|
<CommandGroup heading="Theme">
|
||||||
<CommandItem onSelect={() => runCommand(() => setTheme("light"))}>
|
<CommandItem onSelect={() => runCommand(() => setTheme("light"))}>
|
||||||
<SunMedium className="mr-2 h-4 w-4" />
|
<SunIcon className="mr-2 h-4 w-4" />
|
||||||
Light
|
Light
|
||||||
</CommandItem>
|
</CommandItem>
|
||||||
<CommandItem onSelect={() => runCommand(() => setTheme("dark"))}>
|
<CommandItem onSelect={() => runCommand(() => setTheme("dark"))}>
|
||||||
<Moon className="mr-2 h-4 w-4" />
|
<MoonIcon className="mr-2 h-4 w-4" />
|
||||||
Dark
|
Dark
|
||||||
</CommandItem>
|
</CommandItem>
|
||||||
<CommandItem onSelect={() => runCommand(() => setTheme("system"))}>
|
<CommandItem onSelect={() => runCommand(() => setTheme("system"))}>
|
||||||
<Laptop className="mr-2 h-4 w-4" />
|
<LaptopIcon className="mr-2 h-4 w-4" />
|
||||||
System
|
System
|
||||||
</CommandItem>
|
</CommandItem>
|
||||||
</CommandGroup>
|
</CommandGroup>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React from "react"
|
import React from "react"
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
import { AspectRatio } from "@/components/ui/aspect-ratio"
|
import { AspectRatio } from "@/registry/new-york/ui/aspect-ratio"
|
||||||
|
|
||||||
export function ComponentCard({
|
export function ComponentCard({
|
||||||
className,
|
className,
|
||||||
|
|||||||
@@ -3,8 +3,13 @@
|
|||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
|
|
||||||
import { CopyButton, CopyWithClassNames } from "@/components/copy-button"
|
import { CopyButton, CopyWithClassNames } from "@/components/copy-button"
|
||||||
|
import {
|
||||||
|
Tabs,
|
||||||
|
TabsContent,
|
||||||
|
TabsList,
|
||||||
|
TabsTrigger,
|
||||||
|
} from "@/registry/new-york/ui/tabs"
|
||||||
|
|
||||||
interface ComponentExampleProps extends React.HTMLAttributes<HTMLDivElement> {
|
interface ComponentExampleProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||||
extractClassname?: boolean
|
extractClassname?: boolean
|
||||||
@@ -42,7 +47,7 @@ export function ComponentExample({
|
|||||||
className={cn("group relative my-4 flex flex-col space-y-2", className)}
|
className={cn("group relative my-4 flex flex-col space-y-2", className)}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<Tabs defaultValue="preview" className="mr-auto w-full">
|
<Tabs defaultValue="preview" className="relative mr-auto w-full">
|
||||||
<div className="flex items-center justify-between pb-3">
|
<div className="flex items-center justify-between pb-3">
|
||||||
<TabsList className="w-full justify-start rounded-none border-b bg-transparent p-0">
|
<TabsList className="w-full justify-start rounded-none border-b bg-transparent p-0">
|
||||||
<TabsTrigger
|
<TabsTrigger
|
||||||
@@ -62,9 +67,15 @@ export function ComponentExample({
|
|||||||
<CopyWithClassNames
|
<CopyWithClassNames
|
||||||
value={codeString}
|
value={codeString}
|
||||||
classNames={extractedClassNames}
|
classNames={extractedClassNames}
|
||||||
|
className="absolute right-4 top-20"
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
codeString && <CopyButton value={codeString} />
|
codeString && (
|
||||||
|
<CopyButton
|
||||||
|
value={codeString}
|
||||||
|
className="absolute right-4 top-20"
|
||||||
|
/>
|
||||||
|
)
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<TabsContent value="preview" className="rounded-md border">
|
<TabsContent value="preview" className="rounded-md border">
|
||||||
|
|||||||
139
apps/www/components/component-preview.tsx
Normal file
139
apps/www/components/component-preview.tsx
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
|
import { Index } from "@/__registry__"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
import { useConfig } from "@/hooks/use-config"
|
||||||
|
import { CopyButton, CopyWithClassNames } from "@/components/copy-button"
|
||||||
|
import { Icons } from "@/components/icons"
|
||||||
|
import { StyleSwitcher } from "@/components/style-switcher"
|
||||||
|
import { ThemeWrapper } from "@/components/theme-wrapper"
|
||||||
|
import {
|
||||||
|
Tabs,
|
||||||
|
TabsContent,
|
||||||
|
TabsList,
|
||||||
|
TabsTrigger,
|
||||||
|
} from "@/registry/new-york/ui/tabs"
|
||||||
|
import { styles } from "@/registry/styles"
|
||||||
|
|
||||||
|
interface ComponentPreviewProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||||
|
name: string
|
||||||
|
extractClassname?: boolean
|
||||||
|
extractedClassNames?: string
|
||||||
|
align?: "center" | "start" | "end"
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ComponentPreview({
|
||||||
|
name,
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
extractClassname,
|
||||||
|
extractedClassNames,
|
||||||
|
align = "center",
|
||||||
|
...props
|
||||||
|
}: ComponentPreviewProps) {
|
||||||
|
const [config] = useConfig()
|
||||||
|
const index = styles.findIndex((style) => style.name === config.style)
|
||||||
|
|
||||||
|
const Codes = React.Children.toArray(children) as React.ReactElement[]
|
||||||
|
const Code = Codes[index]
|
||||||
|
|
||||||
|
const 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])
|
||||||
|
|
||||||
|
const codeString = React.useMemo(() => {
|
||||||
|
if (
|
||||||
|
typeof Code?.props["data-rehype-pretty-code-fragment"] !== "undefined"
|
||||||
|
) {
|
||||||
|
const [, Button] = React.Children.toArray(
|
||||||
|
Code.props.children
|
||||||
|
) as React.ReactElement[]
|
||||||
|
return Button?.props?.value || Button?.props?.__rawString__ || null
|
||||||
|
}
|
||||||
|
}, [Code])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cn("group relative my-4 flex flex-col space-y-2", className)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<Tabs defaultValue="preview" className="relative mr-auto w-full">
|
||||||
|
<div className="flex items-center justify-between pb-3">
|
||||||
|
<TabsList className="w-full justify-start rounded-none border-b bg-transparent p-0">
|
||||||
|
<TabsTrigger
|
||||||
|
value="preview"
|
||||||
|
className="relative h-9 rounded-none border-b-2 border-b-transparent bg-transparent px-4 pb-3 pt-2 font-semibold text-muted-foreground shadow-none transition-none data-[state=active]:border-b-primary data-[state=active]:text-foreground data-[state=active]:shadow-none"
|
||||||
|
>
|
||||||
|
Preview
|
||||||
|
</TabsTrigger>
|
||||||
|
<TabsTrigger
|
||||||
|
value="code"
|
||||||
|
className="relative h-9 rounded-none border-b-2 border-b-transparent bg-transparent px-4 pb-3 pt-2 font-semibold text-muted-foreground shadow-none transition-none data-[state=active]:border-b-primary data-[state=active]:text-foreground data-[state=active]:shadow-none"
|
||||||
|
>
|
||||||
|
Code
|
||||||
|
</TabsTrigger>
|
||||||
|
</TabsList>
|
||||||
|
</div>
|
||||||
|
<TabsContent value="preview" className="relative rounded-md border">
|
||||||
|
<div className="flex items-center justify-between p-4">
|
||||||
|
<StyleSwitcher />
|
||||||
|
{extractedClassNames ? (
|
||||||
|
<CopyWithClassNames
|
||||||
|
value={codeString}
|
||||||
|
classNames={extractedClassNames}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
codeString && <CopyButton value={codeString} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<ThemeWrapper defaultTheme="zinc">
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"preview flex min-h-[350px] w-full justify-center p-10",
|
||||||
|
{
|
||||||
|
"items-center": align === "center",
|
||||||
|
"items-start": align === "start",
|
||||||
|
"items-end": align === "end",
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<React.Suspense
|
||||||
|
fallback={
|
||||||
|
<div className="flex items-center text-sm text-muted-foreground">
|
||||||
|
<Icons.spinner className="mr-2 h-4 w-4 animate-spin" />
|
||||||
|
Loading...
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{Preview}
|
||||||
|
</React.Suspense>
|
||||||
|
</div>
|
||||||
|
</ThemeWrapper>
|
||||||
|
</TabsContent>
|
||||||
|
<TabsContent value="code">
|
||||||
|
<div className="flex flex-col space-y-4">
|
||||||
|
<div className="w-full rounded-md [&_pre]:my-0 [&_pre]:max-h-[350px] [&_pre]:overflow-auto">
|
||||||
|
{Code}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</TabsContent>
|
||||||
|
</Tabs>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -9,10 +9,14 @@ interface ComponentSourceProps extends React.HTMLAttributes<HTMLDivElement> {
|
|||||||
src: string
|
src: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ComponentSource({ children, className }: ComponentSourceProps) {
|
export function ComponentSource({
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: ComponentSourceProps) {
|
||||||
return (
|
return (
|
||||||
<CodeBlockWrapper
|
<CodeBlockWrapper
|
||||||
expandButtonTitle="View Primitive"
|
expandButtonTitle="Expand"
|
||||||
className={cn("my-6 overflow-hidden rounded-md", className)}
|
className={cn("my-6 overflow-hidden rounded-md", className)}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -2,17 +2,18 @@
|
|||||||
|
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import { DropdownMenuTriggerProps } from "@radix-ui/react-dropdown-menu"
|
import { DropdownMenuTriggerProps } from "@radix-ui/react-dropdown-menu"
|
||||||
|
import { CheckIcon, CopyIcon } from "@radix-ui/react-icons"
|
||||||
import { NpmCommands } from "types/unist"
|
import { NpmCommands } from "types/unist"
|
||||||
|
|
||||||
import { Event, trackEvent } from "@/lib/events"
|
import { Event, trackEvent } from "@/lib/events"
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
DropdownMenuItem,
|
DropdownMenuItem,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu"
|
} from "@/registry/new-york/ui/dropdown-menu"
|
||||||
import { Icons } from "@/components/icons"
|
|
||||||
|
|
||||||
interface CopyButtonProps extends React.HTMLAttributes<HTMLButtonElement> {
|
interface CopyButtonProps extends React.HTMLAttributes<HTMLButtonElement> {
|
||||||
value: string
|
value: string
|
||||||
@@ -20,7 +21,7 @@ interface CopyButtonProps extends React.HTMLAttributes<HTMLButtonElement> {
|
|||||||
event?: Event["name"]
|
event?: Event["name"]
|
||||||
}
|
}
|
||||||
|
|
||||||
async function copyToClipboardWithMeta(value: string, event?: Event) {
|
export async function copyToClipboardWithMeta(value: string, event?: Event) {
|
||||||
navigator.clipboard.writeText(value)
|
navigator.clipboard.writeText(value)
|
||||||
if (event) {
|
if (event) {
|
||||||
trackEvent(event)
|
trackEvent(event)
|
||||||
@@ -43,9 +44,11 @@ export function CopyButton({
|
|||||||
}, [hasCopied])
|
}, [hasCopied])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<Button
|
||||||
|
size="icon"
|
||||||
|
variant="ghost"
|
||||||
className={cn(
|
className={cn(
|
||||||
"relative z-20 inline-flex h-6 w-6 items-center justify-center rounded-md border bg-background text-sm font-medium transition-all hover:bg-muted focus:outline-none",
|
"relative z-10 h-6 w-6 text-zinc-50 hover:bg-zinc-700 hover:text-zinc-50",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -66,11 +69,11 @@ export function CopyButton({
|
|||||||
>
|
>
|
||||||
<span className="sr-only">Copy</span>
|
<span className="sr-only">Copy</span>
|
||||||
{hasCopied ? (
|
{hasCopied ? (
|
||||||
<Icons.check className="h-3 w-3" />
|
<CheckIcon className="h-3 w-3" />
|
||||||
) : (
|
) : (
|
||||||
<Icons.copy className="h-3 w-3" />
|
<CopyIcon className="h-3 w-3" />
|
||||||
)}
|
)}
|
||||||
</button>
|
</Button>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,28 +104,29 @@ export function CopyWithClassNames({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger
|
<DropdownMenuTrigger asChild>
|
||||||
className={cn(
|
<Button
|
||||||
"relative z-20 inline-flex h-6 w-6 items-center justify-center rounded-md border bg-background text-sm font-medium transition-all hover:bg-muted focus:outline-none",
|
size="icon"
|
||||||
className
|
variant="ghost"
|
||||||
)}
|
className={cn(
|
||||||
{...props}
|
"relative z-10 h-6 w-6 text-zinc-50 hover:bg-zinc-700 hover:text-zinc-50",
|
||||||
>
|
className
|
||||||
{hasCopied ? (
|
)}
|
||||||
<Icons.check className="h-3 w-3" />
|
>
|
||||||
) : (
|
{hasCopied ? (
|
||||||
<Icons.copy className="h-3 w-3" />
|
<CheckIcon className="h-3 w-3" />
|
||||||
)}
|
) : (
|
||||||
<span className="sr-only">Copy</span>
|
<CopyIcon className="h-3 w-3" />
|
||||||
|
)}
|
||||||
|
<span className="sr-only">Copy</span>
|
||||||
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent>
|
<DropdownMenuContent align="end">
|
||||||
<DropdownMenuItem onClick={() => copyToClipboard(value)}>
|
<DropdownMenuItem onClick={() => copyToClipboard(value)}>
|
||||||
<Icons.react className="mr-2 h-4 w-4" />
|
Component
|
||||||
<span>Component</span>
|
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem onClick={() => copyToClipboard(classNames)}>
|
<DropdownMenuItem onClick={() => copyToClipboard(classNames)}>
|
||||||
<Icons.tailwind className="mr-2 h-4 w-4" />
|
Classname
|
||||||
<span>Classname</span>
|
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
@@ -162,38 +166,38 @@ export function CopyNpmCommandButton({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger
|
<DropdownMenuTrigger asChild>
|
||||||
className={cn(
|
<Button
|
||||||
"relative z-20 inline-flex h-6 w-6 items-center justify-center rounded-md border bg-background text-sm font-medium transition-all hover:bg-muted focus:outline-none",
|
size="icon"
|
||||||
className
|
variant="ghost"
|
||||||
)}
|
className={cn(
|
||||||
{...props}
|
"relative z-10 h-6 w-6 text-zinc-50 hover:bg-zinc-700 hover:text-zinc-50",
|
||||||
>
|
className
|
||||||
{hasCopied ? (
|
)}
|
||||||
<Icons.check className="h-3 w-3" />
|
>
|
||||||
) : (
|
{hasCopied ? (
|
||||||
<Icons.copy className="h-3 w-3" />
|
<CheckIcon className="h-3 w-3" />
|
||||||
)}
|
) : (
|
||||||
<span className="sr-only">Copy</span>
|
<CopyIcon className="h-3 w-3" />
|
||||||
|
)}
|
||||||
|
<span className="sr-only">Copy</span>
|
||||||
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent align="end">
|
<DropdownMenuContent align="end">
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
onClick={() => copyCommand(commands.__npmCommand__, "npm")}
|
onClick={() => copyCommand(commands.__npmCommand__, "npm")}
|
||||||
>
|
>
|
||||||
<Icons.npm className="mr-2 h-4 w-4" />
|
npm
|
||||||
<span>npm</span>
|
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
onClick={() => copyCommand(commands.__yarnCommand__, "yarn")}
|
onClick={() => copyCommand(commands.__yarnCommand__, "yarn")}
|
||||||
>
|
>
|
||||||
<Icons.yarn className="mr-2 h-4 w-4" />
|
yarn
|
||||||
<span>yarn</span>
|
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
onClick={() => copyCommand(commands.__pnpmCommand__, "pnpm")}
|
onClick={() => copyCommand(commands.__pnpmCommand__, "pnpm")}
|
||||||
>
|
>
|
||||||
<Icons.pnpm className="mr-2 h-4 w-4" />
|
pnpm
|
||||||
<span>pnpm</span>
|
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</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-zinc-950/60" />
|
||||||
|
<DrawerPrimitive.Content
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"fixed inset-x-0 bottom-0 z-50 mt-24 h-[96%] rounded-t-[10px] bg-background",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<div className="absolute left-1/2 top-3 h-2 w-[100px] translate-x-[-50%] rounded-full bg-muted" />
|
||||||
|
{children}
|
||||||
|
</DrawerPrimitive.Content>
|
||||||
|
</DrawerPrimitive.Portal>
|
||||||
|
))
|
||||||
|
DrawerContent.displayName = "DrawerContent"
|
||||||
|
|
||||||
|
export { DrawerTrigger, DrawerContent }
|
||||||
@@ -2,30 +2,46 @@
|
|||||||
|
|
||||||
import Link from "next/link"
|
import Link from "next/link"
|
||||||
import { usePathname } from "next/navigation"
|
import { usePathname } from "next/navigation"
|
||||||
|
import { ArrowRightIcon } from "@radix-ui/react-icons"
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area"
|
import { ScrollArea, ScrollBar } from "@/registry/new-york/ui/scroll-area"
|
||||||
|
|
||||||
const examples = [
|
const examples = [
|
||||||
{
|
{
|
||||||
name: "Dashboard",
|
name: "Dashboard",
|
||||||
href: "/examples/dashboard",
|
href: "/examples/dashboard",
|
||||||
|
code: "https://github.com/shadcn/ui/tree/main/apps/www/app/examples/dashboard",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Cards",
|
name: "Cards",
|
||||||
href: "/examples/cards",
|
href: "/examples/cards",
|
||||||
|
code: "https://github.com/shadcn/ui/tree/main/apps/www/app/examples/cards",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Tasks",
|
||||||
|
href: "/examples/tasks",
|
||||||
|
code: "https://github.com/shadcn/ui/tree/main/apps/www/app/examples/tasks",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Playground",
|
name: "Playground",
|
||||||
href: "/examples/playground",
|
href: "/examples/playground",
|
||||||
|
code: "https://github.com/shadcn/ui/tree/main/apps/www/app/examples/playground",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Forms",
|
||||||
|
href: "/examples/forms",
|
||||||
|
code: "https://github.com/shadcn/ui/tree/main/apps/www/app/examples/forms",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Music",
|
name: "Music",
|
||||||
href: "/examples/music",
|
href: "/examples/music",
|
||||||
|
code: "https://github.com/shadcn/ui/tree/main/apps/www/app/examples/music",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Authentication",
|
name: "Authentication",
|
||||||
href: "/examples/authentication",
|
href: "/examples/authentication",
|
||||||
|
code: "https://github.com/shadcn/ui/tree/main/apps/www/app/examples/authentication",
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -35,24 +51,53 @@ export function ExamplesNav({ className, ...props }: ExamplesNavProps) {
|
|||||||
const pathname = usePathname()
|
const pathname = usePathname()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScrollArea>
|
<div className="relative">
|
||||||
<div className={cn("mb-4 flex items-center", className)} {...props}>
|
<ScrollArea className="max-w-[600px] lg:max-w-none">
|
||||||
{examples.map((example) => (
|
<div className={cn("mb-4 flex items-center", className)} {...props}>
|
||||||
<Link
|
{examples.map((example) => (
|
||||||
href={example.href}
|
<Link
|
||||||
key={example.href}
|
href={example.href}
|
||||||
className={cn(
|
key={example.href}
|
||||||
"flex px-4 font-medium",
|
className={cn(
|
||||||
pathname === example.href
|
"flex items-center px-4",
|
||||||
? "text-primary"
|
pathname?.startsWith(example.href)
|
||||||
: "text-muted-foreground"
|
? "font-bold text-primary"
|
||||||
)}
|
: "font-medium text-muted-foreground"
|
||||||
>
|
)}
|
||||||
{example.name}
|
>
|
||||||
</Link>
|
{example.name}
|
||||||
))}
|
</Link>
|
||||||
</div>
|
))}
|
||||||
<ScrollBar orientation="horizontal" className="invisible" />
|
</div>
|
||||||
</ScrollArea>
|
<ScrollBar orientation="horizontal" className="invisible" />
|
||||||
|
</ScrollArea>
|
||||||
|
<ExampleCodeLink
|
||||||
|
pathname={pathname === "/" ? "/examples/dashboard" : pathname}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ExampleCodeLinkProps {
|
||||||
|
pathname: string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ExampleCodeLink({ pathname }: ExampleCodeLinkProps) {
|
||||||
|
const example = examples.find((example) => pathname?.startsWith(example.href))
|
||||||
|
|
||||||
|
if (!example?.code) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
href={example?.code}
|
||||||
|
target="_blank"
|
||||||
|
rel="nofollow"
|
||||||
|
className="absolute right-0 top-0 hidden items-center rounded-[0.5rem] text-sm font-medium md:flex"
|
||||||
|
>
|
||||||
|
View code
|
||||||
|
<ArrowRightIcon className="ml-1 h-4 w-4" />
|
||||||
|
</Link>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user