diff --git a/.changeset/config.json b/.changeset/config.json index 5e1305538ef..83d9bafc57b 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -7,5 +7,5 @@ "access": "public", "baseBranch": "main", "updateInternalDependencies": "patch", - "ignore": ["www", "**-template"] + "ignore": ["www", "v4", "tests"] } diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 00000000000..3231e4090e3 --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,9 @@ +{ + "permissions": { + "allow": [ + "Bash(npm test:*)", + "Bash(npm run typecheck:*)" + ], + "deny": [] + } +} \ No newline at end of file diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 00000000000..9e4646c61cb --- /dev/null +++ b/.eslintignore @@ -0,0 +1,8 @@ +node_modules/ +target/ +.next/ +build/ +dist/ + +/templates/ +/fixtures/ diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000000..acd2c4216a1 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,3 @@ +# These are supported funding model platforms + +github: [shadcn] diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000000..aff82a102da --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "npm" + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/workflows/code-check.yml b/.github/workflows/code-check.yml index 714b8dce73a..bfe6fc4525d 100644 --- a/.github/workflows/code-check.yml +++ b/.github/workflows/code-check.yml @@ -16,7 +16,7 @@ jobs: - name: Install Node.js uses: actions/setup-node@v3 with: - node-version: 18 + node-version: 20 - uses: pnpm/action-setup@v4 name: Install pnpm @@ -52,7 +52,7 @@ jobs: - name: Install Node.js uses: actions/setup-node@v3 with: - node-version: 18 + node-version: 20 - uses: pnpm/action-setup@v4 name: Install pnpm @@ -90,7 +90,7 @@ jobs: - name: Install Node.js uses: actions/setup-node@v3 with: - node-version: 18 + node-version: 20 - uses: pnpm/action-setup@v4 name: Install pnpm @@ -113,4 +113,7 @@ jobs: - name: Install dependencies run: pnpm install + - name: Build packages + run: pnpm --filter=shadcn build + - run: pnpm typecheck diff --git a/.github/workflows/issue-stale.yml b/.github/workflows/issue-stale.yml index 500d781b08a..7e6b7dbc6c8 100644 --- a/.github/workflows/issue-stale.yml +++ b/.github/workflows/issue-stale.yml @@ -1,5 +1,5 @@ # Adapted from vercel/next.js -name: Issue Stale +name: "Stale issue handler" on: workflow_dispatch: schedule: @@ -11,17 +11,35 @@ jobs: runs-on: ubuntu-latest if: github.repository_owner == 'shadcn-ui' steps: - - uses: actions/stale@v4 - id: stale-no-repro - name: "Close stale issues with no reproduction" + - uses: actions/stale@v9 + id: issue-stale + name: "Mark stale issues, close stale issues" with: repo-token: ${{ secrets.STALE_TOKEN }} - 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 reopen or leave a comment. Thank you.\n(This is an automated message.)" + ascending: true days-before-issue-close: 7 - days-before-issue-stale: 30 - stale-pr-label: "stale?" + days-before-issue-stale: 365 + 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: 15 - only-pr-labels: "postpone: more info or changes requested,please add a reproduction" - exempt-issue-labels: "roadmap,next,bug" - operations-per-run: 300 # 1 operation per 100 issues, the rest is to label/comment/close + days-before-pr-stale: 365 + remove-pr-stale-when-updated: true + exempt-pr-labels: "roadmap,next,bug" + 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)" + 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)" + operations-per-run: 300 diff --git a/.github/workflows/prerelease-comment.yml b/.github/workflows/prerelease-comment.yml index 7d4089ae1ac..41e7f693a25 100644 --- a/.github/workflows/prerelease-comment.yml +++ b/.github/workflows/prerelease-comment.yml @@ -49,7 +49,7 @@ jobs: A new prerelease is available for testing: ```sh - npx shadcn@${{ env.BETA_PACKAGE_VERSION }} + pnpm dlx shadcn@${{ env.BETA_PACKAGE_VERSION }} ``` - name: "Remove the autorelease label once published" diff --git a/.github/workflows/prerelease.yml b/.github/workflows/prerelease.yml index 1f1932756aa..ad80e2f3086 100644 --- a/.github/workflows/prerelease.yml +++ b/.github/workflows/prerelease.yml @@ -54,7 +54,7 @@ jobs: path: packages/shadcn - name: Upload packaged artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: npm-package-shadcn@${{ steps.package-version.outputs.current-version }}-pr-${{ github.event.number }} # encode the PR number into the artifact name path: packages/shadcn/dist/index.js diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b3c17a17fbb..56cc0ea4597 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -41,7 +41,7 @@ jobs: - name: Create Version PR or Publish to NPM id: changesets - uses: changesets/action@v1.4.1 + uses: changesets/action@v1 with: commit: "chore(release): version packages" title: "chore(release): version packages" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5cce791f415..fe4dd7902bb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,6 +8,9 @@ jobs: test: runs-on: ubuntu-latest name: pnpm test + env: + NEXT_PUBLIC_APP_URL: http://localhost:4000 + NEXT_PUBLIC_V0_URL: https://v0.dev steps: - uses: actions/checkout@v3 with: @@ -39,4 +42,7 @@ jobs: - name: Install dependencies run: pnpm install + - name: Build packages + run: pnpm build --filter=shadcn + - run: pnpm test diff --git a/.github/workflows/validate-registries.yml b/.github/workflows/validate-registries.yml new file mode 100644 index 00000000000..e5dbc743b88 --- /dev/null +++ b/.github/workflows/validate-registries.yml @@ -0,0 +1,54 @@ +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 diff --git a/.npmrc b/.npmrc index 3e775efb0f4..6613b112ac8 100644 --- a/.npmrc +++ b/.npmrc @@ -1 +1,2 @@ auto-install-peers=true +link-workspace-packages=true diff --git a/.vscode/settings.json b/.vscode/settings.json index 48a495a6a0d..86d709b11e2 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,10 +3,7 @@ { "pattern": "apps/*/" }, { "pattern": "packages/*/" } ], - "tailwindCSS.experimental.classRegex": [ - ["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"], - ["cn\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"] - ], + "tailwindCSS.classFunctions": ["cva", "cn"], "vitest.debugExclude": [ "/**", "**/node_modules/**", diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f45807d242e..0927b4b58f9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -91,6 +91,42 @@ pnpm --filter=www 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 registry (main site) to make sure the components are up to date: + + ```bash + pnpm v4:dev + ``` + +2. Run the development script for the CLI: + + ```bash + pnpm shadcn:dev + ``` + +3. 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 -c ~/Desktop/my-app + ``` + +4. To run the tests for the CLI: + + ```bash + pnpm --filter=shadcn test + ``` + +This workflow ensures that you are running the most recent version of the registry and testing the CLI properly in your local environment. + ## Documentation The documentation for this project is located in the `www` workspace. You can run the documentation locally by running the following command: diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000000..366e5bd4982 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,9 @@ +# 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" button. diff --git a/apps/v4/.env.example b/apps/v4/.env.example new file mode 100644 index 00000000000..d2f09fb8818 --- /dev/null +++ b/apps/v4/.env.example @@ -0,0 +1,2 @@ +NEXT_PUBLIC_V0_URL=https://v0.dev +NEXT_PUBLIC_APP_URL=http://localhost:4000 \ No newline at end of file diff --git a/apps/v4/.gitignore b/apps/v4/.gitignore new file mode 100644 index 00000000000..db9ea2f2569 --- /dev/null +++ b/apps/v4/.gitignore @@ -0,0 +1,48 @@ +# 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 diff --git a/apps/v4/.prettierignore b/apps/v4/.prettierignore new file mode 100644 index 00000000000..1a1509ae77e --- /dev/null +++ b/apps/v4/.prettierignore @@ -0,0 +1,7 @@ +dist +node_modules +.next +build +.contentlayer +registry/__index__.tsx +content/docs/components/calendar.mdx diff --git a/apps/v4/README.md b/apps/v4/README.md new file mode 100644 index 00000000000..cb8a74abedc --- /dev/null +++ b/apps/v4/README.md @@ -0,0 +1 @@ +This is a wip registry for the `shadcn` canary version. It has React 19 and Tailwind v4 components. diff --git a/apps/v4/app/(app)/(root)/page.tsx b/apps/v4/app/(app)/(root)/page.tsx new file mode 100644 index 00000000000..a9ef59336a0 --- /dev/null +++ b/apps/v4/app/(app)/(root)/page.tsx @@ -0,0 +1,96 @@ +import { Metadata } from "next" +import Image from "next/image" +import Link from "next/link" + +import { Announcement } from "@/components/announcement" +import { CardsDemo } from "@/components/cards" +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" + +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 ( +
+ + + {title} + {description} + + + + + + + + + +
+
+
+ Dashboard + Dashboard +
+
+ +
+
+
+
+ ) +} diff --git a/apps/v4/app/(app)/blocks/[...categories]/page.tsx b/apps/v4/app/(app)/blocks/[...categories]/page.tsx new file mode 100644 index 00000000000..9c3b9f227cc --- /dev/null +++ b/apps/v4/app/(app)/blocks/[...categories]/page.tsx @@ -0,0 +1,30 @@ +import { getAllBlockIds } from "@/lib/blocks" +import { BlockDisplay } from "@/components/block-display" +import { registryCategories } from "@/registry/registry-categories" + +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 = [] } = await params + const blocks = await getAllBlockIds(["registry:block"], categories) + + return ( +
+ {blocks.map((name) => ( + + ))} +
+ ) +} diff --git a/apps/v4/app/(app)/blocks/layout.tsx b/apps/v4/app/(app)/blocks/layout.tsx new file mode 100644 index 00000000000..7f1923be29e --- /dev/null +++ b/apps/v4/app/(app)/blocks/layout.tsx @@ -0,0 +1,79 @@ +import { 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 ( + <> + + + {title} + {description} + + + + + + + + + +
+
{children}
+
+ + ) +} diff --git a/apps/v4/app/(app)/blocks/page.tsx b/apps/v4/app/(app)/blocks/page.tsx new file mode 100644 index 00000000000..e9687a962a5 --- /dev/null +++ b/apps/v4/app/(app)/blocks/page.tsx @@ -0,0 +1,32 @@ +import Link from "next/link" + +import { BlockDisplay } from "@/components/block-display" +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() { + return ( +
+ {FEATURED_BLOCKS.map((name) => ( + + ))} +
+
+ +
+
+
+ ) +} diff --git a/apps/v4/app/(app)/charts/[type]/page.tsx b/apps/v4/app/(app)/charts/[type]/page.tsx new file mode 100644 index 00000000000..65cdac9ab3c --- /dev/null +++ b/apps/v4/app/(app)/charts/[type]/page.tsx @@ -0,0 +1,71 @@ +import * as React from "react" +import { notFound } from "next/navigation" + +import { cn } from "@/lib/utils" +import { ChartDisplay } from "@/components/chart-display" +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] + + return ( +
+

+ {type.charAt(0).toUpperCase() + type.slice(1)} Charts +

+
+ {Array.from({ length: 12 }).map((_, index) => { + const chart = chartList[index] + return chart ? ( + + + + ) : ( +
+ ) + })} +
+
+ ) +} diff --git a/apps/v4/app/(app)/charts/charts.tsx b/apps/v4/app/(app)/charts/charts.tsx new file mode 100644 index 00000000000..036fc976dd4 --- /dev/null +++ b/apps/v4/app/(app)/charts/charts.tsx @@ -0,0 +1,275 @@ +import * 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, +} diff --git a/apps/v4/app/(app)/charts/layout.tsx b/apps/v4/app/(app)/charts/layout.tsx new file mode 100644 index 00000000000..608bb1e4e02 --- /dev/null +++ b/apps/v4/app/(app)/charts/layout.tsx @@ -0,0 +1,75 @@ +import { 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 ( + <> + + + {title} + {description} + + + + + + + + + +
+
+
{children}
+
+
+ + ) +} diff --git a/apps/v4/app/(app)/colors/layout.tsx b/apps/v4/app/(app)/colors/layout.tsx new file mode 100644 index 00000000000..94dcd833553 --- /dev/null +++ b/apps/v4/app/(app)/colors/layout.tsx @@ -0,0 +1,78 @@ +import { 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 ( +
+ + + {title} + {description} + + + + + +
+
+
+ +
+
+
+
+
+
+ {children} +
+
+
+
+ ) +} diff --git a/apps/v4/app/(app)/colors/page.tsx b/apps/v4/app/(app)/colors/page.tsx new file mode 100644 index 00000000000..be716579301 --- /dev/null +++ b/apps/v4/app/(app)/colors/page.tsx @@ -0,0 +1,17 @@ +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 ( +
+ {colors.map((colorPalette) => ( + + ))} +
+ ) +} diff --git a/apps/v4/app/(app)/docs/[[...slug]]/page.tsx b/apps/v4/app/(app)/docs/[[...slug]]/page.tsx new file mode 100644 index 00000000000..5f545d54dc1 --- /dev/null +++ b/apps/v4/app/(app)/docs/[[...slug]]/page.tsx @@ -0,0 +1,207 @@ +import Link from "next/link" +import { notFound } from "next/navigation" +import { mdxComponents } from "@/mdx-components" +import { + IconArrowLeft, + IconArrowRight, + IconArrowUpRight, +} from "@tabler/icons-react" +import { findNeighbour } from "fumadocs-core/server" + +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 = await findNeighbour(source.pageTree, page.url) + + const links = doc.links + + return ( +
+
+
+
+
+
+
+

+ {doc.title} +

+
+ + {neighbours.previous && ( + + )} + {neighbours.next && ( + + )} +
+
+ {doc.description && ( +

+ {doc.description} +

+ )} +
+ {links ? ( +
+ {links?.doc && ( + + + Docs + + + )} + {links?.api && ( + + + API Reference + + + )} +
+ ) : null} +
+
+ +
+
+
+ {neighbours.previous && ( + + )} + {neighbours.next && ( + + )} +
+
+
+
+ {doc.toc?.length ? ( +
+ +
+
+ ) : null} +
+ +
+
+
+ ) +} diff --git a/apps/v4/app/(app)/docs/layout.tsx b/apps/v4/app/(app)/docs/layout.tsx new file mode 100644 index 00000000000..65e202a9879 --- /dev/null +++ b/apps/v4/app/(app)/docs/layout.tsx @@ -0,0 +1,18 @@ +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 ( +
+ + +
{children}
+
+
+ ) +} diff --git a/apps/v4/app/(app)/examples/authentication/components/user-auth-form.tsx b/apps/v4/app/(app)/examples/authentication/components/user-auth-form.tsx new file mode 100644 index 00000000000..e1f9fed6f14 --- /dev/null +++ b/apps/v4/app/(app)/examples/authentication/components/user-auth-form.tsx @@ -0,0 +1,72 @@ +"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 { Input } from "@/registry/new-york-v4/ui/input" +import { Label } from "@/registry/new-york-v4/ui/label" + +export function UserAuthForm({ + className, + ...props +}: React.ComponentProps<"div">) { + const [isLoading, setIsLoading] = React.useState(false) + + async function onSubmit(event: React.SyntheticEvent) { + event.preventDefault() + setIsLoading(true) + + setTimeout(() => { + setIsLoading(false) + }, 3000) + } + + return ( +
+
+
+
+ + +
+ +
+
+
+
+ +
+
+ + Or continue with + +
+
+ +
+ ) +} diff --git a/apps/v4/app/(app)/examples/authentication/page.tsx b/apps/v4/app/(app)/examples/authentication/page.tsx new file mode 100644 index 00000000000..636faa6b538 --- /dev/null +++ b/apps/v4/app/(app)/examples/authentication/page.tsx @@ -0,0 +1,103 @@ +import { 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 { 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 ( + <> +
+ Authentication + Authentication +
+
+ + Login + +
+
+
+ + + + Acme Inc +
+
+
+ “This library has saved me countless hours of work and + helped me deliver stunning designs to my clients faster than ever + before.” - Sofia Davis +
+
+
+
+
+
+

+ Create an account +

+

+ Enter your email below to create your account +

+
+ +

+ By clicking continue, you agree to our{" "} + + Terms of Service + {" "} + and{" "} + + Privacy Policy + + . +

+
+
+
+ + ) +} diff --git a/apps/v4/app/(app)/examples/dashboard/components/app-sidebar.tsx b/apps/v4/app/(app)/examples/dashboard/components/app-sidebar.tsx new file mode 100644 index 00000000000..c4de9119275 --- /dev/null +++ b/apps/v4/app/(app)/examples/dashboard/components/app-sidebar.tsx @@ -0,0 +1,182 @@ +"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) { + return ( + + + + + + + + Acme Inc. + + + + + + + + + + + + + + + ) +} diff --git a/apps/v4/app/(app)/examples/dashboard/components/chart-area-interactive.tsx b/apps/v4/app/(app)/examples/dashboard/components/chart-area-interactive.tsx new file mode 100644 index 00000000000..dab596a8139 --- /dev/null +++ b/apps/v4/app/(app)/examples/dashboard/components/chart-area-interactive.tsx @@ -0,0 +1,292 @@ +"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 { + ChartConfig, + ChartContainer, + ChartTooltip, + ChartTooltipContent, +} 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("90d") + + React.useEffect(() => { + if (isMobile) { + setTimeRange("7d") + } + }, [isMobile]) + + 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 ( + + + Total Visitors + + + Total for the last 3 months + + Last 3 months + + + + Last 3 months + Last 30 days + Last 7 days + + + + + + + + + + + + + + + + + + + { + const date = new Date(value) + return date.toLocaleDateString("en-US", { + month: "short", + day: "numeric", + }) + }} + /> + { + return new Date(value).toLocaleDateString("en-US", { + month: "short", + day: "numeric", + }) + }} + indicator="dot" + /> + } + /> + + + + + + + ) +} diff --git a/apps/v4/app/(app)/examples/dashboard/components/data-table.tsx b/apps/v4/app/(app)/examples/dashboard/components/data-table.tsx new file mode 100644 index 00000000000..1a34e73067d --- /dev/null +++ b/apps/v4/app/(app)/examples/dashboard/components/data-table.tsx @@ -0,0 +1,807 @@ +"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 { + ColumnDef, + ColumnFiltersState, + flexRender, + getCoreRowModel, + getFacetedRowModel, + getFacetedUniqueValues, + getFilteredRowModel, + getPaginationRowModel, + getSortedRowModel, + Row, + SortingState, + useReactTable, + 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 { + ChartConfig, + ChartContainer, + ChartTooltip, + ChartTooltipContent, +} 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 ( + + ) +} + +const columns: ColumnDef>[] = [ + { + id: "drag", + header: () => null, + cell: ({ row }) => , + }, + { + id: "select", + header: ({ table }) => ( +
+ table.toggleAllPageRowsSelected(!!value)} + aria-label="Select all" + /> +
+ ), + cell: ({ row }) => ( +
+ row.toggleSelected(!!value)} + aria-label="Select row" + /> +
+ ), + enableSorting: false, + enableHiding: false, + }, + { + accessorKey: "header", + header: "Header", + cell: ({ row }) => { + return + }, + enableHiding: false, + }, + { + accessorKey: "type", + header: "Section Type", + cell: ({ row }) => ( +
+ + {row.original.type} + +
+ ), + }, + { + accessorKey: "status", + header: "Status", + cell: ({ row }) => ( + + {row.original.status === "Done" ? ( + + ) : ( + + )} + {row.original.status} + + ), + }, + { + accessorKey: "target", + header: () =>
Target
, + cell: ({ row }) => ( +
{ + e.preventDefault() + toast.promise(new Promise((resolve) => setTimeout(resolve, 1000)), { + loading: `Saving ${row.original.header}`, + success: "Done", + error: "Error", + }) + }} + > + + +
+ ), + }, + { + accessorKey: "limit", + header: () =>
Limit
, + cell: ({ row }) => ( +
{ + e.preventDefault() + toast.promise(new Promise((resolve) => setTimeout(resolve, 1000)), { + loading: `Saving ${row.original.header}`, + success: "Done", + error: "Error", + }) + }} + > + + +
+ ), + }, + { + accessorKey: "reviewer", + header: "Reviewer", + cell: ({ row }) => { + const isAssigned = row.original.reviewer !== "Assign reviewer" + + if (isAssigned) { + return row.original.reviewer + } + + return ( + <> + + + + ) + }, + }, + { + id: "actions", + cell: () => ( + + + + + + Edit + Make a copy + Favorite + + Delete + + + ), + }, +] + +function DraggableRow({ row }: { row: Row> }) { + const { transform, transition, setNodeRef, isDragging } = useSortable({ + id: row.original.id, + }) + + return ( + + {row.getVisibleCells().map((cell) => ( + + {flexRender(cell.column.columnDef.cell, cell.getContext())} + + ))} + + ) +} + +export function DataTable({ + data: initialData, +}: { + data: z.infer[] +}) { + const [data, setData] = React.useState(() => initialData) + const [rowSelection, setRowSelection] = React.useState({}) + const [columnVisibility, setColumnVisibility] = + React.useState({}) + const [columnFilters, setColumnFilters] = React.useState( + [] + ) + const [sorting, setSorting] = React.useState([]) + 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( + () => 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 ( + +
+ + + + Outline + + Past Performance 3 + + + Key Personnel 2 + + Focus Documents + +
+ + + + + + {table + .getAllColumns() + .filter( + (column) => + typeof column.accessorFn !== "undefined" && + column.getCanHide() + ) + .map((column) => { + return ( + + column.toggleVisibility(!!value) + } + > + {column.id} + + ) + })} + + + +
+
+ +
+ + + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + return ( + + {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext() + )} + + ) + })} + + ))} + + + {table.getRowModel().rows?.length ? ( + + {table.getRowModel().rows.map((row) => ( + + ))} + + ) : ( + + + No results. + + + )} + +
+
+
+
+
+ {table.getFilteredSelectedRowModel().rows.length} of{" "} + {table.getFilteredRowModel().rows.length} row(s) selected. +
+
+
+ + +
+
+ Page {table.getState().pagination.pageIndex + 1} of{" "} + {table.getPageCount()} +
+
+ + + + +
+
+
+
+ +
+
+ +
+
+ +
+
+
+ ) +} + +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 }) { + const isMobile = useIsMobile() + + return ( + + + + + + + {item.header} + + Showing total visitors for the last 6 months + + +
+ {!isMobile && ( + <> + + + + value.slice(0, 3)} + hide + /> + } + /> + + + + + +
+
+ Trending up by 5.2% this month{" "} + +
+
+ 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. +
+
+ + + )} +
+
+ + +
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+
+ + +
+
+
+ + + + + + +
+
+ ) +} diff --git a/apps/v4/app/(app)/examples/dashboard/components/nav-documents.tsx b/apps/v4/app/(app)/examples/dashboard/components/nav-documents.tsx new file mode 100644 index 00000000000..503c59938a7 --- /dev/null +++ b/apps/v4/app/(app)/examples/dashboard/components/nav-documents.tsx @@ -0,0 +1,92 @@ +"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 ( + + Documents + + {items.map((item) => ( + + + + + {item.name} + + + + + + + More + + + + + + Open + + + + Share + + + + + Delete + + + + + ))} + + + + More + + + + + ) +} diff --git a/apps/v4/app/(app)/examples/dashboard/components/nav-main.tsx b/apps/v4/app/(app)/examples/dashboard/components/nav-main.tsx new file mode 100644 index 00000000000..fd044401f74 --- /dev/null +++ b/apps/v4/app/(app)/examples/dashboard/components/nav-main.tsx @@ -0,0 +1,40 @@ +"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 ( + + + Home + + {items.map((item) => ( + + + {item.icon && } + {item.title} + + + ))} + + + + ) +} diff --git a/apps/v4/app/(app)/examples/dashboard/components/nav-secondary.tsx b/apps/v4/app/(app)/examples/dashboard/components/nav-secondary.tsx new file mode 100644 index 00000000000..c4f2ea77e63 --- /dev/null +++ b/apps/v4/app/(app)/examples/dashboard/components/nav-secondary.tsx @@ -0,0 +1,42 @@ +"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) { + return ( + + + + {items.map((item) => ( + + + + + {item.title} + + + + ))} + + + + ) +} diff --git a/apps/v4/app/(app)/examples/dashboard/components/nav-user.tsx b/apps/v4/app/(app)/examples/dashboard/components/nav-user.tsx new file mode 100644 index 00000000000..4d63e06cf80 --- /dev/null +++ b/apps/v4/app/(app)/examples/dashboard/components/nav-user.tsx @@ -0,0 +1,110 @@ +"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 ( + + + + + + + + CN + +
+ {user.name} + + {user.email} + +
+ +
+
+ + +
+ + + CN + +
+ {user.name} + + {user.email} + +
+
+
+ + + + + Account + + + + Billing + + + + Notifications + + + + + + Log out + +
+
+
+
+ ) +} diff --git a/apps/v4/app/(app)/examples/dashboard/components/section-cards.tsx b/apps/v4/app/(app)/examples/dashboard/components/section-cards.tsx new file mode 100644 index 00000000000..39cf6ecafb5 --- /dev/null +++ b/apps/v4/app/(app)/examples/dashboard/components/section-cards.tsx @@ -0,0 +1,102 @@ +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 ( +
+ + + Total Revenue + + $1,250.00 + + + + + +12.5% + + + + +
+ Trending up this month +
+
+ Visitors for the last 6 months +
+
+
+ + + New Customers + + 1,234 + + + + + -20% + + + + +
+ Down 20% this period +
+
+ Acquisition needs attention +
+
+
+ + + Active Accounts + + 45,678 + + + + + +12.5% + + + + +
+ Strong user retention +
+
Engagement exceed targets
+
+
+ + + Growth Rate + + 4.5% + + + + + +4.5% + + + + +
+ Steady performance increase +
+
Meets growth projections
+
+
+
+ ) +} diff --git a/apps/v4/app/(app)/examples/dashboard/components/site-header.tsx b/apps/v4/app/(app)/examples/dashboard/components/site-header.tsx new file mode 100644 index 00000000000..f0d1612215d --- /dev/null +++ b/apps/v4/app/(app)/examples/dashboard/components/site-header.tsx @@ -0,0 +1,19 @@ +import { IconCirclePlusFilled } from "@tabler/icons-react" + +import { Button } from "@/registry/new-york-v4/ui/button" + +export function SiteHeader() { + return ( +
+
+

Documents

+
+ +
+
+
+ ) +} diff --git a/apps/v4/app/(app)/examples/dashboard/data.json b/apps/v4/app/(app)/examples/dashboard/data.json new file mode 100644 index 00000000000..ec0873641b7 --- /dev/null +++ b/apps/v4/app/(app)/examples/dashboard/data.json @@ -0,0 +1,614 @@ +[ + { + "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" + } +] diff --git a/apps/v4/app/(app)/examples/dashboard/page.tsx b/apps/v4/app/(app)/examples/dashboard/page.tsx new file mode 100644 index 00000000000..5e0ca2afb80 --- /dev/null +++ b/apps/v4/app/(app)/examples/dashboard/page.tsx @@ -0,0 +1,63 @@ +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 ( + <> +
+ Authentication + Authentication +
+ + + + +
+
+
+ +
+ +
+ +
+
+
+
+
+ + ) +} diff --git a/apps/v4/app/(app)/examples/layout.tsx b/apps/v4/app/(app)/examples/layout.tsx new file mode 100644 index 00000000000..4798a95390d --- /dev/null +++ b/apps/v4/app/(app)/examples/layout.tsx @@ -0,0 +1,83 @@ +import { 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 = "Examples" +const description = "Check out some examples app built using the components." + +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 ( + <> + + + Build your Component Library + + A set of beautifully-designed, accessible components and a code + distribution platform. Works with your favorite frameworks. Open + Source. Open Code. + + + + + + + + + + +
+
+
+ {children} +
+
+
+ + ) +} diff --git a/apps/v4/app/(app)/examples/playground/components/code-viewer.tsx b/apps/v4/app/(app)/examples/playground/components/code-viewer.tsx new file mode 100644 index 00000000000..bc35a655441 --- /dev/null +++ b/apps/v4/app/(app)/examples/playground/components/code-viewer.tsx @@ -0,0 +1,89 @@ +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 ( + + + + + + + View code + + You can use the following code to start integrating your current + prompt and settings into your application. + + +
+
+
+              
+                
+                  import os
+                
+                
+                  import openai
+                
+                
+                
+                  openai.api_key = os.getenv(
+                  
+                    "OPENAI_API_KEY"
+                  
+                  )
+                
+                
+                response = openai.Completion.create(
+                
+                  {" "}
+                  model=
+                  "davinci",
+                
+                
+                  {" "}
+                  prompt="",
+                
+                
+                  {" "}
+                  temperature=0.9,
+                
+                
+                  {" "}
+                  max_tokens=5,
+                
+                
+                  {" "}
+                  top_p=1,
+                
+                
+                  {" "}
+                  frequency_penalty=0,
+                
+                
+                  {" "}
+                  presence_penalty=0,
+                
+                )
+              
+            
+
+
+

+ Your API Key can be found here. You should use environment + variables or a secret management tool to expose your key to your + applications. +

+
+
+
+
+ ) +} diff --git a/apps/v4/app/(app)/examples/playground/components/maxlength-selector.tsx b/apps/v4/app/(app)/examples/playground/components/maxlength-selector.tsx new file mode 100644 index 00000000000..c2b0a277ddf --- /dev/null +++ b/apps/v4/app/(app)/examples/playground/components/maxlength-selector.tsx @@ -0,0 +1,54 @@ +"use client" + +import * as React from "react" +import { 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 ( +
+ + +
+
+ + + {value} + +
+ +
+
+ + 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. + +
+
+ ) +} diff --git a/apps/v4/app/(app)/examples/playground/components/model-selector.tsx b/apps/v4/app/(app)/examples/playground/components/model-selector.tsx new file mode 100644 index 00000000000..a9c3bd6dc61 --- /dev/null +++ b/apps/v4/app/(app)/examples/playground/components/model-selector.tsx @@ -0,0 +1,162 @@ +"use client" + +import * as React from "react" +import { 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 { Model, 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(models[0]) + const [peekedModel, setPeekedModel] = React.useState(models[0]) + + return ( +
+ + + + + + The model which will generate the completion. Some models are suitable + for natural language tasks, others specialize in code. Learn more. + + + + + + + + + +
+

{peekedModel.name}

+
+ {peekedModel.description} +
+ {peekedModel.strengths ? ( +
+
+ Strengths +
+
    + {peekedModel.strengths} +
+
+ ) : null} +
+
+ + + + No Models found. + + {types.map((type) => ( + + {models + .filter((model) => model.type === type) + .map((model) => ( + setPeekedModel(model)} + onSelect={() => { + setSelectedModel(model) + setOpen(false) + }} + /> + ))} + + ))} + + +
+
+
+
+ ) +} + +interface ModelItemProps { + model: Model + isSelected: boolean + onSelect: () => void + onPeek: (model: Model) => void +} + +function ModelItem({ model, isSelected, onSelect, onPeek }: ModelItemProps) { + const ref = React.useRef(null) + + useMutationObserver(ref, (mutations) => { + mutations.forEach((mutation) => { + if ( + mutation.type === "attributes" && + mutation.attributeName === "aria-selected" && + ref.current?.getAttribute("aria-selected") === "true" + ) { + onPeek(model) + } + }) + }) + + return ( + + {model.name} + + + ) +} diff --git a/apps/v4/app/(app)/examples/playground/components/preset-actions.tsx b/apps/v4/app/(app)/examples/playground/components/preset-actions.tsx new file mode 100644 index 00000000000..3f0e80fc28a --- /dev/null +++ b/apps/v4/app/(app)/examples/playground/components/preset-actions.tsx @@ -0,0 +1,121 @@ +"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 ( + <> + + + + + + setIsOpen(true)}> + Content filter preferences + + + setShowDeleteDialog(true)} + className="text-red-600" + > + Delete preset + + + + + + + Content filter preferences + + 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. + + +
+

+ Playground Warnings +

+
+ + +
+
+ + + + + +
+
+ + + + Are you absolutely sure? + + This action cannot be undone. This preset will no longer be + accessible by you or others you've shared it with. + + + + Cancel + + + + + + ) +} diff --git a/apps/v4/app/(app)/examples/playground/components/preset-save.tsx b/apps/v4/app/(app)/examples/playground/components/preset-save.tsx new file mode 100644 index 00000000000..ea0785e94ad --- /dev/null +++ b/apps/v4/app/(app)/examples/playground/components/preset-save.tsx @@ -0,0 +1,45 @@ +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 ( + + + + + + + Save preset + + This will save the current playground state as a preset which you + can access later or share with others. + + +
+
+ + +
+
+ +