Compare commits

...

246 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
shadcn
9f4d65fc8f (9/n) shadcn: add warning for deprecated components (#6576)
* feat(shadcn): add deprecated components warning

* chore: changeset
2025-02-05 22:51:31 +04:00
shadcn
1e357cb20d (8/n) shadcn: default to new-york for v4 (#6574)
* feat(shadcn): handle radius for v4

* feat(shadcn): default to new-york for v4

* chore: changeset
2025-02-05 21:28:49 +04:00
shadcn
6339aaa315 (7/n) shadcn: improve focus-visible for all elements (#6573)
* feat: focus visible colors

* fix: lint

* fix: tabs
2025-02-05 17:49:20 +04:00
shadcn
e2caa3cd35 chore: add v4 to changeset ignore 2025-02-05 15:03:19 +04:00
shadcn
c74a094f14 (6/n) shadcn: handle registry for v4 (#6571)
* feat(shadcn): hotswap registry style for v4

* chore: changeset

* style(shadcn): remove unused import

* chore: registry

* fix(shadcn): cmon
2025-02-05 14:54:24 +04:00
shadcn
bfb5b6357e feat: add vercel analytics 2025-02-05 11:34:14 +04:00
shadcn
ac3d965c6e chore: remove unused code 2025-02-05 11:33:10 +04:00
shadcn
6e7cd11d68 fix(v4): charts 2025-02-05 11:31:50 +04:00
shadcn
a75b4ca80e fix: build 2025-02-05 11:17:48 +04:00
shadcn
475abbbb20 refactor: new-york-v4 2025-02-05 10:38:28 +04:00
shadcn
762cd73554 chore: build registry 2025-02-05 10:17:03 +04:00
shadcn
8c06932800 fix: debug build 2025-02-05 00:37:50 +04:00
shadcn
e516481394 (5/n) shadcn: migrate all components and blocks (#6567)
* feat: add v3

* feat: upgrade lucide-react

* feat: upgrade components to Tailwind v4

* feat(v3): add dark mode

* wip

* feat: components upgrade

* style: format

* feat: add select

* feat: more components

* feat: more components

* fix: shadow

* feat: upgrade sidebar

* refactor: registry

* refactor: internal

* fix: registry

* fix: charts

* chore: build registry

* fix: charts

* feat: form

* feat: clean up

* fix: icons

* fix: command demo

* chore: add new-york-v4 to registry

* fix: revert command in www registry

* fix

* chore: fix tests

* fix: types

* fix: lint errors

* chore: build v4 registry

* fix

* fix: types
2025-02-05 00:15:07 +04:00
shadcn
d1eb24e23a (4/n) shadcn: fix handling of sidebar colors (#6515)
* fix(shadcn): background, foreground and sidebar colors

* chore: changeset

* ci: update artifact
2025-01-30 22:57:57 +04:00
shadcn
9a14c1d092 (3/n) shadcn: tailwind config (#6490)
* feat(shadcn): add tailwind version detection

* chore: changeset

* feat(shadcn): css vars for tailwind v4

* fix(shadcn): handle color space

* fix(shadcn): add oklch support

* feat(shadcn): handle single quote

* feat(shadcn): update tailwind config

* feat(shadcn): add keyframes

* feat(shadcn): add animation vars

* fix: var name

* fix(shadcn): handle color values

* feat(shadcn): handle radius

* feat(shadcn): clean up formatting

* feat(shadcn): update next clean up function

* chore: changeset

* feat(shadcn): fix formatting for v3 and v4

* test(shadcn): update snapshots

* fix(shadcn): update tailwind version handling
2025-01-30 11:59:47 +04:00
shadcn
5ef2bc5f45 (2/n) shadcn: css vars for tailwind v4 (#6487)
* feat(shadcn): add tailwind version detection

* chore: changeset

* feat(shadcn): css vars for tailwind v4

* fix(shadcn): handle color space

* fix(shadcn): add oklch support

* feat(shadcn): handle single quote

* chore: add changeset
2025-01-30 11:55:22 +04:00
shadcn
8f6a64f176 (1/n) shadcn: add tailwind version detection (#6478)
* feat(shadcn): add tailwind version detection

* chore: changeset
2025-01-30 11:49:09 +04:00
Boby Tiwari
e85920c191 Update blocks.mdx (#6477) 2025-01-28 16:25:24 +04:00
github-actions[bot]
c8c4027b6b chore(release): version packages (#6349)
* chore(release): version packages

* chore(release): version packages

* chore: fix version

* deps: update lock file

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: shadcn <m@shadcn.com>
2025-01-28 15:26:05 +04:00
shadcn
699195ba77 deps(shadcn): remove unused lodash.template (#6475)
* deps(shadcn): remove unused lodash.template

* chore: changeset
2025-01-28 15:13:39 +04:00
shadcn
9643db42cf feat: add docs for registry (#6380)
* chore: 2.2.0-canary.2

* feat: add docs for registry

* docs(www): update registry docs

* fix: update dep

* docs(www): update docs

* docs(www): update registry docs

* feat: add new label

* fix: lint
2025-01-28 15:06:22 +04:00
JEM
dd71498762 feat(sidebar blocks): adds sidebar-16 block with components and styles (#5889)
Introduces a new sidebar-16 block with an inset sidebar and site header navigation.
Includes components for main navigation, projects, secondary navigation, and user menu.
Updates registry and scripts to include the new sidebar block.

Addresses: #5629

![image](https://github.com/user-attachments/assets/769148aa-ec83-46f4-b4f0-77947e253d28)
![image](https://github.com/user-attachments/assets/38532b4d-21f6-4ce1-9e92-08edc4f72e46)
2025-01-27 12:40:45 +00:00
Jordan Cruz-Correa
ddf761e802 feat: add oklch color format support for /colors (#6441) 2025-01-24 17:53:45 +04:00
shadcn
5f7957ab51 feat(shadcn): add new registry:file type (#6377)
* feat(shadcn): add new registry:file type

* chore: add changeset

* fix: file target
2025-01-16 15:46:33 +04:00
shadcn
f07c7ad5d0 feat(shadcn): handle nested paths for components (#6369)
* feat(shadcn): handle nested paths for components

* chore: add changeset
2025-01-15 18:01:44 +04:00
shadcn
d5aa527f0b docs(www): add block preview image 2025-01-15 15:14:12 +04:00
shadcn
5ec990a474 docs(www): add blocks contribution docs (#6354)
* docs(www): add blocks contrib docs

* docs(www): update intro

* feat: update banner

* docs: add link

* docs: update block types
2025-01-14 21:56:45 +04:00
shadcn
cb742e9825 feat: add new registry build command (#6350)
* feat: implement shadcn/registry

* feat: add schema field

* fix: import

* chore: add changeset

* chore: remove console

* fix: tests

* fix: diff command

* feat: move to schema/registy-item.json

* fix

* ci: switch to node 20

* ci: build packages

* fix: types

* chore: update schema

* chore: update build registry script

* feat(shadcn): add build command
2025-01-14 15:59:41 +04:00
shadcn
254198b4bf feat: add shadcn/registry (#6339)
* feat: implement shadcn/registry

* feat: add schema field

* fix: import

* chore: add changeset

* chore: remove console

* fix: tests

* fix: diff command

* feat: move to schema/registy-item.json

* fix

* ci: switch to node 20

* ci: build packages
2025-01-14 10:50:19 +04:00
shadcn
1081536246 fix: path 2024-12-20 19:12:22 +04:00
shadcn
811bb59a8f feat(shadcn): monorepo support (#6104) 2024-12-20 19:07:17 +04:00
shadcn
ea677cc74e fix: open in v0 2024-12-17 23:14:59 +04:00
github-actions[bot]
9253b43f29 chore(release): version packages (#6094)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-12-16 23:56:28 +04:00
shadcn
c8fda09a63 feat(cli): detect and use next version (#6093)
* feat: detect and use next version

* chore: changeset
2024-12-16 23:53:40 +04:00
shadcn
1ff01b1bb5 fix(www): adjust reading width 2024-12-16 12:43:11 +04:00
shadcn
f10d59fee9 fix(www): themes page 2024-12-16 12:26:48 +04:00
shadcn
2f869a2590 feat(www): code blocks package manager (#6075)
* feat(www): code blocks

* fix: code style
2024-12-14 19:13:06 +04:00
LiuJie
102b0b0c62 fix: calendar icon components' props not use. (#5222)
* fix: calendar icon components' props not use.

* fix: calendar icon components' props not use.

* style: revert the original className order

* fix: calendar icon components' props not use.

---------

Co-authored-by: shadcn <m@shadcn.com>
2024-12-14 15:16:52 +04:00
Ahad Rizvi
387756c4ab Remove unused imports from shadcn chart (#4391)
* Remove unused imports from shadcn chart

* chore: rebuild registry

---------

Co-authored-by: shadcn <m@shadcn.com>
2024-12-14 15:03:01 +04:00
shadcn
05145e66d3 feat: refactor registry (#6071)
* feat(www): add login blocks

* chore(www): restructure for blocks

* chore: build registry

* chore: clean up chunks

* fix(www): chart categories

* feat(www): big registry refactor

* feat(www): update blocks

* feat: complex blocks

* fix: update schema

* feat: sync new-york and default

* fix: lint

* feat: move charts

* fix(www): code

* fix: src path

* chore: rebuild registry

* fix: screenshot

* fix: set new-york as default
2024-12-14 14:52:55 +04:00
shadcn
704991247c fix: ui imports for open in v0 (#6029) 2024-12-10 14:20:50 +04:00
github-actions[bot]
729b9ec8ca chore(release): version packages (#5812)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-12-10 12:17:32 +04:00
Jens Astrup
a1bed464f3 chore(apps): Update lodash (#4397)
* chore(apps): Refactor usage of lodash.template to lodash to address security vulnerability

* chore(cli): Refactor usage of lodash.template to lodash to address security vulnerability

* deps: update lock

* chore: changesets

* style: fix format

* fix: import

* chore: build registry

---------

Co-authored-by: shadcn <m@shadcn.com>
2024-12-09 12:33:28 +04:00
shadcn
805ed4120a feat(www): refactor examples page (#5818) 2024-11-13 16:01:14 +04:00
shadcn
600a593c87 docs(www): update manual installation (#5817) 2024-11-13 15:42:52 +04:00
Braden Corbold
500dbe2664 fix(shadcn) arrays and nested deeply nested spread (#5711)
* fix: tailwind config updater parser

* fix: remove quote around spread element

* fix: specify deepmerge option for array

* fix(shadcn): Nested and spread array elements

* add test case for boolean primitive

---------

Co-authored-by: matsuyoshi30 <sfbgwm30@gmail.com>
Co-authored-by: shadcn <m@shadcn.com>
2024-11-13 15:19:21 +04:00
JEM
c577ee0666 docs(contributing): add CLI usage instructions (#5807)
Introduce a detailed workflow for running the CLI locally, ensuring contributors can easily test and develop enhancements in their local environment. This addition helps maintain consistency between CLI and component updates.
2024-11-13 15:17:11 +04:00
shadcn
d5bf0018fd fix(shadcn): handling of tsconfig aliases (#5813)
* fix(shadcn): handling of tsconfig aliases

* chore: add changeset
2024-11-13 15:15:22 +04:00
Tobbe Lundberg
fb36ca4159 fix(transform): Support aliases that are longer than one character (#5678)
* fix(transform): Support aliases that are longer than one character

* feat(shadcn): update handling of aliases

* chore: add changeset

---------

Co-authored-by: shadcn <m@shadcn.com>
2024-11-12 22:13:38 +04:00
JEM
824a35ada1 test(snapshots): update test snapshots for consistency (#5801)
Updated snapshot components to use `React.ComponentProps` instead of custom interfaces for `input` and `Dialog` components. This simplifies the code by leveraging built-in React types, ensuring consistency and reducing potential errors arising from custom definitions.
2024-11-12 12:54:31 +04:00
shadcn
8d520c8d49 fix(www): figma link (#5798) 2024-11-11 20:08:24 +04:00
Temkin Mengsitu
0873835339 fix: ensure block component name is copied in shadcn (#5790) 2024-11-11 12:10:04 +04:00
JEM
4a0d4cfdb9 refactor(components): remove redundant empty interfaces from Input, Command, and Textarea components (#5657)
* refactor(components): remove redundant empty interfaces from Input, Command, and Textarea components

Simplified props handling by removing redundant empty interfaces (`InputProps`, `CommandDialogProps`, `TextareaProps`). Directly extended native HTML attributes/lib for each component, enhancing code clarity and maintainability. This refactor ensures compliance with ESLint rules and streamlines future modifications without altering component functionality.

* updated the registry

* rerun build:registry with updated command

* Refactor input and textarea components to use `React.ComponentProps`

* update registry
2024-11-08 20:26:01 +04:00
shadcn
c4c5d8d419 feat(www): update next-themes 2024-11-08 15:18:32 +04:00
Wing Chung Ng
9253682b87 refactor(www): update ThemeProviderProps type extraction for next-themes v0.4.0 compatibility (#5701)
Since next-themes v0.4.0 no longer provides types in `dist`, replaced the import of `ThemeProviderProps` from `next-themes/dist/types` with a new type extraction using `React.ComponentProps`. This change ensures continued type safety and compatibility with the updated library version.

Co-authored-by: shadcn <m@shadcn.com>
2024-11-08 15:16:10 +04:00
shadcn
c7cd16a637 fix(www): interactive chart 2024-11-07 21:36:19 +04:00
shadcn
eff1918d41 fix: update file handling 2024-11-07 21:21:42 +04:00
shadcn
366d6b656b fix(www): file tracing 2024-11-07 21:05:44 +04:00
shadcn
e489c5e08e fix(www): file tracing 2024-11-07 20:54:26 +04:00
shadcn
d87003e0a4 fix(www): sidebar 2024-11-07 20:40:18 +04:00
shadcn
a8633075f7 fix(www): component source 2024-11-07 20:36:21 +04:00
shadcn
432d5e6e28 fix: code view for mdx 2024-11-07 17:45:34 +04:00
shadcn
8f0c26f22a feat(www): code for blocks (#5756)
* feat: update blocks

* fix: scrollbars

* fix: code viewer

* test(shadcn): fix
2024-11-07 17:09:41 +04:00
shadcn
149b321c1b fix: lint 2024-11-06 17:34:21 +04:00
shadcn
c1ae5a57cc feat(www): nav color 2024-11-06 17:29:53 +04:00
shadcn
b8ed303d8c Merge branch 'main' of github.com:shadcn-ui/ui 2024-11-06 17:19:41 +04:00
shadcn
13c97acf9f fix: font size 2024-11-06 17:16:21 +04:00
shadcn
bed277c54d feat( www): minor updates (#5749)
* feat(www): update mode switcher

* feat(www): update layout

* feat(www): update theme-color handling

* fix(www): docs pages
2024-11-06 17:03:28 +04:00
github-actions[bot]
f7c42169a6 chore(release): version packages (#5733)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-11-06 00:19:13 +04:00
shadcn
2c2fe97eb9 feat: move new-york to lucide-react (#5602)
* feat: move new-york to lucide-react

* fix: mail open

* chore: update registry

* chore: add test:dev

* chore: add changeset

* feat: build an icon registry

* chore: add missing registry icons

* feat: add an icons debug page

* feat: add an icon migration

* chore(www): migrate all radix icons to lucide

* feat: update migration script

* chore: update changeset

* feat(shadcn): implement icons transformer

* fix: missing registry icons

* fix(shadcn): handling of missing icons

* feat: add support for multiple libraries
2024-11-06 00:00:41 +04:00
shadcn
d64374d009 fix(www): chart colors 2024-11-04 23:06:14 +04:00
github-actions[bot]
e24e51a2fa chore(release): version packages (#5709)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-11-04 17:25:59 +04:00
shadcn
cdfecd1d97 Revert "feat: remove npm flags" (#5707)
* Revert "feat: remove npm flags (#5686)"

This reverts commit 4ff64ba818.

* chore: temporarily bring back flag
2024-11-04 17:14:08 +04:00
manfromexistence04
2c043e709f fix: svg props casing to camelCase (#5691) 2024-11-03 19:27:28 +04:00
Ghribi Ouassim
aed19aa911 docs(www): added --all option to add command in cli docs page (#5591) 2024-11-03 14:37:08 +04:00
shadcn
9e35d229ae feat: www updates (#5688)
* feat: www updates

* fix: lint
2024-11-03 14:35:04 +04:00
shadcn
db1975ef4d Merge branch 'main' of github.com:shadcn-ui/ui 2024-11-03 13:27:19 +04:00
shadcn
961e0b62d7 fix: a11y for card and calendar 2024-11-03 13:27:10 +04:00
github-actions[bot]
70c684c224 chore(release): version packages (#5687)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-11-03 12:17:58 +04:00
shadcn
4ff64ba818 feat: remove npm flags (#5686)
* docs: update status table and docs

* feat(shadcn): remove react-19 check

* chore: add changeset
2024-11-03 12:12:41 +04:00
shadcn
500a353816 feat: update home page (#5648) 2024-10-30 21:28:58 +04:00
Nicholas Lim
c830780d62 docs(www): fix diff for peerDependencies (#5640)
Co-authored-by: shadcn <m@shadcn.com>
2024-10-30 17:23:06 +04:00
Felix Lu
debd51a854 refactor(sidebar): improve setOpen callback logic (#5593)
* refactor(sidebar): improve setOpen callback logic
- Simplify state handling
- Ensure consistent cookie setting
- Added ability for users to persist sidebar state via cookies
- Update sidebar.mdx doc with SSR persistence instructions
- Run pnpm registry:build to update component

* docs: updates

---------

Co-authored-by: shadcn <m@shadcn.com>
2024-10-30 17:09:09 +04:00
shadcn
78426dd862 docs: fix diff 2024-10-30 16:31:44 +04:00
shadcn
6e47a94a8f chore: format 2024-10-30 15:40:46 +04:00
Barinderpreet Singh
ab6a856930 Docs: update vite installation docs (#4741)
* docs: update vite installation docs

* Update content config

---------

Co-authored-by: shadcn <m@shadcn.com>
2024-10-30 15:37:32 +04:00
shadcn
b33d3868e9 docs(www): update status for radix icons (#5638) 2024-10-30 11:29:48 +04:00
Maou
9e0a86122a fix(docs): resolve link issue in documentation pages (#5633)
Updated broken link in documentation routing file. 
This ensures that the correct content is loaded based on the slug.
2024-10-30 07:10:15 +00:00
github-actions[bot]
2b276de95a chore(release): version packages (#5625)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-10-29 21:39:58 +04:00
shadcn
64739f8399 feat: react 19 (#5621)
* feat(shadcn): add flag prompt for npm

* docs: add docs for react 19

* chore: add changeset

* test: update snapshots

* docs: add notes for recharts

* docs: fix

* fix

* fix: linting
2024-10-29 21:30:05 +04:00
shadcn
f0cff7e0eb fix: gaps in command and dropdown-menu (#5570)
* fix: gaps in command and dropdown-menu

* chore: update snapshots
2024-10-25 18:13:13 +04:00
github-actions[bot]
e242adaa9c chore(release): version packages (#5568)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-10-25 17:52:32 +04:00
Kiyan
986c00ee0e fix: shadcn init with correct packageManager (#5299)
* fix: shadcn init with correct  packageManager

* chore: changeset

---------

Co-authored-by: shadcn <m@shadcn.com>
2024-10-25 17:46:48 +04:00
shadcn
d0eece06d4 fix: update registry (#5530) 2024-10-23 17:35:26 +04:00
github-actions[bot]
0a0a566a4e chore(release): version packages (#5526)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-10-23 11:35:28 +04:00
Braden Corbold
bf5a79c4d4 fix(shadcn): fix transformRsc to account for ' (#5518)
* fix(shadcn): fix transformRsc to account for '

* chore: add changeset

---------

Co-authored-by: shadcn <m@shadcn.com>
2024-10-23 11:33:11 +04:00
shadcn
f02b412478 fix(shadcn): temporarily pin create-next-app (#5525)
* feat: temporarily pin create-next-app

* chore: add changeset
2024-10-23 11:30:29 +04:00
DADDOU Mohamed El Amine
0d31293c7b docs: add missing installation step in the sidebar documentation (#5467) 2024-10-22 12:15:03 +04:00
Jacob Paris
3d1d19fc1b fix(sidebar): only apply text-sidebar-foreground to sidebar (#5491)
The text-sidebar-foreground class was applied to the SidebarProvider, which is designed to wrap the whole app, therefore making the whole app inherit its font color. This has been removed. The correct place for it is in the Sidebar component

Of the 3 possible return cases in the Sidebar component, 2 already applied the text-sidebar-foreground and I have added it to the third
2024-10-22 08:02:20 +00:00
BNaushad
e5b56c84a9 docs(sidebar): fix typo in sidebar documentation (#5487) 2024-10-22 11:42:38 +04:00
shadcn
630afe836e fix: sidebar provider 2024-10-21 12:38:51 +04:00
Jakob Ortmann
52c12bc27a fix import in sidebar example (#5438) 2024-10-21 11:46:27 +04:00
JEM
182f2083cd docs(sidebar): Fix sidebar toggle text for consistent behavior (#5428) 2024-10-21 11:45:11 +04:00
Phaired
3febcdc523 docs: fix type in sidebar.mdx (#5452) 2024-10-21 11:43:35 +04:00
EdamAmex
ced2513137 docs: fix prop name (#5460) 2024-10-21 11:43:08 +04:00
shadcn
93ae8bd67f chore: deprecate next-template (#5478)
* chore: deprecate next-template

* chore: tests
2024-10-21 11:40:54 +04:00
shadcn
3259fb7ca1 fix: overflow on icon 2024-10-19 01:28:07 +04:00
shadcn
d8397d80a8 chore: rebuild registry 2024-10-18 23:25:49 +04:00
shadcn
06e74fce78 Merge branch 'shadcn/sidebar-tsx'
# Conflicts:
#	apps/www/public/r/styles/default/use-mobile.json
#	apps/www/public/r/styles/new-york/button.json
#	apps/www/public/r/styles/new-york/dropdown-menu.json
#	apps/www/public/r/styles/new-york/use-mobile.json
#	apps/www/registry/new-york/ui/button.tsx
#	apps/www/registry/new-york/ui/dropdown-menu.tsx
2024-10-18 23:14:27 +04:00
shadcn
bc9e5eaaab test: fix 2024-10-18 22:52:25 +04:00
shadcn
c9b69d0836 docs: remove beta 2024-10-18 22:22:25 +04:00
shadcn
539212c49e feat: add redirect 2024-10-18 15:29:28 +04:00
shadcn
123887c36c fix: blocks 2024-10-18 00:54:19 +04:00
shadcn
35c1ba57c2 feat: add login-01 to v0 2024-10-18 00:34:03 +04:00
shadcn
b34516f471 fix: lint 2024-10-17 22:59:23 +04:00
shadcn
66b95402c1 feat: add v0 registry blocks 2024-10-17 22:58:20 +04:00
shadcn
5460177a7a fix: sidebar-07 2024-10-17 19:49:28 +04:00
shadcn
b0049c2266 fix: data-name 2024-10-17 17:13:47 +04:00
shadcn
4e6e21f094 fix: dark mode blocks 2024-10-17 17:01:24 +04:00
shadcn
3b808c83be fix: lint 2024-10-17 16:33:34 +04:00
shadcn
0e6b37e99a feat: update preview for mobile 2024-10-17 16:28:09 +04:00
shadcn
9ec433838f fix: lint 2024-10-17 01:11:00 +04:00
shadcn
444ff70590 feat: add notice 2024-10-17 01:06:35 +04:00
shadcn
27bc5deff1 docs: beta testers 2024-10-17 00:55:28 +04:00
shadcn
cacd7c8798 fix: use-mobile 2024-10-17 00:36:50 +04:00
shadcn
f227f93742 fix: sidebar 2024-10-17 00:34:50 +04:00
shadcn
87e099a3d7 feat: update button and dropdown menu (#5408) 2024-10-17 00:21:03 +04:00
shadcn
303d65718c chore: registry updates 2024-10-16 23:58:44 +04:00
shadcn
909219df14 docs 2024-10-16 15:33:16 +04:00
shadcn
e8ada4e3c7 docs 2024-10-16 14:33:43 +04:00
shadcn
8fc80836ff docs 2024-10-16 12:54:21 +04:00
shadcn
d0a308cc64 fix: edit in v0 2024-10-16 11:12:58 +04:00
shadcn
e461c02389 feat: update open in v0 2024-10-15 21:28:38 +04:00
shadcn
50c2f6045a chore: fix lint 2024-10-15 12:52:40 +04:00
shadcn
14aca65eee docs: add sidebar docs 2024-10-15 12:49:22 +04:00
shadcn
14c952b594 chore: update registry 2024-10-15 12:41:20 +04:00
shadcn
1e9434e6f9 chore: build registry 2024-10-15 12:38:45 +04:00
shadcn
f3d14c48cb feat(www): update button and tooltip 2024-10-15 12:38:32 +04:00
shadcn
c668c35bb9 feat(www): add sidebar components 2024-10-15 12:37:47 +04:00
shadcn
1297abc882 fix: v0 2024-10-07 21:13:33 +04:00
shadcn
36ebbf26dc docs(www): update figma 2024-09-29 14:52:52 +04:00
github-actions[bot]
a2abc4ad95 chore(release): version packages (#4963)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-09-27 22:10:05 +04:00
Thomas Raffray
4b546bfb13 fix(cli): remix detection (#4972)
# What

Some remix templates doesn't package a `vite.config.*` file at their root.
It's the case for the recommended starter "stack" templates: blues-stack, indie-stack and grunge-stack.
As recommended in a TODO comment, it's more suitable to check for a `@remix-run/*` dependency in the package dependencies.

# How

- decouple vite and remix checks
- retrieve the `package.json`
- allow passing a `cwd` to the retrieval method
- remove the "empty config file list" that can be empty for a remix stack
- check that the `package.json` contains a `@remix-run/*` dependency

# Test

Added a fixture by running `npx create-remix@latest --template remix-run/indie-stack` in the [frameworks](/Fluf22/shadcn-ui/tree/fix/cli-remix-detection/packages/cli/test/fixtures/frameworks) folder and named it `remix-indie-stack`, if ever we want another stack as a fixture later

---

Fixes shadcn-ui/ui#4967
2024-09-27 17:58:46 +00:00
shadcn
5fc9ade413 chore(cli): add deprecation notice (#4988)
* feat(cli): add deprecation message

* chore: changesets
2024-09-27 17:43:16 +04:00
Nami Sunami
96880e7c9a fix: Fix the typo in error messages (component.jsoncomponents.json) (#4977)
* fix(prompt): fix error message, component.json -> components.json

* fix(config): fix the error message, component.json -> components.json

* chore: changeset

---------

Co-authored-by: shadcn <m@shadcn.com>
2024-09-27 16:01:04 +04:00
Artem Zakharchenko
7dfdb029e7 use "?url" suffix in "tailwind.css" import (Remix) (#4945)
Importing `tailwind.css` is incorrect as it will treat that file as a CSS Module, throwing that it has no `default` export. 

Instead, the tutorial relies on the import returning a _URL_ to the stylesheet. Adding `?url` to the import specifier is the way to go.
2024-09-26 12:42:49 +00:00
shadcn
28f34ed3c3 feat(shadcn): recursively resolve registry dependencies (#4961)
* feat(shadcn): recursively resolve registry dependencies

* chore: add changeset

* ci: update actions/upload-artifact
2024-09-25 22:56:39 +04:00
github-actions[bot]
bd54184e60 chore(release): version packages (#4938)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-09-23 15:21:55 +04:00
Braden Corbold
ce3adfa075 fix(shadcn): Support single quote formatted registry files (#4870) (#4871) 2024-09-23 11:47:37 +04:00
Shivang Rathore
674807c1b4 fix(shadcn): Ensure .scss files are considered in init command (#4866)
Co-authored-by: shadcn <m@shadcn.com>
2024-09-23 11:44:27 +04:00
sapenlei
c62167a449 fix(shadcn): default next styles are not completely cleared (#4922)
* fix(shadcn):  default next styles are not completely cleared

* chore: add changeset

* fix(shadcn): tests

---------

Co-authored-by: shadcn <m@shadcn.com>
2024-09-23 11:38:59 +04:00
github-actions[bot]
061083006f chore(release): version packages (#4857)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-09-15 13:40:31 +04:00
Jack Herrington
1af66c2d08 Adding support for ~ in target specification (#4848)
* Adding support for ~ in target specification

* test(shadcn): add a test for srcDir false

* chore: changeset

---------

Co-authored-by: shadcn <m@shadcn.com>
2024-09-15 13:27:48 +04:00
shadcn
0993d98cc7 ci: update stale config (#4858) 2024-09-15 13:25:06 +04:00
Hichem Fantar
52d223393a (fix Input) choose file text color in dark mode (#4843)
This pull request fixes the issue with the choose file text color in dark mode. The changes ensure that the text color is correctly displayed in dark mode.

the reason this bug doesn't happen in the shadcn website is because it sets `color-scheme:dark` on the document;

this fix makes sure this always works even if color scheme isn't set.

Closes #4842

![image](https://github.com/user-attachments/assets/37cb3f2a-c1a7-49b3-8ef1-77823dd7bd45)

![image](https://github.com/user-attachments/assets/92913fec-5651-4cfc-8e16-2f12c06c33b8)
2024-09-15 09:20:19 +00:00
Shivang Rathore
207b69fe8d fix(cli): Ensure .scss files are considered in getTailwindCssFile (#4797)
* fix(cli): Ensure .scss files are considered in getTailwindCssFile

- Updated getTailwindCssFile function to include .scss files when checking for Tailwind CSS configuration.
- This change ensures that .scss files are properly identified and handled, improving compatibility with projects using SCSS.

* chore: add changeset

---------

Co-authored-by: shadcn <m@shadcn.com>
2024-09-15 12:55:08 +04:00
Rana Haris Ali
408760a93b fix: correct grammar in "path does not contain a package.json" message (#4815)
* fix: correct grammar in "path does not contain a package.json" message

* chore: add changeset

---------

Co-authored-by: shadcn <m@shadcn.com>
2024-09-15 12:51:23 +04:00
Devansh Mahant
a9ab7afebf Fix Incorrect Hook Import Path in Toast Component Example in ShadCN Docs (#4811)
#### Summary:
This pull request addresses a documentation error found in the ShadCN website's Toast component example. Specifically, the import route for the hook is incorrect in the example. and fixes [#4816](https://github.com/shadcn-ui/ui/issues/4816)

#### Issue:
Upon reviewing the [ShadCN Toast documentation](https://ui.shadcn.com/docs/components/toast), I found that the import path for the Toast component hook was wrongly mentioned as being located in the `components` folder. According to the `component.json` file, the correct location of the hook is within the `hooks` folder inside the main directory.

#### Fix:
- Corrected the import path in the Toast component example from `components` to `hooks`, as per the `component.json` file structure.

#### Example of Fix:
**Before:**
```js
import { useToast } from "@/components/use-toast";
```

**After:**
```js
import { useToast } from "@/hooks/use-toast";
```

#### Testing:
- Verified that the corrected path resolves correctly.
- Ensured the example works as expected after the change.

#### Impact:
This fix prevents confusion for users following the example and ensures that the import path accurately reflects the project structure, improving the overall developer experience.
2024-09-15 08:45:29 +00:00
Quinn Blenkinsop
b6221ea524 fix(www): update broken link to headless ui (#3542)
The link appears to have broken at some point, I've updated it

Co-authored-by: shadcn <m@shadcn.com>
2024-09-15 12:31:28 +04:00
github-actions[bot]
9ef7967b0d chore(release): version packages (#4821)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-09-12 17:54:41 +04:00
shadcn
64b2f1a5ad feat(shadcn): add experimental docs (#4820)
* feat(shadcn): add cli docs

* chore: add changeset

* fix: tests
2024-09-12 17:51:59 +04:00
github-actions[bot]
f4ca57a79c chore(release): version packages (#4788)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-09-09 12:34:19 +04:00
shadcn
99ff9caf71 fix(shadcn): add src to content in tailwind config (#4787)
* feat(shadcn): handle src dir

* chore: changeset
2024-09-09 12:32:12 +04:00
3213 changed files with 148379 additions and 56496 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

@@ -7,5 +7,5 @@
"access": "public",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": ["www", "**-template"]
"ignore": ["www", "v4"]
}

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
---
add theme vars support

View File

@@ -0,0 +1,5 @@
---
"shadcn": minor
---
add tailwind version detection

View File

@@ -0,0 +1,5 @@
---
"shadcn": minor
---
add support for tailwind v4

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": minor
---
default for new-york for v4

View File

@@ -0,0 +1,5 @@
---
"shadcn": minor
---
fix handling of sidebar colors

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": minor
---
hotswap style for v4

View File

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

View File

@@ -0,0 +1,5 @@
---
"shadcn": minor
---
add warning for deprecated components

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

8
.eslintignore Normal file
View File

@@ -0,0 +1,8 @@
node_modules/
target/
.next/
build/
dist/
/templates/
/fixtures/

View File

@@ -16,7 +16,7 @@ jobs:
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: 18
node-version: 20
- uses: pnpm/action-setup@v4
name: Install pnpm
@@ -52,7 +52,7 @@ jobs:
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: 18
node-version: 20
- uses: pnpm/action-setup@v4
name: Install pnpm
@@ -90,7 +90,7 @@ jobs:
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: 18
node-version: 20
- uses: pnpm/action-setup@v4
name: Install pnpm
@@ -113,4 +113,7 @@ jobs:
- name: Install dependencies
run: pnpm install
- name: Build packages
run: pnpm --filter=shadcn build
- run: pnpm typecheck

View File

@@ -1,5 +1,5 @@
# Adapted from vercel/next.js
name: Issue Stale
name: "Stale issue handler"
on:
workflow_dispatch:
schedule:
@@ -11,16 +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 leave a comment. Thank you."
ascending: true
days-before-issue-close: 7
days-before-issue-stale: 15
stale-pr-label: "stale?"
days-before-pr-close: -1
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

@@ -49,7 +49,7 @@ jobs:
A new prerelease is available for testing:
```sh
npx shadcn@${{ env.BETA_PACKAGE_VERSION }}
pnpm dlx shadcn@${{ env.BETA_PACKAGE_VERSION }}
```
- name: "Remove the autorelease label once published"

View File

@@ -54,7 +54,7 @@ jobs:
path: packages/shadcn
- name: Upload packaged artifact
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v4
with:
name: npm-package-shadcn@${{ steps.package-version.outputs.current-version }}-pr-${{ github.event.number }} # encode the PR number into the artifact name
path: packages/shadcn/dist/index.js

7
.gitignore vendored
View File

@@ -33,4 +33,9 @@ yarn-error.log*
.turbo
.contentlayer
tsconfig.tsbuildinfo
tsconfig.tsbuildinfo
# ide
.idea
.fleet
.vscode

1
.npmrc
View File

@@ -1 +1,2 @@
auto-install-peers=true
link-workspace-packages=true

View File

@@ -4,8 +4,10 @@
{ "pattern": "packages/*/" }
],
"tailwindCSS.experimental.classRegex": [
["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"],
["cn\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"]
["cva\\(((?:[^()]|\\([^()]*\\))*)\\)", "[\"'`]?([^\"'`]+)[\"'`]?"],
["cn\\(((?:[^()]|\\([^()]*\\))*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)"]
// "cva\\(([^)]*)\\)",
// "[\"'`]([^\"'`]*).*?[\"'`]"
],
"vitest.debugExclude": [
"<node_internals>/**",

View File

@@ -91,6 +91,42 @@ pnpm --filter=www dev
pnpm --filter=shadcn-ui dev
```
## Running the CLI Locally
To run the CLI locally, you can follow the workflow:
1. Start by running the registry (main site) to make sure the components are up to date:
```bash
pnpm www:dev
```
2. Run the development script for the CLI:
```bash
pnpm shadcn:dev
```
3. In another terminal tab, test the CLI by running:
```bash
pnpm shadcn
```
To test the CLI in a specific app, use a command like:
```bash
pnpm shadcn <init | add | ...> -c ~/Desktop/my-app
```
4. To run the tests for the CLI:
```bash
pnpm --filter=shadcn test
```
This workflow ensures that you are running the most recent version of the registry and testing the CLI properly in your local environment.
## Documentation
The documentation for this project is located in the `www` workspace. You can run the documentation locally by running the following command:

View File

@@ -3,7 +3,12 @@
# dependencies
/node_modules
/.pnp
.pnp.js
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions
# testing
/coverage
@@ -23,9 +28,10 @@
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# local env files
.env*.local
# env files (can opt-in for committing if needed)
.env*
# vercel
.vercel

6
apps/v4/.prettierignore Normal file
View File

@@ -0,0 +1,6 @@
dist
node_modules
.next
build
.contentlayer
registry/__index__.tsx

1
apps/v4/README.md Normal file
View File

@@ -0,0 +1 @@
This is a wip registry for the `shadcn` canary version. It has React 19 and Tailwind v4 components.

View File

@@ -0,0 +1 @@
// The content of this directory is autogenerated by the registry server.

View File

View File

@@ -0,0 +1 @@
> Files inside this directory is autogenerated by `./scripts/build-registry.ts`. **Do not edit them manually.** - shadcn

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,76 @@
export { ChartAreaDefault } from "@/registry/new-york-v4/charts/chart-area-default"
export { ChartAreaLinear } from "@/registry/new-york-v4/charts/chart-area-linear"
export { ChartAreaStep } from "@/registry/new-york-v4/charts/chart-area-step"
export { ChartAreaLegend } from "@/registry/new-york-v4/charts/chart-area-legend"
export { ChartAreaStacked } from "@/registry/new-york-v4/charts/chart-area-stacked"
export { ChartAreaStackedExpand } from "@/registry/new-york-v4/charts/chart-area-stacked-expand"
export { ChartAreaIcons } from "@/registry/new-york-v4/charts/chart-area-icons"
export { ChartAreaGradient } from "@/registry/new-york-v4/charts/chart-area-gradient"
export { ChartAreaAxes } from "@/registry/new-york-v4/charts/chart-area-axes"
export { ChartAreaInteractive } from "@/registry/new-york-v4/charts/chart-area-interactive"
export { ChartBarDefault } from "@/registry/new-york-v4/charts/chart-bar-default"
export { ChartBarHorizontal } from "@/registry/new-york-v4/charts/chart-bar-horizontal"
export { ChartBarMultiple } from "@/registry/new-york-v4/charts/chart-bar-multiple"
export { ChartBarStacked } from "@/registry/new-york-v4/charts/chart-bar-stacked"
export { ChartBarLabel } from "@/registry/new-york-v4/charts/chart-bar-label"
export { ChartBarLabelCustom } from "@/registry/new-york-v4/charts/chart-bar-label-custom"
export { ChartBarMixed } from "@/registry/new-york-v4/charts/chart-bar-mixed"
export { ChartBarActive } from "@/registry/new-york-v4/charts/chart-bar-active"
export { ChartBarNegative } from "@/registry/new-york-v4/charts/chart-bar-negative"
export { ChartBarInteractive } from "@/registry/new-york-v4/charts/chart-bar-interactive"
export { ChartLineDefault } from "@/registry/new-york-v4/charts/chart-line-default"
export { ChartLineLinear } from "@/registry/new-york-v4/charts/chart-line-linear"
export { ChartLineStep } from "@/registry/new-york-v4/charts/chart-line-step"
export { ChartLineMultiple } from "@/registry/new-york-v4/charts/chart-line-multiple"
export { ChartLineDots } from "@/registry/new-york-v4/charts/chart-line-dots"
export { ChartLineDotsCustom } from "@/registry/new-york-v4/charts/chart-line-dots-custom"
export { ChartLineDotsColors } from "@/registry/new-york-v4/charts/chart-line-dots-colors"
export { ChartLineLabel } from "@/registry/new-york-v4/charts/chart-line-label"
export { ChartLineLabelCustom } from "@/registry/new-york-v4/charts/chart-line-label-custom"
export { ChartLineInteractive } from "@/registry/new-york-v4/charts/chart-line-interactive"
export { ChartPieSimple } from "@/registry/new-york-v4/charts/chart-pie-simple"
export { ChartPieSeparatorNone } from "@/registry/new-york-v4/charts/chart-pie-separator-none"
export { ChartPieLabel } from "@/registry/new-york-v4/charts/chart-pie-label"
export { ChartPieLabelCustom } from "@/registry/new-york-v4/charts/chart-pie-label-custom"
export { ChartPieLabelList } from "@/registry/new-york-v4/charts/chart-pie-label-list"
export { ChartPieLegend } from "@/registry/new-york-v4/charts/chart-pie-legend"
export { ChartPieDonut } from "@/registry/new-york-v4/charts/chart-pie-donut"
export { ChartPieDonutActive } from "@/registry/new-york-v4/charts/chart-pie-donut-active"
export { ChartPieDonutText } from "@/registry/new-york-v4/charts/chart-pie-donut-text"
export { ChartPieStacked } from "@/registry/new-york-v4/charts/chart-pie-stacked"
export { ChartPieInteractive } from "@/registry/new-york-v4/charts/chart-pie-interactive"
export { ChartRadarDefault } from "@/registry/new-york-v4/charts/chart-radar-default"
export { ChartRadarDots } from "@/registry/new-york-v4/charts/chart-radar-dots"
export { ChartRadarLinesOnly } from "@/registry/new-york-v4/charts/chart-radar-lines-only"
export { ChartRadarLabelCustom } from "@/registry/new-york-v4/charts/chart-radar-label-custom"
export { ChartRadarGridCustom } from "@/registry/new-york-v4/charts/chart-radar-grid-custom"
export { ChartRadarGridNone } from "@/registry/new-york-v4/charts/chart-radar-grid-none"
export { ChartRadarGridCircle } from "@/registry/new-york-v4/charts/chart-radar-grid-circle"
export { ChartRadarGridCircleNoLines } from "@/registry/new-york-v4/charts/chart-radar-grid-circle-no-lines"
export { ChartRadarGridCircleFill } from "@/registry/new-york-v4/charts/chart-radar-grid-circle-fill"
export { ChartRadarGridFill } from "@/registry/new-york-v4/charts/chart-radar-grid-fill"
export { ChartRadarMultiple } from "@/registry/new-york-v4/charts/chart-radar-multiple"
export { ChartRadarLegend } from "@/registry/new-york-v4/charts/chart-radar-legend"
export { ChartRadarIcons } from "@/registry/new-york-v4/charts/chart-radar-icons"
export { ChartRadarRadius } from "@/registry/new-york-v4/charts/chart-radar-radius"
export { ChartRadialSimple } from "@/registry/new-york-v4/charts/chart-radial-simple"
export { ChartRadialLabel } from "@/registry/new-york-v4/charts/chart-radial-label"
export { ChartRadialGrid } from "@/registry/new-york-v4/charts/chart-radial-grid"
export { ChartRadialText } from "@/registry/new-york-v4/charts/chart-radial-text"
export { ChartRadialShape } from "@/registry/new-york-v4/charts/chart-radial-shape"
export { ChartRadialStacked } from "@/registry/new-york-v4/charts/chart-radial-stacked"
export { ChartTooltipDefault } from "@/registry/new-york-v4/charts/chart-tooltip-default"
export { ChartTooltipIndicatorLine } from "@/registry/new-york-v4/charts/chart-tooltip-indicator-line"
export { ChartTooltipIndicatorNone } from "@/registry/new-york-v4/charts/chart-tooltip-indicator-none"
export { ChartTooltipLabelCustom } from "@/registry/new-york-v4/charts/chart-tooltip-label-custom"
export { ChartTooltipLabelFormatter } from "@/registry/new-york-v4/charts/chart-tooltip-label-formatter"
export { ChartTooltipLabelNone } from "@/registry/new-york-v4/charts/chart-tooltip-label-none"
export { ChartTooltipFormatter } from "@/registry/new-york-v4/charts/chart-tooltip-formatter"
export { ChartTooltipIcons } from "@/registry/new-york-v4/charts/chart-tooltip-icons"
export { ChartTooltipAdvanced } from "@/registry/new-york-v4/charts/chart-tooltip-advanced"

View File

@@ -0,0 +1,20 @@
import { ComponentWrapper } from "@/components/component-wrapper"
import * as Charts from "@/app/(app)/charts/charts"
export default function ChartsPage() {
return (
<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]) => (
<ComponentWrapper
key={key}
name={key}
className="w-auto data-[name=chartareainteractive]:col-span-3 data-[name=chartbarinteractive]:col-span-3 data-[name=chartlineinteractive]:col-span-3 **:data-[slot=card]:w-full"
>
<Component />
</ComponentWrapper>
))}
</div>
)
}

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

@@ -0,0 +1,42 @@
import { cookies } from "next/headers"
import { AppSidebar } from "@/components/app-sidebar"
import { ModeSwitcher } from "@/components/mode-switcher"
import { NavHeader } from "@/components/nav-header"
import { Separator } from "@/registry/new-york-v4/ui/separator"
import {
SidebarInset,
SidebarProvider,
SidebarTrigger,
} from "@/registry/new-york-v4/ui/sidebar"
export default async function AppLayout({
children,
}: Readonly<{
children: React.ReactNode
}>) {
const cookieStore = await cookies()
const defaultOpen = cookieStore.get("sidebar_state")?.value === "true"
return (
<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>
</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>
)
}

196
apps/v4/app/(app)/page.tsx Normal file
View File

@@ -0,0 +1,196 @@
import { AccordionDemo } from "@/components/accordion-demo"
import { AlertDemo } from "@/components/alert-demo"
import { AlertDialogDemo } from "@/components/alert-dialog-demo"
import { AspectRatioDemo } from "@/components/aspect-ratio-demo"
import { AvatarDemo } from "@/components/avatar-demo"
import { BadgeDemo } from "@/components/badge-demo"
import { BreadcrumbDemo } from "@/components/breadcrumb-demo"
import { ButtonDemo } from "@/components/button-demo"
import { CalendarDemo } from "@/components/calendar-demo"
import { CardDemo } from "@/components/card-demo"
import { CarouselDemo } from "@/components/carousel-demo"
import { ChartDemo } from "@/components/chart-demo"
import { CheckboxDemo } from "@/components/checkbox-demo"
import { CollapsibleDemo } from "@/components/collapsible-demo"
import { ComboboxDemo } from "@/components/combobox-demo"
import { CommandDemo } from "@/components/command-demo"
import { ComponentWrapper } from "@/components/component-wrapper"
import { ContextMenuDemo } from "@/components/context-menu-demo"
import { DatePickerDemo } from "@/components/date-picker-demo"
import { DialogDemo } from "@/components/dialog-demo"
import { DrawerDemo } from "@/components/drawer-demo"
import { DropdownMenuDemo } from "@/components/dropdown-menu-demo"
import { FormDemo } from "@/components/form-demo"
import { HoverCardDemo } from "@/components/hover-card-demo"
import { InputDemo } from "@/components/input-demo"
import { InputOTPDemo } from "@/components/input-otp-demo"
import { LabelDemo } from "@/components/label-demo"
import { MenubarDemo } from "@/components/menubar-demo"
import { NavigationMenuDemo } from "@/components/navigation-menu-demo"
import { PaginationDemo } from "@/components/pagination-demo"
import { PopoverDemo } from "@/components/popover-demo"
import { ProgressDemo } from "@/components/progress-demo"
import { RadioGroupDemo } from "@/components/radio-group-demo"
import { ResizableDemo } from "@/components/resizable-demo"
import { ScrollAreaDemo } from "@/components/scroll-area-demo"
import { SelectDemo } from "@/components/select-demo"
import { SeparatorDemo } from "@/components/separator-demo"
import { SheetDemo } from "@/components/sheet-demo"
import { SkeletonDemo } from "@/components/skeleton-demo"
import { SliderDemo } from "@/components/slider-demo"
import { SonnerDemo } from "@/components/sonner-demo"
import { SwitchDemo } from "@/components/switch-demo"
import { TableDemo } from "@/components/table-demo"
import { TabsDemo } from "@/components/tabs-demo"
import { TextareaDemo } from "@/components/textarea-demo"
import { ToggleDemo } from "@/components/toggle-demo"
import { ToggleGroupDemo } from "@/components/toggle-group-demo"
import { TooltipDemo } from "@/components/tooltip-demo"
export default function SinkPage() {
return (
<div className="@container grid flex-1 gap-4 p-4">
<ComponentWrapper name="chart" className="w-full">
<ChartDemo />
</ComponentWrapper>
<ComponentWrapper name="accordion">
<AccordionDemo />
</ComponentWrapper>
<ComponentWrapper name="alert">
<AlertDemo />
</ComponentWrapper>
<ComponentWrapper name="alert-dialog">
<AlertDialogDemo />
</ComponentWrapper>
<ComponentWrapper name="aspect-ratio">
<AspectRatioDemo />
</ComponentWrapper>
<ComponentWrapper name="avatar">
<AvatarDemo />
</ComponentWrapper>
<ComponentWrapper name="badge">
<BadgeDemo />
</ComponentWrapper>
<ComponentWrapper name="breadcrumb">
<BreadcrumbDemo />
</ComponentWrapper>
<ComponentWrapper name="button">
<ButtonDemo />
</ComponentWrapper>
<ComponentWrapper name="calendar">
<CalendarDemo />
</ComponentWrapper>
<ComponentWrapper name="card">
<CardDemo />
</ComponentWrapper>
<ComponentWrapper name="carousel">
<CarouselDemo />
</ComponentWrapper>
<ComponentWrapper name="checkbox">
<CheckboxDemo />
</ComponentWrapper>
<ComponentWrapper name="collapsible">
<CollapsibleDemo />
</ComponentWrapper>
<ComponentWrapper name="combobox">
<ComboboxDemo />
</ComponentWrapper>
<ComponentWrapper name="command">
<CommandDemo />
</ComponentWrapper>
<ComponentWrapper name="context-menu">
<ContextMenuDemo />
</ComponentWrapper>
<ComponentWrapper name="date-picker">
<DatePickerDemo />
</ComponentWrapper>
<ComponentWrapper name="dialog">
<DialogDemo />
</ComponentWrapper>
<ComponentWrapper name="drawer">
<DrawerDemo />
</ComponentWrapper>
<ComponentWrapper name="dropdown-menu">
<DropdownMenuDemo />
</ComponentWrapper>
<ComponentWrapper name="form">
<FormDemo />
</ComponentWrapper>
<ComponentWrapper name="hover-card">
<HoverCardDemo />
</ComponentWrapper>
<ComponentWrapper name="input">
<InputDemo />
</ComponentWrapper>
<ComponentWrapper name="input-otp">
<InputOTPDemo />
</ComponentWrapper>
<ComponentWrapper name="label">
<LabelDemo />
</ComponentWrapper>
<ComponentWrapper name="menubar">
<MenubarDemo />
</ComponentWrapper>
<ComponentWrapper name="navigation-menu">
<NavigationMenuDemo />
</ComponentWrapper>
<ComponentWrapper name="pagination">
<PaginationDemo />
</ComponentWrapper>
<ComponentWrapper name="popover">
<PopoverDemo />
</ComponentWrapper>
<ComponentWrapper name="progress">
<ProgressDemo />
</ComponentWrapper>
<ComponentWrapper name="radio-group">
<RadioGroupDemo />
</ComponentWrapper>
<ComponentWrapper name="resizable">
<ResizableDemo />
</ComponentWrapper>
<ComponentWrapper name="scroll-area">
<ScrollAreaDemo />
</ComponentWrapper>
<ComponentWrapper name="select">
<SelectDemo />
</ComponentWrapper>
<ComponentWrapper name="separator">
<SeparatorDemo />
</ComponentWrapper>
<ComponentWrapper name="sheet">
<SheetDemo />
</ComponentWrapper>
<ComponentWrapper name="skeleton">
<SkeletonDemo />
</ComponentWrapper>
<ComponentWrapper name="slider">
<SliderDemo />
</ComponentWrapper>
<ComponentWrapper name="sonner">
<SonnerDemo />
</ComponentWrapper>
<ComponentWrapper name="switch">
<SwitchDemo />
</ComponentWrapper>
<ComponentWrapper name="table">
<TableDemo />
</ComponentWrapper>
<ComponentWrapper name="tabs">
<TabsDemo />
</ComponentWrapper>
<ComponentWrapper name="textarea">
<TextareaDemo />
</ComponentWrapper>
<ComponentWrapper name="toggle">
<ToggleDemo />
</ComponentWrapper>
<ComponentWrapper name="toggle-group">
<ToggleGroupDemo />
</ComponentWrapper>
<ComponentWrapper name="tooltip">
<TooltipDemo />
</ComponentWrapper>
</div>
)
}

View File

@@ -0,0 +1,96 @@
import * as React from "react"
import { Metadata } from "next"
import { notFound } from "next/navigation"
import { registryItemSchema } from "shadcn/registry"
import { z } from "zod"
import { getRegistryComponent, getRegistryItem } from "@/lib/registry"
import { absoluteUrl, cn } from "@/lib/utils"
import { siteConfig } from "@/www/config/site"
const getCachedRegistryItem = React.cache(async (name: string) => {
return await getRegistryItem(name)
})
export async function generateMetadata({
params,
}: {
params: Promise<{
name: string
}>
}): Promise<Metadata> {
const { name } = await params
const item = await getCachedRegistryItem(name)
if (!item) {
return {}
}
const title = item.name
const description = item.description
return {
title: `${item.name}${item.description ? ` - ${item.description}` : ""}`,
description,
openGraph: {
title,
description,
type: "article",
url: absoluteUrl(`/blocks/${item.name}`),
images: [
{
url: siteConfig.ogImage,
width: 1200,
height: 630,
alt: siteConfig.name,
},
],
},
twitter: {
card: "summary_large_image",
title,
description,
images: [siteConfig.ogImage],
creator: "@shadcn",
},
}
}
export const dynamicParams = false
export async function generateStaticParams() {
const { Index } = await import("@/__registry__")
const index = z.record(registryItemSchema).parse(Index)
return Object.values(index)
.filter((block) =>
["registry:block", "registry:component"].includes(block.type)
)
.map((block) => ({
name: block.name,
}))
}
export default async function BlockPage({
params,
}: {
params: Promise<{
name: string
}>
}) {
const { name } = await params
const item = await getCachedRegistryItem(name)
const Component = getRegistryComponent(name)
if (!item || !Component) {
return notFound()
}
return (
<>
<div className={cn("themes-wrapper bg-background", item.meta?.container)}>
<Component />
</div>
</>
)
}

View File

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

199
apps/v4/app/globals.css Normal file
View File

@@ -0,0 +1,199 @@
@import "tailwindcss";
@plugin "tailwindcss-animate";
@custom-variant dark (&:is(.dark *));
:root {
--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: 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);
--color-card-foreground: var(--card-foreground);
--color-popover: var(--popover);
--color-popover-foreground: var(--popover-foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-secondary: var(--secondary);
--color-secondary-foreground: var(--secondary-foreground);
--color-muted: var(--muted);
--color-muted-foreground: var(--muted-foreground);
--color-accent: var(--accent);
--color-accent-foreground: var(--accent-foreground);
--color-destructive: var(--destructive);
--color-destructive-foreground: var(--destructive-foreground);
--color-border: var(--border);
--color-input: var(--input);
--color-ring: var(--ring);
--color-chart-1: var(--chart-1);
--color-chart-2: var(--chart-2);
--color-chart-3: var(--chart-3);
--color-chart-4: var(--chart-4);
--color-chart-5: var(--chart-5);
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
--color-sidebar: var(--sidebar);
--color-sidebar-foreground: var(--sidebar-foreground);
--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;
@keyframes accordion-down {
from {
height: 0;
}
to {
height: var(--radix-accordion-content-height);
}
}
@keyframes accordion-up {
from {
height: var(--radix-accordion-content-height);
}
to {
height: 0;
}
}
}
@layer base {
* {
@apply border-border outline-ring/50;
}
body {
@apply bg-background text-foreground;
}
}

123
apps/v4/app/layout.tsx Normal file
View File

@@ -0,0 +1,123 @@
import type { Metadata, Viewport } from "next"
import { Geist_Mono as FontMono, Inter as FontSans } from "next/font/google"
import { cn } from "@/lib/utils"
import { Analytics } from "@/components/analytics"
import { ThemeProvider } from "@/components/theme-provider"
import { Toaster } from "@/registry/new-york-v4/ui/sonner"
import { siteConfig } from "@/www/config/site"
import "./globals.css"
const fontSans = FontSans({
subsets: ["latin"],
variable: "--font-sans",
})
const fontMono = FontMono({
subsets: ["latin"],
variable: "--font-mono",
})
const META_THEME_COLORS = {
light: "#ffffff",
dark: "#09090b",
}
export const metadata: Metadata = {
title: {
default: siteConfig.name,
template: `%s - ${siteConfig.name}`,
},
metadataBase: new URL(siteConfig.url),
description: siteConfig.description,
keywords: [
"Next.js",
"React",
"Tailwind CSS",
"Server Components",
"Radix UI",
],
authors: [
{
name: "shadcn",
url: "https://shadcn.com",
},
],
creator: "shadcn",
openGraph: {
type: "website",
locale: "en_US",
url: siteConfig.url,
title: siteConfig.name,
description: siteConfig.description,
siteName: siteConfig.name,
images: [
{
url: siteConfig.ogImage,
width: 1200,
height: 630,
alt: siteConfig.name,
},
],
},
twitter: {
card: "summary_large_image",
title: siteConfig.name,
description: siteConfig.description,
images: [siteConfig.ogImage],
creator: "@shadcn",
},
icons: {
icon: "/favicon.ico",
shortcut: "/favicon-16x16.png",
apple: "/apple-touch-icon.png",
},
manifest: `${siteConfig.url}/site.webmanifest`,
}
export const viewport: Viewport = {
themeColor: META_THEME_COLORS.light,
}
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode
}>) {
return (
<html lang="en" suppressHydrationWarning>
<head>
<script
dangerouslySetInnerHTML={{
__html: `
try {
if (localStorage.theme === 'dark' || ((!('theme' in localStorage) || localStorage.theme === 'system') && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
document.querySelector('meta[name="theme-color"]').setAttribute('content', '${META_THEME_COLORS.dark}')
}
} catch (_) {}
`,
}}
/>
</head>
<body
className={cn(
"bg-background overscroll-none font-sans antialiased",
fontSans.variable,
fontMono.variable
)}
>
<ThemeProvider
attribute="class"
defaultTheme="system"
enableSystem
disableTransitionOnChange
>
{children}
<Toaster />
<Analytics />
</ThemeProvider>
</body>
</html>
)
}

21
apps/v4/components.json Normal file
View File

@@ -0,0 +1,21 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "",
"css": "app/globals.css",
"baseColor": "neutral",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/registry/new-york-v4/ui",
"lib": "@/lib",
"hooks": "@/hooks"
},
"iconLibrary": "lucide"
}

View File

@@ -0,0 +1,72 @@
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/registry/new-york-v4/ui/accordion"
export function AccordionDemo() {
return (
<div className="grid w-full max-w-xl gap-4">
<Accordion type="single" collapsible className="w-full">
<AccordionItem value="item-1">
<AccordionTrigger>Is it accessible?</AccordionTrigger>
<AccordionContent>
Yes. It adheres to the WAI-ARIA design pattern.
</AccordionContent>
</AccordionItem>
<AccordionItem value="item-2">
<AccordionTrigger>Is it styled?</AccordionTrigger>
<AccordionContent>
Yes. It comes with default styles that matches the other
components&apos; aesthetic.
</AccordionContent>
</AccordionItem>
<AccordionItem value="item-3">
<AccordionTrigger>Is it animated?</AccordionTrigger>
<AccordionContent>
Yes. It&apos;s animated by default, but you can disable it if you
prefer.
</AccordionContent>
</AccordionItem>
</Accordion>
<Accordion type="single" collapsible className="w-full">
<AccordionItem value="item-1">
<AccordionTrigger>
What are the key considerations when implementing a comprehensive
enterprise-level authentication system?
</AccordionTrigger>
<AccordionContent>
Implementing a robust enterprise authentication system requires
careful consideration of multiple factors. This includes secure
password hashing and storage, multi-factor authentication (MFA)
implementation, session management, OAuth2 and SSO integration,
regular security audits, rate limiting to prevent brute force
attacks, and maintaining detailed audit logs. Additionally,
you&apos;ll need to consider scalability, performance impact, and
compliance with relevant data protection regulations such as GDPR or
HIPAA.
</AccordionContent>
</AccordionItem>
<AccordionItem value="item-2">
<AccordionTrigger>
How does modern distributed system architecture handle eventual
consistency and data synchronization across multiple regions?
</AccordionTrigger>
<AccordionContent>
Modern distributed systems employ various strategies to maintain
data consistency across regions. This often involves using
techniques like CRDT (Conflict-Free Replicated Data Types), vector
clocks, and gossip protocols. Systems might implement event sourcing
patterns, utilize message queues for asynchronous updates, and
employ sophisticated conflict resolution strategies. Popular
solutions like Amazon&apos;s DynamoDB and Google&apos;s Spanner
demonstrate different approaches to solving these challenges,
balancing between consistency, availability, and partition tolerance
as described in the CAP theorem.
</AccordionContent>
</AccordionItem>
</Accordion>
</div>
)
}

View File

@@ -0,0 +1,115 @@
import {
AlertCircleIcon,
BookmarkCheckIcon,
CheckCircle2Icon,
GiftIcon,
PopcornIcon,
ShieldAlertIcon,
} from "lucide-react"
import {
Alert,
AlertDescription,
AlertTitle,
} from "@/registry/new-york-v4/ui/alert"
import { Button } from "@/registry/new-york-v4/ui/button"
export function AlertDemo() {
return (
<div className="grid max-w-xl items-start gap-4">
<Alert>
<CheckCircle2Icon />
<AlertTitle>Success! Your changes have been saved</AlertTitle>
<AlertDescription>
This is an alert with icon, title and description.
</AlertDescription>
</Alert>
<Alert>
<BookmarkCheckIcon>Heads up!</BookmarkCheckIcon>
<AlertDescription>
This one has an icon and a description only. No title.
</AlertDescription>
</Alert>
<Alert>
<AlertDescription>
This one has a description only. No title. No icon.
</AlertDescription>
</Alert>
<Alert>
<PopcornIcon />
<AlertTitle>Let&apos;s try one with icon and title.</AlertTitle>
</Alert>
<Alert>
<ShieldAlertIcon />
<AlertTitle>
This is a very long alert title that demonstrates how the component
handles extended text content and potentially wraps across multiple
lines
</AlertTitle>
</Alert>
<Alert>
<GiftIcon />
<AlertDescription>
This is a very long alert description that demonstrates how the
component handles extended text content and potentially wraps across
multiple lines
</AlertDescription>
</Alert>
<Alert>
<AlertCircleIcon />
<AlertTitle>
This is an extremely long alert title that spans multiple lines to
demonstrate how the component handles very lengthy headings while
maintaining readability and proper text wrapping behavior
</AlertTitle>
<AlertDescription>
This is an equally long description that contains detailed information
about the alert. It shows how the component can accommodate extensive
content while preserving proper spacing, alignment, and readability
across different screen sizes and viewport widths. This helps ensure
the user experience remains consistent regardless of the content
length.
</AlertDescription>
</Alert>
<Alert variant="destructive">
<AlertCircleIcon />
<AlertTitle>Something went wrong!</AlertTitle>
<AlertDescription>
Your session has expired. Please log in again.
</AlertDescription>
</Alert>
<Alert variant="destructive">
<AlertCircleIcon />
<AlertTitle>Unable to process your payment.</AlertTitle>
<AlertDescription>
<p>Please verify your billing information and try again.</p>
<ul className="list-inside list-disc text-sm">
<li>Check your card details</li>
<li>Ensure sufficient funds</li>
<li>Verify billing address</li>
</ul>
</AlertDescription>
</Alert>
<Alert>
<CheckCircle2Icon />
<AlertTitle className="max-w-[calc(100%-4rem)] overflow-ellipsis">
The selected emails have been marked as spam.
</AlertTitle>
<Button
size="sm"
variant="outline"
className="absolute top-2.5 right-3 h-6 shadow-none"
>
Undo
</Button>
</Alert>
<Alert className="border-amber-50 bg-amber-50 text-amber-900 dark:border-amber-950 dark:bg-amber-950 dark:text-amber-100">
<CheckCircle2Icon />
<AlertTitle>Plot Twist: This Alert is Actually Amber!</AlertTitle>
<AlertDescription>
This one has custom colors for light and dark mode.
</AlertDescription>
</Alert>
</div>
)
}

View File

@@ -0,0 +1,35 @@
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/registry/new-york-v4/ui/alert-dialog"
import { Button } from "@/registry/new-york-v4/ui/button"
export function AlertDialogDemo() {
return (
<AlertDialog>
<AlertDialogTrigger asChild>
<Button variant="outline">Show Dialog</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
<AlertDialogDescription>
This action cannot be undone. This will permanently delete your
account and remove your data from our servers.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction>Continue</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
)
}

View File

@@ -0,0 +1,7 @@
"use client"
import { Analytics as VercelAnalytics } from "@vercel/analytics/react"
export function Analytics() {
return <VercelAnalytics />
}

View File

@@ -0,0 +1,243 @@
"use client"
import * as React from "react"
import { Index } from "@/__registry__"
import {
AudioWaveform,
BookOpen,
Bot,
ChevronRightIcon,
Command,
GalleryVerticalEnd,
Search,
Settings2,
SquareTerminal,
} from "lucide-react"
import { NavUser } from "@/registry/new-york-v4/blocks/sidebar-07/components/nav-user"
import { TeamSwitcher } from "@/registry/new-york-v4/blocks/sidebar-07/components/team-switcher"
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "@/registry/new-york-v4/ui/collapsible"
import { Label } from "@/registry/new-york-v4/ui/label"
import {
Sidebar,
SidebarContent,
SidebarFooter,
SidebarGroup,
SidebarGroupContent,
SidebarGroupLabel,
SidebarHeader,
SidebarInput,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
SidebarMenuSub,
SidebarMenuSubButton,
SidebarMenuSubItem,
SidebarRail,
} from "@/registry/new-york-v4/ui/sidebar"
// This is sample data.
const data = {
user: {
name: "shadcn",
email: "m@example.com",
avatar: "/avatars/shadcn.jpg",
},
teams: [
{
name: "Acme Inc",
logo: GalleryVerticalEnd,
plan: "Enterprise",
},
{
name: "Acme Corp.",
logo: AudioWaveform,
plan: "Startup",
},
{
name: "Evil Corp.",
logo: Command,
plan: "Free",
},
],
navMain: [
{
title: "Playground",
url: "#",
icon: SquareTerminal,
isActive: true,
items: [
{
title: "History",
url: "#",
},
{
title: "Starred",
url: "#",
},
{
title: "Settings",
url: "#",
},
],
},
{
title: "Models",
url: "#",
icon: Bot,
items: [
{
title: "Genesis",
url: "#",
},
{
title: "Explorer",
url: "#",
},
{
title: "Quantum",
url: "#",
},
],
},
{
title: "Documentation",
url: "#",
icon: BookOpen,
items: [
{
title: "Introduction",
url: "#",
},
{
title: "Get Started",
url: "#",
},
{
title: "Tutorials",
url: "#",
},
{
title: "Changelog",
url: "#",
},
],
},
{
title: "Settings",
url: "#",
icon: Settings2,
items: [
{
title: "General",
url: "#",
},
{
title: "Team",
url: "#",
},
{
title: "Billing",
url: "#",
},
{
title: "Limits",
url: "#",
},
],
},
],
components: Object.values(Index).filter(
(item) => item.type === "registry:ui"
),
}
export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
return (
<Sidebar collapsible="icon" {...props}>
<SidebarHeader>
<TeamSwitcher teams={data.teams} />
<SidebarGroup className="py-0 group-data-[collapsible=icon]:hidden">
<SidebarGroupContent>
<form className="relative">
<Label htmlFor="search" className="sr-only">
Search
</Label>
<SidebarInput
id="search"
placeholder="Search the docs..."
className="pl-8"
/>
<Search className="pointer-events-none absolute top-1/2 left-2 size-4 -translate-y-1/2 opacity-50 select-none" />
</form>
</SidebarGroupContent>
</SidebarGroup>
</SidebarHeader>
<SidebarContent>
<SidebarGroup>
<SidebarGroupLabel>Platform</SidebarGroupLabel>
<SidebarMenu>
{data.navMain.map((item) => (
<Collapsible
key={item.title}
asChild
defaultOpen={item.isActive}
className="group/collapsible"
>
<SidebarMenuItem>
<CollapsibleTrigger asChild>
<SidebarMenuButton tooltip={item.title}>
{item.icon && <item.icon />}
<span>{item.title}</span>
<ChevronRightIcon className="ml-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90" />
</SidebarMenuButton>
</CollapsibleTrigger>
<CollapsibleContent>
<SidebarMenuSub>
{item.items?.map((subItem) => (
<SidebarMenuSubItem key={subItem.title}>
<SidebarMenuSubButton asChild>
<a href={subItem.url}>
<span>{subItem.title}</span>
</a>
</SidebarMenuSubButton>
</SidebarMenuSubItem>
))}
</SidebarMenuSub>
</CollapsibleContent>
</SidebarMenuItem>
</Collapsible>
))}
</SidebarMenu>
</SidebarGroup>
<SidebarGroup className="group-data-[collapsible=icon]:hidden">
<SidebarGroupLabel>Components</SidebarGroupLabel>
<SidebarMenu>
{data.components.map((item) => (
<SidebarMenuItem key={item.name}>
<SidebarMenuButton asChild>
<a href={`/#${item.name}`}>
<span>{getComponentName(item.name)}</span>
</a>
</SidebarMenuButton>
</SidebarMenuItem>
))}
</SidebarMenu>
</SidebarGroup>
</SidebarContent>
<SidebarFooter>
<NavUser user={data.user} />
</SidebarFooter>
<SidebarRail />
</Sidebar>
)
}
function getComponentName(name: string) {
// convert kebab-case to title case
return name.replace(/-/g, " ").replace(/\b\w/g, (char) => char.toUpperCase())
}

View File

@@ -0,0 +1,26 @@
import Image from "next/image"
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 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-lg object-cover dark:brightness-[0.2] dark:grayscale"
/>
</AspectRatio>
<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-lg object-cover dark:brightness-[0.2] dark:grayscale"
/>
</AspectRatio>
</div>
)
}

View File

@@ -0,0 +1,81 @@
import {
Avatar,
AvatarFallback,
AvatarImage,
} from "@/registry/new-york-v4/ui/avatar"
export function AvatarDemo() {
return (
<div className="flex flex-row flex-wrap items-center gap-4">
<Avatar>
<AvatarImage src="https://github.com/shadcn.png" alt="@shadcn" />
<AvatarFallback>CN</AvatarFallback>
</Avatar>
<Avatar>
<AvatarFallback>CN</AvatarFallback>
</Avatar>
<Avatar className="size-12">
<AvatarImage src="https://github.com/shadcn.png" alt="@shadcn" />
<AvatarFallback>CN</AvatarFallback>
</Avatar>
<Avatar className="rounded-lg">
<AvatarImage
src="https://github.com/evilrabbit.png"
alt="@evilrabbit"
/>
<AvatarFallback>ER</AvatarFallback>
</Avatar>
<div className="*:data-[slot=avatar]:ring-background flex -space-x-2 *:data-[slot=avatar]:ring-2 *:data-[slot=avatar]:grayscale">
<Avatar>
<AvatarImage src="https://github.com/shadcn.png" alt="@shadcn" />
<AvatarFallback>CN</AvatarFallback>
</Avatar>
<Avatar>
<AvatarImage src="https://github.com/leerob.png" alt="@leerob" />
<AvatarFallback>LR</AvatarFallback>
</Avatar>
<Avatar>
<AvatarImage
src="https://github.com/evilrabbit.png"
alt="@evilrabbit"
/>
<AvatarFallback>ER</AvatarFallback>
</Avatar>
</div>
<div className="*:data-[slot=avatar]:ring-background flex -space-x-2 *:data-[slot=avatar]:size-12 *:data-[slot=avatar]:ring-2 *:data-[slot=avatar]:grayscale">
<Avatar>
<AvatarImage src="https://github.com/shadcn.png" alt="@shadcn" />
<AvatarFallback>CN</AvatarFallback>
</Avatar>
<Avatar>
<AvatarImage src="https://github.com/leerob.png" alt="@leerob" />
<AvatarFallback>LR</AvatarFallback>
</Avatar>
<Avatar>
<AvatarImage
src="https://github.com/evilrabbit.png"
alt="@evilrabbit"
/>
<AvatarFallback>ER</AvatarFallback>
</Avatar>
</div>
<div className="*:data-[slot=avatar]:ring-background flex -space-x-2 hover:space-x-1 *:data-[slot=avatar]:size-12 *:data-[slot=avatar]:ring-2 *:data-[slot=avatar]:grayscale *:data-[slot=avatar]:transition-all *:data-[slot=avatar]:duration-300 *:data-[slot=avatar]:ease-in-out">
<Avatar>
<AvatarImage src="https://github.com/shadcn.png" alt="@shadcn" />
<AvatarFallback>CN</AvatarFallback>
</Avatar>
<Avatar>
<AvatarImage src="https://github.com/leerob.png" alt="@leerob" />
<AvatarFallback>LR</AvatarFallback>
</Avatar>
<Avatar>
<AvatarImage
src="https://github.com/evilrabbit.png"
alt="@evilrabbit"
/>
<AvatarFallback>ER</AvatarFallback>
</Avatar>
</div>
</div>
)
}

View File

@@ -0,0 +1,61 @@
import { AlertCircleIcon, ArrowRightIcon, CheckIcon } from "lucide-react"
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-wrap gap-2">
<Badge>Badge</Badge>
<Badge variant="secondary">Secondary</Badge>
<Badge variant="destructive">Destructive</Badge>
<Badge variant="outline">Outline</Badge>
<Badge variant="outline">
<CheckIcon />
Badge
</Badge>
<Badge variant="destructive">
<AlertCircleIcon />
Alert
</Badge>
<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-wrap gap-2">
<Badge asChild>
<a href="#">
Link <ArrowRightIcon />
</a>
</Badge>
<Badge asChild variant="secondary">
<a href="#">
Link <ArrowRightIcon />
</a>
</Badge>
<Badge asChild variant="destructive">
<a href="#">
Link <ArrowRightIcon />
</a>
</Badge>
<Badge asChild variant="outline">
<a href="#">
Link <ArrowRightIcon />
</a>
</Badge>
</div>
</div>
)
}

View File

@@ -0,0 +1,49 @@
import {
Breadcrumb,
BreadcrumbEllipsis,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator,
} from "@/registry/new-york-v4/ui/breadcrumb"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/registry/new-york-v4/ui/dropdown-menu"
export function BreadcrumbDemo() {
return (
<Breadcrumb>
<BreadcrumbList>
<BreadcrumbItem>
<BreadcrumbLink href="/">Home</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator />
<BreadcrumbItem>
<DropdownMenu>
<DropdownMenuTrigger className="flex items-center gap-1">
<BreadcrumbEllipsis className="h-4 w-4" />
<span className="sr-only">Toggle menu</span>
</DropdownMenuTrigger>
<DropdownMenuContent align="start">
<DropdownMenuItem>Documentation</DropdownMenuItem>
<DropdownMenuItem>Themes</DropdownMenuItem>
<DropdownMenuItem>GitHub</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</BreadcrumbItem>
<BreadcrumbSeparator />
<BreadcrumbItem>
<BreadcrumbLink href="/docs/components">Components</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator />
<BreadcrumbItem>
<BreadcrumbPage>Breadcrumb</BreadcrumbPage>
</BreadcrumbItem>
</BreadcrumbList>
</Breadcrumb>
)
}

View File

@@ -0,0 +1,84 @@
import { ArrowRightIcon, Loader2Icon, SendIcon } from "lucide-react"
import { Button } from "@/registry/new-york-v4/ui/button"
export function ButtonDemo() {
return (
<div className="flex flex-col gap-6">
<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>
<Button variant="destructive">Destructive</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="link">Link</Button>
<Button variant="outline">
<SendIcon /> Send
</Button>
<Button variant="outline">
Learn More <ArrowRightIcon />
</Button>
<Button disabled variant="outline">
<Loader2Icon className="animate-spin" />
Please wait
</Button>
</div>
<div className="flex flex-wrap items-center gap-2 md:flex-row">
<Button size="sm">Small</Button>
<Button variant="outline" size="sm">
Outline
</Button>
<Button variant="ghost" size="sm">
Ghost
</Button>
<Button variant="destructive" size="sm">
Destructive
</Button>
<Button variant="secondary" size="sm">
Secondary
</Button>
<Button variant="link" size="sm">
Link
</Button>
<Button variant="outline" size="sm">
<SendIcon /> Send
</Button>
<Button variant="outline" size="sm">
Learn More <ArrowRightIcon />
</Button>
<Button disabled size="sm" variant="outline">
<Loader2Icon className="animate-spin" />
Please wait
</Button>
</div>
<div className="flex flex-wrap items-center gap-2 md:flex-row">
<Button size="lg">Large</Button>
<Button variant="outline" size="lg">
Outline
</Button>
<Button variant="ghost" size="lg">
Ghost
</Button>
<Button variant="destructive" size="lg">
Destructive
</Button>
<Button variant="secondary" size="lg">
Secondary
</Button>
<Button variant="link" size="lg">
Link
</Button>
<Button variant="outline" size="lg">
<SendIcon /> Send
</Button>
<Button variant="outline" size="lg">
Learn More <ArrowRightIcon />
</Button>
<Button disabled size="lg" variant="outline">
<Loader2Icon className="animate-spin" />
Please wait
</Button>
</div>
</div>
)
}

View File

@@ -0,0 +1,47 @@
"use client"
import * as React from "react"
import { addDays } from "date-fns"
import { type DateRange } from "react-day-picker"
import { Calendar } from "@/registry/new-york-v4/ui/calendar"
export function CalendarDemo() {
const [date, setDate] = React.useState<Date | undefined>(new Date())
const [dateRange, setDateRange] = React.useState<DateRange | undefined>({
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 flex-wrap items-start gap-2 @md:flex-row">
<Calendar
mode="single"
selected={date}
onSelect={setDate}
className="rounded-md border shadow-sm"
/>
<Calendar
mode="range"
defaultMonth={dateRange?.from}
selected={dateRange}
onSelect={setDateRange}
numberOfMonths={2}
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

@@ -0,0 +1,191 @@
import Image from "next/image"
import { BathIcon, BedIcon, LandPlotIcon } from "lucide-react"
import {
Avatar,
AvatarFallback,
AvatarImage,
} from "@/registry/new-york-v4/ui/avatar"
import { Badge } from "@/registry/new-york-v4/ui/badge"
import { Button } from "@/registry/new-york-v4/ui/button"
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} 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 CardDemo() {
return (
<div className="flex flex-col items-start gap-4">
<form>
<Card>
<CardHeader>
<CardTitle>Login to your account</CardTitle>
<CardDescription>
Enter your email below to login to your account
</CardDescription>
</CardHeader>
<CardContent>
<div className="flex flex-col gap-6">
<div className="grid gap-2">
<Label htmlFor="email">Email</Label>
<Input
id="email"
type="email"
placeholder="m@example.com"
required
/>
</div>
<div className="grid gap-2">
<div className="flex items-center">
<Label htmlFor="password">Password</Label>
<a
href="#"
className="ml-auto inline-block text-sm underline-offset-4 hover:underline"
>
Forgot your password?
</a>
</div>
<Input id="password" type="password" required />
</div>
</div>
</CardContent>
<CardFooter className="flex-col gap-2">
<Button type="submit" className="w-full">
Login
</Button>
<Button variant="outline" className="w-full">
Login with Google
</Button>
<div className="mt-4 text-center text-sm">
Don&apos;t have an account?{" "}
<a href="#" className="underline underline-offset-4">
Sign up
</a>
</div>
</CardFooter>
</Card>
</form>
<Card>
<CardHeader>
<CardTitle>Meeting Notes</CardTitle>
<CardDescription>
Transcript from the meeting with the client.
</CardDescription>
</CardHeader>
<CardContent className="text-sm">
<p>
Client requested dashboard redesign with focus on mobile
responsiveness.
</p>
<ol className="mt-4 flex list-decimal flex-col gap-2 pl-6">
<li>New analytics widgets for daily/weekly metrics</li>
<li>Simplified navigation menu</li>
<li>Dark mode support</li>
<li>Timeline: 6 weeks</li>
<li>Follow-up meeting scheduled for next Tuesday</li>
</ol>
</CardContent>
<CardFooter>
<div className="*:data-[slot=avatar]:ring-background flex -space-x-2 *:data-[slot=avatar]:ring-2 *:data-[slot=avatar]:grayscale">
<Avatar>
<AvatarImage src="https://github.com/shadcn.png" alt="@shadcn" />
<AvatarFallback>CN</AvatarFallback>
</Avatar>
<Avatar>
<AvatarImage src="https://github.com/leerob.png" alt="@leerob" />
<AvatarFallback>LR</AvatarFallback>
</Avatar>
<Avatar>
<AvatarImage
src="https://github.com/evilrabbit.png"
alt="@evilrabbit"
/>
<AvatarFallback>ER</AvatarFallback>
</Avatar>
</div>
</CardFooter>
</Card>
<Card>
<CardHeader>
<CardTitle>Is this an image?</CardTitle>
<CardDescription>This is a card with an image.</CardDescription>
</CardHeader>
<CardContent className="px-0">
<Image
src="https://images.unsplash.com/photo-1588345921523-c2dcdb7f1dcd?w=800&dpr=2&q=80"
alt="Photo by Drew Beamer"
className="aspect-video object-cover"
width={500}
height={500}
/>
</CardContent>
<CardFooter className="flex items-center gap-2">
<Badge variant="outline">
<BedIcon /> 4
</Badge>
<Badge variant="outline">
<BathIcon /> 2
</Badge>
<Badge variant="outline">
<LandPlotIcon /> 350m²
</Badge>
<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

@@ -0,0 +1,73 @@
import * as React from "react"
import { Card, CardContent } from "@/registry/new-york-v4/ui/card"
import {
Carousel,
CarouselContent,
CarouselItem,
CarouselNext,
CarouselPrevious,
} from "@/registry/new-york-v4/ui/carousel"
export function CarouselDemo() {
return (
<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) => (
<CarouselItem key={index}>
<div className="p-1">
<Card>
<CardContent className="flex aspect-square items-center justify-center p-6">
<span className="text-4xl font-semibold">{index + 1}</span>
</CardContent>
</Card>
</div>
</CarouselItem>
))}
</CarouselContent>
<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={{
align: "start",
}}
>
<CarouselContent>
{Array.from({ length: 5 }).map((_, index) => (
<CarouselItem key={index} className="md:basis-1/2 lg:basis-1/3">
<div className="p-1">
<Card>
<CardContent className="flex aspect-square items-center justify-center p-6">
<span className="text-3xl font-semibold">{index + 1}</span>
</CardContent>
</Card>
</div>
</CarouselItem>
))}
</CarouselContent>
<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">
<CarouselContent className="-ml-1">
{Array.from({ length: 5 }).map((_, index) => (
<CarouselItem key={index} className="pl-1 md:basis-1/2">
<div className="p-1">
<Card>
<CardContent className="flex aspect-square items-center justify-center p-6">
<span className="text-2xl font-semibold">{index + 1}</span>
</CardContent>
</Card>
</div>
</CarouselItem>
))}
</CarouselContent>
<CarouselPrevious />
<CarouselNext />
</Carousel>
</div>
)
}

View File

@@ -0,0 +1,94 @@
"use client"
import { TrendingUp } from "lucide-react"
import { Area, AreaChart, CartesianGrid, XAxis } 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 simple area chart"
const chartData = [
{ month: "January", desktop: 186 },
{ month: "February", desktop: 305 },
{ month: "March", desktop: 237 },
{ month: "April", desktop: 73 },
{ month: "May", desktop: 209 },
{ month: "June", desktop: 214 },
]
const chartConfig = {
desktop: {
label: "Desktop",
color: "var(--chart-1)",
},
} satisfies ChartConfig
export function ChartAreaDemo() {
return (
<Card>
<CardHeader>
<CardTitle>Area Chart</CardTitle>
<CardDescription>
Showing total visitors for the last 6 months
</CardDescription>
</CardHeader>
<CardContent>
<ChartContainer config={chartConfig}>
<AreaChart
accessibilityLayer
data={chartData}
margin={{
left: 12,
right: 12,
}}
>
<CartesianGrid vertical={false} />
<XAxis
dataKey="month"
tickLine={false}
axisLine={false}
tickMargin={8}
tickFormatter={(value) => value.slice(0, 3)}
/>
<ChartTooltip
cursor={false}
content={<ChartTooltipContent indicator="line" />}
/>
<Area
dataKey="desktop"
type="natural"
fill="var(--color-desktop)"
fillOpacity={0.4}
stroke="var(--color-desktop)"
/>
</AreaChart>
</ChartContainer>
</CardContent>
<CardFooter>
<div className="flex w-full items-start gap-2 text-sm">
<div className="grid gap-2">
<div className="flex items-center 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 flex items-center gap-2 leading-none">
January - June 2024
</div>
</div>
</div>
</CardFooter>
</Card>
)
}

View File

@@ -0,0 +1,80 @@
"use client"
import { TrendingUp } from "lucide-react"
import { Bar, BarChart, CartesianGrid, XAxis } 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 multiple bar chart"
const chartData = [
{ month: "January", desktop: 186, mobile: 80 },
{ month: "February", desktop: 305, mobile: 200 },
{ month: "March", desktop: 237, mobile: 120 },
{ month: "April", desktop: 73, mobile: 190 },
{ month: "May", desktop: 209, mobile: 130 },
{ month: "June", desktop: 214, mobile: 140 },
]
const chartConfig = {
desktop: {
label: "Desktop",
color: "var(--chart-1)",
},
mobile: {
label: "Mobile",
color: "var(--chart-2)",
},
} satisfies ChartConfig
export function ChartBarDemo() {
return (
<Card>
<CardHeader>
<CardTitle>Bar Chart - Multiple</CardTitle>
<CardDescription>January - June 2024</CardDescription>
</CardHeader>
<CardContent>
<ChartContainer config={chartConfig}>
<BarChart accessibilityLayer data={chartData}>
<CartesianGrid vertical={false} />
<XAxis
dataKey="month"
tickLine={false}
tickMargin={10}
axisLine={false}
tickFormatter={(value) => value.slice(0, 3)}
/>
<ChartTooltip
cursor={false}
content={<ChartTooltipContent indicator="dashed" />}
/>
<Bar dataKey="desktop" fill="var(--color-desktop)" radius={4} />
<Bar dataKey="mobile" fill="var(--color-mobile)" radius={4} />
</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

@@ -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

@@ -0,0 +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="grid w-full max-w-screen-2xl gap-4 *:data-[slot=card]:flex-1 @2xl:grid-cols-2 @6xl:grid-cols-3">
<ChartAreaDemo />
<ChartBarDemo />
<ChartBarMixed />
<div className="@6xl:hidden">
<ChartLineDemo />
</div>
</div>
)
}

View File

@@ -0,0 +1,100 @@
"use client"
import { TrendingUp } from "lucide-react"
import { CartesianGrid, Line, LineChart, XAxis } 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 multiple line chart"
const chartData = [
{ month: "January", desktop: 186, mobile: 80 },
{ month: "February", desktop: 305, mobile: 200 },
{ month: "March", desktop: 237, mobile: 120 },
{ month: "April", desktop: 73, mobile: 190 },
{ month: "May", desktop: 209, mobile: 130 },
{ month: "June", desktop: 214, mobile: 140 },
]
const chartConfig = {
desktop: {
label: "Desktop",
color: "var(--chart-1)",
},
mobile: {
label: "Mobile",
color: "var(--chart-2)",
},
} satisfies ChartConfig
export function ChartLineDemo() {
return (
<Card>
<CardHeader>
<CardTitle>Line Chart - Multiple</CardTitle>
<CardDescription>January - June 2024</CardDescription>
</CardHeader>
<CardContent>
<ChartContainer config={chartConfig}>
<LineChart
accessibilityLayer
data={chartData}
margin={{
left: 12,
right: 12,
}}
>
<CartesianGrid vertical={false} />
<XAxis
dataKey="month"
tickLine={false}
axisLine={false}
tickMargin={8}
tickFormatter={(value) => value.slice(0, 3)}
/>
<ChartTooltip cursor={false} content={<ChartTooltipContent />} />
<Line
dataKey="desktop"
type="monotone"
stroke="var(--color-desktop)"
strokeWidth={2}
dot={false}
/>
<Line
dataKey="mobile"
type="monotone"
stroke="var(--color-mobile)"
strokeWidth={2}
dot={false}
/>
</LineChart>
</ChartContainer>
</CardContent>
<CardFooter>
<div className="flex w-full items-start gap-2 text-sm">
<div className="grid gap-2">
<div className="flex items-center 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 flex items-center gap-2 leading-none">
Showing total visitors for the last 6 months
</div>
</div>
</div>
</CardFooter>
</Card>
)
}

View File

@@ -0,0 +1,43 @@
"use client"
import { Checkbox } from "@/registry/new-york-v4/ui/checkbox"
import { Label } from "@/registry/new-york-v4/ui/label"
export function CheckboxDemo() {
return (
<div className="flex flex-col gap-6">
<div className="flex items-center gap-3">
<Checkbox id="terms" />
<Label htmlFor="terms">Accept terms and conditions</Label>
</div>
<div className="flex items-start gap-3">
<Checkbox id="terms-2" defaultChecked />
<div className="grid gap-2">
<Label htmlFor="terms-2">Accept terms and conditions</Label>
<p className="text-muted-foreground text-sm">
By clicking this checkbox, you agree to the terms and conditions.
</p>
</div>
</div>
<div className="flex items-start gap-3">
<Checkbox id="toggle" disabled />
<Label htmlFor="toggle">Enable notifications</Label>
</div>
<Label className="hover:bg-accent/50 flex items-start gap-3 rounded-lg border p-3 has-[[aria-checked=true]]:border-blue-600 has-[[aria-checked=true]]:bg-blue-50 dark:has-[[aria-checked=true]]:border-blue-900 dark:has-[[aria-checked=true]]:bg-blue-950">
<Checkbox
id="toggle-2"
defaultChecked
className="data-[state=checked]:border-blue-600 data-[state=checked]:bg-blue-600 data-[state=checked]:text-white dark:data-[state=checked]:border-blue-700 dark:data-[state=checked]:bg-blue-700"
/>
<div className="grid gap-1.5 font-normal">
<p className="text-sm leading-none font-medium">
Enable notifications
</p>
<p className="text-muted-foreground text-sm">
You can enable or disable notifications at any time.
</p>
</div>
</Label>
</div>
)
}

View File

@@ -0,0 +1,46 @@
"use client"
import * as React from "react"
import { ChevronsUpDown } from "lucide-react"
import { Button } from "@/registry/new-york-v4/ui/button"
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "@/registry/new-york-v4/ui/collapsible"
export function CollapsibleDemo() {
const [isOpen, setIsOpen] = React.useState(false)
return (
<Collapsible
open={isOpen}
onOpenChange={setIsOpen}
className="flex w-full flex-col gap-2 md:w-[350px]"
>
<div className="flex items-center justify-between gap-4 px-4">
<h4 className="line-clamp-1 text-sm font-semibold">
@peduarte starred 3 repositories
</h4>
<CollapsibleTrigger asChild>
<Button variant="ghost" size="sm">
<ChevronsUpDown className="h-4 w-4" />
<span className="sr-only">Toggle</span>
</Button>
</CollapsibleTrigger>
</div>
<div className="rounded-md border px-4 py-2 font-mono text-sm shadow-xs">
@radix-ui/primitives
</div>
<CollapsibleContent className="flex flex-col gap-2">
<div className="rounded-md border px-4 py-2 font-mono text-sm shadow-xs">
@radix-ui/colors
</div>
<div className="rounded-md border px-4 py-2 font-mono text-sm shadow-xs">
@stitches/react
</div>
</CollapsibleContent>
</Collapsible>
)
}

View File

@@ -0,0 +1,344 @@
"use client"
import * as React from "react"
import {
CheckIcon,
ChevronDownIcon,
ChevronsUpDown,
PlusCircleIcon,
} from "lucide-react"
import { cn } from "@/lib/utils"
import {
Avatar,
AvatarFallback,
AvatarImage,
} from "@/registry/new-york-v4/ui/avatar"
import { Button } from "@/registry/new-york-v4/ui/button"
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
CommandSeparator,
} from "@/registry/new-york-v4/ui/command"
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/registry/new-york-v4/ui/popover"
const frameworks = [
{
value: "next.js",
label: "Next.js",
},
{
value: "sveltekit",
label: "SvelteKit",
},
{
value: "nuxt.js",
label: "Nuxt.js",
},
{
value: "remix",
label: "Remix",
},
{
value: "astro",
label: "Astro",
},
]
type Framework = (typeof frameworks)[number]
const users = [
{
id: "1",
username: "shadcn",
},
{
id: "2",
username: "leerob",
},
{
id: "3",
username: "evilrabbit",
},
] as const
type User = (typeof users)[number]
const timezones = [
{
label: "Americas",
timezones: [
{ value: "America/New_York", label: "(GMT-5) New York" },
{ value: "America/Los_Angeles", label: "(GMT-8) Los Angeles" },
{ value: "America/Chicago", label: "(GMT-6) Chicago" },
{ value: "America/Toronto", label: "(GMT-5) Toronto" },
{ value: "America/Vancouver", label: "(GMT-8) Vancouver" },
{ value: "America/Sao_Paulo", label: "(GMT-3) São Paulo" },
],
},
{
label: "Europe",
timezones: [
{ value: "Europe/London", label: "(GMT+0) London" },
{ value: "Europe/Paris", label: "(GMT+1) Paris" },
{ value: "Europe/Berlin", label: "(GMT+1) Berlin" },
{ value: "Europe/Rome", label: "(GMT+1) Rome" },
{ value: "Europe/Madrid", label: "(GMT+1) Madrid" },
{ value: "Europe/Amsterdam", label: "(GMT+1) Amsterdam" },
],
},
{
label: "Asia/Pacific",
timezones: [
{ value: "Asia/Tokyo", label: "(GMT+9) Tokyo" },
{ value: "Asia/Shanghai", label: "(GMT+8) Shanghai" },
{ value: "Asia/Singapore", label: "(GMT+8) Singapore" },
{ value: "Asia/Dubai", label: "(GMT+4) Dubai" },
{ value: "Australia/Sydney", label: "(GMT+11) Sydney" },
{ value: "Asia/Seoul", label: "(GMT+9) Seoul" },
],
},
] as const
type Timezone = (typeof timezones)[number]
export function ComboboxDemo() {
return (
<div className="flex w-full flex-wrap items-start gap-4">
<FrameworkCombobox frameworks={[...frameworks]} />
<UserCombobox users={[...users]} selectedUserId={users[0].id} />
<TimezoneCombobox
timezones={[...timezones]}
selectedTimezone={timezones[0].timezones[0]}
/>
</div>
)
}
function FrameworkCombobox({ frameworks }: { frameworks: Framework[] }) {
const [open, setOpen] = React.useState(false)
const [value, setValue] = React.useState("")
return (
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<Button
variant="outline"
role="combobox"
aria-expanded={open}
className="w-full justify-between md:max-w-[200px]"
>
{value
? frameworks.find((framework) => framework.value === value)?.label
: "Select framework..."}
<ChevronsUpDown className="text-muted-foreground" />
</Button>
</PopoverTrigger>
<PopoverContent className="w-(--radix-popover-trigger-width) p-0">
<Command>
<CommandInput placeholder="Search framework..." />
<CommandList>
<CommandEmpty>No framework found.</CommandEmpty>
<CommandGroup>
{frameworks.map((framework) => (
<CommandItem
key={framework.value}
value={framework.value}
onSelect={(currentValue) => {
setValue(currentValue === value ? "" : currentValue)
setOpen(false)
}}
>
{framework.label}
<CheckIcon
className={cn(
"ml-auto",
value === framework.value ? "opacity-100" : "opacity-0"
)}
/>
</CommandItem>
))}
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
)
}
function UserCombobox({
users,
selectedUserId,
}: {
users: User[]
selectedUserId: string
}) {
const [open, setOpen] = React.useState(false)
const [value, setValue] = React.useState(selectedUserId)
const selectedUser = React.useMemo(
() => users.find((user) => user.id === value),
[value, users]
)
return (
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<Button
variant="outline"
role="combobox"
aria-expanded={open}
className="w-full justify-between px-2 md:max-w-[200px]"
>
{selectedUser ? (
<div className="flex items-center gap-2">
<Avatar className="size-5">
<AvatarImage
src={`https://github.com/${selectedUser.username}.png`}
/>
<AvatarFallback>{selectedUser.username[0]}</AvatarFallback>
</Avatar>
{selectedUser.username}
</div>
) : (
"Select user..."
)}
<ChevronsUpDown className="text-muted-foreground" />
</Button>
</PopoverTrigger>
<PopoverContent className="w-(--radix-popover-trigger-width) p-0">
<Command>
<CommandInput placeholder="Search user..." />
<CommandList>
<CommandEmpty>No user found.</CommandEmpty>
<CommandGroup>
{users.map((user) => (
<CommandItem
key={user.id}
value={user.id}
onSelect={(currentValue) => {
setValue(currentValue === value ? "" : currentValue)
setOpen(false)
}}
>
<Avatar className="size-5">
<AvatarImage
src={`https://github.com/${user.username}.png`}
/>
<AvatarFallback>{user.username[0]}</AvatarFallback>
</Avatar>
{user.username}
<CheckIcon
className={cn(
"ml-auto",
value === user.id ? "opacity-100" : "opacity-0"
)}
/>
</CommandItem>
))}
</CommandGroup>
<CommandSeparator />
<CommandGroup>
<CommandItem>
<PlusCircleIcon />
Create user
</CommandItem>
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
)
}
function TimezoneCombobox({
timezones,
selectedTimezone,
}: {
timezones: Timezone[]
selectedTimezone: Timezone["timezones"][number]
}) {
const [open, setOpen] = React.useState(false)
const [value, setValue] = React.useState(selectedTimezone.value)
const selectedGroup = React.useMemo(
() =>
timezones.find((group) =>
group.timezones.find((tz) => tz.value === value)
),
[value, timezones]
)
const selectedTimezoneLabel = React.useMemo(
() => selectedGroup?.timezones.find((tz) => tz.value === value)?.label,
[value, selectedGroup]
)
return (
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<Button
variant="outline"
className="h-12 w-full justify-between px-2.5 md:max-w-[200px]"
>
{selectedTimezone ? (
<div className="flex flex-col items-start gap-0.5">
<span className="text-muted-foreground text-xs font-normal">
{selectedGroup?.label}
</span>
<span>{selectedTimezoneLabel}</span>
</div>
) : (
"Select timezone"
)}
<ChevronDownIcon className="text-muted-foreground" />
</Button>
</PopoverTrigger>
<PopoverContent className="p-0" align="start">
<Command>
<CommandInput placeholder="Search timezone..." />
<CommandList className="scroll-pb-12">
<CommandEmpty>No timezone found.</CommandEmpty>
{timezones.map((region) => (
<CommandGroup key={region.label} heading={region.label}>
{region.timezones.map((timezone) => (
<CommandItem
key={timezone.value}
value={timezone.value}
onSelect={(currentValue) => {
setValue(
currentValue as Timezone["timezones"][number]["value"]
)
setOpen(false)
}}
>
{timezone.label}
<CheckIcon
className="ml-auto opacity-0 data-[selected=true]:opacity-100"
data-selected={value === timezone.value}
/>
</CommandItem>
))}
</CommandGroup>
))}
<CommandSeparator className="sticky bottom-10" />
<CommandGroup className="bg-popover sticky bottom-0">
<CommandItem>
<PlusCircleIcon />
Create timezone
</CommandItem>
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
)
}

View File

@@ -0,0 +1,87 @@
"use client"
import * as React from "react"
import {
Calculator,
Calendar,
CreditCard,
Settings,
Smile,
User,
} from "lucide-react"
import {
CommandDialog,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
CommandSeparator,
CommandShortcut,
} from "@/registry/new-york-v4/ui/command"
export function CommandDemo() {
const [open, setOpen] = React.useState(false)
React.useEffect(() => {
const down = (e: KeyboardEvent) => {
if (e.key === "j" && (e.metaKey || e.ctrlKey)) {
e.preventDefault()
setOpen((open) => !open)
}
}
document.addEventListener("keydown", down)
return () => document.removeEventListener("keydown", down)
}, [])
return (
<>
<p className="text-muted-foreground text-sm">
Press{" "}
<kbd className="bg-muted text-muted-foreground pointer-events-none inline-flex h-5 items-center gap-1 rounded border px-1.5 font-mono text-[10px] font-medium opacity-100 select-none">
<span className="text-xs"></span>J
</kbd>
</p>
<CommandDialog open={open} onOpenChange={setOpen}>
<CommandInput placeholder="Type a command or search..." />
<CommandList>
<CommandEmpty>No results found.</CommandEmpty>
<CommandGroup heading="Suggestions">
<CommandItem>
<Calendar />
<span>Calendar</span>
</CommandItem>
<CommandItem>
<Smile />
<span>Search Emoji</span>
</CommandItem>
<CommandItem>
<Calculator />
<span>Calculator</span>
</CommandItem>
</CommandGroup>
<CommandSeparator />
<CommandGroup heading="Settings">
<CommandItem>
<User />
<span>Profile</span>
<CommandShortcut>P</CommandShortcut>
</CommandItem>
<CommandItem>
<CreditCard />
<span>Billing</span>
<CommandShortcut>B</CommandShortcut>
</CommandItem>
<CommandItem>
<Settings />
<span>Settings</span>
<CommandShortcut>S</CommandShortcut>
</CommandItem>
</CommandGroup>
</CommandList>
</CommandDialog>
</>
)
}

View File

@@ -0,0 +1,66 @@
"use client"
import * as React from "react"
import { cn } from "@/registry/new-york-v4/lib/utils"
export function ComponentWrapper({
className,
name,
children,
...props
}: React.ComponentPropsWithoutRef<"div"> & { name: string }) {
return (
<ComponentErrorBoundary name={name}>
<div
id={name}
data-name={name.toLowerCase()}
className={cn(
"flex w-full scroll-mt-16 flex-col rounded-lg border",
className
)}
{...props}
>
<div className="border-b px-4 py-3">
<div className="text-sm font-medium">{getComponentName(name)}</div>
</div>
<div className="flex flex-1 items-center gap-2 p-4">{children}</div>
</div>
</ComponentErrorBoundary>
)
}
class ComponentErrorBoundary extends React.Component<
{ children: React.ReactNode; name: string },
{ hasError: boolean }
> {
constructor(props: { children: React.ReactNode; name: string }) {
super(props)
this.state = { hasError: false }
}
static getDerivedStateFromError() {
return { hasError: true }
}
componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
console.error(`Error in component ${this.props.name}:`, error, errorInfo)
}
render() {
if (this.state.hasError) {
return (
<div className="p-4 text-red-500">
Something went wrong in component: {this.props.name}
</div>
)
}
return this.props.children
}
}
function getComponentName(name: string) {
// convert kebab-case to title case
return name.replace(/-/g, " ").replace(/\b\w/g, (char) => char.toUpperCase())
}

View File

@@ -0,0 +1,79 @@
import { Code2Icon, PlusIcon, TrashIcon } from "lucide-react"
import {
ContextMenu,
ContextMenuCheckboxItem,
ContextMenuContent,
ContextMenuItem,
ContextMenuLabel,
ContextMenuRadioGroup,
ContextMenuRadioItem,
ContextMenuSeparator,
ContextMenuShortcut,
ContextMenuSub,
ContextMenuSubContent,
ContextMenuSubTrigger,
ContextMenuTrigger,
} from "@/registry/new-york-v4/ui/context-menu"
export function ContextMenuDemo() {
return (
<ContextMenu>
<ContextMenuTrigger className="flex h-[150px] w-[300px] items-center justify-center rounded-md border border-dashed text-sm">
Right click here
</ContextMenuTrigger>
<ContextMenuContent className="w-64">
<ContextMenuItem inset>
Back
<ContextMenuShortcut>[</ContextMenuShortcut>
</ContextMenuItem>
<ContextMenuItem inset disabled>
Forward
<ContextMenuShortcut>]</ContextMenuShortcut>
</ContextMenuItem>
<ContextMenuItem inset>
Reload
<ContextMenuShortcut>R</ContextMenuShortcut>
</ContextMenuItem>
<ContextMenuSub>
<ContextMenuSubTrigger inset>More Tools</ContextMenuSubTrigger>
<ContextMenuSubContent className="w-48">
<ContextMenuItem inset>
Save Page As...
<ContextMenuShortcut>S</ContextMenuShortcut>
</ContextMenuItem>
<ContextMenuItem>
<PlusIcon />
Create Shortcut...
</ContextMenuItem>
<ContextMenuItem inset>Name Window...</ContextMenuItem>
<ContextMenuSeparator />
<ContextMenuItem>
<Code2Icon />
Developer Tools
</ContextMenuItem>
<ContextMenuSeparator />
<ContextMenuItem variant="destructive">
<TrashIcon />
Delete
</ContextMenuItem>
</ContextMenuSubContent>
</ContextMenuSub>
<ContextMenuSeparator />
<ContextMenuCheckboxItem checked>
Show Bookmarks Bar
<ContextMenuShortcut>B</ContextMenuShortcut>
</ContextMenuCheckboxItem>
<ContextMenuCheckboxItem>Show Full URLs</ContextMenuCheckboxItem>
<ContextMenuSeparator />
<ContextMenuRadioGroup value="pedro">
<ContextMenuLabel inset>People</ContextMenuLabel>
<ContextMenuRadioItem value="pedro">
Pedro Duarte
</ContextMenuRadioItem>
<ContextMenuRadioItem value="colm">Colm Tuite</ContextMenuRadioItem>
</ContextMenuRadioGroup>
</ContextMenuContent>
</ContextMenu>
)
}

View File

@@ -0,0 +1,99 @@
"use client"
import * as React from "react"
import { addDays, format } from "date-fns"
import { CalendarIcon } from "lucide-react"
import { DateRange } from "react-day-picker"
import { cn } from "@/lib/utils"
import { Button } from "@/registry/new-york-v4/ui/button"
import { Calendar } from "@/registry/new-york-v4/ui/calendar"
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/registry/new-york-v4/ui/popover"
export function DatePickerDemo() {
return (
<div className="flex flex-col items-start gap-4 md:flex-row">
<DatePickerSimple />
<DatePickerWithRange />
</div>
)
}
function DatePickerSimple() {
const [date, setDate] = React.useState<Date>()
return (
<Popover>
<PopoverTrigger asChild>
<Button
variant={"outline"}
className={cn(
"min-w-[200px] justify-start px-2 font-normal",
!date && "text-muted-foreground"
)}
>
<CalendarIcon className="text-muted-foreground" />
{date ? format(date, "PPP") : <span>Pick a date</span>}
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align="start">
<Calendar
mode="single"
selected={date}
onSelect={setDate}
initialFocus
/>
</PopoverContent>
</Popover>
)
}
function DatePickerWithRange() {
const [date, setDate] = React.useState<DateRange | undefined>({
from: new Date(new Date().getFullYear(), 0, 20),
to: addDays(new Date(new Date().getFullYear(), 0, 20), 20),
})
return (
<Popover>
<PopoverTrigger asChild>
<Button
id="date"
variant={"outline"}
className={cn(
"w-fit justify-start px-2 font-normal",
!date && "text-muted-foreground"
)}
>
<CalendarIcon className="text-muted-foreground" />
{date?.from ? (
date.to ? (
<>
{format(date.from, "LLL dd, y")} -{" "}
{format(date.to, "LLL dd, y")}
</>
) : (
format(date.from, "LLL dd, y")
)
) : (
<span>Pick a date</span>
)}
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align="start">
<Calendar
initialFocus
mode="range"
defaultMonth={date?.from}
selected={date}
onSelect={setDate}
numberOfMonths={2}
/>
</PopoverContent>
</Popover>
)
}

View File

@@ -0,0 +1,129 @@
import { Button } from "@/registry/new-york-v4/ui/button"
import {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/registry/new-york-v4/ui/dialog"
import { Input } from "@/registry/new-york-v4/ui/input"
import { Label } from "@/registry/new-york-v4/ui/label"
export function DialogDemo() {
return (
<div className="flex flex-col items-start gap-4 md:flex-row">
<DialogWithForm />
<DialogScrollableContent />
<DialogWithStickyFooter />
</div>
)
}
function DialogWithForm() {
return (
<Dialog>
<form>
<DialogTrigger asChild>
<Button variant="outline">Edit Profile</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>Edit profile</DialogTitle>
<DialogDescription>
Make changes to your profile here. Click save when you&apos;re
done.
</DialogDescription>
</DialogHeader>
<div className="grid gap-4">
<div className="grid gap-3">
<Label htmlFor="name-1">Name</Label>
<Input id="name-1" name="name" defaultValue="Pedro Duarte" />
</div>
<div className="grid gap-3">
<Label htmlFor="username-1">Username</Label>
<Input id="username-1" name="username" defaultValue="@peduarte" />
</div>
</div>
<DialogFooter>
<DialogClose asChild>
<Button variant="outline">Cancel</Button>
</DialogClose>
<Button type="submit">Save changes</Button>
</DialogFooter>
</DialogContent>
</form>
</Dialog>
)
}
function DialogScrollableContent() {
return (
<Dialog>
<DialogTrigger asChild>
<Button variant="outline">Scrollable Content</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>Scrollable Content</DialogTitle>
<DialogDescription>
This is a dialog with scrollable content.
</DialogDescription>
</DialogHeader>
<div className="-mx-6 max-h-[500px] overflow-y-auto px-6 text-sm">
<h4 className="mb-4 text-lg leading-none font-medium">Lorem Ipsum</h4>
{Array.from({ length: 10 }).map((_, index) => (
<p key={index} className="mb-4 leading-normal">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
enim ad minim veniam, quis nostrud exercitation ullamco laboris
nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in
reprehenderit in voluptate velit esse cillum dolore eu fugiat
nulla pariatur. Excepteur sint occaecat cupidatat non proident,
sunt in culpa qui officia deserunt mollit anim id est laborum.
</p>
))}
</div>
</DialogContent>
</Dialog>
)
}
function DialogWithStickyFooter() {
return (
<Dialog>
<DialogTrigger asChild>
<Button variant="outline">Sticky Footer</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-lg">
<DialogHeader>
<DialogTitle>Scrollable Content</DialogTitle>
<DialogDescription>
This is a dialog with scrollable content.
</DialogDescription>
</DialogHeader>
<div className="-mx-6 max-h-[500px] overflow-y-auto px-6 text-sm">
<h4 className="mb-4 text-lg leading-none font-medium">Lorem Ipsum</h4>
{Array.from({ length: 10 }).map((_, index) => (
<p key={index} className="mb-4 leading-normal">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
enim ad minim veniam, quis nostrud exercitation ullamco laboris
nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in
reprehenderit in voluptate velit esse cillum dolore eu fugiat
nulla pariatur. Excepteur sint occaecat cupidatat non proident,
sunt in culpa qui officia deserunt mollit anim id est laborum.
</p>
))}
</div>
<DialogFooter>
<DialogClose asChild>
<Button variant="outline">Close</Button>
</DialogClose>
</DialogFooter>
</DialogContent>
</Dialog>
)
}

View File

@@ -0,0 +1,228 @@
"use client"
import * as React from "react"
import { Minus, Plus } from "lucide-react"
import { Bar, BarChart, ResponsiveContainer } from "recharts"
import { Button } from "@/registry/new-york-v4/ui/button"
import {
Drawer,
DrawerClose,
DrawerContent,
DrawerDescription,
DrawerFooter,
DrawerHeader,
DrawerTitle,
DrawerTrigger,
} from "@/registry/new-york-v4/ui/drawer"
const data = [
{
goal: 400,
},
{
goal: 300,
},
{
goal: 200,
},
{
goal: 300,
},
{
goal: 200,
},
{
goal: 278,
},
{
goal: 189,
},
{
goal: 239,
},
{
goal: 300,
},
{
goal: 200,
},
{
goal: 278,
},
{
goal: 189,
},
{
goal: 349,
},
]
export function DrawerDemo() {
return (
<div className="flex flex-wrap items-start gap-4">
<DrawerBottom />
<DrawerScrollableContent />
<DrawerDirections />
</div>
)
}
function DrawerBottom() {
const [goal, setGoal] = React.useState(350)
const onClick = React.useCallback((adjustment: number) => {
setGoal((prevGoal) => Math.max(200, Math.min(400, prevGoal + adjustment)))
}, [])
return (
<Drawer>
<DrawerTrigger asChild>
<Button variant="outline">Open Drawer</Button>
</DrawerTrigger>
<DrawerContent>
<div className="mx-auto w-full max-w-sm">
<DrawerHeader>
<DrawerTitle>Move Goal</DrawerTitle>
<DrawerDescription>Set your daily activity goal.</DrawerDescription>
</DrawerHeader>
<div className="p-4 pb-0">
<div className="flex items-center justify-center space-x-2">
<Button
variant="outline"
size="icon"
className="h-8 w-8 shrink-0 rounded-full"
onClick={() => onClick(-10)}
disabled={goal <= 200}
>
<Minus />
<span className="sr-only">Decrease</span>
</Button>
<div className="flex-1 text-center">
<div className="text-7xl font-bold tracking-tighter">
{goal}
</div>
<div className="text-muted-foreground text-[0.70rem] uppercase">
Calories/day
</div>
</div>
<Button
variant="outline"
size="icon"
className="h-8 w-8 shrink-0 rounded-full"
onClick={() => onClick(10)}
disabled={goal >= 400}
>
<Plus />
<span className="sr-only">Increase</span>
</Button>
</div>
<div className="mt-3 h-[120px]">
<ResponsiveContainer width="100%" height="100%">
<BarChart data={data}>
<Bar
dataKey="goal"
style={
{
fill: "hsl(var(--foreground))",
opacity: 0.9,
} as React.CSSProperties
}
/>
</BarChart>
</ResponsiveContainer>
</div>
</div>
<DrawerFooter>
<Button>Submit</Button>
<DrawerClose asChild>
<Button variant="outline">Cancel</Button>
</DrawerClose>
</DrawerFooter>
</div>
</DrawerContent>
</Drawer>
)
}
function DrawerScrollableContent() {
return (
<Drawer direction="right">
<DrawerTrigger asChild>
<Button variant="outline">Scrollable Content</Button>
</DrawerTrigger>
<DrawerContent>
<DrawerHeader>
<DrawerTitle>Move Goal</DrawerTitle>
<DrawerDescription>Set your daily activity goal.</DrawerDescription>
</DrawerHeader>
<div className="overflow-y-auto px-4 text-sm">
<h4 className="mb-4 text-lg leading-none font-medium">Lorem Ipsum</h4>
{Array.from({ length: 10 }).map((_, index) => (
<p key={index} className="mb-4 leading-normal">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
enim ad minim veniam, quis nostrud exercitation ullamco laboris
nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in
reprehenderit in voluptate velit esse cillum dolore eu fugiat
nulla pariatur. Excepteur sint occaecat cupidatat non proident,
sunt in culpa qui officia deserunt mollit anim id est laborum.
</p>
))}
</div>
<DrawerFooter>
<Button>Submit</Button>
<DrawerClose asChild>
<Button variant="outline">Cancel</Button>
</DrawerClose>
</DrawerFooter>
</DrawerContent>
</Drawer>
)
}
const directions = ["top", "right", "bottom", "left"] as const
function DrawerDirections() {
return (
<>
{directions.map((direction) => (
<Drawer key={direction} direction={direction}>
<DrawerTrigger asChild>
<Button variant="outline" className="capitalize">
{direction}
</Button>
</DrawerTrigger>
<DrawerContent>
<DrawerHeader>
<DrawerTitle>Move Goal</DrawerTitle>
<DrawerDescription>
Set your daily activity goal.
</DrawerDescription>
</DrawerHeader>
<div className="overflow-y-auto px-4 text-sm">
{Array.from({ length: 10 }).map((_, index) => (
<p key={index} className="mb-4 leading-normal">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed
do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco
laboris nisi ut aliquip ex ea commodo consequat. Duis aute
irure dolor in reprehenderit in voluptate velit esse cillum
dolore eu fugiat nulla pariatur. Excepteur sint occaecat
cupidatat non proident, sunt in culpa qui officia deserunt
mollit anim id est laborum.
</p>
))}
</div>
<DrawerFooter>
<Button>Submit</Button>
<DrawerClose asChild>
<Button variant="outline">Cancel</Button>
</DrawerClose>
</DrawerFooter>
</DrawerContent>
</Drawer>
))}
</>
)
}

View File

@@ -0,0 +1,360 @@
"use client"
import * as React from "react"
import {
BadgeCheckIcon,
BellIcon,
ChevronsUpDownIcon,
CreditCardIcon,
LogOut,
LogOutIcon,
MoreHorizontalIcon,
PencilIcon,
Settings2Icon,
ShareIcon,
SparklesIcon,
TrashIcon,
UserIcon,
} from "lucide-react"
import {
Avatar,
AvatarFallback,
AvatarImage,
} from "@/registry/new-york-v4/ui/avatar"
import { Button } from "@/registry/new-york-v4/ui/button"
import {
DropdownMenu,
DropdownMenuCheckboxItem,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuPortal,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
DropdownMenuSeparator,
DropdownMenuShortcut,
DropdownMenuSub,
DropdownMenuSubContent,
DropdownMenuSubTrigger,
DropdownMenuTrigger,
} from "@/registry/new-york-v4/ui/dropdown-menu"
export function DropdownMenuDemo() {
return (
<div className="flex flex-wrap items-start gap-4">
<DropdownMenuSimple />
<DropdownMenuCheckboxes />
<DropdownMenuRadioGroupDemo />
<DropdownMenuWithAvatar />
<DropdownMenuAvatarOnly />
<DropdownMenuIconColor />
</div>
)
}
function DropdownMenuSimple() {
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline">Open</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start" className="w-56">
<DropdownMenuLabel>My Account</DropdownMenuLabel>
<DropdownMenuGroup>
<DropdownMenuItem>
Profile
<DropdownMenuShortcut>P</DropdownMenuShortcut>
</DropdownMenuItem>
<DropdownMenuItem>
Billing
<DropdownMenuShortcut>B</DropdownMenuShortcut>
</DropdownMenuItem>
<DropdownMenuItem>
Settings
<DropdownMenuShortcut>S</DropdownMenuShortcut>
</DropdownMenuItem>
<DropdownMenuItem>
Keyboard shortcuts
<DropdownMenuShortcut>K</DropdownMenuShortcut>
</DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuItem>Team</DropdownMenuItem>
<DropdownMenuSub>
<DropdownMenuSubTrigger>Invite users</DropdownMenuSubTrigger>
<DropdownMenuPortal>
<DropdownMenuSubContent>
<DropdownMenuItem>Email</DropdownMenuItem>
<DropdownMenuItem>Message</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem>More...</DropdownMenuItem>
</DropdownMenuSubContent>
</DropdownMenuPortal>
</DropdownMenuSub>
<DropdownMenuItem>
New Team
<DropdownMenuShortcut>+T</DropdownMenuShortcut>
</DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuItem>GitHub</DropdownMenuItem>
<DropdownMenuItem>Support</DropdownMenuItem>
<DropdownMenuItem disabled>API</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem>
Log out
<DropdownMenuShortcut>Q</DropdownMenuShortcut>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
)
}
function DropdownMenuCheckboxes() {
const [showStatusBar, setShowStatusBar] = React.useState(true)
const [showActivityBar, setShowActivityBar] = React.useState(false)
const [showPanel, setShowPanel] = React.useState(false)
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline">Checkboxes</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start" className="w-56">
<DropdownMenuGroup>
<DropdownMenuLabel>Account</DropdownMenuLabel>
<DropdownMenuItem>
<UserIcon /> Profile
</DropdownMenuItem>
<DropdownMenuItem>
<CreditCardIcon /> Billing
</DropdownMenuItem>
<DropdownMenuItem>
<Settings2Icon /> Settings
</DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuLabel>Appearance</DropdownMenuLabel>
<DropdownMenuCheckboxItem
checked={showStatusBar}
onCheckedChange={setShowStatusBar}
>
Status Bar
</DropdownMenuCheckboxItem>
<DropdownMenuCheckboxItem
checked={showActivityBar}
onCheckedChange={setShowActivityBar}
disabled
>
Activity Bar
</DropdownMenuCheckboxItem>
<DropdownMenuCheckboxItem
checked={showPanel}
onCheckedChange={setShowPanel}
>
Panel
</DropdownMenuCheckboxItem>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuItem>
<LogOutIcon /> Sign Out
</DropdownMenuItem>
</DropdownMenuGroup>
</DropdownMenuContent>
</DropdownMenu>
)
}
function DropdownMenuRadioGroupDemo() {
const [position, setPosition] = React.useState("bottom")
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline">Radio Group</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start" className="w-56">
<DropdownMenuLabel inset>Panel Position</DropdownMenuLabel>
<DropdownMenuGroup>
<DropdownMenuRadioGroup value={position} onValueChange={setPosition}>
<DropdownMenuRadioItem value="top">Top</DropdownMenuRadioItem>
<DropdownMenuRadioItem value="bottom">Bottom</DropdownMenuRadioItem>
<DropdownMenuRadioItem value="right" disabled>
Right
</DropdownMenuRadioItem>
</DropdownMenuRadioGroup>
</DropdownMenuGroup>
</DropdownMenuContent>
</DropdownMenu>
)
}
function DropdownMenuWithAvatar() {
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="outline"
className="h-12 justify-start px-2 md:max-w-[200px]"
>
<Avatar>
<AvatarImage src="https://github.com/shadcn.png" alt="Shadcn" />
<AvatarFallback className="rounded-lg">CN</AvatarFallback>
</Avatar>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-semibold">shadcn</span>
<span className="text-muted-foreground truncate text-xs">
shadcn@example.com
</span>
</div>
<ChevronsUpDownIcon className="text-muted-foreground ml-auto" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent
className="w-(--radix-dropdown-menu-trigger-width) min-w-56"
align="start"
>
<DropdownMenuLabel className="p-0 font-normal">
<div className="flex items-center gap-2 px-1 py-1.5 text-left text-sm">
<Avatar>
<AvatarImage src="https://github.com/shadcn.png" alt="Shadcn" />
<AvatarFallback className="rounded-lg">CN</AvatarFallback>
</Avatar>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-semibold">shadcn</span>
<span className="text-muted-foreground truncate text-xs">
shadcn@example.com
</span>
</div>
</div>
</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuItem>
<SparklesIcon />
Upgrade to Pro
</DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuItem>
<BadgeCheckIcon />
Account
</DropdownMenuItem>
<DropdownMenuItem>
<CreditCardIcon />
Billing
</DropdownMenuItem>
<DropdownMenuItem>
<BellIcon />
Notifications
</DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuItem>
<LogOut />
Sign Out
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
)
}
function DropdownMenuAvatarOnly() {
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="outline"
className="size-8 rounded-full border-none p-0"
>
<Avatar>
<AvatarImage src="https://github.com/leerob.png" alt="leerob" />
<AvatarFallback className="rounded-lg">LR</AvatarFallback>
</Avatar>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent
className="w-(--radix-dropdown-menu-trigger-width) min-w-56"
align="start"
>
<DropdownMenuLabel className="p-0 font-normal">
<div className="flex items-center gap-2 px-1 py-1.5 text-left text-sm">
<Avatar>
<AvatarImage src="https://github.com/leerob.png" alt="leerob" />
<AvatarFallback className="rounded-lg">LR</AvatarFallback>
</Avatar>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-semibold">leerob</span>
<span className="text-muted-foreground truncate text-xs">
leerob@example.com
</span>
</div>
</div>
</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuItem>
<SparklesIcon />
Upgrade to Pro
</DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuItem>
<BadgeCheckIcon />
Account
</DropdownMenuItem>
<DropdownMenuItem>
<CreditCardIcon />
Billing
</DropdownMenuItem>
<DropdownMenuItem>
<BellIcon />
Notifications
</DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuItem>
<LogOut />
Sign Out
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
)
}
function DropdownMenuIconColor() {
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" size="icon">
<MoreHorizontalIcon />
<span className="sr-only">Toggle menu</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start">
<DropdownMenuGroup className="*:data-[slot=dropdown-menu-item]:[&>svg]:text-muted-foreground">
<DropdownMenuItem>
<PencilIcon />
Edit
</DropdownMenuItem>
<DropdownMenuItem>
<ShareIcon />
Share
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem variant="destructive">
<TrashIcon />
Delete
</DropdownMenuItem>
</DropdownMenuGroup>
</DropdownMenuContent>
</DropdownMenu>
)
}

View File

@@ -0,0 +1,399 @@
"use client"
import { zodResolver } from "@hookform/resolvers/zod"
import { format } from "date-fns"
import { CalendarIcon } from "lucide-react"
import { useForm } from "react-hook-form"
import { toast } from "sonner"
import { z } from "zod"
import { cn } from "@/lib/utils"
import { Button } from "@/registry/new-york-v4/ui/button"
import { Calendar } from "@/registry/new-york-v4/ui/calendar"
import { Checkbox } from "@/registry/new-york-v4/ui/checkbox"
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/registry/new-york-v4/ui/form"
import { Input } from "@/registry/new-york-v4/ui/input"
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/registry/new-york-v4/ui/popover"
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 { Switch } from "@/registry/new-york-v4/ui/switch"
import { Textarea } from "@/registry/new-york-v4/ui/textarea"
const items = [
{
id: "recents",
label: "Recents",
},
{
id: "home",
label: "Home",
},
{
id: "applications",
label: "Applications",
},
{
id: "desktop",
label: "Desktop",
},
{
id: "downloads",
label: "Downloads",
},
{
id: "documents",
label: "Documents",
},
] as const
const FormSchema = z.object({
username: z.string().min(2, {
message: "Username must be at least 2 characters.",
}),
bio: z
.string()
.min(10, {
message: "Bio must be at least 10 characters.",
})
.max(160, {
message: "Bio must not be longer than 30 characters.",
}),
email: z
.string({
required_error: "Please select an email to display.",
})
.email(),
type: z.enum(["all", "mentions", "none"], {
required_error: "You need to select a notification type.",
}),
mobile: z.boolean().default(false).optional(),
items: z.array(z.string()).refine((value) => value.some((item) => item), {
message: "You have to select at least one item.",
}),
dob: z.date({
required_error: "A date of birth is required.",
}),
marketing_emails: z.boolean().default(false).optional(),
security_emails: z.boolean(),
})
export function FormDemo() {
const form = useForm<z.infer<typeof FormSchema>>({
resolver: zodResolver(FormSchema),
defaultValues: {
username: "",
items: ["recents", "home"],
},
})
function onSubmit(data: z.infer<typeof FormSchema>) {
toast("You submitted the following values:", {
description: (
<pre className="mt-2 w-[340px] rounded-md bg-slate-950 p-4">
<code className="text-white">{JSON.stringify(data, null, 2)}</code>
</pre>
),
})
}
return (
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="grid w-full max-w-sm gap-6"
>
<FormField
control={form.control}
name="username"
render={({ field }) => (
<FormItem>
<FormLabel>Username</FormLabel>
<FormControl>
<Input placeholder="shadcn" {...field} />
</FormControl>
<FormDescription>
This is your public display name.
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<Select onValueChange={field.onChange} defaultValue={field.value}>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Select a verified email to display" />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value="m@example.com">m@example.com</SelectItem>
<SelectItem value="m@google.com">m@google.com</SelectItem>
<SelectItem value="m@support.com">m@support.com</SelectItem>
</SelectContent>
</Select>
<FormDescription>
You can manage email addresses in your email settings.
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="bio"
render={({ field }) => (
<FormItem>
<FormLabel>Bio</FormLabel>
<FormControl>
<Textarea
placeholder="Tell us a little bit about yourself"
className="resize-none"
{...field}
/>
</FormControl>
<FormDescription>
You can <span>@mention</span> other users and organizations.
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="type"
render={({ field }) => (
<FormItem className="flex flex-col gap-3">
<FormLabel>Notify me about...</FormLabel>
<FormControl>
<RadioGroup
onValueChange={field.onChange}
defaultValue={field.value}
className="flex flex-col gap-3"
>
<FormItem className="flex items-center gap-2">
<FormControl>
<RadioGroupItem value="all" />
</FormControl>
<FormLabel className="font-normal">
All new messages
</FormLabel>
</FormItem>
<FormItem className="flex items-center gap-2">
<FormControl>
<RadioGroupItem value="mentions" />
</FormControl>
<FormLabel className="font-normal">
Direct messages and mentions
</FormLabel>
</FormItem>
<FormItem className="flex items-center gap-2">
<FormControl>
<RadioGroupItem value="none" />
</FormControl>
<FormLabel className="font-normal">Nothing</FormLabel>
</FormItem>
</RadioGroup>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="mobile"
render={({ field }) => (
<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="flex flex-col gap-1">
<FormLabel className="leading-snug">
Use different settings for my mobile devices
</FormLabel>
<FormDescription className="leading-snug">
You can manage your mobile notifications in the mobile
settings page.
</FormDescription>
</div>
</FormItem>
)}
/>
<FormField
control={form.control}
name="items"
render={() => (
<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>
<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 leading-tight font-normal">
{item.label}
</FormLabel>
</FormItem>
)
}}
/>
))}
</div>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="dob"
render={({ field }) => (
<FormItem className="flex flex-col">
<FormLabel>Date of birth</FormLabel>
<Popover>
<PopoverTrigger asChild>
<FormControl>
<Button
variant={"outline"}
className={cn(
"w-[240px] pl-3 text-left font-normal",
!field.value && "text-muted-foreground"
)}
>
{field.value ? (
format(field.value, "PPP")
) : (
<span>Pick a date</span>
)}
<CalendarIcon className="ml-auto h-4 w-4 opacity-50" />
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align="start">
<Calendar
mode="single"
selected={field.value}
onSelect={field.onChange}
disabled={(date) =>
date > new Date() || date < new Date("1900-01-01")
}
initialFocus
/>
</PopoverContent>
</Popover>
<FormDescription>
Your date of birth is used to calculate your age.
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<div>
<h3 className="mb-4 text-lg font-medium">Email Notifications</h3>
<div className="flex flex-col gap-4">
<FormField
control={form.control}
name="marketing_emails"
render={({ field }) => (
<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>
<FormControl>
<Switch
checked={field.value}
onCheckedChange={field.onChange}
/>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="security_emails"
render={({ field }) => (
<FormItem className="flex flex-row items-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>
<FormControl>
<Switch
checked={field.value}
onCheckedChange={field.onChange}
disabled
aria-readonly
/>
</FormControl>
</FormItem>
)}
/>
</div>
</div>
<Button type="submit">Submit</Button>
</form>
</Form>
)
}

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

@@ -0,0 +1,43 @@
import { CalendarIcon } from "lucide-react"
import {
Avatar,
AvatarFallback,
AvatarImage,
} from "@/registry/new-york-v4/ui/avatar"
import { Button } from "@/registry/new-york-v4/ui/button"
import {
HoverCard,
HoverCardContent,
HoverCardTrigger,
} from "@/registry/new-york-v4/ui/hover-card"
export function HoverCardDemo() {
return (
<HoverCard>
<HoverCardTrigger asChild>
<Button variant="link">@nextjs</Button>
</HoverCardTrigger>
<HoverCardContent className="w-80" side="right">
<div className="flex justify-between gap-4">
<Avatar>
<AvatarImage src="https://github.com/vercel.png" />
<AvatarFallback>VC</AvatarFallback>
</Avatar>
<div className="flex flex-col gap-1">
<h4 className="text-sm font-semibold">@nextjs</h4>
<p className="text-sm">
The React Framework created and maintained by @vercel.
</p>
<div className="mt-1 flex items-center gap-2">
<CalendarIcon className="text-muted-foreground size-4" />{" "}
<span className="text-muted-foreground text-xs">
Joined December 2021
</span>
</div>
</div>
</div>
</HoverCardContent>
</HoverCard>
)
}

View File

@@ -0,0 +1,23 @@
import { Input } from "@/registry/new-york-v4/ui/input"
export function InputDemo() {
return (
<div className="flex flex-col flex-wrap gap-4 md:flex-row">
<Input type="email" placeholder="Email" />
<Input type="text" placeholder="Error" aria-invalid="true" />
<Input type="password" placeholder="Password" />
<Input type="number" placeholder="Number" />
<Input type="file" placeholder="File" />
<Input type="tel" placeholder="Tel" />
<Input type="text" placeholder="Text" />
<Input type="url" placeholder="URL" />
<Input type="search" placeholder="Search" />
<Input type="date" placeholder="Date" />
<Input type="datetime-local" placeholder="Datetime Local" />
<Input type="month" placeholder="Month" />
<Input type="time" placeholder="Time" />
<Input type="week" placeholder="Week" />
<Input disabled placeholder="Disabled" />
</div>
)
}

View File

@@ -0,0 +1,109 @@
"use client"
import * as React from "react"
import { REGEXP_ONLY_DIGITS } from "input-otp"
import {
InputOTP,
InputOTPGroup,
InputOTPSeparator,
InputOTPSlot,
} from "@/registry/new-york-v4/ui/input-otp"
import { Label } from "@/registry/new-york-v4/ui/label"
export function InputOTPDemo() {
return (
<div className="flex flex-col flex-wrap gap-6 md:flex-row">
<InputOTPSimple />
<InputOTPPattern />
<InputOTPWithSeparator />
<InputOTPWithSpacing />
</div>
)
}
function InputOTPSimple() {
return (
<div className="grid gap-2">
<Label htmlFor="simple">Simple</Label>
<InputOTP id="simple" maxLength={6}>
<InputOTPGroup>
<InputOTPSlot index={0} />
<InputOTPSlot index={1} />
<InputOTPSlot index={2} />
</InputOTPGroup>
<InputOTPSeparator />
<InputOTPGroup>
<InputOTPSlot index={3} />
<InputOTPSlot index={4} />
<InputOTPSlot index={5} />
</InputOTPGroup>
</InputOTP>
</div>
)
}
function InputOTPPattern() {
return (
<div className="grid gap-2">
<Label htmlFor="digits-only">Digits Only</Label>
<InputOTP id="digits-only" maxLength={6} pattern={REGEXP_ONLY_DIGITS}>
<InputOTPGroup>
<InputOTPSlot index={0} />
<InputOTPSlot index={1} />
<InputOTPSlot index={2} />
<InputOTPSlot index={3} />
<InputOTPSlot index={4} />
<InputOTPSlot index={5} />
</InputOTPGroup>
</InputOTP>
</div>
)
}
function InputOTPWithSeparator() {
const [value, setValue] = React.useState("123456")
return (
<div className="grid gap-2">
<Label htmlFor="with-separator">With Separator</Label>
<InputOTP
id="with-separator"
maxLength={6}
value={value}
onChange={setValue}
>
<InputOTPGroup>
<InputOTPSlot index={0} />
<InputOTPSlot index={1} />
</InputOTPGroup>
<InputOTPSeparator />
<InputOTPGroup>
<InputOTPSlot index={2} />
<InputOTPSlot index={3} />
</InputOTPGroup>
<InputOTPSeparator />
<InputOTPGroup>
<InputOTPSlot index={4} />
<InputOTPSlot index={5} />
</InputOTPGroup>
</InputOTP>
</div>
)
}
function InputOTPWithSpacing() {
return (
<div className="grid gap-2">
<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} 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,27 @@
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 { Textarea } from "@/registry/new-york-v4/ui/textarea"
export function LabelDemo() {
return (
<div className="grid w-full max-w-sm gap-6">
<div className="flex items-center gap-3">
<Checkbox id="label-demo-terms" />
<Label htmlFor="label-demo-terms">Accept terms and conditions</Label>
</div>
<div className="grid gap-3">
<Label htmlFor="label-demo-username">Username</Label>
<Input id="label-demo-username" placeholder="Username" />
</div>
<div className="group grid gap-3" data-disabled={true}>
<Label htmlFor="label-demo-disabled">Disabled</Label>
<Input id="label-demo-disabled" placeholder="Disabled" disabled />
</div>
<div className="grid gap-3">
<Label htmlFor="label-demo-message">Message</Label>
<Textarea id="label-demo-message" placeholder="Message" />
</div>
</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

@@ -0,0 +1,130 @@
import { HelpCircleIcon, SettingsIcon, Trash2Icon } from "lucide-react"
import {
Menubar,
MenubarCheckboxItem,
MenubarContent,
MenubarGroup,
MenubarItem,
MenubarMenu,
MenubarRadioGroup,
MenubarRadioItem,
MenubarSeparator,
MenubarShortcut,
MenubarSub,
MenubarSubContent,
MenubarSubTrigger,
MenubarTrigger,
} from "@/registry/new-york-v4/ui/menubar"
export function MenubarDemo() {
return (
<Menubar>
<MenubarMenu>
<MenubarTrigger>File</MenubarTrigger>
<MenubarContent>
<MenubarItem>
New Tab <MenubarShortcut>T</MenubarShortcut>
</MenubarItem>
<MenubarItem>
New Window <MenubarShortcut>N</MenubarShortcut>
</MenubarItem>
<MenubarItem disabled>New Incognito Window</MenubarItem>
<MenubarSeparator />
<MenubarSub>
<MenubarSubTrigger>Share</MenubarSubTrigger>
<MenubarSubContent>
<MenubarItem>Email link</MenubarItem>
<MenubarItem>Messages</MenubarItem>
<MenubarItem>Notes</MenubarItem>
</MenubarSubContent>
</MenubarSub>
<MenubarSeparator />
<MenubarItem>
Print... <MenubarShortcut>P</MenubarShortcut>
</MenubarItem>
</MenubarContent>
</MenubarMenu>
<MenubarMenu>
<MenubarTrigger>Edit</MenubarTrigger>
<MenubarContent>
<MenubarItem>
Undo <MenubarShortcut>Z</MenubarShortcut>
</MenubarItem>
<MenubarItem>
Redo <MenubarShortcut>Z</MenubarShortcut>
</MenubarItem>
<MenubarSeparator />
<MenubarSub>
<MenubarSubTrigger>Find</MenubarSubTrigger>
<MenubarSubContent>
<MenubarItem>Search the web</MenubarItem>
<MenubarSeparator />
<MenubarItem>Find...</MenubarItem>
<MenubarItem>Find Next</MenubarItem>
<MenubarItem>Find Previous</MenubarItem>
</MenubarSubContent>
</MenubarSub>
<MenubarSeparator />
<MenubarItem>Cut</MenubarItem>
<MenubarItem>Copy</MenubarItem>
<MenubarItem>Paste</MenubarItem>
</MenubarContent>
</MenubarMenu>
<MenubarMenu>
<MenubarTrigger>View</MenubarTrigger>
<MenubarContent>
<MenubarCheckboxItem>Always Show Bookmarks Bar</MenubarCheckboxItem>
<MenubarCheckboxItem checked>
Always Show Full URLs
</MenubarCheckboxItem>
<MenubarSeparator />
<MenubarItem inset>
Reload <MenubarShortcut>R</MenubarShortcut>
</MenubarItem>
<MenubarItem disabled inset>
Force Reload <MenubarShortcut>R</MenubarShortcut>
</MenubarItem>
<MenubarSeparator />
<MenubarItem inset>Toggle Fullscreen</MenubarItem>
<MenubarSeparator />
<MenubarItem inset>Hide Sidebar</MenubarItem>
</MenubarContent>
</MenubarMenu>
<MenubarMenu>
<MenubarTrigger>Profiles</MenubarTrigger>
<MenubarContent>
<MenubarRadioGroup value="benoit">
<MenubarRadioItem value="andy">Andy</MenubarRadioItem>
<MenubarRadioItem value="benoit">Benoit</MenubarRadioItem>
<MenubarRadioItem value="Luis">Luis</MenubarRadioItem>
</MenubarRadioGroup>
<MenubarSeparator />
<MenubarItem inset>Edit...</MenubarItem>
<MenubarSeparator />
<MenubarItem inset>Add Profile...</MenubarItem>
</MenubarContent>
</MenubarMenu>
<MenubarMenu>
<MenubarTrigger>More</MenubarTrigger>
<MenubarContent>
<MenubarGroup>
<MenubarItem>
<SettingsIcon />
Settings
</MenubarItem>
<MenubarItem>
<HelpCircleIcon />
Help
</MenubarItem>
<MenubarSeparator />
<MenubarItem variant="destructive">
<Trash2Icon />
Delete
</MenubarItem>
</MenubarGroup>
</MenubarContent>
</MenubarMenu>
</Menubar>
)
}

View File

@@ -0,0 +1,34 @@
"use client"
import * as React from "react"
import { MoonIcon, SunIcon } from "lucide-react"
import { useTheme } from "next-themes"
import { META_THEME_COLORS, useMetaColor } from "@/hooks/use-meta-color"
import { Button } from "@/registry/new-york-v4/ui/button"
export function ModeSwitcher() {
const { setTheme, resolvedTheme } = useTheme()
const { setMetaColor } = useMetaColor()
const toggleTheme = React.useCallback(() => {
setTheme(resolvedTheme === "dark" ? "light" : "dark")
setMetaColor(
resolvedTheme === "dark"
? META_THEME_COLORS.light
: META_THEME_COLORS.dark
)
}, [resolvedTheme, setTheme, setMetaColor])
return (
<Button
variant="ghost"
className="group/toggle h-8 w-8 px-0"
onClick={toggleTheme}
>
<SunIcon className="hidden [html.dark_&]:block" />
<MoonIcon className="hidden [html.light_&]:block" />
<span className="sr-only">Toggle theme</span>
</Button>
)
}

View File

@@ -0,0 +1,27 @@
"use client"
import * as React from "react"
import { MoonIcon, SunIcon } from "lucide-react"
import { useTheme } from "next-themes"
import { Button } from "@/registry/new-york-v4/ui/button"
export function ModeToggle() {
const { setTheme, resolvedTheme } = useTheme()
const toggleTheme = React.useCallback(() => {
setTheme(resolvedTheme === "dark" ? "light" : "dark")
}, [resolvedTheme, setTheme])
return (
<Button
variant="ghost"
className="group/toggle h-8 w-8 px-0"
onClick={toggleTheme}
>
<SunIcon className="hidden [html.dark_&]:block" />
<MoonIcon className="hidden [html.light_&]:block" />
<span className="sr-only">Toggle theme</span>
</Button>
)
}

View File

@@ -0,0 +1,37 @@
"use client"
import Link from "next/link"
import { usePathname } from "next/navigation"
import {
NavigationMenu,
NavigationMenuItem,
NavigationMenuLink,
NavigationMenuList,
} from "@/registry/new-york-v4/ui/navigation-menu"
export function NavHeader() {
const pathname = usePathname()
return (
<NavigationMenu>
<NavigationMenuList className="gap-2 *:data-[slot=navigation-menu-item]:h-7 **:data-[slot=navigation-menu-link]:py-1 **:data-[slot=navigation-menu-link]:font-medium">
<NavigationMenuItem>
<NavigationMenuLink asChild data-active={pathname === "/"}>
<Link href="/">Home</Link>
</NavigationMenuLink>
</NavigationMenuItem>
<NavigationMenuItem>
<NavigationMenuLink asChild data-active={pathname === "/charts"}>
<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

@@ -0,0 +1,114 @@
"use client"
import {
BadgeCheck,
Bell,
ChevronsUpDown,
CreditCard,
LogOut,
Sparkles,
} from "lucide-react"
import {
Avatar,
AvatarFallback,
AvatarImage,
} from "@/registry/new-york-v4/ui/avatar"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/registry/new-york-v4/ui/dropdown-menu"
import {
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
useSidebar,
} from "@/registry/new-york-v4/ui/sidebar"
export function NavUser({
user,
}: {
user: {
name: string
email: string
avatar: string
}
}) {
const { isMobile } = useSidebar()
return (
<SidebarMenu>
<SidebarMenuItem>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<SidebarMenuButton
size="lg"
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
>
<Avatar className="h-8 w-8 rounded-lg">
<AvatarImage src={user.avatar} alt={user.name} />
<AvatarFallback className="rounded-lg">CN</AvatarFallback>
</Avatar>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-semibold">{user.name}</span>
<span className="truncate text-xs">{user.email}</span>
</div>
<ChevronsUpDown className="ml-auto size-4" />
</SidebarMenuButton>
</DropdownMenuTrigger>
<DropdownMenuContent
className="w-(--radix-dropdown-menu-trigger-width) min-w-56 rounded-lg"
side={isMobile ? "bottom" : "right"}
align="end"
sideOffset={4}
>
<DropdownMenuLabel className="p-0 font-normal">
<div className="flex items-center gap-2 px-1 py-1.5 text-left text-sm">
<Avatar className="h-8 w-8 rounded-lg">
<AvatarImage src={user.avatar} alt={user.name} />
<AvatarFallback className="rounded-lg">CN</AvatarFallback>
</Avatar>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-semibold">{user.name}</span>
<span className="truncate text-xs">{user.email}</span>
</div>
</div>
</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuItem>
<Sparkles />
Upgrade to Pro
</DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuItem>
<BadgeCheck />
Account
</DropdownMenuItem>
<DropdownMenuItem>
<CreditCard />
Billing
</DropdownMenuItem>
<DropdownMenuItem>
<Bell />
Notifications
</DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuItem>
<LogOut />
Log out
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</SidebarMenuItem>
</SidebarMenu>
)
}

View File

@@ -0,0 +1,229 @@
"use client"
import * as React from "react"
import Link from "next/link"
import { CircleCheckIcon, CircleHelpIcon, CircleIcon } from "lucide-react"
import {
NavigationMenu,
NavigationMenuContent,
NavigationMenuItem,
NavigationMenuLink,
NavigationMenuList,
NavigationMenuTrigger,
navigationMenuTriggerStyle,
} from "@/registry/new-york-v4/ui/navigation-menu"
const components: { title: string; href: string; description: string }[] = [
{
title: "Alert Dialog",
href: "/docs/primitives/alert-dialog",
description:
"A modal dialog that interrupts the user with important content and expects a response.",
},
{
title: "Hover Card",
href: "/docs/primitives/hover-card",
description:
"For sighted users to preview content available behind a link.",
},
{
title: "Progress",
href: "/docs/primitives/progress",
description:
"Displays an indicator showing the completion progress of a task, typically displayed as a progress bar.",
},
{
title: "Scroll-area",
href: "/docs/primitives/scroll-area",
description: "Visually or semantically separates content.",
},
{
title: "Tabs",
href: "/docs/primitives/tabs",
description:
"A set of layered sections of content—known as tab panels—that are displayed one at a time.",
},
{
title: "Tooltip",
href: "/docs/primitives/tooltip",
description:
"A popup that displays information related to an element when the element receives keyboard focus or the mouse hovers over it.",
},
]
export function NavigationMenuDemo() {
return (
<div className="hidden w-full flex-col items-center justify-center gap-6 @xl:flex">
<NavigationMenu>
<NavigationMenuList>
<NavigationMenuItem>
<NavigationMenuTrigger>Getting started</NavigationMenuTrigger>
<NavigationMenuContent>
<ul className="grid gap-2 md:w-[400px] lg:w-[500px] lg:grid-cols-[.75fr_1fr]">
<li className="row-span-3">
<NavigationMenuLink asChild>
<a
className="from-muted/50 to-muted flex h-full w-full flex-col justify-end rounded-md bg-linear-to-b p-6 no-underline outline-hidden select-none focus:shadow-md"
href="/"
>
<div className="mt-4 mb-2 text-lg font-medium">
shadcn/ui
</div>
<p className="text-muted-foreground text-sm leading-tight">
Beautifully designed components built with Tailwind CSS.
</p>
</a>
</NavigationMenuLink>
</li>
<ListItem href="/docs" title="Introduction">
Re-usable components built using Radix UI and Tailwind CSS.
</ListItem>
<ListItem href="/docs/installation" title="Installation">
How to install dependencies and structure your app.
</ListItem>
<ListItem href="/docs/primitives/typography" title="Typography">
Styles for headings, paragraphs, lists...etc
</ListItem>
</ul>
</NavigationMenuContent>
</NavigationMenuItem>
<NavigationMenuItem>
<NavigationMenuTrigger>Components</NavigationMenuTrigger>
<NavigationMenuContent>
<ul className="grid w-[400px] gap-2 md:w-[500px] md:grid-cols-2 lg:w-[600px]">
{components.map((component) => (
<ListItem
key={component.title}
title={component.title}
href={component.href}
>
{component.description}
</ListItem>
))}
</ul>
</NavigationMenuContent>
</NavigationMenuItem>
<NavigationMenuItem>
<NavigationMenuLink
asChild
className={navigationMenuTriggerStyle()}
>
<Link href="/docs">Documentation</Link>
</NavigationMenuLink>
</NavigationMenuItem>
</NavigationMenuList>
</NavigationMenu>
<NavigationMenu viewport={false}>
<NavigationMenuList>
<NavigationMenuItem>
<NavigationMenuLink
asChild
className={navigationMenuTriggerStyle()}
>
<Link href="/docs">Documentation</Link>
</NavigationMenuLink>
</NavigationMenuItem>
<NavigationMenuItem>
<NavigationMenuTrigger>List</NavigationMenuTrigger>
<NavigationMenuContent>
<ul className="grid w-[300px] gap-4">
<li>
<NavigationMenuLink asChild>
<Link href="#">
<div className="font-medium">Components</div>
<div className="text-muted-foreground">
Browse all components in the library.
</div>
</Link>
</NavigationMenuLink>
<NavigationMenuLink asChild>
<Link href="#">
<div className="font-medium">Documentation</div>
<div className="text-muted-foreground">
Learn how to use the library.
</div>
</Link>
</NavigationMenuLink>
<NavigationMenuLink asChild>
<Link href="#">
<div className="font-medium">Blog</div>
<div className="text-muted-foreground">
Read our latest blog posts.
</div>
</Link>
</NavigationMenuLink>
</li>
</ul>
</NavigationMenuContent>
</NavigationMenuItem>
<NavigationMenuItem>
<NavigationMenuTrigger>Simple List</NavigationMenuTrigger>
<NavigationMenuContent>
<ul className="grid w-[200px] gap-4">
<li>
<NavigationMenuLink asChild>
<Link href="#">Components</Link>
</NavigationMenuLink>
<NavigationMenuLink asChild>
<Link href="#">Documentation</Link>
</NavigationMenuLink>
<NavigationMenuLink asChild>
<Link href="#">Blocks</Link>
</NavigationMenuLink>
</li>
</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>
)
}
function ListItem({
title,
children,
href,
...props
}: React.ComponentPropsWithoutRef<"li"> & { href: string }) {
return (
<li {...props}>
<NavigationMenuLink asChild>
<Link href={href}>
<div className="text-sm leading-none font-medium">{title}</div>
<p className="text-muted-foreground line-clamp-2 text-sm leading-snug">
{children}
</p>
</Link>
</NavigationMenuLink>
</li>
)
}

View File

@@ -0,0 +1,40 @@
import {
Pagination,
PaginationContent,
PaginationEllipsis,
PaginationItem,
PaginationLink,
PaginationNext,
PaginationPrevious,
} from "@/registry/new-york-v4/ui/pagination"
export function PaginationDemo() {
return (
<div className="flex flex-col gap-6">
<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>
)
}

View File

@@ -0,0 +1,62 @@
import { Button } from "@/registry/new-york-v4/ui/button"
import { Input } from "@/registry/new-york-v4/ui/input"
import { Label } from "@/registry/new-york-v4/ui/label"
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/registry/new-york-v4/ui/popover"
export function PopoverDemo() {
return (
<Popover>
<PopoverTrigger asChild>
<Button variant="outline">Open popover</Button>
</PopoverTrigger>
<PopoverContent className="w-80" align="start">
<div className="grid gap-4">
<div className="grid gap-1.5">
<h4 className="leading-none font-medium">Dimensions</h4>
<p className="text-muted-foreground text-sm">
Set the dimensions for the layer.
</p>
</div>
<div className="grid gap-2">
<div className="grid grid-cols-3 items-center gap-4">
<Label htmlFor="width">Width</Label>
<Input
id="width"
defaultValue="100%"
className="col-span-2 h-8"
/>
</div>
<div className="grid grid-cols-3 items-center gap-4">
<Label htmlFor="maxWidth">Max. width</Label>
<Input
id="maxWidth"
defaultValue="300px"
className="col-span-2 h-8"
/>
</div>
<div className="grid grid-cols-3 items-center gap-4">
<Label htmlFor="height">Height</Label>
<Input
id="height"
defaultValue="25px"
className="col-span-2 h-8"
/>
</div>
<div className="grid grid-cols-3 items-center gap-4">
<Label htmlFor="maxHeight">Max. height</Label>
<Input
id="maxHeight"
defaultValue="none"
className="col-span-2 h-8"
/>
</div>
</div>
</div>
</PopoverContent>
</Popover>
)
}

View File

@@ -0,0 +1,16 @@
"use client"
import * as React from "react"
import { Progress } from "@/registry/new-york-v4/ui/progress"
export function ProgressDemo() {
const [progress, setProgress] = React.useState(13)
React.useEffect(() => {
const timer = setTimeout(() => setProgress(66), 500)
return () => clearTimeout(timer)
}, [])
return <Progress value={progress} className="w-[60%]" />
}

View File

@@ -0,0 +1,62 @@
import { Label } from "@/registry/new-york-v4/ui/label"
import {
RadioGroup,
RadioGroupItem,
} from "@/registry/new-york-v4/ui/radio-group"
const plans = [
{
id: "starter",
name: "Starter Plan",
description:
"Perfect for small businesses getting started with our platform",
price: "$10",
},
{
id: "pro",
name: "Pro Plan",
description: "Advanced features for growing businesses with higher demands",
price: "$20",
},
] as const
export function RadioGroupDemo() {
return (
<div className="flex flex-col gap-6">
<RadioGroup defaultValue="comfortable">
<div className="flex items-center gap-3">
<RadioGroupItem value="default" id="r1" />
<Label htmlFor="r1">Default</Label>
</div>
<div className="flex items-center gap-3">
<RadioGroupItem value="comfortable" id="r2" />
<Label htmlFor="r2">Comfortable</Label>
</div>
<div className="flex items-center gap-3">
<RadioGroupItem value="compact" id="r3" />
<Label htmlFor="r3">Compact</Label>
</div>
</RadioGroup>
<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-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
value={plan.id}
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 font-normal">
<div className="font-medium">{plan.name}</div>
<div className="text-muted-foreground leading-snug">
{plan.description}
</div>
</div>
</Label>
))}
</RadioGroup>
</div>
)
}

View File

@@ -0,0 +1,70 @@
import {
ResizableHandle,
ResizablePanel,
ResizablePanelGroup,
} from "@/registry/new-york-v4/ui/resizable"
export function ResizableDemo() {
return (
<div className="flex w-full flex-col gap-6">
<ResizablePanelGroup
direction="horizontal"
className="max-w-md rounded-lg border md:min-w-[450px]"
>
<ResizablePanel defaultSize={50}>
<div className="flex h-[200px] items-center justify-center p-6">
<span className="font-semibold">One</span>
</div>
</ResizablePanel>
<ResizableHandle />
<ResizablePanel defaultSize={50}>
<ResizablePanelGroup direction="vertical">
<ResizablePanel defaultSize={25}>
<div className="flex h-full items-center justify-center p-6">
<span className="font-semibold">Two</span>
</div>
</ResizablePanel>
<ResizableHandle />
<ResizablePanel defaultSize={75}>
<div className="flex h-full items-center justify-center p-6">
<span className="font-semibold">Three</span>
</div>
</ResizablePanel>
</ResizablePanelGroup>
</ResizablePanel>
</ResizablePanelGroup>
<ResizablePanelGroup
direction="horizontal"
className="min-h-[200px] max-w-md rounded-lg border md:min-w-[450px]"
>
<ResizablePanel defaultSize={25}>
<div className="flex h-full items-center justify-center p-6">
<span className="font-semibold">Sidebar</span>
</div>
</ResizablePanel>
<ResizableHandle withHandle />
<ResizablePanel defaultSize={75}>
<div className="flex h-full items-center justify-center p-6">
<span className="font-semibold">Content</span>
</div>
</ResizablePanel>
</ResizablePanelGroup>
<ResizablePanelGroup
direction="vertical"
className="min-h-[200px] max-w-md rounded-lg border md:min-w-[450px]"
>
<ResizablePanel defaultSize={25}>
<div className="flex h-full items-center justify-center p-6">
<span className="font-semibold">Header</span>
</div>
</ResizablePanel>
<ResizableHandle />
<ResizablePanel defaultSize={75}>
<div className="flex h-full items-center justify-center p-6">
<span className="font-semibold">Content</span>
</div>
</ResizablePanel>
</ResizablePanelGroup>
</div>
)
}

View File

@@ -0,0 +1,80 @@
import * as React from "react"
import Image from "next/image"
import { ScrollArea, ScrollBar } from "@/registry/new-york-v4/ui/scroll-area"
import { Separator } from "@/registry/new-york-v4/ui/separator"
export function ScrollAreaDemo() {
return (
<div className="flex flex-col gap-6">
<ScrollAreaVertical />
<ScrollAreaHorizontalDemo />
</div>
)
}
const tags = Array.from({ length: 50 }).map(
(_, i, a) => `v1.2.0-beta.${a.length - i}`
)
function ScrollAreaVertical() {
return (
<div className="flex flex-col gap-6">
<ScrollArea className="h-72 w-48 rounded-md border">
<div className="p-4">
<h4 className="mb-4 text-sm leading-none font-medium">Tags</h4>
{tags.map((tag) => (
<React.Fragment key={tag}>
<div className="text-sm">{tag}</div>
<Separator className="my-2" />
</React.Fragment>
))}
</div>
</ScrollArea>
</div>
)
}
export const works = [
{
artist: "Ornella Binni",
art: "https://images.unsplash.com/photo-1465869185982-5a1a7522cbcb?auto=format&fit=crop&w=300&q=80",
},
{
artist: "Tom Byrom",
art: "https://images.unsplash.com/photo-1548516173-3cabfa4607e9?auto=format&fit=crop&w=300&q=80",
},
{
artist: "Vladimir Malyav",
art: "https://images.unsplash.com/photo-1494337480532-3725c85fd2ab?auto=format&fit=crop&w=300&q=80",
},
] as const
function ScrollAreaHorizontalDemo() {
return (
<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">
<Image
src={artwork.art}
alt={`Photo by ${artwork.artist}`}
className="aspect-[3/4] h-fit w-fit object-cover"
width={300}
height={400}
/>
</div>
<figcaption className="text-muted-foreground pt-2 text-xs">
Photo by{" "}
<span className="text-foreground font-semibold">
{artwork.artist}
</span>
</figcaption>
</figure>
))}
</div>
<ScrollBar orientation="horizontal" />
</ScrollArea>
)
}

View File

@@ -0,0 +1,93 @@
import * as React from "react"
import {
ChartBarIcon,
ChartLineIcon,
ChartPieIcon,
CircleDashed,
} from "lucide-react"
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectLabel,
SelectTrigger,
SelectValue,
} from "@/registry/new-york-v4/ui/select"
export function SelectDemo() {
return (
<div className="flex flex-wrap items-start gap-4">
<Select>
<SelectTrigger className="w-[180px]">
<SelectValue placeholder="Select a fruit" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectLabel>Fruits</SelectLabel>
<SelectItem value="apple">Apple</SelectItem>
<SelectItem value="banana">Banana</SelectItem>
<SelectItem value="blueberry">Blueberry</SelectItem>
<SelectItem value="grapes" disabled>
Grapes
</SelectItem>
<SelectItem value="pineapple">Pineapple</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
<Select>
<SelectTrigger className="w-[180px]">
<SelectValue placeholder="Large List" />
</SelectTrigger>
<SelectContent>
{Array.from({ length: 100 }).map((_, i) => (
<SelectItem key={i} value={`item-${i}`}>
Item {i}
</SelectItem>
))}
</SelectContent>
</Select>
<Select disabled>
<SelectTrigger className="w-[180px]">
<SelectValue placeholder="Disabled" />
</SelectTrigger>
<SelectContent>
<SelectItem value="apple">Apple</SelectItem>
<SelectItem value="banana">Banana</SelectItem>
<SelectItem value="blueberry">Blueberry</SelectItem>
<SelectItem value="grapes" disabled>
Grapes
</SelectItem>
<SelectItem value="pineapple">Pineapple</SelectItem>
</SelectContent>
</Select>
<Select>
<SelectTrigger className="w-[180px]">
<SelectValue
placeholder={
<>
<CircleDashed className="text-muted-foreground" />
With Icon
</>
}
/>
</SelectTrigger>
<SelectContent>
<SelectItem value="line">
<ChartLineIcon />
Line
</SelectItem>
<SelectItem value="bar">
<ChartBarIcon />
Bar
</SelectItem>
<SelectItem value="pie">
<ChartPieIcon />
Pie
</SelectItem>
</SelectContent>
</Select>
</div>
)
}

View File

@@ -0,0 +1,22 @@
import { Separator } from "@/registry/new-york-v4/ui/separator"
export function SeparatorDemo() {
return (
<div>
<div className="flex flex-col gap-1">
<div className="text-sm leading-none font-medium">Tailwind CSS</div>
<div className="text-muted-foreground text-sm">
A utility-first CSS framework.
</div>
</div>
<Separator className="my-4" />
<div className="flex h-5 items-center gap-4 text-sm">
<div>Blog</div>
<Separator orientation="vertical" />
<div>Docs</div>
<Separator orientation="vertical" />
<div>Source</div>
</div>
</div>
)
}

View File

@@ -0,0 +1,95 @@
import { Button } from "@/registry/new-york-v4/ui/button"
import { Input } from "@/registry/new-york-v4/ui/input"
import { Label } from "@/registry/new-york-v4/ui/label"
import {
Sheet,
SheetClose,
SheetContent,
SheetDescription,
SheetFooter,
SheetHeader,
SheetTitle,
SheetTrigger,
} from "@/registry/new-york-v4/ui/sheet"
const SHEET_SIDES = ["top", "right", "bottom", "left"] as const
export function SheetDemo() {
return (
<div className="flex flex-col gap-6 md:flex-row">
<Sheet>
<SheetTrigger asChild>
<Button variant="outline">Open</Button>
</SheetTrigger>
<SheetContent>
<SheetHeader>
<SheetTitle>Edit profile</SheetTitle>
<SheetDescription>
Make changes to your profile here. Click save when you&apos;re
done.
</SheetDescription>
</SheetHeader>
<div className="grid flex-1 auto-rows-min gap-6 px-4">
<div className="grid gap-3">
<Label htmlFor="sheet-demo-name">Name</Label>
<Input id="sheet-demo-name" defaultValue="Pedro Duarte" />
</div>
<div className="grid gap-3">
<Label htmlFor="sheet-demo-username">Username</Label>
<Input id="sheet-demo-username" defaultValue="@peduarte" />
</div>
</div>
<SheetFooter>
<Button type="submit">Save changes</Button>
<SheetClose asChild>
<Button variant="outline">Close</Button>
</SheetClose>
</SheetFooter>
</SheetContent>
</Sheet>
<div className="flex gap-2">
{SHEET_SIDES.map((side) => (
<Sheet key={side}>
<SheetTrigger asChild>
<Button variant="outline" className="capitalize">
{side}
</Button>
</SheetTrigger>
<SheetContent side={side}>
<SheetHeader>
<SheetTitle>Edit profile</SheetTitle>
<SheetDescription>
Make changes to your profile here. Click save when you&apos;re
done.
</SheetDescription>
</SheetHeader>
<div className="overflow-y-auto px-4 text-sm">
<h4 className="mb-4 text-lg leading-none font-medium">
Lorem Ipsum
</h4>
{Array.from({ length: 10 }).map((_, index) => (
<p key={index} className="mb-4 leading-normal">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed
do eiusmod tempor incididunt ut labore et dolore magna
aliqua. Ut enim ad minim veniam, quis nostrud exercitation
ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit
esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
occaecat cupidatat non proident, sunt in culpa qui officia
deserunt mollit anim id est laborum.
</p>
))}
</div>
<SheetFooter>
<Button type="submit">Save changes</Button>
<SheetClose asChild>
<Button variant="outline">Cancel</Button>
</SheetClose>
</SheetFooter>
</SheetContent>
</Sheet>
))}
</div>
</div>
)
}

View File

@@ -0,0 +1,29 @@
import { Card, CardContent, CardHeader } from "@/registry/new-york-v4/ui/card"
import { Skeleton } from "@/registry/new-york-v4/ui/skeleton"
export function SkeletonDemo() {
return (
<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">
<Skeleton className="h-4 w-[150px]" />
<Skeleton className="h-4 w-[100px]" />
</div>
</div>
<div className="flex w-full flex-wrap items-start gap-4">
{Array.from({ length: 3 }).map((_, index) => (
<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" />
</CardHeader>
<CardContent>
<Skeleton className="aspect-square w-full" />
</CardContent>
</Card>
))}
</div>
</div>
)
}

View File

@@ -0,0 +1,44 @@
"use client"
import * as React from "react"
import { Label } from "@/registry/new-york-v4/ui/label"
import { Slider } from "@/registry/new-york-v4/ui/slider"
export function SliderDemo() {
return (
<div className="flex w-full max-w-sm flex-col flex-wrap gap-6 md:flex-row">
<Slider defaultValue={[50]} max={100} step={1} />
<Slider defaultValue={[25, 50]} max={100} step={1} />
<Slider defaultValue={[10, 20]} max={100} step={10} />
<div className="flex w-full items-center gap-6">
<Slider defaultValue={[50]} max={100} step={1} orientation="vertical" />
<Slider defaultValue={[25]} max={100} step={1} orientation="vertical" />
</div>
<SliderControlled />
</div>
)
}
function SliderControlled() {
const [value, setValue] = React.useState([0.3, 0.7])
return (
<div className="grid w-full gap-3">
<div className="flex items-center justify-between gap-2">
<Label htmlFor="slider-demo-temperature">Temperature</Label>
<span className="text-muted-foreground text-sm">
{value.join(", ")}
</span>
</div>
<Slider
id="slider-demo-temperature"
value={value}
onValueChange={setValue}
min={0}
max={1}
step={0.1}
/>
</div>
)
}

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