Compare commits

..

83 Commits

Author SHA1 Message Date
shadcn
e10567393d fix: chart and input-otp lint errors 2025-03-04 13:46:21 +04:00
shadcn
6382edc9bf Merge branch 'main' into main 2025-03-04 13:27:04 +04:00
shadcn
be3c1a9a98 chore: minor fixes for registry blocks (#6850) 2025-03-04 13:26:40 +04:00
shadcn
1befb7a653 chore(shadcn): changeset 2025-03-04 13:01:50 +04:00
shadcn
e8f245812f fix: minor fixes 2025-03-04 11:10:36 +04:00
shadcn
c90bcef401 feat: update default monorepo template 2025-03-04 10:29:35 +04:00
shadcn
7d97a9eba3 feat(shadcn): add monorepo tailwind detection 2025-03-04 10:29:13 +04:00
shadcn
2b23bdcbec Merge branch 'main' into main 2025-03-03 11:58:12 +04:00
shadcn
418b2d9e14 fix(v4): misc select and tabs fixes (#6836) 2025-03-03 11:56:29 +04:00
JEM
7eb77fb3b9 fix(navigation-menu): 6825 - Update open state styles and remove unused classes (#6827)
* fix(navigation-menu): 6825 - Update open state styles and remove unused classes

Adjusts navigation menu trigger open styles to apply opacity /50 only when not hover/focused.
Removes unused data-[active] and data-[active=true] classes.

Fixes #6825

* Adds data-[active=true] back to NavigationMenuLink component.

Includes text color changes for open/active states to improve UI consistency.

* v3 registry

* v4 registry
2025-03-03 11:56:15 +04:00
Bryan Lee
f759a25354 docs(www): update Tailwind v4 migration guide with CSS and React changes (#6813)
Co-authored-by: shadcn <m@shadcn.com>
2025-03-03 11:53:09 +04:00
shadcn
bd09f57cb7 Merge branch 'main' into main 2025-03-03 11:51:45 +04:00
JEM
0b288883d2 fix(sidebar, sidebar-16): 6651 - Resolve unnecessary vertical scroll (#6782)
* fix(sidebar, sidebar-16): 6651 - Resolve unnecessary vertical scroll

Includes "label" in registry dependencies for sidebar-16 components.
Adjusts sidebar and sidebar inset dimensions to eliminate unwanted scroll behavior.

Fixes #6651

* chore: build registry

* Add label component to registry and adjust sidebar styles

Adds 'label' component to the registry.
Corrects sidebar styles and layout for better responsiveness.

* fix(sidebar-09): Add missing Label component dependency

Ensures the Label component is included in the registry dependencies for sidebar-09 to prevent missing component errors.

---------

Co-authored-by: shadcn <m@shadcn.com>
2025-03-02 15:33:09 +04:00
Christian Ivicevic
cf4ccb34e2 docs(www): mention to add suppressHydrationWarning in the dark mode docs for next (#5879)
Users have repeatedly run into hydration errors because they don't read the next-themes readme and don't pay attention to the shadcn/ui docs and miss that the `suppressHydrationWarning` prop must be added.

This commit mentions this explicitly and highlights the line in the snippet (as well as fixing the previously wrong highlighting).
2025-03-02 11:27:48 +00:00
shadcn
dc80a326c2 ci: fix stale bot 2025-03-02 15:24:05 +04:00
Lloyd Richards
92e5c7ab2c ci: fix up stale issue handler (#6457)
Co-authored-by: shadcn <m@shadcn.com>
2025-03-02 14:59:26 +04:00
shadcn
d44971b6c2 feat(v4): add products-01 block 2025-02-28 23:14:59 +04:00
shadcn
8539dd6eec fix(shadcn): do not add outline-ring for v3 (#6814)
* fix(shadcn): do not add ring if tailwind v3

* chore: changeset

* test(shadcn): update snapshots
2025-02-28 21:23:08 +04:00
shadcn
bc7df68620 feat(shadcn): install routes for next-pages, laravel and react-router (#6811)
* feat(shadcn): install routes for next-pages, laravel and react-router

* chore: changeset
2025-02-28 18:06:06 +04:00
迷渡
1832f258bd fix(shadcn): Remove duplicate spaces (#6696) 2025-02-28 17:19:28 +04:00
shadcn
e2730f7276 docs(www): update docs for cli, components.json and theming 2025-02-28 16:38:39 +04:00
shadcn
22ba26152a docs(www): update tanstack docs 2025-02-28 16:21:29 +04:00
shadcn
76d6a59f9f docs(www): update all framework docs (#6809)
* docs(www): update all framework docs

* docs(www): update Tailwind v4 docs
2025-02-28 15:53:04 +04:00
shadcn
3a2a87386f fix(v4): badge overflow 2025-02-28 15:44:04 +04:00
shadcn
1822d95883 docs(www): update docs for vite (#6807) 2025-02-28 14:30:10 +04:00
shadcn
f90f5148eb fix(v4): responsive for the demo (#6803)
* fix(v4): responsive

* fix: type

* fix(v4): remove the login link
2025-02-28 13:31:06 +04:00
shadcn
99b0a5ac90 fix(v4): remove login and sandbox links 2025-02-28 13:29:16 +04:00
shadcn
984cb2a1ea feat(v4): update default ring colors 2025-02-28 13:28:17 +04:00
shadcn
fed2bac1d9 Merge branch 'main' of github.com:shadcn-ui/ui 2025-02-27 15:51:17 +04:00
shadcn
1ebfcd7cd9 feat(registry): update sidebars 2025-02-27 15:51:05 +04:00
Keit Oliveira
535a7d9220 fix(form): avoid undefined message error as string (#6729)
Add validation to prevent converting `undefined` error messages to string in the `FormMessage` component.

This fix addresses issues that occur in complex validation structures, such as validating entire objects.

```ts
const formSchema = z.object({
  location: z.object({
    city: z.string().nonempty(),
    state: z.string().nonempty(),
    country: z.string().nonempty(),
  }).superRefine((data, ctx) => {
    ...
  });
```

```tsx
<FormField
  control={form.control}
  name="location"
  render={() => (
    <FormItem>
      <FormField
        control={form.control}
        name="location.city"
        render={({ field }) => (
          <FormItem>
            <FormLabel>City</FormLabel>
            <FormControl>
              <Input placeholder="Enter city" {...field} />
            </FormControl>
          </FormItem>
        )}
      />

      <FormField
        control={form.control}
        name="location.state"
        render={({ field }) => (
          <FormItem>
            <FormLabel>State</FormLabel>
            <FormControl>
              <Input placeholder="Enter state" {...field} />
            </FormControl>
          </FormItem>
        )}
      />

      <FormField
        control={form.control}
        name="location.country"
        render={({ field }) => (
          <FormItem>
            <FormLabel>Country</FormLabel>
            <FormControl>
              <Input placeholder="Enter country" {...field} />
            </FormControl>
          </FormItem>
        )}
      />

      <FormMessage />
    </FormItem>
  )}
/>
```
2025-02-26 20:35:01 +00:00
Cyrus Zei
e7f8cd4566 chore(www): update the user info to use example instead (#6766) 2025-02-26 20:47:23 +04:00
shadcn
8506977f83 chore: deprecate shadcn-ui (#6780)
* chore: deprecate shadcn-ui

* deps(shadcn-ui): remove unused dependencies

* fix: tests

* deps: bump version
2025-02-26 20:34:51 +04:00
shadcn
575c0214da feat(v4): minor component updates 2025-02-26 17:28:49 +04:00
shadcn
a1ab28bf58 Merge branch 'main' of github.com:shadcn-ui/ui 2025-02-25 16:00:45 +04:00
shadcn
190ae2dcd8 docs(www): link get started to installation 2025-02-25 16:00:19 +04:00
Fin Chen
d5920cc3c1 fix(www): extra semicolon of --radius (#6737) (#6762) 2025-02-25 15:51:38 +04:00
shadcn
89652889db docs(www): update tanstack start docs (#6765) 2025-02-25 15:51:19 +04:00
Pavi
57d15bb2d5 fix(docs): update monorepo docs (#6647)
This pull request includes a small change to the `templates/monorepo-next/README.md` file. The change corrects the import path for the `Button` component to reflect the correct directory structure.

* [`templates/monorepo-next/README.md`](diffhunk://#diff-4f9dc7da681b34ccf99bccdcb804625f61e47afc70a483548b3ae093b564e072L30-R30): Updated the import path for the `Button` component to `@workspace/ui/components/button`.
2025-02-23 07:35:26 +00:00
shadcn
779517a1d4 fix(shadcn): check for empty css vars (#6733)
* fix(shadcn): check for empty css vars

* chore: changeset
2025-02-22 13:44:13 +04:00
Pasquale Vitiello
b567f7a6c1 fix: class name typo (#6633)
Co-authored-by: shadcn <m@shadcn.com>
2025-02-22 13:43:57 +04:00
shadcn
839afa714f feat(shadcn): cache registry calls (#6732)
* fix(shadcn): cache registry calls

* chore: changeset
2025-02-22 13:21:16 +04:00
Aaron
32f0bc0de9 fix(select): make select placeholder text show as faded (#6055)
Use the [data-placeholder] data attribute supplied by radix-ui instead of the `placeholder` state selector, which doesn't do anything for buttons (afaict, which radix-ui renders `SelectTrigger` into).

Reference: https://www.radix-ui.com/primitives/docs/components/select#api-reference

Before:
![Screenshot from 2024-12-12 15-52-50](https://github.com/user-attachments/assets/106c4c44-d6b9-4513-b2a9-77814ae46b8b)

After:
![image](https://github.com/user-attachments/assets/b09556f7-e274-4918-90c4-32c5aa8773cc)
2025-02-22 08:17:56 +00:00
Kaikai
a3fee32c96 feat(monorepo): use tailwindcss v4 in monorepo example 2025-02-22 02:59:48 +08:00
shadcn
7d2499c803 fix(v4): dark mode 2025-02-21 21:08:07 +04:00
shadcn
84b88440c3 fix(v4): login spacing 2025-02-21 21:05:52 +04:00
shadcn
a5122f9029 fix(shadcn): do not override existing vars (#6721)
* fix(shadcn): do not override existing vars

* test(shadcn): update snapshots

* chore: changeset
2025-02-21 20:58:02 +04:00
shadcn
3b90317e3c feat(v4): update cards 2025-02-21 20:44:09 +04:00
shadcn
9d7c7b8978 fix(shadcn): fallback style (#6720) 2025-02-21 20:19:59 +04:00
shadcn
957df8997c fix: misc fixes for v4 components (#6719)
* feat(v4): login tests

* feat(v4): fix button and toggle
2025-02-21 20:07:48 +04:00
shadcn
460240a86c chore: remove console 2025-02-21 19:58:09 +04:00
shadcn
bfffb08f42 fix(shadcn): issue with workspace config (#6712) 2025-02-21 15:18:06 +04:00
shadcn
5282332e52 feat(shadcn): do not ask if file is identical (#6711)
* feat(shadcn): do not ask if file is identical

* fix: add missing files
2025-02-21 15:04:19 +04:00
shadcn
3db8a07b3f feat(shadcn): default to cssVariables: true. add --no-css-variables option (#6707)
* feat(shadcn): default to css vars

* chore: changeset

* fix(shadcn): add command
2025-02-21 11:49:17 +04:00
shadcn
187959435e Merge branch 'main' of github.com:shadcn-ui/ui 2025-02-20 23:41:43 +04:00
shadcn
5953229417 fix(v4): input otp 2025-02-20 23:41:37 +04:00
Zach Warunek
d6159023ed fix(cli): monorepo cn import (#6530)
* fix monorepo cn import

* chore: changeset

* style(shadcn): format

---------

Co-authored-by: shadcn <m@shadcn.com>
2025-02-20 23:41:21 +04:00
shadcn
b0774b0578 fix: slider and demo components 2025-02-20 23:03:39 +04:00
shadcn
7cefabbe98 fix: switch focus 2025-02-20 22:56:33 +04:00
shadcn
3740373f99 feat(v4): update colors (#6693)
* feat(v4): update colors

* fix: sonner button

* feat(shadcn): update base colors to oklch

* fix: button gaps

* fix: sidebar and chart

* feat: update ring colors

* feat(v4): neutral color and fixes

* fix: fonts

* chore: changeset

* fix: revert utils
2025-02-20 19:32:56 +04:00
David Leger
9c4419f249 docs: fix TanStack logo on dark mode (#6695)
Inherit logo color from font color so that it renders properly in dark mode.

### Before
<img width="939" alt="image" src="https://github.com/user-attachments/assets/66327284-354e-4f07-a87b-465c37bac6f3" />


### After
<img width="930" alt="image" src="https://github.com/user-attachments/assets/2cd0b22d-f8be-4993-976e-0b9751400bfc" />
2025-02-20 14:34:51 +00:00
shadcn
ea9b81594d docs(www): update link for vaul 2025-02-17 23:04:58 +04:00
shadcn
4810f744e3 fix(www): misc site fixes 2025-02-16 15:48:54 +04:00
shadcn
38c5fb4ace feat(www): update og (#6658)
* feat(www): update og

* fix: remove export
2025-02-15 14:06:30 +04:00
shadcn
f37425bdb0 feat(shadcn): use canary for create-next-app 2025-02-14 00:21:42 +04:00
Ajay Pawar
592ef33658 style-new-york/mode-toggle button fixed (#6575) 2025-02-12 20:05:58 +04:00
shadcn
1a6da427ff feat(shadcn): add internal_registryResolveItemsTree (#6626) 2025-02-12 16:49:58 +04:00
shadcn
bd8533bd26 (11/n) shadcn: filter out deprecated from --all (#6617)
* fix(shadcn): filter out deprecated from --all

* chore: changeset
2025-02-11 13:28:41 +04:00
Pálmi Þór Valgeirsson
202131cd7b chore(deps): Upgrade @antfu/ni to v23.2.0 (#6414)
* chore(deps): Upgrade @antfu/ni to v23.2.0

* chore: changeset

* test(shadcn): add bun.lock

---------

Co-authored-by: shadcn <m@shadcn.com>
2025-02-11 12:46:53 +04:00
shadcn
7977975c9d fix(www): announcement icon color 2025-02-10 21:20:12 +04:00
shadcn
a23fec1c31 docs(www): typo 2025-02-10 21:12:25 +04:00
shadcn
2af2979cf4 docs(www): typo 2025-02-10 21:10:42 +04:00
HichemTech
aee53d90e2 Update calendar.mdx (#6501)
Expicitly define the version of react-date-picker
2025-02-10 15:43:28 +04:00
shadcn
1a68715048 Merge branch 'main' of github.com:shadcn-ui/ui 2025-02-10 12:00:15 +04:00
shadcn
34c99c1728 fix(www): spacing 2025-02-10 12:00:01 +04:00
shadcn
86642840e2 docs(www): update for tailwind v4 (#6608) 2025-02-10 11:43:03 +04:00
shadcn
fac0daac7b fix(www): code tabs (#6607) 2025-02-10 11:34:02 +04:00
shadcn
13fe24e1f8 docs(www): update introduction (#6605)
* docs(www): update introduction

* feat(www): update vercel cta

* docs(www): minor fixes
2025-02-09 20:43:49 +04:00
Long Zheng
1eb2d74d7c fix: rename sidebar cookie to be valid name (#6558)
* fix: rename sidebar cookie to be valid name

* fix: rename sidebar cookie to underscore

---------

Co-authored-by: shadcn <m@shadcn.com>
2025-02-07 19:40:47 +04:00
Prateek (प्रतीक)
16d4d38f56 fix(tanstack): fix paths value in tsconfig and check for tanstack start in devDeps also (#6590)
* fix(docs): tanstack start guide fix the paths value in tsconfig

* fix(tanstack): check for @tanstack/start in devDependencies as well

* chore: changeset

* fix(shadcn): tanstack detection

---------

Co-authored-by: shadcn <m@shadcn.com>
2025-02-07 14:31:28 +04:00
shadcn
5234c46722 feat(shadcn): add support for TanStack Start (#6507)
* feat(shadcn): add TanStack Start support

* docs(www): add docs for TanStack Start

* chore: changeset
2025-02-06 22:34:16 +04:00
shadcn
f9037239af fix: v4 page 2025-02-06 20:42:21 +04:00
shadcn
14e4726400 feat: misc updates for icons and tooltip (#6584) 2025-02-06 16:44:40 +04:00
553 changed files with 10258 additions and 20037 deletions

View File

@@ -0,0 +1,5 @@
---
"shadcn": minor
---
add support for TanStack Start

View File

@@ -0,0 +1,5 @@
---
"shadcn": patch
---
support for version detection in monorepo

View File

@@ -0,0 +1,5 @@
---
"shadcn": patch
---
upgrade @antfu/ni

View File

@@ -0,0 +1,5 @@
---
"shadcn": patch
---
do not add ring for v3

View File

@@ -0,0 +1,5 @@
---
"shadcn": minor
---
default to css vars. add --no-css-variables

View File

@@ -0,0 +1,5 @@
---
"shadcn": patch
---
cache registry calls

View File

@@ -0,0 +1,5 @@
---
"shadcn": patch
---
do not overwrite user defined vars

View File

@@ -0,0 +1,5 @@
---
"shadcn": patch
---
fix cn import bug in monorepo

View File

@@ -0,0 +1,5 @@
---
"shadcn": patch
---
filter out deprecated from --all

View File

@@ -0,0 +1,5 @@
---
"shadcn": minor
---
add oklch base color

View File

@@ -0,0 +1,5 @@
---
"shadcn": patch
---
check for empty css vars

View File

@@ -0,0 +1,5 @@
---
"shadcn": patch
---
fix tanstack check

View File

@@ -0,0 +1,5 @@
---
"shadcn": minor
---
add support for route install for react-router and laravel

View File

@@ -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-pr-close: 7
days-before-pr-stale: 15
only-pr-labels: "postpone: more info or changes requested,please add a reproduction"
days-before-issue-stale: 365 # ~2 years
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,bug"
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 theres further input. If you believe this issue is still relevant, please leave a comment or provide updated details. Thank you."
close-issue-message: "This issue has been automatically closed due to one year of inactivity. If youre 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!"
operations-per-run: 300 # 1 operation per 100 issues, the rest is to label/comment/close
- 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 # PRs with no activity in over 90 days will be marked as stale
remove-pr-stale-when-updated: true
exempt-pr-labels: "roadmap,nex,awaiting-approval,work-in-progress"
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 theres further input. If you believe this PR is still relevant, please leave a comment or provide updated details. Thank you."
close-pr-message: "This PR has been automatically closed due to one year of inactivity. Thank you for your understanding!"
operations-per-run: 300 # 1 operation per 100 issues, the rest is to label/comment/close

View File

@@ -1446,6 +1446,7 @@ export const Index: Record<string, any> = {
"dropdown-menu",
"avatar",
"switch",
"label",
],
files: [
{
@@ -1783,6 +1784,7 @@ export const Index: Record<string, any> = {
"dropdown-menu",
"avatar",
"button",
"label",
],
files: [
{
@@ -3785,4 +3787,43 @@ export const Index: Record<string, any> = {
}),
meta: undefined,
},
"products-01": {
name: "products-01",
description: "A table of products",
type: "registry:block",
registryDependencies: [
"checkbox",
"badge",
"button",
"dropdown-menu",
"pagination",
"table",
"tabs",
"select",
],
files: [
{
path: "registry/blocks/products-01/page.tsx",
type: "registry:page",
target: "app/products/page.tsx",
},
{
path: "registry/blocks/products-01/components/products-table.tsx",
type: "registry:component",
target: "",
},
],
component: React.lazy(async () => {
const mod = await import(
"@/registry/new-york-v4/blocks/products-01/page.tsx"
)
const exportName =
Object.keys(mod).find(
(key) =>
typeof mod[key] === "function" || typeof mod[key] === "object"
) || item.name
return { default: mod.default || mod[exportName] }
}),
meta: undefined,
},
}

View File

@@ -3,7 +3,7 @@ import * as Charts from "@/app/(app)/charts/charts"
export default function ChartsPage() {
return (
<div className="grid grid-cols-3 items-start gap-4 p-4 2xl:grid-cols-4">
<div className="grid flex-1 grid-cols-3 items-start gap-4 p-4 2xl:grid-cols-4">
{Object.entries(Charts)
.sort()
.map(([key, Component]) => (

View File

@@ -0,0 +1,9 @@
import { FormsDemo } from "@/components/forms-demo"
export default function FormsPage() {
return (
<div className="flex flex-1 items-center justify-center p-4">
<FormsDemo />
</div>
)
}

View File

@@ -19,27 +19,24 @@ export default async function AppLayout({
const defaultOpen = cookieStore.get("sidebar_state")?.value === "true"
return (
<SidebarProvider
defaultOpen={defaultOpen}
className="flex flex-col pt-(--header-height) [--header-height:calc(--spacing(14))]"
>
<header className="bg-background fixed inset-x-0 top-0 isolate z-10 flex shrink-0 items-center gap-2 border-b">
<div className="flex h-(--header-height) w-full items-center gap-2 px-4">
<SidebarTrigger className="-ml-1.5" />
<Separator
orientation="vertical"
className="mr-2 data-[orientation=vertical]:h-4"
/>
<NavHeader />
<div className="ml-auto flex items-center gap-2">
<ModeSwitcher />
<SidebarProvider defaultOpen={defaultOpen}>
<AppSidebar />
<SidebarInset>
<header className="bg-background sticky inset-x-0 top-0 isolate z-10 flex shrink-0 items-center gap-2 border-b">
<div className="flex h-14 w-full items-center gap-2 px-4">
<SidebarTrigger className="-ml-1.5" />
<Separator
orientation="vertical"
className="mr-2 data-[orientation=vertical]:h-4"
/>
<NavHeader />
<div className="ml-auto flex items-center gap-2">
<ModeSwitcher />
</div>
</div>
</div>
</header>
<div className="flex flex-1">
<AppSidebar />
<SidebarInset>{children}</SidebarInset>
</div>
</header>
{children}
</SidebarInset>
</SidebarProvider>
)
}

View File

@@ -0,0 +1,40 @@
import {
Manrope as FontManrope,
Lexend as FontSans,
Newsreader as FontSerif,
} from "next/font/google"
import { cn } from "@/lib/utils"
import { LoginForm } from "@/components/login-form"
const fontSans = FontSans({ subsets: ["latin"], variable: "--font-sans" })
const fontSerif = FontSerif({ subsets: ["latin"], variable: "--font-serif" })
const fontManrope = FontManrope({
subsets: ["latin"],
variable: "--font-manrope",
})
export default function LoginPage() {
return (
<div
className={cn(
"bg-muted flex flex-1 flex-col items-center justify-center gap-16 p-6 md:p-10",
fontSans.variable,
fontSerif.variable,
fontManrope.variable
)}
>
<div className="w-full max-w-sm md:max-w-3xl">
<LoginForm />
</div>
<div className="theme-login-one w-full max-w-sm md:max-w-3xl">
<LoginForm imageUrl="https://images.unsplash.com/photo-1482872376051-5ce74ebf0908?q=80&w=3050&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" />
</div>
<div className="theme-login-two w-full max-w-sm md:max-w-3xl">
<LoginForm imageUrl="https://images.unsplash.com/photo-1498758536662-35b82cd15e29?q=80&w=3088&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" />
</div>
<div className="theme-login-three w-full max-w-sm md:max-w-3xl">
<LoginForm imageUrl="https://images.unsplash.com/photo-1536147116438-62679a5e01f2?q=80&w=2688&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" />
</div>
</div>
)
}

View File

@@ -49,7 +49,10 @@ import { TooltipDemo } from "@/components/tooltip-demo"
export default function SinkPage() {
return (
<div className="grid gap-4 p-4">
<div className="@container grid flex-1 gap-4 p-4">
<ComponentWrapper name="chart" className="w-full">
<ChartDemo />
</ComponentWrapper>
<ComponentWrapper name="accordion">
<AccordionDemo />
</ComponentWrapper>
@@ -80,12 +83,9 @@ export default function SinkPage() {
<ComponentWrapper name="card">
<CardDemo />
</ComponentWrapper>
<ComponentWrapper name="carousel" className="hidden md:flex">
<ComponentWrapper name="carousel">
<CarouselDemo />
</ComponentWrapper>
<ComponentWrapper name="chart" className="w-full">
<ChartDemo />
</ComponentWrapper>
<ComponentWrapper name="checkbox">
<CheckboxDemo />
</ComponentWrapper>

View File

@@ -5,77 +5,132 @@
@custom-variant dark (&:is(.dark *));
:root {
--background: hsl(0 0% 100%);
--foreground: hsl(240 10% 3.9%);
--card: hsl(0 0% 100%);
--card-foreground: hsl(240 10% 3.9%);
--popover: hsl(0 0% 100%);
--popover-foreground: hsl(240 10% 3.9%);
--primary: hsl(240 5.9% 10%);
--primary-foreground: hsl(0 0% 98%);
--secondary: hsl(240 4.8% 95.9%);
--secondary-foreground: hsl(240 5.9% 10%);
--muted: hsl(240 4.8% 95.9%);
--muted-foreground: hsl(240 3.8% 46.1%);
--accent: hsl(240 4.8% 95.9%);
--accent-foreground: hsl(240 5.9% 10%);
--destructive: hsl(0 84.2% 60.2%);
--destructive-foreground: hsl(0 0% 98%);
--border: hsl(240 5.9% 90%);
--input: hsl(240 5.9% 90%);
--ring: hsl(240 10% 3.9%);
--chart-1: hsl(12 76% 61%);
--chart-2: hsl(173 58% 39%);
--chart-3: hsl(197 37% 24%);
--chart-4: hsl(43 74% 66%);
--chart-5: hsl(27 87% 67%);
--radius: 0.6rem;
--sidebar-background: hsl(0 0% 98%);
--sidebar-foreground: hsl(240 5.3% 26.1%);
--sidebar-primary: hsl(240 5.9% 10%);
--sidebar-primary-foreground: hsl(0 0% 98%);
--sidebar-accent: hsl(240 4.8% 95.9%);
--sidebar-accent-foreground: hsl(240 5.9% 10%);
--sidebar-border: hsl(220 13% 91%);
--sidebar-ring: hsl(217.2 91.2% 59.8%);
--background: oklch(1 0 0);
--foreground: oklch(0.145 0 0);
--card: oklch(1 0 0);
--card-foreground: oklch(0.145 0 0);
--popover: oklch(1 0 0);
--popover-foreground: oklch(0.145 0 0);
--primary: oklch(0.205 0 0);
--primary-foreground: oklch(0.985 0 0);
--secondary: oklch(0.97 0 0);
--secondary-foreground: oklch(0.205 0 0);
--muted: oklch(0.97 0 0);
--muted-foreground: oklch(0.556 0 0);
--accent: oklch(0.97 0 0);
--accent-foreground: oklch(0.205 0 0);
--destructive: oklch(0.577 0.245 27.325);
--destructive-foreground: oklch(0.577 0.245 27.325);
--border: oklch(0.922 0 0);
--input: oklch(0.922 0 0);
--ring: oklch(0.708 0 0);
--chart-1: oklch(0.646 0.222 41.116);
--chart-2: oklch(0.6 0.118 184.704);
--chart-3: oklch(0.398 0.07 227.392);
--chart-4: oklch(0.828 0.189 84.429);
--chart-5: oklch(0.769 0.188 70.08);
--radius: 0.625rem;
--sidebar: oklch(0.985 0 0);
--sidebar-foreground: oklch(0.145 0 0);
--sidebar-primary: oklch(0.205 0 0);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.97 0 0);
--sidebar-accent-foreground: oklch(0.205 0 0);
--sidebar-border: oklch(0.922 0 0);
--sidebar-ring: oklch(0.708 0 0);
}
.dark {
--background: hsl(240 10% 3.9%);
--foreground: hsl(0 0% 98%);
--card: hsl(240 10% 3.9%);
--card-foreground: hsl(0 0% 98%);
--popover: hsl(240 10% 3.9%);
--popover-foreground: hsl(0 0% 98%);
--primary: hsl(0 0% 98%);
--primary-foreground: hsl(240 5.9% 10%);
--secondary: hsl(240 3.7% 15.9%);
--secondary-foreground: hsl(0 0% 98%);
--muted: hsl(240 3.7% 15.9%);
--muted-foreground: hsl(240 5% 64.9%);
--accent: hsl(240 3.7% 15.9%);
--accent-foreground: hsl(0 0% 98%);
--destructive: hsl(0 62.8% 30.6%);
--destructive-foreground: hsl(0 0% 98%);
--border: hsl(240 3.7% 15.9%);
--input: hsl(240 3.7% 15.9%);
--ring: hsl(240 4.9% 83.9%);
--chart-1: hsl(220 70% 50%);
--chart-2: hsl(160 60% 45%);
--chart-3: hsl(30 80% 55%);
--chart-4: hsl(280 65% 60%);
--chart-5: hsl(340 75% 55%);
--sidebar-background: hsl(240 5.9% 10%);
--sidebar-foreground: hsl(240 4.8% 95.9%);
--sidebar-primary: hsl(224.3 76.3% 48%);
--sidebar-primary-foreground: hsl(0 0% 100%);
--sidebar-accent: hsl(240 3.7% 15.9%);
--sidebar-accent-foreground: hsl(240 4.8% 95.9%);
--sidebar-border: hsl(240 3.7% 15.9%);
--sidebar-ring: hsl(217.2 91.2% 59.8%);
--background: oklch(0.145 0 0);
--foreground: oklch(0.985 0 0);
--card: oklch(0.145 0 0);
--card-foreground: oklch(0.985 0 0);
--popover: oklch(0.145 0 0);
--popover-foreground: oklch(0.985 0 0);
--primary: oklch(0.985 0 0);
--primary-foreground: oklch(0.205 0 0);
--secondary: oklch(0.269 0 0);
--secondary-foreground: oklch(0.985 0 0);
--muted: oklch(0.269 0 0);
--muted-foreground: oklch(0.708 0 0);
--accent: oklch(0.269 0 0);
--accent-foreground: oklch(0.985 0 0);
--destructive: oklch(0.396 0.141 25.723);
--destructive-foreground: oklch(0.637 0.237 25.331);
--border: oklch(0.269 0 0);
--input: oklch(0.269 0 0);
--ring: oklch(0.556 0 0);
--chart-1: oklch(0.488 0.243 264.376);
--chart-2: oklch(0.696 0.17 162.48);
--chart-3: oklch(0.769 0.188 70.08);
--chart-4: oklch(0.627 0.265 303.9);
--chart-5: oklch(0.645 0.246 16.439);
--sidebar: oklch(0.205 0 0);
--sidebar-foreground: oklch(0.985 0 0);
--sidebar-primary: oklch(0.488 0.243 264.376);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.269 0 0);
--sidebar-accent-foreground: oklch(0.985 0 0);
--sidebar-border: oklch(0.269 0 0);
--sidebar-ring: oklch(0.439 0 0);
}
.theme-login-one {
--primary: #ce2a2d;
--primary-foreground: #fff;
--ring: #ce2a2d9c;
--radius: 0rem;
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
font-family: var(--font-sans);
a {
color: var(--primary);
}
[data-slot="card"] {
border-radius: 0rem;
box-shadow: none;
}
}
.theme-login-two {
--primary: #035fa8;
--primary-foreground: #fff;
--ring: #035fa89c;
font-family: var(--font-serif);
a {
color: var(--primary);
}
}
.theme-login-three {
--primary: #22c55e;
--primary-foreground: #000;
--ring: #22c55e;
--radius: 1.5rem;
font-family: var(--font-manrope);
a {
color: var(--primary);
}
[data-slot="card"] {
@apply shadow-xl;
}
[data-slot="input"] {
@apply dark:bg-input;
}
}
@theme inline {
--font-sans: var(--font-sans);
--font-mono: var(--font-mono);
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-card: var(--card);
@@ -104,14 +159,14 @@
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
--color-sidebar-ring: var(--sidebar-ring);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-accent: var(--sidebar-accent);
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
--color-sidebar-primary: var(--sidebar-primary);
--color-sidebar: var(--sidebar);
--color-sidebar-foreground: var(--sidebar-foreground);
--color-sidebar: var(--sidebar-background);
--color-sidebar-primary: var(--sidebar-primary);
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
--color-sidebar-accent: var(--sidebar-accent);
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-ring: var(--sidebar-ring);
--animate-accordion-down: accordion-down 0.2s ease-out;
--animate-accordion-up: accordion-up 0.2s ease-out;

View File

@@ -1,6 +1,5 @@
import type { Metadata, Viewport } from "next"
import { GeistMono } from "geist/font/mono"
import { GeistSans } from "geist/font/sans"
import { Geist_Mono as FontMono, Inter as FontSans } from "next/font/google"
import { cn } from "@/lib/utils"
import { Analytics } from "@/components/analytics"
@@ -10,9 +9,15 @@ import { siteConfig } from "@/www/config/site"
import "./globals.css"
const fontSans = GeistSans
const fontSans = FontSans({
subsets: ["latin"],
variable: "--font-sans",
})
const fontMono = GeistMono
const fontMono = FontMono({
subsets: ["latin"],
variable: "--font-mono",
})
const META_THEME_COLORS = {
light: "#ffffff",
@@ -97,7 +102,7 @@ export default function RootLayout({
</head>
<body
className={cn(
"bg-background min-h-svh overscroll-none font-sans antialiased",
"bg-background overscroll-none font-sans antialiased",
fontSans.variable,
fontMono.variable
)}

View File

@@ -6,16 +6,16 @@
"tailwind": {
"config": "",
"css": "app/globals.css",
"baseColor": "zinc",
"baseColor": "neutral",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"ui": "@/registry/new-york-v4/ui",
"lib": "@/lib",
"hooks": "@/hooks"
},
"iconLibrary": "lucide"
}
}

View File

@@ -92,7 +92,9 @@ export function AlertDemo() {
</Alert>
<Alert>
<CheckCircle2Icon />
<AlertTitle>The selected emails have been marked as spam.</AlertTitle>
<AlertTitle className="max-w-[calc(100%-4rem)] overflow-ellipsis">
The selected emails have been marked as spam.
</AlertTitle>
<Button
size="sm"
variant="outline"

View File

@@ -158,11 +158,7 @@ const data = {
export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
return (
<Sidebar
collapsible="icon"
className="top-(--delta) h-[calc(100svh-var(--delta))]! [--delta:calc(var(--header-height)+1px)]"
{...props}
>
<Sidebar collapsible="icon" {...props}>
<SidebarHeader>
<TeamSwitcher teams={data.teams} />
<SidebarGroup className="py-0 group-data-[collapsible=icon]:hidden">

View File

@@ -5,20 +5,20 @@ import { AspectRatio } from "@/registry/new-york-v4/ui/aspect-ratio"
export function AspectRatioDemo() {
return (
<div className="grid w-full max-w-sm items-start gap-4">
<AspectRatio ratio={16 / 9} className="bg-muted">
<AspectRatio ratio={16 / 9} className="bg-muted rounded-lg">
<Image
src="https://images.unsplash.com/photo-1588345921523-c2dcdb7f1dcd?w=800&dpr=2&q=80"
alt="Photo by Drew Beamer"
fill
className="h-full w-full rounded-md object-cover dark:brightness-[0.2] dark:grayscale"
className="h-full w-full rounded-lg object-cover dark:brightness-[0.2] dark:grayscale"
/>
</AspectRatio>
<AspectRatio ratio={1 / 1} className="bg-muted">
<AspectRatio ratio={1 / 1} className="bg-muted rounded-lg">
<Image
src="https://images.unsplash.com/photo-1588345921523-c2dcdb7f1dcd?w=800&dpr=2&q=80"
alt="Photo by Drew Beamer"
fill
className="h-full w-full rounded-md object-cover dark:brightness-[0.2] dark:grayscale"
className="h-full w-full rounded-lg object-cover dark:brightness-[0.2] dark:grayscale"
/>
</AspectRatio>
</div>

View File

@@ -6,7 +6,7 @@ import {
export function AvatarDemo() {
return (
<div className="flex flex-col items-center gap-4 md:flex-row">
<div className="flex flex-row flex-wrap items-center gap-4">
<Avatar>
<AvatarImage src="https://github.com/shadcn.png" alt="@shadcn" />
<AvatarFallback>CN</AvatarFallback>

View File

@@ -5,7 +5,7 @@ import { Badge } from "@/registry/new-york-v4/ui/badge"
export function BadgeDemo() {
return (
<div className="flex flex-col items-center gap-2">
<div className="flex w-full flex-col gap-2 md:flex-row">
<div className="flex w-full flex-wrap gap-2">
<Badge>Badge</Badge>
<Badge variant="secondary">Secondary</Badge>
<Badge variant="destructive">Destructive</Badge>
@@ -18,11 +18,23 @@ export function BadgeDemo() {
<AlertCircleIcon />
Alert
</Badge>
<Badge className="size-5 rounded-full p-0 font-mono tabular-nums">
<Badge className="h-5 min-w-5 rounded-full px-1 font-mono tabular-nums">
8
</Badge>
<Badge
className="h-5 min-w-5 rounded-full px-1 font-mono tabular-nums"
variant="destructive"
>
99
</Badge>
<Badge
className="h-5 min-w-5 rounded-full px-1 font-mono tabular-nums"
variant="outline"
>
20+
</Badge>
</div>
<div className="flex w-full flex-col gap-2 md:flex-row">
<div className="flex w-full flex-wrap gap-2">
<Badge asChild>
<a href="#">
Link <ArrowRightIcon />

View File

@@ -5,7 +5,7 @@ import { Button } from "@/registry/new-york-v4/ui/button"
export function ButtonDemo() {
return (
<div className="flex flex-col gap-6">
<div className="flex flex-col items-center gap-2 md:flex-row">
<div className="flex flex-wrap items-center gap-2 md:flex-row">
<Button>Button</Button>
<Button variant="outline">Outline</Button>
<Button variant="ghost">Ghost</Button>
@@ -23,7 +23,7 @@ export function ButtonDemo() {
Please wait
</Button>
</div>
<div className="flex flex-col items-center gap-2 md:flex-row">
<div className="flex flex-wrap items-center gap-2 md:flex-row">
<Button size="sm">Small</Button>
<Button variant="outline" size="sm">
Outline
@@ -51,7 +51,7 @@ export function ButtonDemo() {
Please wait
</Button>
</div>
<div className="flex flex-col flex-wrap items-center gap-2 md:flex-row">
<div className="flex flex-wrap items-center gap-2 md:flex-row">
<Button size="lg">Large</Button>
<Button variant="outline" size="lg">
Outline

View File

@@ -12,9 +12,13 @@ export function CalendarDemo() {
from: new Date(new Date().getFullYear(), 0, 12),
to: addDays(new Date(new Date().getFullYear(), 0, 12), 30),
})
const [range, setRange] = React.useState<DateRange | undefined>({
from: new Date(new Date().getFullYear(), 0, 12),
to: addDays(new Date(new Date().getFullYear(), 0, 12), 50),
})
return (
<div className="flex flex-col items-start gap-2 md:flex-row">
<div className="flex flex-col flex-wrap items-start gap-2 @md:flex-row">
<Calendar
mode="single"
selected={date}
@@ -30,6 +34,14 @@ export function CalendarDemo() {
disabled={(date) => date > new Date() || date < new Date("1900-01-01")}
className="rounded-md border shadow-sm"
/>
<Calendar
mode="range"
defaultMonth={range?.from}
selected={range}
onSelect={setRange}
numberOfMonths={3}
className="hidden rounded-md border shadow-sm @4xl:flex [&>div]:gap-5"
/>
</div>
)
}

View File

@@ -22,8 +22,8 @@ import { Label } from "@/registry/new-york-v4/ui/label"
export function CardDemo() {
return (
<div className="flex flex-col items-start gap-4">
<Card>
<form>
<form>
<Card>
<CardHeader>
<CardTitle>Login to your account</CardTitle>
<CardDescription>
@@ -69,8 +69,8 @@ export function CardDemo() {
</a>
</div>
</CardFooter>
</form>
</Card>
</Card>
</form>
<Card>
<CardHeader>
<CardTitle>Meeting Notes</CardTitle>
@@ -116,7 +116,7 @@ export function CardDemo() {
<CardTitle>Is this an image?</CardTitle>
<CardDescription>This is a card with an image.</CardDescription>
</CardHeader>
<CardContent className="p-0">
<CardContent className="px-0">
<Image
src="https://images.unsplash.com/photo-1588345921523-c2dcdb7f1dcd?w=800&dpr=2&q=80"
alt="Photo by Drew Beamer"
@@ -125,7 +125,7 @@ export function CardDemo() {
height={500}
/>
</CardContent>
<CardFooter className="flex items-center gap-2 p-6">
<CardFooter className="flex items-center gap-2">
<Badge variant="outline">
<BedIcon /> 4
</Badge>
@@ -138,6 +138,54 @@ export function CardDemo() {
<div className="ml-auto font-medium tabular-nums">$135,000</div>
</CardFooter>
</Card>
<div className="flex w-full flex-wrap items-start gap-8 md:*:data-[slot=card]:basis-1/4">
<Card>
<CardContent className="text-sm">Content Only</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Header Only</CardTitle>
<CardDescription>
This is a card with a header and a description.
</CardDescription>
</CardHeader>
</Card>
<Card>
<CardHeader>
<CardTitle>Header and Content</CardTitle>
<CardDescription>
This is a card with a header and a content.
</CardDescription>
</CardHeader>
<CardContent className="text-sm">Content</CardContent>
</Card>
<Card>
<CardFooter className="text-sm">Footer Only</CardFooter>
</Card>
<Card>
<CardHeader>
<CardTitle>Header + Footer</CardTitle>
<CardDescription>
This is a card with a header and a footer.
</CardDescription>
</CardHeader>
<CardFooter className="text-sm">Footer</CardFooter>
</Card>
<Card>
<CardContent className="text-sm">Content</CardContent>
<CardFooter className="text-sm">Footer</CardFooter>
</Card>
<Card>
<CardHeader>
<CardTitle>Header + Footer</CardTitle>
<CardDescription>
This is a card with a header and a footer.
</CardDescription>
</CardHeader>
<CardContent className="text-sm">Content</CardContent>
<CardFooter className="text-sm">Footer</CardFooter>
</Card>
</div>
</div>
)
}

View File

@@ -11,7 +11,7 @@ import {
export function CarouselDemo() {
return (
<div className="w-full flex-col items-center gap-4 md:flex">
<div className="hidden w-full flex-col items-center gap-4 @4xl:flex">
<Carousel className="max-w-sm *:data-[slot=carousel-next]:hidden *:data-[slot=carousel-previous]:hidden *:data-[slot=carousel-next]:md:inline-flex *:data-[slot=carousel-previous]:md:inline-flex">
<CarouselContent>
{Array.from({ length: 5 }).map((_, index) => (
@@ -29,7 +29,6 @@ export function CarouselDemo() {
<CarouselPrevious />
<CarouselNext />
</Carousel>
<Carousel
className="max-w-sm *:data-[slot=carousel-next]:hidden *:data-[slot=carousel-previous]:hidden *:data-[slot=carousel-next]:md:inline-flex *:data-[slot=carousel-previous]:md:inline-flex"
opts={{

View File

@@ -0,0 +1,103 @@
"use client"
import { TrendingUp } from "lucide-react"
import { Bar, BarChart, XAxis, YAxis } from "recharts"
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/registry/new-york-v4/ui/card"
import {
ChartConfig,
ChartContainer,
ChartTooltip,
ChartTooltipContent,
} from "@/registry/new-york-v4/ui/chart"
export const description = "A mixed bar chart"
const chartData = [
{ browser: "chrome", visitors: 275, fill: "var(--color-chrome)" },
{ browser: "safari", visitors: 200, fill: "var(--color-safari)" },
{ browser: "firefox", visitors: 187, fill: "var(--color-firefox)" },
{ browser: "edge", visitors: 173, fill: "var(--color-edge)" },
{ browser: "other", visitors: 90, fill: "var(--color-other)" },
]
const chartConfig = {
visitors: {
label: "Visitors",
},
chrome: {
label: "Chrome",
color: "var(--chart-1)",
},
safari: {
label: "Safari",
color: "var(--chart-2)",
},
firefox: {
label: "Firefox",
color: "var(--chart-3)",
},
edge: {
label: "Edge",
color: "var(--chart-4)",
},
other: {
label: "Other",
color: "var(--chart-5)",
},
} satisfies ChartConfig
export function ChartBarMixed() {
return (
<Card>
<CardHeader>
<CardTitle>Bar Chart - Mixed</CardTitle>
<CardDescription>January - June 2024</CardDescription>
</CardHeader>
<CardContent>
<ChartContainer config={chartConfig}>
<BarChart
accessibilityLayer
data={chartData}
layout="vertical"
margin={{
left: 0,
}}
>
<YAxis
dataKey="browser"
type="category"
tickLine={false}
tickMargin={10}
axisLine={false}
tickFormatter={(value) =>
chartConfig[value as keyof typeof chartConfig]?.label
}
/>
<XAxis dataKey="visitors" type="number" hide />
<ChartTooltip
cursor={false}
content={<ChartTooltipContent hideLabel />}
/>
<Bar dataKey="visitors" layout="vertical" radius={5} />
</BarChart>
</ChartContainer>
</CardContent>
<CardFooter className="flex-col items-start gap-2 text-sm">
<div className="flex gap-2 leading-none font-medium">
Trending up by 5.2% this month <TrendingUp className="h-4 w-4" />
</div>
<div className="text-muted-foreground leading-none">
Showing total visitors for the last 6 months
</div>
</CardFooter>
</Card>
)
}

View File

@@ -1,13 +1,17 @@
import { ChartAreaDemo } from "@/components/chart-area-demo"
import { ChartBarDemo } from "@/components/chart-bar-demo"
import { ChartLineDemo } from "@/components/chart-line-demo"
import { ChartBarMixed } from "@/registry/new-york-v4/charts/chart-bar-mixed"
export function ChartDemo() {
return (
<div className="flex w-full max-w-screen-xl flex-col flex-wrap gap-4 *:data-[slot=card]:flex-1 md:flex-row">
<div className="grid w-full max-w-screen-2xl gap-4 *:data-[slot=card]:flex-1 @2xl:grid-cols-2 @6xl:grid-cols-3">
<ChartAreaDemo />
<ChartBarDemo />
<ChartLineDemo />
<ChartBarMixed />
<div className="@6xl:hidden">
<ChartLineDemo />
</div>
</div>
)
}

View File

@@ -112,7 +112,7 @@ type Timezone = (typeof timezones)[number]
export function ComboboxDemo() {
return (
<div className="flex w-full flex-col items-start gap-4 md:flex-row">
<div className="flex w-full flex-wrap items-start gap-4">
<FrameworkCombobox frameworks={[...frameworks]} />
<UserCombobox users={[...users]} selectedUserId={users[0].id} />
<TimezoneCombobox

View File

@@ -36,7 +36,7 @@ function DatePickerSimple() {
!date && "text-muted-foreground"
)}
>
<CalendarIcon />
<CalendarIcon className="text-muted-foreground" />
{date ? format(date, "PPP") : <span>Pick a date</span>}
</Button>
</PopoverTrigger>
@@ -69,7 +69,7 @@ function DatePickerWithRange() {
!date && "text-muted-foreground"
)}
>
<CalendarIcon />
<CalendarIcon className="text-muted-foreground" />
{date?.from ? (
date.to ? (
<>

View File

@@ -60,7 +60,7 @@ const data = [
export function DrawerDemo() {
return (
<div className="flex flex-col items-start gap-4 md:flex-row md:items-center">
<div className="flex flex-wrap items-start gap-4">
<DrawerBottom />
<DrawerScrollableContent />
<DrawerDirections />

View File

@@ -43,7 +43,7 @@ import {
export function DropdownMenuDemo() {
return (
<div className="flex flex-col items-center gap-4 md:flex-row">
<div className="flex flex-wrap items-start gap-4">
<DropdownMenuSimple />
<DropdownMenuCheckboxes />
<DropdownMenuRadioGroupDemo />

View File

@@ -1,6 +1,5 @@
"use client"
import Link from "next/link"
import { zodResolver } from "@hookform/resolvers/zod"
import { format } from "date-fns"
import { CalendarIcon } from "lucide-react"
@@ -159,8 +158,7 @@ export function FormDemo() {
</SelectContent>
</Select>
<FormDescription>
You can manage email addresses in your{" "}
<Link href="/examples/forms">email settings</Link>.
You can manage email addresses in your email settings.
</FormDescription>
<FormMessage />
</FormItem>
@@ -190,15 +188,15 @@ export function FormDemo() {
control={form.control}
name="type"
render={({ field }) => (
<FormItem className="space-y-3">
<FormItem className="flex flex-col gap-3">
<FormLabel>Notify me about...</FormLabel>
<FormControl>
<RadioGroup
onValueChange={field.onChange}
defaultValue={field.value}
className="flex flex-col space-y-1"
className="flex flex-col gap-3"
>
<FormItem className="flex items-center space-y-0 space-x-3">
<FormItem className="flex items-center gap-2">
<FormControl>
<RadioGroupItem value="all" />
</FormControl>
@@ -206,7 +204,7 @@ export function FormDemo() {
All new messages
</FormLabel>
</FormItem>
<FormItem className="flex items-center space-y-0 space-x-3">
<FormItem className="flex items-center gap-2">
<FormControl>
<RadioGroupItem value="mentions" />
</FormControl>
@@ -214,7 +212,7 @@ export function FormDemo() {
Direct messages and mentions
</FormLabel>
</FormItem>
<FormItem className="flex items-center space-y-0 space-x-3">
<FormItem className="flex items-center gap-2">
<FormControl>
<RadioGroupItem value="none" />
</FormControl>
@@ -230,20 +228,20 @@ export function FormDemo() {
control={form.control}
name="mobile"
render={({ field }) => (
<FormItem className="flex flex-row items-start space-y-0 space-x-3 rounded-md border p-4 shadow">
<FormItem className="flex flex-row items-start gap-3 rounded-md border p-4 shadow-xs">
<FormControl>
<Checkbox
checked={field.value}
onCheckedChange={field.onChange}
/>
</FormControl>
<div className="space-y-1 leading-none">
<FormLabel>
<div className="flex flex-col gap-1">
<FormLabel className="leading-snug">
Use different settings for my mobile devices
</FormLabel>
<FormDescription>
You can manage your mobile notifications in the{" "}
<Link href="/examples/forms">mobile settings</Link> page.
<FormDescription className="leading-snug">
You can manage your mobile notifications in the mobile
settings page.
</FormDescription>
</div>
</FormItem>
@@ -253,46 +251,48 @@ export function FormDemo() {
control={form.control}
name="items"
render={() => (
<FormItem>
<div className="mb-4">
<FormItem className="flex flex-col gap-4">
<div>
<FormLabel className="text-base">Sidebar</FormLabel>
<FormDescription>
Select the items you want to display in the sidebar.
</FormDescription>
</div>
{items.map((item) => (
<FormField
key={item.id}
control={form.control}
name="items"
render={({ field }) => {
return (
<FormItem
key={item.id}
className="flex flex-row items-start space-y-0 space-x-3"
>
<FormControl>
<Checkbox
checked={field.value?.includes(item.id)}
onCheckedChange={(checked) => {
return checked
? field.onChange([...field.value, item.id])
: field.onChange(
field.value?.filter(
(value) => value !== item.id
<div className="flex flex-col gap-2">
{items.map((item) => (
<FormField
key={item.id}
control={form.control}
name="items"
render={({ field }) => {
return (
<FormItem
key={item.id}
className="flex items-start gap-3"
>
<FormControl>
<Checkbox
checked={field.value?.includes(item.id)}
onCheckedChange={(checked) => {
return checked
? field.onChange([...field.value, item.id])
: field.onChange(
field.value?.filter(
(value) => value !== item.id
)
)
)
}}
/>
</FormControl>
<FormLabel className="text-sm font-normal">
{item.label}
</FormLabel>
</FormItem>
)
}}
/>
))}
}}
/>
</FormControl>
<FormLabel className="text-sm leading-tight font-normal">
{item.label}
</FormLabel>
</FormItem>
)
}}
/>
))}
</div>
<FormMessage />
</FormItem>
)}
@@ -343,15 +343,17 @@ export function FormDemo() {
/>
<div>
<h3 className="mb-4 text-lg font-medium">Email Notifications</h3>
<div className="space-y-4">
<div className="flex flex-col gap-4">
<FormField
control={form.control}
name="marketing_emails"
render={({ field }) => (
<FormItem className="flex flex-row items-center justify-between rounded-lg border p-3 shadow-sm">
<div className="space-y-0.5">
<FormLabel>Marketing emails</FormLabel>
<FormDescription>
<FormItem className="flex flex-row items-start justify-between rounded-lg border p-4 shadow-xs">
<div className="flex flex-col gap-0.5">
<FormLabel className="leading-normal">
Marketing emails
</FormLabel>
<FormDescription className="leading-snug">
Receive emails about new products, features, and more.
</FormDescription>
</div>
@@ -368,10 +370,12 @@ export function FormDemo() {
control={form.control}
name="security_emails"
render={({ field }) => (
<FormItem className="flex flex-row items-center justify-between rounded-lg border p-3 shadow-sm">
<div className="space-y-0.5">
<FormLabel>Security emails</FormLabel>
<FormDescription>
<FormItem className="flex flex-row items-start justify-between rounded-lg border p-4 shadow-xs">
<div className="flex flex-col gap-0.5 opacity-60">
<FormLabel className="leading-normal">
Security emails
</FormLabel>
<FormDescription className="leading-snug">
Receive emails about your account security.
</FormDescription>
</div>

View File

@@ -0,0 +1,231 @@
"use client"
import * as React from "react"
import { useTheme } from "next-themes"
import { Button } from "@/registry/new-york-v4/ui/button"
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/registry/new-york-v4/ui/card"
import { Checkbox } from "@/registry/new-york-v4/ui/checkbox"
import { Input } from "@/registry/new-york-v4/ui/input"
import { Label } from "@/registry/new-york-v4/ui/label"
import {
RadioGroup,
RadioGroupItem,
} from "@/registry/new-york-v4/ui/radio-group"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/registry/new-york-v4/ui/select"
import { Textarea } from "@/registry/new-york-v4/ui/textarea"
const plans = [
{
id: "starter",
name: "Starter Plan",
description: "Perfect for small businesses.",
price: "$10",
},
{
id: "pro",
name: "Pro Plan",
description: "Advanced features with more storage.",
price: "$20",
},
] as const
const themes = {
stone: {
light: {
"--primary": "oklch(0.216 0.006 56.043)", // --color-stone-900
"--primary-foreground": "oklch(0.985 0.001 106.423)", // --color-stone-50
"--accent": "oklch(0.97 0.001 106.424)", // --color-stone-100
"--ring": "oklch(0.869 0.005 56.366)", // --color-stone-300
},
dark: {
"--primary": "oklch(0.985 0.001 106.423)", // --color-stone-50
"--primary-foreground": "oklch(0.216 0.006 56.043)", // --color-stone-900
"--accent": "oklch(0.268 0.007 34.298)", // --color-stone-800
"--accent-foreground": "oklch(0.985 0.001 106.423)", // --color-stone-50
"--ring": "oklch(0.553 0.013 58.071)", // --color-stone-500
},
},
blue: {
light: {
"--primary": "oklch(0.546 0.245 262.881)", // --color-blue-600
"--primary-foreground": "oklch(0.985 0.001 106.423)", // --color-blue-50
"--ring": "oklch(0.707 0.165 254.624)", // --color-blue-400
},
dark: {
"--primary": "oklch(0.546 0.245 262.881)", // --color-blue-600
"--primary-foreground": "oklch(0.985 0.001 106.423)", // --color-blue-50
"--ring": "oklch(0.379 0.146 265.522)", // --color-blue-400
},
},
amber: {
light: {
"--primary": "oklch(0.769 0.188 70.08)", // --color-blue-600
"--primary-foreground": "oklch(0.985 0.001 106.423)", // --color-blue-50
"--ring": "oklch(0.82 0.13 92.25)", // --color-blue-400
},
dark: {
"--primary": "oklch(0.985 0.001 106.423)", // --color-stone-50
"--primary-foreground": "oklch(0.216 0.006 56.043)", // --color-stone-900
"--ring": "oklch(0.553 0.013 58.071)", // --color-stone-500
},
},
teal: {
light: {
"--primary": "oklch(0.627 0.194 149.214)", // --color-blue-600
"--primary-foreground": "oklch(0.985 0.001 106.423)", // --color-blue-50
"--ring": "oklch(0.79 0.19 153.13)", // --color-blue-400
},
dark: {
"--primary": "oklch(0.985 0.001 106.423)", // --color-stone-50
"--primary-foreground": "oklch(0.216 0.006 56.043)", // --color-stone-900
"--ring": "oklch(0.553 0.013 58.071)", // --color-stone-500
},
},
} as const
export function FormsDemo() {
const { theme: mode = "light" } = useTheme()
const [theme, setTheme] = React.useState<keyof typeof themes | undefined>(
undefined
)
const themeStyles = React.useMemo(() => {
if (!theme) return undefined
return themes[theme][mode as keyof (typeof themes)[typeof theme]]
}, [theme, mode])
return (
<div className="flex max-w-md flex-col gap-4">
<Card style={themeStyles as React.CSSProperties}>
<CardHeader>
<CardTitle className="text-lg">Upgrade your subscription</CardTitle>
<CardDescription>
You are currently on the free plan. Upgrade to the pro plan to get
access to all features.
</CardDescription>
</CardHeader>
<CardContent>
<div className="flex flex-col gap-6">
<div className="flex flex-col gap-3 md:flex-row">
<div className="flex flex-col gap-2">
<Label htmlFor="name">Name</Label>
<Input id="name" placeholder="Evil Rabbit" />
</div>
<div className="flex flex-col gap-2">
<Label htmlFor="email">Email</Label>
<Input id="email" placeholder="example@acme.com" />
</div>
</div>
<div className="flex flex-col gap-2">
<Label htmlFor="card-number">Card Number</Label>
<div className="grid grid-cols-2 gap-3 md:grid-cols-[1fr_80px_60px]">
<Input
id="card-number"
placeholder="1234 1234 1234 1234"
className="col-span-2 md:col-span-1"
/>
<Input id="card-number-expiry" placeholder="MM/YY" />
<Input id="card-number-cvc" placeholder="CVC" />
</div>
</div>
<div className="flex flex-col gap-2">
<Label htmlFor="color">Color</Label>
<Select
onValueChange={(value) =>
setTheme(value as keyof typeof themes)
}
>
<SelectTrigger id="color" className="w-full">
<SelectValue placeholder="Select a color" />
</SelectTrigger>
<SelectContent>
{Object.keys(themes).map((theme) => (
<SelectItem key={theme} value={theme}>
<div
className="size-3.5 rounded-full"
style={{
backgroundColor:
themes[theme as keyof typeof themes]["light"][
"--primary"
],
}}
/>
{theme}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<fieldset className="flex flex-col gap-3">
<legend className="text-sm font-medium">Plan</legend>
<p className="text-muted-foreground text-sm">
Select the plan that best fits your needs.
</p>
<RadioGroup
defaultValue="starter"
className="grid gap-3 md:grid-cols-2"
>
{plans.map((plan) => (
<Label
className="has-[[data-state=checked]]:border-ring has-[[data-state=checked]]:bg-ring/10 flex items-start gap-3 rounded-lg border p-3"
key={plan.id}
>
<RadioGroupItem
value={plan.id}
id={plan.name}
className="data-[state=checked]:border-primary"
/>
<div className="grid gap-1 font-normal">
<div className="font-medium">{plan.name}</div>
<div className="text-muted-foreground text-xs leading-snug">
{plan.description}
</div>
</div>
</Label>
))}
</RadioGroup>
</fieldset>
<div className="flex flex-col gap-2">
<Label htmlFor="notes">Notes</Label>
<Textarea id="notes" placeholder="Enter notes" />
</div>
<div className="flex flex-col gap-3">
<div className="flex items-center gap-2">
<Checkbox id="terms" />
<Label htmlFor="terms" className="font-normal">
I agree to the terms and conditions
</Label>
</div>
<div className="flex items-center gap-2">
<Checkbox id="newsletter" defaultChecked />
<Label htmlFor="newsletter" className="font-normal">
Allow us to send you emails
</Label>
</div>
</div>
</div>
</CardContent>
<CardFooter className="flex justify-between">
<Button variant="outline" size="sm">
Cancel
</Button>
<Button size="sm">Upgrade Plan</Button>
</CardFooter>
</Card>
</div>
)
}

View File

@@ -98,10 +98,10 @@ function InputOTPWithSpacing() {
<Label htmlFor="with-spacing">With Spacing</Label>
<InputOTP id="with-spacing" maxLength={6}>
<InputOTPGroup className="gap-2 *:data-[slot=input-otp-slot]:rounded-md *:data-[slot=input-otp-slot]:border">
<InputOTPSlot index={0} />
<InputOTPSlot index={1} />
<InputOTPSlot index={2} />
<InputOTPSlot index={3} />
<InputOTPSlot index={0} aria-invalid="true" />
<InputOTPSlot index={1} aria-invalid="true" />
<InputOTPSlot index={2} aria-invalid="true" />
<InputOTPSlot index={3} aria-invalid="true" />
</InputOTPGroup>
</InputOTP>
</div>

View File

@@ -0,0 +1,112 @@
import Image from "next/image"
import { cn } from "@/lib/utils"
import { Button } from "@/registry/new-york-v4/ui/button"
import { Card, CardContent } from "@/registry/new-york-v4/ui/card"
import { Input } from "@/registry/new-york-v4/ui/input"
import { Label } from "@/registry/new-york-v4/ui/label"
export function LoginForm({
className,
imageUrl,
...props
}: React.ComponentProps<"div"> & {
imageUrl?: string
}) {
return (
<div className={cn("flex flex-col gap-6", className)} {...props}>
<Card className="overflow-hidden p-0">
<CardContent className="grid p-0 md:grid-cols-2">
<form className="p-6 md:p-8">
<div className="flex flex-col gap-6">
<div className="flex flex-col items-center text-center">
<h1 className="text-2xl font-bold">Welcome back</h1>
<p className="text-muted-foreground text-balance">
Login to your Acme Inc account
</p>
</div>
<div className="grid gap-3">
<Label htmlFor="email">Email</Label>
<Input
id="email"
type="email"
placeholder="m@example.com"
required
/>
</div>
<div className="grid gap-3">
<div className="flex items-center">
<Label htmlFor="password">Password</Label>
<a
href="#"
className="ml-auto text-sm underline-offset-2 hover:underline"
>
Forgot your password?
</a>
</div>
<Input id="password" type="password" required />
</div>
<Button type="submit" className="w-full">
Login
</Button>
<div className="after:border-border relative text-center text-sm after:absolute after:inset-0 after:top-1/2 after:z-0 after:flex after:items-center after:border-t">
<span className="bg-background text-muted-foreground relative z-10 px-2">
Or continue with
</span>
</div>
<div className="grid grid-cols-3 gap-4">
<Button variant="outline" type="button" className="w-full">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path
d="M12.152 6.896c-.948 0-2.415-1.078-3.96-1.04-2.04.027-3.91 1.183-4.961 3.014-2.117 3.675-.546 9.103 1.519 12.09 1.013 1.454 2.208 3.09 3.792 3.039 1.52-.065 2.09-.987 3.935-.987 1.831 0 2.35.987 3.96.948 1.637-.026 2.676-1.48 3.676-2.948 1.156-1.688 1.636-3.325 1.662-3.415-.039-.013-3.182-1.221-3.22-4.857-.026-3.04 2.48-4.494 2.597-4.559-1.429-2.09-3.623-2.324-4.39-2.376-2-.156-3.675 1.09-4.61 1.09zM15.53 3.83c.843-1.012 1.4-2.427 1.245-3.83-1.207.052-2.662.805-3.532 1.818-.78.896-1.454 2.338-1.273 3.714 1.338.104 2.715-.688 3.559-1.701"
fill="currentColor"
/>
</svg>
<span className="sr-only">Login with Apple</span>
</Button>
<Button variant="outline" type="button" className="w-full">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path
d="M12.48 10.92v3.28h7.84c-.24 1.84-.853 3.187-1.787 4.133-1.147 1.147-2.933 2.4-6.053 2.4-4.827 0-8.6-3.893-8.6-8.72s3.773-8.72 8.6-8.72c2.6 0 4.507 1.027 5.907 2.347l2.307-2.307C18.747 1.44 16.133 0 12.48 0 5.867 0 .307 5.387.307 12s5.56 12 12.173 12c3.573 0 6.267-1.173 8.373-3.36 2.16-2.16 2.84-5.213 2.84-7.667 0-.76-.053-1.467-.173-2.053H12.48z"
fill="currentColor"
/>
</svg>
<span className="sr-only">Login with Google</span>
</Button>
<Button variant="outline" type="button" className="w-full">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path
d="M6.915 4.03c-1.968 0-3.683 1.28-4.871 3.113C.704 9.208 0 11.883 0 14.449c0 .706.07 1.369.21 1.973a6.624 6.624 0 0 0 .265.86 5.297 5.297 0 0 0 .371.761c.696 1.159 1.818 1.927 3.593 1.927 1.497 0 2.633-.671 3.965-2.444.76-1.012 1.144-1.626 2.663-4.32l.756-1.339.186-.325c.061.1.121.196.183.3l2.152 3.595c.724 1.21 1.665 2.556 2.47 3.314 1.046.987 1.992 1.22 3.06 1.22 1.075 0 1.876-.355 2.455-.843a3.743 3.743 0 0 0 .81-.973c.542-.939.861-2.127.861-3.745 0-2.72-.681-5.357-2.084-7.45-1.282-1.912-2.957-2.93-4.716-2.93-1.047 0-2.088.467-3.053 1.308-.652.57-1.257 1.29-1.82 2.05-.69-.875-1.335-1.547-1.958-2.056-1.182-.966-2.315-1.303-3.454-1.303zm10.16 2.053c1.147 0 2.188.758 2.992 1.999 1.132 1.748 1.647 4.195 1.647 6.4 0 1.548-.368 2.9-1.839 2.9-.58 0-1.027-.23-1.664-1.004-.496-.601-1.343-1.878-2.832-4.358l-.617-1.028a44.908 44.908 0 0 0-1.255-1.98c.07-.109.141-.224.211-.327 1.12-1.667 2.118-2.602 3.358-2.602zm-10.201.553c1.265 0 2.058.791 2.675 1.446.307.327.737.871 1.234 1.579l-1.02 1.566c-.757 1.163-1.882 3.017-2.837 4.338-1.191 1.649-1.81 1.817-2.486 1.817-.524 0-1.038-.237-1.383-.794-.263-.426-.464-1.13-.464-2.046 0-2.221.63-4.535 1.66-6.088.454-.687.964-1.226 1.533-1.533a2.264 2.264 0 0 1 1.088-.285z"
fill="currentColor"
/>
</svg>
<span className="sr-only">Login with Meta</span>
</Button>
</div>
<div className="text-center text-sm">
Don&apos;t have an account?{" "}
<a href="#" className="underline underline-offset-4">
Sign up
</a>
</div>
</div>
</form>
<div className="bg-primary/50 relative hidden md:block">
{imageUrl && (
<Image
fill
src={imageUrl}
alt="Image"
className="absolute inset-0 h-full w-full object-cover"
/>
)}
</div>
</CardContent>
</Card>
<div className="text-muted-foreground *:[a]:hover:text-primary text-center text-xs text-balance *:[a]:underline *:[a]:underline-offset-4">
By clicking continue, you agree to our <a href="#">Terms of Service</a>{" "}
and <a href="#">Privacy Policy</a>.
</div>
</div>
)
}

View File

@@ -26,6 +26,11 @@ export function NavHeader() {
<Link href="/charts">Charts</Link>
</NavigationMenuLink>
</NavigationMenuItem>
<NavigationMenuItem>
<NavigationMenuLink asChild data-active={pathname === "/forms"}>
<Link href="/forms">Forms</Link>
</NavigationMenuLink>
</NavigationMenuItem>
</NavigationMenuList>
</NavigationMenu>
)

View File

@@ -2,6 +2,7 @@
import * as React from "react"
import Link from "next/link"
import { CircleCheckIcon, CircleHelpIcon, CircleIcon } from "lucide-react"
import {
NavigationMenu,
@@ -53,7 +54,7 @@ const components: { title: string; href: string; description: string }[] = [
export function NavigationMenuDemo() {
return (
<div className="hidden w-full flex-col items-center justify-center gap-6 md:flex">
<div className="hidden w-full flex-col items-center justify-center gap-6 @xl:flex">
<NavigationMenu>
<NavigationMenuList>
<NavigationMenuItem>
@@ -174,6 +175,33 @@ export function NavigationMenuDemo() {
</ul>
</NavigationMenuContent>
</NavigationMenuItem>
<NavigationMenuItem>
<NavigationMenuTrigger>With Icon</NavigationMenuTrigger>
<NavigationMenuContent>
<ul className="grid w-[200px] gap-4">
<li>
<NavigationMenuLink asChild>
<Link href="#" className="flex-row items-center gap-2">
<CircleHelpIcon />
Backlog
</Link>
</NavigationMenuLink>
<NavigationMenuLink asChild>
<Link href="#" className="flex-row items-center gap-2">
<CircleIcon />
To Do
</Link>
</NavigationMenuLink>
<NavigationMenuLink asChild>
<Link href="#" className="flex-row items-center gap-2">
<CircleCheckIcon />
Done
</Link>
</NavigationMenuLink>
</li>
</ul>
</NavigationMenuContent>
</NavigationMenuItem>
</NavigationMenuList>
</NavigationMenu>
</div>

View File

@@ -40,7 +40,7 @@ export function RadioGroupDemo() {
<RadioGroup defaultValue="starter" className="max-w-sm">
{plans.map((plan) => (
<Label
className="hover:bg-accent/50 flex items-start gap-3 rounded-lg border p-3 has-[[data-state=checked]]:border-green-600 has-[[data-state=checked]]:bg-green-50 dark:has-[[data-state=checked]]:border-green-900 dark:has-[[data-state=checked]]:bg-green-950"
className="hover:bg-accent/50 flex items-start gap-3 rounded-lg border p-4 has-[[data-state=checked]]:border-green-600 has-[[data-state=checked]]:bg-green-50 dark:has-[[data-state=checked]]:border-green-900 dark:has-[[data-state=checked]]:bg-green-950"
key={plan.id}
>
<RadioGroupItem
@@ -48,7 +48,7 @@ export function RadioGroupDemo() {
id={plan.name}
className="shadow-none data-[state=checked]:border-green-600 data-[state=checked]:bg-green-600 *:data-[slot=radio-group-indicator]:[&>svg]:fill-white *:data-[slot=radio-group-indicator]:[&>svg]:stroke-white"
/>
<div className="grid gap-1.5 font-normal">
<div className="grid gap-1 font-normal">
<div className="font-medium">{plan.name}</div>
<div className="text-muted-foreground leading-snug">
{plan.description}

View File

@@ -52,8 +52,8 @@ export const works = [
function ScrollAreaHorizontalDemo() {
return (
<ScrollArea className="w-full max-w-96 rounded-md border">
<div className="flex gap-4 p-4">
<ScrollArea className="w-full max-w-96 rounded-md border p-4">
<div className="flex gap-4">
{works.map((artwork) => (
<figure key={artwork.artist} className="shrink-0">
<div className="overflow-hidden rounded-md">

View File

@@ -18,7 +18,7 @@ import {
export function SelectDemo() {
return (
<div className="flex flex-col gap-4">
<div className="flex flex-wrap items-start gap-4">
<Select>
<SelectTrigger className="w-[180px]">
<SelectValue placeholder="Select a fruit" />

View File

@@ -3,7 +3,7 @@ import { Skeleton } from "@/registry/new-york-v4/ui/skeleton"
export function SkeletonDemo() {
return (
<div className="flex w-full max-w-3xl flex-col gap-6">
<div className="flex w-full flex-wrap items-start gap-4">
<div className="flex items-center gap-4">
<Skeleton className="size-10 shrink-0 rounded-full" />
<div className="grid gap-2">
@@ -11,9 +11,9 @@ export function SkeletonDemo() {
<Skeleton className="h-4 w-[100px]" />
</div>
</div>
<div className="flex w-full flex-col gap-4 md:flex-row">
<div className="flex w-full flex-wrap items-start gap-4">
{Array.from({ length: 3 }).map((_, index) => (
<Card key={index} className="w-full">
<Card key={index} className="w-full @md:w-auto @md:min-w-sm">
<CardHeader>
<Skeleton className="h-4 w-2/3" />
<Skeleton className="h-4 w-1/2" />

View File

@@ -31,6 +31,10 @@ export function TeamSwitcher({
const { isMobile } = useSidebar()
const [activeTeam, setActiveTeam] = React.useState(teams[0])
if (!activeTeam) {
return null
}
return (
<SidebarMenu>
<SidebarMenuItem>

View File

@@ -7,7 +7,7 @@ import {
export function ToggleGroupDemo() {
return (
<div className="flex flex-col items-center gap-6 md:flex-row">
<div className="flex flex-wrap items-start gap-4">
<ToggleGroup type="multiple">
<ToggleGroupItem value="bold" aria-label="Toggle bold">
<BoldIcon />

View File

@@ -9,7 +9,7 @@ import {
export function TooltipDemo() {
return (
<div className="flex flex-col gap-6 md:flex-row">
<div className="flex flex-wrap items-start gap-4">
<Tooltip>
<TooltipTrigger asChild>
<Button variant="outline">Hover</Button>

View File

@@ -48,7 +48,7 @@
"@radix-ui/react-tooltip": "^1.1.7",
"@tailwindcss/postcss": "^4.0.1",
"@vercel/analytics": "^1.2.2",
"class-variance-authority": "^0.7.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"cmdk": "^1.0.4",
"date-fns": "^4.1.0",
@@ -68,10 +68,10 @@
"recharts": "2.15.1",
"rimraf": "^6.0.1",
"shadcn": "2.3.0",
"sonner": "^1.7.4",
"sonner": "^2.0.0",
"tailwind-merge": "^3.0.1",
"tailwindcss": "^4.0.1",
"tailwindcss-animate": "^1.0.6",
"tailwindcss": "^4.0.7",
"tailwindcss-animate": "^1.0.7",
"ts-morph": "^22.0.0",
"vaul": "1.1.2",
"zod": "^3.21.4"

View File

@@ -997,7 +997,8 @@
"collapsible",
"dropdown-menu",
"avatar",
"switch"
"switch",
"label"
],
"files": [
{
@@ -1265,7 +1266,8 @@
"collapsible",
"dropdown-menu",
"avatar",
"button"
"button",
"label"
],
"files": [
{
@@ -2721,6 +2723,32 @@
"type": "registry:hook"
}
]
},
{
"name": "products-01",
"type": "registry:block",
"description": "A table of products",
"registryDependencies": [
"checkbox",
"badge",
"button",
"dropdown-menu",
"pagination",
"table",
"tabs",
"select"
],
"files": [
{
"path": "registry/new-york-v4/blocks/products-01/page.tsx",
"type": "registry:page",
"target": "app/products/page.tsx"
},
{
"path": "registry/new-york-v4/blocks/products-01/components/products-table.tsx",
"type": "registry:component"
}
]
}
]
}

View File

@@ -10,7 +10,7 @@ export function LoginForm({
}: React.ComponentProps<"div">) {
return (
<div className={cn("flex flex-col gap-6", className)} {...props}>
<Card className="overflow-hidden">
<Card className="overflow-hidden p-0">
<CardContent className="grid p-0 md:grid-cols-2">
<form className="p-6 md:p-8">
<div className="flex flex-col gap-6">

View File

@@ -0,0 +1,209 @@
import {
ArrowUpDownIcon,
EllipsisVerticalIcon,
ListFilterIcon,
PlusIcon,
} from "lucide-react"
import { Badge } from "@/registry/new-york-v4/ui/badge"
import { Button } from "@/registry/new-york-v4/ui/button"
import { Checkbox } from "@/registry/new-york-v4/ui/checkbox"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/registry/new-york-v4/ui/dropdown-menu"
import {
Pagination,
PaginationContent,
PaginationEllipsis,
PaginationItem,
PaginationLink,
PaginationNext,
PaginationPrevious,
} from "@/registry/new-york-v4/ui/pagination"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/registry/new-york-v4/ui/select"
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/registry/new-york-v4/ui/table"
import { Tabs, TabsList, TabsTrigger } from "@/registry/new-york-v4/ui/tabs"
export function ProductsTable({
products,
}: {
products: {
id: string
name: string
price: number
stock: number
dateAdded: string
status: string
}[]
}) {
return (
<div className="flex w-full flex-col gap-4">
<div className="flex items-center justify-between gap-4">
<Tabs defaultValue="all">
<TabsList>
<TabsTrigger value="all">All Products</TabsTrigger>
<TabsTrigger value="in-stock">In Stock</TabsTrigger>
<TabsTrigger value="low-stock">Low Stock</TabsTrigger>
<TabsTrigger value="archived">Archived</TabsTrigger>
<TabsTrigger value="add-product" asChild>
<button>
<PlusIcon />
</button>
</TabsTrigger>
</TabsList>
</Tabs>
<div className="flex items-center gap-2 **:data-[slot=button]:size-8 **:data-[slot=select-trigger]:h-8">
<Select defaultValue="all">
<SelectTrigger>
<span className="text-muted-foreground text-sm">Category:</span>
<SelectValue placeholder="Select a product" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">All</SelectItem>
<SelectItem value="in-stock">In Stock</SelectItem>
<SelectItem value="low-stock">Low Stock</SelectItem>
<SelectItem value="archived">Archived</SelectItem>
</SelectContent>
</Select>
<Select defaultValue="all">
<SelectTrigger>
<span className="text-muted-foreground text-sm">Price:</span>
<SelectValue placeholder="Select a product" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">$100-$200</SelectItem>
<SelectItem value="in-stock">$200-$300</SelectItem>
<SelectItem value="low-stock">$300-$400</SelectItem>
<SelectItem value="archived">$400-$500</SelectItem>
</SelectContent>
</Select>
<Select defaultValue="all">
<SelectTrigger>
<span className="text-muted-foreground text-sm">Status:</span>
<SelectValue placeholder="Select a product" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">In Stock</SelectItem>
<SelectItem value="in-stock">Low Stock</SelectItem>
<SelectItem value="low-stock">Archived</SelectItem>
<SelectItem value="archived">Archived</SelectItem>
</SelectContent>
</Select>
<Button variant="outline" size="icon">
<ListFilterIcon />
</Button>
<Button variant="outline" size="icon">
<ArrowUpDownIcon />
</Button>
</div>
</div>
<div className="rounded-lg">
<Table>
<TableHeader className="bg-muted/50">
<TableRow className="!border-0">
<TableHead className="w-12 rounded-l-lg px-4">
<Checkbox />
</TableHead>
<TableHead>Product</TableHead>
<TableHead className="text-right">Price</TableHead>
<TableHead className="text-right">Stock</TableHead>
<TableHead>Status</TableHead>
<TableHead>Date Added</TableHead>
<TableHead className="rounded-r-lg" />
</TableRow>
</TableHeader>
<TableBody className="**:data-[slot=table-cell]:py-2.5">
{products.map((product) => (
<TableRow key={product.id}>
<TableCell className="px-4">
<Checkbox />
</TableCell>
<TableCell className="font-medium">{product.name}</TableCell>
<TableCell className="text-right">
${product.price.toFixed(2)}
</TableCell>
<TableCell className="text-right">{product.stock}</TableCell>
<TableCell>
<Badge
variant="secondary"
className={
product.status === "Low Stock"
? "border-orange-500 bg-transparent text-orange-500 dark:border-orange-500 dark:bg-transparent dark:text-orange-500"
: "bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-100"
}
>
{product.status}
</Badge>
</TableCell>
<TableCell>
{new Date(product.dateAdded).toLocaleDateString("en-US", {
month: "long",
day: "numeric",
year: "numeric",
})}
</TableCell>
<TableCell>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" size="icon" className="size-6">
<EllipsisVerticalIcon />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem>Edit</DropdownMenuItem>
<DropdownMenuItem variant="destructive">
Delete
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
<div className="flex justify-end">
<Pagination>
<PaginationContent>
<PaginationItem>
<PaginationPrevious href="#" />
</PaginationItem>
<PaginationItem>
<PaginationLink href="#">1</PaginationLink>
</PaginationItem>
<PaginationItem>
<PaginationLink href="#" isActive>
2
</PaginationLink>
</PaginationItem>
<PaginationItem>
<PaginationLink href="#">3</PaginationLink>
</PaginationItem>
<PaginationItem>
<PaginationEllipsis />
</PaginationItem>
<PaginationItem>
<PaginationNext href="#" />
</PaginationItem>
</PaginationContent>
</Pagination>
</div>
</div>
)
}

View File

@@ -0,0 +1,173 @@
import { ProductsTable } from "@/registry/new-york-v4/blocks/products-01/components/products-table"
// Load from database.
const products = [
{
id: "1",
name: "BJÖRKSNÄS Dining Table",
price: 599.99,
stock: 12,
dateAdded: "2023-06-15",
status: "In Stock",
},
{
id: "2",
name: "POÄNG Armchair",
price: 249.99,
stock: 28,
dateAdded: "2023-07-22",
status: "In Stock",
},
{
id: "3",
name: "MALM Bed Frame",
price: 399.99,
stock: 15,
dateAdded: "2023-08-05",
status: "In Stock",
},
{
id: "4",
name: "KALLAX Shelf Unit",
price: 179.99,
stock: 32,
dateAdded: "2023-09-12",
status: "In Stock",
},
{
id: "5",
name: "STOCKHOLM Rug",
price: 299.99,
stock: 8,
dateAdded: "2023-10-18",
status: "Low Stock",
},
{
id: "6",
name: "KIVIK Sofa",
price: 899.99,
stock: 6,
dateAdded: "2023-11-02",
status: "Low Stock",
},
{
id: "7",
name: "LISABO Coffee Table",
price: 149.99,
stock: 22,
dateAdded: "2023-11-29",
status: "In Stock",
},
{
id: "8",
name: "HEMNES Bookcase",
price: 249.99,
stock: 17,
dateAdded: "2023-12-10",
status: "In Stock",
},
{
id: "9",
name: "EKEDALEN Dining Chairs (Set of 2)",
price: 199.99,
stock: 14,
dateAdded: "2024-01-05",
status: "In Stock",
},
{
id: "10",
name: "FRIHETEN Sleeper Sofa",
price: 799.99,
stock: 9,
dateAdded: "2024-01-18",
status: "Low Stock",
},
{
id: "11",
name: "NORDEN Extendable Table",
price: 499.99,
stock: 11,
dateAdded: "2024-01-25",
status: "In Stock",
},
{
id: "12",
name: "BILLY Bookcase",
price: 129.99,
stock: 42,
dateAdded: "2024-02-03",
status: "In Stock",
},
{
id: "13",
name: "STRANDMON Wing Chair",
price: 349.99,
stock: 16,
dateAdded: "2024-02-12",
status: "In Stock",
},
{
id: "14",
name: "MALM Dresser",
price: 279.99,
stock: 19,
dateAdded: "2024-02-27",
status: "In Stock",
},
{
id: "15",
name: "BRIMNES TV Unit",
price: 149.99,
stock: 23,
dateAdded: "2024-03-08",
status: "In Stock",
},
{
id: "16",
name: "SÖDERHAMN Sectional Sofa",
price: 1299.99,
stock: 5,
dateAdded: "2024-03-15",
status: "Low Stock",
},
{
id: "17",
name: "BEKANT Desk",
price: 249.99,
stock: 18,
dateAdded: "2024-03-22",
status: "In Stock",
},
{
id: "18",
name: "IVAR Storage System",
price: 199.99,
stock: 14,
dateAdded: "2024-04-01",
status: "In Stock",
},
{
id: "19",
name: "RIBBA Picture Frame Set",
price: 49.99,
stock: 36,
dateAdded: "2024-04-09",
status: "In Stock",
},
{
id: "20",
name: "EKTORP Loveseat",
price: 499.99,
stock: 12,
dateAdded: "2024-04-15",
status: "In Stock",
},
]
export default function ProductsPage() {
return (
<div className="flex h-full flex-1 flex-col gap-4 rounded-xl p-4">
<ProductsTable products={products} />
</div>
)
}

View File

@@ -37,7 +37,7 @@ export function VersionSwitcher({
<GalleryVerticalEnd className="size-4" />
</div>
<div className="flex flex-col gap-0.5 leading-none">
<span className="font-semibold">Documentation</span>
<span className="font-medium">Documentation</span>
<span className="">v{selectedVersion}</span>
</div>
<ChevronsUpDown className="ml-auto" />

View File

@@ -37,7 +37,7 @@ export function VersionSwitcher({
<GalleryVerticalEnd className="size-4" />
</div>
<div className="flex flex-col gap-0.5 leading-none">
<span className="font-semibold">Documentation</span>
<span className="font-medium">Documentation</span>
<span className="">v{selectedVersion}</span>
</div>
<ChevronsUpDown className="ml-auto" />

View File

@@ -168,7 +168,7 @@ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
<GalleryVerticalEnd className="size-4" />
</div>
<div className="flex flex-col gap-0.5 leading-none">
<span className="font-semibold">Documentation</span>
<span className="font-medium">Documentation</span>
<span className="">v1.0.0</span>
</div>
</a>

View File

@@ -167,7 +167,7 @@ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
<GalleryVerticalEnd className="size-4" />
</div>
<div className="flex flex-col gap-0.5 leading-none">
<span className="font-semibold">Documentation</span>
<span className="font-medium">Documentation</span>
<span className="">v1.0.0</span>
</div>
</a>

View File

@@ -174,7 +174,7 @@ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
<GalleryVerticalEnd className="size-4" />
</div>
<div className="flex flex-col gap-0.5 leading-none">
<span className="font-semibold">Documentation</span>
<span className="font-medium">Documentation</span>
<span className="">v1.0.0</span>
</div>
</a>

View File

@@ -157,7 +157,7 @@ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
<GalleryVerticalEnd className="size-4" />
</div>
<div className="flex flex-col gap-0.5 leading-none">
<span className="font-semibold">Documentation</span>
<span className="font-medium">Documentation</span>
<span className="">v1.0.0</span>
</div>
</a>

View File

@@ -10,24 +10,26 @@ import { SidebarInput } from "@/registry/new-york-v4/ui/sidebar"
export function SidebarOptInForm() {
return (
<Card className="shadow-none">
<form>
<CardHeader className="p-4 pb-0">
<CardTitle className="text-sm">Subscribe to our newsletter</CardTitle>
<CardDescription>
Opt-in to receive updates and news about the sidebar.
</CardDescription>
</CardHeader>
<CardContent className="grid gap-2.5 p-4">
<SidebarInput type="email" placeholder="Email" />
<Button
className="bg-sidebar-primary text-sidebar-primary-foreground w-full shadow-none"
size="sm"
>
Subscribe
</Button>
</CardContent>
</form>
<Card className="gap-2 py-4 shadow-none">
<CardHeader className="px-4">
<CardTitle className="text-sm">Subscribe to our newsletter</CardTitle>
<CardDescription>
Opt-in to receive updates and news about the sidebar.
</CardDescription>
</CardHeader>
<CardContent className="px-4">
<form>
<div className="grid gap-2.5">
<SidebarInput type="email" placeholder="Email" />
<Button
className="bg-sidebar-primary text-sidebar-primary-foreground w-full shadow-none"
size="sm"
>
Subscribe
</Button>
</div>
</form>
</CardContent>
</Card>
)
}

View File

@@ -55,7 +55,7 @@ export function NavUser({
<AvatarFallback className="rounded-lg">CN</AvatarFallback>
</Avatar>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-semibold">{user.name}</span>
<span className="truncate font-medium">{user.name}</span>
<span className="truncate text-xs">{user.email}</span>
</div>
<ChevronsUpDown className="ml-auto size-4" />
@@ -74,7 +74,7 @@ export function NavUser({
<AvatarFallback className="rounded-lg">CN</AvatarFallback>
</Avatar>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-semibold">{user.name}</span>
<span className="truncate font-medium">{user.name}</span>
<span className="truncate text-xs">{user.email}</span>
</div>
</div>

View File

@@ -31,6 +31,10 @@ export function TeamSwitcher({
const { isMobile } = useSidebar()
const [activeTeam, setActiveTeam] = React.useState(teams[0])
if (!activeTeam) {
return null
}
return (
<SidebarMenu>
<SidebarMenuItem>
@@ -44,9 +48,7 @@ export function TeamSwitcher({
<activeTeam.logo className="size-4" />
</div>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-semibold">
{activeTeam.name}
</span>
<span className="truncate font-medium">{activeTeam.name}</span>
<span className="truncate text-xs">{activeTeam.plan}</span>
</div>
<ChevronsUpDown className="ml-auto" />

View File

@@ -164,7 +164,7 @@ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
<Command className="size-4" />
</div>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-semibold">Acme Inc</span>
<span className="truncate font-medium">Acme Inc</span>
<span className="truncate text-xs">Enterprise</span>
</div>
</a>

View File

@@ -55,7 +55,7 @@ export function NavUser({
<AvatarFallback className="rounded-lg">CN</AvatarFallback>
</Avatar>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-semibold">{user.name}</span>
<span className="truncate font-medium">{user.name}</span>
<span className="truncate text-xs">{user.email}</span>
</div>
<ChevronsUpDown className="ml-auto size-4" />
@@ -74,7 +74,7 @@ export function NavUser({
<AvatarFallback className="rounded-lg">CN</AvatarFallback>
</Avatar>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-semibold">{user.name}</span>
<span className="truncate font-medium">{user.name}</span>
<span className="truncate text-xs">{user.email}</span>
</div>
</div>

View File

@@ -172,7 +172,7 @@ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
<Command className="size-4" />
</div>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-semibold">Acme Inc</span>
<span className="truncate font-medium">Acme Inc</span>
<span className="truncate text-xs">Enterprise</span>
</div>
</a>
@@ -202,7 +202,7 @@ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
)
setOpen(true)
}}
isActive={activeItem.title === item.title}
isActive={activeItem?.title === item.title}
className="px-2.5 md:px-2"
>
<item.icon />
@@ -225,7 +225,7 @@ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
<SidebarHeader className="gap-3.5 border-b p-4">
<div className="flex w-full items-center justify-between">
<div className="text-foreground text-base font-medium">
{activeItem.title}
{activeItem?.title}
</div>
<Label className="flex items-center gap-2 text-sm">
<span>Unreads</span>

View File

@@ -55,7 +55,7 @@ export function NavUser({
<AvatarFallback className="rounded-lg">CN</AvatarFallback>
</Avatar>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-semibold">{user.name}</span>
<span className="truncate font-medium">{user.name}</span>
<span className="truncate text-xs">{user.email}</span>
</div>
<ChevronsUpDown className="ml-auto size-4" />
@@ -74,7 +74,7 @@ export function NavUser({
<AvatarFallback className="rounded-lg">CN</AvatarFallback>
</Avatar>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-semibold">{user.name}</span>
<span className="truncate font-medium">{user.name}</span>
<span className="truncate text-xs">{user.email}</span>
</div>
</div>

View File

@@ -29,6 +29,10 @@ export function TeamSwitcher({
}) {
const [activeTeam, setActiveTeam] = React.useState(teams[0])
if (!activeTeam) {
return null
}
return (
<SidebarMenu>
<SidebarMenuItem>
@@ -38,7 +42,7 @@ export function TeamSwitcher({
<div className="bg-sidebar-primary text-sidebar-primary-foreground flex aspect-square size-5 items-center justify-center rounded-md">
<activeTeam.logo className="size-3" />
</div>
<span className="truncate font-semibold">{activeTeam.name}</span>
<span className="truncate font-medium">{activeTeam.name}</span>
<ChevronDown className="opacity-50" />
</SidebarMenuButton>
</DropdownMenuTrigger>

View File

@@ -55,7 +55,7 @@ export function NavUser({
<AvatarFallback className="rounded-lg">CN</AvatarFallback>
</Avatar>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-semibold">{user.name}</span>
<span className="truncate font-medium">{user.name}</span>
<span className="truncate text-xs">{user.email}</span>
</div>
<ChevronsUpDown className="ml-auto size-4" />
@@ -74,7 +74,7 @@ export function NavUser({
<AvatarFallback className="rounded-lg">CN</AvatarFallback>
</Avatar>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-semibold">{user.name}</span>
<span className="truncate font-medium">{user.name}</span>
<span className="truncate text-xs">{user.email}</span>
</div>
</div>

View File

@@ -55,7 +55,7 @@ export function NavUser({
<AvatarFallback className="rounded-lg">CN</AvatarFallback>
</Avatar>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-semibold">{user.name}</span>
<span className="truncate font-medium">{user.name}</span>
<span className="truncate text-xs">{user.email}</span>
</div>
<ChevronsUpDown className="ml-auto size-4" />
@@ -74,7 +74,7 @@ export function NavUser({
<AvatarFallback className="rounded-lg">CN</AvatarFallback>
</Avatar>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-semibold">{user.name}</span>
<span className="truncate font-medium">{user.name}</span>
<span className="truncate text-xs">{user.email}</span>
</div>
</div>

View File

@@ -29,6 +29,10 @@ export function TeamSwitcher({
}) {
const [activeTeam, setActiveTeam] = React.useState(teams[0])
if (!activeTeam) {
return null
}
return (
<SidebarMenu>
<SidebarMenuItem>
@@ -38,7 +42,7 @@ export function TeamSwitcher({
<div className="bg-sidebar-primary text-sidebar-primary-foreground flex aspect-square size-5 items-center justify-center rounded-md">
<activeTeam.logo className="size-3" />
</div>
<span className="truncate font-semibold">{activeTeam.name}</span>
<span className="truncate font-medium">{activeTeam.name}</span>
<ChevronDown className="opacity-50" />
</SidebarMenuButton>
</DropdownMenuTrigger>

View File

@@ -167,7 +167,7 @@ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
<Command className="size-4" />
</div>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-semibold">Acme Inc</span>
<span className="truncate font-medium">Acme Inc</span>
<span className="truncate text-xs">Enterprise</span>
</div>
</a>

View File

@@ -55,7 +55,7 @@ export function NavUser({
<AvatarFallback className="rounded-lg">CN</AvatarFallback>
</Avatar>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-semibold">{user.name}</span>
<span className="truncate font-medium">{user.name}</span>
<span className="truncate text-xs">{user.email}</span>
</div>
<ChevronsUpDown className="ml-auto size-4" />
@@ -74,7 +74,7 @@ export function NavUser({
<AvatarFallback className="rounded-lg">CN</AvatarFallback>
</Avatar>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-semibold">{user.name}</span>
<span className="truncate font-medium">{user.name}</span>
<span className="truncate text-xs">{user.email}</span>
</div>
</div>

View File

@@ -35,7 +35,7 @@ function AccordionTrigger({
<AccordionPrimitive.Trigger
data-slot="accordion-trigger"
className={cn(
"ring-ring/10 dark:ring-ring/20 dark:outline-ring/40 outline-ring/50 flex flex-1 items-start justify-between gap-4 rounded-md py-4 text-left text-sm font-medium transition-all hover:underline focus-visible:ring-4 focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 [&[data-state=open]>svg]:rotate-180",
"focus-visible:border-ring focus-visible:ring-ring/50 flex flex-1 items-start justify-between gap-4 rounded-md py-4 text-left text-sm font-medium transition-all outline-none hover:underline focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&[data-state=open]>svg]:rotate-180",
className
)}
{...props}

View File

@@ -10,7 +10,7 @@ const alertVariants = cva(
variant: {
default: "bg-background text-foreground",
destructive:
"border-destructive/50 text-destructive dark:text-destructive-foreground/80 dark:border-destructive [&>svg]:text-current dark:bg-destructive/50",
"text-destructive-foreground [&>svg]:text-current *:data-[slot=alert-description]:text-destructive-foreground/80",
},
},
defaultVariants: {
@@ -55,7 +55,7 @@ function AlertDescription({
<div
data-slot="alert-description"
className={cn(
"col-start-2 grid justify-items-start gap-1 text-sm [&_p]:leading-relaxed",
"text-muted-foreground col-start-2 grid justify-items-start gap-1 text-sm [&_p]:leading-relaxed",
className
)}
{...props}

View File

@@ -5,16 +5,16 @@ import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const badgeVariants = cva(
"inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-semibold w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none ring-ring/10 dark:ring-ring/20 dark:outline-ring/40 outline-ring/50 focus-visible:ring-4 focus-visible:outline-1 aria-invalid:focus-visible:ring-0 transition-[color,box-shadow]",
"inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden",
{
variants: {
variant: {
default:
"border-transparent bg-primary text-primary-foreground shadow-sm [a&]:hover:bg-primary/90",
"border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
secondary:
"border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
destructive:
"border-transparent bg-destructive text-destructive-foreground shadow-sm [a&]:hover:bg-destructive/90",
"border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40",
outline:
"text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
},

View File

@@ -5,14 +5,14 @@ import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-[color,box-shadow] disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0 ring-ring/10 dark:ring-ring/20 dark:outline-ring/40 outline-ring/50 focus-visible:ring-4 focus-visible:outline-1 aria-invalid:focus-visible:ring-0",
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-[color,box-shadow] disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
{
variants: {
variant: {
default:
"bg-primary text-primary-foreground shadow-sm hover:bg-primary/90",
"bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
destructive:
"bg-destructive text-destructive-foreground shadow-xs hover:bg-destructive/90",
"bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40",
outline:
"border border-input bg-background shadow-xs hover:bg-accent hover:text-accent-foreground",
secondary:
@@ -22,7 +22,7 @@ const buttonVariants = cva(
},
size: {
default: "h-9 px-4 py-2 has-[>svg]:px-3",
sm: "h-8 rounded-md px-3 has-[>svg]:px-2.5",
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
icon: "size-9",
},

View File

@@ -35,7 +35,7 @@ function Calendar({
"text-muted-foreground rounded-md w-8 font-normal text-[0.8rem]",
row: "flex w-full mt-2",
cell: cn(
"relative p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([aria-selected])]:bg-accent [&:has([aria-selected].day-outside)]:bg-accent/50 [&:has([aria-selected].day-range-end)]:rounded-r-md",
"relative p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([aria-selected])]:bg-accent [&:has([aria-selected].day-range-end)]:rounded-r-md",
props.mode === "range"
? "[&:has(>.day-range-end)]:rounded-r-md [&:has(>.day-range-start)]:rounded-l-md first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md"
: "[&:has([aria-selected])]:rounded-md"
@@ -44,13 +44,15 @@ function Calendar({
buttonVariants({ variant: "ghost" }),
"size-8 p-0 font-normal aria-selected:opacity-100"
),
day_range_start: "day-range-start",
day_range_end: "day-range-end",
day_range_start:
"day-range-start aria-selected:bg-primary aria-selected:text-primary-foreground",
day_range_end:
"day-range-end aria-selected:bg-primary aria-selected:text-primary-foreground",
day_selected:
"bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground",
day_today: "bg-accent text-accent-foreground",
day_outside:
"day-outside text-muted-foreground aria-selected:bg-accent/50 aria-selected:text-muted-foreground",
"day-outside text-muted-foreground aria-selected:text-muted-foreground",
day_disabled: "text-muted-foreground opacity-50",
day_range_middle:
"aria-selected:bg-accent aria-selected:text-accent-foreground",

View File

@@ -7,7 +7,7 @@ function Card({ className, ...props }: React.ComponentProps<"div">) {
<div
data-slot="card"
className={cn(
"bg-card text-card-foreground rounded-xl border shadow-sm",
"bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",
className
)}
{...props}
@@ -19,7 +19,7 @@ function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-header"
className={cn("flex flex-col gap-1.5 p-6", className)}
className={cn("flex flex-col gap-1.5 px-6", className)}
{...props}
/>
)
@@ -29,7 +29,7 @@ function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-title"
className={cn("leading-none font-semibold tracking-tight", className)}
className={cn("leading-none font-semibold", className)}
{...props}
/>
)
@@ -49,7 +49,7 @@ function CardContent({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-content"
className={cn("p-6 pt-0", className)}
className={cn("px-6", className)}
{...props}
/>
)
@@ -59,7 +59,7 @@ function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-footer"
className={cn("flex items-center p-6 pt-0", className)}
className={cn("flex items-center px-6", className)}
{...props}
/>
)

View File

@@ -34,15 +34,18 @@ function useChart() {
return context
}
const ChartContainer = React.forwardRef<
HTMLDivElement,
React.ComponentProps<"div"> & {
config: ChartConfig
children: React.ComponentProps<
typeof RechartsPrimitive.ResponsiveContainer
>["children"]
}
>(({ id, className, children, config, ...props }, ref) => {
function ChartContainer({
id,
className,
children,
config,
...props
}: React.ComponentProps<"div"> & {
config: ChartConfig
children: React.ComponentProps<
typeof RechartsPrimitive.ResponsiveContainer
>["children"]
}) {
const uniqueId = React.useId()
const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`
@@ -51,7 +54,6 @@ const ChartContainer = React.forwardRef<
<div
data-slot="chart"
data-chart={chartId}
ref={ref}
className={cn(
"[&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border flex aspect-video justify-center text-xs [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-hidden [&_.recharts-sector]:outline-hidden [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-surface]:outline-hidden",
className
@@ -65,8 +67,7 @@ const ChartContainer = React.forwardRef<
</div>
</ChartContext.Provider>
)
})
ChartContainer.displayName = "Chart"
}
const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
const colorConfig = Object.entries(config).filter(
@@ -103,219 +104,205 @@ ${colorConfig
const ChartTooltip = RechartsPrimitive.Tooltip
const ChartTooltipContent = React.forwardRef<
HTMLDivElement,
React.ComponentProps<typeof RechartsPrimitive.Tooltip> &
React.ComponentProps<"div"> & {
hideLabel?: boolean
hideIndicator?: boolean
indicator?: "line" | "dot" | "dashed"
nameKey?: string
labelKey?: string
}
>(
(
{
active,
payload,
className,
indicator = "dot",
hideLabel = false,
hideIndicator = false,
label,
labelFormatter,
labelClassName,
formatter,
color,
nameKey,
labelKey,
},
ref
) => {
const { config } = useChart()
function ChartTooltipContent({
active,
payload,
className,
indicator = "dot",
hideLabel = false,
hideIndicator = false,
label,
labelFormatter,
labelClassName,
formatter,
color,
nameKey,
labelKey,
}: React.ComponentProps<typeof RechartsPrimitive.Tooltip> &
React.ComponentProps<"div"> & {
hideLabel?: boolean
hideIndicator?: boolean
indicator?: "line" | "dot" | "dashed"
nameKey?: string
labelKey?: string
}) {
const { config } = useChart()
const tooltipLabel = React.useMemo(() => {
if (hideLabel || !payload?.length) {
return null
}
const [item] = payload
const key = `${labelKey || item.dataKey || item.name || "value"}`
const itemConfig = getPayloadConfigFromPayload(config, item, key)
const value =
!labelKey && typeof label === "string"
? config[label as keyof typeof config]?.label || label
: itemConfig?.label
if (labelFormatter) {
return (
<div className={cn("font-medium", labelClassName)}>
{labelFormatter(value, payload)}
</div>
)
}
if (!value) {
return null
}
return <div className={cn("font-medium", labelClassName)}>{value}</div>
}, [
label,
labelFormatter,
payload,
hideLabel,
labelClassName,
config,
labelKey,
])
if (!active || !payload?.length) {
const tooltipLabel = React.useMemo(() => {
if (hideLabel || !payload?.length) {
return null
}
const nestLabel = payload.length === 1 && indicator !== "dot"
const [item] = payload
const key = `${labelKey || item?.dataKey || item?.name || "value"}`
const itemConfig = getPayloadConfigFromPayload(config, item, key)
const value =
!labelKey && typeof label === "string"
? config[label as keyof typeof config]?.label || label
: itemConfig?.label
return (
<div
ref={ref}
className={cn(
"border-border/50 bg-background grid min-w-[8rem] items-start gap-1.5 rounded-lg border px-2.5 py-1.5 text-xs shadow-xl",
className
)}
>
{!nestLabel ? tooltipLabel : null}
<div className="grid gap-1.5">
{payload.map((item, index) => {
const key = `${nameKey || item.name || item.dataKey || "value"}`
const itemConfig = getPayloadConfigFromPayload(config, item, key)
const indicatorColor = color || item.payload.fill || item.color
return (
<div
key={item.dataKey}
className={cn(
"[&>svg]:text-muted-foreground flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5",
indicator === "dot" && "items-center"
)}
>
{formatter && item?.value !== undefined && item.name ? (
formatter(item.value, item.name, item, index, item.payload)
) : (
<>
{itemConfig?.icon ? (
<itemConfig.icon />
) : (
!hideIndicator && (
<div
className={cn(
"shrink-0 rounded-[2px] border-(--color-border) bg-(--color-bg)",
{
"h-2.5 w-2.5": indicator === "dot",
"w-1": indicator === "line",
"w-0 border-[1.5px] border-dashed bg-transparent":
indicator === "dashed",
"my-0.5": nestLabel && indicator === "dashed",
}
)}
style={
{
"--color-bg": indicatorColor,
"--color-border": indicatorColor,
} as React.CSSProperties
}
/>
)
)}
<div
className={cn(
"flex flex-1 justify-between leading-none",
nestLabel ? "items-end" : "items-center"
)}
>
<div className="grid gap-1.5">
{nestLabel ? tooltipLabel : null}
<span className="text-muted-foreground">
{itemConfig?.label || item.name}
</span>
</div>
{item.value && (
<span className="text-foreground font-mono font-medium tabular-nums">
{item.value.toLocaleString()}
</span>
)}
</div>
</>
)}
</div>
)
})}
if (labelFormatter) {
return (
<div className={cn("font-medium", labelClassName)}>
{labelFormatter(value, payload)}
</div>
</div>
)
}
)
ChartTooltipContent.displayName = "ChartTooltip"
const ChartLegend = RechartsPrimitive.Legend
const ChartLegendContent = React.forwardRef<
HTMLDivElement,
React.ComponentProps<"div"> &
Pick<RechartsPrimitive.LegendProps, "payload" | "verticalAlign"> & {
hideIcon?: boolean
nameKey?: string
)
}
>(
(
{ className, hideIcon = false, payload, verticalAlign = "bottom", nameKey },
ref
) => {
const { config } = useChart()
if (!payload?.length) {
if (!value) {
return null
}
return (
<div
ref={ref}
className={cn(
"flex items-center justify-center gap-4",
verticalAlign === "top" ? "pb-3" : "pt-3",
className
)}
>
{payload.map((item) => {
const key = `${nameKey || item.dataKey || "value"}`
return <div className={cn("font-medium", labelClassName)}>{value}</div>
}, [
label,
labelFormatter,
payload,
hideLabel,
labelClassName,
config,
labelKey,
])
if (!active || !payload?.length) {
return null
}
const nestLabel = payload.length === 1 && indicator !== "dot"
return (
<div
className={cn(
"border-border/50 bg-background grid min-w-[8rem] items-start gap-1.5 rounded-lg border px-2.5 py-1.5 text-xs shadow-xl",
className
)}
>
{!nestLabel ? tooltipLabel : null}
<div className="grid gap-1.5">
{payload.map((item, index) => {
const key = `${nameKey || item.name || item.dataKey || "value"}`
const itemConfig = getPayloadConfigFromPayload(config, item, key)
const indicatorColor = color || item.payload.fill || item.color
return (
<div
key={item.value}
key={item.dataKey}
className={cn(
"[&>svg]:text-muted-foreground flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3"
"[&>svg]:text-muted-foreground flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5",
indicator === "dot" && "items-center"
)}
>
{itemConfig?.icon && !hideIcon ? (
<itemConfig.icon />
{formatter && item?.value !== undefined && item.name ? (
formatter(item.value, item.name, item, index, item.payload)
) : (
<div
className="h-2 w-2 shrink-0 rounded-[2px]"
style={{
backgroundColor: item.color,
}}
/>
<>
{itemConfig?.icon ? (
<itemConfig.icon />
) : (
!hideIndicator && (
<div
className={cn(
"shrink-0 rounded-[2px] border-(--color-border) bg-(--color-bg)",
{
"h-2.5 w-2.5": indicator === "dot",
"w-1": indicator === "line",
"w-0 border-[1.5px] border-dashed bg-transparent":
indicator === "dashed",
"my-0.5": nestLabel && indicator === "dashed",
}
)}
style={
{
"--color-bg": indicatorColor,
"--color-border": indicatorColor,
} as React.CSSProperties
}
/>
)
)}
<div
className={cn(
"flex flex-1 justify-between leading-none",
nestLabel ? "items-end" : "items-center"
)}
>
<div className="grid gap-1.5">
{nestLabel ? tooltipLabel : null}
<span className="text-muted-foreground">
{itemConfig?.label || item.name}
</span>
</div>
{item.value && (
<span className="text-foreground font-mono font-medium tabular-nums">
{item.value.toLocaleString()}
</span>
)}
</div>
</>
)}
{itemConfig?.label}
</div>
)
})}
</div>
)
</div>
)
}
const ChartLegend = RechartsPrimitive.Legend
function ChartLegendContent({
className,
hideIcon = false,
payload,
verticalAlign = "bottom",
nameKey,
}: React.ComponentProps<"div"> &
Pick<RechartsPrimitive.LegendProps, "payload" | "verticalAlign"> & {
hideIcon?: boolean
nameKey?: string
}) {
const { config } = useChart()
if (!payload?.length) {
return null
}
)
ChartLegendContent.displayName = "ChartLegend"
return (
<div
className={cn(
"flex items-center justify-center gap-4",
verticalAlign === "top" ? "pb-3" : "pt-3",
className
)}
>
{payload.map((item) => {
const key = `${nameKey || item.dataKey || "value"}`
const itemConfig = getPayloadConfigFromPayload(config, item, key)
return (
<div
key={item.value}
className={cn(
"[&>svg]:text-muted-foreground flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3"
)}
>
{itemConfig?.icon && !hideIcon ? (
<itemConfig.icon />
) : (
<div
className="h-2 w-2 shrink-0 rounded-[2px]"
style={{
backgroundColor: item.color,
}}
/>
)}
{itemConfig?.label}
</div>
)
})}
</div>
)
}
// Helper to extract item config from a payload.
function getPayloadConfigFromPayload(

View File

@@ -14,14 +14,14 @@ function Checkbox({
<CheckboxPrimitive.Root
data-slot="checkbox"
className={cn(
"peer border-input data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground data-[state=checked]:border-primary ring-ring/10 dark:ring-ring/20 dark:outline-ring/40 outline-ring/50 size-4 shrink-0 rounded-[4px] border shadow-xs transition-[color,box-shadow] focus-visible:ring-4 focus-visible:outline-1 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:focus-visible:ring-0",
"peer border-input data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
className
)}
{...props}
>
<CheckboxPrimitive.Indicator
data-slot="checkbox-indicator"
className="flex items-center justify-center text-current"
className="flex items-center justify-center text-current transition-none"
>
<CheckIcon className="size-3.5" />
</CheckboxPrimitive.Indicator>

View File

@@ -140,7 +140,7 @@ function CommandItem({
<CommandPrimitive.Item
data-slot="command-item"
className={cn(
"data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
"data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className
)}
{...props}

View File

@@ -126,7 +126,7 @@ function ContextMenuItem({
data-inset={inset}
data-variant={variant}
className={cn(
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive-foreground data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/40 data-[variant=destructive]:focus:text-destructive-foreground data-[variant=destructive]:*:[svg]:!text-destructive-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className
)}
{...props}
@@ -196,7 +196,7 @@ function ContextMenuLabel({
data-slot="context-menu-label"
data-inset={inset}
className={cn(
"text-foreground px-2 py-1.5 text-sm font-semibold data-[inset]:pl-8",
"text-foreground px-2 py-1.5 text-sm font-medium data-[inset]:pl-8",
className
)}
{...props}

View File

@@ -102,10 +102,7 @@ function DialogTitle({
return (
<DialogPrimitive.Title
data-slot="dialog-title"
className={cn(
"text-lg leading-none font-semibold tracking-tight",
className
)}
className={cn("text-lg leading-none font-semibold", className)}
{...props}
/>
)

View File

@@ -99,7 +99,7 @@ function DrawerTitle({
return (
<DrawerPrimitive.Title
data-slot="drawer-title"
className={cn("text-foreground font-semibold tracking-tight", className)}
className={cn("text-foreground font-semibold", className)}
{...props}
/>
)

View File

@@ -74,7 +74,7 @@ function DropdownMenuItem({
data-inset={inset}
data-variant={variant}
className={cn(
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive-foreground data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/40 data-[variant=destructive]:focus:text-destructive-foreground data-[variant=destructive]:*:[svg]:!text-destructive-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className
)}
{...props}
@@ -155,7 +155,7 @@ function DropdownMenuLabel({
data-slot="dropdown-menu-label"
data-inset={inset}
className={cn(
"px-2 py-1.5 text-sm font-semibold data-[inset]:pl-8",
"px-2 py-1.5 text-sm font-medium data-[inset]:pl-8",
className
)}
{...props}

View File

@@ -97,7 +97,7 @@ function FormLabel({
<Label
data-slot="form-label"
data-error={!!error}
className={cn("data-[error=true]:text-destructive", className)}
className={cn("data-[error=true]:text-destructive-foreground", className)}
htmlFor={formItemId}
{...props}
/>
@@ -137,7 +137,7 @@ function FormDescription({ className, ...props }: React.ComponentProps<"p">) {
function FormMessage({ className, ...props }: React.ComponentProps<"p">) {
const { error, formMessageId } = useFormField()
const body = error ? String(error?.message) : props.children
const body = error ? String(error?.message ?? "") : props.children
if (!body) {
return null
@@ -147,7 +147,7 @@ function FormMessage({ className, ...props }: React.ComponentProps<"p">) {
<p
data-slot="form-message"
id={formMessageId}
className={cn("text-destructive text-sm font-medium", className)}
className={cn("text-destructive-foreground text-sm", className)}
{...props}
>
{body}

View File

@@ -44,14 +44,14 @@ function InputOTPSlot({
index: number
}) {
const inputOTPContext = React.useContext(OTPInputContext)
const { char, hasFakeCaret, isActive } = inputOTPContext.slots[index]
const { char, hasFakeCaret, isActive } = inputOTPContext?.slots[index] ?? {}
return (
<div
data-slot="input-otp-slot"
data-active={isActive}
className={cn(
"border-input ring-ring/10 dark:ring-ring/20 dark:outline-ring/40 outline-ring/50 relative flex h-9 w-9 items-center justify-center border-y border-r text-sm shadow-xs transition-all first:rounded-l-md first:border-l last:rounded-r-md data-[active=true]:z-10 data-[active=true]:ring-4 data-[active=true]:outline-1",
"border-input data-[active=true]:border-ring data-[active=true]:ring-ring/50 data-[active=true]:aria-invalid:ring-destructive/20 dark:data-[active=true]:aria-invalid:ring-destructive/40 aria-invalid:border-destructive data-[active=true]:aria-invalid:border-destructive relative flex h-9 w-9 items-center justify-center border-y border-r text-sm shadow-xs transition-all outline-none first:rounded-l-md first:border-l last:rounded-r-md data-[active=true]:z-10 data-[active=true]:ring-[3px]",
className
)}
{...props}

View File

@@ -8,7 +8,9 @@ function Input({ className, type, ...props }: React.ComponentProps<"input">) {
type={type}
data-slot="input"
className={cn(
"border-input file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground aria-invalid:outline-destructive/60 aria-invalid:ring-destructive/20 dark:aria-invalid:outline-destructive dark:aria-invalid:ring-destructive/50 ring-ring/10 dark:ring-ring/20 dark:outline-ring/40 outline-ring/50 aria-invalid:outline-destructive/60 dark:aria-invalid:outline-destructive dark:aria-invalid:ring-destructive/40 aria-invalid:ring-destructive/20 aria-invalid:border-destructive/60 dark:aria-invalid:border-destructive flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:ring-4 focus-visible:outline-1 disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:focus-visible:ring-[3px] aria-invalid:focus-visible:outline-none md:text-sm dark:aria-invalid:focus-visible:ring-4",
"border-input file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
className
)}
{...props}

View File

@@ -13,7 +13,7 @@ function Label({
<LabelPrimitive.Root
data-slot="label"
className={cn(
"text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
"flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
className
)}
{...props}

View File

@@ -103,7 +103,7 @@ function MenubarItem({
data-inset={inset}
data-variant={variant}
className={cn(
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive-foreground data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/40 data-[variant=destructive]:focus:text-destructive-foreground data-[variant=destructive]:*:[svg]:!text-destructive-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className
)}
{...props}
@@ -173,7 +173,7 @@ function MenubarLabel({
data-slot="menubar-label"
data-inset={inset}
className={cn(
"px-2 py-1.5 text-sm font-semibold data-[inset]:pl-8",
"px-2 py-1.5 text-sm font-medium data-[inset]:pl-8",
className
)}
{...props}

View File

@@ -61,7 +61,7 @@ function NavigationMenuItem({
}
const navigationMenuTriggerStyle = cva(
"group inline-flex h-9 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground disabled:pointer-events-none disabled:opacity-50 data-[active=true]:bg-accent/50 data-[state=open]:bg-accent/50 data-[active=true]:text-accent-foreground ring-ring/10 dark:ring-ring/20 dark:outline-ring/40 outline-ring/50 transition-[color,box-shadow] focus-visible:ring-4 focus-visible:outline-1"
"group inline-flex h-9 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=open]:hover:bg-accent data-[state=open]:text-accent-foreground data-[state=open]:focus:bg-accent data-[state=open]:bg-accent/50 ring-ring/10 dark:ring-ring/20 dark:outline-ring/40 outline-ring/50 transition-[color,box-shadow] focus-visible:ring-4 focus-visible:outline-1"
)
function NavigationMenuTrigger({
@@ -131,7 +131,7 @@ function NavigationMenuLink({
<NavigationMenuPrimitive.Link
data-slot="navigation-menu-link"
className={cn(
"hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground data-[active=true]:bg-accent/50 data-[active=true]:text-accent-foreground ring-ring/10 dark:ring-ring/20 dark:outline-ring/40 outline-ring/50 flex flex-col gap-1 rounded-sm p-2 text-sm transition-[color,box-shadow] focus-visible:ring-4 focus-visible:outline-1",
"data-[active=true]:focus:bg-accent data-[active=true]:hover:bg-accent data-[active=true]:bg-accent/50 data-[active=true]:text-accent-foreground hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground ring-ring/10 dark:ring-ring/20 dark:outline-ring/40 outline-ring/50 [&_svg:not([class*='text-'])]:text-muted-foreground flex flex-col gap-1 rounded-sm p-2 text-sm transition-[color,box-shadow] focus-visible:ring-4 focus-visible:outline-1 [&_svg:not([class*='size-'])]:size-4",
className
)}
{...props}

View File

@@ -27,7 +27,7 @@ function RadioGroupItem({
<RadioGroupPrimitive.Item
data-slot="radio-group-item"
className={cn(
"border-input text-primary ring-ring/10 dark:ring-ring/20 dark:outline-ring/40 outline-ring/50 aspect-square size-4 shrink-0 rounded-full border shadow-xs transition-[color,box-shadow] focus-visible:ring-4 focus-visible:outline-1 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:focus-visible:ring-0",
"border-input text-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive aspect-square size-4 shrink-0 rounded-full border shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
className
)}
{...props}

View File

@@ -5,7 +5,7 @@ import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area"
import { cn } from "@/lib/utils"
function ScrollAreaRoot({
function ScrollArea({
className,
children,
...props
@@ -16,7 +16,10 @@ function ScrollAreaRoot({
className={cn("relative", className)}
{...props}
>
<ScrollAreaPrimitive.Viewport className="ring-ring/10 dark:ring-ring/20 dark:outline-ring/40 outline-ring/50 size-full rounded-[inherit] transition-[color,box-shadow] focus-visible:ring-4 focus-visible:outline-1">
<ScrollAreaPrimitive.Viewport
data-slot="scroll-area-viewport"
className="ring-ring/10 dark:ring-ring/20 dark:outline-ring/40 outline-ring/50 size-full rounded-[inherit] transition-[color,box-shadow] focus-visible:ring-4 focus-visible:outline-1"
>
{children}
</ScrollAreaPrimitive.Viewport>
<ScrollBar />
@@ -52,6 +55,4 @@ function ScrollBar({
)
}
const ScrollArea = ScrollAreaRoot
export { ScrollArea, ScrollBar }

View File

@@ -33,7 +33,7 @@ function SelectTrigger({
<SelectPrimitive.Trigger
data-slot="select-trigger"
className={cn(
"border-input data-[placeholder]:text-muted-foreground aria-invalid:border-destructive ring-ring/10 dark:ring-ring/20 dark:outline-ring/40 outline-ring/50 flex h-9 w-full items-center justify-between rounded-md border bg-transparent px-3 py-2 text-sm shadow-xs transition-[color,box-shadow] focus-visible:ring-4 focus-visible:outline-1 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:focus-visible:ring-0 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&>span]:line-clamp-1",
"border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive flex h-9 w-fit items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className
)}
{...props}
@@ -88,7 +88,7 @@ function SelectLabel({
return (
<SelectPrimitive.Label
data-slot="select-label"
className={cn("px-2 py-1.5 text-sm font-semibold", className)}
className={cn("px-2 py-1.5 text-sm font-medium", className)}
{...props}
/>
)
@@ -103,7 +103,7 @@ function SelectItem({
<SelectPrimitive.Item
data-slot="select-item"
className={cn(
"focus:bg-accent focus:text-accent-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
"focus:bg-accent focus:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
className
)}
{...props}

View File

@@ -108,7 +108,7 @@ function SheetTitle({
return (
<SheetPrimitive.Title
data-slot="sheet-title"
className={cn("text-foreground font-semibold tracking-tight", className)}
className={cn("text-foreground font-semibold", className)}
{...props}
/>
)

View File

@@ -53,115 +53,103 @@ function useSidebar() {
return context
}
const SidebarProvider = React.forwardRef<
HTMLDivElement,
React.ComponentProps<"div"> & {
defaultOpen?: boolean
open?: boolean
onOpenChange?: (open: boolean) => void
}
>(
(
{
defaultOpen = true,
open: openProp,
onOpenChange: setOpenProp,
className,
style,
children,
...props
},
ref
) => {
const isMobile = useIsMobile()
const [openMobile, setOpenMobile] = React.useState(false)
function SidebarProvider({
defaultOpen = true,
open: openProp,
onOpenChange: setOpenProp,
className,
style,
children,
...props
}: React.ComponentProps<"div"> & {
defaultOpen?: boolean
open?: boolean
onOpenChange?: (open: boolean) => void
}) {
const isMobile = useIsMobile()
const [openMobile, setOpenMobile] = React.useState(false)
// This is the internal state of the sidebar.
// We use openProp and setOpenProp for control from outside the component.
const [_open, _setOpen] = React.useState(defaultOpen)
const open = openProp ?? _open
const setOpen = React.useCallback(
(value: boolean | ((value: boolean) => boolean)) => {
const openState = typeof value === "function" ? value(open) : value
if (setOpenProp) {
setOpenProp(openState)
} else {
_setOpen(openState)
}
// This sets the cookie to keep the sidebar state.
document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`
},
[setOpenProp, open]
)
// Helper to toggle the sidebar.
const toggleSidebar = React.useCallback(() => {
return isMobile
? setOpenMobile((open) => !open)
: setOpen((open) => !open)
}, [isMobile, setOpen, setOpenMobile])
// Adds a keyboard shortcut to toggle the sidebar.
React.useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
if (
event.key === SIDEBAR_KEYBOARD_SHORTCUT &&
(event.metaKey || event.ctrlKey)
) {
event.preventDefault()
toggleSidebar()
}
// This is the internal state of the sidebar.
// We use openProp and setOpenProp for control from outside the component.
const [_open, _setOpen] = React.useState(defaultOpen)
const open = openProp ?? _open
const setOpen = React.useCallback(
(value: boolean | ((value: boolean) => boolean)) => {
const openState = typeof value === "function" ? value(open) : value
if (setOpenProp) {
setOpenProp(openState)
} else {
_setOpen(openState)
}
window.addEventListener("keydown", handleKeyDown)
return () => window.removeEventListener("keydown", handleKeyDown)
}, [toggleSidebar])
// This sets the cookie to keep the sidebar state.
document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`
},
[setOpenProp, open]
)
// We add a state so that we can do data-state="expanded" or "collapsed".
// This makes it easier to style the sidebar with Tailwind classes.
const state = open ? "expanded" : "collapsed"
// Helper to toggle the sidebar.
const toggleSidebar = React.useCallback(() => {
return isMobile ? setOpenMobile((open) => !open) : setOpen((open) => !open)
}, [isMobile, setOpen, setOpenMobile])
const contextValue = React.useMemo<SidebarContext>(
() => ({
state,
open,
setOpen,
isMobile,
openMobile,
setOpenMobile,
toggleSidebar,
}),
[state, open, setOpen, isMobile, openMobile, setOpenMobile, toggleSidebar]
)
// Adds a keyboard shortcut to toggle the sidebar.
React.useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
if (
event.key === SIDEBAR_KEYBOARD_SHORTCUT &&
(event.metaKey || event.ctrlKey)
) {
event.preventDefault()
toggleSidebar()
}
}
return (
<SidebarContext.Provider value={contextValue}>
<TooltipProvider delayDuration={0}>
<div
data-slot="sidebar-wrapper"
style={
{
"--sidebar-width": SIDEBAR_WIDTH,
"--sidebar-width-icon": SIDEBAR_WIDTH_ICON,
...style,
} as React.CSSProperties
}
className={cn(
"group/sidebar-wrapper has-data-[variant=inset]:bg-sidebar flex min-h-svh w-full",
className
)}
ref={ref}
{...props}
>
{children}
</div>
</TooltipProvider>
</SidebarContext.Provider>
)
}
)
SidebarProvider.displayName = "SidebarProvider"
window.addEventListener("keydown", handleKeyDown)
return () => window.removeEventListener("keydown", handleKeyDown)
}, [toggleSidebar])
// We add a state so that we can do data-state="expanded" or "collapsed".
// This makes it easier to style the sidebar with Tailwind classes.
const state = open ? "expanded" : "collapsed"
const contextValue = React.useMemo<SidebarContext>(
() => ({
state,
open,
setOpen,
isMobile,
openMobile,
setOpenMobile,
toggleSidebar,
}),
[state, open, setOpen, isMobile, openMobile, setOpenMobile, toggleSidebar]
)
return (
<SidebarContext.Provider value={contextValue}>
<TooltipProvider delayDuration={0}>
<div
data-slot="sidebar-wrapper"
style={
{
"--sidebar-width": SIDEBAR_WIDTH,
"--sidebar-width-icon": SIDEBAR_WIDTH_ICON,
...style,
} as React.CSSProperties
}
className={cn(
"group/sidebar-wrapper has-data-[variant=inset]:bg-sidebar flex min-h-svh w-full",
className
)}
{...props}
>
{children}
</div>
</TooltipProvider>
</SidebarContext.Provider>
)
}
function Sidebar({
side = "left",
@@ -195,10 +183,6 @@ function Sidebar({
if (isMobile) {
return (
<Sheet open={openMobile} onOpenChange={setOpenMobile} {...props}>
<SheetHeader className="sr-only">
<SheetTitle>Sidebar</SheetTitle>
<SheetDescription>Displays the mobile sidebar.</SheetDescription>
</SheetHeader>
<SheetContent
data-sidebar="sidebar"
data-slot="sidebar"
@@ -211,6 +195,10 @@ function Sidebar({
}
side={side}
>
<SheetHeader className="sr-only">
<SheetTitle>Sidebar</SheetTitle>
<SheetDescription>Displays the mobile sidebar.</SheetDescription>
</SheetHeader>
<div className="flex h-full w-full flex-col">{children}</div>
</SheetContent>
</Sheet>
@@ -229,7 +217,7 @@ function Sidebar({
{/* This is what handles the sidebar gap on desktop */}
<div
className={cn(
"relative h-svh w-(--sidebar-width) bg-transparent transition-[width] duration-200 ease-linear",
"relative w-(--sidebar-width) bg-transparent transition-[width] duration-200 ease-linear",
"group-data-[collapsible=offcanvas]:w-0",
"group-data-[side=right]:rotate-180",
variant === "floating" || variant === "inset"
@@ -318,8 +306,8 @@ function SidebarInset({ className, ...props }: React.ComponentProps<"main">) {
<main
data-slot="sidebar-inset"
className={cn(
"bg-background relative flex min-h-svh flex-1 flex-col",
"peer-data-[variant=inset]:min-h-[calc(100svh-(--spacing(4)))] md:peer-data-[variant=inset]:m-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow-sm md:peer-data-[variant=inset]:peer-data-[state=collapsed]:ml-2",
"bg-background relative flex w-full flex-1 flex-col",
"md:peer-data-[variant=inset]:m-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow-sm md:peer-data-[variant=inset]:peer-data-[state=collapsed]:ml-2",
className
)}
{...props}
@@ -414,7 +402,7 @@ function SidebarGroupLabel({
data-slot="sidebar-group-label"
data-sidebar="group-label"
className={cn(
"text-sidebar-foreground/70 ring-sidebar-ring flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium outline-hidden transition-[margin,opa] duration-200 ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
"text-sidebar-foreground/70 ring-sidebar-ring flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium outline-hidden transition-[margin,opacity] duration-200 ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
"group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0",
className
)}

View File

@@ -53,7 +53,7 @@ function Slider({
<SliderPrimitive.Thumb
data-slot="slider-thumb"
key={index}
className="border-primary bg-background ring-ring/20 block size-4 shrink-0 rounded-full border shadow-sm transition-[color,box-shadow] hover:ring-4 focus-visible:ring-4 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50"
className="border-primary bg-background ring-ring/50 block size-4 shrink-0 rounded-full border shadow-sm transition-[color,box-shadow] hover:ring-4 focus-visible:ring-4 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50"
/>
))}
</SliderPrimitive.Root>

Some files were not shown because too many files have changed in this diff Show More