mirror of
https://github.com/shadcn-ui/ui.git
synced 2026-06-16 04:11:34 +00:00
Compare commits
1 Commits
fix/cli-va
...
shadcn/deb
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
93cb808f3a |
@@ -7,5 +7,5 @@
|
|||||||
"access": "public",
|
"access": "public",
|
||||||
"baseBranch": "main",
|
"baseBranch": "main",
|
||||||
"updateInternalDependencies": "patch",
|
"updateInternalDependencies": "patch",
|
||||||
"ignore": ["v4", "tests"]
|
"ignore": ["www", "**-template"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
"shadcn": patch
|
|
||||||
---
|
|
||||||
|
|
||||||
validate app name on create
|
|
||||||
5
.changeset/wet-eagles-leave.md
Normal file
5
.changeset/wet-eagles-leave.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"shadcn-ui": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
update zod imports
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
{
|
|
||||||
"permissions": {
|
|
||||||
"allow": [
|
|
||||||
"Bash(npm test:*)",
|
|
||||||
"Bash(npm run typecheck:*)",
|
|
||||||
"Bash(ls:*)",
|
|
||||||
"Bash(cat:*)",
|
|
||||||
"WebSearch",
|
|
||||||
"WebFetch(domain:github.com)"
|
|
||||||
],
|
|
||||||
"deny": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
node_modules/
|
|
||||||
target/
|
|
||||||
.next/
|
|
||||||
build/
|
|
||||||
dist/
|
|
||||||
|
|
||||||
/templates/
|
|
||||||
/fixtures/
|
|
||||||
@@ -8,7 +8,6 @@
|
|||||||
"plugin:tailwindcss/recommended"
|
"plugin:tailwindcss/recommended"
|
||||||
],
|
],
|
||||||
"plugins": ["tailwindcss"],
|
"plugins": ["tailwindcss"],
|
||||||
"ignorePatterns": ["**/fixtures/**"],
|
|
||||||
"rules": {
|
"rules": {
|
||||||
"@next/next/no-html-link-for-pages": "off",
|
"@next/next/no-html-link-for-pages": "off",
|
||||||
"tailwindcss/no-custom-classname": "off",
|
"tailwindcss/no-custom-classname": "off",
|
||||||
|
|||||||
3
.github/FUNDING.yml
vendored
3
.github/FUNDING.yml
vendored
@@ -1,3 +0,0 @@
|
|||||||
# These are supported funding model platforms
|
|
||||||
|
|
||||||
github: [shadcn]
|
|
||||||
63
.github/ISSUE_TEMPLATE/registry_directory.yml
vendored
63
.github/ISSUE_TEMPLATE/registry_directory.yml
vendored
@@ -1,63 +0,0 @@
|
|||||||
name: Add registry to directory
|
|
||||||
description: Add your registry to the directory
|
|
||||||
title: "[Registry Directory]: "
|
|
||||||
labels: ["registry", "directory"]
|
|
||||||
assignees: []
|
|
||||||
body:
|
|
||||||
- type: input
|
|
||||||
id: name
|
|
||||||
attributes:
|
|
||||||
label: Name
|
|
||||||
description: The name of your registry. This is also the namespace.
|
|
||||||
placeholder: e.g., "@acme"
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: input
|
|
||||||
id: url
|
|
||||||
attributes:
|
|
||||||
label: URL
|
|
||||||
description: The URL to your registry index. Use {name} placeholder.
|
|
||||||
placeholder: https://ui.acme.com/r/{name}.json
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: input
|
|
||||||
id: homepage
|
|
||||||
attributes:
|
|
||||||
label: Homepage
|
|
||||||
description: The URL to your registry homepage. This is where users can browse your registry.
|
|
||||||
placeholder: https://ui.acme.com
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: description
|
|
||||||
attributes:
|
|
||||||
label: Description
|
|
||||||
description: Briefly describe what is your registry and what type of components or code it distributes.
|
|
||||||
placeholder:
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: logo
|
|
||||||
attributes:
|
|
||||||
label: Logo
|
|
||||||
description: Add your SVG logo here.
|
|
||||||
placeholder:
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: checkboxes
|
|
||||||
id: requirements
|
|
||||||
attributes:
|
|
||||||
label: Checklist
|
|
||||||
description: Verify that your registry meets the following requirements.
|
|
||||||
options:
|
|
||||||
- label: The registry must be open source and publicly accessible.
|
|
||||||
- label: The registry must be a valid JSON file that conforms to the [registry schema](https://ui.shadcn.com/docs/registry/registry-json) specification.
|
|
||||||
- label: The `files` array, if present on your registry items, must NOT include a `content` property.
|
|
||||||
- label: I've attached a square SVG logo to this issue
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
6
.github/dependabot.yml
vendored
6
.github/dependabot.yml
vendored
@@ -1,6 +0,0 @@
|
|||||||
version: 2
|
|
||||||
updates:
|
|
||||||
- package-ecosystem: "npm"
|
|
||||||
directory: "/"
|
|
||||||
schedule:
|
|
||||||
interval: "weekly"
|
|
||||||
2
.github/version-script-beta.js
vendored
2
.github/version-script-beta.js
vendored
@@ -4,7 +4,7 @@
|
|||||||
import { exec } from "child_process"
|
import { exec } from "child_process"
|
||||||
import fs from "fs"
|
import fs from "fs"
|
||||||
|
|
||||||
const pkgJsonPath = "packages/shadcn/package.json"
|
const pkgJsonPath = "packages/cli/package.json"
|
||||||
try {
|
try {
|
||||||
const pkg = JSON.parse(fs.readFileSync(pkgJsonPath))
|
const pkg = JSON.parse(fs.readFileSync(pkgJsonPath))
|
||||||
exec("git rev-parse --short HEAD", (err, stdout) => {
|
exec("git rev-parse --short HEAD", (err, stdout) => {
|
||||||
|
|||||||
2
.github/version-script-next.js
vendored
2
.github/version-script-next.js
vendored
@@ -4,7 +4,7 @@
|
|||||||
import { exec } from "child_process"
|
import { exec } from "child_process"
|
||||||
import fs from "fs"
|
import fs from "fs"
|
||||||
|
|
||||||
const pkgJsonPath = "packages/shadcn/package.json"
|
const pkgJsonPath = "packages/cli/package.json"
|
||||||
try {
|
try {
|
||||||
const pkg = JSON.parse(fs.readFileSync(pkgJsonPath))
|
const pkg = JSON.parse(fs.readFileSync(pkgJsonPath))
|
||||||
exec("git rev-parse --short HEAD", (err, stdout) => {
|
exec("git rev-parse --short HEAD", (err, stdout) => {
|
||||||
|
|||||||
21
.github/workflows/code-check.yml
vendored
21
.github/workflows/code-check.yml
vendored
@@ -16,13 +16,13 @@ jobs:
|
|||||||
- name: Install Node.js
|
- name: Install Node.js
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 18
|
||||||
|
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v2.2.4
|
||||||
name: Install pnpm
|
name: Install pnpm
|
||||||
id: pnpm-install
|
id: pnpm-install
|
||||||
with:
|
with:
|
||||||
version: 9.0.6
|
version: 8.6.1
|
||||||
run_install: false
|
run_install: false
|
||||||
|
|
||||||
- name: Get pnpm store directory
|
- name: Get pnpm store directory
|
||||||
@@ -52,13 +52,13 @@ jobs:
|
|||||||
- name: Install Node.js
|
- name: Install Node.js
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 18
|
||||||
|
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v2.2.4
|
||||||
name: Install pnpm
|
name: Install pnpm
|
||||||
id: pnpm-install
|
id: pnpm-install
|
||||||
with:
|
with:
|
||||||
version: 9.0.6
|
version: 8.6.1
|
||||||
run_install: false
|
run_install: false
|
||||||
|
|
||||||
- name: Get pnpm store directory
|
- name: Get pnpm store directory
|
||||||
@@ -90,13 +90,13 @@ jobs:
|
|||||||
- name: Install Node.js
|
- name: Install Node.js
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 18
|
||||||
|
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v2.2.4
|
||||||
name: Install pnpm
|
name: Install pnpm
|
||||||
id: pnpm-install
|
id: pnpm-install
|
||||||
with:
|
with:
|
||||||
version: 9.0.6
|
version: 8.6.1
|
||||||
run_install: false
|
run_install: false
|
||||||
|
|
||||||
- name: Get pnpm store directory
|
- name: Get pnpm store directory
|
||||||
@@ -113,7 +113,4 @@ jobs:
|
|||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install
|
run: pnpm install
|
||||||
|
|
||||||
- name: Build packages
|
|
||||||
run: pnpm --filter=shadcn build
|
|
||||||
|
|
||||||
- run: pnpm typecheck
|
- run: pnpm typecheck
|
||||||
|
|||||||
78
.github/workflows/deprecated.yml
vendored
78
.github/workflows/deprecated.yml
vendored
@@ -1,78 +0,0 @@
|
|||||||
name: Deprecated
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request_target:
|
|
||||||
types: [opened, synchronize]
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
issues: write
|
|
||||||
contents: read
|
|
||||||
pull-requests: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
deprecated:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout PR
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
ref: ${{ github.event.pull_request.head.sha }}
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Get changed files
|
|
||||||
id: changed-files
|
|
||||||
uses: tj-actions/changed-files@v46
|
|
||||||
with:
|
|
||||||
files: |
|
|
||||||
apps/www/**
|
|
||||||
files_ignore: |
|
|
||||||
apps/www/public/r/**
|
|
||||||
base_sha: ${{ github.event.pull_request.base.sha }}
|
|
||||||
sha: ${{ github.event.pull_request.head.sha }}
|
|
||||||
|
|
||||||
- name: Comment on PR if www files changed
|
|
||||||
if: steps.changed-files.outputs.any_changed == 'true'
|
|
||||||
uses: actions/github-script@v7
|
|
||||||
with:
|
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
script: |
|
|
||||||
const changedFiles = `${{ steps.changed-files.outputs.all_changed_files }}`.split(' ');
|
|
||||||
const wwwFiles = changedFiles.filter(file =>
|
|
||||||
file.startsWith('apps/www/') &&
|
|
||||||
!file.startsWith('apps/www/public/r/') &&
|
|
||||||
file !== 'apps/www/package.json'
|
|
||||||
);
|
|
||||||
|
|
||||||
if (wwwFiles.length > 0) {
|
|
||||||
const comment = `Looks like this PR modifies files in \`apps/www\`, which is deprecated.
|
|
||||||
|
|
||||||
Consider applying the change to \`apps/v4\` if relevant.`;
|
|
||||||
|
|
||||||
await github.rest.issues.createComment({
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
issue_number: context.issue.number,
|
|
||||||
body: comment
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add deprecated label
|
|
||||||
await github.rest.issues.addLabels({
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
issue_number: context.issue.number,
|
|
||||||
labels: ['deprecated']
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// Remove deprecated label if no www files are changed
|
|
||||||
try {
|
|
||||||
await github.rest.issues.removeLabel({
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
issue_number: context.issue.number,
|
|
||||||
name: 'deprecated'
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
// Label doesn't exist, which is fine
|
|
||||||
console.log('Deprecated label not found, skipping removal');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
39
.github/workflows/issue-stale.yml
vendored
39
.github/workflows/issue-stale.yml
vendored
@@ -1,5 +1,5 @@
|
|||||||
# Adapted from vercel/next.js
|
# Adapted from vercel/next.js
|
||||||
name: "Stale issue handler"
|
name: Issue Stale
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
schedule:
|
schedule:
|
||||||
@@ -11,35 +11,16 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: github.repository_owner == 'shadcn-ui'
|
if: github.repository_owner == 'shadcn-ui'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/stale@v9
|
- uses: actions/stale@v4
|
||||||
id: issue-stale
|
id: stale-no-repro
|
||||||
name: "Mark stale issues, close stale issues"
|
name: "Close stale issues with no reproduction"
|
||||||
with:
|
with:
|
||||||
repo-token: ${{ secrets.STALE_TOKEN }}
|
repo-token: ${{ secrets.STALE_TOKEN }}
|
||||||
ascending: true
|
close-issue-message: "This issue has been automatically closed because it received no activity for a while. If you think it was closed by accident, please leave a comment. Thank you."
|
||||||
days-before-issue-close: 7
|
days-before-issue-close: 7
|
||||||
days-before-issue-stale: 365
|
days-before-issue-stale: 15
|
||||||
days-before-pr-stale: -1
|
|
||||||
days-before-pr-close: -1
|
|
||||||
remove-issue-stale-when-updated: true
|
|
||||||
stale-issue-label: "stale?"
|
|
||||||
exempt-issue-labels: "roadmap,next"
|
|
||||||
stale-issue-message: "This issue has been automatically marked as stale due to one year of inactivity. It will be closed in 7 days unless there’s further input. If you believe this issue is still relevant, please leave a comment or provide updated details. Thank you. (This is an automated message)"
|
|
||||||
close-issue-message: "This issue has been automatically closed due to one year of inactivity. If you’re still experiencing a similar problem or have additional details to share, please open a new issue following our current issue template. Your updated report helps us investigate and address concerns more efficiently. Thank you for your understanding! (This is an automated message)"
|
|
||||||
operations-per-run: 300
|
|
||||||
- uses: actions/stale@v9
|
|
||||||
id: pr-state
|
|
||||||
name: "Mark stale PRs, close stale PRs"
|
|
||||||
with:
|
|
||||||
repo-token: ${{ secrets.STALE_TOKEN }}
|
|
||||||
ascending: true
|
|
||||||
days-before-issue-close: -1
|
|
||||||
days-before-issue-stale: -1
|
|
||||||
days-before-pr-close: 7
|
|
||||||
days-before-pr-stale: 365
|
|
||||||
remove-pr-stale-when-updated: true
|
|
||||||
exempt-pr-labels: "roadmap,next,bug"
|
|
||||||
stale-pr-label: "stale?"
|
stale-pr-label: "stale?"
|
||||||
stale-pr-message: "This PR has been automatically marked as stale due to one year of inactivity. It will be closed in 7 days unless there’s further input. If you believe this PR is still relevant, please leave a comment or provide updated details. Thank you. (This is an automated message)"
|
days-before-pr-close: -1
|
||||||
close-pr-message: "This PR has been automatically closed due to one year of inactivity. Thank you for your understanding! (This is an automated message)"
|
days-before-pr-stale: -1
|
||||||
operations-per-run: 300
|
exempt-issue-labels: "roadmap,next,bug"
|
||||||
|
operations-per-run: 300 # 1 operation per 100 issues, the rest is to label/comment/close
|
||||||
|
|||||||
4
.github/workflows/prerelease-comment.yml
vendored
4
.github/workflows/prerelease-comment.yml
vendored
@@ -28,7 +28,7 @@ jobs:
|
|||||||
|
|
||||||
for (const artifact of allArtifacts.data.artifacts) {
|
for (const artifact of allArtifacts.data.artifacts) {
|
||||||
// Extract the PR number and package version from the artifact name
|
// Extract the PR number and package version from the artifact name
|
||||||
const match = /^npm-package-shadcn@(.*?)-pr-(\d+)/.exec(artifact.name);
|
const match = /^npm-package-shadcn-ui@(.*?)-pr-(\d+)/.exec(artifact.name);
|
||||||
|
|
||||||
if (match) {
|
if (match) {
|
||||||
require("fs").appendFileSync(
|
require("fs").appendFileSync(
|
||||||
@@ -49,7 +49,7 @@ jobs:
|
|||||||
A new prerelease is available for testing:
|
A new prerelease is available for testing:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
pnpm dlx shadcn@${{ env.BETA_PACKAGE_VERSION }}
|
npx shadcn-ui@${{ env.BETA_PACKAGE_VERSION }}
|
||||||
```
|
```
|
||||||
|
|
||||||
- name: "Remove the autorelease label once published"
|
- name: "Remove the autorelease label once published"
|
||||||
|
|||||||
34
.github/workflows/prerelease.yml
vendored
34
.github/workflows/prerelease.yml
vendored
@@ -7,11 +7,6 @@ on:
|
|||||||
types: [labeled]
|
types: [labeled]
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
|
||||||
permissions:
|
|
||||||
id-token: write
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
prerelease:
|
prerelease:
|
||||||
if: |
|
if: |
|
||||||
@@ -23,31 +18,32 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout Repo
|
- name: Checkout Repo
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Use PNPM
|
- name: Use PNPM
|
||||||
uses: pnpm/action-setup@v4
|
uses: pnpm/action-setup@v2.2.4
|
||||||
with:
|
with:
|
||||||
version: 9.0.6
|
version: 8.6.1
|
||||||
|
|
||||||
- name: Use Node.js 20
|
- name: Use Node.js 18
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 18
|
||||||
registry-url: "https://registry.npmjs.org"
|
|
||||||
cache: "pnpm"
|
cache: "pnpm"
|
||||||
|
|
||||||
- name: Update npm for OIDC support
|
|
||||||
run: npm install -g npm@latest
|
|
||||||
|
|
||||||
- name: Install NPM Dependencies
|
- name: Install NPM Dependencies
|
||||||
run: pnpm install
|
run: pnpm install
|
||||||
|
|
||||||
- name: Modify package.json version
|
- name: Modify package.json version
|
||||||
run: node .github/version-script-beta.js
|
run: node .github/version-script-beta.js
|
||||||
|
|
||||||
|
- name: Authenticate to NPM
|
||||||
|
run: echo "//registry.npmjs.org/:_authToken=$NPM_ACCESS_TOKEN" >> packages/cli/.npmrc
|
||||||
|
env:
|
||||||
|
NPM_ACCESS_TOKEN: ${{ secrets.NPM_ACCESS_TOKEN }}
|
||||||
|
|
||||||
- name: Publish Beta to NPM
|
- name: Publish Beta to NPM
|
||||||
run: pnpm pub:beta
|
run: pnpm pub:beta
|
||||||
|
|
||||||
@@ -55,10 +51,10 @@ jobs:
|
|||||||
id: package-version
|
id: package-version
|
||||||
uses: martinbeentjes/npm-get-version-action@main
|
uses: martinbeentjes/npm-get-version-action@main
|
||||||
with:
|
with:
|
||||||
path: packages/shadcn
|
path: packages/cli
|
||||||
|
|
||||||
- name: Upload packaged artifact
|
- name: Upload packaged artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: npm-package-shadcn@${{ steps.package-version.outputs.current-version }}-pr-${{ github.event.number }} # encode the PR number into the artifact name
|
name: npm-package-shadcn-ui@${{ steps.package-version.outputs.current-version }}-pr-${{ github.event.number }} # encode the PR number into the artifact name
|
||||||
path: packages/shadcn/dist/index.js
|
path: packages/cli/dist/index.js
|
||||||
|
|||||||
25
.github/workflows/release.yml
vendored
25
.github/workflows/release.yml
vendored
@@ -7,11 +7,6 @@ on:
|
|||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
|
||||||
permissions:
|
|
||||||
id-token: write
|
|
||||||
contents: write
|
|
||||||
pull-requests: write
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
release:
|
release:
|
||||||
if: ${{ github.repository_owner == 'shadcn-ui' }}
|
if: ${{ github.repository_owner == 'shadcn-ui' }}
|
||||||
@@ -24,20 +19,17 @@ jobs:
|
|||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Use PNPM
|
- name: Use PNPM
|
||||||
uses: pnpm/action-setup@v4
|
uses: pnpm/action-setup@v2.2.4
|
||||||
with:
|
with:
|
||||||
version: 9.0.6
|
version: 8.6.1
|
||||||
|
|
||||||
- name: Use Node.js 20
|
- name: Use Node.js 18
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
version: 8.6.1
|
||||||
registry-url: "https://registry.npmjs.org"
|
node-version: 18
|
||||||
cache: "pnpm"
|
cache: "pnpm"
|
||||||
|
|
||||||
- name: Update npm for OIDC support
|
|
||||||
run: npm install -g npm@latest
|
|
||||||
|
|
||||||
- name: Install NPM Dependencies
|
- name: Install NPM Dependencies
|
||||||
run: pnpm install
|
run: pnpm install
|
||||||
|
|
||||||
@@ -45,11 +37,11 @@ jobs:
|
|||||||
# run: pnpm check
|
# run: pnpm check
|
||||||
|
|
||||||
- name: Build the package
|
- name: Build the package
|
||||||
run: pnpm shadcn:build
|
run: pnpm build:cli
|
||||||
|
|
||||||
- name: Create Version PR or Publish to NPM
|
- name: Create Version PR or Publish to NPM
|
||||||
id: changesets
|
id: changesets
|
||||||
uses: changesets/action@v1
|
uses: changesets/action@v1.4.1
|
||||||
with:
|
with:
|
||||||
commit: "chore(release): version packages"
|
commit: "chore(release): version packages"
|
||||||
title: "chore(release): version packages"
|
title: "chore(release): version packages"
|
||||||
@@ -57,4 +49,5 @@ jobs:
|
|||||||
publish: npx changeset publish
|
publish: npx changeset publish
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
NPM_TOKEN: ${{ secrets.NPM_ACCESS_TOKEN }}
|
||||||
NODE_ENV: "production"
|
NODE_ENV: "production"
|
||||||
|
|||||||
12
.github/workflows/test.yml
vendored
12
.github/workflows/test.yml
vendored
@@ -8,9 +8,6 @@ jobs:
|
|||||||
test:
|
test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
name: pnpm test
|
name: pnpm test
|
||||||
env:
|
|
||||||
NEXT_PUBLIC_APP_URL: http://localhost:4000
|
|
||||||
NEXT_PUBLIC_V0_URL: https://v0.dev
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
@@ -19,13 +16,13 @@ jobs:
|
|||||||
- name: Install Node.js
|
- name: Install Node.js
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 18
|
||||||
|
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v2.2.4
|
||||||
name: Install pnpm
|
name: Install pnpm
|
||||||
id: pnpm-install
|
id: pnpm-install
|
||||||
with:
|
with:
|
||||||
version: 9.0.6
|
version: 8.6.1
|
||||||
run_install: false
|
run_install: false
|
||||||
|
|
||||||
- name: Get pnpm store directory
|
- name: Get pnpm store directory
|
||||||
@@ -42,7 +39,4 @@ jobs:
|
|||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install
|
run: pnpm install
|
||||||
|
|
||||||
- name: Build packages
|
|
||||||
run: pnpm build --filter=shadcn
|
|
||||||
|
|
||||||
- run: pnpm test
|
- run: pnpm test
|
||||||
|
|||||||
54
.github/workflows/validate-registries.yml
vendored
54
.github/workflows/validate-registries.yml
vendored
@@ -1,54 +0,0 @@
|
|||||||
name: Validate Registries
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
paths:
|
|
||||||
- "apps/v4/public/r/registries.json"
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
paths:
|
|
||||||
- "apps/v4/public/r/registries.json"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
validate:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
name: pnpm validate:registries
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Install Node.js
|
|
||||||
uses: actions/setup-node@v3
|
|
||||||
with:
|
|
||||||
node-version: 20
|
|
||||||
|
|
||||||
- uses: pnpm/action-setup@v4
|
|
||||||
name: Install pnpm
|
|
||||||
id: pnpm-install
|
|
||||||
with:
|
|
||||||
version: 9.0.6
|
|
||||||
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
|
|
||||||
|
|
||||||
- name: Build packages
|
|
||||||
run: pnpm build --filter=shadcn
|
|
||||||
|
|
||||||
- name: Validate registries
|
|
||||||
run: pnpm --filter=v4 validate:registries
|
|
||||||
7
.gitignore
vendored
7
.gitignore
vendored
@@ -34,10 +34,3 @@ yarn-error.log*
|
|||||||
|
|
||||||
.contentlayer
|
.contentlayer
|
||||||
tsconfig.tsbuildinfo
|
tsconfig.tsbuildinfo
|
||||||
|
|
||||||
# ide
|
|
||||||
.idea
|
|
||||||
.fleet
|
|
||||||
.vscode
|
|
||||||
|
|
||||||
.notes
|
|
||||||
|
|||||||
4
.husky/commit-msg
Executable file
4
.husky/commit-msg
Executable file
@@ -0,0 +1,4 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
. "$(dirname -- "$0")/_/husky.sh"
|
||||||
|
|
||||||
|
npx commitlint --edit $1
|
||||||
1
.npmrc
1
.npmrc
@@ -1,2 +1 @@
|
|||||||
auto-install-peers=true
|
auto-install-peers=true
|
||||||
link-workspace-packages=true
|
|
||||||
|
|||||||
@@ -3,5 +3,4 @@ node_modules
|
|||||||
.next
|
.next
|
||||||
build
|
build
|
||||||
.contentlayer
|
.contentlayer
|
||||||
**/fixtures
|
apps/www/pages/api/registry.json
|
||||||
deprecated
|
|
||||||
18
.vscode/settings.json
vendored
18
.vscode/settings.json
vendored
@@ -3,18 +3,8 @@
|
|||||||
{ "pattern": "apps/*/" },
|
{ "pattern": "apps/*/" },
|
||||||
{ "pattern": "packages/*/" }
|
{ "pattern": "packages/*/" }
|
||||||
],
|
],
|
||||||
"tailwindCSS.classFunctions": ["cva", "cn"],
|
"tailwindCSS.experimental.classRegex": [
|
||||||
"vitest.debugExclude": [
|
["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"],
|
||||||
"<node_internals>/**",
|
["cn\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"]
|
||||||
"**/node_modules/**",
|
]
|
||||||
"**/fixtures/**"
|
|
||||||
],
|
|
||||||
"files.exclude": {
|
|
||||||
"deprecated": true
|
|
||||||
},
|
|
||||||
"search.exclude": {
|
|
||||||
"apps/v4/registry/radix-*": true,
|
|
||||||
"apps/v4/public/r/*": true,
|
|
||||||
"packages/shadcn/test/fixtures/*": true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,25 +20,28 @@ This repository is structured as follows:
|
|||||||
|
|
||||||
```
|
```
|
||||||
apps
|
apps
|
||||||
└── v4
|
└── www
|
||||||
├── app
|
├── app
|
||||||
├── components
|
├── components
|
||||||
├── content
|
├── content
|
||||||
└── registry
|
└── registry
|
||||||
└── new-york-v4
|
├── default
|
||||||
|
│ ├── example
|
||||||
|
│ └── ui
|
||||||
|
└── new-york
|
||||||
├── example
|
├── example
|
||||||
└── ui
|
└── ui
|
||||||
packages
|
packages
|
||||||
└── shadcn
|
└── cli
|
||||||
```
|
```
|
||||||
|
|
||||||
| Path | Description |
|
| Path | Description |
|
||||||
| -------------------- | ---------------------------------------- |
|
| --------------------- | ---------------------------------------- |
|
||||||
| `apps/v4/app` | The Next.js application for the website. |
|
| `apps/www/app` | The Next.js application for the website. |
|
||||||
| `apps/v4/components` | The React components for the website. |
|
| `apps/www/components` | The React components for the website. |
|
||||||
| `apps/v4/content` | The content for the website. |
|
| `apps/www/content` | The content for the website. |
|
||||||
| `apps/v4/registry` | The registry for the components. |
|
| `apps/www/registry` | The registry for the components. |
|
||||||
| `packages/shadcn` | The `shadcn` package. |
|
| `packages/cli` | The `shadcn-ui` package. |
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
@@ -79,58 +82,37 @@ You can use the `pnpm --filter=[WORKSPACE]` command to start the development pro
|
|||||||
1. To run the `ui.shadcn.com` website:
|
1. To run the `ui.shadcn.com` website:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pnpm --filter=v4 dev
|
pnpm --filter=www dev
|
||||||
```
|
```
|
||||||
|
|
||||||
2. To run the `shadcn` package:
|
2. To run the `shadcn-ui` package:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pnpm --filter=shadcn dev
|
pnpm --filter=shadcn-ui dev
|
||||||
```
|
```
|
||||||
|
|
||||||
## Running the CLI Locally
|
|
||||||
|
|
||||||
To run the CLI locally, you can follow the workflow:
|
|
||||||
|
|
||||||
1. Start by running the dev server:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pnpm dev
|
|
||||||
```
|
|
||||||
|
|
||||||
2. In another terminal tab, test the CLI by running:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pnpm shadcn
|
|
||||||
```
|
|
||||||
|
|
||||||
To test the CLI in a specific app, use a command like:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pnpm shadcn <init | add | ...> -c ~/Desktop/my-app
|
|
||||||
```
|
|
||||||
|
|
||||||
This workflow ensures that you are running the most recent version of the registry and testing the CLI properly in your local environment.
|
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
The documentation for this project is located in the `v4` workspace. You can run the documentation locally by running the following command:
|
The documentation for this project is located in the `www` workspace. You can run the documentation locally by running the following command:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pnpm --filter=v4 dev
|
pnpm --filter=www dev
|
||||||
```
|
```
|
||||||
|
|
||||||
Documentation is written using [MDX](https://mdxjs.com). You can find the documentation files in the `apps/v4/content/docs` directory.
|
Documentation is written using [MDX](https://mdxjs.com). You can find the documentation files in the `apps/www/content/docs` directory.
|
||||||
|
|
||||||
## Components
|
## Components
|
||||||
|
|
||||||
We use a registry system for developing components. You can find the source code for the components under `apps/v4/registry`. The components are organized by styles.
|
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
|
```bash
|
||||||
apps
|
apps
|
||||||
└── v4
|
└── www
|
||||||
└── registry
|
└── registry
|
||||||
└── new-york-v4
|
├── default
|
||||||
|
│ ├── example
|
||||||
|
│ └── ui
|
||||||
|
└── new-york
|
||||||
├── example
|
├── example
|
||||||
└── ui
|
└── ui
|
||||||
```
|
```
|
||||||
@@ -139,7 +121,7 @@ When adding or modifying components, please ensure that:
|
|||||||
|
|
||||||
1. You make the changes for every style.
|
1. You make the changes for every style.
|
||||||
2. You update the documentation.
|
2. You update the documentation.
|
||||||
3. You run `pnpm registry:build` to update the registry.
|
3. You run `pnpm build:registry` to update the registry.
|
||||||
|
|
||||||
## Commit Convention
|
## Commit Convention
|
||||||
|
|
||||||
@@ -178,9 +160,9 @@ If you have a request for a new component, please open a discussion on GitHub. W
|
|||||||
|
|
||||||
## CLI
|
## CLI
|
||||||
|
|
||||||
The `shadcn` 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).
|
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/shadcn` directory. If you can, it would be great if you could add tests for your changes.
|
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
|
## Testing
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
# shadcn/ui
|
# shadcn/ui
|
||||||
|
|
||||||
A set of beautifully designed components that you can customize, extend, and build on. Start here then make it your own. Open Source. Open Code. **Use this to build your own component library**.
|
Accessible and customizable components that you can copy and paste into your apps. Free. Open Source. **Use this to build your own component library**.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
# Security Policy
|
|
||||||
|
|
||||||
If you believe you have found a security vulnerability, we encourage you to let us know right away.
|
|
||||||
|
|
||||||
We will investigate all legitimate reports and do our best to quickly fix the problem.
|
|
||||||
|
|
||||||
Our preference is that you make use of GitHub's private vulnerability reporting feature to disclose potential security vulnerabilities in our Open Source Software.
|
|
||||||
|
|
||||||
To do this, please visit the security tab of the repository and click the [Report a vulnerability](https://github.com/shadcn-ui/ui/security/advisories/new) button.
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
NEXT_PUBLIC_V0_URL=https://v0.dev
|
|
||||||
NEXT_PUBLIC_APP_URL=http://localhost:4000
|
|
||||||
48
apps/v4/.gitignore
vendored
48
apps/v4/.gitignore
vendored
@@ -1,48 +0,0 @@
|
|||||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
|
||||||
|
|
||||||
# dependencies
|
|
||||||
/node_modules
|
|
||||||
/.pnp
|
|
||||||
.pnp.*
|
|
||||||
.yarn/*
|
|
||||||
!.yarn/patches
|
|
||||||
!.yarn/plugins
|
|
||||||
!.yarn/releases
|
|
||||||
!.yarn/versions
|
|
||||||
|
|
||||||
# testing
|
|
||||||
/coverage
|
|
||||||
|
|
||||||
# next.js
|
|
||||||
/.next/
|
|
||||||
/out/
|
|
||||||
|
|
||||||
# production
|
|
||||||
/build
|
|
||||||
|
|
||||||
# misc
|
|
||||||
.DS_Store
|
|
||||||
*.pem
|
|
||||||
|
|
||||||
# debug
|
|
||||||
npm-debug.log*
|
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
||||||
.pnpm-debug.log*
|
|
||||||
|
|
||||||
# env files (can opt-in for committing if needed)
|
|
||||||
.env*
|
|
||||||
!.env.example
|
|
||||||
|
|
||||||
# vercel
|
|
||||||
.vercel
|
|
||||||
|
|
||||||
# typescript
|
|
||||||
*.tsbuildinfo
|
|
||||||
next-env.d.ts
|
|
||||||
|
|
||||||
|
|
||||||
# generated content
|
|
||||||
.contentlayer
|
|
||||||
.content-collections
|
|
||||||
.source
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
dist
|
|
||||||
node_modules
|
|
||||||
.next
|
|
||||||
build
|
|
||||||
.contentlayer
|
|
||||||
registry/__index__.tsx
|
|
||||||
content/docs/components/calendar.mdx
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
This is a wip registry for the `shadcn` canary version. It has React 19 and Tailwind v4 components.
|
|
||||||
@@ -1,138 +0,0 @@
|
|||||||
"use client"
|
|
||||||
|
|
||||||
import * as React from "react"
|
|
||||||
import { IconMinus, IconPlus } from "@tabler/icons-react"
|
|
||||||
|
|
||||||
import { Button } from "@/registry/new-york-v4/ui/button"
|
|
||||||
import { ButtonGroup } from "@/registry/new-york-v4/ui/button-group"
|
|
||||||
import {
|
|
||||||
Field,
|
|
||||||
FieldContent,
|
|
||||||
FieldDescription,
|
|
||||||
FieldGroup,
|
|
||||||
FieldLabel,
|
|
||||||
FieldLegend,
|
|
||||||
FieldSeparator,
|
|
||||||
FieldSet,
|
|
||||||
FieldTitle,
|
|
||||||
} from "@/registry/new-york-v4/ui/field"
|
|
||||||
import { Input } from "@/registry/new-york-v4/ui/input"
|
|
||||||
import {
|
|
||||||
RadioGroup,
|
|
||||||
RadioGroupItem,
|
|
||||||
} from "@/registry/new-york-v4/ui/radio-group"
|
|
||||||
import { Switch } from "@/registry/new-york-v4/ui/switch"
|
|
||||||
|
|
||||||
export function AppearanceSettings() {
|
|
||||||
const [gpuCount, setGpuCount] = React.useState(8)
|
|
||||||
|
|
||||||
const handleGpuAdjustment = React.useCallback((adjustment: number) => {
|
|
||||||
setGpuCount((prevCount) =>
|
|
||||||
Math.max(1, Math.min(99, prevCount + adjustment))
|
|
||||||
)
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
const handleGpuInputChange = React.useCallback(
|
|
||||||
(e: React.ChangeEvent<HTMLInputElement>) => {
|
|
||||||
const value = parseInt(e.target.value, 10)
|
|
||||||
if (!isNaN(value) && value >= 1 && value <= 99) {
|
|
||||||
setGpuCount(value)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[]
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<FieldSet>
|
|
||||||
<FieldGroup>
|
|
||||||
<FieldSet>
|
|
||||||
<FieldLegend>Compute Environment</FieldLegend>
|
|
||||||
<FieldDescription>
|
|
||||||
Select the compute environment for your cluster.
|
|
||||||
</FieldDescription>
|
|
||||||
<RadioGroup defaultValue="kubernetes">
|
|
||||||
<FieldLabel htmlFor="kubernetes-r2h">
|
|
||||||
<Field orientation="horizontal">
|
|
||||||
<FieldContent>
|
|
||||||
<FieldTitle>Kubernetes</FieldTitle>
|
|
||||||
<FieldDescription>
|
|
||||||
Run GPU workloads on a K8s configured cluster. This is the
|
|
||||||
default.
|
|
||||||
</FieldDescription>
|
|
||||||
</FieldContent>
|
|
||||||
<RadioGroupItem
|
|
||||||
value="kubernetes"
|
|
||||||
id="kubernetes-r2h"
|
|
||||||
aria-label="Kubernetes"
|
|
||||||
/>
|
|
||||||
</Field>
|
|
||||||
</FieldLabel>
|
|
||||||
<FieldLabel htmlFor="vm-z4k">
|
|
||||||
<Field orientation="horizontal">
|
|
||||||
<FieldContent>
|
|
||||||
<FieldTitle>Virtual Machine</FieldTitle>
|
|
||||||
<FieldDescription>
|
|
||||||
Access a VM configured cluster to run workloads. (Coming
|
|
||||||
soon)
|
|
||||||
</FieldDescription>
|
|
||||||
</FieldContent>
|
|
||||||
<RadioGroupItem
|
|
||||||
value="vm"
|
|
||||||
id="vm-z4k"
|
|
||||||
aria-label="Virtual Machine"
|
|
||||||
/>
|
|
||||||
</Field>
|
|
||||||
</FieldLabel>
|
|
||||||
</RadioGroup>
|
|
||||||
</FieldSet>
|
|
||||||
<FieldSeparator />
|
|
||||||
<Field orientation="horizontal">
|
|
||||||
<FieldContent>
|
|
||||||
<FieldLabel htmlFor="number-of-gpus-f6l">Number of GPUs</FieldLabel>
|
|
||||||
<FieldDescription>You can add more later.</FieldDescription>
|
|
||||||
</FieldContent>
|
|
||||||
<ButtonGroup>
|
|
||||||
<Input
|
|
||||||
id="number-of-gpus-f6l"
|
|
||||||
value={gpuCount}
|
|
||||||
onChange={handleGpuInputChange}
|
|
||||||
size={3}
|
|
||||||
className="h-8 !w-14 font-mono"
|
|
||||||
maxLength={3}
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
size="icon-sm"
|
|
||||||
type="button"
|
|
||||||
aria-label="Decrement"
|
|
||||||
onClick={() => handleGpuAdjustment(-1)}
|
|
||||||
disabled={gpuCount <= 1}
|
|
||||||
>
|
|
||||||
<IconMinus />
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
size="icon-sm"
|
|
||||||
type="button"
|
|
||||||
aria-label="Increment"
|
|
||||||
onClick={() => handleGpuAdjustment(1)}
|
|
||||||
disabled={gpuCount >= 99}
|
|
||||||
>
|
|
||||||
<IconPlus />
|
|
||||||
</Button>
|
|
||||||
</ButtonGroup>
|
|
||||||
</Field>
|
|
||||||
<FieldSeparator />
|
|
||||||
<Field orientation="horizontal">
|
|
||||||
<FieldContent>
|
|
||||||
<FieldLabel htmlFor="tinting">Wallpaper Tinting</FieldLabel>
|
|
||||||
<FieldDescription>
|
|
||||||
Allow the wallpaper to be tinted.
|
|
||||||
</FieldDescription>
|
|
||||||
</FieldContent>
|
|
||||||
<Switch id="tinting" defaultChecked />
|
|
||||||
</Field>
|
|
||||||
</FieldGroup>
|
|
||||||
</FieldSet>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,120 +0,0 @@
|
|||||||
"use client"
|
|
||||||
|
|
||||||
import * as React from "react"
|
|
||||||
import {
|
|
||||||
ArchiveIcon,
|
|
||||||
ArrowLeftIcon,
|
|
||||||
CalendarPlusIcon,
|
|
||||||
ClockIcon,
|
|
||||||
ListFilterIcon,
|
|
||||||
MailCheckIcon,
|
|
||||||
MoreHorizontalIcon,
|
|
||||||
TagIcon,
|
|
||||||
Trash2Icon,
|
|
||||||
} from "lucide-react"
|
|
||||||
|
|
||||||
import { Button } from "@/registry/new-york-v4/ui/button"
|
|
||||||
import { ButtonGroup } from "@/registry/new-york-v4/ui/button-group"
|
|
||||||
import {
|
|
||||||
DropdownMenu,
|
|
||||||
DropdownMenuContent,
|
|
||||||
DropdownMenuGroup,
|
|
||||||
DropdownMenuItem,
|
|
||||||
DropdownMenuRadioGroup,
|
|
||||||
DropdownMenuRadioItem,
|
|
||||||
DropdownMenuSeparator,
|
|
||||||
DropdownMenuSub,
|
|
||||||
DropdownMenuSubContent,
|
|
||||||
DropdownMenuSubTrigger,
|
|
||||||
DropdownMenuTrigger,
|
|
||||||
} from "@/registry/new-york-v4/ui/dropdown-menu"
|
|
||||||
|
|
||||||
export function ButtonGroupDemo() {
|
|
||||||
const [label, setLabel] = React.useState("personal")
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ButtonGroup>
|
|
||||||
<ButtonGroup className="hidden sm:flex">
|
|
||||||
<Button variant="outline" size="icon-sm" aria-label="Go Back">
|
|
||||||
<ArrowLeftIcon />
|
|
||||||
</Button>
|
|
||||||
</ButtonGroup>
|
|
||||||
<ButtonGroup>
|
|
||||||
<Button variant="outline" size="sm">
|
|
||||||
Archive
|
|
||||||
</Button>
|
|
||||||
<Button variant="outline" size="sm">
|
|
||||||
Report
|
|
||||||
</Button>
|
|
||||||
</ButtonGroup>
|
|
||||||
<ButtonGroup>
|
|
||||||
<Button variant="outline" size="sm">
|
|
||||||
Snooze
|
|
||||||
</Button>
|
|
||||||
<DropdownMenu>
|
|
||||||
<DropdownMenuTrigger asChild>
|
|
||||||
<Button variant="outline" size="icon-sm" aria-label="More Options">
|
|
||||||
<MoreHorizontalIcon />
|
|
||||||
</Button>
|
|
||||||
</DropdownMenuTrigger>
|
|
||||||
<DropdownMenuContent align="end" className="w-48 [--radius:1rem]">
|
|
||||||
<DropdownMenuGroup>
|
|
||||||
<DropdownMenuItem>
|
|
||||||
<MailCheckIcon />
|
|
||||||
Mark as Read
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem>
|
|
||||||
<ArchiveIcon />
|
|
||||||
Archive
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</DropdownMenuGroup>
|
|
||||||
<DropdownMenuSeparator />
|
|
||||||
<DropdownMenuGroup>
|
|
||||||
<DropdownMenuItem>
|
|
||||||
<ClockIcon />
|
|
||||||
Snooze
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem>
|
|
||||||
<CalendarPlusIcon />
|
|
||||||
Add to Calendar
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem>
|
|
||||||
<ListFilterIcon />
|
|
||||||
Add to List
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuSub>
|
|
||||||
<DropdownMenuSubTrigger>
|
|
||||||
<TagIcon />
|
|
||||||
Label As...
|
|
||||||
</DropdownMenuSubTrigger>
|
|
||||||
<DropdownMenuSubContent>
|
|
||||||
<DropdownMenuRadioGroup
|
|
||||||
value={label}
|
|
||||||
onValueChange={setLabel}
|
|
||||||
>
|
|
||||||
<DropdownMenuRadioItem value="personal">
|
|
||||||
Personal
|
|
||||||
</DropdownMenuRadioItem>
|
|
||||||
<DropdownMenuRadioItem value="work">
|
|
||||||
Work
|
|
||||||
</DropdownMenuRadioItem>
|
|
||||||
<DropdownMenuRadioItem value="other">
|
|
||||||
Other
|
|
||||||
</DropdownMenuRadioItem>
|
|
||||||
</DropdownMenuRadioGroup>
|
|
||||||
</DropdownMenuSubContent>
|
|
||||||
</DropdownMenuSub>
|
|
||||||
</DropdownMenuGroup>
|
|
||||||
<DropdownMenuSeparator />
|
|
||||||
<DropdownMenuGroup>
|
|
||||||
<DropdownMenuItem variant="destructive">
|
|
||||||
<Trash2Icon />
|
|
||||||
Trash
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</DropdownMenuGroup>
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
</ButtonGroup>
|
|
||||||
</ButtonGroup>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
"use client"
|
|
||||||
|
|
||||||
import * as React from "react"
|
|
||||||
import { AudioLinesIcon, PlusIcon } from "lucide-react"
|
|
||||||
|
|
||||||
import { Button } from "@/registry/new-york-v4/ui/button"
|
|
||||||
import { ButtonGroup } from "@/registry/new-york-v4/ui/button-group"
|
|
||||||
import {
|
|
||||||
InputGroup,
|
|
||||||
InputGroupAddon,
|
|
||||||
InputGroupButton,
|
|
||||||
InputGroupInput,
|
|
||||||
} from "@/registry/new-york-v4/ui/input-group"
|
|
||||||
import {
|
|
||||||
Tooltip,
|
|
||||||
TooltipContent,
|
|
||||||
TooltipTrigger,
|
|
||||||
} from "@/registry/new-york-v4/ui/tooltip"
|
|
||||||
|
|
||||||
export function ButtonGroupInputGroup() {
|
|
||||||
const [voiceEnabled, setVoiceEnabled] = React.useState(false)
|
|
||||||
return (
|
|
||||||
<ButtonGroup className="[--radius:9999rem]">
|
|
||||||
<ButtonGroup>
|
|
||||||
<Button variant="outline" size="icon" aria-label="Add">
|
|
||||||
<PlusIcon />
|
|
||||||
</Button>
|
|
||||||
</ButtonGroup>
|
|
||||||
<ButtonGroup className="flex-1">
|
|
||||||
<InputGroup>
|
|
||||||
<InputGroupInput
|
|
||||||
placeholder={
|
|
||||||
voiceEnabled ? "Record and send audio..." : "Send a message..."
|
|
||||||
}
|
|
||||||
disabled={voiceEnabled}
|
|
||||||
/>
|
|
||||||
<InputGroupAddon align="inline-end">
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<InputGroupButton
|
|
||||||
onClick={() => setVoiceEnabled(!voiceEnabled)}
|
|
||||||
data-active={voiceEnabled}
|
|
||||||
className="data-[active=true]:bg-primary data-[active=true]:text-primary-foreground"
|
|
||||||
aria-pressed={voiceEnabled}
|
|
||||||
size="icon-xs"
|
|
||||||
aria-label="Voice Mode"
|
|
||||||
>
|
|
||||||
<AudioLinesIcon />
|
|
||||||
</InputGroupButton>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent>Voice Mode</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
</InputGroupAddon>
|
|
||||||
</InputGroup>
|
|
||||||
</ButtonGroup>
|
|
||||||
</ButtonGroup>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
"use client"
|
|
||||||
|
|
||||||
import { ArrowLeftIcon, ArrowRightIcon } from "lucide-react"
|
|
||||||
|
|
||||||
import { Button } from "@/registry/new-york-v4/ui/button"
|
|
||||||
import { ButtonGroup } from "@/registry/new-york-v4/ui/button-group"
|
|
||||||
|
|
||||||
export function ButtonGroupNested() {
|
|
||||||
return (
|
|
||||||
<ButtonGroup>
|
|
||||||
<ButtonGroup>
|
|
||||||
<Button variant="outline" size="sm">
|
|
||||||
1
|
|
||||||
</Button>
|
|
||||||
<Button variant="outline" size="sm">
|
|
||||||
2
|
|
||||||
</Button>
|
|
||||||
<Button variant="outline" size="sm">
|
|
||||||
3
|
|
||||||
</Button>
|
|
||||||
</ButtonGroup>
|
|
||||||
<ButtonGroup>
|
|
||||||
<Button variant="outline" size="icon-sm" aria-label="Previous">
|
|
||||||
<ArrowLeftIcon />
|
|
||||||
</Button>
|
|
||||||
<Button variant="outline" size="icon-sm" aria-label="Next">
|
|
||||||
<ArrowRightIcon />
|
|
||||||
</Button>
|
|
||||||
</ButtonGroup>
|
|
||||||
</ButtonGroup>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
import { BotIcon, ChevronDownIcon } from "lucide-react"
|
|
||||||
|
|
||||||
import { Button } from "@/registry/new-york-v4/ui/button"
|
|
||||||
import { ButtonGroup } from "@/registry/new-york-v4/ui/button-group"
|
|
||||||
import {
|
|
||||||
Popover,
|
|
||||||
PopoverContent,
|
|
||||||
PopoverTrigger,
|
|
||||||
} from "@/registry/new-york-v4/ui/popover"
|
|
||||||
import { Separator } from "@/registry/new-york-v4/ui/separator"
|
|
||||||
import { Textarea } from "@/registry/new-york-v4/ui/textarea"
|
|
||||||
|
|
||||||
export function ButtonGroupPopover() {
|
|
||||||
return (
|
|
||||||
<ButtonGroup>
|
|
||||||
<Button variant="outline" size="sm">
|
|
||||||
<BotIcon /> Copilot
|
|
||||||
</Button>
|
|
||||||
<Popover>
|
|
||||||
<PopoverTrigger asChild>
|
|
||||||
<Button variant="outline" size="icon-sm" aria-label="Open Popover">
|
|
||||||
<ChevronDownIcon />
|
|
||||||
</Button>
|
|
||||||
</PopoverTrigger>
|
|
||||||
<PopoverContent align="end" className="rounded-xl p-0 text-sm">
|
|
||||||
<div className="px-4 py-3">
|
|
||||||
<div className="text-sm font-medium">Agent Tasks</div>
|
|
||||||
</div>
|
|
||||||
<Separator />
|
|
||||||
<div className="p-4 text-sm *:[p:not(:last-child)]:mb-2">
|
|
||||||
<Textarea
|
|
||||||
placeholder="Describe your task in natural language."
|
|
||||||
className="mb-4 resize-none"
|
|
||||||
/>
|
|
||||||
<p className="font-medium">Start a new task with Copilot</p>
|
|
||||||
<p className="text-muted-foreground">
|
|
||||||
Describe your task in natural language. Copilot will work in the
|
|
||||||
background and open a pull request for your review.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</PopoverContent>
|
|
||||||
</Popover>
|
|
||||||
</ButtonGroup>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
import { PlusIcon } from "lucide-react"
|
|
||||||
|
|
||||||
import {
|
|
||||||
Avatar,
|
|
||||||
AvatarFallback,
|
|
||||||
AvatarImage,
|
|
||||||
} from "@/registry/new-york-v4/ui/avatar"
|
|
||||||
import { Button } from "@/registry/new-york-v4/ui/button"
|
|
||||||
import {
|
|
||||||
Empty,
|
|
||||||
EmptyContent,
|
|
||||||
EmptyDescription,
|
|
||||||
EmptyHeader,
|
|
||||||
EmptyMedia,
|
|
||||||
EmptyTitle,
|
|
||||||
} from "@/registry/new-york-v4/ui/empty"
|
|
||||||
|
|
||||||
export function EmptyAvatarGroup() {
|
|
||||||
return (
|
|
||||||
<Empty className="flex-none border">
|
|
||||||
<EmptyHeader>
|
|
||||||
<EmptyMedia>
|
|
||||||
<div className="*:data-[slot=avatar]:ring-background flex -space-x-2 *:data-[slot=avatar]:size-12 *:data-[slot=avatar]:ring-2 *:data-[slot=avatar]:grayscale">
|
|
||||||
<Avatar>
|
|
||||||
<AvatarImage src="https://github.com/shadcn.png" alt="@shadcn" />
|
|
||||||
<AvatarFallback>CN</AvatarFallback>
|
|
||||||
</Avatar>
|
|
||||||
<Avatar>
|
|
||||||
<AvatarImage
|
|
||||||
src="https://github.com/maxleiter.png"
|
|
||||||
alt="@maxleiter"
|
|
||||||
/>
|
|
||||||
<AvatarFallback>LR</AvatarFallback>
|
|
||||||
</Avatar>
|
|
||||||
<Avatar>
|
|
||||||
<AvatarImage
|
|
||||||
src="https://github.com/evilrabbit.png"
|
|
||||||
alt="@evilrabbit"
|
|
||||||
/>
|
|
||||||
<AvatarFallback>ER</AvatarFallback>
|
|
||||||
</Avatar>
|
|
||||||
</div>
|
|
||||||
</EmptyMedia>
|
|
||||||
<EmptyTitle>No Team Members</EmptyTitle>
|
|
||||||
<EmptyDescription>
|
|
||||||
Invite your team to collaborate on this project.
|
|
||||||
</EmptyDescription>
|
|
||||||
</EmptyHeader>
|
|
||||||
<EmptyContent>
|
|
||||||
<Button size="sm">
|
|
||||||
<PlusIcon />
|
|
||||||
Invite Members
|
|
||||||
</Button>
|
|
||||||
</EmptyContent>
|
|
||||||
</Empty>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
import { SearchIcon } from "lucide-react"
|
|
||||||
|
|
||||||
import {
|
|
||||||
Empty,
|
|
||||||
EmptyContent,
|
|
||||||
EmptyDescription,
|
|
||||||
EmptyHeader,
|
|
||||||
EmptyTitle,
|
|
||||||
} from "@/registry/new-york-v4/ui/empty"
|
|
||||||
import {
|
|
||||||
InputGroup,
|
|
||||||
InputGroupAddon,
|
|
||||||
InputGroupInput,
|
|
||||||
} from "@/registry/new-york-v4/ui/input-group"
|
|
||||||
import { Kbd } from "@/registry/new-york-v4/ui/kbd"
|
|
||||||
|
|
||||||
export function EmptyInputGroup() {
|
|
||||||
return (
|
|
||||||
<Empty>
|
|
||||||
<EmptyHeader>
|
|
||||||
<EmptyTitle>404 - Not Found</EmptyTitle>
|
|
||||||
<EmptyDescription>
|
|
||||||
The page you're looking for doesn't exist. Try searching for
|
|
||||||
what you need below.
|
|
||||||
</EmptyDescription>
|
|
||||||
</EmptyHeader>
|
|
||||||
<EmptyContent>
|
|
||||||
<InputGroup className="w-3/4">
|
|
||||||
<InputGroupInput placeholder="Try searching for pages..." />
|
|
||||||
<InputGroupAddon>
|
|
||||||
<SearchIcon />
|
|
||||||
</InputGroupAddon>
|
|
||||||
<InputGroupAddon align="inline-end">
|
|
||||||
<Kbd>/</Kbd>
|
|
||||||
</InputGroupAddon>
|
|
||||||
</InputGroup>
|
|
||||||
<EmptyDescription>
|
|
||||||
Need help? <a href="#">Contact support</a>
|
|
||||||
</EmptyDescription>
|
|
||||||
</EmptyContent>
|
|
||||||
</Empty>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
import { Checkbox } from "@/registry/new-york-v4/ui/checkbox"
|
|
||||||
import { Field, FieldLabel } from "@/registry/new-york-v4/ui/field"
|
|
||||||
|
|
||||||
export function FieldCheckbox() {
|
|
||||||
return (
|
|
||||||
<FieldLabel htmlFor="checkbox-demo">
|
|
||||||
<Field orientation="horizontal">
|
|
||||||
<Checkbox id="checkbox-demo" defaultChecked />
|
|
||||||
<FieldLabel htmlFor="checkbox-demo" className="line-clamp-1">
|
|
||||||
I agree to the terms and conditions
|
|
||||||
</FieldLabel>
|
|
||||||
</Field>
|
|
||||||
</FieldLabel>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
import {
|
|
||||||
Field,
|
|
||||||
FieldContent,
|
|
||||||
FieldDescription,
|
|
||||||
FieldGroup,
|
|
||||||
FieldLabel,
|
|
||||||
FieldSet,
|
|
||||||
FieldTitle,
|
|
||||||
} from "@/registry/new-york-v4/ui/field"
|
|
||||||
import {
|
|
||||||
RadioGroup,
|
|
||||||
RadioGroupItem,
|
|
||||||
} from "@/registry/new-york-v4/ui/radio-group"
|
|
||||||
|
|
||||||
export function FieldChoiceCard() {
|
|
||||||
return (
|
|
||||||
<div className="w-full max-w-md">
|
|
||||||
<FieldGroup>
|
|
||||||
<FieldSet>
|
|
||||||
<FieldLabel htmlFor="compute-environment-p8w">
|
|
||||||
Compute Environment
|
|
||||||
</FieldLabel>
|
|
||||||
<FieldDescription>
|
|
||||||
Select the compute environment for your cluster.
|
|
||||||
</FieldDescription>
|
|
||||||
<RadioGroup defaultValue="kubernetes">
|
|
||||||
<FieldLabel htmlFor="kubernetes-r2h">
|
|
||||||
<Field orientation="horizontal">
|
|
||||||
<RadioGroupItem
|
|
||||||
value="kubernetes"
|
|
||||||
id="kubernetes-r2h"
|
|
||||||
aria-label="Kubernetes"
|
|
||||||
/>
|
|
||||||
<FieldContent>
|
|
||||||
<FieldTitle>Kubernetes</FieldTitle>
|
|
||||||
<FieldDescription>
|
|
||||||
Run GPU workloads on a K8s configured cluster.
|
|
||||||
</FieldDescription>
|
|
||||||
</FieldContent>
|
|
||||||
</Field>
|
|
||||||
</FieldLabel>
|
|
||||||
<FieldLabel htmlFor="vm-z4k">
|
|
||||||
<Field orientation="horizontal">
|
|
||||||
<RadioGroupItem
|
|
||||||
value="vm"
|
|
||||||
id="vm-z4k"
|
|
||||||
aria-label="Virtual Machine"
|
|
||||||
/>
|
|
||||||
<FieldContent>
|
|
||||||
<FieldTitle>Virtual Machine</FieldTitle>
|
|
||||||
<FieldDescription>
|
|
||||||
Access a VM configured cluster to run workloads.
|
|
||||||
</FieldDescription>
|
|
||||||
</FieldContent>
|
|
||||||
</Field>
|
|
||||||
</FieldLabel>
|
|
||||||
</RadioGroup>
|
|
||||||
</FieldSet>
|
|
||||||
</FieldGroup>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,153 +0,0 @@
|
|||||||
import { Button } from "@/registry/new-york-v4/ui/button"
|
|
||||||
import { Checkbox } from "@/registry/new-york-v4/ui/checkbox"
|
|
||||||
import {
|
|
||||||
Field,
|
|
||||||
FieldDescription,
|
|
||||||
FieldGroup,
|
|
||||||
FieldLabel,
|
|
||||||
FieldLegend,
|
|
||||||
FieldSeparator,
|
|
||||||
FieldSet,
|
|
||||||
} from "@/registry/new-york-v4/ui/field"
|
|
||||||
import { Input } from "@/registry/new-york-v4/ui/input"
|
|
||||||
import {
|
|
||||||
Select,
|
|
||||||
SelectContent,
|
|
||||||
SelectItem,
|
|
||||||
SelectTrigger,
|
|
||||||
SelectValue,
|
|
||||||
} from "@/registry/new-york-v4/ui/select"
|
|
||||||
import { Textarea } from "@/registry/new-york-v4/ui/textarea"
|
|
||||||
|
|
||||||
export function FieldDemo() {
|
|
||||||
return (
|
|
||||||
<div className="w-full max-w-md rounded-lg border p-6">
|
|
||||||
<form>
|
|
||||||
<FieldGroup>
|
|
||||||
<FieldSet>
|
|
||||||
<FieldLegend>Payment Method</FieldLegend>
|
|
||||||
<FieldDescription>
|
|
||||||
All transactions are secure and encrypted
|
|
||||||
</FieldDescription>
|
|
||||||
<FieldGroup>
|
|
||||||
<Field>
|
|
||||||
<FieldLabel htmlFor="checkout-7j9-card-name-43j">
|
|
||||||
Name on Card
|
|
||||||
</FieldLabel>
|
|
||||||
<Input
|
|
||||||
id="checkout-7j9-card-name-43j"
|
|
||||||
placeholder="John Doe"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</Field>
|
|
||||||
<div className="grid grid-cols-3 gap-4">
|
|
||||||
<Field className="col-span-2">
|
|
||||||
<FieldLabel htmlFor="checkout-7j9-card-number-uw1">
|
|
||||||
Card Number
|
|
||||||
</FieldLabel>
|
|
||||||
<Input
|
|
||||||
id="checkout-7j9-card-number-uw1"
|
|
||||||
placeholder="1234 5678 9012 3456"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
<FieldDescription>
|
|
||||||
Enter your 16-digit number.
|
|
||||||
</FieldDescription>
|
|
||||||
</Field>
|
|
||||||
<Field className="col-span-1">
|
|
||||||
<FieldLabel htmlFor="checkout-7j9-cvv">CVV</FieldLabel>
|
|
||||||
<Input id="checkout-7j9-cvv" placeholder="123" required />
|
|
||||||
</Field>
|
|
||||||
</div>
|
|
||||||
<div className="grid grid-cols-2 gap-4">
|
|
||||||
<Field>
|
|
||||||
<FieldLabel htmlFor="checkout-7j9-exp-month-ts6">
|
|
||||||
Month
|
|
||||||
</FieldLabel>
|
|
||||||
<Select defaultValue="">
|
|
||||||
<SelectTrigger id="checkout-7j9-exp-month-ts6">
|
|
||||||
<SelectValue placeholder="MM" />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
<SelectItem value="01">01</SelectItem>
|
|
||||||
<SelectItem value="02">02</SelectItem>
|
|
||||||
<SelectItem value="03">03</SelectItem>
|
|
||||||
<SelectItem value="04">04</SelectItem>
|
|
||||||
<SelectItem value="05">05</SelectItem>
|
|
||||||
<SelectItem value="06">06</SelectItem>
|
|
||||||
<SelectItem value="07">07</SelectItem>
|
|
||||||
<SelectItem value="08">08</SelectItem>
|
|
||||||
<SelectItem value="09">09</SelectItem>
|
|
||||||
<SelectItem value="10">10</SelectItem>
|
|
||||||
<SelectItem value="11">11</SelectItem>
|
|
||||||
<SelectItem value="12">12</SelectItem>
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</Field>
|
|
||||||
<Field>
|
|
||||||
<FieldLabel htmlFor="checkout-7j9-exp-year-f59">
|
|
||||||
Year
|
|
||||||
</FieldLabel>
|
|
||||||
<Select defaultValue="">
|
|
||||||
<SelectTrigger id="checkout-7j9-exp-year-f59">
|
|
||||||
<SelectValue placeholder="YYYY" />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
<SelectItem value="2024">2024</SelectItem>
|
|
||||||
<SelectItem value="2025">2025</SelectItem>
|
|
||||||
<SelectItem value="2026">2026</SelectItem>
|
|
||||||
<SelectItem value="2027">2027</SelectItem>
|
|
||||||
<SelectItem value="2028">2028</SelectItem>
|
|
||||||
<SelectItem value="2029">2029</SelectItem>
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</Field>
|
|
||||||
</div>
|
|
||||||
</FieldGroup>
|
|
||||||
</FieldSet>
|
|
||||||
<FieldSeparator />
|
|
||||||
<FieldSet>
|
|
||||||
<FieldLegend>Billing Address</FieldLegend>
|
|
||||||
<FieldDescription>
|
|
||||||
The billing address associated with your payment method
|
|
||||||
</FieldDescription>
|
|
||||||
<FieldGroup>
|
|
||||||
<Field orientation="horizontal">
|
|
||||||
<Checkbox
|
|
||||||
id="checkout-7j9-same-as-shipping-wgm"
|
|
||||||
defaultChecked
|
|
||||||
/>
|
|
||||||
<FieldLabel
|
|
||||||
htmlFor="checkout-7j9-same-as-shipping-wgm"
|
|
||||||
className="font-normal"
|
|
||||||
>
|
|
||||||
Same as shipping address
|
|
||||||
</FieldLabel>
|
|
||||||
</Field>
|
|
||||||
</FieldGroup>
|
|
||||||
</FieldSet>
|
|
||||||
<FieldSeparator />
|
|
||||||
<FieldSet>
|
|
||||||
<FieldGroup>
|
|
||||||
<Field>
|
|
||||||
<FieldLabel htmlFor="checkout-7j9-optional-comments">
|
|
||||||
Comments
|
|
||||||
</FieldLabel>
|
|
||||||
<Textarea
|
|
||||||
id="checkout-7j9-optional-comments"
|
|
||||||
placeholder="Add any additional comments"
|
|
||||||
/>
|
|
||||||
</Field>
|
|
||||||
</FieldGroup>
|
|
||||||
</FieldSet>
|
|
||||||
<Field orientation="horizontal">
|
|
||||||
<Button type="submit">Submit</Button>
|
|
||||||
<Button variant="outline" type="button">
|
|
||||||
Cancel
|
|
||||||
</Button>
|
|
||||||
</Field>
|
|
||||||
</FieldGroup>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
import { Card, CardContent } from "@/registry/new-york-v4/ui/card"
|
|
||||||
import { Checkbox } from "@/registry/new-york-v4/ui/checkbox"
|
|
||||||
import {
|
|
||||||
Field,
|
|
||||||
FieldDescription,
|
|
||||||
FieldGroup,
|
|
||||||
FieldLabel,
|
|
||||||
FieldLegend,
|
|
||||||
FieldSet,
|
|
||||||
FieldTitle,
|
|
||||||
} from "@/registry/new-york-v4/ui/field"
|
|
||||||
|
|
||||||
const options = [
|
|
||||||
{
|
|
||||||
label: "Social Media",
|
|
||||||
value: "social-media",
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
label: "Search Engine",
|
|
||||||
value: "search-engine",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Referral",
|
|
||||||
value: "referral",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Other",
|
|
||||||
value: "other",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
export function FieldHear() {
|
|
||||||
return (
|
|
||||||
<Card className="py-4 shadow-none">
|
|
||||||
<CardContent className="px-4">
|
|
||||||
<form>
|
|
||||||
<FieldGroup>
|
|
||||||
<FieldSet className="gap-4">
|
|
||||||
<FieldLegend>How did you hear about us?</FieldLegend>
|
|
||||||
<FieldDescription className="line-clamp-1">
|
|
||||||
Select the option that best describes how you heard about us.
|
|
||||||
</FieldDescription>
|
|
||||||
<FieldGroup className="flex flex-row flex-wrap gap-2 [--radius:9999rem]">
|
|
||||||
{options.map((option) => (
|
|
||||||
<FieldLabel
|
|
||||||
htmlFor={option.value}
|
|
||||||
key={option.value}
|
|
||||||
className="!w-fit"
|
|
||||||
>
|
|
||||||
<Field
|
|
||||||
orientation="horizontal"
|
|
||||||
className="gap-1.5 overflow-hidden !px-3 !py-1.5 transition-all duration-100 ease-linear group-has-data-[state=checked]/field-label:!px-2"
|
|
||||||
>
|
|
||||||
<Checkbox
|
|
||||||
value={option.value}
|
|
||||||
id={option.value}
|
|
||||||
defaultChecked={option.value === "social-media"}
|
|
||||||
className="-ml-6 -translate-x-1 rounded-full transition-all duration-100 ease-linear data-[state=checked]:ml-0 data-[state=checked]:translate-x-0"
|
|
||||||
/>
|
|
||||||
<FieldTitle>{option.label}</FieldTitle>
|
|
||||||
</Field>
|
|
||||||
</FieldLabel>
|
|
||||||
))}
|
|
||||||
</FieldGroup>
|
|
||||||
</FieldSet>
|
|
||||||
</FieldGroup>
|
|
||||||
</form>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
"use client"
|
|
||||||
|
|
||||||
import { useState } from "react"
|
|
||||||
|
|
||||||
import {
|
|
||||||
Field,
|
|
||||||
FieldDescription,
|
|
||||||
FieldTitle,
|
|
||||||
} from "@/registry/new-york-v4/ui/field"
|
|
||||||
import { Slider } from "@/registry/new-york-v4/ui/slider"
|
|
||||||
|
|
||||||
export function FieldSlider() {
|
|
||||||
const [value, setValue] = useState([200, 800])
|
|
||||||
return (
|
|
||||||
<div className="w-full max-w-md">
|
|
||||||
<Field>
|
|
||||||
<FieldTitle>Price Range</FieldTitle>
|
|
||||||
<FieldDescription>
|
|
||||||
Set your budget range ($
|
|
||||||
<span className="font-medium tabular-nums">{value[0]}</span> -{" "}
|
|
||||||
<span className="font-medium tabular-nums">{value[1]}</span>).
|
|
||||||
</FieldDescription>
|
|
||||||
<Slider
|
|
||||||
value={value}
|
|
||||||
onValueChange={setValue}
|
|
||||||
max={1000}
|
|
||||||
min={0}
|
|
||||||
step={10}
|
|
||||||
className="mt-2 w-full"
|
|
||||||
aria-label="Price Range"
|
|
||||||
/>
|
|
||||||
</Field>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
import { FieldSeparator } from "@/registry/new-york-v4/ui/field"
|
|
||||||
|
|
||||||
import { AppearanceSettings } from "./appearance-settings"
|
|
||||||
import { ButtonGroupDemo } from "./button-group-demo"
|
|
||||||
import { ButtonGroupInputGroup } from "./button-group-input-group"
|
|
||||||
import { ButtonGroupNested } from "./button-group-nested"
|
|
||||||
import { ButtonGroupPopover } from "./button-group-popover"
|
|
||||||
import { EmptyAvatarGroup } from "./empty-avatar-group"
|
|
||||||
import { FieldCheckbox } from "./field-checkbox"
|
|
||||||
import { FieldDemo } from "./field-demo"
|
|
||||||
import { FieldHear } from "./field-hear"
|
|
||||||
import { FieldSlider } from "./field-slider"
|
|
||||||
import { InputGroupButtonExample } from "./input-group-button"
|
|
||||||
import { InputGroupDemo } from "./input-group-demo"
|
|
||||||
import { ItemDemo } from "./item-demo"
|
|
||||||
import { NotionPromptForm } from "./notion-prompt-form"
|
|
||||||
import { SpinnerBadge } from "./spinner-badge"
|
|
||||||
import { SpinnerEmpty } from "./spinner-empty"
|
|
||||||
|
|
||||||
export function RootComponents() {
|
|
||||||
return (
|
|
||||||
<div className="theme-container mx-auto grid gap-8 py-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 xl:gap-6 2xl:gap-8">
|
|
||||||
<div className="flex flex-col gap-6 *:[div]:w-full *:[div]:max-w-full">
|
|
||||||
<FieldDemo />
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col gap-6 *:[div]:w-full *:[div]:max-w-full">
|
|
||||||
<EmptyAvatarGroup />
|
|
||||||
<SpinnerBadge />
|
|
||||||
<ButtonGroupInputGroup />
|
|
||||||
<FieldSlider />
|
|
||||||
<InputGroupDemo />
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col gap-6 *:[div]:w-full *:[div]:max-w-full">
|
|
||||||
<InputGroupButtonExample />
|
|
||||||
<ItemDemo />
|
|
||||||
<FieldSeparator className="my-4">Appearance Settings</FieldSeparator>
|
|
||||||
<AppearanceSettings />
|
|
||||||
</div>
|
|
||||||
<div className="order-first flex flex-col gap-6 lg:hidden xl:order-last xl:flex *:[div]:w-full *:[div]:max-w-full">
|
|
||||||
<NotionPromptForm />
|
|
||||||
<ButtonGroupDemo />
|
|
||||||
<FieldCheckbox />
|
|
||||||
<div className="flex justify-between gap-4">
|
|
||||||
<ButtonGroupNested />
|
|
||||||
<ButtonGroupPopover />
|
|
||||||
</div>
|
|
||||||
<FieldHear />
|
|
||||||
<SpinnerEmpty />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
"use client"
|
|
||||||
|
|
||||||
import * as React from "react"
|
|
||||||
import { IconInfoCircle, IconStar } from "@tabler/icons-react"
|
|
||||||
|
|
||||||
import {
|
|
||||||
InputGroup,
|
|
||||||
InputGroupAddon,
|
|
||||||
InputGroupButton,
|
|
||||||
InputGroupInput,
|
|
||||||
} from "@/registry/new-york-v4/ui/input-group"
|
|
||||||
import { Label } from "@/registry/new-york-v4/ui/label"
|
|
||||||
import {
|
|
||||||
Popover,
|
|
||||||
PopoverContent,
|
|
||||||
PopoverTrigger,
|
|
||||||
} from "@/registry/new-york-v4/ui/popover"
|
|
||||||
|
|
||||||
export function InputGroupButtonExample() {
|
|
||||||
const [isFavorite, setIsFavorite] = React.useState(false)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="grid w-full max-w-sm gap-6">
|
|
||||||
<Label htmlFor="input-secure-19" className="sr-only">
|
|
||||||
Input Secure
|
|
||||||
</Label>
|
|
||||||
<InputGroup className="[--radius:9999px]">
|
|
||||||
<InputGroupInput id="input-secure-19" className="!pl-0.5" />
|
|
||||||
<Popover>
|
|
||||||
<PopoverTrigger asChild>
|
|
||||||
<InputGroupAddon>
|
|
||||||
<InputGroupButton
|
|
||||||
variant="secondary"
|
|
||||||
size="icon-xs"
|
|
||||||
aria-label="Info"
|
|
||||||
>
|
|
||||||
<IconInfoCircle />
|
|
||||||
</InputGroupButton>
|
|
||||||
</InputGroupAddon>
|
|
||||||
</PopoverTrigger>
|
|
||||||
<PopoverContent
|
|
||||||
align="start"
|
|
||||||
alignOffset={10}
|
|
||||||
className="flex flex-col gap-1 rounded-xl text-sm"
|
|
||||||
>
|
|
||||||
<p className="font-medium">Your connection is not secure.</p>
|
|
||||||
<p>You should not enter any sensitive information on this site.</p>
|
|
||||||
</PopoverContent>
|
|
||||||
</Popover>
|
|
||||||
<InputGroupAddon className="text-muted-foreground !pl-1">
|
|
||||||
https://
|
|
||||||
</InputGroupAddon>
|
|
||||||
<InputGroupAddon align="inline-end">
|
|
||||||
<InputGroupButton
|
|
||||||
onClick={() => setIsFavorite(!isFavorite)}
|
|
||||||
size="icon-xs"
|
|
||||||
aria-label="Favorite"
|
|
||||||
>
|
|
||||||
<IconStar
|
|
||||||
data-favorite={isFavorite}
|
|
||||||
className="data-[favorite=true]:fill-primary data-[favorite=true]:stroke-primary"
|
|
||||||
/>
|
|
||||||
</InputGroupButton>
|
|
||||||
</InputGroupAddon>
|
|
||||||
</InputGroup>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,102 +0,0 @@
|
|||||||
import { IconCheck, IconInfoCircle, IconPlus } from "@tabler/icons-react"
|
|
||||||
import { ArrowUpIcon, Search } from "lucide-react"
|
|
||||||
|
|
||||||
import {
|
|
||||||
DropdownMenu,
|
|
||||||
DropdownMenuContent,
|
|
||||||
DropdownMenuItem,
|
|
||||||
DropdownMenuTrigger,
|
|
||||||
} from "@/registry/new-york-v4/ui/dropdown-menu"
|
|
||||||
import {
|
|
||||||
InputGroup,
|
|
||||||
InputGroupAddon,
|
|
||||||
InputGroupButton,
|
|
||||||
InputGroupInput,
|
|
||||||
InputGroupText,
|
|
||||||
InputGroupTextarea,
|
|
||||||
} from "@/registry/new-york-v4/ui/input-group"
|
|
||||||
import { Separator } from "@/registry/new-york-v4/ui/separator"
|
|
||||||
import {
|
|
||||||
Tooltip,
|
|
||||||
TooltipContent,
|
|
||||||
TooltipTrigger,
|
|
||||||
} from "@/registry/new-york-v4/ui/tooltip"
|
|
||||||
|
|
||||||
export function InputGroupDemo() {
|
|
||||||
return (
|
|
||||||
<div className="grid w-full max-w-sm gap-6">
|
|
||||||
<InputGroup>
|
|
||||||
<InputGroupInput placeholder="Search..." />
|
|
||||||
<InputGroupAddon>
|
|
||||||
<Search />
|
|
||||||
</InputGroupAddon>
|
|
||||||
<InputGroupAddon align="inline-end">12 results</InputGroupAddon>
|
|
||||||
</InputGroup>
|
|
||||||
<InputGroup>
|
|
||||||
<InputGroupInput placeholder="example.com" className="!pl-1" />
|
|
||||||
<InputGroupAddon>
|
|
||||||
<InputGroupText>https://</InputGroupText>
|
|
||||||
</InputGroupAddon>
|
|
||||||
<InputGroupAddon align="inline-end">
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<InputGroupButton
|
|
||||||
className="rounded-full"
|
|
||||||
size="icon-xs"
|
|
||||||
aria-label="Info"
|
|
||||||
>
|
|
||||||
<IconInfoCircle />
|
|
||||||
</InputGroupButton>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent>This is content in a tooltip.</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
</InputGroupAddon>
|
|
||||||
</InputGroup>
|
|
||||||
<InputGroup>
|
|
||||||
<InputGroupTextarea placeholder="Ask, Search or Chat..." />
|
|
||||||
<InputGroupAddon align="block-end">
|
|
||||||
<InputGroupButton
|
|
||||||
variant="outline"
|
|
||||||
className="rounded-full"
|
|
||||||
size="icon-xs"
|
|
||||||
aria-label="Add"
|
|
||||||
>
|
|
||||||
<IconPlus />
|
|
||||||
</InputGroupButton>
|
|
||||||
<DropdownMenu>
|
|
||||||
<DropdownMenuTrigger asChild>
|
|
||||||
<InputGroupButton variant="ghost">Auto</InputGroupButton>
|
|
||||||
</DropdownMenuTrigger>
|
|
||||||
<DropdownMenuContent
|
|
||||||
side="top"
|
|
||||||
align="start"
|
|
||||||
className="[--radius:0.95rem]"
|
|
||||||
>
|
|
||||||
<DropdownMenuItem>Auto</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem>Agent</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem>Manual</DropdownMenuItem>
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
<InputGroupText className="ml-auto">52% used</InputGroupText>
|
|
||||||
<Separator orientation="vertical" className="!h-4" />
|
|
||||||
<InputGroupButton
|
|
||||||
variant="default"
|
|
||||||
className="rounded-full"
|
|
||||||
size="icon-xs"
|
|
||||||
>
|
|
||||||
<ArrowUpIcon />
|
|
||||||
<span className="sr-only">Send</span>
|
|
||||||
</InputGroupButton>
|
|
||||||
</InputGroupAddon>
|
|
||||||
</InputGroup>
|
|
||||||
<InputGroup>
|
|
||||||
<InputGroupInput placeholder="@shadcn" />
|
|
||||||
<InputGroupAddon align="inline-end">
|
|
||||||
<div className="bg-primary text-foreground flex size-4 items-center justify-center rounded-full">
|
|
||||||
<IconCheck className="size-3 text-white" />
|
|
||||||
</div>
|
|
||||||
</InputGroupAddon>
|
|
||||||
</InputGroup>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
import {
|
|
||||||
IconBrandJavascript,
|
|
||||||
IconCopy,
|
|
||||||
IconCornerDownLeft,
|
|
||||||
IconRefresh,
|
|
||||||
} from "@tabler/icons-react"
|
|
||||||
|
|
||||||
import {
|
|
||||||
InputGroup,
|
|
||||||
InputGroupAddon,
|
|
||||||
InputGroupButton,
|
|
||||||
InputGroupText,
|
|
||||||
InputGroupTextarea,
|
|
||||||
} from "@/registry/new-york-v4/ui/input-group"
|
|
||||||
|
|
||||||
export function InputGroupTextareaExample() {
|
|
||||||
return (
|
|
||||||
<div className="grid w-full max-w-md gap-4">
|
|
||||||
<InputGroup>
|
|
||||||
<InputGroupTextarea
|
|
||||||
id="textarea-code-32"
|
|
||||||
placeholder="console.log('Hello, world!');"
|
|
||||||
className="min-h-[180px]"
|
|
||||||
/>
|
|
||||||
<InputGroupAddon align="block-end" className="border-t">
|
|
||||||
<InputGroupText>Line 1, Column 1</InputGroupText>
|
|
||||||
<InputGroupButton size="sm" className="ml-auto" variant="default">
|
|
||||||
Run <IconCornerDownLeft />
|
|
||||||
</InputGroupButton>
|
|
||||||
</InputGroupAddon>
|
|
||||||
<InputGroupAddon align="block-start" className="border-b">
|
|
||||||
<InputGroupText className="font-mono font-medium">
|
|
||||||
<IconBrandJavascript />
|
|
||||||
script.js
|
|
||||||
</InputGroupText>
|
|
||||||
<InputGroupButton className="ml-auto">
|
|
||||||
<IconRefresh />
|
|
||||||
</InputGroupButton>
|
|
||||||
<InputGroupButton variant="ghost">
|
|
||||||
<IconCopy />
|
|
||||||
</InputGroupButton>
|
|
||||||
</InputGroupAddon>
|
|
||||||
</InputGroup>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
import { Plus } from "lucide-react"
|
|
||||||
|
|
||||||
import {
|
|
||||||
Avatar,
|
|
||||||
AvatarFallback,
|
|
||||||
AvatarImage,
|
|
||||||
} from "@/registry/new-york-v4/ui/avatar"
|
|
||||||
import { Button } from "@/registry/new-york-v4/ui/button"
|
|
||||||
import {
|
|
||||||
Item,
|
|
||||||
ItemActions,
|
|
||||||
ItemContent,
|
|
||||||
ItemDescription,
|
|
||||||
ItemMedia,
|
|
||||||
ItemTitle,
|
|
||||||
} from "@/registry/new-york-v4/ui/item"
|
|
||||||
|
|
||||||
export function ItemAvatar() {
|
|
||||||
return (
|
|
||||||
<div className="flex w-full max-w-lg flex-col gap-6">
|
|
||||||
<Item variant="outline" className="hidden">
|
|
||||||
<ItemMedia>
|
|
||||||
<Avatar className="size-10">
|
|
||||||
<AvatarImage src="https://github.com/maxleiter.png" />
|
|
||||||
<AvatarFallback>LR</AvatarFallback>
|
|
||||||
</Avatar>
|
|
||||||
</ItemMedia>
|
|
||||||
<ItemContent>
|
|
||||||
<ItemTitle>Max Leiter</ItemTitle>
|
|
||||||
<ItemDescription>Last seen 5 months ago</ItemDescription>
|
|
||||||
</ItemContent>
|
|
||||||
<ItemActions>
|
|
||||||
<Button
|
|
||||||
size="icon-sm"
|
|
||||||
variant="outline"
|
|
||||||
className="rounded-full"
|
|
||||||
aria-label="Invite"
|
|
||||||
>
|
|
||||||
<Plus />
|
|
||||||
</Button>
|
|
||||||
</ItemActions>
|
|
||||||
</Item>
|
|
||||||
<Item variant="outline">
|
|
||||||
<ItemMedia>
|
|
||||||
<div className="*:data-[slot=avatar]:ring-background flex -space-x-2 *:data-[slot=avatar]:ring-2 *:data-[slot=avatar]:grayscale">
|
|
||||||
<Avatar className="hidden sm:flex">
|
|
||||||
<AvatarImage src="https://github.com/shadcn.png" alt="@shadcn" />
|
|
||||||
<AvatarFallback>CN</AvatarFallback>
|
|
||||||
</Avatar>
|
|
||||||
<Avatar className="hidden sm:flex">
|
|
||||||
<AvatarImage
|
|
||||||
src="https://github.com/maxleiter.png"
|
|
||||||
alt="@maxleiter"
|
|
||||||
/>
|
|
||||||
<AvatarFallback>LR</AvatarFallback>
|
|
||||||
</Avatar>
|
|
||||||
<Avatar>
|
|
||||||
<AvatarImage
|
|
||||||
src="https://github.com/evilrabbit.png"
|
|
||||||
alt="@evilrabbit"
|
|
||||||
/>
|
|
||||||
<AvatarFallback>ER</AvatarFallback>
|
|
||||||
</Avatar>
|
|
||||||
</div>
|
|
||||||
</ItemMedia>
|
|
||||||
<ItemContent>
|
|
||||||
<ItemTitle>No Team Members</ItemTitle>
|
|
||||||
<ItemDescription>Invite your team to collaborate.</ItemDescription>
|
|
||||||
</ItemContent>
|
|
||||||
<ItemActions>
|
|
||||||
<Button size="sm" variant="outline">
|
|
||||||
Invite
|
|
||||||
</Button>
|
|
||||||
</ItemActions>
|
|
||||||
</Item>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
import { BadgeCheckIcon, ChevronRightIcon } from "lucide-react"
|
|
||||||
|
|
||||||
import { Button } from "@/registry/new-york-v4/ui/button"
|
|
||||||
import {
|
|
||||||
Item,
|
|
||||||
ItemActions,
|
|
||||||
ItemContent,
|
|
||||||
ItemDescription,
|
|
||||||
ItemMedia,
|
|
||||||
ItemTitle,
|
|
||||||
} from "@/registry/new-york-v4/ui/item"
|
|
||||||
|
|
||||||
export function ItemDemo() {
|
|
||||||
return (
|
|
||||||
<div className="flex w-full max-w-md flex-col gap-6">
|
|
||||||
<Item variant="outline">
|
|
||||||
<ItemContent>
|
|
||||||
<ItemTitle>Two-factor authentication</ItemTitle>
|
|
||||||
<ItemDescription className="text-pretty xl:hidden 2xl:block">
|
|
||||||
Verify via email or phone number.
|
|
||||||
</ItemDescription>
|
|
||||||
</ItemContent>
|
|
||||||
<ItemActions>
|
|
||||||
<Button size="sm">Enable</Button>
|
|
||||||
</ItemActions>
|
|
||||||
</Item>
|
|
||||||
<Item variant="outline" size="sm" asChild>
|
|
||||||
<a href="#">
|
|
||||||
<ItemMedia>
|
|
||||||
<BadgeCheckIcon className="size-5" />
|
|
||||||
</ItemMedia>
|
|
||||||
<ItemContent>
|
|
||||||
<ItemTitle>Your profile has been verified.</ItemTitle>
|
|
||||||
</ItemContent>
|
|
||||||
<ItemActions>
|
|
||||||
<ChevronRightIcon className="size-4" />
|
|
||||||
</ItemActions>
|
|
||||||
</a>
|
|
||||||
</Item>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,456 +0,0 @@
|
|||||||
"use client"
|
|
||||||
|
|
||||||
import { useMemo, useState } from "react"
|
|
||||||
import {
|
|
||||||
IconApps,
|
|
||||||
IconArrowUp,
|
|
||||||
IconAt,
|
|
||||||
IconBook,
|
|
||||||
IconCircleDashedPlus,
|
|
||||||
IconPaperclip,
|
|
||||||
IconPlus,
|
|
||||||
IconWorld,
|
|
||||||
IconX,
|
|
||||||
} from "@tabler/icons-react"
|
|
||||||
|
|
||||||
import {
|
|
||||||
Avatar,
|
|
||||||
AvatarFallback,
|
|
||||||
AvatarImage,
|
|
||||||
} from "@/registry/new-york-v4/ui/avatar"
|
|
||||||
import { Badge } from "@/registry/new-york-v4/ui/badge"
|
|
||||||
import {
|
|
||||||
Command,
|
|
||||||
CommandEmpty,
|
|
||||||
CommandGroup,
|
|
||||||
CommandInput,
|
|
||||||
CommandItem,
|
|
||||||
CommandList,
|
|
||||||
} from "@/registry/new-york-v4/ui/command"
|
|
||||||
import {
|
|
||||||
DropdownMenu,
|
|
||||||
DropdownMenuCheckboxItem,
|
|
||||||
DropdownMenuContent,
|
|
||||||
DropdownMenuGroup,
|
|
||||||
DropdownMenuItem,
|
|
||||||
DropdownMenuLabel,
|
|
||||||
DropdownMenuSeparator,
|
|
||||||
DropdownMenuSub,
|
|
||||||
DropdownMenuSubContent,
|
|
||||||
DropdownMenuSubTrigger,
|
|
||||||
DropdownMenuTrigger,
|
|
||||||
} from "@/registry/new-york-v4/ui/dropdown-menu"
|
|
||||||
import { Field, FieldLabel } from "@/registry/new-york-v4/ui/field"
|
|
||||||
import {
|
|
||||||
InputGroup,
|
|
||||||
InputGroupAddon,
|
|
||||||
InputGroupButton,
|
|
||||||
InputGroupTextarea,
|
|
||||||
} from "@/registry/new-york-v4/ui/input-group"
|
|
||||||
import {
|
|
||||||
Popover,
|
|
||||||
PopoverContent,
|
|
||||||
PopoverTrigger,
|
|
||||||
} from "@/registry/new-york-v4/ui/popover"
|
|
||||||
import { Switch } from "@/registry/new-york-v4/ui/switch"
|
|
||||||
import {
|
|
||||||
Tooltip,
|
|
||||||
TooltipContent,
|
|
||||||
TooltipTrigger,
|
|
||||||
} from "@/registry/new-york-v4/ui/tooltip"
|
|
||||||
|
|
||||||
const SAMPLE_DATA = {
|
|
||||||
mentionable: [
|
|
||||||
{
|
|
||||||
type: "page",
|
|
||||||
title: "Meeting Notes",
|
|
||||||
image: "📝",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "page",
|
|
||||||
title: "Project Dashboard",
|
|
||||||
image: "📊",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "page",
|
|
||||||
title: "Ideas & Brainstorming",
|
|
||||||
image: "💡",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "page",
|
|
||||||
title: "Calendar & Events",
|
|
||||||
image: "📅",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "page",
|
|
||||||
title: "Documentation",
|
|
||||||
image: "📚",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "page",
|
|
||||||
title: "Goals & Objectives",
|
|
||||||
image: "🎯",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "page",
|
|
||||||
title: "Budget Planning",
|
|
||||||
image: "💰",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "page",
|
|
||||||
title: "Team Directory",
|
|
||||||
image: "👥",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "page",
|
|
||||||
title: "Technical Specs",
|
|
||||||
image: "🔧",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "page",
|
|
||||||
title: "Analytics Report",
|
|
||||||
image: "📈",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "user",
|
|
||||||
title: "shadcn",
|
|
||||||
image: "https://github.com/shadcn.png",
|
|
||||||
workspace: "Workspace",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "user",
|
|
||||||
title: "maxleiter",
|
|
||||||
image: "https://github.com/maxleiter.png",
|
|
||||||
workspace: "Workspace",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "user",
|
|
||||||
title: "evilrabbit",
|
|
||||||
image: "https://github.com/evilrabbit.png",
|
|
||||||
workspace: "Workspace",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
models: [
|
|
||||||
{
|
|
||||||
name: "Auto",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Agent Mode",
|
|
||||||
badge: "Beta",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Plan Mode",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
||||||
function MentionableIcon({
|
|
||||||
item,
|
|
||||||
}: {
|
|
||||||
item: (typeof SAMPLE_DATA.mentionable)[0]
|
|
||||||
}) {
|
|
||||||
return item.type === "page" ? (
|
|
||||||
<span className="flex size-4 items-center justify-center">
|
|
||||||
{item.image}
|
|
||||||
</span>
|
|
||||||
) : (
|
|
||||||
<Avatar className="size-4">
|
|
||||||
<AvatarImage src={item.image} />
|
|
||||||
<AvatarFallback>{item.title[0]}</AvatarFallback>
|
|
||||||
</Avatar>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function NotionPromptForm() {
|
|
||||||
const [mentions, setMentions] = useState<string[]>([])
|
|
||||||
const [mentionPopoverOpen, setMentionPopoverOpen] = useState(false)
|
|
||||||
const [modelPopoverOpen, setModelPopoverOpen] = useState(false)
|
|
||||||
const [selectedModel, setSelectedModel] = useState<
|
|
||||||
(typeof SAMPLE_DATA.models)[0]
|
|
||||||
>(SAMPLE_DATA.models[0])
|
|
||||||
const [scopeMenuOpen, setScopeMenuOpen] = useState(false)
|
|
||||||
|
|
||||||
const grouped = useMemo(() => {
|
|
||||||
return SAMPLE_DATA.mentionable.reduce(
|
|
||||||
(acc, item) => {
|
|
||||||
const isAvailable = !mentions.includes(item.title)
|
|
||||||
|
|
||||||
if (isAvailable) {
|
|
||||||
if (!acc[item.type]) {
|
|
||||||
acc[item.type] = []
|
|
||||||
}
|
|
||||||
acc[item.type].push(item)
|
|
||||||
}
|
|
||||||
return acc
|
|
||||||
},
|
|
||||||
{} as Record<string, typeof SAMPLE_DATA.mentionable>
|
|
||||||
)
|
|
||||||
}, [mentions])
|
|
||||||
|
|
||||||
const hasMentions = mentions.length > 0
|
|
||||||
|
|
||||||
return (
|
|
||||||
<form className="[--radius:1.2rem]">
|
|
||||||
<Field>
|
|
||||||
<FieldLabel htmlFor="notion-prompt" className="sr-only">
|
|
||||||
Prompt
|
|
||||||
</FieldLabel>
|
|
||||||
<InputGroup>
|
|
||||||
<InputGroupTextarea
|
|
||||||
id="notion-prompt"
|
|
||||||
placeholder="Ask, search, or make anything..."
|
|
||||||
/>
|
|
||||||
<InputGroupAddon align="block-start">
|
|
||||||
<Popover
|
|
||||||
open={mentionPopoverOpen}
|
|
||||||
onOpenChange={setMentionPopoverOpen}
|
|
||||||
>
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger
|
|
||||||
asChild
|
|
||||||
onFocusCapture={(e) => e.stopPropagation()}
|
|
||||||
>
|
|
||||||
<PopoverTrigger asChild>
|
|
||||||
<InputGroupButton
|
|
||||||
variant="outline"
|
|
||||||
size={!hasMentions ? "sm" : "icon-sm"}
|
|
||||||
className="rounded-full transition-transform"
|
|
||||||
>
|
|
||||||
<IconAt /> {!hasMentions && "Add context"}
|
|
||||||
</InputGroupButton>
|
|
||||||
</PopoverTrigger>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent>Mention a person, page, or date</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
<PopoverContent className="p-0 [--radius:1.2rem]" align="start">
|
|
||||||
<Command>
|
|
||||||
<CommandInput placeholder="Search pages..." />
|
|
||||||
<CommandList>
|
|
||||||
<CommandEmpty>No pages found</CommandEmpty>
|
|
||||||
{Object.entries(grouped).map(([type, items]) => (
|
|
||||||
<CommandGroup
|
|
||||||
key={type}
|
|
||||||
heading={type === "page" ? "Pages" : "Users"}
|
|
||||||
>
|
|
||||||
{items.map((item) => (
|
|
||||||
<CommandItem
|
|
||||||
key={item.title}
|
|
||||||
value={item.title}
|
|
||||||
onSelect={(currentValue) => {
|
|
||||||
setMentions((prev) => [...prev, currentValue])
|
|
||||||
setMentionPopoverOpen(false)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<MentionableIcon item={item} />
|
|
||||||
{item.title}
|
|
||||||
</CommandItem>
|
|
||||||
))}
|
|
||||||
</CommandGroup>
|
|
||||||
))}
|
|
||||||
</CommandList>
|
|
||||||
</Command>
|
|
||||||
</PopoverContent>
|
|
||||||
</Popover>
|
|
||||||
<div className="no-scrollbar -m-1.5 flex gap-1 overflow-y-auto p-1.5">
|
|
||||||
{mentions.map((mention) => {
|
|
||||||
const item = SAMPLE_DATA.mentionable.find(
|
|
||||||
(item) => item.title === mention
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!item) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<InputGroupButton
|
|
||||||
key={mention}
|
|
||||||
size="sm"
|
|
||||||
variant="secondary"
|
|
||||||
className="rounded-full !pl-2"
|
|
||||||
onClick={() => {
|
|
||||||
setMentions((prev) => prev.filter((m) => m !== mention))
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<MentionableIcon item={item} />
|
|
||||||
{item.title}
|
|
||||||
<IconX />
|
|
||||||
</InputGroupButton>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</InputGroupAddon>
|
|
||||||
<InputGroupAddon align="block-end" className="gap-1">
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<InputGroupButton
|
|
||||||
size="icon-sm"
|
|
||||||
className="rounded-full"
|
|
||||||
aria-label="Attach file"
|
|
||||||
>
|
|
||||||
<IconPaperclip />
|
|
||||||
</InputGroupButton>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent>Attach file</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
<DropdownMenu
|
|
||||||
open={modelPopoverOpen}
|
|
||||||
onOpenChange={setModelPopoverOpen}
|
|
||||||
>
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<DropdownMenuTrigger asChild>
|
|
||||||
<InputGroupButton size="sm" className="rounded-full">
|
|
||||||
{selectedModel.name}
|
|
||||||
</InputGroupButton>
|
|
||||||
</DropdownMenuTrigger>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent>Select AI model</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
<DropdownMenuContent
|
|
||||||
side="top"
|
|
||||||
align="start"
|
|
||||||
className="[--radius:1rem]"
|
|
||||||
>
|
|
||||||
<DropdownMenuGroup className="w-42">
|
|
||||||
<DropdownMenuLabel className="text-muted-foreground text-xs">
|
|
||||||
Select Agent Mode
|
|
||||||
</DropdownMenuLabel>
|
|
||||||
{SAMPLE_DATA.models.map((model) => (
|
|
||||||
<DropdownMenuCheckboxItem
|
|
||||||
key={model.name}
|
|
||||||
checked={model.name === selectedModel.name}
|
|
||||||
onCheckedChange={(checked) => {
|
|
||||||
if (checked) {
|
|
||||||
setSelectedModel(model)
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
className="pl-2 *:[span:first-child]:right-2 *:[span:first-child]:left-auto"
|
|
||||||
>
|
|
||||||
{model.name}
|
|
||||||
{model.badge && (
|
|
||||||
<Badge
|
|
||||||
variant="secondary"
|
|
||||||
className="h-5 rounded-sm bg-blue-100 px-1 text-xs text-blue-800 dark:bg-blue-900 dark:text-blue-100"
|
|
||||||
>
|
|
||||||
{model.badge}
|
|
||||||
</Badge>
|
|
||||||
)}
|
|
||||||
</DropdownMenuCheckboxItem>
|
|
||||||
))}
|
|
||||||
</DropdownMenuGroup>
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
<DropdownMenu open={scopeMenuOpen} onOpenChange={setScopeMenuOpen}>
|
|
||||||
<DropdownMenuTrigger asChild>
|
|
||||||
<InputGroupButton size="sm" className="rounded-full">
|
|
||||||
<IconWorld /> All Sources
|
|
||||||
</InputGroupButton>
|
|
||||||
</DropdownMenuTrigger>
|
|
||||||
<DropdownMenuContent
|
|
||||||
side="top"
|
|
||||||
align="end"
|
|
||||||
className="[--radius:1rem]"
|
|
||||||
>
|
|
||||||
<DropdownMenuGroup>
|
|
||||||
<DropdownMenuItem
|
|
||||||
asChild
|
|
||||||
onSelect={(e) => e.preventDefault()}
|
|
||||||
>
|
|
||||||
<label htmlFor="web-search">
|
|
||||||
<IconWorld /> Web Search{" "}
|
|
||||||
<Switch
|
|
||||||
id="web-search"
|
|
||||||
className="ml-auto"
|
|
||||||
defaultChecked
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</DropdownMenuGroup>
|
|
||||||
<DropdownMenuSeparator />
|
|
||||||
<DropdownMenuGroup>
|
|
||||||
<DropdownMenuItem
|
|
||||||
asChild
|
|
||||||
onSelect={(e) => e.preventDefault()}
|
|
||||||
>
|
|
||||||
<label htmlFor="apps">
|
|
||||||
<IconApps /> Apps and Integrations
|
|
||||||
<Switch id="apps" className="ml-auto" defaultChecked />
|
|
||||||
</label>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem>
|
|
||||||
<IconCircleDashedPlus /> All Sources I can access
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuSub>
|
|
||||||
<DropdownMenuSubTrigger>
|
|
||||||
<Avatar className="size-4">
|
|
||||||
<AvatarImage src="https://github.com/shadcn.png" />
|
|
||||||
<AvatarFallback>CN</AvatarFallback>
|
|
||||||
</Avatar>
|
|
||||||
shadcn
|
|
||||||
</DropdownMenuSubTrigger>
|
|
||||||
<DropdownMenuSubContent className="w-72 p-0 [--radius:1rem]">
|
|
||||||
<Command>
|
|
||||||
<CommandInput
|
|
||||||
placeholder="Find or use knowledge in..."
|
|
||||||
autoFocus
|
|
||||||
/>
|
|
||||||
<CommandList>
|
|
||||||
<CommandEmpty>No knowledge found</CommandEmpty>
|
|
||||||
<CommandGroup>
|
|
||||||
{SAMPLE_DATA.mentionable
|
|
||||||
.filter((item) => item.type === "user")
|
|
||||||
.map((user) => (
|
|
||||||
<CommandItem
|
|
||||||
key={user.title}
|
|
||||||
value={user.title}
|
|
||||||
onSelect={() => {
|
|
||||||
// Handle user selection here
|
|
||||||
console.log("Selected user:", user.title)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Avatar className="size-4">
|
|
||||||
<AvatarImage src={user.image} />
|
|
||||||
<AvatarFallback>
|
|
||||||
{user.title[0]}
|
|
||||||
</AvatarFallback>
|
|
||||||
</Avatar>
|
|
||||||
{user.title}{" "}
|
|
||||||
<span className="text-muted-foreground">
|
|
||||||
- {user.workspace}
|
|
||||||
</span>
|
|
||||||
</CommandItem>
|
|
||||||
))}
|
|
||||||
</CommandGroup>
|
|
||||||
</CommandList>
|
|
||||||
</Command>
|
|
||||||
</DropdownMenuSubContent>
|
|
||||||
</DropdownMenuSub>
|
|
||||||
<DropdownMenuItem>
|
|
||||||
<IconBook /> Help Center
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</DropdownMenuGroup>
|
|
||||||
<DropdownMenuSeparator />
|
|
||||||
<DropdownMenuGroup>
|
|
||||||
<DropdownMenuItem>
|
|
||||||
<IconPlus /> Connect Apps
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuLabel className="text-muted-foreground text-xs">
|
|
||||||
We'll only search in the sources selected here.
|
|
||||||
</DropdownMenuLabel>
|
|
||||||
</DropdownMenuGroup>
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
<InputGroupButton
|
|
||||||
aria-label="Send"
|
|
||||||
className="ml-auto rounded-full"
|
|
||||||
variant="default"
|
|
||||||
size="icon-sm"
|
|
||||||
>
|
|
||||||
<IconArrowUp />
|
|
||||||
</InputGroupButton>
|
|
||||||
</InputGroupAddon>
|
|
||||||
</InputGroup>
|
|
||||||
</Field>
|
|
||||||
</form>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
import { Badge } from "@/registry/new-york-v4/ui/badge"
|
|
||||||
import { Spinner } from "@/registry/new-york-v4/ui/spinner"
|
|
||||||
|
|
||||||
export function SpinnerBadge() {
|
|
||||||
return (
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<Badge>
|
|
||||||
<Spinner />
|
|
||||||
Syncing
|
|
||||||
</Badge>
|
|
||||||
<Badge variant="secondary">
|
|
||||||
<Spinner />
|
|
||||||
Updating
|
|
||||||
</Badge>
|
|
||||||
<Badge variant="outline">
|
|
||||||
<Spinner />
|
|
||||||
Loading
|
|
||||||
</Badge>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
import { Button } from "@/registry/new-york-v4/ui/button"
|
|
||||||
import {
|
|
||||||
Empty,
|
|
||||||
EmptyContent,
|
|
||||||
EmptyDescription,
|
|
||||||
EmptyHeader,
|
|
||||||
EmptyMedia,
|
|
||||||
EmptyTitle,
|
|
||||||
} from "@/registry/new-york-v4/ui/empty"
|
|
||||||
import { Spinner } from "@/registry/new-york-v4/ui/spinner"
|
|
||||||
|
|
||||||
export function SpinnerEmpty() {
|
|
||||||
return (
|
|
||||||
<Empty className="w-full border md:p-6">
|
|
||||||
<EmptyHeader>
|
|
||||||
<EmptyMedia variant="icon">
|
|
||||||
<Spinner />
|
|
||||||
</EmptyMedia>
|
|
||||||
<EmptyTitle>Processing your request</EmptyTitle>
|
|
||||||
<EmptyDescription>
|
|
||||||
Please wait while we process your request. Do not refresh the page.
|
|
||||||
</EmptyDescription>
|
|
||||||
</EmptyHeader>
|
|
||||||
<EmptyContent>
|
|
||||||
<Button variant="outline" size="sm">
|
|
||||||
Cancel
|
|
||||||
</Button>
|
|
||||||
</EmptyContent>
|
|
||||||
</Empty>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,102 +0,0 @@
|
|||||||
import { type Metadata } from "next"
|
|
||||||
import Image from "next/image"
|
|
||||||
import Link from "next/link"
|
|
||||||
import { PlusSignIcon } from "@hugeicons/core-free-icons"
|
|
||||||
import { HugeiconsIcon } from "@hugeicons/react"
|
|
||||||
|
|
||||||
import { Announcement } from "@/components/announcement"
|
|
||||||
import { ExamplesNav } from "@/components/examples-nav"
|
|
||||||
import {
|
|
||||||
PageActions,
|
|
||||||
PageHeader,
|
|
||||||
PageHeaderDescription,
|
|
||||||
PageHeaderHeading,
|
|
||||||
} from "@/components/page-header"
|
|
||||||
import { PageNav } from "@/components/page-nav"
|
|
||||||
import { ThemeSelector } from "@/components/theme-selector"
|
|
||||||
import { Button } from "@/registry/new-york-v4/ui/button"
|
|
||||||
|
|
||||||
import { RootComponents } from "./components"
|
|
||||||
|
|
||||||
const title = "The Foundation for your Design System"
|
|
||||||
const description =
|
|
||||||
"A set of beautifully designed components that you can customize, extend, and build on. Start here then make it your own. Open Source. Open Code."
|
|
||||||
|
|
||||||
export const dynamic = "force-static"
|
|
||||||
export const revalidate = false
|
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
|
||||||
title,
|
|
||||||
description,
|
|
||||||
openGraph: {
|
|
||||||
images: [
|
|
||||||
{
|
|
||||||
url: `/og?title=${encodeURIComponent(
|
|
||||||
title
|
|
||||||
)}&description=${encodeURIComponent(description)}`,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
twitter: {
|
|
||||||
card: "summary_large_image",
|
|
||||||
images: [
|
|
||||||
{
|
|
||||||
url: `/og?title=${encodeURIComponent(
|
|
||||||
title
|
|
||||||
)}&description=${encodeURIComponent(description)}`,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function IndexPage() {
|
|
||||||
return (
|
|
||||||
<div className="flex flex-1 flex-col">
|
|
||||||
<PageHeader>
|
|
||||||
<Announcement />
|
|
||||||
<PageHeaderHeading className="max-w-4xl">{title}</PageHeaderHeading>
|
|
||||||
<PageHeaderDescription>{description}</PageHeaderDescription>
|
|
||||||
<PageActions>
|
|
||||||
<Button asChild size="sm" className="h-[31px] rounded-lg">
|
|
||||||
<Link href="/create">
|
|
||||||
<HugeiconsIcon icon={PlusSignIcon} />
|
|
||||||
New Project
|
|
||||||
</Link>
|
|
||||||
</Button>
|
|
||||||
<Button asChild size="sm" variant="ghost" className="rounded-lg">
|
|
||||||
<Link href="/docs/components">View Components</Link>
|
|
||||||
</Button>
|
|
||||||
</PageActions>
|
|
||||||
</PageHeader>
|
|
||||||
<PageNav className="hidden md:flex">
|
|
||||||
<ExamplesNav className="[&>a:first-child]:text-primary flex-1 overflow-hidden" />
|
|
||||||
<ThemeSelector className="mr-4 hidden md:flex" />
|
|
||||||
</PageNav>
|
|
||||||
<div className="container-wrapper section-soft flex-1 pb-6">
|
|
||||||
<div className="container overflow-hidden">
|
|
||||||
<section className="border-border/50 -mx-4 w-[160vw] overflow-hidden rounded-lg border md:hidden md:w-[150vw]">
|
|
||||||
<Image
|
|
||||||
src="/r/styles/new-york-v4/dashboard-01-light.png"
|
|
||||||
width={1400}
|
|
||||||
height={875}
|
|
||||||
alt="Dashboard"
|
|
||||||
className="block dark:hidden"
|
|
||||||
priority
|
|
||||||
/>
|
|
||||||
<Image
|
|
||||||
src="/r/styles/new-york-v4/dashboard-01-dark.png"
|
|
||||||
width={1400}
|
|
||||||
height={875}
|
|
||||||
alt="Dashboard"
|
|
||||||
className="hidden dark:block"
|
|
||||||
priority
|
|
||||||
/>
|
|
||||||
</section>
|
|
||||||
<section className="theme-container hidden md:block">
|
|
||||||
<RootComponents />
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
import { getAllBlockIds } from "@/lib/blocks"
|
|
||||||
import { registryCategories } from "@/lib/categories"
|
|
||||||
import { BlockDisplay } from "@/components/block-display"
|
|
||||||
import { getActiveStyle } from "@/registry/_legacy-styles"
|
|
||||||
|
|
||||||
export const revalidate = false
|
|
||||||
export const dynamic = "force-static"
|
|
||||||
export const dynamicParams = false
|
|
||||||
|
|
||||||
export async function generateStaticParams() {
|
|
||||||
return registryCategories.map((category) => ({
|
|
||||||
categories: [category.slug],
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
export default async function BlocksPage({
|
|
||||||
params,
|
|
||||||
}: {
|
|
||||||
params: Promise<{ categories?: string[] }>
|
|
||||||
}) {
|
|
||||||
const [{ categories = [] }, activeStyle] = await Promise.all([
|
|
||||||
params,
|
|
||||||
getActiveStyle(),
|
|
||||||
])
|
|
||||||
const blocks = await getAllBlockIds(["registry:block"], categories)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="flex flex-col gap-12 md:gap-24">
|
|
||||||
{blocks.map((name) => (
|
|
||||||
<BlockDisplay name={name} key={name} styleName={activeStyle.name} />
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
import { type Metadata } from "next"
|
|
||||||
import Link from "next/link"
|
|
||||||
|
|
||||||
import { Announcement } from "@/components/announcement"
|
|
||||||
import { BlocksNav } from "@/components/blocks-nav"
|
|
||||||
import {
|
|
||||||
PageActions,
|
|
||||||
PageHeader,
|
|
||||||
PageHeaderDescription,
|
|
||||||
PageHeaderHeading,
|
|
||||||
} from "@/components/page-header"
|
|
||||||
import { PageNav } from "@/components/page-nav"
|
|
||||||
import { Button } from "@/registry/new-york-v4/ui/button"
|
|
||||||
|
|
||||||
const title = "Building Blocks for the Web"
|
|
||||||
const description =
|
|
||||||
"Clean, modern building blocks. Copy and paste into your apps. Works with all React frameworks. Open Source. Free forever."
|
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
|
||||||
title,
|
|
||||||
description,
|
|
||||||
openGraph: {
|
|
||||||
images: [
|
|
||||||
{
|
|
||||||
url: `/og?title=${encodeURIComponent(
|
|
||||||
title
|
|
||||||
)}&description=${encodeURIComponent(description)}`,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
twitter: {
|
|
||||||
card: "summary_large_image",
|
|
||||||
images: [
|
|
||||||
{
|
|
||||||
url: `/og?title=${encodeURIComponent(
|
|
||||||
title
|
|
||||||
)}&description=${encodeURIComponent(description)}`,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function BlocksLayout({
|
|
||||||
children,
|
|
||||||
}: {
|
|
||||||
children: React.ReactNode
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<PageHeader>
|
|
||||||
<Announcement />
|
|
||||||
<PageHeaderHeading>{title}</PageHeaderHeading>
|
|
||||||
<PageHeaderDescription>{description}</PageHeaderDescription>
|
|
||||||
<PageActions>
|
|
||||||
<Button asChild size="sm">
|
|
||||||
<a href="#blocks">Browse Blocks</a>
|
|
||||||
</Button>
|
|
||||||
<Button asChild variant="ghost" size="sm">
|
|
||||||
<Link href="/docs/blocks">Add a block</Link>
|
|
||||||
</Button>
|
|
||||||
</PageActions>
|
|
||||||
</PageHeader>
|
|
||||||
<PageNav id="blocks">
|
|
||||||
<BlocksNav />
|
|
||||||
<Button
|
|
||||||
asChild
|
|
||||||
variant="secondary"
|
|
||||||
size="sm"
|
|
||||||
className="mr-7 hidden shadow-none lg:flex"
|
|
||||||
>
|
|
||||||
<Link href="/blocks/sidebar">Browse all blocks</Link>
|
|
||||||
</Button>
|
|
||||||
</PageNav>
|
|
||||||
<div className="container-wrapper section-soft flex-1 md:py-12">
|
|
||||||
<div className="container">{children}</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
import Link from "next/link"
|
|
||||||
|
|
||||||
import { BlockDisplay } from "@/components/block-display"
|
|
||||||
import { getActiveStyle } from "@/registry/_legacy-styles"
|
|
||||||
import { Button } from "@/registry/new-york-v4/ui/button"
|
|
||||||
|
|
||||||
export const dynamic = "force-static"
|
|
||||||
export const revalidate = false
|
|
||||||
|
|
||||||
const FEATURED_BLOCKS = [
|
|
||||||
"dashboard-01",
|
|
||||||
"sidebar-07",
|
|
||||||
"sidebar-03",
|
|
||||||
"login-03",
|
|
||||||
"login-04",
|
|
||||||
]
|
|
||||||
|
|
||||||
export default async function BlocksPage() {
|
|
||||||
const activeStyle = await getActiveStyle()
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="flex flex-col gap-12 md:gap-24">
|
|
||||||
{FEATURED_BLOCKS.map((name) => (
|
|
||||||
<BlockDisplay name={name} key={name} styleName={activeStyle.name} />
|
|
||||||
))}
|
|
||||||
<div className="container-wrapper">
|
|
||||||
<div className="container flex justify-center py-6">
|
|
||||||
<Button asChild variant="outline">
|
|
||||||
<Link href="/blocks/sidebar">Browse more blocks</Link>
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
import * as React from "react"
|
|
||||||
import { notFound } from "next/navigation"
|
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
|
||||||
import { ChartDisplay } from "@/components/chart-display"
|
|
||||||
import { getActiveStyle } from "@/registry/_legacy-styles"
|
|
||||||
import { charts } from "@/app/(app)/charts/charts"
|
|
||||||
|
|
||||||
export const revalidate = false
|
|
||||||
export const dynamic = "force-static"
|
|
||||||
export const dynamicParams = false
|
|
||||||
|
|
||||||
interface ChartPageProps {
|
|
||||||
params: Promise<{
|
|
||||||
type: string
|
|
||||||
}>
|
|
||||||
}
|
|
||||||
|
|
||||||
const chartTypes = [
|
|
||||||
"area",
|
|
||||||
"bar",
|
|
||||||
"line",
|
|
||||||
"pie",
|
|
||||||
"radar",
|
|
||||||
"radial",
|
|
||||||
"tooltip",
|
|
||||||
] as const
|
|
||||||
type ChartType = (typeof chartTypes)[number]
|
|
||||||
|
|
||||||
export async function generateStaticParams() {
|
|
||||||
return chartTypes.map((type) => ({
|
|
||||||
type,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
export default async function ChartPage({ params }: ChartPageProps) {
|
|
||||||
const { type } = await params
|
|
||||||
|
|
||||||
if (!chartTypes.includes(type as ChartType)) {
|
|
||||||
return notFound()
|
|
||||||
}
|
|
||||||
|
|
||||||
const chartType = type as ChartType
|
|
||||||
const chartList = charts[chartType]
|
|
||||||
const activeStyle = await getActiveStyle()
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="grid flex-1 gap-12 lg:gap-24">
|
|
||||||
<h2 className="sr-only">
|
|
||||||
{type.charAt(0).toUpperCase() + type.slice(1)} Charts
|
|
||||||
</h2>
|
|
||||||
<div className="grid flex-1 scroll-mt-20 items-stretch gap-10 md:grid-cols-2 md:gap-6 lg:grid-cols-3 xl:gap-10">
|
|
||||||
{Array.from({ length: 12 }).map((_, index) => {
|
|
||||||
const chart = chartList[index]
|
|
||||||
return chart ? (
|
|
||||||
<ChartDisplay
|
|
||||||
key={chart.id}
|
|
||||||
name={chart.id}
|
|
||||||
styleName={activeStyle.name}
|
|
||||||
className={cn(chart.fullWidth && "md:col-span-2 lg:col-span-3")}
|
|
||||||
>
|
|
||||||
<chart.component />
|
|
||||||
</ChartDisplay>
|
|
||||||
) : (
|
|
||||||
<div
|
|
||||||
key={`empty-${index}`}
|
|
||||||
className="hidden aspect-square w-full rounded-lg border border-dashed xl:block"
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,275 +0,0 @@
|
|||||||
import type * as React from "react"
|
|
||||||
|
|
||||||
import { ChartAreaAxes } from "@/registry/new-york-v4/charts/chart-area-axes"
|
|
||||||
import { ChartAreaDefault } from "@/registry/new-york-v4/charts/chart-area-default"
|
|
||||||
import { ChartAreaGradient } from "@/registry/new-york-v4/charts/chart-area-gradient"
|
|
||||||
import { ChartAreaIcons } from "@/registry/new-york-v4/charts/chart-area-icons"
|
|
||||||
import { ChartAreaInteractive } from "@/registry/new-york-v4/charts/chart-area-interactive"
|
|
||||||
import { ChartAreaLegend } from "@/registry/new-york-v4/charts/chart-area-legend"
|
|
||||||
import { ChartAreaLinear } from "@/registry/new-york-v4/charts/chart-area-linear"
|
|
||||||
import { ChartAreaStacked } from "@/registry/new-york-v4/charts/chart-area-stacked"
|
|
||||||
import { ChartAreaStackedExpand } from "@/registry/new-york-v4/charts/chart-area-stacked-expand"
|
|
||||||
import { ChartAreaStep } from "@/registry/new-york-v4/charts/chart-area-step"
|
|
||||||
import { ChartBarActive } from "@/registry/new-york-v4/charts/chart-bar-active"
|
|
||||||
import { ChartBarDefault } from "@/registry/new-york-v4/charts/chart-bar-default"
|
|
||||||
import { ChartBarHorizontal } from "@/registry/new-york-v4/charts/chart-bar-horizontal"
|
|
||||||
import { ChartBarInteractive } from "@/registry/new-york-v4/charts/chart-bar-interactive"
|
|
||||||
import { ChartBarLabel } from "@/registry/new-york-v4/charts/chart-bar-label"
|
|
||||||
import { ChartBarLabelCustom } from "@/registry/new-york-v4/charts/chart-bar-label-custom"
|
|
||||||
import { ChartBarMixed } from "@/registry/new-york-v4/charts/chart-bar-mixed"
|
|
||||||
import { ChartBarMultiple } from "@/registry/new-york-v4/charts/chart-bar-multiple"
|
|
||||||
import { ChartBarNegative } from "@/registry/new-york-v4/charts/chart-bar-negative"
|
|
||||||
import { ChartBarStacked } from "@/registry/new-york-v4/charts/chart-bar-stacked"
|
|
||||||
import { ChartLineDefault } from "@/registry/new-york-v4/charts/chart-line-default"
|
|
||||||
import { ChartLineDots } from "@/registry/new-york-v4/charts/chart-line-dots"
|
|
||||||
import { ChartLineDotsColors } from "@/registry/new-york-v4/charts/chart-line-dots-colors"
|
|
||||||
import { ChartLineDotsCustom } from "@/registry/new-york-v4/charts/chart-line-dots-custom"
|
|
||||||
import { ChartLineInteractive } from "@/registry/new-york-v4/charts/chart-line-interactive"
|
|
||||||
import { ChartLineLabel } from "@/registry/new-york-v4/charts/chart-line-label"
|
|
||||||
import { ChartLineLabelCustom } from "@/registry/new-york-v4/charts/chart-line-label-custom"
|
|
||||||
import { ChartLineLinear } from "@/registry/new-york-v4/charts/chart-line-linear"
|
|
||||||
import { ChartLineMultiple } from "@/registry/new-york-v4/charts/chart-line-multiple"
|
|
||||||
import { ChartLineStep } from "@/registry/new-york-v4/charts/chart-line-step"
|
|
||||||
import { ChartPieDonut } from "@/registry/new-york-v4/charts/chart-pie-donut"
|
|
||||||
import { ChartPieDonutActive } from "@/registry/new-york-v4/charts/chart-pie-donut-active"
|
|
||||||
import { ChartPieDonutText } from "@/registry/new-york-v4/charts/chart-pie-donut-text"
|
|
||||||
import { ChartPieInteractive } from "@/registry/new-york-v4/charts/chart-pie-interactive"
|
|
||||||
import { ChartPieLabel } from "@/registry/new-york-v4/charts/chart-pie-label"
|
|
||||||
import { ChartPieLabelCustom } from "@/registry/new-york-v4/charts/chart-pie-label-custom"
|
|
||||||
import { ChartPieLabelList } from "@/registry/new-york-v4/charts/chart-pie-label-list"
|
|
||||||
import { ChartPieLegend } from "@/registry/new-york-v4/charts/chart-pie-legend"
|
|
||||||
import { ChartPieSeparatorNone } from "@/registry/new-york-v4/charts/chart-pie-separator-none"
|
|
||||||
import { ChartPieSimple } from "@/registry/new-york-v4/charts/chart-pie-simple"
|
|
||||||
import { ChartPieStacked } from "@/registry/new-york-v4/charts/chart-pie-stacked"
|
|
||||||
import { ChartRadarDefault } from "@/registry/new-york-v4/charts/chart-radar-default"
|
|
||||||
import { ChartRadarDots } from "@/registry/new-york-v4/charts/chart-radar-dots"
|
|
||||||
import { ChartRadarGridCircle } from "@/registry/new-york-v4/charts/chart-radar-grid-circle"
|
|
||||||
import { ChartRadarGridCircleFill } from "@/registry/new-york-v4/charts/chart-radar-grid-circle-fill"
|
|
||||||
import { ChartRadarGridCircleNoLines } from "@/registry/new-york-v4/charts/chart-radar-grid-circle-no-lines"
|
|
||||||
import { ChartRadarGridCustom } from "@/registry/new-york-v4/charts/chart-radar-grid-custom"
|
|
||||||
import { ChartRadarGridFill } from "@/registry/new-york-v4/charts/chart-radar-grid-fill"
|
|
||||||
import { ChartRadarGridNone } from "@/registry/new-york-v4/charts/chart-radar-grid-none"
|
|
||||||
import { ChartRadarIcons } from "@/registry/new-york-v4/charts/chart-radar-icons"
|
|
||||||
import { ChartRadarLabelCustom } from "@/registry/new-york-v4/charts/chart-radar-label-custom"
|
|
||||||
import { ChartRadarLegend } from "@/registry/new-york-v4/charts/chart-radar-legend"
|
|
||||||
import { ChartRadarLinesOnly } from "@/registry/new-york-v4/charts/chart-radar-lines-only"
|
|
||||||
import { ChartRadarMultiple } from "@/registry/new-york-v4/charts/chart-radar-multiple"
|
|
||||||
import { ChartRadarRadius } from "@/registry/new-york-v4/charts/chart-radar-radius"
|
|
||||||
import { ChartRadialGrid } from "@/registry/new-york-v4/charts/chart-radial-grid"
|
|
||||||
import { ChartRadialLabel } from "@/registry/new-york-v4/charts/chart-radial-label"
|
|
||||||
import { ChartRadialShape } from "@/registry/new-york-v4/charts/chart-radial-shape"
|
|
||||||
import { ChartRadialSimple } from "@/registry/new-york-v4/charts/chart-radial-simple"
|
|
||||||
import { ChartRadialStacked } from "@/registry/new-york-v4/charts/chart-radial-stacked"
|
|
||||||
import { ChartRadialText } from "@/registry/new-york-v4/charts/chart-radial-text"
|
|
||||||
import { ChartTooltipAdvanced } from "@/registry/new-york-v4/charts/chart-tooltip-advanced"
|
|
||||||
import { ChartTooltipDefault } from "@/registry/new-york-v4/charts/chart-tooltip-default"
|
|
||||||
import { ChartTooltipFormatter } from "@/registry/new-york-v4/charts/chart-tooltip-formatter"
|
|
||||||
import { ChartTooltipIcons } from "@/registry/new-york-v4/charts/chart-tooltip-icons"
|
|
||||||
import { ChartTooltipIndicatorLine } from "@/registry/new-york-v4/charts/chart-tooltip-indicator-line"
|
|
||||||
import { ChartTooltipIndicatorNone } from "@/registry/new-york-v4/charts/chart-tooltip-indicator-none"
|
|
||||||
import { ChartTooltipLabelCustom } from "@/registry/new-york-v4/charts/chart-tooltip-label-custom"
|
|
||||||
import { ChartTooltipLabelFormatter } from "@/registry/new-york-v4/charts/chart-tooltip-label-formatter"
|
|
||||||
import { ChartTooltipLabelNone } from "@/registry/new-york-v4/charts/chart-tooltip-label-none"
|
|
||||||
|
|
||||||
type ChartComponent = React.ComponentType
|
|
||||||
|
|
||||||
interface ChartItem {
|
|
||||||
id: string
|
|
||||||
component: ChartComponent
|
|
||||||
fullWidth?: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ChartGroups {
|
|
||||||
area: ChartItem[]
|
|
||||||
bar: ChartItem[]
|
|
||||||
line: ChartItem[]
|
|
||||||
pie: ChartItem[]
|
|
||||||
radar: ChartItem[]
|
|
||||||
radial: ChartItem[]
|
|
||||||
tooltip: ChartItem[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export const charts: ChartGroups = {
|
|
||||||
area: [
|
|
||||||
{
|
|
||||||
id: "chart-area-interactive",
|
|
||||||
component: ChartAreaInteractive,
|
|
||||||
fullWidth: true,
|
|
||||||
},
|
|
||||||
{ id: "chart-area-default", component: ChartAreaDefault },
|
|
||||||
{ id: "chart-area-linear", component: ChartAreaLinear },
|
|
||||||
{ id: "chart-area-step", component: ChartAreaStep },
|
|
||||||
{ id: "chart-area-legend", component: ChartAreaLegend },
|
|
||||||
{ id: "chart-area-stacked", component: ChartAreaStacked },
|
|
||||||
{ id: "chart-area-stacked-expand", component: ChartAreaStackedExpand },
|
|
||||||
{ id: "chart-area-icons", component: ChartAreaIcons },
|
|
||||||
{ id: "chart-area-gradient", component: ChartAreaGradient },
|
|
||||||
{ id: "chart-area-axes", component: ChartAreaAxes },
|
|
||||||
],
|
|
||||||
bar: [
|
|
||||||
{
|
|
||||||
id: "chart-bar-interactive",
|
|
||||||
component: ChartBarInteractive,
|
|
||||||
fullWidth: true,
|
|
||||||
},
|
|
||||||
{ id: "chart-bar-default", component: ChartBarDefault },
|
|
||||||
{ id: "chart-bar-horizontal", component: ChartBarHorizontal },
|
|
||||||
{ id: "chart-bar-multiple", component: ChartBarMultiple },
|
|
||||||
{ id: "chart-bar-stacked", component: ChartBarStacked },
|
|
||||||
{ id: "chart-bar-label", component: ChartBarLabel },
|
|
||||||
{ id: "chart-bar-label-custom", component: ChartBarLabelCustom },
|
|
||||||
{ id: "chart-bar-mixed", component: ChartBarMixed },
|
|
||||||
{ id: "chart-bar-active", component: ChartBarActive },
|
|
||||||
{ id: "chart-bar-negative", component: ChartBarNegative },
|
|
||||||
],
|
|
||||||
line: [
|
|
||||||
{
|
|
||||||
id: "chart-line-interactive",
|
|
||||||
component: ChartLineInteractive,
|
|
||||||
fullWidth: true,
|
|
||||||
},
|
|
||||||
{ id: "chart-line-default", component: ChartLineDefault },
|
|
||||||
{ id: "chart-line-linear", component: ChartLineLinear },
|
|
||||||
{ id: "chart-line-step", component: ChartLineStep },
|
|
||||||
{ id: "chart-line-multiple", component: ChartLineMultiple },
|
|
||||||
{ id: "chart-line-dots", component: ChartLineDots },
|
|
||||||
{ id: "chart-line-dots-custom", component: ChartLineDotsCustom },
|
|
||||||
{ id: "chart-line-dots-colors", component: ChartLineDotsColors },
|
|
||||||
{ id: "chart-line-label", component: ChartLineLabel },
|
|
||||||
{ id: "chart-line-label-custom", component: ChartLineLabelCustom },
|
|
||||||
],
|
|
||||||
pie: [
|
|
||||||
{ id: "chart-pie-simple", component: ChartPieSimple },
|
|
||||||
{ id: "chart-pie-separator-none", component: ChartPieSeparatorNone },
|
|
||||||
{ id: "chart-pie-label", component: ChartPieLabel },
|
|
||||||
{ id: "chart-pie-label-custom", component: ChartPieLabelCustom },
|
|
||||||
{ id: "chart-pie-label-list", component: ChartPieLabelList },
|
|
||||||
{ id: "chart-pie-legend", component: ChartPieLegend },
|
|
||||||
{ id: "chart-pie-donut", component: ChartPieDonut },
|
|
||||||
{ id: "chart-pie-donut-active", component: ChartPieDonutActive },
|
|
||||||
{ id: "chart-pie-donut-text", component: ChartPieDonutText },
|
|
||||||
{ id: "chart-pie-stacked", component: ChartPieStacked },
|
|
||||||
{ id: "chart-pie-interactive", component: ChartPieInteractive },
|
|
||||||
],
|
|
||||||
radar: [
|
|
||||||
{ id: "chart-radar-default", component: ChartRadarDefault },
|
|
||||||
{ id: "chart-radar-dots", component: ChartRadarDots },
|
|
||||||
{ id: "chart-radar-lines-only", component: ChartRadarLinesOnly },
|
|
||||||
{ id: "chart-radar-label-custom", component: ChartRadarLabelCustom },
|
|
||||||
{ id: "chart-radar-grid-custom", component: ChartRadarGridCustom },
|
|
||||||
{ id: "chart-radar-grid-none", component: ChartRadarGridNone },
|
|
||||||
{ id: "chart-radar-grid-circle", component: ChartRadarGridCircle },
|
|
||||||
{
|
|
||||||
id: "chart-radar-grid-circle-no-lines",
|
|
||||||
component: ChartRadarGridCircleNoLines,
|
|
||||||
},
|
|
||||||
{ id: "chart-radar-grid-circle-fill", component: ChartRadarGridCircleFill },
|
|
||||||
{ id: "chart-radar-grid-fill", component: ChartRadarGridFill },
|
|
||||||
{ id: "chart-radar-multiple", component: ChartRadarMultiple },
|
|
||||||
{ id: "chart-radar-legend", component: ChartRadarLegend },
|
|
||||||
{ id: "chart-radar-icons", component: ChartRadarIcons },
|
|
||||||
{ id: "chart-radar-radius", component: ChartRadarRadius },
|
|
||||||
],
|
|
||||||
radial: [
|
|
||||||
{ id: "chart-radial-simple", component: ChartRadialSimple },
|
|
||||||
{ id: "chart-radial-label", component: ChartRadialLabel },
|
|
||||||
{ id: "chart-radial-grid", component: ChartRadialGrid },
|
|
||||||
{ id: "chart-radial-text", component: ChartRadialText },
|
|
||||||
{ id: "chart-radial-shape", component: ChartRadialShape },
|
|
||||||
{ id: "chart-radial-stacked", component: ChartRadialStacked },
|
|
||||||
],
|
|
||||||
tooltip: [
|
|
||||||
{ id: "chart-tooltip-default", component: ChartTooltipDefault },
|
|
||||||
{
|
|
||||||
id: "chart-tooltip-indicator-line",
|
|
||||||
component: ChartTooltipIndicatorLine,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "chart-tooltip-indicator-none",
|
|
||||||
component: ChartTooltipIndicatorNone,
|
|
||||||
},
|
|
||||||
{ id: "chart-tooltip-label-custom", component: ChartTooltipLabelCustom },
|
|
||||||
{
|
|
||||||
id: "chart-tooltip-label-formatter",
|
|
||||||
component: ChartTooltipLabelFormatter,
|
|
||||||
},
|
|
||||||
{ id: "chart-tooltip-label-none", component: ChartTooltipLabelNone },
|
|
||||||
{ id: "chart-tooltip-formatter", component: ChartTooltipFormatter },
|
|
||||||
{ id: "chart-tooltip-icons", component: ChartTooltipIcons },
|
|
||||||
{ id: "chart-tooltip-advanced", component: ChartTooltipAdvanced },
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
||||||
// Export individual components for backward compatibility
|
|
||||||
export {
|
|
||||||
ChartAreaDefault,
|
|
||||||
ChartAreaLinear,
|
|
||||||
ChartAreaStep,
|
|
||||||
ChartAreaLegend,
|
|
||||||
ChartAreaStacked,
|
|
||||||
ChartAreaStackedExpand,
|
|
||||||
ChartAreaIcons,
|
|
||||||
ChartAreaGradient,
|
|
||||||
ChartAreaAxes,
|
|
||||||
ChartAreaInteractive,
|
|
||||||
ChartBarDefault,
|
|
||||||
ChartBarHorizontal,
|
|
||||||
ChartBarMultiple,
|
|
||||||
ChartBarStacked,
|
|
||||||
ChartBarLabel,
|
|
||||||
ChartBarLabelCustom,
|
|
||||||
ChartBarMixed,
|
|
||||||
ChartBarActive,
|
|
||||||
ChartBarNegative,
|
|
||||||
ChartBarInteractive,
|
|
||||||
ChartLineDefault,
|
|
||||||
ChartLineLinear,
|
|
||||||
ChartLineStep,
|
|
||||||
ChartLineMultiple,
|
|
||||||
ChartLineDots,
|
|
||||||
ChartLineDotsCustom,
|
|
||||||
ChartLineDotsColors,
|
|
||||||
ChartLineLabel,
|
|
||||||
ChartLineLabelCustom,
|
|
||||||
ChartLineInteractive,
|
|
||||||
ChartPieSimple,
|
|
||||||
ChartPieSeparatorNone,
|
|
||||||
ChartPieLabel,
|
|
||||||
ChartPieLabelCustom,
|
|
||||||
ChartPieLabelList,
|
|
||||||
ChartPieLegend,
|
|
||||||
ChartPieDonut,
|
|
||||||
ChartPieDonutActive,
|
|
||||||
ChartPieDonutText,
|
|
||||||
ChartPieStacked,
|
|
||||||
ChartPieInteractive,
|
|
||||||
ChartRadarDefault,
|
|
||||||
ChartRadarDots,
|
|
||||||
ChartRadarLinesOnly,
|
|
||||||
ChartRadarLabelCustom,
|
|
||||||
ChartRadarGridCustom,
|
|
||||||
ChartRadarGridNone,
|
|
||||||
ChartRadarGridCircle,
|
|
||||||
ChartRadarGridCircleNoLines,
|
|
||||||
ChartRadarGridCircleFill,
|
|
||||||
ChartRadarGridFill,
|
|
||||||
ChartRadarMultiple,
|
|
||||||
ChartRadarLegend,
|
|
||||||
ChartRadarIcons,
|
|
||||||
ChartRadarRadius,
|
|
||||||
ChartRadialSimple,
|
|
||||||
ChartRadialLabel,
|
|
||||||
ChartRadialGrid,
|
|
||||||
ChartRadialText,
|
|
||||||
ChartRadialShape,
|
|
||||||
ChartRadialStacked,
|
|
||||||
ChartTooltipDefault,
|
|
||||||
ChartTooltipIndicatorLine,
|
|
||||||
ChartTooltipIndicatorNone,
|
|
||||||
ChartTooltipLabelCustom,
|
|
||||||
ChartTooltipLabelFormatter,
|
|
||||||
ChartTooltipLabelNone,
|
|
||||||
ChartTooltipFormatter,
|
|
||||||
ChartTooltipIcons,
|
|
||||||
ChartTooltipAdvanced,
|
|
||||||
}
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
import { type Metadata } from "next"
|
|
||||||
import Link from "next/link"
|
|
||||||
|
|
||||||
import { Announcement } from "@/components/announcement"
|
|
||||||
import { ChartsNav } from "@/components/charts-nav"
|
|
||||||
import {
|
|
||||||
PageActions,
|
|
||||||
PageHeader,
|
|
||||||
PageHeaderDescription,
|
|
||||||
PageHeaderHeading,
|
|
||||||
} from "@/components/page-header"
|
|
||||||
import { PageNav } from "@/components/page-nav"
|
|
||||||
import { ThemeSelector } from "@/components/theme-selector"
|
|
||||||
import { Button } from "@/registry/new-york-v4/ui/button"
|
|
||||||
|
|
||||||
const title = "Beautiful Charts & Graphs"
|
|
||||||
const description =
|
|
||||||
"A collection of ready-to-use chart components built with Recharts. From basic charts to rich data displays, copy and paste into your apps."
|
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
|
||||||
title,
|
|
||||||
description,
|
|
||||||
openGraph: {
|
|
||||||
images: [
|
|
||||||
{
|
|
||||||
url: `/og?title=${encodeURIComponent(
|
|
||||||
title
|
|
||||||
)}&description=${encodeURIComponent(description)}`,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
twitter: {
|
|
||||||
card: "summary_large_image",
|
|
||||||
images: [
|
|
||||||
{
|
|
||||||
url: `/og?title=${encodeURIComponent(
|
|
||||||
title
|
|
||||||
)}&description=${encodeURIComponent(description)}`,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function ChartsLayout({
|
|
||||||
children,
|
|
||||||
}: {
|
|
||||||
children: React.ReactNode
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<PageHeader>
|
|
||||||
<Announcement />
|
|
||||||
<PageHeaderHeading>{title}</PageHeaderHeading>
|
|
||||||
<PageHeaderDescription>{description}</PageHeaderDescription>
|
|
||||||
<PageActions>
|
|
||||||
<Button asChild size="sm">
|
|
||||||
<a href="#charts">Browse Charts</a>
|
|
||||||
</Button>
|
|
||||||
<Button asChild variant="ghost" size="sm">
|
|
||||||
<Link href="/docs/components/chart">Documentation</Link>
|
|
||||||
</Button>
|
|
||||||
</PageActions>
|
|
||||||
</PageHeader>
|
|
||||||
<PageNav id="charts">
|
|
||||||
<ChartsNav />
|
|
||||||
<ThemeSelector className="mr-4 hidden md:flex" />
|
|
||||||
</PageNav>
|
|
||||||
<div className="container-wrapper section-soft flex-1">
|
|
||||||
<div className="container pb-6">
|
|
||||||
<section className="theme-container">{children}</section>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
import { type Metadata } from "next"
|
|
||||||
import Link from "next/link"
|
|
||||||
|
|
||||||
import { Announcement } from "@/components/announcement"
|
|
||||||
import { ColorsNav } from "@/components/colors-nav"
|
|
||||||
import {
|
|
||||||
PageActions,
|
|
||||||
PageHeader,
|
|
||||||
PageHeaderDescription,
|
|
||||||
PageHeaderHeading,
|
|
||||||
} from "@/components/page-header"
|
|
||||||
import { Button } from "@/registry/new-york-v4/ui/button"
|
|
||||||
|
|
||||||
const title = "Tailwind Colors in Every Format"
|
|
||||||
const description =
|
|
||||||
"The complete Tailwind color palette in HEX, RGB, HSL, CSS variables, and classes. Ready to copy and paste into your project."
|
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
|
||||||
title,
|
|
||||||
description,
|
|
||||||
openGraph: {
|
|
||||||
images: [
|
|
||||||
{
|
|
||||||
url: `/og?title=${encodeURIComponent(
|
|
||||||
title
|
|
||||||
)}&description=${encodeURIComponent(description)}`,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
twitter: {
|
|
||||||
card: "summary_large_image",
|
|
||||||
images: [
|
|
||||||
{
|
|
||||||
url: `/og?title=${encodeURIComponent(
|
|
||||||
title
|
|
||||||
)}&description=${encodeURIComponent(description)}`,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function ColorsLayout({
|
|
||||||
children,
|
|
||||||
}: {
|
|
||||||
children: React.ReactNode
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<PageHeader>
|
|
||||||
<Announcement />
|
|
||||||
<PageHeaderHeading>{title}</PageHeaderHeading>
|
|
||||||
<PageHeaderDescription>{description}</PageHeaderDescription>
|
|
||||||
<PageActions>
|
|
||||||
<Button asChild size="sm">
|
|
||||||
<a href="#colors">Browse Colors</a>
|
|
||||||
</Button>
|
|
||||||
<Button asChild variant="ghost" size="sm">
|
|
||||||
<Link href="/docs/theming">Documentation</Link>
|
|
||||||
</Button>
|
|
||||||
</PageActions>
|
|
||||||
</PageHeader>
|
|
||||||
<div className="hidden">
|
|
||||||
<div className="container-wrapper">
|
|
||||||
<div className="container flex items-center justify-between gap-8 py-4">
|
|
||||||
<ColorsNav className="[&>a:first-child]:text-primary flex-1 overflow-hidden" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="container-wrapper">
|
|
||||||
<div className="container py-6">
|
|
||||||
<section id="colors" className="scroll-mt-20">
|
|
||||||
{children}
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
import { getColors } from "@/lib/colors"
|
|
||||||
import { ColorPalette } from "@/components/color-palette"
|
|
||||||
|
|
||||||
export const dynamic = "force-static"
|
|
||||||
export const revalidate = false
|
|
||||||
|
|
||||||
export default function ColorsPage() {
|
|
||||||
const colors = getColors()
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="grid gap-8 lg:gap-16 xl:gap-20">
|
|
||||||
{colors.map((colorPalette) => (
|
|
||||||
<ColorPalette key={colorPalette.name} colorPalette={colorPalette} />
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,214 +0,0 @@
|
|||||||
import Link from "next/link"
|
|
||||||
import { notFound } from "next/navigation"
|
|
||||||
import { mdxComponents } from "@/mdx-components"
|
|
||||||
import {
|
|
||||||
IconArrowLeft,
|
|
||||||
IconArrowRight,
|
|
||||||
IconArrowUpRight,
|
|
||||||
} from "@tabler/icons-react"
|
|
||||||
import fm from "front-matter"
|
|
||||||
import { findNeighbour } from "fumadocs-core/page-tree"
|
|
||||||
import z from "zod"
|
|
||||||
|
|
||||||
import { source } from "@/lib/source"
|
|
||||||
import { absoluteUrl } from "@/lib/utils"
|
|
||||||
import { DocsCopyPage } from "@/components/docs-copy-page"
|
|
||||||
import { DocsTableOfContents } from "@/components/docs-toc"
|
|
||||||
import { OpenInV0Cta } from "@/components/open-in-v0-cta"
|
|
||||||
import { Badge } from "@/registry/new-york-v4/ui/badge"
|
|
||||||
import { Button } from "@/registry/new-york-v4/ui/button"
|
|
||||||
|
|
||||||
export const revalidate = false
|
|
||||||
export const dynamic = "force-static"
|
|
||||||
export const dynamicParams = false
|
|
||||||
|
|
||||||
export function generateStaticParams() {
|
|
||||||
return source.generateParams()
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function generateMetadata(props: {
|
|
||||||
params: Promise<{ slug: string[] }>
|
|
||||||
}) {
|
|
||||||
const params = await props.params
|
|
||||||
const page = source.getPage(params.slug)
|
|
||||||
|
|
||||||
if (!page) {
|
|
||||||
notFound()
|
|
||||||
}
|
|
||||||
|
|
||||||
const doc = page.data
|
|
||||||
|
|
||||||
if (!doc.title || !doc.description) {
|
|
||||||
notFound()
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
title: doc.title,
|
|
||||||
description: doc.description,
|
|
||||||
openGraph: {
|
|
||||||
title: doc.title,
|
|
||||||
description: doc.description,
|
|
||||||
type: "article",
|
|
||||||
url: absoluteUrl(page.url),
|
|
||||||
images: [
|
|
||||||
{
|
|
||||||
url: `/og?title=${encodeURIComponent(
|
|
||||||
doc.title
|
|
||||||
)}&description=${encodeURIComponent(doc.description)}`,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
twitter: {
|
|
||||||
card: "summary_large_image",
|
|
||||||
title: doc.title,
|
|
||||||
description: doc.description,
|
|
||||||
images: [
|
|
||||||
{
|
|
||||||
url: `/og?title=${encodeURIComponent(
|
|
||||||
doc.title
|
|
||||||
)}&description=${encodeURIComponent(doc.description)}`,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
creator: "@shadcn",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default async function Page(props: {
|
|
||||||
params: Promise<{ slug: string[] }>
|
|
||||||
}) {
|
|
||||||
const params = await props.params
|
|
||||||
const page = source.getPage(params.slug)
|
|
||||||
if (!page) {
|
|
||||||
notFound()
|
|
||||||
}
|
|
||||||
|
|
||||||
const doc = page.data
|
|
||||||
const MDX = doc.body
|
|
||||||
const neighbours = findNeighbour(source.pageTree, page.url)
|
|
||||||
|
|
||||||
const raw = await page.data.getText("raw")
|
|
||||||
const { attributes } = fm(raw)
|
|
||||||
const { links } = z
|
|
||||||
.object({
|
|
||||||
links: z
|
|
||||||
.object({
|
|
||||||
doc: z.string().optional(),
|
|
||||||
api: z.string().optional(),
|
|
||||||
})
|
|
||||||
.optional(),
|
|
||||||
})
|
|
||||||
.parse(attributes)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="flex items-stretch text-[1.05rem] sm:text-[15px] xl:w-full">
|
|
||||||
<div className="flex min-w-0 flex-1 flex-col">
|
|
||||||
<div className="h-(--top-spacing) shrink-0" />
|
|
||||||
<div className="mx-auto flex w-full max-w-2xl min-w-0 flex-1 flex-col gap-8 px-4 py-6 text-neutral-800 md:px-0 lg:py-8 dark:text-neutral-300">
|
|
||||||
<div className="flex flex-col gap-2">
|
|
||||||
<div className="flex flex-col gap-2">
|
|
||||||
<div className="flex items-start justify-between">
|
|
||||||
<h1 className="scroll-m-20 text-4xl font-semibold tracking-tight sm:text-3xl xl:text-4xl">
|
|
||||||
{doc.title}
|
|
||||||
</h1>
|
|
||||||
<div className="docs-nav bg-background/80 border-border/50 fixed inset-x-0 bottom-0 isolate z-50 flex items-center gap-2 border-t px-6 py-4 backdrop-blur-sm sm:static sm:z-0 sm:border-t-0 sm:bg-transparent sm:px-0 sm:pt-1.5 sm:backdrop-blur-none">
|
|
||||||
<DocsCopyPage page={raw} url={absoluteUrl(page.url)} />
|
|
||||||
{neighbours.previous && (
|
|
||||||
<Button
|
|
||||||
variant="secondary"
|
|
||||||
size="icon"
|
|
||||||
className="extend-touch-target ml-auto size-8 shadow-none md:size-7"
|
|
||||||
asChild
|
|
||||||
>
|
|
||||||
<Link href={neighbours.previous.url}>
|
|
||||||
<IconArrowLeft />
|
|
||||||
<span className="sr-only">Previous</span>
|
|
||||||
</Link>
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
{neighbours.next && (
|
|
||||||
<Button
|
|
||||||
variant="secondary"
|
|
||||||
size="icon"
|
|
||||||
className="extend-touch-target size-8 shadow-none md:size-7"
|
|
||||||
asChild
|
|
||||||
>
|
|
||||||
<Link href={neighbours.next.url}>
|
|
||||||
<span className="sr-only">Next</span>
|
|
||||||
<IconArrowRight />
|
|
||||||
</Link>
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{doc.description && (
|
|
||||||
<p className="text-muted-foreground text-[1.05rem] text-balance sm:text-base">
|
|
||||||
{doc.description}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
{links ? (
|
|
||||||
<div className="flex items-center gap-2 pt-4">
|
|
||||||
{links?.doc && (
|
|
||||||
<Badge asChild variant="secondary" className="rounded-full">
|
|
||||||
<a href={links.doc} target="_blank" rel="noreferrer">
|
|
||||||
Docs <IconArrowUpRight />
|
|
||||||
</a>
|
|
||||||
</Badge>
|
|
||||||
)}
|
|
||||||
{links?.api && (
|
|
||||||
<Badge asChild variant="secondary" className="rounded-full">
|
|
||||||
<a href={links.api} target="_blank" rel="noreferrer">
|
|
||||||
API Reference <IconArrowUpRight />
|
|
||||||
</a>
|
|
||||||
</Badge>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
<div className="w-full flex-1 *:data-[slot=alert]:first:mt-0">
|
|
||||||
<MDX components={mdxComponents} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="mx-auto hidden h-16 w-full max-w-2xl items-center gap-2 px-4 sm:flex md:px-0">
|
|
||||||
{neighbours.previous && (
|
|
||||||
<Button
|
|
||||||
variant="secondary"
|
|
||||||
size="sm"
|
|
||||||
asChild
|
|
||||||
className="shadow-none"
|
|
||||||
>
|
|
||||||
<Link href={neighbours.previous.url}>
|
|
||||||
<IconArrowLeft /> {neighbours.previous.name}
|
|
||||||
</Link>
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
{neighbours.next && (
|
|
||||||
<Button
|
|
||||||
variant="secondary"
|
|
||||||
size="sm"
|
|
||||||
className="ml-auto shadow-none"
|
|
||||||
asChild
|
|
||||||
>
|
|
||||||
<Link href={neighbours.next.url}>
|
|
||||||
{neighbours.next.name} <IconArrowRight />
|
|
||||||
</Link>
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="sticky top-[calc(var(--header-height)+1px)] z-30 ml-auto hidden h-[calc(100svh-var(--footer-height)+2rem)] w-72 flex-col gap-4 overflow-hidden overscroll-none pb-8 xl:flex">
|
|
||||||
<div className="h-(--top-spacing) shrink-0" />
|
|
||||||
{doc.toc?.length ? (
|
|
||||||
<div className="no-scrollbar overflow-y-auto px-8">
|
|
||||||
<DocsTableOfContents toc={doc.toc} />
|
|
||||||
<div className="h-12" />
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
<div className="flex flex-1 flex-col gap-12 px-6">
|
|
||||||
<OpenInV0Cta />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
import { source } from "@/lib/source"
|
|
||||||
import { DocsSidebar } from "@/components/docs-sidebar"
|
|
||||||
import { SidebarProvider } from "@/registry/new-york-v4/ui/sidebar"
|
|
||||||
|
|
||||||
export default function DocsLayout({
|
|
||||||
children,
|
|
||||||
}: {
|
|
||||||
children: React.ReactNode
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<div className="container-wrapper flex flex-1 flex-col px-2">
|
|
||||||
<SidebarProvider className="3xl:fixed:container 3xl:fixed:px-3 min-h-min flex-1 items-start px-0 [--sidebar-width:220px] [--top-spacing:0] lg:grid lg:grid-cols-[var(--sidebar-width)_minmax(0,1fr)] lg:[--sidebar-width:240px] lg:[--top-spacing:calc(var(--spacing)*4)]">
|
|
||||||
<DocsSidebar tree={source.pageTree} />
|
|
||||||
<div className="h-full w-full">{children}</div>
|
|
||||||
</SidebarProvider>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
"use client"
|
|
||||||
|
|
||||||
import * as React from "react"
|
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
|
||||||
import { Icons } from "@/components/icons"
|
|
||||||
import { Button } from "@/registry/new-york-v4/ui/button"
|
|
||||||
import {
|
|
||||||
Field,
|
|
||||||
FieldGroup,
|
|
||||||
FieldLabel,
|
|
||||||
FieldSeparator,
|
|
||||||
} from "@/registry/new-york-v4/ui/field"
|
|
||||||
import { Input } from "@/registry/new-york-v4/ui/input"
|
|
||||||
import { Spinner } from "@/registry/new-york-v4/ui/spinner"
|
|
||||||
|
|
||||||
export function UserAuthForm({
|
|
||||||
className,
|
|
||||||
...props
|
|
||||||
}: React.ComponentProps<"div">) {
|
|
||||||
const [isLoading, setIsLoading] = React.useState<boolean>(false)
|
|
||||||
|
|
||||||
async function onSubmit(event: React.SyntheticEvent) {
|
|
||||||
event.preventDefault()
|
|
||||||
setIsLoading(true)
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
setIsLoading(false)
|
|
||||||
}, 3000)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={cn("grid gap-6", className)} {...props}>
|
|
||||||
<form onSubmit={onSubmit}>
|
|
||||||
<FieldGroup>
|
|
||||||
<Field>
|
|
||||||
<FieldLabel className="sr-only" htmlFor="email">
|
|
||||||
Email
|
|
||||||
</FieldLabel>
|
|
||||||
<Input
|
|
||||||
id="email"
|
|
||||||
placeholder="name@example.com"
|
|
||||||
type="email"
|
|
||||||
autoCapitalize="none"
|
|
||||||
autoComplete="email"
|
|
||||||
autoCorrect="off"
|
|
||||||
disabled={isLoading}
|
|
||||||
/>
|
|
||||||
</Field>
|
|
||||||
<Field>
|
|
||||||
<Button disabled={isLoading}>
|
|
||||||
{isLoading && <Spinner />}
|
|
||||||
Sign In with Email
|
|
||||||
</Button>
|
|
||||||
</Field>
|
|
||||||
</FieldGroup>
|
|
||||||
</form>
|
|
||||||
<FieldSeparator>Or continue with</FieldSeparator>
|
|
||||||
<Button variant="outline" type="button" disabled={isLoading}>
|
|
||||||
{isLoading ? <Spinner /> : <Icons.gitHub className="mr-2 h-4 w-4" />}{" "}
|
|
||||||
GitHub
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
import { type Metadata } from "next"
|
|
||||||
import Image from "next/image"
|
|
||||||
import Link from "next/link"
|
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
|
||||||
import { buttonVariants } from "@/registry/new-york-v4/ui/button"
|
|
||||||
import { FieldDescription } from "@/registry/new-york-v4/ui/field"
|
|
||||||
import { UserAuthForm } from "@/app/(app)/examples/authentication/components/user-auth-form"
|
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
|
||||||
title: "Authentication",
|
|
||||||
description: "Authentication forms built using the components.",
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function AuthenticationPage() {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className="md:hidden">
|
|
||||||
<Image
|
|
||||||
src="/examples/authentication-light.png"
|
|
||||||
width={1280}
|
|
||||||
height={843}
|
|
||||||
alt="Authentication"
|
|
||||||
className="block dark:hidden"
|
|
||||||
priority
|
|
||||||
/>
|
|
||||||
<Image
|
|
||||||
src="/examples/authentication-dark.png"
|
|
||||||
width={1280}
|
|
||||||
height={843}
|
|
||||||
alt="Authentication"
|
|
||||||
className="hidden dark:block"
|
|
||||||
priority
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="relative container hidden flex-1 shrink-0 items-center justify-center md:grid lg:max-w-none lg:grid-cols-2 lg:px-0">
|
|
||||||
<Link
|
|
||||||
href="/examples/authentication"
|
|
||||||
className={cn(
|
|
||||||
buttonVariants({ variant: "ghost" }),
|
|
||||||
"absolute top-4 right-4 md:top-8 md:right-8"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
Login
|
|
||||||
</Link>
|
|
||||||
<div className="text-primary relative hidden h-full flex-col p-10 lg:flex dark:border-r">
|
|
||||||
<div className="bg-primary/5 absolute inset-0" />
|
|
||||||
<div className="relative z-20 flex items-center text-lg font-medium">
|
|
||||||
<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 className="relative z-20 mt-auto">
|
|
||||||
<blockquote className="leading-normal text-balance">
|
|
||||||
“This library has saved me countless hours of work and
|
|
||||||
helped me deliver stunning designs to my clients faster than ever
|
|
||||||
before.” - Sofia Davis
|
|
||||||
</blockquote>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center justify-center lg:h-[1000px] lg:p-8">
|
|
||||||
<div className="mx-auto flex w-full flex-col justify-center gap-6 sm:w-[350px]">
|
|
||||||
<div className="flex flex-col gap-2 text-center">
|
|
||||||
<h1 className="text-2xl font-semibold tracking-tight">
|
|
||||||
Create an account
|
|
||||||
</h1>
|
|
||||||
<p className="text-muted-foreground text-sm">
|
|
||||||
Enter your email below to create your account
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<UserAuthForm />
|
|
||||||
<FieldDescription className="px-6 text-center">
|
|
||||||
By clicking continue, you agree to our{" "}
|
|
||||||
<Link href="/terms">Terms of Service</Link> and{" "}
|
|
||||||
<Link href="/privacy">Privacy Policy</Link>.
|
|
||||||
</FieldDescription>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,182 +0,0 @@
|
|||||||
"use client"
|
|
||||||
|
|
||||||
import * as React from "react"
|
|
||||||
import Link from "next/link"
|
|
||||||
import {
|
|
||||||
IconCamera,
|
|
||||||
IconChartBar,
|
|
||||||
IconDashboard,
|
|
||||||
IconDatabase,
|
|
||||||
IconFileAi,
|
|
||||||
IconFileDescription,
|
|
||||||
IconFileWord,
|
|
||||||
IconFolder,
|
|
||||||
IconHelp,
|
|
||||||
IconInnerShadowTop,
|
|
||||||
IconListDetails,
|
|
||||||
IconReport,
|
|
||||||
IconSearch,
|
|
||||||
IconSettings,
|
|
||||||
IconUsers,
|
|
||||||
} from "@tabler/icons-react"
|
|
||||||
|
|
||||||
import {
|
|
||||||
Sidebar,
|
|
||||||
SidebarContent,
|
|
||||||
SidebarFooter,
|
|
||||||
SidebarHeader,
|
|
||||||
SidebarMenu,
|
|
||||||
SidebarMenuButton,
|
|
||||||
SidebarMenuItem,
|
|
||||||
} from "@/registry/new-york-v4/ui/sidebar"
|
|
||||||
import { NavDocuments } from "@/app/(app)/examples/dashboard/components/nav-documents"
|
|
||||||
import { NavMain } from "@/app/(app)/examples/dashboard/components/nav-main"
|
|
||||||
import { NavSecondary } from "@/app/(app)/examples/dashboard/components/nav-secondary"
|
|
||||||
import { NavUser } from "@/app/(app)/examples/dashboard/components/nav-user"
|
|
||||||
|
|
||||||
const data = {
|
|
||||||
user: {
|
|
||||||
name: "shadcn",
|
|
||||||
email: "m@example.com",
|
|
||||||
avatar: "/avatars/shadcn.jpg",
|
|
||||||
},
|
|
||||||
navMain: [
|
|
||||||
{
|
|
||||||
title: "Dashboard",
|
|
||||||
url: "#",
|
|
||||||
icon: IconDashboard,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Lifecycle",
|
|
||||||
url: "#",
|
|
||||||
icon: IconListDetails,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Analytics",
|
|
||||||
url: "#",
|
|
||||||
icon: IconChartBar,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Projects",
|
|
||||||
url: "#",
|
|
||||||
icon: IconFolder,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Team",
|
|
||||||
url: "#",
|
|
||||||
icon: IconUsers,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
navClouds: [
|
|
||||||
{
|
|
||||||
title: "Capture",
|
|
||||||
icon: IconCamera,
|
|
||||||
isActive: true,
|
|
||||||
url: "#",
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
title: "Active Proposals",
|
|
||||||
url: "#",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Archived",
|
|
||||||
url: "#",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Proposal",
|
|
||||||
icon: IconFileDescription,
|
|
||||||
url: "#",
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
title: "Active Proposals",
|
|
||||||
url: "#",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Archived",
|
|
||||||
url: "#",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Prompts",
|
|
||||||
icon: IconFileAi,
|
|
||||||
url: "#",
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
title: "Active Proposals",
|
|
||||||
url: "#",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Archived",
|
|
||||||
url: "#",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
navSecondary: [
|
|
||||||
{
|
|
||||||
title: "Settings",
|
|
||||||
url: "#",
|
|
||||||
icon: IconSettings,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Get Help",
|
|
||||||
url: "#",
|
|
||||||
icon: IconHelp,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Search",
|
|
||||||
url: "#",
|
|
||||||
icon: IconSearch,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
documents: [
|
|
||||||
{
|
|
||||||
name: "Data Library",
|
|
||||||
url: "#",
|
|
||||||
icon: IconDatabase,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Reports",
|
|
||||||
url: "#",
|
|
||||||
icon: IconReport,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Word Assistant",
|
|
||||||
url: "#",
|
|
||||||
icon: IconFileWord,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
||||||
export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
|
|
||||||
return (
|
|
||||||
<Sidebar collapsible="none" className="h-auto border-r" {...props}>
|
|
||||||
<SidebarHeader className="border-b">
|
|
||||||
<SidebarMenu>
|
|
||||||
<SidebarMenuItem>
|
|
||||||
<SidebarMenuButton
|
|
||||||
asChild
|
|
||||||
className="data-[slot=sidebar-menu-button]:!p-1.5"
|
|
||||||
>
|
|
||||||
<Link href="#">
|
|
||||||
<IconInnerShadowTop className="!size-5" />
|
|
||||||
<span className="text-base font-semibold">Acme Inc.</span>
|
|
||||||
</Link>
|
|
||||||
</SidebarMenuButton>
|
|
||||||
</SidebarMenuItem>
|
|
||||||
</SidebarMenu>
|
|
||||||
</SidebarHeader>
|
|
||||||
<SidebarContent>
|
|
||||||
<NavMain items={data.navMain} />
|
|
||||||
<NavDocuments items={data.documents} />
|
|
||||||
<NavSecondary items={data.navSecondary} className="mt-auto" />
|
|
||||||
</SidebarContent>
|
|
||||||
<SidebarFooter>
|
|
||||||
<NavUser user={data.user} />
|
|
||||||
</SidebarFooter>
|
|
||||||
</Sidebar>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,286 +0,0 @@
|
|||||||
"use client"
|
|
||||||
|
|
||||||
import * as React from "react"
|
|
||||||
import { Area, AreaChart, CartesianGrid, XAxis } from "recharts"
|
|
||||||
|
|
||||||
import { useIsMobile } from "@/registry/new-york-v4/hooks/use-mobile"
|
|
||||||
import {
|
|
||||||
Card,
|
|
||||||
CardAction,
|
|
||||||
CardContent,
|
|
||||||
CardDescription,
|
|
||||||
CardHeader,
|
|
||||||
CardTitle,
|
|
||||||
} from "@/registry/new-york-v4/ui/card"
|
|
||||||
import {
|
|
||||||
ChartContainer,
|
|
||||||
ChartTooltip,
|
|
||||||
ChartTooltipContent,
|
|
||||||
type ChartConfig,
|
|
||||||
} from "@/registry/new-york-v4/ui/chart"
|
|
||||||
import {
|
|
||||||
Select,
|
|
||||||
SelectContent,
|
|
||||||
SelectItem,
|
|
||||||
SelectTrigger,
|
|
||||||
SelectValue,
|
|
||||||
} from "@/registry/new-york-v4/ui/select"
|
|
||||||
import {
|
|
||||||
ToggleGroup,
|
|
||||||
ToggleGroupItem,
|
|
||||||
} from "@/registry/new-york-v4/ui/toggle-group"
|
|
||||||
|
|
||||||
export const description = "An interactive area chart"
|
|
||||||
|
|
||||||
const chartData = [
|
|
||||||
{ date: "2024-04-01", desktop: 222, mobile: 150 },
|
|
||||||
{ date: "2024-04-02", desktop: 97, mobile: 180 },
|
|
||||||
{ date: "2024-04-03", desktop: 167, mobile: 120 },
|
|
||||||
{ date: "2024-04-04", desktop: 242, mobile: 260 },
|
|
||||||
{ date: "2024-04-05", desktop: 373, mobile: 290 },
|
|
||||||
{ date: "2024-04-06", desktop: 301, mobile: 340 },
|
|
||||||
{ date: "2024-04-07", desktop: 245, mobile: 180 },
|
|
||||||
{ date: "2024-04-08", desktop: 409, mobile: 320 },
|
|
||||||
{ date: "2024-04-09", desktop: 59, mobile: 110 },
|
|
||||||
{ date: "2024-04-10", desktop: 261, mobile: 190 },
|
|
||||||
{ date: "2024-04-11", desktop: 327, mobile: 350 },
|
|
||||||
{ date: "2024-04-12", desktop: 292, mobile: 210 },
|
|
||||||
{ date: "2024-04-13", desktop: 342, mobile: 380 },
|
|
||||||
{ date: "2024-04-14", desktop: 137, mobile: 220 },
|
|
||||||
{ date: "2024-04-15", desktop: 120, mobile: 170 },
|
|
||||||
{ date: "2024-04-16", desktop: 138, mobile: 190 },
|
|
||||||
{ date: "2024-04-17", desktop: 446, mobile: 360 },
|
|
||||||
{ date: "2024-04-18", desktop: 364, mobile: 410 },
|
|
||||||
{ date: "2024-04-19", desktop: 243, mobile: 180 },
|
|
||||||
{ date: "2024-04-20", desktop: 89, mobile: 150 },
|
|
||||||
{ date: "2024-04-21", desktop: 137, mobile: 200 },
|
|
||||||
{ date: "2024-04-22", desktop: 224, mobile: 170 },
|
|
||||||
{ date: "2024-04-23", desktop: 138, mobile: 230 },
|
|
||||||
{ date: "2024-04-24", desktop: 387, mobile: 290 },
|
|
||||||
{ date: "2024-04-25", desktop: 215, mobile: 250 },
|
|
||||||
{ date: "2024-04-26", desktop: 75, mobile: 130 },
|
|
||||||
{ date: "2024-04-27", desktop: 383, mobile: 420 },
|
|
||||||
{ date: "2024-04-28", desktop: 122, mobile: 180 },
|
|
||||||
{ date: "2024-04-29", desktop: 315, mobile: 240 },
|
|
||||||
{ date: "2024-04-30", desktop: 454, mobile: 380 },
|
|
||||||
{ date: "2024-05-01", desktop: 165, mobile: 220 },
|
|
||||||
{ date: "2024-05-02", desktop: 293, mobile: 310 },
|
|
||||||
{ date: "2024-05-03", desktop: 247, mobile: 190 },
|
|
||||||
{ date: "2024-05-04", desktop: 385, mobile: 420 },
|
|
||||||
{ date: "2024-05-05", desktop: 481, mobile: 390 },
|
|
||||||
{ date: "2024-05-06", desktop: 498, mobile: 520 },
|
|
||||||
{ date: "2024-05-07", desktop: 388, mobile: 300 },
|
|
||||||
{ date: "2024-05-08", desktop: 149, mobile: 210 },
|
|
||||||
{ date: "2024-05-09", desktop: 227, mobile: 180 },
|
|
||||||
{ date: "2024-05-10", desktop: 293, mobile: 330 },
|
|
||||||
{ date: "2024-05-11", desktop: 335, mobile: 270 },
|
|
||||||
{ date: "2024-05-12", desktop: 197, mobile: 240 },
|
|
||||||
{ date: "2024-05-13", desktop: 197, mobile: 160 },
|
|
||||||
{ date: "2024-05-14", desktop: 448, mobile: 490 },
|
|
||||||
{ date: "2024-05-15", desktop: 473, mobile: 380 },
|
|
||||||
{ date: "2024-05-16", desktop: 338, mobile: 400 },
|
|
||||||
{ date: "2024-05-17", desktop: 499, mobile: 420 },
|
|
||||||
{ date: "2024-05-18", desktop: 315, mobile: 350 },
|
|
||||||
{ date: "2024-05-19", desktop: 235, mobile: 180 },
|
|
||||||
{ date: "2024-05-20", desktop: 177, mobile: 230 },
|
|
||||||
{ date: "2024-05-21", desktop: 82, mobile: 140 },
|
|
||||||
{ date: "2024-05-22", desktop: 81, mobile: 120 },
|
|
||||||
{ date: "2024-05-23", desktop: 252, mobile: 290 },
|
|
||||||
{ date: "2024-05-24", desktop: 294, mobile: 220 },
|
|
||||||
{ date: "2024-05-25", desktop: 201, mobile: 250 },
|
|
||||||
{ date: "2024-05-26", desktop: 213, mobile: 170 },
|
|
||||||
{ date: "2024-05-27", desktop: 420, mobile: 460 },
|
|
||||||
{ date: "2024-05-28", desktop: 233, mobile: 190 },
|
|
||||||
{ date: "2024-05-29", desktop: 78, mobile: 130 },
|
|
||||||
{ date: "2024-05-30", desktop: 340, mobile: 280 },
|
|
||||||
{ date: "2024-05-31", desktop: 178, mobile: 230 },
|
|
||||||
{ date: "2024-06-01", desktop: 178, mobile: 200 },
|
|
||||||
{ date: "2024-06-02", desktop: 470, mobile: 410 },
|
|
||||||
{ date: "2024-06-03", desktop: 103, mobile: 160 },
|
|
||||||
{ date: "2024-06-04", desktop: 439, mobile: 380 },
|
|
||||||
{ date: "2024-06-05", desktop: 88, mobile: 140 },
|
|
||||||
{ date: "2024-06-06", desktop: 294, mobile: 250 },
|
|
||||||
{ date: "2024-06-07", desktop: 323, mobile: 370 },
|
|
||||||
{ date: "2024-06-08", desktop: 385, mobile: 320 },
|
|
||||||
{ date: "2024-06-09", desktop: 438, mobile: 480 },
|
|
||||||
{ date: "2024-06-10", desktop: 155, mobile: 200 },
|
|
||||||
{ date: "2024-06-11", desktop: 92, mobile: 150 },
|
|
||||||
{ date: "2024-06-12", desktop: 492, mobile: 420 },
|
|
||||||
{ date: "2024-06-13", desktop: 81, mobile: 130 },
|
|
||||||
{ date: "2024-06-14", desktop: 426, mobile: 380 },
|
|
||||||
{ date: "2024-06-15", desktop: 307, mobile: 350 },
|
|
||||||
{ date: "2024-06-16", desktop: 371, mobile: 310 },
|
|
||||||
{ date: "2024-06-17", desktop: 475, mobile: 520 },
|
|
||||||
{ date: "2024-06-18", desktop: 107, mobile: 170 },
|
|
||||||
{ date: "2024-06-19", desktop: 341, mobile: 290 },
|
|
||||||
{ date: "2024-06-20", desktop: 408, mobile: 450 },
|
|
||||||
{ date: "2024-06-21", desktop: 169, mobile: 210 },
|
|
||||||
{ date: "2024-06-22", desktop: 317, mobile: 270 },
|
|
||||||
{ date: "2024-06-23", desktop: 480, mobile: 530 },
|
|
||||||
{ date: "2024-06-24", desktop: 132, mobile: 180 },
|
|
||||||
{ date: "2024-06-25", desktop: 141, mobile: 190 },
|
|
||||||
{ date: "2024-06-26", desktop: 434, mobile: 380 },
|
|
||||||
{ date: "2024-06-27", desktop: 448, mobile: 490 },
|
|
||||||
{ date: "2024-06-28", desktop: 149, mobile: 200 },
|
|
||||||
{ date: "2024-06-29", desktop: 103, mobile: 160 },
|
|
||||||
{ date: "2024-06-30", desktop: 446, mobile: 400 },
|
|
||||||
]
|
|
||||||
|
|
||||||
const chartConfig = {
|
|
||||||
visitors: {
|
|
||||||
label: "Visitors",
|
|
||||||
},
|
|
||||||
desktop: {
|
|
||||||
label: "Desktop",
|
|
||||||
color: "var(--primary)",
|
|
||||||
},
|
|
||||||
mobile: {
|
|
||||||
label: "Mobile",
|
|
||||||
color: "var(--primary)",
|
|
||||||
},
|
|
||||||
} satisfies ChartConfig
|
|
||||||
|
|
||||||
export function ChartAreaInteractive() {
|
|
||||||
const isMobile = useIsMobile()
|
|
||||||
const [timeRange, setTimeRange] = React.useState("7d")
|
|
||||||
|
|
||||||
const filteredData = chartData.filter((item) => {
|
|
||||||
const date = new Date(item.date)
|
|
||||||
const referenceDate = new Date("2024-06-30")
|
|
||||||
let daysToSubtract = 90
|
|
||||||
if (timeRange === "30d") {
|
|
||||||
daysToSubtract = 30
|
|
||||||
} else if (timeRange === "7d") {
|
|
||||||
daysToSubtract = 7
|
|
||||||
}
|
|
||||||
const startDate = new Date(referenceDate)
|
|
||||||
startDate.setDate(startDate.getDate() - daysToSubtract)
|
|
||||||
return date >= startDate
|
|
||||||
})
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Card className="@container/card">
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle>Total Visitors</CardTitle>
|
|
||||||
<CardDescription>
|
|
||||||
<span className="hidden @[540px]/card:block">
|
|
||||||
Total for the last 3 months
|
|
||||||
</span>
|
|
||||||
<span className="@[540px]/card:hidden">Last 3 months</span>
|
|
||||||
</CardDescription>
|
|
||||||
<CardAction>
|
|
||||||
<ToggleGroup
|
|
||||||
type="single"
|
|
||||||
value={timeRange}
|
|
||||||
onValueChange={setTimeRange}
|
|
||||||
variant="outline"
|
|
||||||
className="hidden *:data-[slot=toggle-group-item]:!px-4 @[767px]/card:flex"
|
|
||||||
>
|
|
||||||
<ToggleGroupItem value="90d">Last 3 months</ToggleGroupItem>
|
|
||||||
<ToggleGroupItem value="30d">Last 30 days</ToggleGroupItem>
|
|
||||||
<ToggleGroupItem value="7d">Last 7 days</ToggleGroupItem>
|
|
||||||
</ToggleGroup>
|
|
||||||
<Select value={timeRange} onValueChange={setTimeRange}>
|
|
||||||
<SelectTrigger
|
|
||||||
className="flex w-40 **:data-[slot=select-value]:block **:data-[slot=select-value]:truncate @[767px]/card:hidden"
|
|
||||||
size="sm"
|
|
||||||
aria-label="Select a value"
|
|
||||||
>
|
|
||||||
<SelectValue placeholder="Last 3 months" />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent className="rounded-xl">
|
|
||||||
<SelectItem value="90d" className="rounded-lg">
|
|
||||||
Last 3 months
|
|
||||||
</SelectItem>
|
|
||||||
<SelectItem value="30d" className="rounded-lg">
|
|
||||||
Last 30 days
|
|
||||||
</SelectItem>
|
|
||||||
<SelectItem value="7d" className="rounded-lg">
|
|
||||||
Last 7 days
|
|
||||||
</SelectItem>
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</CardAction>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent className="px-2 pt-4 sm:px-6 sm:pt-6">
|
|
||||||
<ChartContainer
|
|
||||||
config={chartConfig}
|
|
||||||
className="aspect-auto h-[250px] w-full"
|
|
||||||
>
|
|
||||||
<AreaChart data={filteredData}>
|
|
||||||
<defs>
|
|
||||||
<linearGradient id="fillDesktop" x1="0" y1="0" x2="0" y2="1">
|
|
||||||
<stop
|
|
||||||
offset="5%"
|
|
||||||
stopColor="var(--color-desktop)"
|
|
||||||
stopOpacity={1.0}
|
|
||||||
/>
|
|
||||||
<stop
|
|
||||||
offset="95%"
|
|
||||||
stopColor="var(--color-desktop)"
|
|
||||||
stopOpacity={0.1}
|
|
||||||
/>
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient id="fillMobile" x1="0" y1="0" x2="0" y2="1">
|
|
||||||
<stop
|
|
||||||
offset="5%"
|
|
||||||
stopColor="var(--color-mobile)"
|
|
||||||
stopOpacity={0.8}
|
|
||||||
/>
|
|
||||||
<stop
|
|
||||||
offset="95%"
|
|
||||||
stopColor="var(--color-mobile)"
|
|
||||||
stopOpacity={0.1}
|
|
||||||
/>
|
|
||||||
</linearGradient>
|
|
||||||
</defs>
|
|
||||||
<CartesianGrid vertical={false} />
|
|
||||||
<XAxis
|
|
||||||
dataKey="date"
|
|
||||||
tickLine={false}
|
|
||||||
axisLine={false}
|
|
||||||
tickMargin={8}
|
|
||||||
minTickGap={32}
|
|
||||||
tickFormatter={(value) => {
|
|
||||||
const date = new Date(value)
|
|
||||||
return date.toLocaleDateString("en-US", {
|
|
||||||
month: "short",
|
|
||||||
day: "numeric",
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<ChartTooltip
|
|
||||||
cursor={false}
|
|
||||||
defaultIndex={isMobile ? -1 : 10}
|
|
||||||
content={
|
|
||||||
<ChartTooltipContent
|
|
||||||
labelFormatter={(value) => {
|
|
||||||
return new Date(value).toLocaleDateString("en-US", {
|
|
||||||
month: "short",
|
|
||||||
day: "numeric",
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
indicator="dot"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Area
|
|
||||||
dataKey="mobile"
|
|
||||||
type="natural"
|
|
||||||
fill="url(#fillMobile)"
|
|
||||||
stroke="var(--color-mobile)"
|
|
||||||
stackId="a"
|
|
||||||
/>
|
|
||||||
<Area
|
|
||||||
dataKey="desktop"
|
|
||||||
type="natural"
|
|
||||||
fill="url(#fillDesktop)"
|
|
||||||
stroke="var(--color-desktop)"
|
|
||||||
stackId="a"
|
|
||||||
/>
|
|
||||||
</AreaChart>
|
|
||||||
</ChartContainer>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,807 +0,0 @@
|
|||||||
"use client"
|
|
||||||
|
|
||||||
import * as React from "react"
|
|
||||||
import {
|
|
||||||
closestCenter,
|
|
||||||
DndContext,
|
|
||||||
KeyboardSensor,
|
|
||||||
MouseSensor,
|
|
||||||
TouchSensor,
|
|
||||||
useSensor,
|
|
||||||
useSensors,
|
|
||||||
type DragEndEvent,
|
|
||||||
type UniqueIdentifier,
|
|
||||||
} from "@dnd-kit/core"
|
|
||||||
import { restrictToVerticalAxis } from "@dnd-kit/modifiers"
|
|
||||||
import {
|
|
||||||
arrayMove,
|
|
||||||
SortableContext,
|
|
||||||
useSortable,
|
|
||||||
verticalListSortingStrategy,
|
|
||||||
} from "@dnd-kit/sortable"
|
|
||||||
import { CSS } from "@dnd-kit/utilities"
|
|
||||||
import {
|
|
||||||
IconChevronDown,
|
|
||||||
IconChevronLeft,
|
|
||||||
IconChevronRight,
|
|
||||||
IconChevronsLeft,
|
|
||||||
IconChevronsRight,
|
|
||||||
IconCircleCheckFilled,
|
|
||||||
IconDotsVertical,
|
|
||||||
IconGripVertical,
|
|
||||||
IconLayoutColumns,
|
|
||||||
IconLoader,
|
|
||||||
IconPlus,
|
|
||||||
IconTrendingUp,
|
|
||||||
} from "@tabler/icons-react"
|
|
||||||
import {
|
|
||||||
flexRender,
|
|
||||||
getCoreRowModel,
|
|
||||||
getFacetedRowModel,
|
|
||||||
getFacetedUniqueValues,
|
|
||||||
getFilteredRowModel,
|
|
||||||
getPaginationRowModel,
|
|
||||||
getSortedRowModel,
|
|
||||||
useReactTable,
|
|
||||||
type ColumnDef,
|
|
||||||
type ColumnFiltersState,
|
|
||||||
type Row,
|
|
||||||
type SortingState,
|
|
||||||
type VisibilityState,
|
|
||||||
} from "@tanstack/react-table"
|
|
||||||
import { Area, AreaChart, CartesianGrid, XAxis } from "recharts"
|
|
||||||
import { toast } from "sonner"
|
|
||||||
import { z } from "zod"
|
|
||||||
|
|
||||||
import { useIsMobile } from "@/registry/new-york-v4/hooks/use-mobile"
|
|
||||||
import { Badge } from "@/registry/new-york-v4/ui/badge"
|
|
||||||
import { Button } from "@/registry/new-york-v4/ui/button"
|
|
||||||
import {
|
|
||||||
ChartContainer,
|
|
||||||
ChartTooltip,
|
|
||||||
ChartTooltipContent,
|
|
||||||
type ChartConfig,
|
|
||||||
} from "@/registry/new-york-v4/ui/chart"
|
|
||||||
import { Checkbox } from "@/registry/new-york-v4/ui/checkbox"
|
|
||||||
import {
|
|
||||||
Drawer,
|
|
||||||
DrawerClose,
|
|
||||||
DrawerContent,
|
|
||||||
DrawerDescription,
|
|
||||||
DrawerFooter,
|
|
||||||
DrawerHeader,
|
|
||||||
DrawerTitle,
|
|
||||||
DrawerTrigger,
|
|
||||||
} from "@/registry/new-york-v4/ui/drawer"
|
|
||||||
import {
|
|
||||||
DropdownMenu,
|
|
||||||
DropdownMenuCheckboxItem,
|
|
||||||
DropdownMenuContent,
|
|
||||||
DropdownMenuItem,
|
|
||||||
DropdownMenuSeparator,
|
|
||||||
DropdownMenuTrigger,
|
|
||||||
} from "@/registry/new-york-v4/ui/dropdown-menu"
|
|
||||||
import { Input } from "@/registry/new-york-v4/ui/input"
|
|
||||||
import { Label } from "@/registry/new-york-v4/ui/label"
|
|
||||||
import {
|
|
||||||
Select,
|
|
||||||
SelectContent,
|
|
||||||
SelectItem,
|
|
||||||
SelectTrigger,
|
|
||||||
SelectValue,
|
|
||||||
} from "@/registry/new-york-v4/ui/select"
|
|
||||||
import { Separator } from "@/registry/new-york-v4/ui/separator"
|
|
||||||
import {
|
|
||||||
Table,
|
|
||||||
TableBody,
|
|
||||||
TableCell,
|
|
||||||
TableHead,
|
|
||||||
TableHeader,
|
|
||||||
TableRow,
|
|
||||||
} from "@/registry/new-york-v4/ui/table"
|
|
||||||
import {
|
|
||||||
Tabs,
|
|
||||||
TabsContent,
|
|
||||||
TabsList,
|
|
||||||
TabsTrigger,
|
|
||||||
} from "@/registry/new-york-v4/ui/tabs"
|
|
||||||
|
|
||||||
export const schema = z.object({
|
|
||||||
id: z.number(),
|
|
||||||
header: z.string(),
|
|
||||||
type: z.string(),
|
|
||||||
status: z.string(),
|
|
||||||
target: z.string(),
|
|
||||||
limit: z.string(),
|
|
||||||
reviewer: z.string(),
|
|
||||||
})
|
|
||||||
|
|
||||||
// Create a separate component for the drag handle
|
|
||||||
function DragHandle({ id }: { id: number }) {
|
|
||||||
const { attributes, listeners } = useSortable({
|
|
||||||
id,
|
|
||||||
})
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Button
|
|
||||||
{...attributes}
|
|
||||||
{...listeners}
|
|
||||||
variant="ghost"
|
|
||||||
size="icon"
|
|
||||||
className="text-muted-foreground size-7 hover:bg-transparent"
|
|
||||||
>
|
|
||||||
<IconGripVertical className="text-muted-foreground size-3" />
|
|
||||||
<span className="sr-only">Drag to reorder</span>
|
|
||||||
</Button>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const columns: ColumnDef<z.infer<typeof schema>>[] = [
|
|
||||||
{
|
|
||||||
id: "drag",
|
|
||||||
header: () => null,
|
|
||||||
cell: ({ row }) => <DragHandle id={row.original.id} />,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "select",
|
|
||||||
header: ({ table }) => (
|
|
||||||
<div className="flex items-center justify-center">
|
|
||||||
<Checkbox
|
|
||||||
checked={
|
|
||||||
table.getIsAllPageRowsSelected() ||
|
|
||||||
(table.getIsSomePageRowsSelected() && "indeterminate")
|
|
||||||
}
|
|
||||||
onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
|
|
||||||
aria-label="Select all"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
cell: ({ row }) => (
|
|
||||||
<div className="flex items-center justify-center">
|
|
||||||
<Checkbox
|
|
||||||
checked={row.getIsSelected()}
|
|
||||||
onCheckedChange={(value) => row.toggleSelected(!!value)}
|
|
||||||
aria-label="Select row"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
enableSorting: false,
|
|
||||||
enableHiding: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "header",
|
|
||||||
header: "Header",
|
|
||||||
cell: ({ row }) => {
|
|
||||||
return <TableCellViewer item={row.original} />
|
|
||||||
},
|
|
||||||
enableHiding: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "type",
|
|
||||||
header: "Section Type",
|
|
||||||
cell: ({ row }) => (
|
|
||||||
<div className="w-32">
|
|
||||||
<Badge variant="outline" className="text-muted-foreground px-1.5">
|
|
||||||
{row.original.type}
|
|
||||||
</Badge>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "status",
|
|
||||||
header: "Status",
|
|
||||||
cell: ({ row }) => (
|
|
||||||
<Badge variant="outline" className="text-muted-foreground px-1.5">
|
|
||||||
{row.original.status === "Done" ? (
|
|
||||||
<IconCircleCheckFilled className="fill-green-500 dark:fill-green-400" />
|
|
||||||
) : (
|
|
||||||
<IconLoader />
|
|
||||||
)}
|
|
||||||
{row.original.status}
|
|
||||||
</Badge>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "target",
|
|
||||||
header: () => <div className="w-full text-right">Target</div>,
|
|
||||||
cell: ({ row }) => (
|
|
||||||
<form
|
|
||||||
onSubmit={(e) => {
|
|
||||||
e.preventDefault()
|
|
||||||
toast.promise(new Promise((resolve) => setTimeout(resolve, 1000)), {
|
|
||||||
loading: `Saving ${row.original.header}`,
|
|
||||||
success: "Done",
|
|
||||||
error: "Error",
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Label htmlFor={`${row.original.id}-target`} className="sr-only">
|
|
||||||
Target
|
|
||||||
</Label>
|
|
||||||
<Input
|
|
||||||
className="hover:bg-input/30 focus-visible:bg-background dark:hover:bg-input/30 dark:focus-visible:bg-input/30 h-8 w-16 border-transparent bg-transparent text-right shadow-none focus-visible:border dark:bg-transparent"
|
|
||||||
defaultValue={row.original.target}
|
|
||||||
id={`${row.original.id}-target`}
|
|
||||||
/>
|
|
||||||
</form>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "limit",
|
|
||||||
header: () => <div className="w-full text-right">Limit</div>,
|
|
||||||
cell: ({ row }) => (
|
|
||||||
<form
|
|
||||||
onSubmit={(e) => {
|
|
||||||
e.preventDefault()
|
|
||||||
toast.promise(new Promise((resolve) => setTimeout(resolve, 1000)), {
|
|
||||||
loading: `Saving ${row.original.header}`,
|
|
||||||
success: "Done",
|
|
||||||
error: "Error",
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Label htmlFor={`${row.original.id}-limit`} className="sr-only">
|
|
||||||
Limit
|
|
||||||
</Label>
|
|
||||||
<Input
|
|
||||||
className="hover:bg-input/30 focus-visible:bg-background dark:hover:bg-input/30 dark:focus-visible:bg-input/30 h-8 w-16 border-transparent bg-transparent text-right shadow-none focus-visible:border dark:bg-transparent"
|
|
||||||
defaultValue={row.original.limit}
|
|
||||||
id={`${row.original.id}-limit`}
|
|
||||||
/>
|
|
||||||
</form>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "reviewer",
|
|
||||||
header: "Reviewer",
|
|
||||||
cell: ({ row }) => {
|
|
||||||
const isAssigned = row.original.reviewer !== "Assign reviewer"
|
|
||||||
|
|
||||||
if (isAssigned) {
|
|
||||||
return row.original.reviewer
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Label htmlFor={`${row.original.id}-reviewer`} className="sr-only">
|
|
||||||
Reviewer
|
|
||||||
</Label>
|
|
||||||
<Select>
|
|
||||||
<SelectTrigger
|
|
||||||
className="w-38 **:data-[slot=select-value]:block **:data-[slot=select-value]:truncate"
|
|
||||||
size="sm"
|
|
||||||
id={`${row.original.id}-reviewer`}
|
|
||||||
>
|
|
||||||
<SelectValue placeholder="Assign reviewer" />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent align="end">
|
|
||||||
<SelectItem value="Eddie Lake">Eddie Lake</SelectItem>
|
|
||||||
<SelectItem value="Jamik Tashpulatov">
|
|
||||||
Jamik Tashpulatov
|
|
||||||
</SelectItem>
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "actions",
|
|
||||||
cell: () => (
|
|
||||||
<DropdownMenu>
|
|
||||||
<DropdownMenuTrigger asChild>
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
className="data-[state=open]:bg-muted text-muted-foreground flex size-8"
|
|
||||||
size="icon"
|
|
||||||
>
|
|
||||||
<IconDotsVertical />
|
|
||||||
<span className="sr-only">Open menu</span>
|
|
||||||
</Button>
|
|
||||||
</DropdownMenuTrigger>
|
|
||||||
<DropdownMenuContent align="end" className="w-32">
|
|
||||||
<DropdownMenuItem>Edit</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem>Make a copy</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem>Favorite</DropdownMenuItem>
|
|
||||||
<DropdownMenuSeparator />
|
|
||||||
<DropdownMenuItem variant="destructive">Delete</DropdownMenuItem>
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
function DraggableRow({ row }: { row: Row<z.infer<typeof schema>> }) {
|
|
||||||
const { transform, transition, setNodeRef, isDragging } = useSortable({
|
|
||||||
id: row.original.id,
|
|
||||||
})
|
|
||||||
|
|
||||||
return (
|
|
||||||
<TableRow
|
|
||||||
data-state={row.getIsSelected() && "selected"}
|
|
||||||
data-dragging={isDragging}
|
|
||||||
ref={setNodeRef}
|
|
||||||
className="relative z-0 data-[dragging=true]:z-10 data-[dragging=true]:opacity-80"
|
|
||||||
style={{
|
|
||||||
transform: CSS.Transform.toString(transform),
|
|
||||||
transition: transition,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{row.getVisibleCells().map((cell) => (
|
|
||||||
<TableCell key={cell.id}>
|
|
||||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
|
||||||
</TableCell>
|
|
||||||
))}
|
|
||||||
</TableRow>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function DataTable({
|
|
||||||
data: initialData,
|
|
||||||
}: {
|
|
||||||
data: z.infer<typeof schema>[]
|
|
||||||
}) {
|
|
||||||
const [data, setData] = React.useState(() => initialData)
|
|
||||||
const [rowSelection, setRowSelection] = React.useState({})
|
|
||||||
const [columnVisibility, setColumnVisibility] =
|
|
||||||
React.useState<VisibilityState>({})
|
|
||||||
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
|
||||||
[]
|
|
||||||
)
|
|
||||||
const [sorting, setSorting] = React.useState<SortingState>([])
|
|
||||||
const [pagination, setPagination] = React.useState({
|
|
||||||
pageIndex: 0,
|
|
||||||
pageSize: 10,
|
|
||||||
})
|
|
||||||
const sortableId = React.useId()
|
|
||||||
const sensors = useSensors(
|
|
||||||
useSensor(MouseSensor, {}),
|
|
||||||
useSensor(TouchSensor, {}),
|
|
||||||
useSensor(KeyboardSensor, {})
|
|
||||||
)
|
|
||||||
|
|
||||||
const dataIds = React.useMemo<UniqueIdentifier[]>(
|
|
||||||
() => data?.map(({ id }) => id) || [],
|
|
||||||
[data]
|
|
||||||
)
|
|
||||||
|
|
||||||
const table = useReactTable({
|
|
||||||
data,
|
|
||||||
columns,
|
|
||||||
state: {
|
|
||||||
sorting,
|
|
||||||
columnVisibility,
|
|
||||||
rowSelection,
|
|
||||||
columnFilters,
|
|
||||||
pagination,
|
|
||||||
},
|
|
||||||
getRowId: (row) => row.id.toString(),
|
|
||||||
enableRowSelection: true,
|
|
||||||
onRowSelectionChange: setRowSelection,
|
|
||||||
onSortingChange: setSorting,
|
|
||||||
onColumnFiltersChange: setColumnFilters,
|
|
||||||
onColumnVisibilityChange: setColumnVisibility,
|
|
||||||
onPaginationChange: setPagination,
|
|
||||||
getCoreRowModel: getCoreRowModel(),
|
|
||||||
getFilteredRowModel: getFilteredRowModel(),
|
|
||||||
getPaginationRowModel: getPaginationRowModel(),
|
|
||||||
getSortedRowModel: getSortedRowModel(),
|
|
||||||
getFacetedRowModel: getFacetedRowModel(),
|
|
||||||
getFacetedUniqueValues: getFacetedUniqueValues(),
|
|
||||||
})
|
|
||||||
|
|
||||||
function handleDragEnd(event: DragEndEvent) {
|
|
||||||
const { active, over } = event
|
|
||||||
if (active && over && active.id !== over.id) {
|
|
||||||
setData((data) => {
|
|
||||||
const oldIndex = dataIds.indexOf(active.id)
|
|
||||||
const newIndex = dataIds.indexOf(over.id)
|
|
||||||
return arrayMove(data, oldIndex, newIndex)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Tabs
|
|
||||||
defaultValue="outline"
|
|
||||||
className="w-full flex-col justify-start gap-6"
|
|
||||||
>
|
|
||||||
<div className="flex items-center justify-between px-4 lg:px-6">
|
|
||||||
<Label htmlFor="view-selector" className="sr-only">
|
|
||||||
View
|
|
||||||
</Label>
|
|
||||||
<Select defaultValue="outline">
|
|
||||||
<SelectTrigger
|
|
||||||
className="flex w-fit @4xl/main:hidden"
|
|
||||||
size="sm"
|
|
||||||
id="view-selector"
|
|
||||||
>
|
|
||||||
<SelectValue placeholder="Select a view" />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
<SelectItem value="outline">Outline</SelectItem>
|
|
||||||
<SelectItem value="past-performance">Past Performance</SelectItem>
|
|
||||||
<SelectItem value="key-personnel">Key Personnel</SelectItem>
|
|
||||||
<SelectItem value="focus-documents">Focus Documents</SelectItem>
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
<TabsList className="**:data-[slot=badge]:bg-muted-foreground/30 hidden **:data-[slot=badge]:size-5 **:data-[slot=badge]:rounded-full **:data-[slot=badge]:px-1 @4xl/main:flex">
|
|
||||||
<TabsTrigger value="outline">Outline</TabsTrigger>
|
|
||||||
<TabsTrigger value="past-performance">
|
|
||||||
Past Performance <Badge variant="secondary">3</Badge>
|
|
||||||
</TabsTrigger>
|
|
||||||
<TabsTrigger value="key-personnel">
|
|
||||||
Key Personnel <Badge variant="secondary">2</Badge>
|
|
||||||
</TabsTrigger>
|
|
||||||
<TabsTrigger value="focus-documents">Focus Documents</TabsTrigger>
|
|
||||||
</TabsList>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<DropdownMenu>
|
|
||||||
<DropdownMenuTrigger asChild>
|
|
||||||
<Button variant="outline" size="sm">
|
|
||||||
<IconLayoutColumns />
|
|
||||||
<span className="hidden lg:inline">Customize Columns</span>
|
|
||||||
<span className="lg:hidden">Columns</span>
|
|
||||||
<IconChevronDown />
|
|
||||||
</Button>
|
|
||||||
</DropdownMenuTrigger>
|
|
||||||
<DropdownMenuContent align="end" className="w-56">
|
|
||||||
{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>
|
|
||||||
<Button variant="outline" size="sm">
|
|
||||||
<IconPlus />
|
|
||||||
<span className="hidden lg:inline">Add Section</span>
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<TabsContent
|
|
||||||
value="outline"
|
|
||||||
className="relative flex flex-col gap-4 overflow-auto px-4 lg:px-6"
|
|
||||||
>
|
|
||||||
<div className="overflow-hidden rounded-lg border">
|
|
||||||
<DndContext
|
|
||||||
collisionDetection={closestCenter}
|
|
||||||
modifiers={[restrictToVerticalAxis]}
|
|
||||||
onDragEnd={handleDragEnd}
|
|
||||||
sensors={sensors}
|
|
||||||
id={sortableId}
|
|
||||||
>
|
|
||||||
<Table>
|
|
||||||
<TableHeader className="bg-muted sticky top-0 z-10">
|
|
||||||
{table.getHeaderGroups().map((headerGroup) => (
|
|
||||||
<TableRow key={headerGroup.id}>
|
|
||||||
{headerGroup.headers.map((header) => {
|
|
||||||
return (
|
|
||||||
<TableHead key={header.id} colSpan={header.colSpan}>
|
|
||||||
{header.isPlaceholder
|
|
||||||
? null
|
|
||||||
: flexRender(
|
|
||||||
header.column.columnDef.header,
|
|
||||||
header.getContext()
|
|
||||||
)}
|
|
||||||
</TableHead>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</TableRow>
|
|
||||||
))}
|
|
||||||
</TableHeader>
|
|
||||||
<TableBody className="**:data-[slot=table-cell]:first:w-8">
|
|
||||||
{table.getRowModel().rows?.length ? (
|
|
||||||
<SortableContext
|
|
||||||
items={dataIds}
|
|
||||||
strategy={verticalListSortingStrategy}
|
|
||||||
>
|
|
||||||
{table.getRowModel().rows.map((row) => (
|
|
||||||
<DraggableRow key={row.id} row={row} />
|
|
||||||
))}
|
|
||||||
</SortableContext>
|
|
||||||
) : (
|
|
||||||
<TableRow>
|
|
||||||
<TableCell
|
|
||||||
colSpan={columns.length}
|
|
||||||
className="h-24 text-center"
|
|
||||||
>
|
|
||||||
No results.
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
)}
|
|
||||||
</TableBody>
|
|
||||||
</Table>
|
|
||||||
</DndContext>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center justify-between px-4">
|
|
||||||
<div className="text-muted-foreground hidden flex-1 text-sm lg:flex">
|
|
||||||
{table.getFilteredSelectedRowModel().rows.length} of{" "}
|
|
||||||
{table.getFilteredRowModel().rows.length} row(s) selected.
|
|
||||||
</div>
|
|
||||||
<div className="flex w-full items-center gap-8 lg:w-fit">
|
|
||||||
<div className="hidden items-center gap-2 lg:flex">
|
|
||||||
<Label htmlFor="rows-per-page" className="text-sm font-medium">
|
|
||||||
Rows per page
|
|
||||||
</Label>
|
|
||||||
<Select
|
|
||||||
value={`${table.getState().pagination.pageSize}`}
|
|
||||||
onValueChange={(value) => {
|
|
||||||
table.setPageSize(Number(value))
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<SelectTrigger size="sm" className="w-20" id="rows-per-page">
|
|
||||||
<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-fit items-center justify-center text-sm font-medium">
|
|
||||||
Page {table.getState().pagination.pageIndex + 1} of{" "}
|
|
||||||
{table.getPageCount()}
|
|
||||||
</div>
|
|
||||||
<div className="ml-auto flex items-center gap-2 lg:ml-0">
|
|
||||||
<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>
|
|
||||||
<IconChevronsLeft />
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
className="size-8"
|
|
||||||
size="icon"
|
|
||||||
onClick={() => table.previousPage()}
|
|
||||||
disabled={!table.getCanPreviousPage()}
|
|
||||||
>
|
|
||||||
<span className="sr-only">Go to previous page</span>
|
|
||||||
<IconChevronLeft />
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
className="size-8"
|
|
||||||
size="icon"
|
|
||||||
onClick={() => table.nextPage()}
|
|
||||||
disabled={!table.getCanNextPage()}
|
|
||||||
>
|
|
||||||
<span className="sr-only">Go to next page</span>
|
|
||||||
<IconChevronRight />
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
className="hidden size-8 lg:flex"
|
|
||||||
size="icon"
|
|
||||||
onClick={() => table.setPageIndex(table.getPageCount() - 1)}
|
|
||||||
disabled={!table.getCanNextPage()}
|
|
||||||
>
|
|
||||||
<span className="sr-only">Go to last page</span>
|
|
||||||
<IconChevronsRight />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</TabsContent>
|
|
||||||
<TabsContent
|
|
||||||
value="past-performance"
|
|
||||||
className="flex flex-col px-4 lg:px-6"
|
|
||||||
>
|
|
||||||
<div className="aspect-video w-full flex-1 rounded-lg border border-dashed"></div>
|
|
||||||
</TabsContent>
|
|
||||||
<TabsContent value="key-personnel" className="flex flex-col px-4 lg:px-6">
|
|
||||||
<div className="aspect-video w-full flex-1 rounded-lg border border-dashed"></div>
|
|
||||||
</TabsContent>
|
|
||||||
<TabsContent
|
|
||||||
value="focus-documents"
|
|
||||||
className="flex flex-col px-4 lg:px-6"
|
|
||||||
>
|
|
||||||
<div className="aspect-video w-full flex-1 rounded-lg border border-dashed"></div>
|
|
||||||
</TabsContent>
|
|
||||||
</Tabs>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const chartData = [
|
|
||||||
{ month: "January", desktop: 186, mobile: 80 },
|
|
||||||
{ month: "February", desktop: 305, mobile: 200 },
|
|
||||||
{ month: "March", desktop: 237, mobile: 120 },
|
|
||||||
{ month: "April", desktop: 73, mobile: 190 },
|
|
||||||
{ month: "May", desktop: 209, mobile: 130 },
|
|
||||||
{ month: "June", desktop: 214, mobile: 140 },
|
|
||||||
]
|
|
||||||
|
|
||||||
const chartConfig = {
|
|
||||||
desktop: {
|
|
||||||
label: "Desktop",
|
|
||||||
color: "var(--primary)",
|
|
||||||
},
|
|
||||||
mobile: {
|
|
||||||
label: "Mobile",
|
|
||||||
color: "var(--primary)",
|
|
||||||
},
|
|
||||||
} satisfies ChartConfig
|
|
||||||
|
|
||||||
function TableCellViewer({ item }: { item: z.infer<typeof schema> }) {
|
|
||||||
const isMobile = useIsMobile()
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Drawer direction={isMobile ? "bottom" : "right"}>
|
|
||||||
<DrawerTrigger asChild>
|
|
||||||
<Button variant="link" className="text-foreground w-fit px-0 text-left">
|
|
||||||
{item.header}
|
|
||||||
</Button>
|
|
||||||
</DrawerTrigger>
|
|
||||||
<DrawerContent>
|
|
||||||
<DrawerHeader className="gap-1">
|
|
||||||
<DrawerTitle>{item.header}</DrawerTitle>
|
|
||||||
<DrawerDescription>
|
|
||||||
Showing total visitors for the last 6 months
|
|
||||||
</DrawerDescription>
|
|
||||||
</DrawerHeader>
|
|
||||||
<div className="flex flex-col gap-4 overflow-y-auto px-4 text-sm">
|
|
||||||
{!isMobile && (
|
|
||||||
<>
|
|
||||||
<ChartContainer config={chartConfig}>
|
|
||||||
<AreaChart
|
|
||||||
accessibilityLayer
|
|
||||||
data={chartData}
|
|
||||||
margin={{
|
|
||||||
left: 0,
|
|
||||||
right: 10,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<CartesianGrid vertical={false} />
|
|
||||||
<XAxis
|
|
||||||
dataKey="month"
|
|
||||||
tickLine={false}
|
|
||||||
axisLine={false}
|
|
||||||
tickMargin={8}
|
|
||||||
tickFormatter={(value) => value.slice(0, 3)}
|
|
||||||
hide
|
|
||||||
/>
|
|
||||||
<ChartTooltip
|
|
||||||
cursor={false}
|
|
||||||
content={<ChartTooltipContent indicator="dot" />}
|
|
||||||
/>
|
|
||||||
<Area
|
|
||||||
dataKey="mobile"
|
|
||||||
type="natural"
|
|
||||||
fill="var(--color-mobile)"
|
|
||||||
fillOpacity={0.6}
|
|
||||||
stroke="var(--color-mobile)"
|
|
||||||
stackId="a"
|
|
||||||
/>
|
|
||||||
<Area
|
|
||||||
dataKey="desktop"
|
|
||||||
type="natural"
|
|
||||||
fill="var(--color-desktop)"
|
|
||||||
fillOpacity={0.4}
|
|
||||||
stroke="var(--color-desktop)"
|
|
||||||
stackId="a"
|
|
||||||
/>
|
|
||||||
</AreaChart>
|
|
||||||
</ChartContainer>
|
|
||||||
<Separator />
|
|
||||||
<div className="grid gap-2">
|
|
||||||
<div className="flex gap-2 leading-none font-medium">
|
|
||||||
Trending up by 5.2% this month{" "}
|
|
||||||
<IconTrendingUp className="size-4" />
|
|
||||||
</div>
|
|
||||||
<div className="text-muted-foreground">
|
|
||||||
Showing total visitors for the last 6 months. This is just
|
|
||||||
some random text to test the layout. It spans multiple lines
|
|
||||||
and should wrap around.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Separator />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
<form className="flex flex-col gap-4">
|
|
||||||
<div className="flex flex-col gap-3">
|
|
||||||
<Label htmlFor="header">Header</Label>
|
|
||||||
<Input id="header" defaultValue={item.header} />
|
|
||||||
</div>
|
|
||||||
<div className="grid grid-cols-2 gap-4">
|
|
||||||
<div className="flex flex-col gap-3">
|
|
||||||
<Label htmlFor="type">Type</Label>
|
|
||||||
<Select defaultValue={item.type}>
|
|
||||||
<SelectTrigger id="type" className="w-full">
|
|
||||||
<SelectValue placeholder="Select a type" />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
<SelectItem value="Table of Contents">
|
|
||||||
Table of Contents
|
|
||||||
</SelectItem>
|
|
||||||
<SelectItem value="Executive Summary">
|
|
||||||
Executive Summary
|
|
||||||
</SelectItem>
|
|
||||||
<SelectItem value="Technical Approach">
|
|
||||||
Technical Approach
|
|
||||||
</SelectItem>
|
|
||||||
<SelectItem value="Design">Design</SelectItem>
|
|
||||||
<SelectItem value="Capabilities">Capabilities</SelectItem>
|
|
||||||
<SelectItem value="Focus Documents">
|
|
||||||
Focus Documents
|
|
||||||
</SelectItem>
|
|
||||||
<SelectItem value="Narrative">Narrative</SelectItem>
|
|
||||||
<SelectItem value="Cover Page">Cover Page</SelectItem>
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col gap-3">
|
|
||||||
<Label htmlFor="status">Status</Label>
|
|
||||||
<Select defaultValue={item.status}>
|
|
||||||
<SelectTrigger id="status" className="w-full">
|
|
||||||
<SelectValue placeholder="Select a status" />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
<SelectItem value="Done">Done</SelectItem>
|
|
||||||
<SelectItem value="In Progress">In Progress</SelectItem>
|
|
||||||
<SelectItem value="Not Started">Not Started</SelectItem>
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="grid grid-cols-2 gap-4">
|
|
||||||
<div className="flex flex-col gap-3">
|
|
||||||
<Label htmlFor="target">Target</Label>
|
|
||||||
<Input id="target" defaultValue={item.target} />
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col gap-3">
|
|
||||||
<Label htmlFor="limit">Limit</Label>
|
|
||||||
<Input id="limit" defaultValue={item.limit} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col gap-3">
|
|
||||||
<Label htmlFor="reviewer">Reviewer</Label>
|
|
||||||
<Select defaultValue={item.reviewer}>
|
|
||||||
<SelectTrigger id="reviewer" className="w-full">
|
|
||||||
<SelectValue placeholder="Select a reviewer" />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
<SelectItem value="Eddie Lake">Eddie Lake</SelectItem>
|
|
||||||
<SelectItem value="Jamik Tashpulatov">
|
|
||||||
Jamik Tashpulatov
|
|
||||||
</SelectItem>
|
|
||||||
<SelectItem value="Emily Whalen">Emily Whalen</SelectItem>
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<DrawerFooter>
|
|
||||||
<Button>Submit</Button>
|
|
||||||
<DrawerClose asChild>
|
|
||||||
<Button variant="outline">Done</Button>
|
|
||||||
</DrawerClose>
|
|
||||||
</DrawerFooter>
|
|
||||||
</DrawerContent>
|
|
||||||
</Drawer>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
"use client"
|
|
||||||
|
|
||||||
import {
|
|
||||||
IconDots,
|
|
||||||
IconFolder,
|
|
||||||
IconShare3,
|
|
||||||
IconTrash,
|
|
||||||
type Icon,
|
|
||||||
} from "@tabler/icons-react"
|
|
||||||
|
|
||||||
import {
|
|
||||||
DropdownMenu,
|
|
||||||
DropdownMenuContent,
|
|
||||||
DropdownMenuItem,
|
|
||||||
DropdownMenuSeparator,
|
|
||||||
DropdownMenuTrigger,
|
|
||||||
} from "@/registry/new-york-v4/ui/dropdown-menu"
|
|
||||||
import {
|
|
||||||
SidebarGroup,
|
|
||||||
SidebarGroupLabel,
|
|
||||||
SidebarMenu,
|
|
||||||
SidebarMenuAction,
|
|
||||||
SidebarMenuButton,
|
|
||||||
SidebarMenuItem,
|
|
||||||
useSidebar,
|
|
||||||
} from "@/registry/new-york-v4/ui/sidebar"
|
|
||||||
|
|
||||||
export function NavDocuments({
|
|
||||||
items,
|
|
||||||
}: {
|
|
||||||
items: {
|
|
||||||
name: string
|
|
||||||
url: string
|
|
||||||
icon: Icon
|
|
||||||
}[]
|
|
||||||
}) {
|
|
||||||
const { isMobile } = useSidebar()
|
|
||||||
|
|
||||||
return (
|
|
||||||
<SidebarGroup className="group-data-[collapsible=icon]:hidden">
|
|
||||||
<SidebarGroupLabel>Documents</SidebarGroupLabel>
|
|
||||||
<SidebarMenu>
|
|
||||||
{items.map((item) => (
|
|
||||||
<SidebarMenuItem key={item.name}>
|
|
||||||
<SidebarMenuButton asChild>
|
|
||||||
<a href={item.url}>
|
|
||||||
<item.icon />
|
|
||||||
<span>{item.name}</span>
|
|
||||||
</a>
|
|
||||||
</SidebarMenuButton>
|
|
||||||
<DropdownMenu>
|
|
||||||
<DropdownMenuTrigger asChild>
|
|
||||||
<SidebarMenuAction
|
|
||||||
showOnHover
|
|
||||||
className="data-[state=open]:bg-accent rounded-sm"
|
|
||||||
>
|
|
||||||
<IconDots />
|
|
||||||
<span className="sr-only">More</span>
|
|
||||||
</SidebarMenuAction>
|
|
||||||
</DropdownMenuTrigger>
|
|
||||||
<DropdownMenuContent
|
|
||||||
className="w-24 rounded-lg"
|
|
||||||
side={isMobile ? "bottom" : "right"}
|
|
||||||
align={isMobile ? "end" : "start"}
|
|
||||||
>
|
|
||||||
<DropdownMenuItem>
|
|
||||||
<IconFolder />
|
|
||||||
<span>Open</span>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem>
|
|
||||||
<IconShare3 />
|
|
||||||
<span>Share</span>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuSeparator />
|
|
||||||
<DropdownMenuItem variant="destructive">
|
|
||||||
<IconTrash />
|
|
||||||
<span>Delete</span>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
</SidebarMenuItem>
|
|
||||||
))}
|
|
||||||
<SidebarMenuItem>
|
|
||||||
<SidebarMenuButton className="text-sidebar-foreground/70">
|
|
||||||
<IconDots className="text-sidebar-foreground/70" />
|
|
||||||
<span>More</span>
|
|
||||||
</SidebarMenuButton>
|
|
||||||
</SidebarMenuItem>
|
|
||||||
</SidebarMenu>
|
|
||||||
</SidebarGroup>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
"use client"
|
|
||||||
|
|
||||||
import { type Icon } from "@tabler/icons-react"
|
|
||||||
|
|
||||||
import {
|
|
||||||
SidebarGroup,
|
|
||||||
SidebarGroupContent,
|
|
||||||
SidebarGroupLabel,
|
|
||||||
SidebarMenu,
|
|
||||||
SidebarMenuButton,
|
|
||||||
SidebarMenuItem,
|
|
||||||
} from "@/registry/new-york-v4/ui/sidebar"
|
|
||||||
|
|
||||||
export function NavMain({
|
|
||||||
items,
|
|
||||||
}: {
|
|
||||||
items: {
|
|
||||||
title: string
|
|
||||||
url: string
|
|
||||||
icon?: Icon
|
|
||||||
}[]
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<SidebarGroup>
|
|
||||||
<SidebarGroupContent>
|
|
||||||
<SidebarGroupLabel>Home</SidebarGroupLabel>
|
|
||||||
<SidebarMenu>
|
|
||||||
{items.map((item) => (
|
|
||||||
<SidebarMenuItem key={item.title}>
|
|
||||||
<SidebarMenuButton tooltip={item.title}>
|
|
||||||
{item.icon && <item.icon />}
|
|
||||||
<span>{item.title}</span>
|
|
||||||
</SidebarMenuButton>
|
|
||||||
</SidebarMenuItem>
|
|
||||||
))}
|
|
||||||
</SidebarMenu>
|
|
||||||
</SidebarGroupContent>
|
|
||||||
</SidebarGroup>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
"use client"
|
|
||||||
|
|
||||||
import * as React from "react"
|
|
||||||
import { type Icon } from "@tabler/icons-react"
|
|
||||||
|
|
||||||
import {
|
|
||||||
SidebarGroup,
|
|
||||||
SidebarGroupContent,
|
|
||||||
SidebarMenu,
|
|
||||||
SidebarMenuButton,
|
|
||||||
SidebarMenuItem,
|
|
||||||
} from "@/registry/new-york-v4/ui/sidebar"
|
|
||||||
|
|
||||||
export function NavSecondary({
|
|
||||||
items,
|
|
||||||
...props
|
|
||||||
}: {
|
|
||||||
items: {
|
|
||||||
title: string
|
|
||||||
url: string
|
|
||||||
icon: Icon
|
|
||||||
}[]
|
|
||||||
} & React.ComponentPropsWithoutRef<typeof SidebarGroup>) {
|
|
||||||
return (
|
|
||||||
<SidebarGroup {...props}>
|
|
||||||
<SidebarGroupContent>
|
|
||||||
<SidebarMenu>
|
|
||||||
{items.map((item) => (
|
|
||||||
<SidebarMenuItem key={item.title}>
|
|
||||||
<SidebarMenuButton asChild>
|
|
||||||
<a href={item.url}>
|
|
||||||
<item.icon />
|
|
||||||
<span>{item.title}</span>
|
|
||||||
</a>
|
|
||||||
</SidebarMenuButton>
|
|
||||||
</SidebarMenuItem>
|
|
||||||
))}
|
|
||||||
</SidebarMenu>
|
|
||||||
</SidebarGroupContent>
|
|
||||||
</SidebarGroup>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,110 +0,0 @@
|
|||||||
"use client"
|
|
||||||
|
|
||||||
import {
|
|
||||||
IconCreditCard,
|
|
||||||
IconDotsVertical,
|
|
||||||
IconLogout,
|
|
||||||
IconNotification,
|
|
||||||
IconUserCircle,
|
|
||||||
} from "@tabler/icons-react"
|
|
||||||
|
|
||||||
import {
|
|
||||||
Avatar,
|
|
||||||
AvatarFallback,
|
|
||||||
AvatarImage,
|
|
||||||
} from "@/registry/new-york-v4/ui/avatar"
|
|
||||||
import {
|
|
||||||
DropdownMenu,
|
|
||||||
DropdownMenuContent,
|
|
||||||
DropdownMenuGroup,
|
|
||||||
DropdownMenuItem,
|
|
||||||
DropdownMenuLabel,
|
|
||||||
DropdownMenuSeparator,
|
|
||||||
DropdownMenuTrigger,
|
|
||||||
} from "@/registry/new-york-v4/ui/dropdown-menu"
|
|
||||||
import {
|
|
||||||
SidebarMenu,
|
|
||||||
SidebarMenuButton,
|
|
||||||
SidebarMenuItem,
|
|
||||||
useSidebar,
|
|
||||||
} from "@/registry/new-york-v4/ui/sidebar"
|
|
||||||
|
|
||||||
export function NavUser({
|
|
||||||
user,
|
|
||||||
}: {
|
|
||||||
user: {
|
|
||||||
name: string
|
|
||||||
email: string
|
|
||||||
avatar: string
|
|
||||||
}
|
|
||||||
}) {
|
|
||||||
const { isMobile } = useSidebar()
|
|
||||||
|
|
||||||
return (
|
|
||||||
<SidebarMenu>
|
|
||||||
<SidebarMenuItem>
|
|
||||||
<DropdownMenu>
|
|
||||||
<DropdownMenuTrigger asChild>
|
|
||||||
<SidebarMenuButton
|
|
||||||
size="lg"
|
|
||||||
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
|
|
||||||
>
|
|
||||||
<Avatar className="h-8 w-8 rounded-lg grayscale">
|
|
||||||
<AvatarImage src={user.avatar} alt={user.name} />
|
|
||||||
<AvatarFallback className="rounded-lg">CN</AvatarFallback>
|
|
||||||
</Avatar>
|
|
||||||
<div className="grid flex-1 text-left text-sm leading-tight">
|
|
||||||
<span className="truncate font-medium">{user.name}</span>
|
|
||||||
<span className="text-muted-foreground truncate text-xs">
|
|
||||||
{user.email}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<IconDotsVertical className="ml-auto size-4" />
|
|
||||||
</SidebarMenuButton>
|
|
||||||
</DropdownMenuTrigger>
|
|
||||||
<DropdownMenuContent
|
|
||||||
className="w-(--radix-dropdown-menu-trigger-width) min-w-56 rounded-lg"
|
|
||||||
side={isMobile ? "bottom" : "right"}
|
|
||||||
align="end"
|
|
||||||
sideOffset={4}
|
|
||||||
>
|
|
||||||
<DropdownMenuLabel className="p-0 font-normal">
|
|
||||||
<div className="flex items-center gap-2 px-1 py-1.5 text-left text-sm">
|
|
||||||
<Avatar className="h-8 w-8 rounded-lg">
|
|
||||||
<AvatarImage src={user.avatar} alt={user.name} />
|
|
||||||
<AvatarFallback className="rounded-lg">CN</AvatarFallback>
|
|
||||||
</Avatar>
|
|
||||||
<div className="grid flex-1 text-left text-sm leading-tight">
|
|
||||||
<span className="truncate font-medium">{user.name}</span>
|
|
||||||
<span className="text-muted-foreground truncate text-xs">
|
|
||||||
{user.email}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</DropdownMenuLabel>
|
|
||||||
<DropdownMenuSeparator />
|
|
||||||
<DropdownMenuGroup>
|
|
||||||
<DropdownMenuItem>
|
|
||||||
<IconUserCircle />
|
|
||||||
Account
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem>
|
|
||||||
<IconCreditCard />
|
|
||||||
Billing
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem>
|
|
||||||
<IconNotification />
|
|
||||||
Notifications
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</DropdownMenuGroup>
|
|
||||||
<DropdownMenuSeparator />
|
|
||||||
<DropdownMenuItem>
|
|
||||||
<IconLogout />
|
|
||||||
Log out
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
</SidebarMenuItem>
|
|
||||||
</SidebarMenu>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,102 +0,0 @@
|
|||||||
import { IconTrendingDown, IconTrendingUp } from "@tabler/icons-react"
|
|
||||||
|
|
||||||
import { Badge } from "@/registry/new-york-v4/ui/badge"
|
|
||||||
import {
|
|
||||||
Card,
|
|
||||||
CardAction,
|
|
||||||
CardDescription,
|
|
||||||
CardFooter,
|
|
||||||
CardHeader,
|
|
||||||
CardTitle,
|
|
||||||
} from "@/registry/new-york-v4/ui/card"
|
|
||||||
|
|
||||||
export function SectionCards() {
|
|
||||||
return (
|
|
||||||
<div className="grid grid-cols-1 gap-4 px-4 *:data-[slot=card]:bg-gradient-to-t *:data-[slot=card]:shadow-xs lg:px-6 @xl/main:grid-cols-2 @5xl/main:grid-cols-4">
|
|
||||||
<Card className="@container/card">
|
|
||||||
<CardHeader>
|
|
||||||
<CardDescription>Total Revenue</CardDescription>
|
|
||||||
<CardTitle className="text-2xl font-semibold tabular-nums @[250px]/card:text-3xl">
|
|
||||||
$1,250.00
|
|
||||||
</CardTitle>
|
|
||||||
<CardAction>
|
|
||||||
<Badge variant="outline">
|
|
||||||
<IconTrendingUp />
|
|
||||||
+12.5%
|
|
||||||
</Badge>
|
|
||||||
</CardAction>
|
|
||||||
</CardHeader>
|
|
||||||
<CardFooter className="flex-col items-start gap-1.5 text-sm">
|
|
||||||
<div className="line-clamp-1 flex gap-2 font-medium">
|
|
||||||
Trending up this month <IconTrendingUp className="size-4" />
|
|
||||||
</div>
|
|
||||||
<div className="text-muted-foreground">
|
|
||||||
Visitors for the last 6 months
|
|
||||||
</div>
|
|
||||||
</CardFooter>
|
|
||||||
</Card>
|
|
||||||
<Card className="@container/card">
|
|
||||||
<CardHeader>
|
|
||||||
<CardDescription>New Customers</CardDescription>
|
|
||||||
<CardTitle className="text-2xl font-semibold tabular-nums @[250px]/card:text-3xl">
|
|
||||||
1,234
|
|
||||||
</CardTitle>
|
|
||||||
<CardAction>
|
|
||||||
<Badge variant="outline">
|
|
||||||
<IconTrendingDown />
|
|
||||||
-20%
|
|
||||||
</Badge>
|
|
||||||
</CardAction>
|
|
||||||
</CardHeader>
|
|
||||||
<CardFooter className="flex-col items-start gap-1.5 text-sm">
|
|
||||||
<div className="line-clamp-1 flex gap-2 font-medium">
|
|
||||||
Down 20% this period <IconTrendingDown className="size-4" />
|
|
||||||
</div>
|
|
||||||
<div className="text-muted-foreground">
|
|
||||||
Acquisition needs attention
|
|
||||||
</div>
|
|
||||||
</CardFooter>
|
|
||||||
</Card>
|
|
||||||
<Card className="@container/card">
|
|
||||||
<CardHeader>
|
|
||||||
<CardDescription>Active Accounts</CardDescription>
|
|
||||||
<CardTitle className="text-2xl font-semibold tabular-nums @[250px]/card:text-3xl">
|
|
||||||
45,678
|
|
||||||
</CardTitle>
|
|
||||||
<CardAction>
|
|
||||||
<Badge variant="outline">
|
|
||||||
<IconTrendingUp />
|
|
||||||
+12.5%
|
|
||||||
</Badge>
|
|
||||||
</CardAction>
|
|
||||||
</CardHeader>
|
|
||||||
<CardFooter className="flex-col items-start gap-1.5 text-sm">
|
|
||||||
<div className="line-clamp-1 flex gap-2 font-medium">
|
|
||||||
Strong user retention <IconTrendingUp className="size-4" />
|
|
||||||
</div>
|
|
||||||
<div className="text-muted-foreground">Engagement exceed targets</div>
|
|
||||||
</CardFooter>
|
|
||||||
</Card>
|
|
||||||
<Card className="@container/card">
|
|
||||||
<CardHeader>
|
|
||||||
<CardDescription>Growth Rate</CardDescription>
|
|
||||||
<CardTitle className="text-2xl font-semibold tabular-nums @[250px]/card:text-3xl">
|
|
||||||
4.5%
|
|
||||||
</CardTitle>
|
|
||||||
<CardAction>
|
|
||||||
<Badge variant="outline">
|
|
||||||
<IconTrendingUp />
|
|
||||||
+4.5%
|
|
||||||
</Badge>
|
|
||||||
</CardAction>
|
|
||||||
</CardHeader>
|
|
||||||
<CardFooter className="flex-col items-start gap-1.5 text-sm">
|
|
||||||
<div className="line-clamp-1 flex gap-2 font-medium">
|
|
||||||
Steady performance increase <IconTrendingUp className="size-4" />
|
|
||||||
</div>
|
|
||||||
<div className="text-muted-foreground">Meets growth projections</div>
|
|
||||||
</CardFooter>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
import { IconCirclePlusFilled } from "@tabler/icons-react"
|
|
||||||
|
|
||||||
import { Button } from "@/registry/new-york-v4/ui/button"
|
|
||||||
|
|
||||||
export function SiteHeader() {
|
|
||||||
return (
|
|
||||||
<header className="bg-background/90 sticky top-0 z-10 flex h-(--header-height) shrink-0 items-center gap-2 border-b transition-[width,height] ease-linear group-has-data-[collapsible=icon]/sidebar-wrapper:h-(--header-height)">
|
|
||||||
<div className="flex w-full items-center gap-1 px-4 lg:gap-2 lg:px-6">
|
|
||||||
<h1 className="text-base font-medium">Documents</h1>
|
|
||||||
<div className="ml-auto flex items-center gap-2">
|
|
||||||
<Button size="sm" className="hidden h-7 sm:flex">
|
|
||||||
<IconCirclePlusFilled />
|
|
||||||
<span>Quick Create</span>
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,614 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"id": 1,
|
|
||||||
"header": "Cover page",
|
|
||||||
"type": "Cover page",
|
|
||||||
"status": "In Process",
|
|
||||||
"target": "18",
|
|
||||||
"limit": "5",
|
|
||||||
"reviewer": "Eddie Lake"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 2,
|
|
||||||
"header": "Table of contents",
|
|
||||||
"type": "Table of contents",
|
|
||||||
"status": "Done",
|
|
||||||
"target": "29",
|
|
||||||
"limit": "24",
|
|
||||||
"reviewer": "Eddie Lake"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 3,
|
|
||||||
"header": "Executive summary",
|
|
||||||
"type": "Narrative",
|
|
||||||
"status": "Done",
|
|
||||||
"target": "10",
|
|
||||||
"limit": "13",
|
|
||||||
"reviewer": "Eddie Lake"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 4,
|
|
||||||
"header": "Technical approach",
|
|
||||||
"type": "Narrative",
|
|
||||||
"status": "Done",
|
|
||||||
"target": "27",
|
|
||||||
"limit": "23",
|
|
||||||
"reviewer": "Jamik Tashpulatov"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 5,
|
|
||||||
"header": "Design",
|
|
||||||
"type": "Narrative",
|
|
||||||
"status": "In Process",
|
|
||||||
"target": "2",
|
|
||||||
"limit": "16",
|
|
||||||
"reviewer": "Jamik Tashpulatov"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 6,
|
|
||||||
"header": "Capabilities",
|
|
||||||
"type": "Narrative",
|
|
||||||
"status": "In Process",
|
|
||||||
"target": "20",
|
|
||||||
"limit": "8",
|
|
||||||
"reviewer": "Jamik Tashpulatov"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 7,
|
|
||||||
"header": "Integration with existing systems",
|
|
||||||
"type": "Narrative",
|
|
||||||
"status": "In Process",
|
|
||||||
"target": "19",
|
|
||||||
"limit": "21",
|
|
||||||
"reviewer": "Jamik Tashpulatov"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 8,
|
|
||||||
"header": "Innovation and Advantages",
|
|
||||||
"type": "Narrative",
|
|
||||||
"status": "Done",
|
|
||||||
"target": "25",
|
|
||||||
"limit": "26",
|
|
||||||
"reviewer": "Assign reviewer"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 9,
|
|
||||||
"header": "Overview of EMR's Innovative Solutions",
|
|
||||||
"type": "Technical content",
|
|
||||||
"status": "Done",
|
|
||||||
"target": "7",
|
|
||||||
"limit": "23",
|
|
||||||
"reviewer": "Assign reviewer"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 10,
|
|
||||||
"header": "Advanced Algorithms and Machine Learning",
|
|
||||||
"type": "Narrative",
|
|
||||||
"status": "Done",
|
|
||||||
"target": "30",
|
|
||||||
"limit": "28",
|
|
||||||
"reviewer": "Assign reviewer"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 11,
|
|
||||||
"header": "Adaptive Communication Protocols",
|
|
||||||
"type": "Narrative",
|
|
||||||
"status": "Done",
|
|
||||||
"target": "9",
|
|
||||||
"limit": "31",
|
|
||||||
"reviewer": "Assign reviewer"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 12,
|
|
||||||
"header": "Advantages Over Current Technologies",
|
|
||||||
"type": "Narrative",
|
|
||||||
"status": "Done",
|
|
||||||
"target": "12",
|
|
||||||
"limit": "0",
|
|
||||||
"reviewer": "Assign reviewer"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 13,
|
|
||||||
"header": "Past Performance",
|
|
||||||
"type": "Narrative",
|
|
||||||
"status": "Done",
|
|
||||||
"target": "22",
|
|
||||||
"limit": "33",
|
|
||||||
"reviewer": "Assign reviewer"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 14,
|
|
||||||
"header": "Customer Feedback and Satisfaction Levels",
|
|
||||||
"type": "Narrative",
|
|
||||||
"status": "Done",
|
|
||||||
"target": "15",
|
|
||||||
"limit": "34",
|
|
||||||
"reviewer": "Assign reviewer"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 15,
|
|
||||||
"header": "Implementation Challenges and Solutions",
|
|
||||||
"type": "Narrative",
|
|
||||||
"status": "Done",
|
|
||||||
"target": "3",
|
|
||||||
"limit": "35",
|
|
||||||
"reviewer": "Assign reviewer"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 16,
|
|
||||||
"header": "Security Measures and Data Protection Policies",
|
|
||||||
"type": "Narrative",
|
|
||||||
"status": "In Process",
|
|
||||||
"target": "6",
|
|
||||||
"limit": "36",
|
|
||||||
"reviewer": "Assign reviewer"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 17,
|
|
||||||
"header": "Scalability and Future Proofing",
|
|
||||||
"type": "Narrative",
|
|
||||||
"status": "Done",
|
|
||||||
"target": "4",
|
|
||||||
"limit": "37",
|
|
||||||
"reviewer": "Assign reviewer"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 18,
|
|
||||||
"header": "Cost-Benefit Analysis",
|
|
||||||
"type": "Plain language",
|
|
||||||
"status": "Done",
|
|
||||||
"target": "14",
|
|
||||||
"limit": "38",
|
|
||||||
"reviewer": "Assign reviewer"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 19,
|
|
||||||
"header": "User Training and Onboarding Experience",
|
|
||||||
"type": "Narrative",
|
|
||||||
"status": "Done",
|
|
||||||
"target": "17",
|
|
||||||
"limit": "39",
|
|
||||||
"reviewer": "Assign reviewer"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 20,
|
|
||||||
"header": "Future Development Roadmap",
|
|
||||||
"type": "Narrative",
|
|
||||||
"status": "Done",
|
|
||||||
"target": "11",
|
|
||||||
"limit": "40",
|
|
||||||
"reviewer": "Assign reviewer"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 21,
|
|
||||||
"header": "System Architecture Overview",
|
|
||||||
"type": "Technical content",
|
|
||||||
"status": "In Process",
|
|
||||||
"target": "24",
|
|
||||||
"limit": "18",
|
|
||||||
"reviewer": "Maya Johnson"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 22,
|
|
||||||
"header": "Risk Management Plan",
|
|
||||||
"type": "Narrative",
|
|
||||||
"status": "Done",
|
|
||||||
"target": "15",
|
|
||||||
"limit": "22",
|
|
||||||
"reviewer": "Carlos Rodriguez"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 23,
|
|
||||||
"header": "Compliance Documentation",
|
|
||||||
"type": "Legal",
|
|
||||||
"status": "In Process",
|
|
||||||
"target": "31",
|
|
||||||
"limit": "27",
|
|
||||||
"reviewer": "Sarah Chen"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 24,
|
|
||||||
"header": "API Documentation",
|
|
||||||
"type": "Technical content",
|
|
||||||
"status": "Done",
|
|
||||||
"target": "8",
|
|
||||||
"limit": "12",
|
|
||||||
"reviewer": "Raj Patel"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 25,
|
|
||||||
"header": "User Interface Mockups",
|
|
||||||
"type": "Visual",
|
|
||||||
"status": "In Process",
|
|
||||||
"target": "19",
|
|
||||||
"limit": "25",
|
|
||||||
"reviewer": "Leila Ahmadi"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 26,
|
|
||||||
"header": "Database Schema",
|
|
||||||
"type": "Technical content",
|
|
||||||
"status": "Done",
|
|
||||||
"target": "22",
|
|
||||||
"limit": "20",
|
|
||||||
"reviewer": "Thomas Wilson"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 27,
|
|
||||||
"header": "Testing Methodology",
|
|
||||||
"type": "Technical content",
|
|
||||||
"status": "In Process",
|
|
||||||
"target": "17",
|
|
||||||
"limit": "14",
|
|
||||||
"reviewer": "Assign reviewer"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 28,
|
|
||||||
"header": "Deployment Strategy",
|
|
||||||
"type": "Narrative",
|
|
||||||
"status": "Done",
|
|
||||||
"target": "26",
|
|
||||||
"limit": "30",
|
|
||||||
"reviewer": "Eddie Lake"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 29,
|
|
||||||
"header": "Budget Breakdown",
|
|
||||||
"type": "Financial",
|
|
||||||
"status": "In Process",
|
|
||||||
"target": "13",
|
|
||||||
"limit": "16",
|
|
||||||
"reviewer": "Jamik Tashpulatov"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 30,
|
|
||||||
"header": "Market Analysis",
|
|
||||||
"type": "Research",
|
|
||||||
"status": "Done",
|
|
||||||
"target": "29",
|
|
||||||
"limit": "32",
|
|
||||||
"reviewer": "Sophia Martinez"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 31,
|
|
||||||
"header": "Competitor Comparison",
|
|
||||||
"type": "Research",
|
|
||||||
"status": "In Process",
|
|
||||||
"target": "21",
|
|
||||||
"limit": "19",
|
|
||||||
"reviewer": "Assign reviewer"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 32,
|
|
||||||
"header": "Maintenance Plan",
|
|
||||||
"type": "Technical content",
|
|
||||||
"status": "Done",
|
|
||||||
"target": "16",
|
|
||||||
"limit": "23",
|
|
||||||
"reviewer": "Alex Thompson"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 33,
|
|
||||||
"header": "User Personas",
|
|
||||||
"type": "Research",
|
|
||||||
"status": "In Process",
|
|
||||||
"target": "27",
|
|
||||||
"limit": "24",
|
|
||||||
"reviewer": "Nina Patel"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 34,
|
|
||||||
"header": "Accessibility Compliance",
|
|
||||||
"type": "Legal",
|
|
||||||
"status": "Done",
|
|
||||||
"target": "18",
|
|
||||||
"limit": "21",
|
|
||||||
"reviewer": "Assign reviewer"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 35,
|
|
||||||
"header": "Performance Metrics",
|
|
||||||
"type": "Technical content",
|
|
||||||
"status": "In Process",
|
|
||||||
"target": "23",
|
|
||||||
"limit": "26",
|
|
||||||
"reviewer": "David Kim"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 36,
|
|
||||||
"header": "Disaster Recovery Plan",
|
|
||||||
"type": "Technical content",
|
|
||||||
"status": "Done",
|
|
||||||
"target": "14",
|
|
||||||
"limit": "17",
|
|
||||||
"reviewer": "Jamik Tashpulatov"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 37,
|
|
||||||
"header": "Third-party Integrations",
|
|
||||||
"type": "Technical content",
|
|
||||||
"status": "In Process",
|
|
||||||
"target": "25",
|
|
||||||
"limit": "28",
|
|
||||||
"reviewer": "Eddie Lake"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 38,
|
|
||||||
"header": "User Feedback Summary",
|
|
||||||
"type": "Research",
|
|
||||||
"status": "Done",
|
|
||||||
"target": "20",
|
|
||||||
"limit": "15",
|
|
||||||
"reviewer": "Assign reviewer"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 39,
|
|
||||||
"header": "Localization Strategy",
|
|
||||||
"type": "Narrative",
|
|
||||||
"status": "In Process",
|
|
||||||
"target": "12",
|
|
||||||
"limit": "19",
|
|
||||||
"reviewer": "Maria Garcia"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 40,
|
|
||||||
"header": "Mobile Compatibility",
|
|
||||||
"type": "Technical content",
|
|
||||||
"status": "Done",
|
|
||||||
"target": "28",
|
|
||||||
"limit": "31",
|
|
||||||
"reviewer": "James Wilson"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 41,
|
|
||||||
"header": "Data Migration Plan",
|
|
||||||
"type": "Technical content",
|
|
||||||
"status": "In Process",
|
|
||||||
"target": "19",
|
|
||||||
"limit": "22",
|
|
||||||
"reviewer": "Assign reviewer"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 42,
|
|
||||||
"header": "Quality Assurance Protocols",
|
|
||||||
"type": "Technical content",
|
|
||||||
"status": "Done",
|
|
||||||
"target": "30",
|
|
||||||
"limit": "33",
|
|
||||||
"reviewer": "Priya Singh"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 43,
|
|
||||||
"header": "Stakeholder Analysis",
|
|
||||||
"type": "Research",
|
|
||||||
"status": "In Process",
|
|
||||||
"target": "11",
|
|
||||||
"limit": "14",
|
|
||||||
"reviewer": "Eddie Lake"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 44,
|
|
||||||
"header": "Environmental Impact Assessment",
|
|
||||||
"type": "Research",
|
|
||||||
"status": "Done",
|
|
||||||
"target": "24",
|
|
||||||
"limit": "27",
|
|
||||||
"reviewer": "Assign reviewer"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 45,
|
|
||||||
"header": "Intellectual Property Rights",
|
|
||||||
"type": "Legal",
|
|
||||||
"status": "In Process",
|
|
||||||
"target": "17",
|
|
||||||
"limit": "20",
|
|
||||||
"reviewer": "Sarah Johnson"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 46,
|
|
||||||
"header": "Customer Support Framework",
|
|
||||||
"type": "Narrative",
|
|
||||||
"status": "Done",
|
|
||||||
"target": "22",
|
|
||||||
"limit": "25",
|
|
||||||
"reviewer": "Jamik Tashpulatov"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 47,
|
|
||||||
"header": "Version Control Strategy",
|
|
||||||
"type": "Technical content",
|
|
||||||
"status": "In Process",
|
|
||||||
"target": "15",
|
|
||||||
"limit": "18",
|
|
||||||
"reviewer": "Assign reviewer"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 48,
|
|
||||||
"header": "Continuous Integration Pipeline",
|
|
||||||
"type": "Technical content",
|
|
||||||
"status": "Done",
|
|
||||||
"target": "26",
|
|
||||||
"limit": "29",
|
|
||||||
"reviewer": "Michael Chen"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 49,
|
|
||||||
"header": "Regulatory Compliance",
|
|
||||||
"type": "Legal",
|
|
||||||
"status": "In Process",
|
|
||||||
"target": "13",
|
|
||||||
"limit": "16",
|
|
||||||
"reviewer": "Assign reviewer"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 50,
|
|
||||||
"header": "User Authentication System",
|
|
||||||
"type": "Technical content",
|
|
||||||
"status": "Done",
|
|
||||||
"target": "28",
|
|
||||||
"limit": "31",
|
|
||||||
"reviewer": "Eddie Lake"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 51,
|
|
||||||
"header": "Data Analytics Framework",
|
|
||||||
"type": "Technical content",
|
|
||||||
"status": "In Process",
|
|
||||||
"target": "21",
|
|
||||||
"limit": "24",
|
|
||||||
"reviewer": "Jamik Tashpulatov"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 52,
|
|
||||||
"header": "Cloud Infrastructure",
|
|
||||||
"type": "Technical content",
|
|
||||||
"status": "Done",
|
|
||||||
"target": "16",
|
|
||||||
"limit": "19",
|
|
||||||
"reviewer": "Assign reviewer"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 53,
|
|
||||||
"header": "Network Security Measures",
|
|
||||||
"type": "Technical content",
|
|
||||||
"status": "In Process",
|
|
||||||
"target": "29",
|
|
||||||
"limit": "32",
|
|
||||||
"reviewer": "Lisa Wong"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 54,
|
|
||||||
"header": "Project Timeline",
|
|
||||||
"type": "Planning",
|
|
||||||
"status": "Done",
|
|
||||||
"target": "14",
|
|
||||||
"limit": "17",
|
|
||||||
"reviewer": "Eddie Lake"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 55,
|
|
||||||
"header": "Resource Allocation",
|
|
||||||
"type": "Planning",
|
|
||||||
"status": "In Process",
|
|
||||||
"target": "27",
|
|
||||||
"limit": "30",
|
|
||||||
"reviewer": "Assign reviewer"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 56,
|
|
||||||
"header": "Team Structure and Roles",
|
|
||||||
"type": "Planning",
|
|
||||||
"status": "Done",
|
|
||||||
"target": "20",
|
|
||||||
"limit": "23",
|
|
||||||
"reviewer": "Jamik Tashpulatov"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 57,
|
|
||||||
"header": "Communication Protocols",
|
|
||||||
"type": "Planning",
|
|
||||||
"status": "In Process",
|
|
||||||
"target": "15",
|
|
||||||
"limit": "18",
|
|
||||||
"reviewer": "Assign reviewer"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 58,
|
|
||||||
"header": "Success Metrics",
|
|
||||||
"type": "Planning",
|
|
||||||
"status": "Done",
|
|
||||||
"target": "30",
|
|
||||||
"limit": "33",
|
|
||||||
"reviewer": "Eddie Lake"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 59,
|
|
||||||
"header": "Internationalization Support",
|
|
||||||
"type": "Technical content",
|
|
||||||
"status": "In Process",
|
|
||||||
"target": "23",
|
|
||||||
"limit": "26",
|
|
||||||
"reviewer": "Jamik Tashpulatov"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 60,
|
|
||||||
"header": "Backup and Recovery Procedures",
|
|
||||||
"type": "Technical content",
|
|
||||||
"status": "Done",
|
|
||||||
"target": "18",
|
|
||||||
"limit": "21",
|
|
||||||
"reviewer": "Assign reviewer"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 61,
|
|
||||||
"header": "Monitoring and Alerting System",
|
|
||||||
"type": "Technical content",
|
|
||||||
"status": "In Process",
|
|
||||||
"target": "25",
|
|
||||||
"limit": "28",
|
|
||||||
"reviewer": "Daniel Park"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 62,
|
|
||||||
"header": "Code Review Guidelines",
|
|
||||||
"type": "Technical content",
|
|
||||||
"status": "Done",
|
|
||||||
"target": "12",
|
|
||||||
"limit": "15",
|
|
||||||
"reviewer": "Eddie Lake"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 63,
|
|
||||||
"header": "Documentation Standards",
|
|
||||||
"type": "Technical content",
|
|
||||||
"status": "In Process",
|
|
||||||
"target": "27",
|
|
||||||
"limit": "30",
|
|
||||||
"reviewer": "Jamik Tashpulatov"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 64,
|
|
||||||
"header": "Release Management Process",
|
|
||||||
"type": "Planning",
|
|
||||||
"status": "Done",
|
|
||||||
"target": "22",
|
|
||||||
"limit": "25",
|
|
||||||
"reviewer": "Assign reviewer"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 65,
|
|
||||||
"header": "Feature Prioritization Matrix",
|
|
||||||
"type": "Planning",
|
|
||||||
"status": "In Process",
|
|
||||||
"target": "19",
|
|
||||||
"limit": "22",
|
|
||||||
"reviewer": "Emma Davis"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 66,
|
|
||||||
"header": "Technical Debt Assessment",
|
|
||||||
"type": "Technical content",
|
|
||||||
"status": "Done",
|
|
||||||
"target": "24",
|
|
||||||
"limit": "27",
|
|
||||||
"reviewer": "Eddie Lake"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 67,
|
|
||||||
"header": "Capacity Planning",
|
|
||||||
"type": "Planning",
|
|
||||||
"status": "In Process",
|
|
||||||
"target": "21",
|
|
||||||
"limit": "24",
|
|
||||||
"reviewer": "Jamik Tashpulatov"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 68,
|
|
||||||
"header": "Service Level Agreements",
|
|
||||||
"type": "Legal",
|
|
||||||
"status": "Done",
|
|
||||||
"target": "26",
|
|
||||||
"limit": "29",
|
|
||||||
"reviewer": "Assign reviewer"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
import Image from "next/image"
|
|
||||||
|
|
||||||
import {
|
|
||||||
SidebarInset,
|
|
||||||
SidebarProvider,
|
|
||||||
} from "@/registry/new-york-v4/ui/sidebar"
|
|
||||||
import { AppSidebar } from "@/app/(app)/examples/dashboard/components/app-sidebar"
|
|
||||||
import { ChartAreaInteractive } from "@/app/(app)/examples/dashboard/components/chart-area-interactive"
|
|
||||||
import { DataTable } from "@/app/(app)/examples/dashboard/components/data-table"
|
|
||||||
import { SectionCards } from "@/app/(app)/examples/dashboard/components/section-cards"
|
|
||||||
import { SiteHeader } from "@/app/(app)/examples/dashboard/components/site-header"
|
|
||||||
|
|
||||||
import data from "./data.json"
|
|
||||||
|
|
||||||
export default function Page() {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className="md:hidden">
|
|
||||||
<Image
|
|
||||||
src="/examples/dashboard-light.png"
|
|
||||||
width={1280}
|
|
||||||
height={843}
|
|
||||||
alt="Authentication"
|
|
||||||
className="block dark:hidden"
|
|
||||||
priority
|
|
||||||
/>
|
|
||||||
<Image
|
|
||||||
src="/examples/dashboard-dark.png"
|
|
||||||
width={1280}
|
|
||||||
height={843}
|
|
||||||
alt="Authentication"
|
|
||||||
className="hidden dark:block"
|
|
||||||
priority
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<SidebarProvider
|
|
||||||
className="hidden md:flex"
|
|
||||||
style={
|
|
||||||
{
|
|
||||||
"--sidebar-width": "calc(var(--spacing) * 64)",
|
|
||||||
"--header-height": "calc(var(--spacing) * 12 + 1px)",
|
|
||||||
} as React.CSSProperties
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<AppSidebar variant="sidebar" />
|
|
||||||
<SidebarInset>
|
|
||||||
<SiteHeader />
|
|
||||||
<div className="flex flex-1 flex-col">
|
|
||||||
<div className="@container/main flex flex-1 flex-col gap-2">
|
|
||||||
<div className="flex flex-col gap-4 py-4 md:gap-6 md:py-6">
|
|
||||||
<SectionCards />
|
|
||||||
<div className="px-4 lg:px-6">
|
|
||||||
<ChartAreaInteractive />
|
|
||||||
</div>
|
|
||||||
<DataTable data={data} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</SidebarInset>
|
|
||||||
</SidebarProvider>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
import { type Metadata } from "next"
|
|
||||||
import Link from "next/link"
|
|
||||||
|
|
||||||
import { Announcement } from "@/components/announcement"
|
|
||||||
import { ExamplesNav } from "@/components/examples-nav"
|
|
||||||
import {
|
|
||||||
PageActions,
|
|
||||||
PageHeader,
|
|
||||||
PageHeaderDescription,
|
|
||||||
PageHeaderHeading,
|
|
||||||
} from "@/components/page-header"
|
|
||||||
import { PageNav } from "@/components/page-nav"
|
|
||||||
import { ThemeSelector } from "@/components/theme-selector"
|
|
||||||
import { Button } from "@/registry/new-york-v4/ui/button"
|
|
||||||
|
|
||||||
export const dynamic = "force-static"
|
|
||||||
export const revalidate = false
|
|
||||||
|
|
||||||
const title = "The Foundation for your Design System"
|
|
||||||
const description =
|
|
||||||
"A set of beautifully designed components that you can customize, extend, and build on. Start here then make it your own. Open Source. Open Code."
|
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
|
||||||
title,
|
|
||||||
description,
|
|
||||||
openGraph: {
|
|
||||||
images: [
|
|
||||||
{
|
|
||||||
url: `/og?title=${encodeURIComponent(
|
|
||||||
title
|
|
||||||
)}&description=${encodeURIComponent(description)}`,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
twitter: {
|
|
||||||
card: "summary_large_image",
|
|
||||||
images: [
|
|
||||||
{
|
|
||||||
url: `/og?title=${encodeURIComponent(
|
|
||||||
title
|
|
||||||
)}&description=${encodeURIComponent(description)}`,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function ExamplesLayout({
|
|
||||||
children,
|
|
||||||
}: {
|
|
||||||
children: React.ReactNode
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<PageHeader>
|
|
||||||
<Announcement />
|
|
||||||
<PageHeaderHeading className="max-w-4xl">{title}</PageHeaderHeading>
|
|
||||||
<PageHeaderDescription>{description}</PageHeaderDescription>
|
|
||||||
<PageActions>
|
|
||||||
<Button asChild size="sm">
|
|
||||||
<Link href="/docs/installation">Get Started</Link>
|
|
||||||
</Button>
|
|
||||||
<Button asChild size="sm" variant="ghost">
|
|
||||||
<Link href="/docs/components">View Components</Link>
|
|
||||||
</Button>
|
|
||||||
</PageActions>
|
|
||||||
</PageHeader>
|
|
||||||
<PageNav id="examples" className="hidden md:flex">
|
|
||||||
<ExamplesNav className="[&>a:first-child]:text-primary flex-1 overflow-hidden" />
|
|
||||||
<ThemeSelector className="mr-4 hidden md:flex" />
|
|
||||||
</PageNav>
|
|
||||||
<div className="container-wrapper section-soft flex flex-1 flex-col pb-6">
|
|
||||||
<div className="theme-container container flex flex-1 scroll-mt-20 flex-col">
|
|
||||||
<div className="bg-background flex flex-col overflow-hidden rounded-lg border bg-clip-padding md:flex-1 xl:rounded-xl">
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
import { Button } from "@/registry/new-york-v4/ui/button"
|
|
||||||
import {
|
|
||||||
Dialog,
|
|
||||||
DialogContent,
|
|
||||||
DialogDescription,
|
|
||||||
DialogHeader,
|
|
||||||
DialogTitle,
|
|
||||||
DialogTrigger,
|
|
||||||
} from "@/registry/new-york-v4/ui/dialog"
|
|
||||||
|
|
||||||
export function CodeViewer() {
|
|
||||||
return (
|
|
||||||
<Dialog>
|
|
||||||
<DialogTrigger asChild>
|
|
||||||
<Button variant="secondary">View code</Button>
|
|
||||||
</DialogTrigger>
|
|
||||||
<DialogContent>
|
|
||||||
<DialogHeader>
|
|
||||||
<DialogTitle>View code</DialogTitle>
|
|
||||||
<DialogDescription>
|
|
||||||
You can use the following code to start integrating your current
|
|
||||||
prompt and settings into your application.
|
|
||||||
</DialogDescription>
|
|
||||||
</DialogHeader>
|
|
||||||
<div className="grid gap-4">
|
|
||||||
<div className="rounded-md bg-black p-6">
|
|
||||||
<pre>
|
|
||||||
<code className="grid gap-1 text-sm text-white [&_span]:h-4">
|
|
||||||
<span>
|
|
||||||
<span className="text-sky-300">import</span> os
|
|
||||||
</span>
|
|
||||||
<span>
|
|
||||||
<span className="text-sky-300">import</span> openai
|
|
||||||
</span>
|
|
||||||
<span />
|
|
||||||
<span>
|
|
||||||
openai.api_key = os.getenv(
|
|
||||||
<span className="text-green-300">
|
|
||||||
"OPENAI_API_KEY"
|
|
||||||
</span>
|
|
||||||
)
|
|
||||||
</span>
|
|
||||||
<span />
|
|
||||||
<span>response = openai.Completion.create(</span>
|
|
||||||
<span>
|
|
||||||
{" "}
|
|
||||||
model=
|
|
||||||
<span className="text-green-300">"davinci"</span>,
|
|
||||||
</span>
|
|
||||||
<span>
|
|
||||||
{" "}
|
|
||||||
prompt=<span className="text-amber-300">""</span>,
|
|
||||||
</span>
|
|
||||||
<span>
|
|
||||||
{" "}
|
|
||||||
temperature=<span className="text-amber-300">0.9</span>,
|
|
||||||
</span>
|
|
||||||
<span>
|
|
||||||
{" "}
|
|
||||||
max_tokens=<span className="text-amber-300">5</span>,
|
|
||||||
</span>
|
|
||||||
<span>
|
|
||||||
{" "}
|
|
||||||
top_p=<span className="text-amber-300">1</span>,
|
|
||||||
</span>
|
|
||||||
<span>
|
|
||||||
{" "}
|
|
||||||
frequency_penalty=<span className="text-amber-300">0</span>,
|
|
||||||
</span>
|
|
||||||
<span>
|
|
||||||
{" "}
|
|
||||||
presence_penalty=<span className="text-green-300">0</span>,
|
|
||||||
</span>
|
|
||||||
<span>)</span>
|
|
||||||
</code>
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<p className="text-muted-foreground text-sm">
|
|
||||||
Your API Key can be found here. You should use environment
|
|
||||||
variables or a secret management tool to expose your key to your
|
|
||||||
applications.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
"use client"
|
|
||||||
|
|
||||||
import * as React from "react"
|
|
||||||
import { type SliderProps } from "@radix-ui/react-slider"
|
|
||||||
|
|
||||||
import {
|
|
||||||
HoverCard,
|
|
||||||
HoverCardContent,
|
|
||||||
HoverCardTrigger,
|
|
||||||
} from "@/registry/new-york-v4/ui/hover-card"
|
|
||||||
import { Label } from "@/registry/new-york-v4/ui/label"
|
|
||||||
import { Slider } from "@/registry/new-york-v4/ui/slider"
|
|
||||||
|
|
||||||
interface MaxLengthSelectorProps {
|
|
||||||
defaultValue: SliderProps["defaultValue"]
|
|
||||||
}
|
|
||||||
|
|
||||||
export function MaxLengthSelector({ defaultValue }: MaxLengthSelectorProps) {
|
|
||||||
const [value, setValue] = React.useState(defaultValue)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="grid gap-2 pt-2">
|
|
||||||
<HoverCard openDelay={200}>
|
|
||||||
<HoverCardTrigger asChild>
|
|
||||||
<div className="grid gap-4">
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<Label htmlFor="maxlength">Maximum Length</Label>
|
|
||||||
<span className="text-muted-foreground hover:border-border w-12 rounded-md border border-transparent px-2 py-0.5 text-right text-sm">
|
|
||||||
{value}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<Slider
|
|
||||||
id="maxlength"
|
|
||||||
max={4000}
|
|
||||||
defaultValue={value}
|
|
||||||
step={10}
|
|
||||||
onValueChange={setValue}
|
|
||||||
aria-label="Maximum Length"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</HoverCardTrigger>
|
|
||||||
<HoverCardContent
|
|
||||||
align="start"
|
|
||||||
className="w-[260px] text-sm"
|
|
||||||
side="left"
|
|
||||||
>
|
|
||||||
The maximum number of tokens to generate. Requests can use up to 2,048
|
|
||||||
or 4,000 tokens, shared between prompt and completion. The exact limit
|
|
||||||
varies by model.
|
|
||||||
</HoverCardContent>
|
|
||||||
</HoverCard>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,162 +0,0 @@
|
|||||||
"use client"
|
|
||||||
|
|
||||||
import * as React from "react"
|
|
||||||
import { type PopoverProps } from "@radix-ui/react-popover"
|
|
||||||
import { Check, ChevronsUpDown } from "lucide-react"
|
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
|
||||||
import { useMutationObserver } from "@/hooks/use-mutation-observer"
|
|
||||||
import { Button } from "@/registry/new-york-v4/ui/button"
|
|
||||||
import {
|
|
||||||
Command,
|
|
||||||
CommandEmpty,
|
|
||||||
CommandGroup,
|
|
||||||
CommandInput,
|
|
||||||
CommandItem,
|
|
||||||
CommandList,
|
|
||||||
} from "@/registry/new-york-v4/ui/command"
|
|
||||||
import {
|
|
||||||
HoverCard,
|
|
||||||
HoverCardContent,
|
|
||||||
HoverCardTrigger,
|
|
||||||
} from "@/registry/new-york-v4/ui/hover-card"
|
|
||||||
import { Label } from "@/registry/new-york-v4/ui/label"
|
|
||||||
import {
|
|
||||||
Popover,
|
|
||||||
PopoverContent,
|
|
||||||
PopoverTrigger,
|
|
||||||
} from "@/registry/new-york-v4/ui/popover"
|
|
||||||
|
|
||||||
import { type Model, type ModelType } from "../data/models"
|
|
||||||
|
|
||||||
interface ModelSelectorProps extends PopoverProps {
|
|
||||||
types: readonly ModelType[]
|
|
||||||
models: Model[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ModelSelector({ models, types, ...props }: ModelSelectorProps) {
|
|
||||||
const [open, setOpen] = React.useState(false)
|
|
||||||
const [selectedModel, setSelectedModel] = React.useState<Model>(models[0])
|
|
||||||
const [peekedModel, setPeekedModel] = React.useState<Model>(models[0])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="grid gap-3">
|
|
||||||
<HoverCard openDelay={200}>
|
|
||||||
<HoverCardTrigger asChild>
|
|
||||||
<Label htmlFor="model">Model</Label>
|
|
||||||
</HoverCardTrigger>
|
|
||||||
<HoverCardContent
|
|
||||||
align="start"
|
|
||||||
className="w-[260px] text-sm"
|
|
||||||
side="left"
|
|
||||||
>
|
|
||||||
The model which will generate the completion. Some models are suitable
|
|
||||||
for natural language tasks, others specialize in code. Learn more.
|
|
||||||
</HoverCardContent>
|
|
||||||
</HoverCard>
|
|
||||||
<Popover open={open} onOpenChange={setOpen} {...props}>
|
|
||||||
<PopoverTrigger asChild>
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
role="combobox"
|
|
||||||
aria-expanded={open}
|
|
||||||
aria-label="Select a model"
|
|
||||||
className="w-full justify-between"
|
|
||||||
>
|
|
||||||
{selectedModel ? selectedModel.name : "Select a model..."}
|
|
||||||
<ChevronsUpDown className="text-muted-foreground" />
|
|
||||||
</Button>
|
|
||||||
</PopoverTrigger>
|
|
||||||
<PopoverContent align="end" className="w-[250px] p-0">
|
|
||||||
<HoverCard>
|
|
||||||
<HoverCardContent
|
|
||||||
side="left"
|
|
||||||
align="start"
|
|
||||||
forceMount
|
|
||||||
className="min-h-[280px]"
|
|
||||||
>
|
|
||||||
<div className="grid gap-2">
|
|
||||||
<h4 className="leading-none font-medium">{peekedModel.name}</h4>
|
|
||||||
<div className="text-muted-foreground text-sm">
|
|
||||||
{peekedModel.description}
|
|
||||||
</div>
|
|
||||||
{peekedModel.strengths ? (
|
|
||||||
<div className="mt-4 grid gap-2">
|
|
||||||
<h5 className="text-sm leading-none font-medium">
|
|
||||||
Strengths
|
|
||||||
</h5>
|
|
||||||
<ul className="text-muted-foreground text-sm">
|
|
||||||
{peekedModel.strengths}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
</HoverCardContent>
|
|
||||||
<Command loop>
|
|
||||||
<CommandList className="h-(--cmdk-list-height) max-h-[400px]">
|
|
||||||
<CommandInput placeholder="Search Models..." />
|
|
||||||
<CommandEmpty>No Models found.</CommandEmpty>
|
|
||||||
<HoverCardTrigger />
|
|
||||||
{types.map((type) => (
|
|
||||||
<CommandGroup key={type} heading={type}>
|
|
||||||
{models
|
|
||||||
.filter((model) => model.type === type)
|
|
||||||
.map((model) => (
|
|
||||||
<ModelItem
|
|
||||||
key={model.id}
|
|
||||||
model={model}
|
|
||||||
isSelected={selectedModel?.id === model.id}
|
|
||||||
onPeek={(model) => setPeekedModel(model)}
|
|
||||||
onSelect={() => {
|
|
||||||
setSelectedModel(model)
|
|
||||||
setOpen(false)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</CommandGroup>
|
|
||||||
))}
|
|
||||||
</CommandList>
|
|
||||||
</Command>
|
|
||||||
</HoverCard>
|
|
||||||
</PopoverContent>
|
|
||||||
</Popover>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ModelItemProps {
|
|
||||||
model: Model
|
|
||||||
isSelected: boolean
|
|
||||||
onSelect: () => void
|
|
||||||
onPeek: (model: Model) => void
|
|
||||||
}
|
|
||||||
|
|
||||||
function ModelItem({ model, isSelected, onSelect, onPeek }: ModelItemProps) {
|
|
||||||
const ref = React.useRef<HTMLDivElement>(null)
|
|
||||||
|
|
||||||
useMutationObserver(ref, (mutations) => {
|
|
||||||
mutations.forEach((mutation) => {
|
|
||||||
if (
|
|
||||||
mutation.type === "attributes" &&
|
|
||||||
mutation.attributeName === "aria-selected" &&
|
|
||||||
ref.current?.getAttribute("aria-selected") === "true"
|
|
||||||
) {
|
|
||||||
onPeek(model)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
return (
|
|
||||||
<CommandItem
|
|
||||||
key={model.id}
|
|
||||||
onSelect={onSelect}
|
|
||||||
ref={ref}
|
|
||||||
className="data-[selected=true]:bg-primary data-[selected=true]:text-primary-foreground"
|
|
||||||
>
|
|
||||||
{model.name}
|
|
||||||
<Check
|
|
||||||
className={cn("ml-auto", isSelected ? "opacity-100" : "opacity-0")}
|
|
||||||
/>
|
|
||||||
</CommandItem>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,121 +0,0 @@
|
|||||||
"use client"
|
|
||||||
|
|
||||||
import * as React from "react"
|
|
||||||
import { Dialog } from "@radix-ui/react-dialog"
|
|
||||||
import { MoreHorizontal } from "lucide-react"
|
|
||||||
import { toast } from "sonner"
|
|
||||||
|
|
||||||
import {
|
|
||||||
AlertDialog,
|
|
||||||
AlertDialogCancel,
|
|
||||||
AlertDialogContent,
|
|
||||||
AlertDialogDescription,
|
|
||||||
AlertDialogFooter,
|
|
||||||
AlertDialogHeader,
|
|
||||||
AlertDialogTitle,
|
|
||||||
} from "@/registry/new-york-v4/ui/alert-dialog"
|
|
||||||
import { Button } from "@/registry/new-york-v4/ui/button"
|
|
||||||
import {
|
|
||||||
DialogClose,
|
|
||||||
DialogContent,
|
|
||||||
DialogDescription,
|
|
||||||
DialogFooter,
|
|
||||||
DialogHeader,
|
|
||||||
DialogTitle,
|
|
||||||
} from "@/registry/new-york-v4/ui/dialog"
|
|
||||||
import {
|
|
||||||
DropdownMenu,
|
|
||||||
DropdownMenuContent,
|
|
||||||
DropdownMenuItem,
|
|
||||||
DropdownMenuSeparator,
|
|
||||||
DropdownMenuTrigger,
|
|
||||||
} from "@/registry/new-york-v4/ui/dropdown-menu"
|
|
||||||
import { Label } from "@/registry/new-york-v4/ui/label"
|
|
||||||
import { Switch } from "@/registry/new-york-v4/ui/switch"
|
|
||||||
|
|
||||||
export function PresetActions() {
|
|
||||||
const [open, setIsOpen] = React.useState(false)
|
|
||||||
const [showDeleteDialog, setShowDeleteDialog] = React.useState(false)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<DropdownMenu>
|
|
||||||
<DropdownMenuTrigger asChild>
|
|
||||||
<Button variant="secondary" size="icon">
|
|
||||||
<span className="sr-only">Actions</span>
|
|
||||||
<MoreHorizontal />
|
|
||||||
</Button>
|
|
||||||
</DropdownMenuTrigger>
|
|
||||||
<DropdownMenuContent align="end">
|
|
||||||
<DropdownMenuItem onSelect={() => setIsOpen(true)}>
|
|
||||||
Content filter preferences
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuSeparator />
|
|
||||||
<DropdownMenuItem
|
|
||||||
onSelect={() => setShowDeleteDialog(true)}
|
|
||||||
className="text-red-600"
|
|
||||||
>
|
|
||||||
Delete preset
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
<Dialog open={open} onOpenChange={setIsOpen}>
|
|
||||||
<DialogContent>
|
|
||||||
<DialogHeader>
|
|
||||||
<DialogTitle>Content filter preferences</DialogTitle>
|
|
||||||
<DialogDescription>
|
|
||||||
The content filter flags text that may violate our content policy.
|
|
||||||
It's powered by our moderation endpoint which is free to use
|
|
||||||
to moderate your OpenAI API traffic. Learn more.
|
|
||||||
</DialogDescription>
|
|
||||||
</DialogHeader>
|
|
||||||
<div className="py-6">
|
|
||||||
<h4 className="text-muted-foreground text-sm">
|
|
||||||
Playground Warnings
|
|
||||||
</h4>
|
|
||||||
<div className="flex items-start justify-between gap-4 pt-3">
|
|
||||||
<Switch name="show" id="show" defaultChecked={true} />
|
|
||||||
<Label className="grid gap-1 font-normal" htmlFor="show">
|
|
||||||
<span className="font-semibold">
|
|
||||||
Show a warning when content is flagged
|
|
||||||
</span>
|
|
||||||
<span className="text-muted-foreground text-sm">
|
|
||||||
A warning will be shown when sexual, hateful, violent or
|
|
||||||
self-harm content is detected.
|
|
||||||
</span>
|
|
||||||
</Label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<DialogFooter>
|
|
||||||
<DialogClose asChild>
|
|
||||||
<Button variant="secondary">Close</Button>
|
|
||||||
</DialogClose>
|
|
||||||
</DialogFooter>
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
<AlertDialog open={showDeleteDialog} onOpenChange={setShowDeleteDialog}>
|
|
||||||
<AlertDialogContent>
|
|
||||||
<AlertDialogHeader>
|
|
||||||
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
|
|
||||||
<AlertDialogDescription>
|
|
||||||
This action cannot be undone. This preset will no longer be
|
|
||||||
accessible by you or others you've shared it with.
|
|
||||||
</AlertDialogDescription>
|
|
||||||
</AlertDialogHeader>
|
|
||||||
<AlertDialogFooter>
|
|
||||||
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
|
||||||
<Button
|
|
||||||
variant="destructive"
|
|
||||||
onClick={() => {
|
|
||||||
setShowDeleteDialog(false)
|
|
||||||
toast.success("This preset has been deleted.")
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Delete
|
|
||||||
</Button>
|
|
||||||
</AlertDialogFooter>
|
|
||||||
</AlertDialogContent>
|
|
||||||
</AlertDialog>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
import { Button } from "@/registry/new-york-v4/ui/button"
|
|
||||||
import {
|
|
||||||
Dialog,
|
|
||||||
DialogContent,
|
|
||||||
DialogDescription,
|
|
||||||
DialogFooter,
|
|
||||||
DialogHeader,
|
|
||||||
DialogTitle,
|
|
||||||
DialogTrigger,
|
|
||||||
} from "@/registry/new-york-v4/ui/dialog"
|
|
||||||
import { Input } from "@/registry/new-york-v4/ui/input"
|
|
||||||
import { Label } from "@/registry/new-york-v4/ui/label"
|
|
||||||
import { Textarea } from "@/registry/new-york-v4/ui/textarea"
|
|
||||||
|
|
||||||
export function PresetSave() {
|
|
||||||
return (
|
|
||||||
<Dialog>
|
|
||||||
<DialogTrigger asChild>
|
|
||||||
<Button variant="secondary">Save</Button>
|
|
||||||
</DialogTrigger>
|
|
||||||
<DialogContent>
|
|
||||||
<DialogHeader>
|
|
||||||
<DialogTitle>Save preset</DialogTitle>
|
|
||||||
<DialogDescription>
|
|
||||||
This will save the current playground state as a preset which you
|
|
||||||
can access later or share with others.
|
|
||||||
</DialogDescription>
|
|
||||||
</DialogHeader>
|
|
||||||
<div className="grid gap-6 py-4">
|
|
||||||
<div className="grid gap-3">
|
|
||||||
<Label htmlFor="name">Name</Label>
|
|
||||||
<Input id="name" autoFocus />
|
|
||||||
</div>
|
|
||||||
<div className="grid gap-3">
|
|
||||||
<Label htmlFor="description">Description</Label>
|
|
||||||
<Textarea id="description" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<DialogFooter>
|
|
||||||
<Button type="submit">Save</Button>
|
|
||||||
</DialogFooter>
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
"use client"
|
|
||||||
|
|
||||||
import * as React from "react"
|
|
||||||
import { type PopoverProps } from "@radix-ui/react-popover"
|
|
||||||
import { Check, ChevronsUpDown } from "lucide-react"
|
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
|
||||||
import { Button } from "@/registry/new-york-v4/ui/button"
|
|
||||||
import {
|
|
||||||
Command,
|
|
||||||
CommandEmpty,
|
|
||||||
CommandGroup,
|
|
||||||
CommandInput,
|
|
||||||
CommandItem,
|
|
||||||
CommandList,
|
|
||||||
CommandSeparator,
|
|
||||||
} from "@/registry/new-york-v4/ui/command"
|
|
||||||
import {
|
|
||||||
Popover,
|
|
||||||
PopoverContent,
|
|
||||||
PopoverTrigger,
|
|
||||||
} from "@/registry/new-york-v4/ui/popover"
|
|
||||||
|
|
||||||
import { type Preset } from "../data/presets"
|
|
||||||
|
|
||||||
interface PresetSelectorProps extends PopoverProps {
|
|
||||||
presets: Preset[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export function PresetSelector({ presets, ...props }: PresetSelectorProps) {
|
|
||||||
const [open, setOpen] = React.useState(false)
|
|
||||||
const [selectedPreset, setSelectedPreset] = React.useState<Preset>()
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Popover open={open} onOpenChange={setOpen} {...props}>
|
|
||||||
<PopoverTrigger asChild>
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
role="combobox"
|
|
||||||
aria-label="Load a preset..."
|
|
||||||
aria-expanded={open}
|
|
||||||
className="flex-1 justify-between md:max-w-[200px] lg:max-w-[300px]"
|
|
||||||
>
|
|
||||||
{selectedPreset ? selectedPreset.name : "Load a preset..."}
|
|
||||||
<ChevronsUpDown className="opacity-50" />
|
|
||||||
</Button>
|
|
||||||
</PopoverTrigger>
|
|
||||||
<PopoverContent className="w-[300px] p-0">
|
|
||||||
<Command>
|
|
||||||
<CommandInput placeholder="Search presets..." />
|
|
||||||
<CommandList>
|
|
||||||
<CommandEmpty>No presets found.</CommandEmpty>
|
|
||||||
<CommandGroup heading="Examples">
|
|
||||||
{presets.map((preset) => (
|
|
||||||
<CommandItem
|
|
||||||
key={preset.id}
|
|
||||||
onSelect={() => {
|
|
||||||
setSelectedPreset(preset)
|
|
||||||
setOpen(false)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{preset.name}
|
|
||||||
<Check
|
|
||||||
className={cn(
|
|
||||||
"ml-auto",
|
|
||||||
selectedPreset?.id === preset.id
|
|
||||||
? "opacity-100"
|
|
||||||
: "opacity-0"
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</CommandItem>
|
|
||||||
))}
|
|
||||||
</CommandGroup>
|
|
||||||
<CommandSeparator />
|
|
||||||
<CommandGroup>
|
|
||||||
<CommandItem>More examples</CommandItem>
|
|
||||||
</CommandGroup>
|
|
||||||
</CommandList>
|
|
||||||
</Command>
|
|
||||||
</PopoverContent>
|
|
||||||
</Popover>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
import { Copy } from "lucide-react"
|
|
||||||
|
|
||||||
import { Button } from "@/registry/new-york-v4/ui/button"
|
|
||||||
import { Input } from "@/registry/new-york-v4/ui/input"
|
|
||||||
import { Label } from "@/registry/new-york-v4/ui/label"
|
|
||||||
import {
|
|
||||||
Popover,
|
|
||||||
PopoverContent,
|
|
||||||
PopoverTrigger,
|
|
||||||
} from "@/registry/new-york-v4/ui/popover"
|
|
||||||
|
|
||||||
export function PresetShare() {
|
|
||||||
return (
|
|
||||||
<Popover>
|
|
||||||
<PopoverTrigger asChild>
|
|
||||||
<Button variant="secondary">Share</Button>
|
|
||||||
</PopoverTrigger>
|
|
||||||
<PopoverContent align="end" className="flex w-[520px] flex-col gap-4">
|
|
||||||
<div className="flex flex-col gap-1 text-center sm:text-left">
|
|
||||||
<h3 className="text-lg font-semibold">Share preset</h3>
|
|
||||||
<p className="text-muted-foreground text-sm">
|
|
||||||
Anyone who has this link and an OpenAI account will be able to view
|
|
||||||
this.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="relative flex-1">
|
|
||||||
<Label htmlFor="link" className="sr-only">
|
|
||||||
Link
|
|
||||||
</Label>
|
|
||||||
<Input
|
|
||||||
id="link"
|
|
||||||
defaultValue="https://platform.openai.com/playground/p/7bbKYQvsVkNmVb8NGcdUOLae?model=text-davinci-003"
|
|
||||||
readOnly
|
|
||||||
className="h-9 pr-10"
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
type="submit"
|
|
||||||
size="icon"
|
|
||||||
variant="ghost"
|
|
||||||
className="absolute top-1 right-1 size-7"
|
|
||||||
>
|
|
||||||
<span className="sr-only">Copy</span>
|
|
||||||
<Copy className="size-3.5" />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</PopoverContent>
|
|
||||||
</Popover>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
"use client"
|
|
||||||
|
|
||||||
import * as React from "react"
|
|
||||||
import { type SliderProps } from "@radix-ui/react-slider"
|
|
||||||
|
|
||||||
import {
|
|
||||||
HoverCard,
|
|
||||||
HoverCardContent,
|
|
||||||
HoverCardTrigger,
|
|
||||||
} from "@/registry/new-york-v4/ui/hover-card"
|
|
||||||
import { Label } from "@/registry/new-york-v4/ui/label"
|
|
||||||
import { Slider } from "@/registry/new-york-v4/ui/slider"
|
|
||||||
|
|
||||||
interface TemperatureSelectorProps {
|
|
||||||
defaultValue: SliderProps["defaultValue"]
|
|
||||||
}
|
|
||||||
|
|
||||||
export function TemperatureSelector({
|
|
||||||
defaultValue,
|
|
||||||
}: TemperatureSelectorProps) {
|
|
||||||
const [value, setValue] = React.useState(defaultValue)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="grid gap-2 pt-2">
|
|
||||||
<HoverCard openDelay={200}>
|
|
||||||
<HoverCardTrigger asChild>
|
|
||||||
<div className="grid gap-4">
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<Label htmlFor="temperature">Temperature</Label>
|
|
||||||
<span className="text-muted-foreground hover:border-border w-12 rounded-md border border-transparent px-2 py-0.5 text-right text-sm">
|
|
||||||
{value}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<Slider
|
|
||||||
id="temperature"
|
|
||||||
max={1}
|
|
||||||
defaultValue={value}
|
|
||||||
step={0.1}
|
|
||||||
onValueChange={setValue}
|
|
||||||
aria-label="Temperature"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</HoverCardTrigger>
|
|
||||||
<HoverCardContent
|
|
||||||
align="start"
|
|
||||||
className="w-[260px] text-sm"
|
|
||||||
side="left"
|
|
||||||
>
|
|
||||||
Controls randomness: lowering results in less random completions. As
|
|
||||||
the temperature approaches zero, the model will become deterministic
|
|
||||||
and repetitive.
|
|
||||||
</HoverCardContent>
|
|
||||||
</HoverCard>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
"use client"
|
|
||||||
|
|
||||||
import * as React from "react"
|
|
||||||
import { type SliderProps } from "@radix-ui/react-slider"
|
|
||||||
|
|
||||||
import {
|
|
||||||
HoverCard,
|
|
||||||
HoverCardContent,
|
|
||||||
HoverCardTrigger,
|
|
||||||
} from "@/registry/new-york-v4/ui/hover-card"
|
|
||||||
import { Label } from "@/registry/new-york-v4/ui/label"
|
|
||||||
import { Slider } from "@/registry/new-york-v4/ui/slider"
|
|
||||||
|
|
||||||
interface TopPSelectorProps {
|
|
||||||
defaultValue: SliderProps["defaultValue"]
|
|
||||||
}
|
|
||||||
|
|
||||||
export function TopPSelector({ defaultValue }: TopPSelectorProps) {
|
|
||||||
const [value, setValue] = React.useState(defaultValue)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="grid gap-2 pt-2">
|
|
||||||
<HoverCard openDelay={200}>
|
|
||||||
<HoverCardTrigger asChild>
|
|
||||||
<div className="grid gap-4">
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<Label htmlFor="top-p">Top P</Label>
|
|
||||||
<span className="text-muted-foreground hover:border-border w-12 rounded-md border border-transparent px-2 py-0.5 text-right text-sm">
|
|
||||||
{value}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<Slider
|
|
||||||
id="top-p"
|
|
||||||
max={1}
|
|
||||||
defaultValue={value}
|
|
||||||
step={0.1}
|
|
||||||
onValueChange={setValue}
|
|
||||||
aria-label="Top P"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</HoverCardTrigger>
|
|
||||||
<HoverCardContent
|
|
||||||
align="start"
|
|
||||||
className="w-[260px] text-sm"
|
|
||||||
side="left"
|
|
||||||
>
|
|
||||||
Control diversity via nucleus sampling: 0.5 means half of all
|
|
||||||
likelihood-weighted options are considered.
|
|
||||||
</HoverCardContent>
|
|
||||||
</HoverCard>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,332 +0,0 @@
|
|||||||
import { type Metadata } from "next"
|
|
||||||
import Image from "next/image"
|
|
||||||
import { RotateCcw } from "lucide-react"
|
|
||||||
|
|
||||||
import { Button } from "@/registry/new-york-v4/ui/button"
|
|
||||||
import {
|
|
||||||
HoverCard,
|
|
||||||
HoverCardContent,
|
|
||||||
HoverCardTrigger,
|
|
||||||
} from "@/registry/new-york-v4/ui/hover-card"
|
|
||||||
import { Label } from "@/registry/new-york-v4/ui/label"
|
|
||||||
import { Separator } from "@/registry/new-york-v4/ui/separator"
|
|
||||||
import {
|
|
||||||
Tabs,
|
|
||||||
TabsContent,
|
|
||||||
TabsList,
|
|
||||||
TabsTrigger,
|
|
||||||
} from "@/registry/new-york-v4/ui/tabs"
|
|
||||||
import { Textarea } from "@/registry/new-york-v4/ui/textarea"
|
|
||||||
|
|
||||||
import { CodeViewer } from "./components/code-viewer"
|
|
||||||
import { MaxLengthSelector } from "./components/maxlength-selector"
|
|
||||||
import { ModelSelector } from "./components/model-selector"
|
|
||||||
import { PresetActions } from "./components/preset-actions"
|
|
||||||
import { PresetSave } from "./components/preset-save"
|
|
||||||
import { PresetSelector } from "./components/preset-selector"
|
|
||||||
import { PresetShare } from "./components/preset-share"
|
|
||||||
import { TemperatureSelector } from "./components/temperature-selector"
|
|
||||||
import { TopPSelector } from "./components/top-p-selector"
|
|
||||||
import { models, types } from "./data/models"
|
|
||||||
import { presets } from "./data/presets"
|
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
|
||||||
title: "Playground",
|
|
||||||
description: "The OpenAI Playground built using the components.",
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function PlaygroundPage() {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className="md:hidden">
|
|
||||||
<Image
|
|
||||||
src="/examples/playground-light.png"
|
|
||||||
width={1280}
|
|
||||||
height={916}
|
|
||||||
alt="Playground"
|
|
||||||
className="block dark:hidden"
|
|
||||||
/>
|
|
||||||
<Image
|
|
||||||
src="/examples/playground-dark.png"
|
|
||||||
width={1280}
|
|
||||||
height={916}
|
|
||||||
alt="Playground"
|
|
||||||
className="hidden dark:block"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="hidden flex-1 flex-col md:flex">
|
|
||||||
<div className="container flex flex-col items-start justify-between gap-2 py-4 sm:flex-row sm:items-center sm:gap-0 md:h-16">
|
|
||||||
<h2 className="pl-0.5 text-lg font-semibold">Playground</h2>
|
|
||||||
<div className="ml-auto flex w-full gap-2 sm:justify-end">
|
|
||||||
<PresetSelector presets={presets} />
|
|
||||||
<PresetSave />
|
|
||||||
<div className="hidden gap-2 md:flex">
|
|
||||||
<CodeViewer />
|
|
||||||
<PresetShare />
|
|
||||||
</div>
|
|
||||||
<PresetActions />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Separator />
|
|
||||||
<Tabs defaultValue="complete" className="flex flex-1 flex-col">
|
|
||||||
<div className="container flex flex-1 flex-col py-6">
|
|
||||||
<div className="grid flex-1 items-stretch gap-6 md:grid-cols-[1fr_200px]">
|
|
||||||
<div className="hidden flex-col gap-6 sm:flex md:order-2">
|
|
||||||
<div className="grid gap-3">
|
|
||||||
<HoverCard openDelay={200}>
|
|
||||||
<HoverCardTrigger asChild>
|
|
||||||
<span className="text-sm leading-none font-medium peer-disabled:cursor-not-allowed peer-disabled:opacity-70">
|
|
||||||
Mode
|
|
||||||
</span>
|
|
||||||
</HoverCardTrigger>
|
|
||||||
<HoverCardContent className="w-[320px] text-sm" side="left">
|
|
||||||
Choose the interface that best suits your task. You can
|
|
||||||
provide: a simple prompt to complete, starting and ending
|
|
||||||
text to insert a completion within, or some text with
|
|
||||||
instructions to edit it.
|
|
||||||
</HoverCardContent>
|
|
||||||
</HoverCard>
|
|
||||||
<TabsList className="grid w-full grid-cols-3">
|
|
||||||
<TabsTrigger value="complete">
|
|
||||||
<span className="sr-only">Complete</span>
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="none"
|
|
||||||
>
|
|
||||||
<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 value="insert">
|
|
||||||
<span className="sr-only">Insert</span>
|
|
||||||
<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 value="edit">
|
|
||||||
<span className="sr-only">Edit</span>
|
|
||||||
<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>
|
|
||||||
</TabsList>
|
|
||||||
</div>
|
|
||||||
<ModelSelector types={types} models={models} />
|
|
||||||
<TemperatureSelector defaultValue={[0.56]} />
|
|
||||||
<MaxLengthSelector defaultValue={[256]} />
|
|
||||||
<TopPSelector defaultValue={[0.9]} />
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-1 flex-col *:data-[slot=tab-content]:flex-1 md:order-1">
|
|
||||||
<TabsContent value="complete" className="mt-0 border-0 p-0">
|
|
||||||
<div className="flex h-full flex-col gap-4">
|
|
||||||
<Textarea
|
|
||||||
placeholder="Write a tagline for an ice cream shop"
|
|
||||||
className="min-h-[400px] flex-1 p-4 md:min-h-[700px] lg:min-h-[700px]"
|
|
||||||
/>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<Button>Submit</Button>
|
|
||||||
<Button variant="secondary">
|
|
||||||
<span className="sr-only">Show history</span>
|
|
||||||
<RotateCcw />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</TabsContent>
|
|
||||||
<TabsContent
|
|
||||||
value="insert"
|
|
||||||
className="mt-0 flex flex-col gap-4 border-0 p-0"
|
|
||||||
>
|
|
||||||
<div className="grid h-full grid-rows-2 gap-6 lg:grid-cols-2 lg:grid-rows-1">
|
|
||||||
<Textarea
|
|
||||||
placeholder="We're writing to [inset]. Congrats from OpenAI!"
|
|
||||||
className="h-full min-h-[300px] p-4 lg:min-h-[700px] xl:min-h-[700px]"
|
|
||||||
/>
|
|
||||||
<div className="bg-muted rounded-md border"></div>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<Button>Submit</Button>
|
|
||||||
<Button variant="secondary">
|
|
||||||
<span className="sr-only">Show history</span>
|
|
||||||
<RotateCcw />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</TabsContent>
|
|
||||||
<TabsContent
|
|
||||||
value="edit"
|
|
||||||
className="mt-0 flex flex-col gap-4 border-0 p-0"
|
|
||||||
>
|
|
||||||
<div className="grid h-full gap-6 lg:grid-cols-2">
|
|
||||||
<div className="flex flex-col gap-4">
|
|
||||||
<div className="flex flex-1 flex-col gap-2">
|
|
||||||
<Label htmlFor="input" className="sr-only">
|
|
||||||
Input
|
|
||||||
</Label>
|
|
||||||
<Textarea
|
|
||||||
id="input"
|
|
||||||
placeholder="We is going to the market."
|
|
||||||
className="flex-1 p-4 lg:min-h-[580px]"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col gap-2">
|
|
||||||
<Label htmlFor="instructions">Instructions</Label>
|
|
||||||
<Textarea
|
|
||||||
id="instructions"
|
|
||||||
placeholder="Fix the grammar."
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="bg-muted min-h-[400px] rounded-md border lg:min-h-[700px]" />
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<Button>Submit</Button>
|
|
||||||
<Button variant="secondary">
|
|
||||||
<span className="sr-only">Show history</span>
|
|
||||||
<RotateCcw />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</TabsContent>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Tabs>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,123 +0,0 @@
|
|||||||
"use client"
|
|
||||||
|
|
||||||
import { type ColumnDef } from "@tanstack/react-table"
|
|
||||||
|
|
||||||
import { Badge } from "@/registry/new-york-v4/ui/badge"
|
|
||||||
import { Checkbox } from "@/registry/new-york-v4/ui/checkbox"
|
|
||||||
|
|
||||||
import { labels, priorities, statuses } from "../data/data"
|
|
||||||
import { type 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() ||
|
|
||||||
(table.getIsSomePageRowsSelected() && "indeterminate")
|
|
||||||
}
|
|
||||||
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 gap-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 gap-2">
|
|
||||||
{status.icon && (
|
|
||||||
<status.icon className="text-muted-foreground size-4" />
|
|
||||||
)}
|
|
||||||
<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 gap-2">
|
|
||||||
{priority.icon && (
|
|
||||||
<priority.icon className="text-muted-foreground size-4" />
|
|
||||||
)}
|
|
||||||
<span>{priority.label}</span>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
filterFn: (row, id, value) => {
|
|
||||||
return value.includes(row.getValue(id))
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "actions",
|
|
||||||
cell: ({ row }) => <DataTableRowActions row={row} />,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
import { type Column } from "@tanstack/react-table"
|
|
||||||
import { ArrowDown, ArrowUp, ChevronsUpDown, EyeOff } from "lucide-react"
|
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
|
||||||
import { Button } from "@/registry/new-york-v4/ui/button"
|
|
||||||
import {
|
|
||||||
DropdownMenu,
|
|
||||||
DropdownMenuContent,
|
|
||||||
DropdownMenuItem,
|
|
||||||
DropdownMenuSeparator,
|
|
||||||
DropdownMenuTrigger,
|
|
||||||
} from "@/registry/new-york-v4/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 gap-2", className)}>
|
|
||||||
<DropdownMenu>
|
|
||||||
<DropdownMenuTrigger asChild>
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
size="sm"
|
|
||||||
className="data-[state=open]:bg-accent -ml-3 h-8"
|
|
||||||
>
|
|
||||||
<span>{title}</span>
|
|
||||||
{column.getIsSorted() === "desc" ? (
|
|
||||||
<ArrowDown />
|
|
||||||
) : column.getIsSorted() === "asc" ? (
|
|
||||||
<ArrowUp />
|
|
||||||
) : (
|
|
||||||
<ChevronsUpDown />
|
|
||||||
)}
|
|
||||||
</Button>
|
|
||||||
</DropdownMenuTrigger>
|
|
||||||
<DropdownMenuContent align="start">
|
|
||||||
<DropdownMenuItem onClick={() => column.toggleSorting(false)}>
|
|
||||||
<ArrowUp />
|
|
||||||
Asc
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem onClick={() => column.toggleSorting(true)}>
|
|
||||||
<ArrowDown />
|
|
||||||
Desc
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuSeparator />
|
|
||||||
<DropdownMenuItem onClick={() => column.toggleVisibility(false)}>
|
|
||||||
<EyeOff />
|
|
||||||
Hide
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,147 +0,0 @@
|
|||||||
import * as React from "react"
|
|
||||||
import { type Column } from "@tanstack/react-table"
|
|
||||||
import { Check, PlusCircle } from "lucide-react"
|
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
|
||||||
import { Badge } from "@/registry/new-york-v4/ui/badge"
|
|
||||||
import { Button } from "@/registry/new-york-v4/ui/button"
|
|
||||||
import {
|
|
||||||
Command,
|
|
||||||
CommandEmpty,
|
|
||||||
CommandGroup,
|
|
||||||
CommandInput,
|
|
||||||
CommandItem,
|
|
||||||
CommandList,
|
|
||||||
CommandSeparator,
|
|
||||||
} from "@/registry/new-york-v4/ui/command"
|
|
||||||
import {
|
|
||||||
Popover,
|
|
||||||
PopoverContent,
|
|
||||||
PopoverTrigger,
|
|
||||||
} from "@/registry/new-york-v4/ui/popover"
|
|
||||||
import { Separator } from "@/registry/new-york-v4/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">
|
|
||||||
<PlusCircle />
|
|
||||||
{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 gap-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(
|
|
||||||
"flex size-4 items-center justify-center rounded-[4px] border",
|
|
||||||
isSelected
|
|
||||||
? "bg-primary border-primary text-primary-foreground"
|
|
||||||
: "border-input [&_svg]:invisible"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<Check className="text-primary-foreground size-3.5" />
|
|
||||||
</div>
|
|
||||||
{option.icon && (
|
|
||||||
<option.icon className="text-muted-foreground size-4" />
|
|
||||||
)}
|
|
||||||
<span>{option.label}</span>
|
|
||||||
{facets?.get(option.value) && (
|
|
||||||
<span className="text-muted-foreground ml-auto flex size-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>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
import { type Table } from "@tanstack/react-table"
|
|
||||||
import {
|
|
||||||
ChevronLeft,
|
|
||||||
ChevronRight,
|
|
||||||
ChevronsLeft,
|
|
||||||
ChevronsRight,
|
|
||||||
} from "lucide-react"
|
|
||||||
|
|
||||||
import { Button } from "@/registry/new-york-v4/ui/button"
|
|
||||||
import {
|
|
||||||
Select,
|
|
||||||
SelectContent,
|
|
||||||
SelectItem,
|
|
||||||
SelectTrigger,
|
|
||||||
SelectValue,
|
|
||||||
} from "@/registry/new-york-v4/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="text-muted-foreground flex-1 text-sm">
|
|
||||||
{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, 25, 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"
|
|
||||||
size="icon"
|
|
||||||
className="hidden size-8 lg:flex"
|
|
||||||
onClick={() => table.setPageIndex(0)}
|
|
||||||
disabled={!table.getCanPreviousPage()}
|
|
||||||
>
|
|
||||||
<span className="sr-only">Go to first page</span>
|
|
||||||
<ChevronsLeft />
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
size="icon"
|
|
||||||
className="size-8"
|
|
||||||
onClick={() => table.previousPage()}
|
|
||||||
disabled={!table.getCanPreviousPage()}
|
|
||||||
>
|
|
||||||
<span className="sr-only">Go to previous page</span>
|
|
||||||
<ChevronLeft />
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
size="icon"
|
|
||||||
className="size-8"
|
|
||||||
onClick={() => table.nextPage()}
|
|
||||||
disabled={!table.getCanNextPage()}
|
|
||||||
>
|
|
||||||
<span className="sr-only">Go to next page</span>
|
|
||||||
<ChevronRight />
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
size="icon"
|
|
||||||
className="hidden size-8 lg:flex"
|
|
||||||
onClick={() => table.setPageIndex(table.getPageCount() - 1)}
|
|
||||||
disabled={!table.getCanNextPage()}
|
|
||||||
>
|
|
||||||
<span className="sr-only">Go to last page</span>
|
|
||||||
<ChevronsRight />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
"use client"
|
|
||||||
|
|
||||||
import { type Row } from "@tanstack/react-table"
|
|
||||||
import { MoreHorizontal } from "lucide-react"
|
|
||||||
|
|
||||||
import { Button } from "@/registry/new-york-v4/ui/button"
|
|
||||||
import {
|
|
||||||
DropdownMenu,
|
|
||||||
DropdownMenuContent,
|
|
||||||
DropdownMenuItem,
|
|
||||||
DropdownMenuRadioGroup,
|
|
||||||
DropdownMenuRadioItem,
|
|
||||||
DropdownMenuSeparator,
|
|
||||||
DropdownMenuShortcut,
|
|
||||||
DropdownMenuSub,
|
|
||||||
DropdownMenuSubContent,
|
|
||||||
DropdownMenuSubTrigger,
|
|
||||||
DropdownMenuTrigger,
|
|
||||||
} from "@/registry/new-york-v4/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"
|
|
||||||
size="icon"
|
|
||||||
className="data-[state=open]:bg-muted size-8"
|
|
||||||
>
|
|
||||||
<MoreHorizontal />
|
|
||||||
<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 variant="destructive">
|
|
||||||
Delete
|
|
||||||
<DropdownMenuShortcut>⌘⌫</DropdownMenuShortcut>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
"use client"
|
|
||||||
|
|
||||||
import { type Table } from "@tanstack/react-table"
|
|
||||||
import { X } from "lucide-react"
|
|
||||||
|
|
||||||
import { Button } from "@/registry/new-york-v4/ui/button"
|
|
||||||
import { Input } from "@/registry/new-york-v4/ui/input"
|
|
||||||
import { DataTableViewOptions } from "@/app/(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 gap-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"
|
|
||||||
size="sm"
|
|
||||||
onClick={() => table.resetColumnFilters()}
|
|
||||||
>
|
|
||||||
Reset
|
|
||||||
<X />
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<DataTableViewOptions table={table} />
|
|
||||||
<Button size="sm">Add Task</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
"use client"
|
|
||||||
|
|
||||||
import { DropdownMenuTrigger } from "@radix-ui/react-dropdown-menu"
|
|
||||||
import { type Table } from "@tanstack/react-table"
|
|
||||||
import { Settings2 } from "lucide-react"
|
|
||||||
|
|
||||||
import { Button } from "@/registry/new-york-v4/ui/button"
|
|
||||||
import {
|
|
||||||
DropdownMenu,
|
|
||||||
DropdownMenuCheckboxItem,
|
|
||||||
DropdownMenuContent,
|
|
||||||
DropdownMenuLabel,
|
|
||||||
DropdownMenuSeparator,
|
|
||||||
} from "@/registry/new-york-v4/ui/dropdown-menu"
|
|
||||||
|
|
||||||
export function DataTableViewOptions<TData>({
|
|
||||||
table,
|
|
||||||
}: {
|
|
||||||
table: Table<TData>
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<DropdownMenu>
|
|
||||||
<DropdownMenuTrigger asChild>
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
size="sm"
|
|
||||||
className="ml-auto hidden h-8 lg:flex"
|
|
||||||
>
|
|
||||||
<Settings2 />
|
|
||||||
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>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,131 +0,0 @@
|
|||||||
"use client"
|
|
||||||
|
|
||||||
import * as React from "react"
|
|
||||||
import {
|
|
||||||
flexRender,
|
|
||||||
getCoreRowModel,
|
|
||||||
getFacetedRowModel,
|
|
||||||
getFacetedUniqueValues,
|
|
||||||
getFilteredRowModel,
|
|
||||||
getPaginationRowModel,
|
|
||||||
getSortedRowModel,
|
|
||||||
useReactTable,
|
|
||||||
type ColumnDef,
|
|
||||||
type ColumnFiltersState,
|
|
||||||
type SortingState,
|
|
||||||
type VisibilityState,
|
|
||||||
} from "@tanstack/react-table"
|
|
||||||
|
|
||||||
import {
|
|
||||||
Table,
|
|
||||||
TableBody,
|
|
||||||
TableCell,
|
|
||||||
TableHead,
|
|
||||||
TableHeader,
|
|
||||||
TableRow,
|
|
||||||
} from "@/registry/new-york-v4/ui/table"
|
|
||||||
|
|
||||||
import { DataTablePagination } from "./data-table-pagination"
|
|
||||||
import { DataTableToolbar } from "./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,
|
|
||||||
},
|
|
||||||
initialState: {
|
|
||||||
pagination: {
|
|
||||||
pageSize: 25,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
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="flex flex-col gap-4">
|
|
||||||
<DataTableToolbar table={table} />
|
|
||||||
<div className="overflow-hidden rounded-md border">
|
|
||||||
<Table>
|
|
||||||
<TableHeader>
|
|
||||||
{table.getHeaderGroups().map((headerGroup) => (
|
|
||||||
<TableRow key={headerGroup.id}>
|
|
||||||
{headerGroup.headers.map((header) => {
|
|
||||||
return (
|
|
||||||
<TableHead key={header.id} colSpan={header.colSpan}>
|
|
||||||
{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>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
import {
|
|
||||||
Avatar,
|
|
||||||
AvatarFallback,
|
|
||||||
AvatarImage,
|
|
||||||
} from "@/registry/new-york-v4/ui/avatar"
|
|
||||||
import { Button } from "@/registry/new-york-v4/ui/button"
|
|
||||||
import {
|
|
||||||
DropdownMenu,
|
|
||||||
DropdownMenuContent,
|
|
||||||
DropdownMenuGroup,
|
|
||||||
DropdownMenuItem,
|
|
||||||
DropdownMenuLabel,
|
|
||||||
DropdownMenuSeparator,
|
|
||||||
DropdownMenuShortcut,
|
|
||||||
DropdownMenuTrigger,
|
|
||||||
} from "@/registry/new-york-v4/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 leading-none font-medium">shadcn</p>
|
|
||||||
<p className="text-muted-foreground text-xs leading-none">
|
|
||||||
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>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
import {
|
|
||||||
ArrowDown,
|
|
||||||
ArrowRight,
|
|
||||||
ArrowUp,
|
|
||||||
CheckCircle,
|
|
||||||
Circle,
|
|
||||||
CircleOff,
|
|
||||||
HelpCircle,
|
|
||||||
Timer,
|
|
||||||
} from "lucide-react"
|
|
||||||
|
|
||||||
export const labels = [
|
|
||||||
{
|
|
||||||
value: "bug",
|
|
||||||
label: "Bug",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: "feature",
|
|
||||||
label: "Feature",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: "documentation",
|
|
||||||
label: "Documentation",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
export const statuses = [
|
|
||||||
{
|
|
||||||
value: "backlog",
|
|
||||||
label: "Backlog",
|
|
||||||
icon: HelpCircle,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: "todo",
|
|
||||||
label: "Todo",
|
|
||||||
icon: Circle,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: "in progress",
|
|
||||||
label: "In Progress",
|
|
||||||
icon: Timer,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: "done",
|
|
||||||
label: "Done",
|
|
||||||
icon: CheckCircle,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: "canceled",
|
|
||||||
label: "Canceled",
|
|
||||||
icon: CircleOff,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
export const priorities = [
|
|
||||||
{
|
|
||||||
label: "Low",
|
|
||||||
value: "low",
|
|
||||||
icon: ArrowDown,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Medium",
|
|
||||||
value: "medium",
|
|
||||||
icon: ArrowRight,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "High",
|
|
||||||
value: "high",
|
|
||||||
icon: ArrowUp,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
import { promises as fs } from "fs"
|
|
||||||
import path from "path"
|
|
||||||
import { type 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/(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 gap-8 p-8 md:flex">
|
|
||||||
<div className="flex items-center justify-between gap-2">
|
|
||||||
<div className="flex flex-col gap-1">
|
|
||||||
<h2 className="text-2xl font-semibold 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 gap-2">
|
|
||||||
<UserNav />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<DataTable data={tasks} columns={columns} />
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
import { SiteFooter } from "@/components/site-footer"
|
|
||||||
import { SiteHeader } from "@/components/site-header"
|
|
||||||
|
|
||||||
export default function AppLayout({ children }: { children: React.ReactNode }) {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
data-slot="layout"
|
|
||||||
className="bg-background relative z-10 flex min-h-svh flex-col"
|
|
||||||
>
|
|
||||||
<SiteHeader />
|
|
||||||
<main className="flex flex-1 flex-col">{children}</main>
|
|
||||||
<SiteFooter />
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
import { notFound } from "next/navigation"
|
|
||||||
import { NextResponse, type NextRequest } from "next/server"
|
|
||||||
|
|
||||||
import { processMdxForLLMs } from "@/lib/llm"
|
|
||||||
import { source } from "@/lib/source"
|
|
||||||
import { getActiveStyle } from "@/registry/_legacy-styles"
|
|
||||||
|
|
||||||
export const revalidate = false
|
|
||||||
|
|
||||||
export async function GET(
|
|
||||||
_req: NextRequest,
|
|
||||||
{ params }: { params: Promise<{ slug?: string[] }> }
|
|
||||||
) {
|
|
||||||
const [{ slug }, activeStyle] = await Promise.all([params, getActiveStyle()])
|
|
||||||
|
|
||||||
const page = source.getPage(slug)
|
|
||||||
|
|
||||||
if (!page) {
|
|
||||||
notFound()
|
|
||||||
}
|
|
||||||
|
|
||||||
const processedContent = processMdxForLLMs(
|
|
||||||
await page.data.getText("raw"),
|
|
||||||
activeStyle.name
|
|
||||||
)
|
|
||||||
|
|
||||||
return new NextResponse(processedContent, {
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "text/markdown; charset=utf-8",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export function generateStaticParams() {
|
|
||||||
return source.generateParams()
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user