Compare commits

...

332 Commits
v0.8.4 ... dev

Author SHA1 Message Date
N1kl8s
47f53cb985 Migrate gitlab to gitea 2024-12-28 22:39:40 +01:00
1e1f335853 README.md aktualisiert 2024-12-28 22:32:52 +01:00
32d6b8b397 README.md aktualisiert 2024-12-28 22:26:05 +01:00
Boris
3eaef4a748
docs: fix broken link in footer (#950)
* Fix 404 link in footer

* Change link

Co-authored-by: zernonia <59365435+zernonia@users.noreply.github.com>

---------

Co-authored-by: zernonia <59365435+zernonia@users.noreply.github.com>
2024-12-17 16:35:47 +08:00
slow-groovin
5b1f952e0a
docs: missing .value for combobox usage (#947) 2024-12-17 12:32:11 +08:00
zernonia
693b0d2a08 fix(Sidebar): component import and registry dependencies 2024-12-17 12:21:45 +08:00
Fabian Bernhart
8a24d11a65
fix: add missing useMediaQuery in SidebarProvider.vue new-york style (#927) 2024-12-04 22:36:12 +03:30
Leo Chiu
e4fea78b33
docs: update code highlight and step configurations in vite.md (#936) 2024-12-04 10:22:52 +03:30
Stephan Koglin-Fischer
5869165a84
docs: added missing import in auto-form example (#919) 2024-12-03 12:36:41 +03:30
Damien Roche
857f10de51
docs: update responsive breadcrumb example (#933) 2024-12-03 12:26:41 +03:30
Thomas La Salmonie
5ada562803
build(cli): update proxy agent using undici agent to match ofetch requirements (#934) 2024-12-03 12:25:37 +03:30
zernonia
6c0ab55e92 chore: release v0.11.3 2024-11-19 16:13:22 +08:00
Fatih Solhan
3ddd70dd6b
docs: prevent shrinking theme customizer circles (#914)
* fix: prevent shrinking theme customizer circles

* use `shrink-0` instead of deprecated `flex-shrink-0`
2024-11-19 14:51:56 +08:00
Whbbit1999
2d5ad5b962
fix: mobile sidebar component #840 (#884)
* fix: sidebar component mobile #840

* chore: update new-york style too

* chore: run registry build

* revert: change in templates.ts

---------

Co-authored-by: zernonia <zernonia@gmail.com>
2024-11-19 14:51:24 +08:00
Leo Chiu
16f1d19460
fix(CLI): temporarily downgrade package to 3.2.0 due to TSInterfaceHeritage issue (#913)
Co-authored-by: leochiu <leochiu@hahow.in>
2024-11-19 14:31:12 +08:00
Louis Van Aken
ac69980ac9
fix: Error in the build of the auto form with Vite and TypeScript #870 (#896)
* fix: Error in the build of the auto form with Vite and TypeScript #870

* fix: new york registry
2024-11-16 17:40:29 +08:00
Titus Kirch
58fc125974
docs: fix primitive link in data-table.md (#898) 2024-11-16 17:34:26 +08:00
sinsky
383c846c02
docs: input components use "default-value" instead of "value" (#904)
* docs(example): input components used in SheetDemo use default-value instead of value

* docs(example): input components used in DialogDemo use default-value instead of value
2024-11-16 17:32:00 +08:00
sea
b7ef4653f7
docs: update * scrollbar style (#885) 2024-11-11 12:24:52 +03:30
Brian Le
384c87a91c
fix: add missing classes to button (#877) 2024-11-10 08:20:18 +03:30
Tony Zhang
75a5bce92f
docs: fix invisible code highlight in light mode (#875)
Signed-off-by: ZTL-UwU <zhangtianli2006@163.com>
2024-11-09 19:23:43 +03:30
Rhanlin
3d0db2de7b
docs: fix dialog form example onsubmit (#873) 2024-11-09 08:52:17 +03:30
Damien Roche
a5b25a9c43
fix: switch from placeholder: to data-placeholder: prefix class for SelectValue (#867) 2024-11-09 08:33:01 +03:30
zernonia
be888c80b6 chore: run changelogithub first 2024-11-04 15:20:54 +08:00
zernonia
93be758257 fix: module build 2024-11-04 15:17:59 +08:00
zernonia
26df827d33 chore: release v0.11.2 2024-11-04 14:53:22 +08:00
zernonia
f267b0ba4a chore: improve cd 2024-11-04 14:52:23 +08:00
zernonia
3c506ef188 chore: release v0.11.1 2024-11-04 14:45:18 +08:00
zernonia
df60e15a97 chore: improve cd 2024-11-04 14:43:40 +08:00
Selemon Brahanu
3646203d4f
refactor(CLI): base dependencies not installing when using shadcn-nuxt (#821)
* refactor: 815 shadcn-nuxt dependencies and CLI init

Closes: 815

* chore: remove unnecessary dependencies

* chore: update pnpm-lock.yaml file

* chore: cleanup

* chore: update pnpm-lock

---------

Co-authored-by: zernonia <zernonia@gmail.com>
2024-11-04 14:20:14 +08:00
zernonia
68c40f6908 chore: codegen 2024-11-03 23:52:53 +08:00
Hookin.
ff1b5f0a1b
fix(Sidebar): use open.value when setting the cookie (#862) 2024-11-03 17:11:46 +08:00
苗大
15f3eb305b
fix: missing NavigationMenuViewport component (#860) 2024-11-02 23:38:57 +08:00
Rnz Brngn
baceb4ce91
docs(blocks): correct tooltip prop for dynamic binding (#850) 2024-11-02 23:37:12 +08:00
IllustrisJack
2cb767122a
fix(Nuxt): component override warnings (#838) 2024-11-02 23:33:09 +08:00
Maxim Kim
eff3e75466
fix(Sidebar): missing slot into component (#847)
* fix(SidebarMenuAction): add slot into component

* fix(SidebarMenuAction): add changes in new-york style
2024-11-02 23:31:39 +08:00
xiaomo
83419c4dc3
docs: replace unexported cssVariables with highlight (#841) 2024-10-29 08:33:28 +03:30
xiaomo
52b9b20b3c
docs: add theme to highlight code block (#831) 2024-10-28 15:32:30 +03:30
zernonia
e0d4980e31
feat: Sidebar (1/2) (#827)
* feat: add default sidebar

* chore: add images, docs item

* refactor: rename and fix styling`

* feat: add new-york style

* chore: move typescript to catalog

* docs: fix block preview

* chore: build registry, add sidebar block

* docs: update sidebar demo

* chore: bump radix-vue

* chore: fix build
2024-10-28 01:05:55 +08:00
Mohammed Essam Helewa
6760ebb5ae
feat: enhance the behavior of Switch component (#835)
* Add named slot inside SwitchThumb

* Add documentation for switch thumb slot

* chore: re-build registry
2024-10-26 21:52:03 +03:30
Ciro Lo Sapio
d143272fb8
docs: update footer links (#820) 2024-10-22 20:17:17 +03:30
Otabek
4f3bb61283
docs: update vite.md for latest Vite configuration in Vue projects (#817) 2024-10-22 20:09:47 +03:30
zernonia
f9615d3657 chore: use format=cover 2024-10-17 15:22:02 +02:00
sea
d48ffcfffb
chore(vscode): add tailwindcss extensions (#810)
* chore(vscode): add tailwindcss extensions

* chore: add tailwind setting
2024-10-17 09:16:39 +03:30
sea
e63d5553e3
docs: fix navigation menu example link (#809) 2024-10-16 12:22:53 +03:30
zernonia
a01a1bd94d docs: fix carbon 2024-10-16 14:11:36 +08:00
zernonia
f5b02256bc chore: add carbon ads 2024-10-15 22:24:13 +08:00
zernonia
c164421e58 chore: release v0.11.0 2024-10-14 20:39:41 +08:00
zernonia
5c71911104 chore: run lint:fix, re-build registry 2024-10-14 19:48:05 +08:00
smdbs
982f918e53
docs: fix highlighted line (#807) 2024-10-14 19:42:15 +08:00
zernonia
f8f3fc754f
feat: support tailwind prefix (#619)
* chore: enable tw prefix

* chore: enable tw  prefix during init

* fix: cater for cn function

* fix: prevent transforming importDeclaration

* chore: update registry to make sure tailwind prefix parse correctly

* chore: fix wrong import

* chore: checkpoint

* refactor: goodbye ts-morph

* chore: remove ts-morpg

* chore: update test

* chore: cleanup

* chore: fix test

* fix: move vue-metamorph to dep

* refactor: transform tw prefix by specific case

* fix: transform-sfc not parsing .ts file

* fix: prefix double quote

* chore: patch vue-eslint-parser

* refactor: transform to cater only for class in sfc

* refactor: replace detypes with @unovue/detypes

* chore: update test snapshot

* chore: update pnpm-lock, fix import

* chore: bump detypes version

* chore: update deps
2024-10-14 19:39:58 +08:00
Roga
1c7c60330a
docs: update nuxt.md (#804)
Expanded on the TailwindCSS installation instructions.
The added information is important because from time to time the Nuxt module installation throws the following error:
```bash
$ npx nuxi@latest module add tailwindcss

 ERROR  [GET] "https://npm.fontawesome.com/@nuxtjs/tailwindcss/latest": 401 Unauthorized                                              8:39:48 a.m.

  at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
  at async $fetch2 (/C:/Users/ryanr/AppData/Local/npm-cache/_npx/b95349761371180e/node_modules/nuxi/dist/shared/nuxi.b88e7b0f.mjs:340:15)
  at async resolveModule (/C:/Users/ryanr/AppData/Local/npm-cache/_npx/b95349761371180e/node_modules/nuxi/dist/chunks/add2.mjs:262:15)
  at async Object.setup (/C:/Users/ryanr/AppData/Local/npm-cache/_npx/b95349761371180e/node_modules/nuxi/dist/chunks/add2.mjs:136:15)
  at async runCommand$1 (/C:/Users/ryanr/AppData/Local/npm-cache/_npx/b95349761371180e/node_modules/nuxi/dist/shared/nuxi.3e201632.mjs:1620:5)
  at async runCommand$1 (/C:/Users/ryanr/AppData/Local/npm-cache/_npx/b95349761371180e/node_modules/nuxi/dist/shared/nuxi.3e201632.mjs:1639:11)
  at async runCommand$1 (/C:/Users/ryanr/AppData/Local/npm-cache/_npx/b95349761371180e/node_modules/nuxi/dist/shared/nuxi.3e201632.mjs:1639:11)
  at async runMain$1 (/C:/Users/ryanr/AppData/Local/npm-cache/_npx/b95349761371180e/node_modules/nuxi/dist/shared/nuxi.3e201632.mjs:1777:7) 
```
Manually installing the package and then adding to the config fixes this issue.
2024-10-11 13:05:31 +08:00
Jan Kremlacek
ff6d9d0da6
chore(Slider): support vertical orientation (#734) 2024-10-08 13:46:53 +08:00
Carlos Marques
603497e822
docs(DataTable): fix broken link (#794) 2024-10-08 13:42:16 +08:00
Alex
235ec8a691
docs: update aspect-ratio example (#795)
Added 'w-full h-full' to <img> in example.
2024-10-08 13:41:00 +08:00
AlvesJorge
ec350df9d4
fix(cli): replace tab symbol with spaces (#756) 2024-09-19 14:59:05 +03:30
Sadegh Barati
64b6d6ac38
docs: fix and cleanup some of demos (#773)
chore: update deps
2024-09-19 14:58:28 +03:30
Vladimir Ivakhno
775d59b51d
docs: replace lucide-react with lucide-vue-next (#770) 2024-09-18 08:39:42 +03:30
Ralf Heinsoo
7c9aac46a1
docs: update default-value prop in calendar year-month select example (#755) 2024-09-09 14:09:47 +03:30
Krishna Bhandari
ccbdc2f90f
docs: update laravel.md creating project command (#752) 2024-09-09 14:04:10 +03:30
Horu
573443f352
chore(nuxt): add nuxt modules package required for development (#750) 2024-09-04 23:33:42 +03:30
Roman Hrynevych
481bebf413
docs(data-table): add DataTableReactive example (#744)
* docs(table): add `DataTableReactive` example and tanstack docs

* chore: build:registry

* docs: refrase

* docs(data-table): update `defineModel`

* fix(data-table): change `reactive` data to `shallowRef`

* docs(data-table): add info about `ref` and `shallowRef`
2024-08-31 11:01:36 +03:30
Sadegh Barati
cc84ac172c
fix: carousel ref unwrapping with top-level property in template (#743) 2024-08-30 23:32:07 +03:30
Selemondev
501137a672
feat: auto install @nuxtjs/color-mode module (#737)
* feat(app): #537 auto install the  module

This pull request is intended to install the  module during the installation of the  module

Closes: #537

* docs: update Nuxt dark mode

---------

Co-authored-by: selemondev-triply <selemon@triply.co>
2024-08-30 21:32:24 +03:30
Roman Hrynevych
f73e1ddaaf
docs: add vertical Tabs example (#739)
* feat(docs): add vertical Tabs example and components for account/password management

* fix(TabsTrigger): wrap slot content in a span with truncate class for better text handling

* chore: build:registry
2024-08-30 20:51:17 +03:30
Roman Hrynevych
85b10641c2
fix(Select): add truncate and text-start (#726)
* fix(select): add `shrink-0` to Icon
2024-08-22 11:13:55 +03:30
Roman Hrynevych
68d9804109
docs: add TagsInput form example (#725) 2024-08-22 10:47:56 +03:30
βoγ Woηdεr
1a1dd4a611
docs: update Tags with Combobox example to make Popover close by clicking outside (#723) 2024-08-21 08:52:48 +03:30
Damien Roche
0fc50183e4
docs: fix typo in TeamMembers.vue (#722) 2024-08-21 08:17:07 +03:30
Oscar Recio
2a50f65ec5
fix: arrange range calendar day alignment 2024-08-20 15:50:03 +03:30
Sinh
3dc6e10897
chore: update autoform emit submit event type(#716) (#717)
If compiler options are only added to just tsconfig.json, module resolution fails for `@/lib/utils` during build.

<!---☝️ PR title should follow conventional commits (https://conventionalcommits.org) -->

<!-- Please ensure there is an open issue and mention its number as #123 -->

<!-- What types of changes does your code introduce? Put an `x` in all the boxes that apply. -->

- [ ] 📖 Documentation (updates to the documentation, readme or JSdoc annotations)
- [ ] 🐞 Bug fix (a non-breaking change that fixes an issue)
- [x] 👌 Enhancement (improving an existing functionality like performance)
- [ ]  New feature (a non-breaking change that adds functionality)
- [ ] 🧹 Chore (updates to the build process or auxiliary tools and libraries)
- [ ] ⚠️ Breaking change (fix or feature that would cause existing functionality to change)

The change is mean to allow event type to support `values: z.infer<typeof schema>`, not only `values: Record<string, any>`

Resolves #716

<!-- Add screenshots to help explain the change. -->

<!-- Put an `x` in all the boxes that apply. -->
<!-- If your change requires a documentation PR, please link it appropriately -->
<!-- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->

- [x] I have linked an issue or discussion.
- [ ] I have updated the documentation accordingly.
2024-08-20 00:05:37 +08:00
jtidwe11
12657dab5c
docs: update vite instructions (#713)
If compiler options are only added to just tsconfig.json, module resolution fails for `@/lib/utils` during build.
2024-08-16 12:14:08 +03:30
Giga
bdcd555c84
refactor: fix import path and add optional class prop to NumberFieldInput (#707) 2024-08-13 15:04:10 +03:30
Sadegh Barati
514949706f
chore: fix StepperForm and NumberFieldForm examples (#711)
update deps
2024-08-13 14:47:12 +03:30
Giga
0d5d2d0d10
docs: fix a typo (#696) 2024-08-02 20:07:36 +03:30
Oleh Aleinyk
6ac90c10ab
docs: don't source radix-vue from catalog: in code editor context (#693) 2024-08-01 08:53:27 +03:30
Alan Kersaudy
5708af535a
docs: fix astro.md globals.css import path 2024-07-29 08:19:31 +03:30
Alan Kersaudy
b2952b6416
docs: astro dark-mode.md typo (#686) 2024-07-29 08:16:43 +03:30
Sadegh Barati
0ee23fb53d
docs: add dialog with form example (#682) 2024-07-26 00:56:56 +03:30
Sadegh Barati
dbb29de523
docs: add Stepper examples (#681) 2024-07-25 22:28:48 +03:30
Gerzon Rangel
183da0890d
fix: changed color of stepper component icons (#676) 2024-07-25 07:32:25 +03:30
Sadegh Barati
bec12b1653
chore: add pnpm catalog (#674)
run nolyfill to found redundant packages
update deps
2024-07-23 14:17:28 +08:00
Can Stand
45557fd8e7
fix(Calendar): data-outside-month should be data-outside-view (#672) 2024-07-23 00:41:23 +03:30
Eduard Predescu
6757908fe1
feat(Stepper): implement stepper (#669) 2024-07-22 08:43:10 +03:30
Kikky Skrlin
6550f34842
docs: add comment how to enable transitions when switching mode (#662)
useColorMode disables transitions by default.
2024-07-22 08:00:17 +03:30
Sadegh Barati
4d4804231b chore: update deps 2024-07-19 16:00:15 +03:30
Hannes
edad815fd9
docs: add @internationalized/date package installation instruction (#654) 2024-07-10 07:58:54 +03:30
Ilya Torovin
9ddbd5c0d2
docs: expanding table in data-table component 2024-07-08 15:08:54 +03:30
Kristian Arvidsson
7a03a4caab
fix: add chartRef to solve gradiant in same color issue (#636) 2024-07-08 07:17:07 +03:30
sadeghbarati
b371e5f3ad chore: update deps 2024-07-07 16:10:42 +03:30
Roman Hrynevych
d50ea38f4a
fix(Form): added name to fieldState (#643) 2024-07-02 13:41:53 +03:00
Yakir Reznik
f03778304d
docs: replace import of custom 'ListItem' component with inline html in Navigation Menu (#642) 2024-07-02 08:21:24 +03:30
Saeid Zareie
0f1befad1f
docs: adding syncing section (#640)
* docs: adding syncing section

* chore: fixing constant link
2024-06-30 21:06:32 +03:30
yali
cdfbd51190
fix: remove bg-white in separator in dark mode (#634) 2024-06-27 08:51:07 +03:30
Miguel
f597d258b0
feat(separator): add label props on separator component (#626) 2024-06-23 22:53:34 +03:30
Hendrig Sellik
eeca60d09b
fix: make NumberField more flexible (#613) (#623) 2024-06-23 22:28:28 +03:30
Jie
6aebbc1a90
docs: fix modify the style generation location (#625) 2024-06-21 12:35:59 +03:30
sadeghbarati
2e93d15af4 chore: update deps
close #584

root
www
cli
module
2024-06-18 09:26:41 +03:30
zernonia
f1a2dab738 docs: add copy code 2024-06-18 09:36:02 +08:00
Saeid Zareie
63732f1c47
docs: adding changelog page (#617)
* chore: adding changelog page

* docs: cleanup

---------

Co-authored-by: zernonia <zernonia@gmail.com>
2024-06-18 09:18:04 +08:00
Saeid Zareie
9d303e91ca
chore: updating source/docs to match the changes in shadcn/ui version (#616) 2024-06-18 08:46:28 +08:00
Artemiy
edc4ee9437
fix(carousel): reading properties of undefined api (#612) 2024-06-15 11:10:38 +03:30
pereira0x
36ad0f846d
chore: change label/value order in tasks priorities data (#603) 2024-06-15 11:10:07 +03:30
Daniel Roe
88a93ce22e
chore: indicate compatibility with new v4 major (#610) 2024-06-14 17:43:04 +03:30
Kyle Milloy
8cbd55660f
fix: export TableFooter.vue from ui/table components (#605) 2024-06-13 21:55:08 +03:30
roiLeo
ae43e5e08a
chore: rename Nuxt.js to Nuxt (#602) 2024-06-13 21:25:37 +03:30
Sadegh Barati
a1faecc7d2
chore: use nolyfill to speed up installation process (#597) 2024-06-09 13:43:11 +03:30
Sadegh Barati
9960925881
chore: update deps and fix tailwindcss for charts reproductions (#596) 2024-06-09 09:15:46 +03:30
Roman Slonov
ce6eb79a3d
feat: add number-field component (#571) 2024-06-03 12:59:39 +03:30
zernonia
f48d1d4f4c docs: fix data-table colspan 2024-06-02 00:16:12 +08:00
zernonia
47ff130ffb docs: fix form profile 2024-06-02 00:05:39 +08:00
zernonia
e54e06bb98 docs: fix form page 2024-06-02 00:01:53 +08:00
Simone Colabufalo
15f630552f
refactor: FormItem single script setup (#573)
* refactor(form): move FORM_ITEM_INJECTION_KEY to a different module

By exporting the  from the  file, we can remove the need of having
multiple script tags inside the component.
This change helps with the linters and formatters, expecially with  defaults.
2024-05-31 20:03:40 +03:30
zernonia
80f06066c6 chore: bump radix-vue version 2024-05-28 13:43:52 +08:00
Muhammad Mahmoud
6a6968b8ee
ci: use corepack to automatically handle pnpm version & use GitHub composite actions (#568) 2024-05-25 08:36:39 +03:30
Muhammad Mahmoud
ebe3e6d8d2
chore: remove file's duplicate extension (#569) 2024-05-25 03:53:23 +03:30
Muhammad Mahmoud
bd0a8206ef
chore(module): add critical packages to module's dependencies (#531) 2024-05-24 17:03:48 +03:30
久染 | JiuRan
63bb21808f
docs: change forms href (#553) 2024-05-23 18:13:24 +08:00
David Dong
e63eec02e4
docs(blocks): add TooltipProvider for Dashboard03,05,06,07 (#562) 2024-05-22 15:36:08 +03:30
sadeghbarati
dce0bd0c30 chore: fix pipeline 2024-05-21 15:13:34 +03:30
sadeghbarati
af5d0b323d chore: update deps
root
apps/www

fix line numbers line-height
2024-05-21 15:06:41 +03:30
zernonia
1aa9b6172e chore: release v0.10.5 2024-05-21 13:28:12 +08:00
zernonia
18c64563a1 fix: chart installations error 2024-05-21 13:22:26 +08:00
Dunqing
175762a959
chore: bump detypes (#548)
* chore: bump detypes

* test: update snapshot

* test: add test to check all type references

---------

Co-authored-by: zernonia <zernonia@gmail.com>
2024-05-21 13:12:12 +08:00
tangyongjie
61dcd63ef2
docs: fix installation configure components.json (#556) 2024-05-16 18:38:13 +03:30
Safal Pokharel
61ed5f1758
docs: fix breadcrumb.md import path for components (#552) 2024-05-15 19:08:06 +03:30
Sadegh Barati
5eb41b0ba9
chore: add vue-sonner with dialog example (#551) 2024-05-14 14:36:06 +03:30
Sadegh Barati
7f73e4d74b
fix: v-calendar range picker is overflown in mobile screen (#550) 2024-05-14 13:36:30 +03:30
zernonia
ead6ad8437 chore: fix pnpm-lock 2024-05-13 15:08:39 +08:00
zernonia
3e5dd97fc5 chore: rerun pnpm-lock 2024-05-13 14:56:31 +08:00
Dunqing
cc3503689f
chore: bump vitest to 1.6.0 (#549)
* chore: bump vitest to 1.6.0

* fix: update
2024-05-13 14:32:36 +08:00
zernonia
08e10236fc chore: run lint 2024-05-13 14:25:38 +08:00
Roman Hrynevych
7fdf916443
refactor: change setValues to setFieldValue for better validation (#547) 2024-05-13 00:50:25 +03:00
Roman Hrynevych
b20a45552a
fix: update PinInput form (#544)
* fix: update default modelValue for Pin input

* fix: update `vee-validate` pin-input setValue

* chore: run `build:registry` command
2024-05-10 15:19:44 +03:00
Alan Ye
0721d1c864
docs: fix typo PIN Input was misspelled as Pin Input (#542) 2024-05-09 20:05:39 +03:30
Sadegh Barati
60e12b962f
fix: add tailwind.config and global CSS overwrite warning (#541) 2024-05-09 11:21:00 +03:30
Sadegh Barati
bcbfab5c9b
chore: add intellisense for vue component inside .md files (#534) 2024-05-07 16:34:51 +08:00
Muhammad Mahmoud
631ffb81d5
feat(module): auto inject lib/utils & refresh the playground (#528)
* chore: add dev:nuxt script

* chore(module): add `.npmrc` to playground

* chore: bump 'nuxtjs/tailwindcss' and add it to modules

* chore: add schema to components.json

* chore: bump playground deps

* fix: add missing tailwind.css by the cli

* chore: bump tailwind config

* chore(playground): bump Button component

* chore: add comments

* refactor: simplify components registration

* chore(module): bump deps

* chore(module): remove `oxc-parser`

* chore: dedupe

* feat(module): auto generate `lib/utils`

* feat(module): refresh playground style and dark mode

* chore: revert components registration and link the comment

* chore: readd oxc-parser

* chore: bump vitest
2024-05-07 12:44:15 +08:00
Andrei Ivanov
3dd2fbda95
docs: change controlled CommandDialog from prop to event (#533) 2024-05-07 08:08:56 +03:30
zernonia
9ebbd30175 chore: bump deps 2024-05-06 23:50:57 +08:00
zernonia
d17bb2bb2a chore: bump deps 2024-05-06 23:32:31 +08:00
Sam K
10319df960
fix(cli): add an option to component.json to resolve nuxt buildDir (#447)
* fix(cli): add an option to `component.json` to resolve nuxt buildDir dynamically

* feat: add `tsConfigPath` option

* docs: updated installation docs to reflect the new option `tsConfigPath`

* feat: include option in init

---------

Co-authored-by: Sadegh Barati <sadeghbaratiwork@gmail.com>
Co-authored-by: zernonia <zernonia@gmail.com>
2024-05-06 16:13:46 +08:00
Michael DeGiovanni
e67c98b485
fix: AutoFromFieldObject not utilising base schema (#526) 2024-05-06 15:22:02 +08:00
zernonia
9dfb0b86c0 feat: dynamic breadcrumb 2024-05-01 16:06:49 +08:00
zernonia
b468704853 chore: update announcement 2024-05-01 15:37:45 +08:00
zernonia
db3fff29c8 fix(Chart): maximum call stack 2024-05-01 11:56:54 +08:00
zernonia
77c6a16040
feat: Charts (#166)
* chore: update unovis deps

* chore: update color to use the themePrimary

* docs: use gradient for overview component

* docs: add themePopover to MainLayout

* docs: enable global theme on every page

* feat: introduce area, line, bar, donut chart

* feat: add more props

* fix: revert old pipeline

* fix: patch @unovis/vue deps

* fix: patch @unovis/vue deps again

* chore: revert unovis/ts to 1.2.1

* chore: wip

* docs: add alpha tag, fix tooltipo styling

* docs: add charts installations step

* feat: use generic, add better color

* chore: build registry

* feat: improve generic props

* chore: build registry

* docs: add alpha label

* fix: collapsible not open correctly

* docs: add badge to mobile nav

* chore: better types

* chore: run registry

* chore: wip

* fix: crosshair issue

* chore: fix type, import missing error

* chore: build registry

* chore: arrange interface, expose margin, slot

* chore: build registry

* docs: guide page
feat: add prop to barchart

* chore: fix pnpm-lock

* chore: add feature

* chore: run build registry

* refactor: change color var

* feat: codegen

* chore: add meta tables

* feat: add line, area example

* feat: bar and donut examples

* docs: codege

* chore: build registry

* docs: improve chart doc

* chore: fix missing icon package
2024-05-01 11:34:58 +08:00
zernonia
32d7b9ca4a
feat: Auto Form (#497)
* chore: initial poc

* chore: cleanup, log on the docs

* feat: add file component

* feat: typing for nested config

* feat: more props for form field

* feat: export field component, expose more slotprops

* feat: array, config label

* feat: improve array

* feat: support custom form state

* chore: prevent schema props showing on attribute

* feat: dependencies rendering

* refactor: change name to fieldName to allow easier slotProps binding

* feat: improve file upload

* feat: expose custom auto form slot

* chore: bump

* chore: replicate to default styling

* chore: build registry

* fix: export component before init

* chore: add examples

* chore: add form api example

* fix: warning in console

* chore: bump package version

* chore: update example, complete md

* feat: allow zod description as label, allow custom component

* docs: fix link

* feat: show required field for object

* chore: replace enumProps
2024-05-01 09:39:09 +08:00
Sadegh Barati
18e40cf002
chore: typos (#519) 2024-04-28 09:52:26 +03:30
zernonia
f6bc669106 chore: kick ci 2024-04-25 23:48:42 +08:00
zernonia
d21fa783e9 fix: node-version error 2024-04-25 23:42:53 +08:00
zernonia
8633bc95f2 chore: release v0.10.4 2024-04-25 23:40:50 +08:00
chachew
93b98252a4
docs: update form.md (#511)
Small text change for clarity
2024-04-25 23:03:53 +08:00
Sadegh Barati
5f774f83d9
fix: replace lodash.template with lodash-es to prevent vulnerabilities (#515) 2024-04-25 23:03:26 +08:00
zernonia
919770c6ca chore: kick pipeline 2024-04-24 11:49:13 +08:00
zernonia
042321a3f4 chore: fix pipeline 2024-04-24 11:45:39 +08:00
sadeghbarati
0937fb6af5 chore: bump deps
root
www
cli
2024-04-23 09:45:42 +03:30
chachew
8e132b1987
docs: fix typo in vite.md (#510) 2024-04-22 19:02:24 +03:30
chachew
95f4e6b56d
docs: update vite.md (#505) 2024-04-20 11:35:55 +03:30
Josh King
c77b087c9c
chore: update incorrect link (#499) 2024-04-20 10:47:55 +03:30
Eduard Predescu
6774842937
docs: remove unused placeholder prop (#506) 2024-04-19 04:42:28 +03:30
Eduard Predescu
57ee2fc017
docs: fix prev button on second month in DatePickerWithIndependentMonths.vue (#501) 2024-04-17 23:07:32 +03:30
Eduard Predescu
fbe14a20c1
docs: multiple month range picker (#496) 2024-04-15 23:56:23 +03:30
Selemondev
f7e475f16a
docs: use nuxi command for modules installation (#493) 2024-04-14 22:54:10 +03:30
Onur Usluca
cedce319ba
docs: update popover.md (#491)
Description only had the closing tag and it was wrong. Added the opening tag, fixed the closing tag and some text.
2024-04-14 18:10:16 +08:00
Saeid Zareie
798aa0ec24
fix: oxc-parser wasm issue (#484) 2024-04-13 09:45:06 +08:00
LeeChan
f51a8da08d
docs: add vaul-vue scale background note (#481) 2024-04-12 19:33:24 +03:30
Sadegh Barati
8052eb1a33 chore: update deps, fix build error
- root
- www
- cli
- module

remove @iconify/json pkg
2024-04-11 19:06:45 +03:30
Sadegh Barati
cfccb8e510
feat: new calendar components (#468) 2024-04-11 18:42:20 +03:30
LeeChan
11d77fb9a1
docs: include Toaster component in toast.md usage (#479) 2024-04-11 15:21:16 +03:30
Kristian Frølund
e36402491b
fix: multiple thumb sliders (#464) 2024-04-10 20:58:49 +03:30
zernonia
92d3e2c7f0 chore: release v0.10.3 2024-04-06 11:22:45 +08:00
Sadegh Barati
9712ba9bae
fix(CLI): shadcn-vue init not installing all dependencies or devDependencies (#469) 2024-04-06 10:57:20 +08:00
Jonathan Wilke
75d10d5b8e
docs: add documentation for aliases.ui (#466) 2024-04-04 16:04:38 +03:30
zernonia
8aa0e2a41d chore: bump radix-vue 2024-04-04 17:21:19 +08:00
Kristian Frølund
cb955c82a7
fix: add type button to v-calendar navigations to prevent form submission (#461) 2024-04-04 12:07:18 +03:30
zernonia
0c3c703cb0 chore: fix column not hidden 2024-04-03 23:46:49 +08:00
zernonia
3e2e4ec5a3 chore: fix column not hidden 2024-04-03 23:33:34 +08:00
zernonia
4f9a5af907 chore: update blocks metadata 2024-04-03 23:23:58 +08:00
zernonia
d868c9fd48 chore: updates min-size 2024-04-03 23:15:20 +08:00
Lucas Bois
81a7515617
feat(Blocks): create Dashboards 05, 06 and 07 (#452)
* Create Dashboard05.vue

* chore: update index.ts

Add Dashboard05 to index.ts

* chore: create Dashboard05.vue new-york

Create Dashboard05 new-york version

* build: build registry

* chore: add Dashboard06 and Dashboard07

* fix: update `html-for` to `for`
2024-04-03 22:52:59 +08:00
Lucas Bois
f91cfe9add
chore: add missing Separator import to Announcement.vue badge (#450) 2024-04-01 23:22:14 +03:30
Mike Ovyan
f55633ab8a
chore: fix wrong class at Authentication03.vue block example (#449)
used grid gap-2 instead of space-y-2
2024-04-01 23:09:51 +03:30
Lucas Bois
46f7ffb65a
chore: update Dashboard03.vue (#446)
Fix mismatch header lines in Block - Dashboard03 component.
2024-04-01 18:23:44 +08:00
Roman Slonov
c1c1f171fe
docs: fix AccountForm.vue example select language empty state (#442) 2024-03-28 16:04:39 +03:30
Artemiy
623fe06aa6
fix(hover-card): add emits from radix-vue (#435) 2024-03-25 16:53:26 +03:30
Sadegh Barati
1f97ce0d24
fix: pin input highlight border (#432) 2024-03-24 00:26:18 +03:30
Sadegh Barati
cd3d24438a fix: can't install drawer in js project 2024-03-24 00:08:31 +03:30
zernonia
a03bace32c
feat: Blocks (#428)
* chore: build registry

* feat: block preview

* refactor: change to use iframe
feat: add more blocks

* chore: fix build

* feat: add all other blocks

* feat: add copy button

* chore: cleanup
2024-03-23 18:24:48 +08:00
Dev By Ray
8982ec3862
docs: fix combobox.md ref usage (#420) 2024-03-21 19:16:22 +03:30
Sadegh Barati
d34c620055
chore: lint and enable Volar hybrid mode (#419)
* chore: update deps

* feat: enable Volar hybrid mode

* chore: lint

* chore: build registry
2024-03-20 22:29:42 +03:30
tortorse
24fc8f755a
chore: correct the spelling of col-span to colspan (#417) 2024-03-20 21:21:16 +03:30
Carl Lewis Castillo
ac434a3d24
chore: correct spelling in search functionality (#418)
Changed 'serachValue' to 'searchValue' for consistent spelling.
2024-03-20 21:15:11 +03:30
helloAhao096
67777d64f3
docs: resizable verticalDemo example code error (#414) 2024-03-17 20:15:12 +03:30
Saeid Zareie
38f5672e59
docs: adding contribution guide (#413) 2024-03-15 21:35:57 +03:30
zernonia
874ee643aa chore: release v0.10.2 2024-03-15 09:09:10 +08:00
zernonia
8e58b6e196 chore: update deps 2024-03-15 09:08:56 +08:00
Sadegh Barati
4672f2247d chore: update deps
root
apps/www
packages/cli
2024-03-14 21:00:25 +03:30
Lucas Bois
4aeb6140d4 docs: fixes breadcrumb installation command (#412) 2024-03-14 20:51:20 +03:30
zernonia
d4c7b0107f docs: fix sb having wrong path 2024-03-14 20:53:10 +08:00
zernonia
6d0cde4b8e docs: stackblitz not showing code on app.vue 2024-03-14 18:46:29 +08:00
zernonia
72f9bd5ef5
refactor: code preview (#411)
* feat: generate code dynamically

* chore: cleanup and transform path on component

* feat: create config sheet

* feat: code wrapper

* fix: not acting immediately

* chore: add key to vnode

* chore: add vue-sonner to demos dependencies, add placeholder for codeConfigs

* chore: fix wrong icons

* chore: improve crawling logic

---------

Co-authored-by: Sadegh Barati <sadeghbaratiwork@gmail.com>
2024-03-14 18:28:13 +08:00
Saeid Zareie
4d08adc81e
feat: breadcrumb component (#405)
* feat: adding breadcrumb component, resolves #395

* fix: revert kbd component in main layout

* feat: add slot for `BreadcrumbEllipsis` icons too

build registry, bump radix-vue

* refactor: using primitive instead of computed

* chore: update

---------

Co-authored-by: Sadegh Barati <sadeghbaratiwork@gmail.com>
2024-03-13 15:33:22 +08:00
Sadegh Barati
454ecf0df7
feat: PinInput with input-otp demos and styles (#402) 2024-03-10 19:15:39 +03:30
Sadegh Barati
7af3b612d6
fix: unable to close Combobox with esc key (#401) 2024-03-09 20:36:35 +03:30
Sadegh Barati
f2f5641b33
chore: docs stuff (#400) 2024-03-09 20:21:24 +03:30
Saeid Zareie
f530e3e4a7
docs: fix small issue of collapsed sidenav in mail page (#396) 2024-03-08 19:51:47 +03:30
Davlatov Shahzod
7e8d658c21
docs: resizable demos not working (#399) 2024-03-08 19:43:59 +03:30
zernonia
86a0ef2854 chore: fix hydration issue 2024-03-07 21:43:17 +08:00
Saeid Zareie
d832e9f48a
docs: adding mail example (#389) 2024-03-07 16:52:39 +03:30
Saeid Zareie
e817da4b8b
docs: adding dark-mode setup page (#384) 2024-03-07 16:33:49 +03:30
zernonia
b516995e15 chore: release v0.10.1 2024-03-07 13:33:04 +08:00
zernonia
d5802370f0 chore: remove uneeded plugin 2024-03-07 13:32:15 +08:00
Sadegh Barati
a3e1db9578
fix: make c12 look only for components.json for now (#388) 2024-03-07 09:01:07 +03:30
zernonia
33003deadf chore: release v0.10.0 2024-03-07 08:53:13 +08:00
Sadegh Barati
a02d16ca62 chore: build registry 2024-03-06 18:25:40 +03:30
Sadegh Barati
126e1877fd
feat: resizeable component (#385) 2024-03-06 18:21:31 +03:30
Sadegh Barati
0e84af73de
feat: use unjs modules and improve cli from main shadcn-ui source, custom ui dir (#324)
* feat: add devDeps, add nypm for installing deps

* feat: custom ui dir

* refactor: use consola instead of chalk

* test: ui alias

* refactor: import { z } from 'zod' instead of *, replace node:path with pathe

* chore: add components name to `configFile` option

* chore: update `c12` which fix json5 parse issue

and it also supports .config directory

* chore: update `https-proxy-agent`

* fix: await until dependencies are installed then run detypes process

* feat: add tailwind prefix

* test: tw-prefix snapshot

* chore: add prefix option to init

* test: apply prefix

* fix: tw-prefix parse wrongly

* chore: hide prefix temporarily

---------

Co-authored-by: zernonia <zernonia@gmail.com>
2024-03-06 05:38:19 +03:30
Saeid Zareie
c487137ac5
docs: adding more examples for DropdownMenu and Select components 2024-03-05 16:52:07 +03:30
Saeid Zareie
75cf9c40c4
docs: adding remaining usages of drawer (#380) 2024-03-05 14:45:30 +03:30
Saeid Zareie
3ec55ada1b
docs: adding skeleton card example (#382) 2024-03-05 14:37:35 +03:30
ɹǝʞɹɐԀ uǝʌS
edd713fd08
docs: add new example for slider in form (#377) 2024-03-04 15:28:02 +03:30
Saeid Zareie
64e2f9c199
feat: vaul-vue integration (#374) 2024-03-04 10:04:28 +03:30
Sadegh Barati
0265b48b35 chore: update deps
root
apps/www
packages/module
2024-03-04 06:56:57 +03:30
Sadegh Barati
5d9176725b fix: vue-sonner wrong props
update deps
run registry
2024-03-01 21:18:14 +03:30
Braden Wong
3d3c5ab3e6
docs: fix form examples tag capitalization (#367) 2024-03-01 13:13:16 +03:30
Braden Wong
6aa1ce4f1b
chore: remove unused imports in utils (#364) 2024-02-29 21:05:18 +03:30
Hannes Küttner
c9012b3157
docs: fix DataTableColumnHeader usage (#359) 2024-02-25 00:41:43 +03:30
zernonia
27fae5c24c chore: bump radix 2024-02-21 14:25:12 +08:00
zernonia
83c3c207cd chore: build registry 2024-02-21 14:24:22 +08:00
zernonia
0aaad536fb chore: update DialogScrollContent component 2024-02-21 14:23:59 +08:00
Dunqing
24576e3ef6
chore(cli): update detypes version to fix the problem that calendar cannot be installed (#348) 2024-02-20 19:11:49 +03:30
Michael Krebs
2d22ef1878
docs: add ScrollArea to TableOfContent.vue (#351) 2024-02-20 09:39:22 +03:30
Sadegh Barati
48f3ee050f chore: build registry 2024-02-15 23:06:24 +03:30
Roman Hrynevych
915df39075
feat: add ScrollBody and ScrollOverlay demos for Dialog (#287)
* feat(dialog-with-scroll-body): add demos with body scroll for Dialog component


---------

Co-authored-by: Sadegh Barati <sadeghbaratiwork@gmail.com>
2024-02-15 22:47:09 +03:30
Sadegh Barati
28efdc07c4
chore: update antfu config, update root deps (#347) 2024-02-15 22:20:52 +03:30
Sadegh Barati
43f9f56077
feat: tags input (#328)
* feat: tags input

* chore: add `tags-input` to sidebar links

* chore: update

* chore: add combobox demo

* chore: improve tag highlight

* chore: update

* chore: rename title

* chore: add static width to `TagsInputCombo` example

---------

Co-authored-by: zernonia <zernonia@gmail.com>
2024-02-15 22:06:15 +03:30
zernonia
60fbe49004 chore: bump radix 2024-02-15 14:58:12 +01:00
Roman Hrynevych
9c015067e3
docs: add Input with icon example (#346) 2024-02-15 16:38:29 +03:30
Greg Sanderson
406e4ff8a8
fix: calendar weeks alignment (#344)
The component's CSS adds additional padding which results in Saturday and Sunday not being aligned with the rest of the days.


---------

Co-authored-by: Sadegh Barati <sadeghbaratiwork@gmail.com>
2024-02-15 16:20:23 +03:30
zernonia
999676aef7 chore: build registry 2024-02-15 12:49:53 +01:00
zernonia
a01c83c718 chore: bump radix-vue 2024-02-15 12:48:35 +01:00
Roman Hrynevych
5cc6ef1e9d
docs: add Geist default font (#339) 2024-02-14 18:13:25 +03:30
Greg Sanderson
3d99ca42b7
refactor: update SelectItem.vue to put the check icon on the left (#340)
The original shadcn library put the check on the left (for the default style) of the selected option
2024-02-14 17:49:59 +03:30
Jared Jolton
41660d36cd
docs: add line highlighting for resolve paths instructions (#331) 2024-02-11 23:04:37 +03:30
Victor Garcia
08d6ea6a53
docs: calendar typo (#330) 2024-02-09 15:57:05 +03:30
zernonia
aef277bbfe docs: update landing page 2024-02-07 22:21:48 +08:00
Lucas Nunes
25f4a7292a
docs: minor fix to display the correct icon on dark mode (#326)
fixing the glitch when reloading the page on dark mode and the icon changing back to light mode.
2024-02-06 20:31:39 +03:30
Sadegh Barati
6ab704a6fb
feat: pin input (#325)
* feat: pin input

* chore: build registry

* chore: build registry, add form example

* chore: update demo abit

---------

Co-authored-by: zernonia <zernonia@gmail.com>
2024-02-06 10:06:59 +08:00
Sadegh Barati
b0e1b55537
docs: add simple column pinning example (#320) 2024-02-05 21:00:31 +03:30
zernonia
67d10e1191 chore: revert dismissable 2024-02-05 16:58:07 +08:00
Sadegh Barati
b486a10129
fix: CommandItem hightlighted bg (#322)
* fix: `CommandItem` hightlighted bg

* fix: escape key not closing combobox

---------

Co-authored-by: zernonia <zernonia@gmail.com>
2024-02-05 16:30:32 +08:00
zernonia
32552dc83a chore: bump radix-vue 2024-02-05 11:06:21 +08:00
zernonia
5dc01fa62a chore: release v0.9.0 2024-02-04 21:55:01 +08:00
zernonia
69576afcff chore: cleanup 2024-02-04 21:53:32 +08:00
zernonia
6c99fd6dd1 chore: fix testing pipeline 2024-02-04 21:53:00 +08:00
itsTPM
83d0a06e6f
fix(cli): add xl item to borderRadius in tailwind.config template (#319) 2024-02-03 21:12:49 +03:30
Dunqing
5c69b2160c
fix(cli): cannot add Carousel component without enabled typescript (#318) 2024-02-03 18:44:10 +03:30
Sadegh Barati
ed70e4e4b8 chore: rebuild registry, fresh lockfile 2024-02-02 22:54:21 +03:30
Roman Hrynevych
fc718145ae
refactor: tailwindcss, remove all unnecessary arbitrary values, update TailwindCSS to v3.4 (#269) 2024-02-02 22:48:58 +03:30
Wasim Thoufiq
6d24fb801b
fix: month and year popover (#317) 2024-02-02 22:01:07 +03:30
Sadegh Barati
a829212d42
refactor: use class as prop to prevent class duplication with cn function (#241) 2024-02-02 19:08:06 +03:30
Jackson Bowe
8875261576
fix: weekdays spacing (#300) 2024-01-31 20:46:01 +03:30
Sadegh Barati
1758a91d72
chore: simplify GitHub issues report (#312)
* Update bug-report.yml

* Update bug-report.yml

* Update bug-report.yml
2024-02-01 01:08:32 +08:00
Pier107
8bd5525368
fix: ScrollAreaThumb width in horizontal orientation (#309) 2024-01-28 13:11:54 +03:30
Sadegh Barati
42f0086586 chore: add vue-sonner to dependencies in registry files 2024-01-22 20:30:08 +03:30
Sadegh Barati
c94443eabe
chore: fix vue-sonner Unknown file extension ".css" (#302) 2024-01-22 20:06:03 +03:30
Robert Shaw
eba85c6b5c
feat(sonner): add sonner component (#301) 2024-01-22 19:15:30 +03:30
Sadegh Barati
f9ab01e11b
docs: fix new line-number, fix highlight color on txt md block in shikiji (#299)
* docs: tweaks container size and shiki styles
2024-01-22 01:16:48 +03:30
trolladam
f41642f5d8
fix: separator background color (#295) 2024-01-20 15:12:36 +03:30
Sadegh Barati
50586487d5
docs: update vite installation (#250) 2024-01-18 16:45:38 +03:30
Rukshan Ranatunge
5336fce443
docs: add @types/node section in Vite installation(#289)
Add install @types/node to follow the documentation in shadcn-ui. And import path without an error
2024-01-18 16:08:39 +03:30
Valentin Hutter
cb974f95e0
feat: Calendar component inherits the VCalendar slots (#285) 2024-01-18 16:06:56 +03:30
Valentin Hutter
8e7bbe3a8d
fix: hide navigation in "time" mode (#283)
Co-authored-by: Valentin Hutter <valentin@macbook-pro-de-valentin.home>
2024-01-17 22:24:28 +03:30
Sadegh Barati
948a9c9e7c
chore: update deps and vitepress (#278) 2024-01-17 14:46:08 +08:00
zernonia
e65fe20e6d chore: run build registry 2024-01-17 14:30:26 +08:00
Anton Reshetov
7727c7282c
fix: radio group indicator fill (#280)
* fix: radio group indicator fill

---------

Co-authored-by: zernonia <zernonia@gmail.com>
2024-01-17 14:28:32 +08:00
zernonia
53c539ddae chore: bump action version 2024-01-17 14:10:22 +08:00
zernonia
f1c62676c5 fix: github action error 2024-01-17 13:59:47 +08:00
zernonia
18fafd7c95 fix: github action error 2024-01-17 13:45:37 +08:00
zernonia
8860b44715 chore: add conditional to the branch naming 2024-01-17 13:39:33 +08:00
cvsouth
2a590360b6
docs: fix a typo in form (#281)
* docs: fix a typo
2024-01-17 13:34:05 +08:00
zernonia
6ff7b6f3c4 chore: update the pipeline to allow deploy for forked pr 2024-01-17 13:25:26 +08:00
broki
5409b33992 docs: corrected a url typo (#279) 2024-01-16 15:37:33 +03:30
zernonia
72ed16a343 chore: test publish pipeline 2024-01-16 18:53:37 +08:00
Sadegh Barati
0c7da48f8f
feat: toggle group (#275)
* feat: toggle group

* chore: remove dummy examples

* chore: update pathe and typescript, include scripts in tsconfig include

* refactor: move import type from normal script to script setup

don't know how detypes would react with that normal script
2024-01-16 10:56:54 +03:30
Sadegh Barati
c33acba4ff
fix: prevent page zoom while tapping carousel buttons (#274)
* fix: prevent page zoom while tapping carousel buttons

choose shadcn-ui icons and sizes for buttons

* docs: fix carousel page responsive issues and ordering docs as shadcn-ui
2024-01-15 18:55:22 +03:30
Anton Reshetov
72857b6a56
fix: select popper position (#272) 2024-01-14 18:03:12 +03:30
Roman Hrynevych
825f14e8b5
fix: add whitespace-nowrap for Button, Select, Tab (#266)
* fix(Button): add 'whitespace-nowrap' to base styles

* refactor(Button): use VariantProps for Button Props instead of NonNullable

* fix(Select): add whitespace-nowrap and truncate to SelectTrigger
2024-01-13 00:04:05 +03:30
zernonia
d3806fc056 chore: release v0.8.7 2024-01-12 10:20:28 +08:00
zernonia
ab835ff46e chore: run registry 2024-01-12 10:20:15 +08:00
zernonia
7a7cf9d05d
fix: Nuxt module throwing error due to parsing error (#267)
* chore: add carousel

* chore: add oxc-parser

* feat: use oxc-parser to get ExportNamedDeclaration node

* chore: add todo
2024-01-12 10:18:30 +08:00
Valentin Hutter
dfbb738aee
feat: add style to work with v-calendar time picker (#243) (#265)
* feat: add style to work with v-calendar time picker (#243)

* docs: add datetime picker to doc + build registery

* build: build registery in apps/www

* chore: tweaks and fix darkmode selectbox bg

---------

Co-authored-by: Valentin Hutter <valentin@macbook-pro-de-valentin.home>
Co-authored-by: Sadegh Barati <sadeghbaratiwork@gmail.com>
2024-01-12 01:55:22 +03:30
Roman Hrynevych
b941b92dee
fix(popover): add max-height to SelectContent (#263) (#264)
* fix(popover): add max-height to SelectContent (#263)

Closes: #263

* fix(popover): add max-height to SelectContent [default style] (#263)

Closes: #263
2024-01-11 23:45:52 +03:30
zernonia
f46e2b98cd test: fix test missing safelist 2024-01-11 14:43:50 +08:00
zernonia
1d563bbcdc chore: upgrade deps 2024-01-11 14:36:47 +08:00
Antoine Lethimonnier
f05922dfd0
feat(cli): Add JSON schema (#256)
* Add schema

* Update schema.json
2024-01-10 10:15:02 +03:30
Hai Yang Tang
1ff244c475
fix(cli): safelist dark when CSS variable colors are used. (#255) 2024-01-10 09:15:21 +03:30
zernonia
856ab15179 chore: bump version 2024-01-09 16:20:38 +08:00
Wasim Thoufiq
97c7417352
feat: add carousel component (#227)
* feat: create new carousel component with embala-carousel

* feat: create demos for the carousel component

* feat: add the default carousel component to the docs

* feat: add new-york styling for carousels

* feat: add more examples for spacing, size and options

* refactor: change ways to better pass the data to parent

* feat: add examples for carousel api handling

* feat: add example for using embla plugin

* chore: add carousel component doc to the table of contents

* feat: add focusability on carousel element

* fix: update docs

* chore: add docs for slot props

* feat: expose api for the parent component

* chore: include missing filenames

* chore: update embla carousel dependency versions

* chore: fix typescript error by getting the types from core package

* chore: prevent duplicate classes by using class as prop

* feat: use slot fallback content

so user could change navigation button icons

* fix: change attribute inheritance element

* chore: update www package.json `scripts`

update tsconfig exclude for the strict registry build

* refactor: fix embla-carousel types after v8.0.0-rc18

update embla deps

* chore: update @vue/tsconfig

* chore: run registry

* refactor: remove uneended ref

* fix: dependencies for embla missing

* docs: update carousel for optional plugin installation

---------

Co-authored-by: sadeghbarati <sadeghbaratiwork@gmail.com>
Co-authored-by: zernonia <zernonia@gmail.com>
2024-01-09 00:51:55 +08:00
Valentin Hutter
4214134e18
fix remove today design when in the current range in new york style following #252 (#253) 2024-01-08 13:08:31 +03:30
Valentin Hutter
54a846f93a
fix remove today design when in the current range (#252) 2024-01-08 12:55:43 +03:30
Valentin Hutter
1702ca75ad
#244 - fix day borders (#251)
* fix: #244 - day borders

* fix: vc-day is-today style, unhover bg color and radius glitch

update new-york `Calendar.vue`

---------

Co-authored-by: sadeghbarati <sadeghbaratiwork@gmail.com>
2024-01-08 09:43:23 +03:30
Hubert Kowalski
dbdf91294e
fix(module): fixed the typo (#249) 2024-01-07 20:33:35 +03:30
Sadegh Barati
989c0e2024
chore(cli): match cli prompt question (global CSS) with the documentation 2024-01-07 09:35:54 +03:30
Sadegh Barati
5332610012
docs: prevent default browser behaviour with ctrl-k/j in useMagicKeys (#246)
build registry for stackblitz and codesandbox demos
2024-01-06 11:10:26 +03:30
Starker-xp
9d9a6f929c
fix: dialog props missing (#242) 2024-01-04 16:37:25 +03:30
Paras Solanki
0f0f1ef3ca
fix: Input attrs not reactive (#228)
* fix: default input props not reactive

* fix: new-york input props are not reactive

* fix: default input not reactive added class prop

* fix: new-york input not reactive added class prop

* fix: update

---------

Co-authored-by: Sadegh Barati <sadeghbaratiwork@gmail.com>
2024-01-03 00:07:30 +03:30
zernonia
1d2cce5a40 docs: update nuxt to allow manual module 2024-01-02 18:12:22 +08:00
zernonia
9adf96dd2b fix: typo for @nuxt/kit import 2024-01-02 16:19:22 +08:00
zernonia
755d864f14 chore: release v0.8.6 2024-01-02 16:13:30 +08:00
zernonia
af57b63458 chore: run build registry 2024-01-02 16:13:03 +08:00
zernonia
2f4f5a8291
fix(Module): HMR not working when edit component load too slow (#235)
* feat: add recast as deps

* refactor: use recast instead of ts-morph due to performance issue
2024-01-02 16:08:38 +08:00
Sadegh Barati
f6f87d3cd6
chore: fix ComboboxDemo.vue types (#233)
* chore: fix `ComboboxDemo.vue` example types

* chore: update `radix-vue`
2023-12-31 22:37:23 +03:30
Ole Marius Løset
ef3ec54a4e
Output consistency across platforms (#181)
* fix: normalize newline

* fix: sort readdir

* fix: correct sorting

* fix: missing npm run in build:registry npm script

* Update apps/www/package.json

Co-authored-by: Sadegh Barati <sadeghbaratiwork@gmail.com>

---------

Co-authored-by: Sadegh Barati <sadeghbaratiwork@gmail.com>
2023-12-31 21:10:47 +03:30
itsTPM
c2e05239f6
docs: change cards radius to rounded-lg (#231) 2023-12-30 20:40:31 +03:30
itsTPM
583bf60ce8
docs: replace nbsp with a space when copying (#230) 2023-12-30 14:14:32 +03:30
Jan Kremlacek
15c42f9ee0
docs: make <DataTable /> reactive (#229) 2023-12-30 05:50:10 +03:30
Sadegh Barati
5e22ffc037
fix: toggle not setting pressed property on init (#223)
* fix: toggle not setting pressed property on init

* refactor: move class outside of toggleVariants
2023-12-30 00:24:20 +03:30
Gary Thomas
9d66d15801
fix: allow .ts or .js extension in module package (#225)
* fix: allow .ts or .js extension in module package

* Update module.ts to replace node:path with pathe package
2023-12-30 00:22:32 +03:30
zernonia
c75b40245f docs: add missing API references 2023-12-23 11:47:15 +08:00
zernonia
eccfc9940d chore: update test snapshot 2023-12-21 09:11:07 +08:00
zernonia
3a049d53ad test: include radix-vue in deps 2023-12-21 09:10:15 +08:00
zernonia
2513dc5429 chore: release v0.8.5 2023-12-21 09:07:47 +08:00
Sadegh Barati
010e377669
fix: avoid failing resolve types by adding radix-vue to project deps in shadcn-vue init before the add command (#216)
* fix: avoid failing resolve types by adding `radix-vue` to project deps in `init` command

* chore: add `shell-emulator` and remove `cross-env`

fix some registry example import path

* chore: remove additional `radix-vue`  dependency from components registry

* chore: update `tsx` and `vue-tsc`

* fix: normalize `components:example` path to process `crawlExample` function correctly

* chore: build registry

---------

Co-authored-by: zernonia <zernonia@gmail.com>
2023-12-21 09:06:14 +08:00
Sadegh Barati
b40321e3d9
docs: fix vee-validate checkbox and radio-group examples (#213)
* docs: fix checkbox and radio form examples

add type="checkbox|radio" and name in FormField

* docs: add `vee-validate` documentation link for checkbox and radio inputs

* chore: update `vee-validate` deps
2023-12-19 18:23:05 +03:30
Björn
96da2d8ffe
docs: change docs link in sheet component (#211) 2023-12-17 11:27:45 +03:30
sadeghbarati
f82e4011e6 chore: fix label for attribute in registry examples
`html-for` -> `for`
2023-12-12 10:02:55 +03:30
Sasa Markovic
24027aa1a1 docs: fix create project command typo (#208)
`create-next-app` -> `create-nuxt-app`
2023-12-12 09:49:39 +03:30
zernonia
269caf00b0
fix(Button): missing asChild (#203)
* fix: button should use primitive

* docs: fix missing NuxtLink import

* chore: build registry
2023-12-06 09:59:36 +08:00
sadeghbarati
58bad186dc chore: fix ThemingLayout.vue TooltipContent color in dark mode 2023-12-02 09:23:19 +03:30
Josh Larsen
c38624cb50
fix: theme switcher current colors (#198)
* fix border color

* match bg color closer to border

* fix tooltip bg color
2023-12-02 06:15:11 +03:30
1488 changed files with 64042 additions and 18893 deletions

12
.editorconfig Normal file
View File

@ -0,0 +1,12 @@
# EditorConfig is awesome: https://EditorConfig.org
# top-most EditorConfig file
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

View File

@ -1,19 +0,0 @@
// const process = require('node:process')
// process.env.ESLINT_TSCONFIG = 'tsconfig.json'
module.exports = {
extends: '@antfu',
rules: {
'vue/one-component-per-file': 'off',
'vue/no-reserved-component-names': 'off',
'vue/no-useless-v-bind': 'off',
'symbol-description': 'off',
'no-console': 'warn',
'no-tabs': 'off',
'no-invalid-character': 'off',
'import/first': 'off',
'@stylistic/js/no-tabs': 'off',
'n/prefer-global/process': 'off',
},
}

View File

@ -0,0 +1,55 @@
name: 🐞 Bug report
description: Create a report to help us improve shadcn-vue.
title: '[Bug]: '
labels: [bug]
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this bug report!
This form is only for submitting bug reports. If you have a usage question
or are unsure if this is really a bug, make sure to:
- Read the [docs](https://radix-vue.com/)
- Ask on [Discord Chat](https://chat.radix-vue.com/)
- Ask on [GitHub Discussions](https://github.com/shadcn-vue/shadcn-vue/discussions)
- type: input
id: reproduction
attributes:
label: Reproduction
description: |
A [minimal reproduction](https://stackoverflow.com/help/minimal-reproducible-example) is **required**, otherwise the issue might be closed without further notice. [**Why?**](https://antfu.me/posts/why-reproductions-are-required)
To get started, you can use the StackBlitz and CodeSandbox playgrounds in shadcn-vue demos:
https://www.shadcn-vue.com/docs/components/accordion.html
or use template presets
https://vite.new
https://nuxt.new
placeholder: Reproduction
validations:
required: true
- type: textarea
id: bug-description
attributes:
label: Describe the bug
description: A clear and concise description of what the bug is. If you intend to submit a PR for this issue, tell us in the description. Thanks!
placeholder: Bug description
validations:
required: true
- type: textarea
id: system-info
attributes:
label: System Info
description: Output of `npx envinfo --system --npmPackages vue,@vueuse/core,radix-vue,nuxt,shadcn-vue,shadcn-nuxt,unplugin-auto-import --binaries --browsers`
render: bash
placeholder: System, Binaries, Browsers
validations:
required: true
- type: checkboxes
id: contributes
attributes:
label: Contributes
options:
- label: I am willing to submit a PR to fix this issue
- label: I am willing to submit a PR with failing tests

View File

@ -0,0 +1,17 @@
name: Setup
description: Installs Node, Enables Corepack and caches pnpm.
runs:
using: composite
steps:
- name: Enable corepack
run: corepack enable
shell: bash
- name: Setup node & pnpm
uses: actions/setup-node@v4
with:
node-version: lts/*
cache: pnpm
registry-url: 'https://registry.npmjs.org'

View File

@ -0,0 +1,90 @@
name: Publish www
on:
push:
branches:
- dev
paths:
- 'apps/www/**'
pull_request:
branches:
- dev
paths:
- 'apps/www/**'
pull_request_target:
types:
# When a created pull request from forked repo, it will be comment 'Should deploy to add label'
- opened
# When a labeled '🚀request-deploy' pull request from forked repo, it will be deploy to Cloudflare Pages
- labeled
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
permissions:
# default contents: read & write (in forked repos, only read)
contents: write
# default deployments: read & write (in forked repos, only read)
deployments: write
# default pull-requests: read & write (in forked repos, only read)
pull-requests: write
jobs:
publish:
runs-on: ubuntu-latest
name: Publish to Cloudflare Pages
# push event in main branch
# workflow_dispatch event
# pull_request event from not forked repo
# pull_request_target event with label "🚀request-deploy" from forked repo
if: ${{
github.event_name == 'push' ||
github.event_name == 'workflow_dispatch' ||
(github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false) ||
(github.event_name == 'pull_request_target' &&
github.event.action == 'labeled' &&
github.event.pull_request.head.repo.fork == true &&
contains(github.event.label.name, '🚀request-deploy'))
}}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup (Install Node & pnpm)
uses: ./.github/actions/setup
- name: Install dependencies
run: pnpm i --frozen-lockfile
- name: Build www
run: pnpm build
# Run a action to publish docs
- name: Publish to Cloudflare Pages
uses: zernonia/cloudflare-pages-action@v0.0.7
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
projectName: shadcn-vue
directory: .vitepress/dist
# Optional: Enable this if you want to have GitHub Deployments triggered
gitHubToken: ${{ secrets.GITHUB_TOKEN }}
# Optional: Switch what branch you are publishing to.
# By default this will be the branch which triggered this workflow
branch: ${{ github.ref == 'refs/heads/dev' && 'dev' || format('refs/pull/{0}/merge', github.event.number) }}
# Optional: Change the working directory
workingDirectory: apps/www
wranglerVersion: '3'
- name: Remove label
if: ${{ github.event_name == 'pull_request_target' && contains(github.event.label.name, '🚀request-deploy') }}
uses: actions/github-script@v6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
github.rest.issues.removeLabel({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
name: ['🚀request-deploy']
})

View File

@ -0,0 +1,39 @@
# .github/workflows/release.yml
name: Release
permissions:
contents: write
on:
push:
tags:
- 'v*'
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup (Install Node & pnpm)
uses: ./.github/actions/setup
- name: Install dependencies
run: pnpm i --frozen-lockfile
- run: pnpm dlx changelogithub
env:
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
- name: Build CLI & Publish to npm
run: pnpm --filter shadcn-vue pub:release
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Build Module & Publish to npm
run: pnpm --filter shadcn-nuxt pub:release
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

View File

@ -0,0 +1,31 @@
name: Test
on:
push:
branches:
- dev
paths:
- 'packages/**'
pull_request:
branches:
- dev
paths:
- 'packages/**'
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup (Install Node & pnpm)
uses: ./.github/actions/setup
- name: Install dependencies
run: pnpm i --frozen-lockfile
- name: Test
run: pnpm test

View File

@ -1,86 +0,0 @@
name: 🐞 Bug report
description: Create a report to help us improve shadcn-vue.
title: '[Bug]: '
labels: [bug]
body:
- type: markdown
attributes:
value: |
**Before You Start...**
This form is only for submitting bug reports. If you have a usage question
or are unsure if this is really a bug, make sure to:
- Read the [docs](https://radix-vue.com/)
- Ask on [Discord Chat](https://chat.radix-vue.com/)
- Ask on [GitHub Discussions](https://github.com/shadcn-vue/shadcn-vue/discussions)
Also try to search for your issue - it may have already been answered or even fixed.
However, if you find that an old, closed issue still persists in the latest version,
you should open a new issue using the form below instead of commenting on the old issue.
- type: textarea
id: bug-env
attributes:
label: Environment
description: Please provide the following information about your environment.
value: |
Developement/Production OS: Windows 10 19043.1110
Node version: 16.0.0
Package manager: pnpm@8.6.0
Radix Vue version: 1.0.0
Shadcn Vue version: 1.0.0
Vue version: 3.0.0
Nuxt version: 3.0.0
Nuxt mode: universal
Nuxt target: server
CSS framework: tailwindcss@3.3.3
Client OS: Windows 10 19043.1110
Browser: Chrome 90.0.4430.212
render: bash
validations:
required: true
- type: input
id: reproduction-link
attributes:
label: Link to minimal reproduction
description: |
Please provide a link to a minimal reproduction of the bug.
A minimal reproduction is a CodeSandbox, CodePen, or a StackBlitz that contains the bare minimum amount of code needed to show the bug.
A minimal reproduction is required unless you are absolutely sure that the issue is obvious and the provided information is enough to understand the problem
This is **required** for us to be able to triage your issue in a timely manner.
Please do not just fill in a random link. The issue will be closed if no valid reproduction is provided.
placeholder: Reproduction Link
validations:
required: true
- type: textarea
id: steps-to-reproduce
attributes:
label: Steps to reproduce
description: |
How do you trigger this bug? Please walk us through it step by step.
Note that you can use [Markdown](https://guides.github.com/features/mastering-markdown/) to format lists and code.
placeholder: Steps to reproduce
validations:
required: true
- type: textarea
id: bug-description
attributes:
label: Describe the bug
description: A clear and concise description of what the bug is. If you intend to submit a PR for this issue, tell us in the description. Thanks!
placeholder: Bug description
validations:
required: true
- type: textarea
id: expected-behavior
attributes:
label: Expected behavior
description: A clear and concise description of what you expected to happen.
- type: textarea
id: screenshots
attributes:
label: Conext & Screenshots (if applicable)
description: |
If applicable, provide any additional context or screenshots of the bug.
You can drag and drop images here to add them to the issue.

View File

@ -1,72 +0,0 @@
name: Publish www
on:
push:
branches:
- dev
paths:
- 'apps/www/**'
pull_request:
branches:
- dev
paths:
- 'apps/www/**'
jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: read
deployments: write
name: Publish to Cloudflare Pages
steps:
- name: Checkout
uses: actions/checkout@v3
# Run a build step here
- name: Setup Node.js environment
uses: actions/setup-node@v2
with:
node-version: 18
- uses: pnpm/action-setup@v2
name: Install pnpm
with:
version: 8
run_install: false
- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm i --frozen-lockfile
- name: Build www
run: pnpm build
# Run a action to publish docs
- name: Publish to Cloudflare Pages
uses: cloudflare/pages-action@v1.5.0
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
projectName: shadcn-vue
directory: .vitepress/dist
# Optional: Enable this if you want to have GitHub Deployments triggered
gitHubToken: ${{ secrets.GITHUB_TOKEN }}
# Optional: Switch what branch you are publishing to.
# By default this will be the branch which triggered this workflow
# branch: main
# Optional: Change the working directory
workingDirectory: apps/www
wranglerVersion: '3'

View File

@ -1,27 +0,0 @@
# .github/workflows/release.yml
name: Release
permissions:
contents: write
on:
push:
tags:
- 'v*'
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: actions/setup-node@v3
with:
node-version: 18.x
- run: npx changelogithub
env:
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}

View File

@ -1,52 +0,0 @@
name: Test
on:
push:
branches:
- dev
paths:
- 'packages/**'
pull_request:
branches:
- dev
paths:
- 'packages/**'
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Setup Node.js environment
uses: actions/setup-node@v2
with:
node-version: 16
- uses: pnpm/action-setup@v2
name: Install pnpm
with:
version: 8
run_install: false
- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm i --frozen-lockfile
- name: Test
run: pnpm test

1
.npmrc Normal file
View File

@ -0,0 +1 @@
shell-emulator=true

View File

@ -1,7 +1,7 @@
{
"recommendations": [
"Vue.volar",
"Vue.vscode-typescript-vue-plugin",
"dbaeumer.vscode-eslint"
"dbaeumer.vscode-eslint",
"bradlc.vscode-tailwindcss"
]
}

27
.vscode/settings.json vendored
View File

@ -1,11 +1,28 @@
{
"vue.server.hybridMode": true,
"vue.server.includeLanguages": [
"vue",
"markdown"
],
"prettier.enable": false,
"editor.formatOnSave": false,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true,
"source.organizeImports": false
"source.fixAll.eslint": "explicit",
"source.organizeImports": "never"
},
"eslint.useFlatConfig": true,
"eslint.rules.customizations": [
{ "rule": "style/*", "severity": "off" },
{ "rule": "format/*", "severity": "off" },
{ "rule": "*-indent", "severity": "off" },
{ "rule": "*-spacing", "severity": "off" },
{ "rule": "*-spaces", "severity": "off" },
{ "rule": "*-order", "severity": "off" },
{ "rule": "*-dangle", "severity": "off" },
{ "rule": "*-newline", "severity": "off" },
{ "rule": "*quotes", "severity": "off" },
{ "rule": "*semi", "severity": "off" }
],
"eslint.validate": [
"javascript",
"javascriptreact",
@ -17,5 +34,9 @@
"json",
"jsonc",
"yaml"
],
"tailwindCSS.experimental.classRegex": [
["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"],
["cn\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"]
]
}

View File

@ -19,7 +19,7 @@ This repository is structured as follows:
```
apps
└── www
├── src
├── src
│ └── content
└── registry
├── default
@ -32,12 +32,12 @@ packages
└── cli
```
| Path | Description |
| --------------------- | ---------------------------------------- |
| `apps/www/app` | The Next.js application for the website. |
| `apps/www/content` | The content for the website. |
| `apps/www/registry` | The registry for the components. |
| `packages/cli` | The `shadcn-vue` package. |
| Path | Description |
| ----------------------------| -------------------------------------------|
| `apps/www/.vitepress` | The Vitepress application for the website. |
| `apps/www/src/content` | The content for the website. |
| `apps/www/src/lib/registry` | The registry for the components. |
| `packages/cli` | The `shadcn-vue` package. |
## Development
@ -79,22 +79,24 @@ The documentation for this project is located in the `www` workspace. You can ru
pnpm dev
```
Documentation is written using [md](https://vitepress.dev/guide/markdown). You can find the documentation files in the `apps/www/content/docs` directory.
Documentation is written using [md](https://vitepress.dev/guide/markdown). You can find the documentation files in the `apps/www/src/content` directory.
## Components
We use a registry system for developing components. You can find the source code for the components under `apps/www/registry`. The components are organized by styles.
We use a registry system for developing components. You can find the source code for the components under `apps/www/src/lib/registry`. The components are organized by styles.
```bash
apps
└── www
└── registry
├── default
│ ├── example
│ └── ui
└── new-york
├── example
└── ui
└── src
└── lib
└── registry
├── default
│ ├── example
│ └── ui
└── new-york
├── example
└── ui
```
When adding or modifying components, please ensure that:
@ -130,13 +132,10 @@ the following categories:
e.g. `feat(components): add new prop to the avatar component`
If you are interested in the detailed specification you can visit
https://www.conventionalcommits.org/ or check out the
[Angular Commit Message Guidelines](https://github.com/angular/angular/blob/22b96b9/CONTRIBUTING.md#-commit-message-guidelines).
## Requests for new components
If you have a request for a new component, please open a discussion on GitHub. We'll be happy to help you out.
@ -155,4 +154,4 @@ Tests are written using [Vitest](https://vitest.dev). You can run all the tests
pnpm test
```
Please ensure that the tests are passing when submitting a pull request. If you're adding new features, please include tests.
Please ensure that the tests are passing when submitting a pull request. If you're adding new features, please include tests.

View File

@ -1,7 +1,7 @@
<p align="center">
<img align="center" src="https://raw.githubusercontent.com/radix-vue/shadcn-vue/dev/apps/www/src/public/android-chrome-192x192.png" height="96" />
<h1 align="center">
shadcn-vue
shadcn-vue by Niklas Hermanns
</h1>
</p>
@ -31,3 +31,7 @@ All credits go to these open-source works and resources
## License
Licensed under the [MIT license](https://github.com/shadcn/ui/blob/main/LICENSE.md).
## Actions
- Test

View File

@ -1,9 +1,12 @@
import path from 'node:path'
import { defineConfig } from 'vitepress'
import Icons from 'unplugin-icons/vite'
import tailwind from 'tailwindcss'
import { transformerMetaWordHighlight } from '@shikijs/transformers'
import autoprefixer from 'autoprefixer'
import tailwind from 'tailwindcss'
import Icons from 'unplugin-icons/vite'
import { defineConfig } from 'vitepress'
import { siteConfig } from './theme/config/site'
import CodeWrapperPlugin from './theme/plugins/codewrapper'
import ComponentPreviewPlugin from './theme/plugins/previewer'
// https://vitepress.dev/reference/site-config
@ -27,7 +30,6 @@ export default defineConfig({
['meta', { name: 'og:site_name', content: siteConfig.name }],
['meta', { name: 'og:image', content: siteConfig.ogImage }],
['meta', { name: 'twitter:image', content: siteConfig.ogImage }],
],
sitemap: {
@ -46,13 +48,20 @@ export default defineConfig({
pattern: 'https://github.com/radix-vue/shadcn-vue/tree/dev/apps/www/src/:path',
text: 'Edit this page on GitHub',
},
carbonAds: {
code: 'CW7DK27U',
placement: 'wwwshadcn-vuecom',
},
},
srcDir: path.resolve(__dirname, '../src'),
markdown: {
theme: 'css-variables',
codeTransformers: [
transformerMetaWordHighlight(),
],
config(md) {
md.use(ComponentPreviewPlugin)
md.use(CodeWrapperPlugin)
},
},
rewrites: {
@ -62,13 +71,13 @@ export default defineConfig({
css: {
postcss: {
plugins: [
tailwind(),
tailwind() as any,
autoprefixer(),
],
},
},
plugins: [
Icons({ compiler: 'vue3', autoInstall: true }),
Icons({ compiler: 'vue3', autoInstall: true }) as any,
],
resolve: {
alias: {

View File

@ -0,0 +1,26 @@
<script setup lang="ts">
import { capitalize } from 'vue'
defineProps<{
type: 'prop' | 'emit' | 'slot' | 'method'
data: { name: string, description: string, type: string, required: boolean }[]
}>()
</script>
<template>
<div>
<h3>{{ capitalize(type) }}</h3>
<div v-for="(item, index) in data" :key="index" class="py-4 border-b text-sm">
<div class="flex items-center gap-2 flex-wrap">
<h5 class="text-sm">
<code>{{ item.name }}</code>
</h5>
<code>{{ item.type }}</code>
<span v-if="item.required" class="font-normal text-red-500 text-xs">Required*</span>
</div>
<div class="[&_p]:!my-2 ml-1" v-html="item.description" />
</div>
</div>
</template>

View File

@ -0,0 +1,19 @@
<script setup lang="ts">
import { Separator } from '@/lib/registry/default/ui/separator'
import ArrowRightIcon from '~icons/radix-icons/arrow-right'
import { announcementConfig } from '../config/site'
</script>
<template>
<a
:href="announcementConfig.link"
class="inline-flex items-center rounded-lg bg-muted px-3 py-1 text-sm font-medium"
>
{{ announcementConfig.icon }} <Separator class="mx-2 h-4" orientation="vertical" />
<span class="sm:hidden">{{ announcementConfig.title }}</span>
<span class="hidden sm:inline">
{{ announcementConfig.title }}
</span>
<ArrowRightIcon class="ml-1 h-4 w-4" />
</a>
</template>

View File

@ -0,0 +1,233 @@
<script setup lang="ts">
import { useConfigStore } from '@/stores/config'
import { CircleHelp, Info, Monitor, Smartphone, Tablet } from 'lucide-vue-next'
import MagicString from 'magic-string'
import { reactive, ref, watch } from 'vue'
import { compileScript, parse, walk } from 'vue/compiler-sfc'
import { highlight } from '../config/shiki'
import BlockCopyButton from './BlockCopyButton.vue'
import StyleSwitcher from './StyleSwitcher.vue'
// import { V0Button } from '@/components/v0-button'
import { Badge } from '@/lib/registry/new-york/ui/badge'
import { Popover, PopoverContent, PopoverTrigger } from '@/lib/registry/new-york/ui/popover'
import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from '@/lib/registry/new-york/ui/resizable'
import { Separator } from '@/lib/registry/new-york/ui/separator'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/lib/registry/new-york/ui/tabs'
import { ToggleGroup, ToggleGroupItem } from '@/lib/registry/new-york/ui/toggle-group'
import BlockPreview from './BlockPreview.vue'
const props = defineProps<{
name: string
}>()
const { style, codeConfig } = useConfigStore()
const isLoading = ref(true)
const tabValue = ref('preview')
const resizableRef = ref<InstanceType<typeof ResizablePanel>>()
const rawString = ref('')
const codeHtml = ref('')
const metadata = reactive({
description: null as string | null,
iframeHeight: null as string | null,
containerClass: null as string | null,
})
function removeScript(code: string) {
const s = new MagicString(code)
const scriptTagRegex = /<script\s+lang="ts"\s*>[\s\S]+?<\/script>/g
let match
// eslint-disable-next-line no-cond-assign
while ((match = scriptTagRegex.exec(code)) !== null) {
const start = match.index
const end = match.index + match[0].length
s.overwrite(start, end, '') // Replace the script tag with an empty string
}
return s.trimStart().toString()
}
function transformImportPath(code: string) {
const s = new MagicString(code)
s.replaceAll(`@/lib/registry/${style.value}`, codeConfig.value.componentsPath)
s.replaceAll(`@/lib/utils`, codeConfig.value.utilsPath)
return s.toString()
}
watch([style, codeConfig], async () => {
try {
const baseRawString = await import(`../../../src/lib/registry/${style.value}/block/${props.name}.vue?raw`).then(res => res.default.trim())
rawString.value = transformImportPath(removeScript(baseRawString))
if (!metadata.description) {
const { descriptor } = parse(baseRawString)
const ast = compileScript(descriptor, { id: '' })
walk(ast.scriptAst, {
enter(node: any) {
const declaration = node.declaration
// Check if the declaration is a variable declaration
if (declaration?.type === 'VariableDeclaration') {
// Extract variable names and their values
declaration.declarations.forEach((decl: any) => {
// @ts-expect-error ignore missing type
metadata[decl.id.name] = decl.init ? decl.init.value : null
})
}
},
})
}
codeHtml.value = highlight(rawString.value, 'vue')
}
catch (err) {
console.error(err)
}
}, { immediate: true, deep: true })
</script>
<template>
<Tabs
:id="name"
v-model="tabValue"
class="relative grid w-full scroll-m-20 gap-4"
:style=" {
'--container-height': metadata.iframeHeight ?? '600px',
}"
>
<div class="flex flex-col items-center gap-4 sm:flex-row">
<div class="flex items-center gap-2">
<TabsList class="hidden sm:flex">
<TabsTrigger value="preview">
Preview
</TabsTrigger>
<TabsTrigger value="code">
Code
</TabsTrigger>
</TabsList>
<div class="hidden items-center gap-2 sm:flex">
<Separator
orientation="vertical"
class="mx-2 hidden h-4 md:flex"
/>
<div class="flex items-center gap-2">
<a :href="`#${name}`">
<Badge variant="outline">{{ name }}</Badge>
</a>
<Popover>
<PopoverTrigger class="hidden text-muted-foreground hover:text-foreground sm:flex">
<Info class="h-3.5 w-3.5" />
<span class="sr-only">Block description</span>
</PopoverTrigger>
<PopoverContent
side="right"
:side-offset="10"
class="text-sm"
>
{{ metadata.description }}
</PopoverContent>
</Popover>
</div>
</div>
</div>
<div class="flex items-center gap-2 pr-[14px] sm:ml-auto">
<div class="hidden h-[28px] items-center gap-1.5 rounded-md border p-[2px] shadow-sm md:flex">
<ToggleGroup
type="single"
default-value="100"
@update:model-value="(value) => {
resizableRef?.resize(parseInt(value as string))
}"
>
<ToggleGroupItem
value="100"
class="h-[22px] w-[22px] rounded-sm p-0"
>
<Monitor class="h-3.5 w-3.5" />
</ToggleGroupItem>
<ToggleGroupItem
value="60"
class="h-[22px] w-[22px] rounded-sm p-0"
>
<Tablet class="h-3.5 w-3.5" />
</ToggleGroupItem>
<ToggleGroupItem
value="30"
class="h-[22px] w-[22px] rounded-sm p-0"
>
<Smartphone class="h-3.5 w-3.5" />
</ToggleGroupItem>
</ToggleGroup>
</div>
<Separator
orientation="vertical"
class="mx-2 hidden h-4 md:flex"
/>
<StyleSwitcher class="h-7" />
<Popover>
<PopoverTrigger class="hidden text-muted-foreground hover:text-foreground sm:flex">
<CircleHelp class="h-3.5 w-3.5" />
<span class="sr-only">Block description</span>
</PopoverTrigger>
<PopoverContent
side="top"
:side-offset="20"
class="space-y-3 rounded-[0.5rem] text-sm"
>
<p class="font-medium">
What is the difference between the New York and Default style?
</p>
<p>
A style comes with its own set of components, animations,
icons and more.
</p>
<p>
The <span class="font-medium">Default</span> style has
larger inputs, uses lucide-vue-next for icons and
tailwindcss-animate for animations.
</p>
<p>
The <span class="font-medium">New York</span> style ships
with smaller buttons and inputs. It also uses shadows on cards
and buttons.
</p>
</PopoverContent>
</Popover>
<Separator orientation="vertical" class="mx-2 h-4" />
<BlockCopyButton :code="rawString" />
<!-- <V0Button
name="{block.name}"
description="{block.description" || "Edit in v0"}
code="{block.code}"
style="{block.style}"
/> -->
</div>
</div>
<TabsContent
v-show="tabValue === 'preview'"
force-mount
value="preview"
class="relative after:absolute after:inset-0 after:right-3 after:z-0 after:rounded-lg after:bg-muted h-[--container-height] px-0"
>
<ResizablePanelGroup id="block-resizable" direction="horizontal" class="relative z-10">
<ResizablePanel
id="block-resizable-panel-1"
ref="resizableRef"
:default-size="100"
:min-size="30"
:as-child="true"
>
<BlockPreview :name="name" styles="default" :container-class="metadata.containerClass ?? ''" container />
</ResizablePanel>
<ResizableHandle id="block-resizable-handle" class="relative hidden w-3 bg-transparent p-0 after:absolute after:right-0 after:top-1/2 after:h-8 after:w-[6px] after:-translate-y-1/2 after:translate-x-[-1px] after:rounded-full after:bg-border after:transition-all after:hover:h-10 sm:block" />
<ResizablePanel id="block-resizable-panel-2" :default-size="0" :min-size="0" />
</ResizablePanelGroup>
</TabsContent>
<TabsContent value="code" class="h-[--container-height]">
<div
class="language-vue !h-full !max-h-[none] !mt-0"
v-html="codeHtml"
/>
</TabsContent>
</Tabs>
</template>

View File

@ -0,0 +1,38 @@
<script setup lang="ts">
import { Button } from '@/lib/registry/new-york/ui/button'
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from '@/lib/registry/new-york/ui/tooltip'
import { CheckIcon, ClipboardIcon } from '@radix-icons/vue'
import { useClipboard } from '@vueuse/core'
import { toRefs } from 'vue'
const props = withDefaults(defineProps<{
code?: string
}>(), {
code: '',
})
const { code } = toRefs(props)
const { copy, copied } = useClipboard({ source: code })
</script>
<template>
<Tooltip :delay-duration="100">
<TooltipTrigger as-child>
<Button
size="icon"
variant="outline"
class="h-7 w-7 [&_svg]:size-3.5"
@click="copy()"
>
<span class="sr-only">Copy</span>
<CheckIcon v-if="copied" />
<ClipboardIcon v-else />
</Button>
</TooltipTrigger>
<TooltipContent>Copy code</TooltipContent>
</Tooltip>
</template>

View File

@ -0,0 +1,12 @@
<script setup lang="ts">
import { useUrlSearchParams } from '@vueuse/core'
import ComponentLoader from './ComponentLoader.vue'
const params = useUrlSearchParams('history')
</script>
<template>
<div v-if="params.name" :class="params.containerClass">
<ComponentLoader :key="params.style?.toString()" :name="params.name?.toString()" :type-name="'block'" />
</div>
</template>

View File

@ -0,0 +1,44 @@
<script setup lang="ts">
import { computed, ref } from 'vue'
import Spinner from './Spinner.vue'
const props = defineProps<{
name: string
styles?: string
containerClass?: string
container?: boolean
}>()
const isLoading = ref(true)
const iframeURL = computed(() => {
// @ts-expect-error env available in import.meta
if (import.meta.env.SSR)
return ''
const url = new URL(`${window.location.origin}/blocks/renderer`)
Object.entries(props).forEach(([key, value]) => {
if (value)
url.searchParams.append(key, value as string)
})
return url.href
})
</script>
<template>
<div class="relative rounded-lg border overflow-hidden bg-background" :class="[container ? '' : 'aspect-[4/2.5]']">
<div v-if="isLoading" class="flex items-center justify-center h-full">
<Spinner />
</div>
<div
:class="[container ? 'w-full' : 'absolute inset-0 hidden w-[1600px] bg-background md:block']"
>
<iframe
v-show="!isLoading"
:src="iframeURL"
class="relative z-20 w-full bg-background" :class="[container ? 'h-[--container-height]' : 'size-full']"
@load="isLoading = false"
/>
</div>
</div>
</template>

View File

@ -0,0 +1,53 @@
<script setup lang="ts">
import { buttonVariants } from '@/lib/registry/new-york/ui/button'
import { cn } from '@/lib/utils'
import GitHubIcon from '~icons/radix-icons/github-logo'
import { ref } from 'vue'
import Announcement from '../components/Announcement.vue'
import PageAction from '../components/PageAction.vue'
import PageHeader from '../components/PageHeader.vue'
import PageHeaderDescription from '../components/PageHeaderDescription.vue'
import PageHeaderHeading from '../components/PageHeaderHeading.vue'
import BlockContainer from './BlockContainer.vue'
const blocks = ref<string[]>([])
import('../../../__registry__/index').then((res) => {
blocks.value = Object.values(res.Index.default).filter(i => i.type === 'components:block').map(i => i.name)
})
</script>
<template>
<PageHeader class="page-header pb-8">
<Announcement />
<PageHeaderHeading>Building Blocks for the Web</PageHeaderHeading>
<PageHeaderDescription>
Beautifully designed. Copy and paste into your apps. Open Source.
</PageHeaderDescription>
<PageAction>
<a
href="/blocks.html#blocks"
:class="cn(buttonVariants(), 'rounded-[6px]')"
>
Browse
</a>
<a
href="https://github.com/radix-vue/shadcn-vue"
target="_blank"
:class="cn(
buttonVariants({ variant: 'outline' }),
'rounded-[6px]',
)"
>
<GitHubIcon class="mr-2 h-4 w-4" />
GitHub
</a>
</PageAction>
</PageHeader>
<section id="blocks" class="grid scroll-mt-24 gap-24 lg:gap-48">
<BlockContainer v-for="block in blocks" :key="block" :name="block" />
</section>
</template>

View File

@ -19,7 +19,7 @@ defineProps<CalloutProps>()
<AlertTitle v-if="title">
{{ title }}
</AlertTitle>
<AlertDescription>
<AlertDescription class="[&_a]:underline">
<slot />
</AlertDescription>
</Alert>

View File

@ -0,0 +1,71 @@
<script setup lang="ts">
import { useData } from 'vitepress'
import { onMounted, ref, watch } from 'vue'
const { page, theme } = useData()
const carbonOptions = theme.value.carbonAds
const container = ref()
let isInitialized = false
function init() {
if (!isInitialized) {
isInitialized = true
const s = document.createElement('script')
s.type = 'text/javascript'
s.id = '_carbonads_js'
s.src = `//cdn.carbonads.com/carbon.js?serve=${carbonOptions.code}&placement=${carbonOptions.placement}&format=cover`
s.async = true
container.value.appendChild(s)
}
}
watch(() => page.value.relativePath, () => {
if (isInitialized) {
;(window as any)._carbonads?.refresh()
}
})
// no need to account for option changes during dev, we can just
// refresh the page
if (carbonOptions) {
onMounted(() => {
// @ts-expect-error ignoring env
if (import.meta.env.DEV)
return
// if the page is loaded when aside is active, load carbon directly.
// otherwise, only load it if the page resizes to wide enough. this avoids
// loading carbon at all on mobile where it's never shown
init()
})
}
</script>
<template>
<div
ref="container"
/>
</template>
<style>
#carbon-responsive {
@apply w-[238px] !mt-6;
}
.carbon-responsive-wrap {
@apply bg-muted/50 border border-muted p-4 rounded-md flex flex-col items-center !important;
}
.carbon-responsive-wrap .carbon-img {
@apply flex-none rounded overflow-hidden !important;
}
.carbon-responsive-wrap .carbon-text {
@apply text-muted-foreground text-sm flex-none text-center !important;
}
#carbonads .carbon-poweredby {
@apply bg-background text-muted-foreground block text-right text-[10px] uppercase no-underline !important;
}
</style>

View File

@ -0,0 +1,98 @@
<script lang="ts" setup>
import { Button } from '@/lib/registry/new-york/ui/button'
import { FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from '@/lib/registry/new-york/ui/form'
import { Input } from '@/lib/registry/new-york/ui/input'
import { Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger } from '@/lib/registry/new-york/ui/sheet'
import { useConfigStore } from '@/stores/config'
import { toTypedSchema } from '@vee-validate/zod'
import RadixIconsGear from '~icons/radix-icons/gear'
import { useForm } from 'vee-validate'
import * as z from 'zod'
const { codeConfig, setCodeConfig } = useConfigStore()
const formSchema = toTypedSchema(z.object({
prefix: z.string().default(''),
componentsPath: z.string().default('@/components'),
utilsPath: z.string().default('@/utils'),
}))
const { handleSubmit, setValues } = useForm({
validationSchema: formSchema,
initialValues: codeConfig.value,
})
const onSubmit = handleSubmit((values) => {
setCodeConfig(values)
setValues(values)
})
</script>
<template>
<Sheet
@update:open="(open) => {
if (open) setValues(codeConfig)
}"
>
<SheetTrigger as-child>
<Button
class="w-9 h-9"
:variant="'ghost'"
:size="'icon'"
>
<RadixIconsGear class="w-5 h-5" />
</Button>
</SheetTrigger>
<SheetContent>
<form @submit="onSubmit">
<SheetHeader>
<SheetTitle>Edit code config</SheetTitle>
<SheetDescription>
Configure how the CodeBlock should render on the site.
</SheetDescription>
</SheetHeader>
<div class="my-4">
<!-- <FormField v-slot="{ componentField }" name="prefix">
<FormItem>
<FormLabel>Prefix</FormLabel>
<FormControl>
<Input placeholder="" v-bind="componentField" />
</FormControl>
<FormDescription />
<FormMessage />
</FormItem>
</FormField> -->
<FormField v-slot="{ componentField }" name="componentsPath">
<FormItem>
<FormLabel>Components Path</FormLabel>
<FormControl>
<Input placeholder="@/components" v-bind="componentField" />
</FormControl>
<FormDescription />
<FormMessage />
</FormItem>
</FormField>
<FormField v-slot="{ componentField }" name="utilsPath">
<FormItem>
<FormLabel>Utils Path</FormLabel>
<FormControl>
<Input placeholder="@/utils" v-bind="componentField" />
</FormControl>
<FormDescription />
<FormMessage />
</FormItem>
</FormField>
</div>
<SheetFooter>
<SheetClose as-child>
<Button type="submit">
Save changes
</Button>
</SheetClose>
</SheetFooter>
</form>
</SheetContent>
</Sheet>
</template>

View File

@ -1,10 +1,10 @@
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import type { Style } from '@/lib/registry/styles'
import { Button } from '@/lib/registry/new-york/ui/button'
import { Icon } from '@iconify/vue'
import { ref, toRefs, watch } from 'vue'
import { makeCodeSandboxParams } from '../utils/codeeditor'
import Tooltip from './Tooltip.vue'
import { Button } from '@/lib/registry/new-york/ui/button'
import { type Style } from '@/lib/registry/styles'
const props = defineProps<{
name: string
@ -12,11 +12,12 @@ const props = defineProps<{
style: Style
}>()
const { code } = toRefs(props)
const sources = ref<Record<string, string>>({})
onMounted(() => {
sources.value['App.vue'] = props.code
})
watch(code, () => {
sources.value['App.vue'] = code.value
}, { immediate: true })
</script>
<template>

View File

@ -0,0 +1,46 @@
import { useConfigStore } from '@/stores/config'
import { cloneVNode, defineComponent, type VNode, type VNodeArrayChildren } from 'vue'
function crawlSpan(children: VNodeArrayChildren, cb: (vnode: VNode) => void) {
children.forEach((childNode) => {
if (!Array.isArray(childNode) && typeof childNode === 'object') {
if (typeof childNode?.children === 'string')
cb(childNode)
else
crawlSpan(childNode?.children as VNodeArrayChildren ?? [], cb)
}
})
}
export default defineComponent(
(props, { slots }) => {
const { codeConfig } = useConfigStore()
return () => {
const clonedVNode = slots.default?.()?.[0]
? cloneVNode(slots.default?.()?.[0], {
key: JSON.stringify(codeConfig.value),
})
: undefined
// @ts-expect-error cloneVNode
const preVNode = [...clonedVNode?.children].find((node: VNode) => node.type === 'pre') as VNode
// @ts-expect-error cloneVNode
const codeVNode = preVNode.children?.at(0) as VNode
if (codeVNode) {
crawlSpan(codeVNode.children as VNodeArrayChildren, (vnode) => {
if (typeof vnode.children === 'string') {
vnode.children = vnode.children.replaceAll('@/components', codeConfig.value.componentsPath)
vnode.children = vnode.children.replaceAll('@/libs', codeConfig.value.utilsPath)
}
})
return clonedVNode
}
else {
return slots.default?.()
}
}
},
)

View File

@ -1,16 +1,17 @@
<script setup lang="ts">
import { useConfigStore } from '@/stores/config'
import { defineAsyncComponent } from 'vue'
import Spinner from './Spinner.vue'
import { useConfigStore } from '@/stores/config'
const props = defineProps<{
name: string
typeName?: 'example' | 'block'
}>()
const { style } = useConfigStore()
const Component = defineAsyncComponent({
loadingComponent: Spinner,
loader: () => import(`../../../src/lib/registry/${style.value}/example/${props.name}.vue`),
loader: () => import(`../../../src/lib/registry/${style.value}/${props.typeName}/${props.name}.vue`),
timeout: 5000,
})
</script>

View File

@ -1,24 +1,49 @@
<script setup lang="ts">
import StyleSwitcher from './StyleSwitcher.vue'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/lib/registry/default/ui/tabs'
import { cn } from '@/lib/utils'
import { useConfigStore } from '@/stores/config'
import { useClipboard } from '@vueuse/core'
import MagicString from 'magic-string'
import { computed, ref, watch } from 'vue'
import { highlight } from '../config/shiki'
import CodeSandbox from './CodeSandbox.vue'
import ComponentLoader from './ComponentLoader.vue'
import Stackblitz from './Stackblitz.vue'
import CodeSandbox from './CodeSandbox.vue'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/lib/registry/default/ui/tabs'
import { useConfigStore } from '@/stores/config'
import { cn } from '@/lib/utils'
import StyleSwitcher from './StyleSwitcher.vue'
defineOptions({
inheritAttrs: false,
})
withDefaults(defineProps<{
const props = withDefaults(defineProps<{
name: string
align?: 'center' | 'start' | 'end'
sfcTsCode?: string
sfcTsHtml?: string
}>(), { align: 'center' })
const { style } = useConfigStore()
const { style, codeConfig } = useConfigStore()
const rawString = ref('')
const codeHtml = ref('')
const transformedRawString = computed(() => transformImportPath(rawString.value))
function transformImportPath(code: string) {
const s = new MagicString(code)
s.replaceAll(`@/lib/registry/${style.value}`, codeConfig.value.componentsPath)
s.replaceAll(`@/lib/utils`, codeConfig.value.utilsPath)
return s.toString()
}
watch([style, codeConfig], async () => {
try {
rawString.value = await import(`../../../src/lib/registry/${style.value}/example/${props.name}.vue?raw`).then(res => res.default.trim())
codeHtml.value = highlight(transformedRawString.value, 'vue')
}
catch (err) {
console.error(err)
}
}, { immediate: true, deep: true })
const { copy, copied } = useClipboard()
</script>
<template>
@ -47,22 +72,26 @@ const { style } = useConfigStore()
<StyleSwitcher />
<div class="flex items-center gap-x-1">
<Stackblitz :key="style" :style="style" :name="name" :code="decodeURIComponent(sfcTsCode ?? '')" />
<CodeSandbox :key="style" :style="style" :name="name" :code="decodeURIComponent(sfcTsCode ?? '')" />
<Stackblitz :key="style" :style="style" :name="name" :code="rawString" />
<CodeSandbox :key="style" :style="style" :name="name" :code="rawString" />
</div>
</div>
<div
:class="cn('preview flex min-h-[350px] w-full justify-center p-6 lg:p-10', {
:class="cn('preview flex min-h-[350px] w-full justify-center p-10 items-center', {
'items-center': align === 'center',
'items-start': align === 'start',
'items-end': align === 'end',
})"
>
<ComponentLoader v-bind="$attrs" :key="style" :name="name" />
<ComponentLoader v-bind="$attrs" :key="style" :name="name" :type-name="'example'" />
</div>
</TabsContent>
<TabsContent value="code">
<div v-if="sfcTsHtml" class="language-vue" style="flex: 1;" v-html="decodeURIComponent(sfcTsHtml)" />
<TabsContent value="code" class="vp-doc">
<div v-if="codeHtml" class="language-vue" style="flex: 1;">
<button title="Copy Code" class="copy" :class="{ copied }" @click="copy(transformedRawString)" />
<div v-html="codeHtml" />
</div>
<slot v-else />
</TabsContent>
</Tabs>

View File

@ -1,11 +1,11 @@
<script setup lang="ts">
import { computed, ref } from 'vue'
import { useClipboard } from '@vueuse/core'
import { useConfigStore } from '@/stores/config'
import { themes } from '@/lib/registry'
import { Button } from '@/lib/registry/new-york/ui/button'
import { useConfigStore } from '@/stores/config'
import { useClipboard } from '@vueuse/core'
import CheckIcon from '~icons/radix-icons/check'
import CopyIcon from '~icons/radix-icons/copy'
import { computed, ref } from 'vue'
const { theme, config } = useConfigStore()
@ -15,7 +15,7 @@ const { copy, copied } = useClipboard()
const codeRef = ref<HTMLElement>()
async function copyCode() {
await copy(codeRef.value?.innerText ?? '')
await copy(codeRef.value?.textContent?.replace(/\u00A0/g, ' ') ?? '')
}
</script>

View File

@ -0,0 +1,53 @@
<script setup lang="ts">
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbSeparator,
} from '@/lib/registry/new-york/ui/breadcrumb'
import { useRoute } from 'vitepress'
import { computed } from 'vue'
const route = useRoute()
interface Item {
title: string
href: string
}
function generateBreadcrumb(url: string): Item[] {
const breadcrumbItems: Item[] = []
const segments = url.split('/').filter(segment => segment !== '') // Remove empty segments
// Construct breadcrumb for each segment
let href = ''
for (let i = 0; i < segments.length; i++) {
const segment = segments[i].replace('.html', '')
href += `/${segment}`
breadcrumbItems.push({ title: segment, href })
}
return breadcrumbItems
}
const breadcrumbs = computed(() => generateBreadcrumb(route.path))
</script>
<template>
<Breadcrumb>
<BreadcrumbList>
<template v-for="(breadcrumb, index) in breadcrumbs" :key="breadcrumb.title">
<BreadcrumbItem>
<BreadcrumbLink
class="capitalize"
:href="index === 0 ? undefined : breadcrumb.href"
:class="{ 'text-foreground': index === breadcrumbs.length - 1 }"
>
{{ breadcrumb.title }}
</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator v-if="index !== breadcrumbs.length - 1" />
</template>
</BreadcrumbList>
</Breadcrumb>
</template>

View File

@ -1,8 +1,8 @@
<script setup lang="ts">
import { Button } from '@/lib/registry/default/ui/button'
import Pencil2Icon from '~icons/radix-icons/pencil-2'
import { useData } from 'vitepress'
import { computed } from 'vue'
import Pencil2Icon from '~icons/radix-icons/pencil-2'
import { Button } from '@/lib/registry/default/ui/button'
const { theme, page } = useData()

View File

@ -1,13 +1,18 @@
<script setup lang="ts">
import { ScrollArea, ScrollBar } from '@/lib/registry/default/ui/scroll-area'
import { cn } from '@/lib/utils'
import ArrowRightIcon from '~icons/radix-icons/arrow-right'
import { useRoute } from 'vitepress'
import { computed, toRefs } from 'vue'
import { cn } from '@/lib/utils'
import { ScrollArea, ScrollBar } from '@/lib/registry/default/ui/scroll-area'
import ArrowRightIcon from '~icons/radix-icons/arrow-right'
const { path } = toRefs(useRoute())
const examples = [
{
name: 'Mail',
href: '/examples/mail',
code: 'https://github.com/radix-vue/shadcn-vue/tree/dev/apps/www/src/examples/mail',
},
{
name: 'Dashboard',
href: '/examples/dashboard',
@ -30,7 +35,7 @@ const examples = [
},
{
name: 'Forms',
href: '/examples/forms/forms',
href: '/examples/forms',
code: 'https://github.com/radix-vue/shadcn-vue/tree/dev/apps/www/src/examples/forms',
},
{
@ -58,7 +63,7 @@ const currentExample = computed(() => examples.find(ex => path.value.startsWith(
:href="example.href"
:class="cn(
'flex items-center px-4',
path?.startsWith(example.href) || (path === '/' && example.name === 'Dashboard')
path?.startsWith(example.href) || (path === '/' && example.name === 'Mail')
? 'font-bold text-primary'
: 'font-medium text-muted-foreground',
)"

View File

@ -0,0 +1,54 @@
<script lang="ts" setup>
import type { Color } from '../types/colors'
import { colors } from '@/lib/registry'
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/lib/registry/new-york/ui/tooltip'
import { useConfigStore } from '@/stores/config'
import RadixIconsCheck from '~icons/radix-icons/check'
defineProps<{
allColors: Color[]
}>()
const { theme, setTheme } = useConfigStore()
</script>
<template>
<div>
<TooltipProvider
v-for="(color, index) in allColors.slice(0, 5)"
:key="index"
>
<Tooltip>
<TooltipTrigger as-child>
<button
:key="index"
class="flex h-9 w-9 items-center justify-center rounded-full border-2 border-border text-xs"
:class="
color === theme
? 'border-primary'
: 'border-transparent'
"
@click="setTheme(color)"
>
<span
class="flex h-6 w-6 items-center justify-center rounded-full"
:style="{ backgroundColor: colors[color][6].rgb }"
>
<RadixIconsCheck
v-if="color === theme"
class="h-4 w-4 text-white"
/>
</span>
</button>
</TooltipTrigger>
<TooltipContent
align="center"
:side-offset="1"
class="capitalize bg-zinc-900 text-zinc-50"
>
{{ allColors[index] }}
</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
</template>

View File

@ -18,9 +18,9 @@ const kbdClass = computed(() => {
{
variants: {
size: {
xs: 'min-h-[16px] text-[10px] h-4 px-1',
sm: 'min-h-[20px] text-[11px] h-5 px-1',
md: 'min-h-[24px] text-[12px] h-6 px-1.5',
xs: 'min-h-4 text-[10px] h-4 px-1',
sm: 'min-h-5 text-[11px] h-5 px-1',
md: 'min-h-6 text-[12px] h-6 px-1.5',
},
},
},

View File

@ -1,37 +1,28 @@
<script setup lang="ts">
import PageHeader from '../components/PageHeader.vue'
import PageHeaderHeading from '../components/PageHeaderHeading.vue'
import PageHeaderDescription from '../components/PageHeaderDescription.vue'
import ExamplesNav from '../components/ExamplesNav.vue'
import { announcementConfig } from '../config/site'
import GitHubIcon from '~icons/radix-icons/github-logo'
import MailExample from '@/examples/mail/Example.vue'
import { buttonVariants } from '@/lib/registry/new-york/ui/button'
import { Separator } from '@/lib/registry/new-york/ui/separator'
import { cn } from '@/lib/utils'
import GitHubIcon from '~icons/radix-icons/github-logo'
import Announcement from '../components/Announcement.vue'
import ExamplesNav from '../components/ExamplesNav.vue'
import PageAction from '../components/PageAction.vue'
import DashboardExample from '@/examples/dashboard/Example.vue'
import PageHeader from '../components/PageHeader.vue'
import PageHeaderDescription from '../components/PageHeaderDescription.vue'
import PageHeaderHeading from '../components/PageHeaderHeading.vue'
</script>
<template>
<PageHeader class="page-header pb-8">
<a
:href="announcementConfig.link"
class="inline-flex items-center rounded-lg bg-muted px-3 py-1 text-sm font-medium"
>
{{ announcementConfig.icon }} <Separator class="mx-2 h-4" orientation="vertical" />
<span class="sm:hidden">{{ announcementConfig.title }}</span>
<span class="hidden sm:inline">{{ announcementConfig.title }}
</span>
<!-- <ArrowRightIcon class="ml-1 h-4 w-4" /> -->
</a>
<Announcement />
<PageHeaderHeading>Build your component library.</PageHeaderHeading>
<PageHeaderDescription>
Beautifully designed components that you can copy and paste into your
apps. Accessible. Customizable. Open Source.
</PageHeaderDescription>
<section class="flex w-full items-center space-x-4 pb-8 pt-4 md:pb-10">
<PageAction>
<a
href="/docs/introduction"
:class="cn(buttonVariants(), 'rounded-[6px]')"
@ -49,22 +40,22 @@ import DashboardExample from '@/examples/dashboard/Example.vue'
<GitHubIcon class="mr-2 h-4 w-4" />
GitHub
</a>
</section>
</PageAction>
</PageHeader>
<ExamplesNav />
<section class="space-y-8 overflow-hidden rounded-lg border-2 border-primary dark:border-muted md:hidden">
<VPImage
alt="Dashboard"
alt="Mail"
width="1280"
height="866" class="block" :image="{
dark: '/examples/dashboard-dark.png',
light: '/examples/dashboard-light.png',
dark: '/examples/mail-dark.png',
light: '/examples/mail-light.png',
}"
/>
</section>
<section class="hidden md:block">
<div class="overflow-hidden rounded-[0.5rem] border bg-background shadow">
<DashboardExample />
<MailExample />
</div>
</section>
</template>

View File

@ -3,7 +3,7 @@ import { cn } from '@/lib/utils'
</script>
<template>
<a :class="cn('flex w-full flex-col items-center rounded-xl border bg-card p-6 text-card-foreground shadow transition-colors hover:bg-muted/50 sm:p-10', $attrs.class ?? '')">
<a :class="cn('flex w-full flex-col items-center rounded-lg border bg-card p-6 text-card-foreground shadow transition-colors hover:bg-muted/50 sm:p-10', $attrs.class ?? '')">
<slot />
</a>
</template>

View File

@ -2,7 +2,7 @@
</script>
<template>
<a href="/" class="mr-6 flex items-center space-x-2">
<a href="/" class="mr-4 md:mr-2 lg:mr-6 flex items-center lg:space-x1 xl:space-x-2">
<svg class="h-6 w-6" viewBox="0 0 256 256" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_102_1338)">
<path d="M208 128L128 208" stroke="#41B883" stroke-width="16" stroke-linecap="round" stroke-linejoin="round" />
@ -15,7 +15,7 @@
</defs>
</svg>
<span class="font-bold ">
<span class="font-bold">
shadcn-vue
</span>
</a>

View File

@ -1,11 +1,10 @@
<script setup lang="ts">
import { Button } from '@/lib/registry/default/ui/button'
import { ScrollArea } from '@/lib/registry/default/ui/scroll-area'
import { Sheet, SheetContent, SheetTrigger } from '@/lib/registry/default/ui/sheet'
import { ref } from 'vue'
import { docsConfig } from '../config/docs'
import Logo from './Logo.vue'
import { Sheet, SheetContent, SheetTrigger } from '@/lib/registry/default/ui/sheet'
import { Button } from '@/lib/registry/default/ui/button'
import { ScrollArea } from '@/lib/registry/default/ui/scroll-area'
import ViewVerticalIcon from '~icons/radix-icons/view-vertical'
const open = ref(false)
</script>
@ -17,7 +16,35 @@ const open = ref(false)
variant="ghost"
class="mr-2 px-2 text-base flex-shrink-0 hover:bg-transparent focus-visible:bg-transparent focus-visible:ring-0 focus-visible:ring-offset-0 md:hidden"
>
<ViewVerticalIcon class="h-5 w-5" />
<svg
strokeWidth="1.5"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
class="h-5 w-5"
>
<path
d="M3 5H11"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M3 12H16"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M3 19H21"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
<span class="sr-only">Toggle Menu</span>
</Button>
</SheetTrigger>
@ -36,17 +63,26 @@ const open = ref(false)
</div>
<div class="flex flex-col space-y-2">
<div v-for="(items, index) in docsConfig.sidebarNav" :key="index" class="flex flex-col space-y-3 pt-6">
<h4 class="font-medium">
{{ items.title }}
</h4>
<div class="flex items-center">
<h4 class="font-medium">
{{ items.title }}
</h4>
<span v-if="items.label" class="ml-2 rounded-md bg-[#adfa1d] px-1.5 py-0.5 text-xs leading-none text-[#000000] no-underline group-hover:no-underline">
{{ items.label }}
</span>
</div>
<a
v-for="item in items.items" :key="item.href"
:href="item.href"
class="text-muted-foreground"
class="text-muted-foreground inline-flex items-center"
@click="open = false"
>
{{ item.title }}
<span v-if="item.label" class="ml-2 rounded-md bg-[#adfa1d] px-1.5 py-0.5 text-xs leading-none text-[#000000] no-underline group-hover:no-underline">
{{ item.label }}
</span>
</a>
</div>
</div>

View File

@ -0,0 +1,14 @@
<script setup lang="ts">
import { cn } from '@/lib/utils'
</script>
<template>
<section
:class="cn(
'flex w-full items-center justify-center space-x-4 py-4 md:pb-10',
$attrs.class ?? '',
)"
>
<slot />
</section>
</template>

View File

@ -7,7 +7,7 @@ import { cn } from '@/lib/utils'
<template>
<section
:class="cn(
'flex max-w-[980px] flex-col items-start gap-2 px-4 pt-8 md:pt-12',
'mx-auto flex max-w-[980px] flex-col items-center gap-2 py-8 md:py-12 md:pb-8 lg:py-24 lg:pb-20',
$attrs.class ?? '',
)"
>

View File

@ -1,10 +1,10 @@
<script setup lang="ts">
import WrapBalancer from 'vue-wrap-balancer'
import { cn } from '@/lib/utils'
import WrapBalancer from 'vue-wrap-balancer'
</script>
<template>
<WrapBalancer :class="cn('max-w-[750px] text-lg text-muted-foreground sm:text-xl', $attrs.class ?? '')" :prefer-native="false">
<WrapBalancer :class="cn('max-w-[750px] text-center text-lg font-light text-foreground', $attrs.class ?? '')" :prefer-native="false">
<slot />
</WrapBalancer>
</template>

View File

@ -5,7 +5,7 @@ import { cn } from '@/lib/utils'
<template>
<h1
:class="cn(
'text-3xl font-bold leading-tight tracking-tighter md:text-5xl lg:leading-[1.1]',
'text-center text-3xl font-bold leading-tight tracking-tighter md:text-5xl lg:leading-[1.1]',
$attrs.class ?? '',
)"
>

View File

@ -1,10 +1,10 @@
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import type { Style } from '@/lib/registry/styles'
import { Button } from '@/lib/registry/new-york/ui/button'
import { Icon } from '@iconify/vue'
import { ref, toRefs, watch } from 'vue'
import { makeStackblitzParams } from '../utils/codeeditor'
import Tooltip from './Tooltip.vue'
import { Button } from '@/lib/registry/new-york/ui/button'
import { type Style } from '@/lib/registry/styles'
const props = defineProps<{
name: string
@ -12,11 +12,12 @@ const props = defineProps<{
style: Style
}>()
const { code } = toRefs(props)
const sources = ref<Record<string, string>>({})
onMounted(() => {
sources.value['App.vue'] = props.code
})
watch(code, () => {
sources.value['App.vue'] = code.value
}, { immediate: true })
function handleClick() {
makeStackblitzParams(props.name, props.style, sources.value)

View File

@ -1,8 +1,5 @@
<script setup lang="ts">
import { type SelectTriggerProps } from 'radix-vue'
import { useConfigStore } from '@/stores/config'
import { cn } from '@/lib/utils'
import type { SelectTriggerProps } from 'radix-vue'
import {
Select,
SelectContent,
@ -10,7 +7,10 @@ import {
SelectTrigger,
SelectValue,
} from '@/lib/registry/new-york/ui/select'
import { styles } from '@/lib/registry/styles'
import { cn } from '@/lib/utils'
import { useConfigStore } from '@/stores/config'
const props = defineProps<SelectTriggerProps & { class?: string }>()
const { config } = useConfigStore()

View File

@ -0,0 +1,15 @@
<script setup lang="ts">
import { TabsContent } from '@/lib/registry/default/ui/tabs'
withDefaults(defineProps<{
title?: string
}>(), {
title: '',
})
</script>
<template>
<TabsContent :value="title" class="relative space-y-10">
<slot />
</TabsContent>
</template>

View File

@ -1,10 +1,16 @@
<script setup lang="ts">
import type { TableOfContents, TableOfContentsItem } from '../types/docs'
import { buttonVariants } from '@/lib/registry/default/ui/button'
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/lib/registry/default/ui/collapsible'
import { ScrollArea } from '@/lib/registry/default/ui/scroll-area'
import { onContentUpdated } from 'vitepress'
import { shallowRef } from 'vue'
import type { TableOfContents, TableOfContentsItem } from '../types/docs'
import CarbonAds from '../components/CarbonAds.vue'
import TableOfContentTree from './TableOfContentTree.vue'
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/lib/registry/default/ui/collapsible'
import { buttonVariants } from '@/lib/registry/default/ui/button'
defineProps<{
showCarbonAds?: boolean
}>()
const headers = shallowRef<TableOfContents>()
@ -22,8 +28,8 @@ function getHeadingsWithHierarchy(divId: string) {
headings.forEach((heading: HTMLHeadingElement) => {
const level = Number.parseInt(heading.tagName.charAt(1))
if (!heading.id) {
const newId = heading.innerText
.replaceAll(/[^a-zA-Z0-9 ]/g, '')
const newId = heading.textContent
?.replaceAll(/[^a-z0-9 ]/gi, '')
.replaceAll(' ', '-')
.toLowerCase()
heading.id = `${newId}`
@ -55,11 +61,16 @@ onContentUpdated(() => {
</script>
<template>
<div class="space-y-2 hidden xl:block">
<p class="font-medium">
On This Page
</p>
<TableOfContentTree :tree="headers" :level="1" />
<div class="hidden xl:block">
<ScrollArea orientation="vertical" class="h-[calc(100vh-6.5rem)] z-30 md:block overflow-y-auto" type="hover">
<div class="space-y-2">
<p class="font-medium">
On This Page
</p>
<TableOfContentTree :tree="headers" :level="1" />
<CarbonAds v-if="showCarbonAds" />
</div>
</ScrollArea>
</div>
<div class="block xl:hidden mb-6">

View File

@ -1,8 +1,8 @@
<script setup lang="ts">
import { onMounted, onUnmounted, ref, watch } from 'vue'
import { useRoute } from 'vitepress'
import type { TableOfContentsItem } from '../types/docs'
import { cn } from '@/lib/utils'
import { useRoute } from 'vitepress'
import { onMounted, onUnmounted, ref, watch } from 'vue'
withDefaults(defineProps<{
level: number

View File

@ -0,0 +1,22 @@
<script setup lang="ts">
import { Tabs, TabsList, TabsTrigger } from '@/lib/registry/default/ui/tabs'
import { computed, useSlots } from 'vue'
const slots = useSlots()
const tabs = computed(() => slots.default?.()?.map(i => i?.props?.title as string) ?? [])
</script>
<template>
<Tabs :default-value="tabs[0]" class="relative mr-auto w-full">
<div class="flex items-center justify-between">
<TabsList class="w-full justify-start rounded-none border-b bg-transparent p-0">
<TabsTrigger v-for="tab in tabs" :key="tab" :value="tab" class="relative h-9 rounded-none border-b-2 border-b-transparent bg-transparent px-4 pb-3 pt-2 font-semibold text-muted-foreground shadow-none transition-none data-[state=active]:border-b-primary data-[state=active]:text-foreground data-[state=active]:shadow-none">
{{ tab }}
</TabsTrigger>
</TabsList>
</div>
<slot />
</Tabs>
</template>

View File

@ -0,0 +1,106 @@
<script lang="ts" setup>
import type { Color } from '../types/colors'
import { colors } from '@/lib/registry'
import { Button } from '@/lib/registry/new-york/ui/button'
import { Label } from '@/lib/registry/new-york/ui/label'
import { RADII, useConfigStore } from '@/stores/config'
import RadixIconsCheck from '~icons/radix-icons/check'
import RadixIconsMoon from '~icons/radix-icons/moon'
import RadixIconsSun from '~icons/radix-icons/sun'
import { useData } from 'vitepress'
defineProps<{
allColors: Color[]
}>()
const { theme, radius, setRadius, setTheme } = useConfigStore()
const { isDark } = useData()
</script>
<template>
<div class="p-4">
<div class="grid space-y-1">
<h1 class="text-md text-foreground font-semibold">
Customize
</h1>
<p class="text-xs text-muted-foreground">
Pick a style and color for your components.
</p>
</div>
<div class="space-y-1.5 pt-6">
<Label for="color" class="text-xs"> Color </Label>
<div class="grid grid-cols-3 gap-2 py-1.5">
<Button
v-for="(color, index) in allColors"
:key="index"
variant="outline"
class="h-8 justify-start px-3"
:class="
color === theme
? 'border-foreground border-2'
: ''
"
@click="setTheme(color)"
>
<span
class="h-5 w-5 rounded-full flex items-center justify-center shrink-0"
:style="{ backgroundColor: colors[color][7].rgb }"
>
<RadixIconsCheck
v-if="color === theme"
class="h-3 w-3 text-white"
/>
</span>
<span class="ml-2 text-xs capitalize">
{{ color }}
</span>
</Button>
</div>
</div>
<div class="space-y-1.5 pt-6">
<Label for="radius" class="text-xs"> Radius </Label>
<div class="grid grid-cols-5 gap-2 py-1.5">
<Button
v-for="(r, index) in RADII"
:key="index"
variant="outline"
class="h-8 justify-center px-3"
:class="
r === radius
? 'border-foreground border-2'
: ''
"
@click="setRadius(r)"
>
<span class="text-xs">
{{ r }}
</span>
</Button>
</div>
</div>
<div class="space-y-1.5 pt-6">
<Label for="theme" class="text-xs"> Theme </Label>
<div class="flex space-x-2 py-1.5">
<Button
class="h-8"
variant="outline"
:class="{ 'border-2 border-foreground': !isDark }"
@click="isDark = false"
>
<RadixIconsSun class="w-4 h-4 mr-2" />
<span class="text-xs">Light</span>
</Button>
<Button
class="h-8"
variant="outline"
:class="{ 'border-2 border-foreground': isDark }"
@click="isDark = true"
>
<RadixIconsMoon class="w-4 h-4 mr-2" />
<span class="text-xs">Dark</span>
</Button>
</div>
</div>
</div>
</template>

View File

@ -0,0 +1,47 @@
<script setup lang="ts">
import { Button } from '@/lib/registry/new-york/ui/button'
import { Popover, PopoverContent, PopoverTrigger } from '@/lib/registry/new-york/ui/popover'
import { useConfigStore } from '@/stores/config'
import { Paintbrush } from 'lucide-vue-next'
import { onMounted, watch } from 'vue'
import ThemeCustomizer from './ThemeCustomizer.vue'
import { allColors } from './theming/utils/data'
const { theme, radius } = useConfigStore()
// Whenever the component is mounted, update the document class list
onMounted(() => {
document.documentElement.style.setProperty('--radius', `${radius.value}rem`)
document.documentElement.classList.add(`theme-${theme.value}`)
})
// Whenever the theme value changes, update the document class list
watch(theme, (theme) => {
document.documentElement.classList.remove(
...allColors.map(color => `theme-${color}`),
)
document.documentElement.classList.add(`theme-${theme}`)
})
// Whenever the radius value changes, update the document style
watch(radius, (radius) => {
document.documentElement.style.setProperty('--radius', `${radius}rem`)
})
</script>
<template>
<Popover>
<PopoverTrigger as-child>
<Button
class="w-9 h-9"
:variant="'ghost'"
:size="'icon'"
>
<Paintbrush class="w-4 h-4" />
</Button>
</PopoverTrigger>
<PopoverContent :side-offset="8" align="end" class="w-96">
<ThemeCustomizer :all-colors="allColors" />
</PopoverContent>
</Popover>
</template>

View File

@ -1,7 +1,12 @@
export { default as ComponentPreview } from './ComponentPreview.vue'
export { default as TabPreview } from './TabPreview.vue'
export { default as APITable } from './APITable.vue'
export { default as BlockPreview } from './BlockPreview.vue'
export { default as Callout } from './Callout.vue'
export { default as CodeWrapper } from './CodeWrapper'
export { default as ComponentPreview } from './ComponentPreview.vue'
export { default as LinkedCard } from './LinkedCard.vue'
export { default as ManualInstall } from './ManualInstall.vue'
export { default as Steps } from './Steps.vue'
export { default as TabMarkdown } from './TabMarkdown.vue'
export { default as TabPreview } from './TabPreview.vue'
export { default as TabsMarkdown } from './TabsMarkdown.vue'
export { default as VPImage } from './VPImage.vue'

View File

@ -1,32 +1,32 @@
<script setup lang="ts">
import { ref } from 'vue'
import { addDays, startOfToday } from 'date-fns'
import ThemingLayout from './../../layout/ThemingLayout.vue'
import type { DateRange } from 'radix-vue'
import CookieSettings from '@/examples/cards/components/CookieSettings.vue'
import CreateAccount from '@/examples/cards/components/CreateAccount.vue'
import PaymentMethod from '@/examples/cards/components/PaymentMethod.vue'
import ReportAnIssue from '@/examples/cards/components/ReportAnIssue.vue'
import ShareDocument from '@/examples/cards/components/ShareDocument.vue'
import TeamMembers from '@/examples/cards/components/TeamMembers.vue'
import CardChat from '@/lib/registry/new-york/example/CardChat.vue'
import ActivityGoal from '@/lib/registry/new-york/example/Cards/ActivityGoal.vue'
import Metric from '@/lib/registry/new-york/example/Cards/Metric.vue'
import DataTable from '@/lib/registry/new-york/example/Cards/DataTable.vue'
import CardStats from '@/lib/registry/default/example/CardStats.vue'
import {
Card,
} from '@/lib/registry/new-york/ui/card'
import { Calendar } from '@/lib/registry/new-york/ui/calendar'
import Metric from '@/lib/registry/new-york/example/Cards/Metric.vue'
import CardStats from '@/lib/registry/new-york/example/CardStats.vue'
import { Card } from '@/lib/registry/new-york/ui/card'
import { RangeCalendar } from '@/lib/registry/new-york/ui/range-calendar'
import { getLocalTimeZone, today } from '@internationalized/date'
const goal = ref(350)
import { type Ref, ref } from 'vue'
import ThemingLayout from './../../layout/ThemingLayout.vue'
const now = today(getLocalTimeZone())
const range = ref({
start: startOfToday(),
end: addDays(startOfToday(), 8),
})
start: now,
end: now.add({ days: 8 }),
}) as Ref<DateRange>
</script>
<template>
@ -54,7 +54,7 @@ const range = ref({
<div class="space-y-4 lg:col-span-6 xl:col-span-5 xl:space-y-4">
<div class="hidden gap-1 sm:grid-cols-[280px_1fr] md:grid">
<Card class="max-w-[280px]">
<Calendar v-model.range="range" />
<RangeCalendar v-model="range" />
</Card>
<div class="pt-3 sm:pl-2 sm:pt-0 xl:pl-3">

View File

@ -1,12 +1,42 @@
import { CreditCard } from 'lucide-vue-next'
import RiAppleFill from '~icons/ri/apple-fill'
import RiPaypalFill from '~icons/ri/paypal-fill'
import { CreditCard } from 'lucide-vue-next'
interface Payment {
status: string
email: string
amount: number
}
type Color =
| 'zinc'
| 'slate'
| 'stone'
| 'gray'
| 'neutral'
| 'red'
| 'rose'
| 'orange'
| 'green'
| 'blue'
| 'yellow'
| 'violet'
// Create an array of color values
export const allColors: Color[] = [
'zinc',
'rose',
'blue',
'green',
'orange',
'red',
'slate',
'stone',
'gray',
'neutral',
'yellow',
'violet',
]
// interface Payment {
// status: string
// email: string
// amount: number
// }
interface TeamMember {
name: string

View File

@ -8,11 +8,11 @@ export interface NavItem {
}
export type SidebarNavItem = NavItem & {
items: SidebarNavItem[]
items?: SidebarNavItem[]
}
export type NavItemWithChildren = NavItem & {
items: NavItemWithChildren[]
items?: NavItemWithChildren[]
}
interface DocsConfig {
@ -23,7 +23,7 @@ interface DocsConfig {
export const docsConfig: DocsConfig = {
mainNav: [
{
title: 'Documentation',
title: 'Docs',
href: '/docs/introduction',
},
{
@ -36,7 +36,11 @@ export const docsConfig: DocsConfig = {
},
{
title: 'Examples',
href: '/examples/dashboard',
href: '/examples/mail',
},
{
title: 'Blocks',
href: '/blocks',
},
{
title: 'GitHub',
@ -51,46 +55,47 @@ export const docsConfig: DocsConfig = {
{
title: 'Introduction',
href: '/docs/introduction',
items: [],
},
{
title: 'Installation',
href: '/docs/installation',
items: [],
},
{
title: 'components.json',
href: '/docs/components-json',
items: [],
},
{
title: 'Theming',
href: '/docs/theming',
},
{
title: 'Dark Mode',
href: '/docs/dark-mode',
items: [],
},
{
title: 'CLI',
href: '/docs/cli',
items: [],
},
{
title: 'Typography',
href: '/docs/typography',
items: [],
},
{
title: 'Figma',
href: '/docs/figma',
items: [],
},
{
title: 'Changelog',
href: '/docs/changelog',
items: [],
},
{
title: 'About',
href: '/docs/about',
},
{
title: 'Contribution',
href: '/docs/contribution',
items: [],
},
],
@ -101,21 +106,32 @@ export const docsConfig: DocsConfig = {
{
title: 'Vite',
href: '/docs/installation/vite',
items: [],
},
{
title: 'Nuxt',
href: '/docs/installation/nuxt',
items: [],
},
{
title: 'Astro',
href: '/docs/installation/astro',
items: [],
},
{
title: 'Laravel',
href: '/docs/installation/laravel',
},
],
},
{
title: 'Extended',
items: [
{
title: 'Auto Form',
href: '/docs/components/auto-form',
items: [],
},
{
title: 'Charts',
href: '/docs/charts',
items: [],
},
],
@ -123,40 +139,43 @@ export const docsConfig: DocsConfig = {
{
title: 'Components',
items: [
{
title: 'Sidebar',
href: '/docs/components/sidebar',
label: 'New',
},
{
title: 'Accordion',
href: '/docs/components/accordion',
items: [],
},
{
title: 'Alert',
href: '/docs/components/alert',
items: [],
},
{
title: 'Alert Dialog',
href: '/docs/components/alert-dialog',
items: [],
},
{
title: 'Aspect Ratio',
href: '/docs/components/aspect-ratio',
items: [],
},
{
title: 'Avatar',
href: '/docs/components/avatar',
items: [],
},
{
title: 'Badge',
href: '/docs/components/badge',
},
{
title: 'Breadcrumb',
href: '/docs/components/breadcrumb',
items: [],
},
{
title: 'Button',
href: '/docs/components/button',
items: [],
},
{
title: 'Calendar',
@ -166,37 +185,35 @@ export const docsConfig: DocsConfig = {
{
title: 'Card',
href: '/docs/components/card',
},
{
title: 'Carousel',
href: '/docs/components/carousel',
items: [],
},
{
title: 'Checkbox',
href: '/docs/components/checkbox',
items: [],
},
{
title: 'Collapsible',
href: '/docs/components/collapsible',
items: [],
},
{
title: 'Combobox',
href: '/docs/components/combobox',
items: [],
},
{
title: 'Command',
href: '/docs/components/command',
items: [],
},
{
title: 'Context Menu',
href: '/docs/components/context-menu',
items: [],
},
{
title: 'Data Table',
href: '/docs/components/data-table',
items: [],
},
{
title: 'Date Picker',
@ -206,128 +223,144 @@ export const docsConfig: DocsConfig = {
{
title: 'Dialog',
href: '/docs/components/dialog',
},
{
title: 'Drawer',
href: '/docs/components/drawer',
items: [],
},
{
title: 'Dropdown Menu',
href: '/docs/components/dropdown-menu',
items: [],
},
{
title: 'Form',
href: '/docs/components/form',
items: [],
},
{
title: 'Hover Card',
href: '/docs/components/hover-card',
items: [],
},
{
title: 'Input',
href: '/docs/components/input',
items: [],
},
{
title: 'Label',
href: '/docs/components/label',
items: [],
},
{
title: 'Menubar',
href: '/docs/components/menubar',
items: [],
},
{
title: 'Navigation Menu',
href: '/docs/components/navigation-menu',
items: [],
},
{
title: 'Number Field',
href: '/docs/components/number-field',
},
{
title: 'Pagination',
href: '/docs/components/pagination',
},
{
title: 'PIN Input',
href: '/docs/components/pin-input',
items: [],
},
{
title: 'Popover',
href: '/docs/components/popover',
items: [],
},
{
title: 'Progress',
href: '/docs/components/progress',
items: [],
},
{
title: 'Radio Group',
href: '/docs/components/radio-group',
},
{
title: 'Range Calendar',
href: '/docs/components/range-calendar',
items: [],
},
{
title: 'Resizable',
href: '/docs/components/resizable',
items: [],
},
{
title: 'Scroll Area',
href: '/docs/components/scroll-area',
items: [],
},
{
title: 'Select',
href: '/docs/components/select',
items: [],
},
{
title: 'Separator',
href: '/docs/components/separator',
items: [],
},
{
title: 'Sheet',
href: '/docs/components/sheet',
items: [],
},
{
title: 'Skeleton',
href: '/docs/components/skeleton',
items: [],
},
{
title: 'Slider',
href: '/docs/components/slider',
},
{
title: 'Sonner',
href: '/docs/components/sonner',
items: [],
},
{
title: 'Stepper',
href: '/docs/components/stepper',
},
{
title: 'Switch',
href: '/docs/components/switch',
items: [],
},
{
title: 'Table',
href: '/docs/components/table',
items: [],
},
{
title: 'Tabs',
href: '/docs/components/tabs',
},
{
title: 'Tags Input',
href: '/docs/components/tags-input',
items: [],
},
{
title: 'Textarea',
href: '/docs/components/textarea',
items: [],
},
{
title: 'Toast',
href: '/docs/components/toast',
label: 'New',
items: [],
},
{
title: 'Toggle',
href: '/docs/components/toggle',
items: [],
},
{
title: 'Toggle Group',
href: '/docs/components/toggle-group',
},
{
title: 'Tooltip',
href: '/docs/components/tooltip',
items: [],
},
],
},
@ -341,6 +374,11 @@ interface Example {
code: string
}
export const examples: Example[] = [
{
name: 'Mail',
href: '/examples/mail',
code: 'https://github.com/radix-vue/shadcn-vue/tree/dev/apps/www/src/examples/mail',
},
{
name: 'Dashboard',
href: '/examples/dashboard',

View File

@ -0,0 +1,41 @@
import type { HighlighterCore } from 'shiki/core'
import type { ThemeOptions } from 'vitepress'
import { computedAsync } from '@vueuse/core'
import { createHighlighterCore } from 'shiki/core'
import { createJavaScriptRegexEngine } from 'shiki/engine/javascript'
export const shikiThemes: ThemeOptions = {
light: 'github-light-default',
dark: 'github-dark-default',
}
export const highlighter = computedAsync<HighlighterCore>(async (onCancel) => {
const shiki = await createHighlighterCore({
engine: createJavaScriptRegexEngine(),
themes: [
() => import('shiki/themes/github-dark-default.mjs'),
() => import('shiki/themes/github-light-default.mjs'),
],
langs: [
() => import('shiki/langs/javascript.mjs'),
() => import('shiki/langs/vue.mjs'),
],
})
onCancel(() => shiki?.dispose())
return shiki
})
export function highlight(code: string, lang: string) {
if (!highlighter.value)
return code
return highlighter.value.codeToHtml(code, {
lang,
defaultColor: false,
themes: {
dark: 'github-dark-default',
light: 'github-light-default',
},
})
}

View File

@ -15,6 +15,6 @@ export const siteConfig = {
export const announcementConfig = {
icon: '✨',
title: 'VSCode extension',
link: '/docs/installation.html#vscode-extension',
title: 'Extended: Auto Form, Charts',
link: '/docs/components/auto-form.html',
}

View File

@ -1,9 +1,8 @@
/* eslint-disable vue/component-definition-name-casing */
// https://vitepress.dev/guide/custom-theme
import Layout from './layout/MainLayout.vue'
import * as components from './components'
import DocsLayout from './layout/DocsLayout.vue'
import ExamplesLayout from './layout/ExamplesLayout.vue'
import * as components from './components'
// https://vitepress.dev/guide/custom-theme
import Layout from './layout/MainLayout.vue'
import './style.css'
import './styles/vp-doc.css'
import './styles/shiki.css'

View File

@ -1,12 +1,12 @@
<script setup lang="ts">
import { useData, useRoute } from 'vitepress'
import { docsConfig } from '../config/docs'
import TableOfContentVue from '../components/TableOfContent.vue'
import EditLink from '../components/EditLink.vue'
import { ScrollArea } from '@/lib/registry/default/ui/scroll-area'
import { Badge } from '@/lib/registry/default/ui/badge'
import RadixIconsCode from '~icons/radix-icons/code'
import ChevronRightIcon from '~icons/lucide/chevron-right'
import RadixIconsExternalLink from '~icons/radix-icons/external-link'
import { useData, useRoute } from 'vitepress'
import DocsBreadcrumb from '../components/DocsBreadcrumb.vue'
import EditLink from '../components/EditLink.vue'
import TableOfContent from '../components/TableOfContent.vue'
import { docsConfig } from '../config/docs'
const $route = useRoute()
const { frontmatter } = useData()
@ -20,13 +20,17 @@ const sourceLink = 'https://github.com/radix-vue/shadcn-vue/tree/dev/'
<aside
class="fixed top-14 z-30 -ml-2 hidden h-[calc(100vh-3.5rem)] w-full shrink-0 md:sticky md:block overflow-y-auto"
>
<ScrollArea orientation="vertical" class="h-full py-6 pl-8 pr-6 lg:py-8" :type="'auto'">
<ScrollArea orientation="vertical" class="relative overflow-hidden h-full py-6 pr-6 lg:py-8" :type="'auto'">
<div class="w-full">
<div v-for="docsGroup in docsConfig.sidebarNav" :key="docsGroup.title" class="pb-4">
<h4
class="mb-1 rounded-md px-2 py-1 text-sm font-semibold"
>
{{ docsGroup.title }}
<span v-if="docsGroup.label" class="ml-2 font-normal rounded-md bg-[#adfa1d] px-1.5 py-0.5 text-xs leading-none text-[#000000] no-underline group-hover:no-underline">
{{ docsGroup.label }}
</span>
</h4>
<div
@ -45,9 +49,9 @@ const sourceLink = 'https://github.com/radix-vue/shadcn-vue/tree/dev/'
>
{{ doc.title }}
<Badge v-if="doc.label" class="ml-2" :variant="'secondary'">
<span v-if="doc.label" class="ml-2 rounded-md bg-[#adfa1d] px-1.5 py-0.5 text-xs leading-none text-[#000000] no-underline group-hover:no-underline">
{{ doc.label }}
</Badge>
</span>
</a>
</div>
</div>
@ -58,29 +62,30 @@ const sourceLink = 'https://github.com/radix-vue/shadcn-vue/tree/dev/'
<main class="relative py-6 lg:gap-10 lg:py-8 xl:grid xl:grid-cols-[1fr_300px]">
<div class="mx-auto w-full min-w-0">
<div class="block xl:hidden">
<TableOfContentVue />
<TableOfContent />
</div>
<div class="mb-4 flex items-center space-x-1 text-sm text-muted-foreground">
<div class="overflow-hidden text-ellipsis whitespace-nowrap">
Docs
</div>
<ChevronRightIcon class="h-4 w-4" />
<div class="font-medium text-foreground">
{{ frontmatter.title }}
</div>
</div>
<DocsBreadcrumb class="mb-4" />
<div class="space-y-2">
<h1 class="scroll-m-20 text-4xl font-bold tracking-tight">
{{ frontmatter.title }}
</h1>
<div class="flex items-center space-x-4">
<h1 class="scroll-m-20 text-4xl font-bold tracking-tight">
{{ frontmatter.title }}
</h1>
<span v-if="frontmatter.label" class="ml-2 rounded-md bg-[#adfa1d] px-1.5 py-0.5 text-xs leading-none text-[#000000] no-underline group-hover:no-underline">
{{ frontmatter.label }}
</span>
</div>
<p class="text-lg text-muted-foreground">
{{ frontmatter.description }}
</p>
</div>
<div class="flex items-center space-x-2 pt-4">
<a v-if="frontmatter.docs" :href="frontmatter.docs" target="_blank" class="inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 select-none border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80">
<RadixIconsExternalLink class="mr-1" />
Docs
</a>
<a v-if="frontmatter.source" :href="sourceLink + frontmatter.source" target="_blank" class="inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 select-none border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80">
<RadixIconsCode class="mr-1" />
Component Source
@ -99,7 +104,7 @@ const sourceLink = 'https://github.com/radix-vue/shadcn-vue/tree/dev/'
<div class="hidden text-sm xl:block">
<div class="sticky top-16 -mt-10 h-[calc(100vh-3.5rem)] overflow-hidden pt-6">
<TableOfContentVue />
<TableOfContent show-carbon-ads />
</div>
</div>
</main>

View File

@ -1,30 +1,19 @@
<script setup lang="ts">
import PageHeader from '../components/PageHeader.vue'
import PageHeaderHeading from '../components/PageHeaderHeading.vue'
import PageHeaderDescription from '../components/PageHeaderDescription.vue'
import ExamplesNav from '../components/ExamplesNav.vue'
import { announcementConfig } from '../config/site'
import ArrowRightIcon from '~icons/radix-icons/arrow-right'
import { buttonVariants } from '@/lib/registry/new-york/ui/button'
import { Separator } from '@/lib/registry/new-york/ui/separator'
import { cn } from '@/lib/utils'
import Announcement from '../components/Announcement.vue'
import ExamplesNav from '../components/ExamplesNav.vue'
import PageAction from '../components/PageAction.vue'
import PageHeader from '../components/PageHeader.vue'
import PageHeaderDescription from '../components/PageHeaderDescription.vue'
import PageHeaderHeading from '../components/PageHeaderHeading.vue'
</script>
<template>
<div class="container relative">
<PageHeader class="page-header pb-8">
<a
:href="announcementConfig.link"
class="inline-flex items-center rounded-lg bg-muted px-3 py-1 text-sm font-medium"
>
{{ announcementConfig.icon }} <Separator class="mx-2 h-4" orientation="vertical" />
<span class="sm:hidden">{{ announcementConfig.title }}</span>
<span class="hidden sm:inline">
{{ announcementConfig.title }}
</span>
<ArrowRightIcon class="ml-1 h-4 w-4" />
</a>
<Announcement />
<PageHeaderHeading class="hidden md:block">
Check out some examples.
</PageHeaderHeading>
@ -36,7 +25,7 @@ import { cn } from '@/lib/utils'
components. Use this as a guide to build your own.
</PageHeaderDescription>
<section class="flex w-full items-center space-x-4 pb-8 pt-4 md:pb-10">
<PageAction>
<a
href="/docs/introduction"
:class="cn(buttonVariants(), 'rounded-[6px]')"
@ -52,7 +41,7 @@ import { cn } from '@/lib/utils'
>
Components
</a>
</section>
</PageAction>
</PageHeader>
<section>
<ExamplesNav />

View File

@ -1,26 +1,29 @@
<script setup lang="ts">
import { useMagicKeys, useToggle } from '@vueuse/core'
import { onMounted, ref, watch } from 'vue'
import { Content, useData, useRoute, useRouter } from 'vitepress'
import { SearchIcon } from 'lucide-vue-next'
import { type NavItem, docsConfig } from '../config/docs'
import Logo from '../components/Logo.vue'
import MobileNav from '../components/MobileNav.vue'
import Kbd from '../components/Kbd.vue'
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator } from '@/lib/registry/default/ui/command'
import { Button } from '@/lib/registry/default/ui/button'
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator } from '@/lib/registry/default/ui/command'
import { Dialog, DialogContent } from '@/lib/registry/default/ui/dialog'
import { Toaster as DefaultToaster } from '@/lib/registry/default/ui/toast'
import { Toaster as NewYorkSonner } from '@/lib/registry/new-york/ui/sonner'
import { Toaster as NewYorkToaster } from '@/lib/registry/new-york/ui/toast'
import { TooltipProvider } from '@/lib/registry/new-york/ui/tooltip'
import { useConfigStore } from '@/stores/config'
import { useMagicKeys, useToggle } from '@vueuse/core'
import Circle from '~icons/radix-icons/circle'
import File from '~icons/radix-icons/file'
import RadixIconsGithubLogo from '~icons/radix-icons/github-logo'
import RadixIconsMoon from '~icons/radix-icons/moon'
import RadixIconsSun from '~icons/radix-icons/sun'
import { useConfigStore } from '@/stores/config'
import { Dialog, DialogContent } from '@/lib/registry/default/ui/dialog'
import { Toaster as DefaultToaster } from '@/lib/registry/default/ui/toast'
import { Toaster as NewYorkToaster } from '@/lib/registry/new-york/ui/toast'
import { Content, useData, useRoute, useRouter } from 'vitepress'
import { onMounted, ref, watch } from 'vue'
import CodeConfigCustomizer from '../components/CodeConfigCustomizer.vue'
import Kbd from '../components/Kbd.vue'
import Logo from '../components/Logo.vue'
import MobileNav from '../components/MobileNav.vue'
import File from '~icons/radix-icons/file'
import Circle from '~icons/radix-icons/circle'
import ThemePopover from '../components/ThemePopover.vue'
import { docsConfig, type NavItem } from '../config/docs'
const { radius, theme } = useConfigStore()
// Whenever the component is mounted, update the document class list
@ -38,7 +41,7 @@ const toggleDark = useToggle(isDark)
const links = [
{
name: 'GitHub',
href: 'https://github.com/radix-vue/shadcn-vue',
href: 'https://github.com/unovue/shadcn-vue',
icon: RadixIconsGithubLogo,
},
// {
@ -49,7 +52,13 @@ const links = [
]
const isOpen = ref(false)
const { Meta_K, Ctrl_K } = useMagicKeys()
const { Meta_K, Ctrl_K } = useMagicKeys({
passive: false,
onEventFired(e) {
if (e.key === 'k' && (e.metaKey || e.ctrlKey))
e.preventDefault()
},
})
watch([Meta_K, Ctrl_K], (v) => {
if (v[0] || v[1])
@ -77,210 +86,225 @@ watch(() => $route.path, (n) => {
</script>
<template>
<div class="flex min-h-screen flex-col bg-background">
<header class="sticky z-40 top-0 bg-background/80 backdrop-blur-lg border-b border-border">
<div
class="container flex justify-between h-14 items-center"
>
<MobileNav />
<TooltipProvider>
<div v-if="$route.data.frontmatter.layout === false">
<Content :key="$route.path" />
</div>
<div v-else vaul-drawer-wrapper class="flex min-h-screen flex-col bg-background">
<header class="sticky z-40 top-0 bg-background/80 backdrop-blur-lg border-b border-border">
<div
class="container flex h-14 max-w-screen-2xl items-center"
>
<div class="mr-4 md:mr-1 hidden md:flex">
<Logo />
<div class="mr-4 hidden md:flex">
<Logo />
<nav
class="flex items-center space-x-6 text-sm font-medium"
>
<a
v-for="route in docsConfig.mainNav"
:key="route.title"
:href="route.href"
:target="route.external ? '_target' : undefined"
class="transition-colors hover:text-foreground/80 text-foreground/60"
:class="{
'font-semibold !text-foreground': $route.path === `${route.href}.html`,
}"
<nav
class="flex items-center max-lg:space-x-4 space-x-6 text-sm font-medium"
>
{{ route.title }}
</a>
</nav>
</div>
<a
v-for="route in docsConfig.mainNav"
:key="route.title"
:href="route.href"
:target="route.external ? '_target' : undefined"
class="transition-colors hover:text-foreground/80 text-foreground/60"
:class="{
'font-semibold !text-foreground': $route.path === `${route.href}.html`,
'hidden lg:block': route?.href?.includes('github'),
}"
>
{{ route.title }}
</a>
</nav>
</div>
<MobileNav />
<div class=" flex items-center justify-end space-x-4 ">
<Button
variant="outline"
class="w-72 h-8 px-3 hidden lg:flex lg:justify-between lg:items-center"
@click="isOpen = true"
>
<div class="flex items-center">
<SearchIcon class="w-4 h-4 mr-2 text-muted-foreground" />
<span class="text-muted-foreground"> Search for anything... </span>
<div class="flex flex-1 items-center justify-between space-x-2 md:justify-end">
<div class="w-full flex-1 md:w-auto md:flex-none">
<Button
variant="outline"
class="relative h-8 w-full justify-start rounded-[0.5rem] bg-background text-sm font-normal text-muted-foreground shadow-none sm:pr-12 md:w-40 lg:w-64"
@click="isOpen = true"
>
<span class="hidden lg:inline-flex">Search documentation...</span>
<span class="inline-flex lg:hidden">Search...</span>
<Kbd class="pointer-events-none absolute right-[0.3rem] top-[0.3rem] hidden h-5 select-none items-center gap-1 rounded border bg-muted px-1.5 font-mono text-[10px] font-medium opacity-100 sm:flex">
<span class="text-xs"></span>K
</Kbd>
</Button>
</div>
<div class="flex items-center gap-x-1">
<Kbd> <span></span>K </Kbd>
</div>
</Button>
<div class="flex items-center gap-x-1">
<Button
v-for="link in links"
:key="link.name"
as="a"
:href="link.href" target="_blank"
:variant="'ghost'" :size="'icon'"
>
<component :is="link.icon" class="w-[20px] h-[20px]" />
</Button>
<nav class="flex items-center">
<ThemePopover />
<Button
class="flex items-center justify-center"
aria-label="Toggle dark mode"
:variant="'ghost'"
:size="'icon'" @click="toggleDark()"
>
<component
:is="isDark ? RadixIconsSun : RadixIconsMoon"
class="w-[20px] h-[20px] text-foreground"
/>
</Button>
<CodeConfigCustomizer />
<Button
v-for="link in links"
:key="link.name"
as="a"
class="w-9 h-9"
:href="link.href" target="_blank"
:variant="'ghost'"
:size="'icon'"
>
<component :is="link.icon" class="w-5 h-5" />
</Button>
<ClientOnly>
<Button
class="w-9 h-9"
aria-label="Toggle dark mode"
:variant="'ghost'"
:size="'icon'"
@click="toggleDark()"
>
<component
:is="isDark ? RadixIconsSun : RadixIconsMoon"
class="w-5 h-5 text-foreground"
/>
</Button>
</ClientOnly>
</nav>
</div>
</div>
</header>
<div class="flex-1 bg-background">
<Transition name="fade" mode="out-in">
<component :is="'docs'" v-if="$route.path.includes('docs')">
<Transition name="fade" mode="out-in">
<Content :key="$route.path" />
</Transition>
</component>
<component :is="'examples'" v-else-if="$route.path.includes('examples')">
<Transition name="fade" mode="out-in">
<Content :key="$route.path" />
</Transition>
</component>
<component :is="frontmatter.layout" v-else-if="frontmatter.layout">
<slot />
</component>
<main v-else class="container">
<Transition name="fade" mode="out-in">
<Content :key="$route.path" />
</Transition>
</main>
</Transition>
</div>
</header>
<div class="flex-1 bg-background">
<Transition name="fade" mode="out-in">
<component :is="'docs'" v-if="$route.path.includes('docs')">
<Transition name="fade" mode="out-in">
<Content :key="$route.path" />
</Transition>
</component>
<component :is="'examples'" v-else-if="$route.path.includes('examples')">
<Transition name="fade" mode="out-in">
<Content :key="$route.path" />
</Transition>
</component>
<component :is="frontmatter.layout" v-else-if="frontmatter.layout">
<slot />
</component>
<main v-else class="container">
<Transition name="fade" mode="out-in">
<Content :key="$route.path" />
</Transition>
</main>
</Transition>
</div>
<footer class="py-6 md:px-8 md:py-0">
<div class="container flex flex-col items-center justify-between gap-4 md:h-24 md:flex-row">
<div class="text-center text-sm leading-loose text-muted-foreground md:text-left">
<span class="inline-block">
Built and designed by
<a
href="https://twitter.com/shadcn"
target="_blank"
class="underline underline-offset-4 font-bold decoration-foreground"
>
shadcn
</a>
</span>
<span class="ml-0.5"> . </span>
<span class="inline-block ml-2">
Ported to Vue by
<a
href="https://github.com/radix-vue"
target="_blank"
class="underline underline-offset-4 font-bold decoration-foreground"
>
Radix Vue
</a>
</span>
<span class="ml-0.5"> . </span>
<span class="inline-block ml-2">
The code source is available on
<a
href="https://github.com/radix-vue/shadcn-vue"
target="_blank"
class="underline underline-offset-4 font-bold decoration-foreground"
>
GitHub
</a>
</span>
<footer class="py-6 md:px-8 md:py-0">
<div class="container flex flex-col items-center justify-between gap-4 md:h-24 md:flex-row">
<div class="text-center text-sm leading-loose text-muted-foreground md:text-left">
<span class="inline-block">
Built and designed by
<a
href="https://twitter.com/shadcn"
target="_blank"
class="underline underline-offset-4 font-bold decoration-foreground"
>
shadcn
</a>
</span>
<span class="ml-0.5"> . </span>
<span class="inline-block ml-2">
Ported to Vue by
<a
href="https://github.com/unovue"
target="_blank"
class="underline underline-offset-4 font-bold decoration-foreground"
>
Radix Vue
</a>
</span>
<span class="ml-0.5"> . </span>
<span class="inline-block ml-2">
The code source is available on
<a
href="https://github.com/unovue/shadcn-vue"
target="_blank"
class="underline underline-offset-4 font-bold decoration-foreground"
>
GitHub
</a>
</span>
</div>
</div>
</div>
</footer>
</footer>
<Dialog v-model:open="isOpen">
<DialogContent class="p-0">
<Command>
<CommandInput placeholder="Type a command or search..." />
<CommandEmpty>
No results found.
</CommandEmpty>
<CommandList
@escape-key-down=" isOpen = false"
>
<CommandGroup heading="Links">
<CommandItem
v-for="item in docsConfig.mainNav"
:key="item.title"
:heading="item.title"
:value="item.title"
class="py-3"
@select="handleSelectLink(item)"
>
<File class="mr-2 h-5 w-5" />
<span>{{ item.title }}</span>
</CommandItem>
</CommandGroup>
<CommandSeparator />
<CommandGroup v-for="item in docsConfig.sidebarNav" :key="item.title" :heading="item.title">
<CommandItem
v-for="subItem in item.items"
:key="subItem.title"
:heading="subItem.title"
:value="subItem.title"
class="py-3"
@select="
handleSelectLink(subItem)"
>
<Circle class="mr-2 h-4 w-4" />
<span>{{ subItem.title }}</span>
</CommandItem>
</CommandGroup>
<CommandSeparator />
<CommandGroup heading="Theme">
<CommandItem
value="light-theme"
class="py-3"
@select="
() => {
isDark = false;
isOpen = false;
}
"
>
<RadixIconsSun class="mr-2 h-5 w-5" />
<span>Light Theme</span>
</CommandItem>
<CommandItem
value="dark-theme"
class="py-3"
@select="
() => {
isDark = true;
isOpen = false;
}
"
>
<RadixIconsMoon class="mr-2 h-5 w-5" />
<span>Dark Theme</span>
</CommandItem>
</CommandGroup>
</CommandList>
</Command>
</DialogContent>
</Dialog>
<DefaultToaster />
<NewYorkToaster />
</div>
<Dialog v-model:open="isOpen">
<DialogContent class="p-0">
<Command>
<CommandInput placeholder="Type a command or search..." />
<CommandEmpty>
No results found.
</CommandEmpty>
<CommandList
@escape-key-down=" isOpen = false"
>
<CommandGroup heading="Links">
<CommandItem
v-for="item in docsConfig.mainNav"
:key="item.title"
:heading="item.title"
:value="item.title"
class="py-3"
@select="handleSelectLink(item)"
>
<File class="mr-2 h-5 w-5" />
<span>{{ item.title }}</span>
</CommandItem>
</CommandGroup>
<CommandSeparator />
<CommandGroup v-for="item in docsConfig.sidebarNav" :key="item.title" :heading="item.title">
<CommandItem
v-for="subItem in item.items"
:key="subItem.title"
:heading="subItem.title"
:value="subItem.title"
class="py-3"
@select="
handleSelectLink(subItem)"
>
<Circle class="mr-2 h-4 w-4" />
<span>{{ subItem.title }}</span>
</CommandItem>
</CommandGroup>
<CommandSeparator />
<CommandGroup heading="Theme">
<CommandItem
value="light-theme"
class="py-3"
@select="
() => {
isDark = false;
isOpen = false;
}
"
>
<RadixIconsSun class="mr-2 h-5 w-5" />
<span>Light Theme</span>
</CommandItem>
<CommandItem
value="dark-theme"
class="py-3"
@select="
() => {
isDark = true;
isOpen = false;
}
"
>
<RadixIconsMoon class="mr-2 h-5 w-5" />
<span>Dark Theme</span>
</CommandItem>
</CommandGroup>
</CommandList>
</Command>
</DialogContent>
</Dialog>
<DefaultToaster />
<NewYorkSonner class="pointer-events-auto" :theme="'system'" />
<NewYorkToaster />
</div>
</TooltipProvider>
</template>

View File

@ -1,35 +1,19 @@
<script setup lang="ts">
import { onMounted, watch } from 'vue'
import { Paintbrush } from 'lucide-vue-next'
import { useData } from 'vitepress'
import PageHeader from '../components/PageHeader.vue'
import PageHeaderHeading from '../components/PageHeaderHeading.vue'
import PageHeaderDescription from '../components/PageHeaderDescription.vue'
import CustomizerCode from '../components/CustomizerCode.vue'
import { RADII, useConfigStore } from '@/stores/config'
import { colors } from '@/lib/registry'
import type { Color } from '../types/colors'
import { Button } from '@/lib/registry/new-york/ui/button'
import { Label } from '@/lib/registry/new-york/ui/label'
import { Popover, PopoverContent, PopoverTrigger } from '@/lib/registry/new-york/ui/popover'
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/lib/registry/new-york/ui/tooltip'
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from '@/lib/registry/new-york/ui/dialog'
import RadixIconsCheck from '~icons/radix-icons/check'
import RadixIconsSun from '~icons/radix-icons/sun'
import RadixIconsMoon from '~icons/radix-icons/moon'
type Color =
| 'zinc'
| 'slate'
| 'stone'
| 'gray'
| 'neutral'
| 'red'
| 'rose'
| 'orange'
| 'green'
| 'blue'
| 'yellow'
| 'violet'
import { Drawer, DrawerContent, DrawerTrigger } from '@/lib/registry/new-york/ui/drawer'
import { Popover, PopoverContent, PopoverTrigger } from '@/lib/registry/new-york/ui/popover'
import { useConfigStore } from '@/stores/config'
import { Paintbrush } from 'lucide-vue-next'
import { onMounted, watch } from 'vue'
import CustomizerCode from '../components/CustomizerCode.vue'
import InlineThemePicker from '../components/InlineThemePicker.vue'
import PageAction from '../components/PageAction.vue'
import PageHeader from '../components/PageHeader.vue'
import PageHeaderDescription from '../components/PageHeaderDescription.vue'
import PageHeaderHeading from '../components/PageHeaderHeading.vue'
import ThemeCustomizer from '../components/ThemeCustomizer.vue'
// Create an array of color values
const allColors: Color[] = [
@ -47,8 +31,7 @@ const allColors: Color[] = [
'violet',
]
const { theme, radius, setRadius, setTheme } = useConfigStore()
const { isDark } = useData()
const { theme, radius } = useConfigStore()
// Whenever the component is mounted, update the document class list
onMounted(() => {
@ -72,173 +55,63 @@ watch(radius, (radius) => {
<template>
<div class="container relative">
<div class="flex justify-between items-center">
<div>
<PageHeader class="page-header pb-8">
<PageHeaderHeading class="hidden md:block">
Make it yours.
</PageHeaderHeading>
<PageHeaderDescription>
Hand-picked themes that you can copy and paste into your apps.
</PageHeaderDescription>
</PageHeader>
</div>
<div class="px-4 pb-8 md:ml-auto md:pb-0">
<div class="flex items-center space-x-2">
<div class="hidden md:flex">
<div class="mr-4 hidden items-center space-x-1 lg:flex">
<TooltipProvider
v-for="(color, index) in allColors.slice(0, 5)"
:key="index"
>
<Tooltip>
<TooltipTrigger as-child>
<button
:key="index"
class="flex h-9 w-9 items-center justify-center rounded-full border-2 border-border text-xs"
:class="
color === theme
? 'border-foreground'
: 'border-transparent'
"
@click="setTheme(color)"
>
<span
class="flex h-6 w-6 items-center justify-center rounded-full"
:style="{ backgroundColor: colors[color][7].rgb }"
>
<RadixIconsCheck
v-if="color === theme"
class="h-4 w-4 text-white"
/>
</span>
</button>
</TooltipTrigger>
<TooltipContent
align="center"
:side-offset="1"
class="capitalize"
>
{{ allColors[index] }}
</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
<Popover>
<PopoverTrigger as-child>
<Button variant="outline" class="h-9 rounded-[0.5rem]">
<Paintbrush class="w-4 h-4 mr-2" />
Customize
</Button>
</PopoverTrigger>
<PopoverContent :side-offset="8" align="end" class="w-96">
<div class="p-4">
<div class="grid space-y-1">
<h1 class="text-md text-foreground font-semibold">
Customize
</h1>
<p class="text-xs text-muted-foreground">
Pick a style and color for your components.
</p>
</div>
<div class="space-y-1.5 pt-6">
<Label for="color" class="text-xs"> Color </Label>
<div class="grid grid-cols-3 gap-2 py-1.5">
<Button
v-for="(color, index) in allColors"
:key="index"
variant="outline"
class="h-8 justify-start px-3"
:class="
color === theme
? 'border-foreground border-2'
: ''
"
@click="setTheme(color)"
>
<span
class="h-5 w-5 rounded-full flex items-center justify-center"
:style="{ backgroundColor: colors[color][7].rgb }"
>
<RadixIconsCheck
v-if="color === theme"
class="h-3 w-3 text-white"
/>
</span>
<span class="ml-2 text-xs capitalize">
{{ color }}
</span>
</Button>
</div>
</div>
<div class="space-y-1.5 pt-6">
<Label for="radius" class="text-xs"> Radius </Label>
<div class="grid grid-cols-5 gap-2 py-1.5">
<Button
v-for="(r, index) in RADII"
:key="index"
variant="outline"
class="h-8 justify-center px-3"
:class="
r === radius
? 'border-foreground border-2'
: ''
"
@click="setRadius(r)"
>
<span class="text-xs">
{{ r }}
</span>
</Button>
</div>
</div>
<div class="space-y-1.5 pt-6">
<Label for="theme" class="text-xs"> Theme </Label>
<PageHeader>
<PageHeaderHeading class="hidden md:block">
Add colors. Make it yours.
</PageHeaderHeading>
<PageHeaderHeading class="md:hidden">
Make it yours
</PageHeaderHeading>
<PageHeaderDescription>
Hand-picked themes that you can copy and paste into your apps.
</PageHeaderDescription>
<PageAction>
<InlineThemePicker class="gap-x-1 me-4 hidden lg:flex" :all-colors="allColors" />
<Drawer>
<DrawerTrigger as-child>
<Button variant="outline" class="md:hidden h-9 rounded-[0.5rem]">
<Paintbrush class="w-4 h-4 mr-2" />
Customize
</Button>
</DrawerTrigger>
<DrawerContent class="p-6 pt-0">
<ThemeCustomizer :all-colors="allColors" />
</DrawerContent>
</Drawer>
<Popover>
<PopoverTrigger as-child>
<Button variant="outline" class="hidden md:flex h-9 rounded-[0.5rem]">
<Paintbrush class="w-4 h-4 mr-2" />
Customize
</Button>
</PopoverTrigger>
<PopoverContent :side-offset="8" align="end" class="w-96">
<ThemeCustomizer :all-colors="allColors" />
</PopoverContent>
</Popover>
<Dialog>
<DialogTrigger as-child>
<Button class="h-9 ml-2 rounded-[0.5rem]">
Copy code
</Button>
</DialogTrigger>
<DialogContent class="sm:max-w-[625px]">
<DialogHeader>
<DialogTitle>Theme</DialogTitle>
<DialogDescription>
Copy and paste the following code into your CSS file.
</DialogDescription>
</DialogHeader>
<CustomizerCode />
</DialogContent>
</Dialog>
</PageAction>
</PageHeader>
<div class="flex space-x-2 py-1.5">
<Button
class="h-8"
variant="outline"
:class="{ 'border-2 border-foreground': !isDark }"
@click="isDark = false"
>
<RadixIconsSun class="w-4 h-4 mr-2" />
<span class="text-xs">Light</span>
</Button>
<Button
class="h-8"
variant="outline"
:class="{ 'border-2 border-foreground': isDark }"
@click="isDark = true"
>
<RadixIconsMoon class="w-4 h-4 mr-2" />
<span class="text-xs">Dark</span>
</Button>
</div>
</div>
</div>
</PopoverContent>
</Popover>
<Dialog>
<DialogTrigger as-child>
<Button class="h-9 ml-2 rounded-[0.5rem]">
Copy code
</Button>
</DialogTrigger>
<DialogContent class="sm:max-w-[625px]">
<DialogHeader>
<DialogTitle>Theme</DialogTitle>
<DialogDescription>
Copy and paste the following code into your CSS file.
</DialogDescription>
</DialogHeader>
<CustomizerCode />
</DialogContent>
</Dialog>
</div>
</div>
</div>
</div>
<section>
<slot />
</section>

View File

@ -0,0 +1,20 @@
import type { MarkdownRenderer } from 'vitepress'
export default function (md: MarkdownRenderer) {
const defaultFenceRenderer = md.renderer.rules.fence
if (!defaultFenceRenderer)
return
md.renderer.rules.fence = function (tokens, idx, options, env, self) {
// Check if this is a code block
const token = tokens[idx]
const isAllowedExtension = (token.info.includes('vue') || token.info.includes('astro') || token.info.includes('ts'))
if (token && token.tag === 'code' && isAllowedExtension) {
// Wrap the code block in CodeWrapper
return `<CodeWrapper>${defaultFenceRenderer(tokens, idx, options, env, self)}</CodeWrapper>`
}
// If not a code block, return the default rendering
return defaultFenceRenderer(tokens, idx, options, env, self)
}
}

View File

@ -1,7 +1,5 @@
import { dirname, resolve } from 'node:path'
import fs from 'node:fs'
import type { MarkdownEnv, MarkdownRenderer } from 'vitepress'
import { generateDemoComponent, parseProps } from './utils'
import type { MarkdownRenderer } from 'vitepress'
import { parseProps } from './utils'
export default function (md: MarkdownRenderer) {
function addRenderRule(type: string) {
@ -12,31 +10,9 @@ export default function (md: MarkdownRenderer) {
if (!content.match(/^<ComponentPreview\s/) || !content.endsWith('/>'))
return defaultRender!(tokens, idx, options, env, self)
const { path } = env as MarkdownEnv
const props = parseProps(content)
const { name, attrs } = props
const pluginPath = dirname(__dirname)
const srcPath = resolve(pluginPath, '../../src/lib/registry/default/example/', `${name}.vue`).replace(/\\/g, '/')
if (!fs.existsSync(srcPath)) {
console.error(`rendering ${path}: ${srcPath} does not exist`)
return defaultRender!(tokens, idx, options, env, self)
}
let code = fs.readFileSync(srcPath, 'utf-8')
code = code.replaceAll(
'@/lib/registry/default/',
'@/components/',
)
const demoScripts = generateDemoComponent(md, env, {
attrs,
props,
code,
path: srcPath,
})
const { attrs } = props
const demoScripts = `<ComponentPreview ${attrs ?? ''} v-bind='${JSON.stringify(props)}'></ComponentPreview>`.trim()
return demoScripts
}
}

View File

@ -1,8 +1,7 @@
// Credit to @hairyf https://github.com/hairyf/markdown-it-vitepress-demo
import type { MarkdownEnv, MarkdownRenderer } from 'vitepress'
import { baseParse } from '@vue/compiler-core'
import type { AttributeNode, ElementNode } from '@vue/compiler-core'
import { baseParse } from '@vue/compiler-core'
export interface GenerateOptions {
attrs?: string
@ -11,36 +10,6 @@ export interface GenerateOptions {
code: string
}
export function parse(
md: MarkdownRenderer,
env: MarkdownEnv,
{ code, attrs: _attrs, props }: GenerateOptions,
) {
const highlightedHtml = md.options.highlight!(code, 'vue', _attrs || '')
const sfcTsHtml = highlightedHtml
const attrs
= `sfcTsCode="${encodeURIComponent(code)}"\n`
+ `sfcTsHtml="${encodeURIComponent(sfcTsHtml)}"\n`
+ `v-bind='${JSON.stringify(props)}'\n`
return {
attrs,
highlightedHtml,
sfcTsCode: code,
sfcTsHtml,
}
}
export function generateDemoComponent(
md: MarkdownRenderer,
env: MarkdownEnv,
options: GenerateOptions,
) {
const { attrs } = parse(md, env, options)
return `<ComponentPreview \n${attrs}></ComponentPreview>`.trim()
}
export function isUndefined(v: any): v is undefined {
return v === undefined || v === null
}

View File

@ -1,10 +1,12 @@
@import url("https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700;800;900&display=swap");
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
:root {
--font-geist-sans: "geist-sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
--background: 0 0% 100%;
--foreground: 240 10% 3.9%;
--card: 0 0% 100%;
@ -19,39 +21,75 @@
--muted-foreground: 240 3.8% 46.1%;
--accent: 240 4.8% 95.9%;
--accent-foreground: 240 5.9% 10%;
--destructive: 0 84.2% 60.2%;
--destructive: 0 72.22% 50.59%;
--destructive-foreground: 0 0% 98%;
--border: 240 5.9% 90%;
--input: 240 5.9% 90%;
--ring: 240 5% 64.9%;
--radius: 0.5rem;
}
.dark {
--background: 240 10% 3.9%;
--foreground: 0 0% 98%;
--card: 240 10% 3.9%;
--card-foreground: 0 0% 98%;
--popover: 240 10% 3.9%;
--popover-foreground: 0 0% 98%;
--primary: 0 0% 98%;
--primary-foreground: 240 5.9% 10%;
--secondary: 240 3.7% 15.9%;
--secondary-foreground: 0 0% 98%;
--muted: 240 3.7% 15.9%;
--muted-foreground: 240 5% 64.9%;
--accent: 240 3.7% 15.9%;
--accent-foreground: 0 0% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 0 85.7% 97.3%;
--border: 240 3.7% 15.9%;
--input: 240 3.7% 15.9%;
--ring: 240 4.9% 83.9%;
}
--sidebar-background: 0 0% 98%;
--sidebar-foreground: 240 5.3% 26.1%;
--sidebar-primary: 240 5.9% 10%;
--sidebar-primary-foreground: 0 0% 98%;
--sidebar-accent: 240 4.8% 95.9%;
--sidebar-accent-foreground: 240 5.9% 10%;
--sidebar-border: 220 13% 91%;
--sidebar-ring: 217.2 91.2% 59.8%;
--vis-primary-color: var(--primary);
--vis-secondary-color: 160 81% 40%;
--vis-text-color: var(--muted-foreground);
--vis-font-family: inherit !important;
--vis-area-stroke-width: 2px !important;
--vis-donut-central-label-text-color: hsl(var(--muted-foreground)) !important;
--vis-tooltip-background-color: none !important;
--vis-tooltip-border-color: none !important;
--vis-tooltip-text-color: none !important;
--vis-tooltip-shadow-color: none !important;
--vis-tooltip-backdrop-filter: none !important;
--vis-tooltip-padding: none !important;
}
.dark {
--background: 240 10% 3.9%;
--foreground: 0 0% 98%;
--card: 240 10% 3.9%;
--card-foreground: 0 0% 98%;
--popover: 240 10% 3.9%;
--popover-foreground: 0 0% 98%;
--primary: 0 0% 98%;
--primary-foreground: 240 5.9% 10%;
--secondary: 240 3.7% 15.9%;
--secondary-foreground: 0 0% 98%;
--muted: 240 3.7% 15.9%;
--muted-foreground: 240 5% 64.9%;
--accent: 240 3.7% 15.9%;
--accent-foreground: 0 0% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 0 85.7% 97.3%;
--border: 240 3.7% 15.9%;
--input: 240 3.7% 15.9%;
--ring: 240 4.9% 83.9%;
--sidebar-background: 240 5.9% 10%;
--sidebar-foreground: 240 4.8% 95.9%;
--sidebar-primary: 224.3 76.3% 48%;
--sidebar-primary-foreground: 0 0% 100%;
--sidebar-accent: 240 3.7% 15.9%;
--sidebar-accent-foreground: 240 4.8% 95.9%;
--sidebar-border: 240 3.7% 15.9%;
--sidebar-ring: 217.2 91.2% 59.8%;
}
* {
@apply border-border;
scrollbar-width: thin;
scrollbar-color: hsl(var(--border)) transparent;
}
html {
-webkit-text-size-adjust: 100%;
@ -59,7 +97,9 @@
}
body {
@apply bg-background text-foreground min-h-screen antialiased font-sans;
font-feature-settings: "rlig" 1, "calt" 1;
/* font-feature-settings: "rlig" 1, "calt" 1; */
font-synthesis-weight: none;
text-rendering: optimizeLegibility;
}
/* Mobile tap highlight */
@ -68,19 +108,16 @@
-webkit-tap-highlight-color: rgba(128, 128, 128, 0.5);
}
/* === Scrollbars === */
/* Font face Geist font */
::-webkit-scrollbar {
@apply w-2;
@apply h-2;
@font-face {
font-family: "geist-sans";
font-style: normal;
font-weight: 100 900;
font-display: swap;
src: url("/fonts/Geist/GeistVariableVF.woff2") format("woff2");
}
::-webkit-scrollbar-track {
@apply !bg-muted;
}
::-webkit-scrollbar-thumb {
@apply rounded-sm !bg-muted-foreground/30;
}
/* Firefox */
/* https://developer.mozilla.org/en-US/docs/Web/CSS/scrollbar-color#browser_compatibility */
@ -92,14 +129,14 @@
scrollbar-color: hsl(215.4 16.3% 56.9% / 0.3);
}
.hide-scrollbar::-webkit-scrollbar {
display: none;
}
.hide-scrollbar {
-ms-overflow-style: none;
scrollbar-width: none;
}
html.dark .shiki,
html.dark .shiki span {
color: var(--shiki-dark);
}
html:not(.dark) .shiki,
html:not(.dark) .shiki span {
color: var(--shiki-light);
}
.antialised {
-webkit-font-smoothing: antialiased;
@ -116,9 +153,9 @@
.step:before {
@apply absolute w-9 h-9 bg-muted rounded-full font-mono font-medium text-center text-base inline-flex items-center justify-center -indent-px border-4 border-background;
@apply ml-[-50px] mt-[-4px];
@apply -ml-[50px] -mt-1;
content: counter(step);
}
}
}
@media (max-width: 640px) {
@ -128,25 +165,33 @@
}
div[class^="language-"] {
@apply mb-4 mt-6 max-h-[650px] overflow-x-auto md:rounded-lg border !bg-zinc-950 dark:!bg-zinc-900
@apply mb-4 mt-6 max-h-[650px] overflow-x-auto md:rounded-lg border
}
pre {
@apply py-4;
}
pre code {
@apply relative font-mono text-sm ;
@apply relative font-mono text-sm ;
}
.line-numbers-wrapper, code {
--vp-code-line-height: 1.7;
}
.line-numbers-wrapper {
@apply font-mono;
}
pre code .line {
@apply px-4 min-h-[1.5rem] !py-0.5 w-full inline-block;
}
@apply px-4 min-h-4 !py-0.5 w-full inline-block leading-[--vp-code-line-height];
}
.line-number {
@apply min-h-[1.375rem] !text-sm !inline-block text-muted-foreground;
@apply !text-[.75rem] !inline-block text-muted-foreground leading-[--vp-code-line-height];
}
::view-transition-old(root),
::view-transition-new(root) {
animation-duration: 0.3s;
}
}

View File

@ -1,5 +1,5 @@
:root {
--shiki-color-text: #EEEEEE;
--shiki-foreground: #FFF8;
--shiki-color-background: #ffffff;
--shiki-token-constant: #ffffff;
--shiki-token-string: #ffffff88;
@ -7,7 +7,14 @@
--shiki-token-keyword: #ffffff88;
--shiki-token-parameter: #AA0000;
--shiki-token-function: #ffffff;
--shiki-token-string-expression: #ffffff88;
--shiki-token-string-expression: #ebebeb;
--shiki-token-punctuation: #ffffff;
--shiki-token-link: #EE0000;
}
.shiki .highlighted-word {
border-radius: calc(var(--radius) - 2px);
border-color: rgba(63,63,70,.7);
background-color: rgba(63,63,70,.5);
padding: 0.25rem;
}

View File

@ -3,6 +3,7 @@
--vp-icon-copied: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' height='20' width='20' stroke='rgba(128,128,128,1)' stroke-width='2' viewBox='0 0 24 24'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M9 5H7a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-2M9 5a2 2 0 0 0 2 2h2a2 2 0 0 0 2-2M9 5a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2m-6 9 2 2 4-4'/%3E%3C/svg%3E");
--vp-code-bg: hsl(var(--muted));
--vp-c-divider: hsl(var(--muted));
--vp-code-block-color: #fff
}
@ -344,13 +345,13 @@
}
.vp-doc [class*='language-'] code .highlighted {
background-color: hsl(240 3.7% 15.9%);
transition: background-color 0.5s;
/* margin: 0 -24px;
padding: 0 24px; */
width: calc(100% + 2 * 24px);
display: inline-block;
}
@apply bg-[hsl(var(--muted))] dark:bg-[hsl(var(--muted))]
}
.vp-doc [class*='language-'] code .highlighted.error {
background-color: var(--vp-code-line-error-color);
@ -414,7 +415,7 @@
.vp-doc div[class*='language-'].line-numbers-mode {
/*rtl:ignore*/
padding-left: 32px;
padding-left: 0px;
}
.vp-doc .line-numbers-wrapper {
@ -566,4 +567,12 @@
.vp-external-link-icon::after {
content: '';
}
.vp-doc [class*=language-] code {
color: var(--vp-code-block-color);
}
.line-numbers-mode pre code .line {
padding-left: 3rem !important;
}

View File

@ -0,0 +1,13 @@
export type Color =
| 'zinc'
| 'slate'
| 'stone'
| 'gray'
| 'neutral'
| 'red'
| 'rose'
| 'orange'
| 'green'
| 'blue'
| 'yellow'
| 'violet'

View File

@ -1,10 +1,11 @@
import { getParameters } from 'codesandbox/lib/api/define'
import type { Style } from '@/lib/registry/styles'
import sdk from '@stackblitz/sdk'
import { dependencies as deps } from '../../../package.json'
import { getParameters } from 'codesandbox/lib/api/define'
import { Index as demoIndex } from '../../../../www/__registry__'
// @ts-expect-error ?raw
import tailwindConfigRaw from '../../../tailwind.config?raw'
// @ts-expect-error ?raw
import cssRaw from '../../../../../packages/cli/test/fixtures/nuxt/assets/css/tailwind.css?raw'
import { type Style } from '@/lib/registry/styles'
export function makeCodeSandboxParams(componentName: string, style: Style, sources: Record<string, string>) {
let files: Record<string, any> = {}
@ -18,6 +19,7 @@ export function makeCodeSandboxParams(componentName: string, style: Style, sourc
export function makeStackblitzParams(componentName: string, style: Style, sources: Record<string, string>) {
const files: Record<string, string> = {}
Object.entries(constructFiles(componentName, style, sources)).forEach(([k, v]) => (files[`${k}`] = typeof v.content === 'object' ? JSON.stringify(v.content, null, 2) : v.content))
return sdk.openProject({
title: `${componentName} - Radix Vue`,
files,
@ -34,7 +36,15 @@ const viteConfig = {
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import tailwind from 'tailwindcss';
import autoprefixer from 'autoprefixer';
export default defineConfig({
css: {
postcss: {
plugins: [tailwind(), autoprefixer()],
},
},
plugins: [vue()],
resolve: {
alias: {
@ -54,7 +64,7 @@ export default defineConfig({
<title>Vite + Vue + TS</title>
</head>
<body>
<div id="app"></div>
<div vaul-drawer-wrapper id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
@ -81,7 +91,7 @@ function constructFiles(componentName: string, style: Style, sources: Record<str
const iconPackage = style === 'default' ? 'lucide-vue-next' : '@radix-icons/vue'
const dependencies = {
'vue': 'latest',
'radix-vue': deps['radix-vue'],
'radix-vue': 'latest',
'@radix-ui/colors': 'latest',
'clsx': 'latest',
'class-variance-authority': 'latest',
@ -90,6 +100,10 @@ function constructFiles(componentName: string, style: Style, sources: Record<str
[iconPackage]: 'latest',
'shadcn-vue': 'latest',
'typescript': 'latest',
'vaul-vue': 'latest',
'vue-sonner': 'latest',
'@unovis/vue': 'latest',
'@unovis/ts': 'latest',
}
const devDependencies = {
@ -97,10 +111,10 @@ function constructFiles(componentName: string, style: Style, sources: Record<str
'@vitejs/plugin-vue': 'latest',
'vue-tsc': 'latest',
'tailwindcss': 'latest',
'postcss': 'latest',
'autoprefixer': 'latest',
}
// We have static replace here as this is only showing for code reproduction, doesn't need dynamic codeConfig
const transformImportPath = (code: string) => {
let parsed = code
parsed = parsed.replaceAll(`@/lib/registry/${style}`, '@/components')
@ -117,7 +131,7 @@ function constructFiles(componentName: string, style: Style, sources: Record<str
}
})
// @ts-expect-error componentName migth not exist in Index
// @ts-expect-error componentName might not exist in Index
const registryDependencies = demoIndex[style][componentName as any]?.registryDependencies?.filter(i => i !== 'utils')
const files = {
@ -139,15 +153,6 @@ function constructFiles(componentName: string, style: Style, sources: Record<str
content: tailwindConfigRaw,
isBinary: false,
},
'postcss.config.js': {
content: `module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
}
}`,
isBinary: false,
},
'tsconfig.json': {
content: `{
"$schema": "https://json.schemastore.org/tsconfig",
@ -164,7 +169,6 @@ function constructFiles(componentName: string, style: Style, sources: Record<str
isBinary: false,
content: `import { type ClassValue, clsx } from 'clsx'
import { twMerge } from 'tailwind-merge'
import { camelize, getCurrentInstance, toHandlerKey } from 'vue'
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
{
"name": "www",
"type": "module",
"version": "0.8.4",
"version": "0.11.3",
"files": [
"dist"
],
@ -9,56 +9,73 @@
"dev": "vitepress dev",
"build": "vitepress build",
"preview": "vitepress preview",
"typecheck": "vue-tsc --noEmit",
"typecheck:registry": "vue-tsc --noEmit -p tsconfig.registry.json",
"build:registry": "pnpm typecheck:registry && tsx ./scripts/build-registry.ts"
"typecheck": "vue-tsc",
"typecheck:registry": "vue-tsc -p tsconfig.registry.json",
"build:registry": "tsx ./scripts/build-registry.ts",
"build:registry-strict": "pnpm typecheck:registry && tsx ./scripts/build-registry.ts",
"docs:gen": "tsx ./scripts/autogen.ts"
},
"dependencies": {
"@formkit/auto-animate": "^0.8.0",
"@morev/vue-transitions": "^2.3.6",
"@formkit/auto-animate": "^0.8.2",
"@internationalized/date": "^3.5.5",
"@radix-icons/vue": "^1.0.0",
"@stackblitz/sdk": "^1.9.0",
"@tanstack/vue-table": "^8.10.7",
"@unovis/ts": "^1.2.3",
"@unovis/vue": "1.3.0-beta.3",
"@vee-validate/zod": "^4.11.8",
"@vueuse/core": "^10.5.0",
"@stackblitz/sdk": "^1.11.0",
"@tanstack/vue-table": "^8.20.5",
"@unovis/ts": "^1.4.4",
"@unovis/vue": "^1.4.4",
"@vee-validate/zod": "^4.13.2",
"@vueuse/core": "^11.1.0",
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
"clsx": "^2.1.1",
"codesandbox": "^2.2.3",
"date-fns": "^2.30.0",
"lucide-vue-next": "^0.276.0",
"radix-vue": "^1.2.3",
"date-fns": "^4.1.0",
"embla-carousel-autoplay": "^8.3.0",
"embla-carousel-vue": "^8.3.0",
"lucide-vue-next": "^0.441.0",
"magic-string": "^0.30.11",
"radix-vue": "catalog:",
"tailwindcss-animate": "^1.0.7",
"v-calendar": "^3.1.2",
"vee-validate": "4.11.8",
"vue": "^3.3.7",
"vue-wrap-balancer": "^1.1.3",
"zod": "^3.22.4"
"vaul-vue": "^0.2.0",
"vee-validate": "4.13.2",
"vue": "^3.5.6",
"vue-sonner": "^1.1.5",
"vue-wrap-balancer": "^1.2.1",
"zod": "catalog:"
},
"devDependencies": {
"@iconify-json/radix-icons": "^1.1.11",
"@iconify-json/tabler": "^1.1.89",
"@iconify/json": "^2.2.108",
"@iconify/vue": "^4.1.1",
"@types/lodash.template": "^4.5.2",
"@types/node": "^20.8.10",
"@vitejs/plugin-vue": "^4.4.0",
"@vitejs/plugin-vue-jsx": "^3.0.2",
"@vue/compiler-core": "^3.3.7",
"@vue/compiler-dom": "^3.3.7",
"@vue/tsconfig": "^0.4.0",
"autoprefixer": "^10.4.16",
"lodash.template": "^4.5.0",
"pathe": "^1.1.1",
"rimraf": "^5.0.5",
"tailwind-merge": "^2.0.0",
"tailwindcss": "^3.3.5",
"tsx": "^3.14.0",
"typescript": "^5.2.2",
"unplugin-icons": "^0.17.1",
"vite": "^4.5.0",
"vitepress": "^1.0.0-rc.24",
"vue-tsc": "^1.8.22"
"@babel/traverse": "^7.25.6",
"@iconify-json/gravity-ui": "^1.1.3",
"@iconify-json/lucide": "^1.1.198",
"@iconify-json/ph": "^1.1.13",
"@iconify-json/radix-icons": "^1.1.14",
"@iconify-json/ri": "^1.1.21",
"@iconify-json/simple-icons": "^1.1.108",
"@iconify-json/tabler": "^1.1.116",
"@iconify/vue": "^4.1.2",
"@oxc-parser/wasm": "catalog:",
"@shikijs/transformers": "^1.17.7",
"@types/lodash-es": "^4.17.12",
"@types/node": "^22.5.5",
"@vitejs/plugin-vue": "^5.1.4",
"@vitejs/plugin-vue-jsx": "^4.0.1",
"@vue/compiler-core": "^3.5.6",
"@vue/compiler-dom": "^3.5.6",
"@vue/tsconfig": "^0.5.1",
"autoprefixer": "^10.4.20",
"fast-glob": "^3.3.2",
"lodash-es": "^4.17.21",
"markdown-it": "^14.1.0",
"pathe": "^1.1.2",
"rimraf": "^6.0.1",
"shiki": "^1.22.1",
"tailwind-merge": "^2.5.2",
"tailwindcss": "^3.4.12",
"tsx": "^4.19.1",
"typescript": "catalog:",
"unplugin-icons": "^0.19.3",
"vitepress": "^1.3.4",
"vue-component-meta": "^2.1.6",
"vue-tsc": "^2.1.6"
}
}

150
apps/www/scripts/autogen.ts Normal file
View File

@ -0,0 +1,150 @@
import type { ComponentMeta, MetaCheckerOptions, PropertyMeta, PropertyMetaSchema } from 'vue-component-meta'
import { existsSync, mkdirSync, writeFileSync } from 'node:fs'
import { join, parse, resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
import fg from 'fast-glob'
import MarkdownIt from 'markdown-it'
import { createComponentMetaChecker } from 'vue-component-meta'
const __dirname = fileURLToPath(new URL('.', import.meta.url))
const md = new MarkdownIt()
const ROOTPATH = '../'
const OUTPUTPATH = '../src/content/meta'
const checkerOptions: MetaCheckerOptions = {
forceUseTs: true,
printer: { newLine: 1 },
}
const tsconfigChecker = createComponentMetaChecker(
resolve(__dirname, ROOTPATH, 'tsconfig.registry.json'),
checkerOptions,
)
const components = fg.sync(['chart/**/*.vue', 'chart*/**/*.vue'], {
cwd: resolve(__dirname, ROOTPATH, 'src/lib/registry/default/ui/'),
absolute: true,
})
components.forEach((componentPath) => {
try {
const componentName = parse(componentPath).name
const meta = parseMeta(tsconfigChecker.getComponentMeta(componentPath))
const metaDirPath = resolve(__dirname, OUTPUTPATH)
// if meta dir doesn't exist create
if (!existsSync(metaDirPath))
mkdirSync(metaDirPath)
const metaMdFilePath = join(metaDirPath, `${componentName}.md`)
let parsedString = '<!-- This file was automatic generated. Do not edit it manually -->\n\n'
if (meta.props.length)
parsedString += `<APITable :type="'prop'" :data="${JSON.stringify(meta.props, null, 2).replace(/"/g, '\'')}" />\n`
if (meta.events.length)
parsedString += `\n<APITable :type="'emit'" :data="${JSON.stringify(meta.events, null, 2).replace(/"/g, '\'')}" />\n`
if (meta.slots.length)
parsedString += `\n<APITable :type="'slot'" :data="${JSON.stringify(meta.slots, null, 2).replace(/"/g, '\'')}" />\n`
if (meta.methods.length)
parsedString += `\n<APITable :type="'method'" :data="${JSON.stringify(meta.methods, null, 2).replace(/"/g, '\'')}" />\n`
writeFileSync(metaMdFilePath, parsedString)
}
catch (err) {
console.log(err)
}
})
function parseTypeFromSchema(schema: PropertyMetaSchema): string {
if (typeof schema === 'object' && (schema.kind === 'enum' || schema.kind === 'array')) {
const isFlatEnum = schema.schema?.every(val => typeof val === 'string')
const enumValue = schema?.schema?.filter(i => i !== 'undefined') ?? []
if (isFlatEnum && /^[A-Z]/.test(schema.type))
return enumValue.join(' | ')
else if (typeof schema.schema?.[0] === 'object' && schema.schema?.[0].kind === 'enum')
return schema.schema.map((s: PropertyMetaSchema) => parseTypeFromSchema(s)).join(' | ')
else
return schema.type
}
else if (typeof schema === 'object' && schema.kind === 'object') {
return schema.type
}
else if (typeof schema === 'string') {
return schema
}
else {
return ''
}
}
// Utilities
function parseMeta(meta: ComponentMeta) {
const props = meta.props
// Exclude global props
.filter(prop => !prop.global)
.map((prop) => {
let defaultValue = prop.default
const { name, description, required } = prop
if (name === 'as')
defaultValue = defaultValue ?? '"div"'
if (defaultValue === 'undefined')
defaultValue = undefined
return ({
name,
description: md.render(description),
type: prop.type.replace(/\s*\|\s*undefined/g, ''),
required,
default: defaultValue ?? undefined,
})
})
const events = meta.events
.map((event) => {
const { name, type } = event
return ({
name,
type: type.replace(/\s*\|\s*undefined/g, ''),
})
})
const defaultSlot = meta.slots?.[0]
const slots: { name: string, description: string, type: string }[] = []
if (defaultSlot && defaultSlot.type !== '{}') {
const schema = defaultSlot.schema
if (typeof schema === 'object' && schema.schema) {
Object.values(schema.schema).forEach((childMeta: PropertyMeta) => {
slots.push({
name: childMeta.name,
description: md.render(childMeta.description),
type: parseTypeFromSchema(childMeta.schema),
})
})
}
}
// exposed method
const methods = meta.exposed
.filter(expose => typeof expose.schema === 'object' && expose.schema.kind === 'event')
.map(expose => ({
name: expose.name,
description: md.render(expose.description),
type: expose.type,
}))
return {
props,
events,
slots,
methods,
}
}

View File

@ -1,13 +1,13 @@
import fs from 'node:fs'
import path, { basename } from 'node:path'
import template from 'lodash.template'
import { template } from 'lodash-es'
import { rimraf } from 'rimraf'
import { colorMapping, colors } from '../src/lib/registry/colors'
import { buildRegistry } from '../src/lib/registry/registry'
import { registrySchema } from '../src/lib/registry/schema'
import { styles } from '../src/lib/registry/styles'
import { themes } from '../src/lib/registry/themes'
import { buildRegistry } from '../src/lib/registry/registry'
const REGISTRY_PATH = path.join(process.cwd(), 'src/public/registry')
@ -40,7 +40,7 @@ for (const style of styles) {
file => `../src/lib/registry/${style.name}/${file}`,
)
const type = item.type.split(':')[1]
// const type = item.type.split(':')[1]
index += `
"${item.name}": {
name: "${item.name}",
@ -66,6 +66,8 @@ fs.writeFileSync(path.join(process.cwd(), '__registry__/index.ts'), index)
// ----------------------------------------------------------------------------
// Build registry/styles/[style]/[name].json.
// ----------------------------------------------------------------------------
const newLine = '\n'
for (const style of styles) {
const targetPath = path.join(REGISTRY_PATH, 'styles', style.name)
@ -78,10 +80,20 @@ for (const style of styles) {
continue
const files = item.files?.map((file) => {
const content = fs.readFileSync(
path.join(process.cwd(), 'src/lib/registry', style.name, file),
'utf8',
)
let content: string = ''
try {
content = fs.readFileSync(
path.join(process.cwd(), 'src/lib/registry', style.name, file),
'utf8',
)
}
catch (err) {
console.log(`'${file}' is missing`)
}
// Replace Windows-style newlines with Unix-style newlines
content = content.replace(/\r\n/g, newLine)
return {
name: basename(file),
@ -94,9 +106,11 @@ for (const style of styles) {
files,
}
const payloadStr = `${JSON.stringify(payload, null, 2).replace(/\r\n/g, newLine)}\n`
fs.writeFileSync(
path.join(targetPath, `${item.name}.json`),
JSON.stringify(payload, null, 2),
payloadStr,
'utf8',
)
}
@ -188,73 +202,73 @@ export const BASE_STYLES = `@tailwind base;
export const BASE_STYLES_WITH_VARIABLES = `@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
:root {
--background: <%- colors.light["background"] %>;
--foreground: <%- colors.light["foreground"] %>;
--muted: <%- colors.light["muted"] %>;
--muted-foreground: <%- colors.light["muted-foreground"] %>;
--popover: <%- colors.light["popover"] %>;
--popover-foreground: <%- colors.light["popover-foreground"] %>;
--card: <%- colors.light["card"] %>;
--card-foreground: <%- colors.light["card-foreground"] %>;
--border: <%- colors.light["border"] %>;
--input: <%- colors.light["input"] %>;
--primary: <%- colors.light["primary"] %>;
--primary-foreground: <%- colors.light["primary-foreground"] %>;
--secondary: <%- colors.light["secondary"] %>;
--secondary-foreground: <%- colors.light["secondary-foreground"] %>;
--accent: <%- colors.light["accent"] %>;
--accent-foreground: <%- colors.light["accent-foreground"] %>;
--destructive: <%- colors.light["destructive"] %>;
--destructive-foreground: <%- colors.light["destructive-foreground"] %>;
--ring: <%- colors.light["ring"] %>;
--radius: 0.5rem;
}
.dark {
--background: <%- colors.dark["background"] %>;
--foreground: <%- colors.dark["foreground"] %>;
--muted: <%- colors.dark["muted"] %>;
--muted-foreground: <%- colors.dark["muted-foreground"] %>;
--popover: <%- colors.dark["popover"] %>;
--popover-foreground: <%- colors.dark["popover-foreground"] %>;
--card: <%- colors.dark["card"] %>;
--card-foreground: <%- colors.dark["card-foreground"] %>;
--border: <%- colors.dark["border"] %>;
--input: <%- colors.dark["input"] %>;
--primary: <%- colors.dark["primary"] %>;
--primary-foreground: <%- colors.dark["primary-foreground"] %>;
--secondary: <%- colors.dark["secondary"] %>;
--secondary-foreground: <%- colors.dark["secondary-foreground"] %>;
--accent: <%- colors.dark["accent"] %>;
--accent-foreground: <%- colors.dark["accent-foreground"] %>;
--destructive: <%- colors.dark["destructive"] %>;
--destructive-foreground: <%- colors.dark["destructive-foreground"] %>;
--ring: <%- colors.dark["ring"] %>;
}
}
@layer base {
* {
@apply border-border;
@ -275,8 +289,8 @@ for (const baseColor of ['slate', 'gray', 'zinc', 'neutral', 'stone', 'lime']) {
for (const [key, value] of Object.entries(values)) {
if (typeof value === 'string') {
const resolvedColor = value.replace(
/{{base}}-/g,
`${baseColor}-`,
/\{\{base\}\}-/g,
`${baseColor}-`,
)
base.inlineColors[mode][key] = resolvedColor
@ -312,64 +326,64 @@ export const THEME_STYLES_WITH_VARIABLES = `
.theme-<%- theme %> {
--background: <%- colors.light["background"] %>;
--foreground: <%- colors.light["foreground"] %>;
--muted: <%- colors.light["muted"] %>;
--muted-foreground: <%- colors.light["muted-foreground"] %>;
--popover: <%- colors.light["popover"] %>;
--popover-foreground: <%- colors.light["popover-foreground"] %>;
--card: <%- colors.light["card"] %>;
--card-foreground: <%- colors.light["card-foreground"] %>;
--border: <%- colors.light["border"] %>;
--input: <%- colors.light["input"] %>;
--primary: <%- colors.light["primary"] %>;
--primary-foreground: <%- colors.light["primary-foreground"] %>;
--secondary: <%- colors.light["secondary"] %>;
--secondary-foreground: <%- colors.light["secondary-foreground"] %>;
--accent: <%- colors.light["accent"] %>;
--accent-foreground: <%- colors.light["accent-foreground"] %>;
--destructive: <%- colors.light["destructive"] %>;
--destructive-foreground: <%- colors.light["destructive-foreground"] %>;
--ring: <%- colors.light["ring"] %>;
--radius: 0.5rem;
}
.dark .theme-<%- theme %> {
--background: <%- colors.dark["background"] %>;
--foreground: <%- colors.dark["foreground"] %>;
--muted: <%- colors.dark["muted"] %>;
--muted-foreground: <%- colors.dark["muted-foreground"] %>;
--popover: <%- colors.dark["popover"] %>;
--popover-foreground: <%- colors.dark["popover-foreground"] %>;
--card: <%- colors.dark["card"] %>;
--card-foreground: <%- colors.dark["card-foreground"] %>;
--border: <%- colors.dark["border"] %>;
--input: <%- colors.dark["input"] %>;
--primary: <%- colors.dark["primary"] %>;
--primary-foreground: <%- colors.dark["primary-foreground"] %>;
--secondary: <%- colors.dark["secondary"] %>;
--secondary-foreground: <%- colors.dark["secondary-foreground"] %>;
--accent: <%- colors.dark["accent"] %>;
--accent-foreground: <%- colors.dark["accent-foreground"] %>;
--destructive: <%- colors.dark["destructive"] %>;
--destructive-foreground: <%- colors.dark["destructive-foreground"] %>;
--ring: <%- colors.dark["ring"] %>;
}`
@ -389,4 +403,4 @@ fs.writeFileSync(
'utf8',
)
console.log('✅ Done!')
console.log('✅ Done!!')

View File

@ -0,0 +1,176 @@
<mxfile host="app.diagrams.net" modified="2024-03-15T08:14:00.888Z" agent="Mozilla/5.0 (X11; Linux x86_64; rv:124.0) Gecko/20100101 Firefox/124.0" etag="eUNOuIh_rCPXdI6LG0BE" version="24.0.6" type="device">
<diagram name="Page-1" id="10a91c8b-09ff-31b1-d368-03940ed4cc9e">
<mxGraphModel dx="1636" dy="971" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1100" pageHeight="850" background="none" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="PaMXV6_IjdSjTMUUNi7L-41" value="" style="rounded=0;whiteSpace=wrap;html=1;fontColor=none;noLabel=1;strokeColor=none;fillColor=none;" vertex="1" parent="1">
<mxGeometry x="30" y="70" width="1010" height="680" as="geometry" />
</mxCell>
<mxCell id="62893188c0fa7362-1" value="Shadcn/Vue" style="whiteSpace=wrap;html=1;rounded=1;shadow=0;labelBackgroundColor=none;strokeWidth=1;fontFamily=Garamond;fontSize=17;align=center;sketch=1;curveFitting=1;jiggle=2;" parent="1" vertex="1">
<mxGeometry x="380" y="130" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="62893188c0fa7362-2" value="Packages" style="whiteSpace=wrap;html=1;rounded=1;shadow=0;labelBackgroundColor=none;strokeWidth=1;fontFamily=Garamond;fontSize=17;align=center;sketch=1;curveFitting=1;jiggle=2;" parent="1" vertex="1">
<mxGeometry x="160" y="250" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="62893188c0fa7362-3" value="Apps/www" style="whiteSpace=wrap;html=1;rounded=1;shadow=0;labelBackgroundColor=none;strokeWidth=1;fontFamily=Garamond;fontSize=17;align=center;sketch=1;curveFitting=1;jiggle=2;" parent="1" vertex="1">
<mxGeometry x="630" y="250" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="62893188c0fa7362-4" value="" style="rounded=0;html=1;labelBackgroundColor=none;startArrow=none;startFill=0;startSize=5;endArrow=none;endFill=0;endSize=5;jettySize=auto;orthogonalLoop=1;strokeWidth=1;fontFamily=Garamond;fontSize=17;sketch=1;curveFitting=1;jiggle=2;shadow=0;" parent="1" source="PaMXV6_IjdSjTMUUNi7L-27" target="62893188c0fa7362-3" edge="1">
<mxGeometry x="-0.3002" y="13" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="62893188c0fa7362-5" value="" style="rounded=0;html=1;labelBackgroundColor=none;startArrow=none;startFill=0;startSize=5;endArrow=none;endFill=0;endSize=5;jettySize=auto;orthogonalLoop=1;strokeWidth=1;fontFamily=Garamond;fontSize=17;sketch=1;curveFitting=1;jiggle=2;shadow=0;" parent="1" source="62893188c0fa7362-1" target="62893188c0fa7362-2" edge="1">
<mxGeometry x="-0.359" y="-11" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="62893188c0fa7362-8" value="Module" style="whiteSpace=wrap;html=1;rounded=1;shadow=0;labelBackgroundColor=none;strokeWidth=1;fontFamily=Garamond;fontSize=17;align=center;sketch=1;curveFitting=1;jiggle=2;" parent="1" vertex="1">
<mxGeometry x="80" y="360" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="62893188c0fa7362-9" value="CLI" style="whiteSpace=wrap;html=1;rounded=1;shadow=0;labelBackgroundColor=none;strokeWidth=1;fontFamily=Garamond;fontSize=17;align=center;sketch=1;curveFitting=1;jiggle=2;" parent="1" vertex="1">
<mxGeometry x="220" y="360" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="62893188c0fa7362-14" value="" style="rounded=0;html=1;labelBackgroundColor=none;startArrow=none;startFill=0;startSize=5;endArrow=none;endFill=0;endSize=5;jettySize=auto;orthogonalLoop=1;strokeWidth=1;fontFamily=Garamond;fontSize=17;sketch=1;curveFitting=1;jiggle=2;shadow=0;" parent="1" source="62893188c0fa7362-2" target="62893188c0fa7362-8" edge="1">
<mxGeometry x="-0.2" y="-14" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="62893188c0fa7362-15" value="" style="rounded=0;html=1;labelBackgroundColor=none;startArrow=none;startFill=0;startSize=5;endArrow=none;endFill=0;endSize=5;jettySize=auto;orthogonalLoop=1;strokeWidth=1;fontFamily=Garamond;fontSize=17;sketch=1;curveFitting=1;jiggle=2;shadow=0;" parent="1" source="62893188c0fa7362-2" target="62893188c0fa7362-9" edge="1">
<mxGeometry x="-0.2" y="14" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="62893188c0fa7362-16" value="" style="rounded=0;html=1;labelBackgroundColor=none;startArrow=none;startFill=0;startSize=5;endArrow=none;endFill=0;endSize=5;jettySize=auto;orthogonalLoop=1;strokeWidth=1;fontFamily=Garamond;fontSize=17;entryX=0.75;entryY=0;entryDx=0;entryDy=0;sketch=1;curveFitting=1;jiggle=2;shadow=0;" parent="1" source="62893188c0fa7362-3" target="PaMXV6_IjdSjTMUUNi7L-2" edge="1">
<mxGeometry x="-0.2614" y="-13" relative="1" as="geometry">
<mxPoint as="offset" />
<mxPoint x="644.5454545454545" y="360" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="62893188c0fa7362-17" value="" style="rounded=0;html=1;labelBackgroundColor=none;startArrow=none;startFill=0;startSize=5;endArrow=none;endFill=0;endSize=5;jettySize=auto;orthogonalLoop=1;strokeWidth=1;fontFamily=Garamond;fontSize=17;entryX=0.25;entryY=0;entryDx=0;entryDy=0;sketch=1;curveFitting=1;jiggle=2;shadow=0;" parent="1" source="62893188c0fa7362-3" target="PaMXV6_IjdSjTMUUNi7L-1" edge="1">
<mxGeometry x="-0.1294" y="17" relative="1" as="geometry">
<mxPoint as="offset" />
<mxPoint x="782.7272727272725" y="360" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="PaMXV6_IjdSjTMUUNi7L-1" value="Registry" style="whiteSpace=wrap;html=1;rounded=1;shadow=0;labelBackgroundColor=none;strokeWidth=1;fontFamily=Garamond;fontSize=17;align=center;sketch=1;curveFitting=1;jiggle=2;" vertex="1" parent="1">
<mxGeometry x="720" y="370" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="PaMXV6_IjdSjTMUUNi7L-2" value=".vitepress" style="whiteSpace=wrap;html=1;rounded=1;shadow=0;labelBackgroundColor=none;strokeWidth=1;fontFamily=Garamond;fontSize=17;align=center;sketch=1;curveFitting=1;jiggle=2;" vertex="1" parent="1">
<mxGeometry x="420" y="370" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="PaMXV6_IjdSjTMUUNi7L-3" value="Scripts" style="whiteSpace=wrap;html=1;rounded=1;shadow=0;labelBackgroundColor=none;strokeWidth=1;fontFamily=Garamond;fontSize=17;align=center;sketch=1;curveFitting=1;jiggle=2;" vertex="1" parent="1">
<mxGeometry x="870" y="370" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="PaMXV6_IjdSjTMUUNi7L-4" value="Src" style="whiteSpace=wrap;html=1;rounded=1;shadow=0;labelBackgroundColor=none;strokeWidth=1;fontFamily=Garamond;fontSize=17;align=center;sketch=1;curveFitting=1;jiggle=2;" vertex="1" parent="1">
<mxGeometry x="570" y="370" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="PaMXV6_IjdSjTMUUNi7L-7" value="" style="rounded=0;html=1;labelBackgroundColor=none;startArrow=none;startFill=0;startSize=5;endArrow=none;endFill=0;endSize=5;jettySize=auto;orthogonalLoop=1;strokeWidth=1;fontFamily=Garamond;fontSize=17;entryX=0.333;entryY=0.017;entryDx=0;entryDy=0;exitX=1;exitY=1;exitDx=0;exitDy=0;entryPerimeter=0;sketch=1;curveFitting=1;jiggle=2;shadow=0;" edge="1" parent="1" source="62893188c0fa7362-3" target="PaMXV6_IjdSjTMUUNi7L-3">
<mxGeometry x="-0.1294" y="17" relative="1" as="geometry">
<mxPoint as="offset" />
<mxPoint x="841" y="270" as="sourcePoint" />
<mxPoint x="910" y="320" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="PaMXV6_IjdSjTMUUNi7L-9" value="" style="rounded=0;html=1;labelBackgroundColor=none;startArrow=none;startFill=0;startSize=5;endArrow=none;endFill=0;endSize=5;jettySize=auto;orthogonalLoop=1;strokeWidth=1;fontFamily=Garamond;fontSize=17;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitX=0.358;exitY=1.017;exitDx=0;exitDy=0;exitPerimeter=0;sketch=1;curveFitting=1;jiggle=2;shadow=0;" edge="1" parent="1" source="62893188c0fa7362-3" target="PaMXV6_IjdSjTMUUNi7L-4">
<mxGeometry x="-0.2614" y="-13" relative="1" as="geometry">
<mxPoint as="offset" />
<mxPoint x="720" y="540" as="sourcePoint" />
<mxPoint x="613" y="600" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="PaMXV6_IjdSjTMUUNi7L-11" value="Content" style="whiteSpace=wrap;html=1;rounded=1;shadow=0;labelBackgroundColor=none;strokeWidth=1;fontFamily=Garamond;fontSize=17;align=center;sketch=1;curveFitting=1;jiggle=2;" vertex="1" parent="1">
<mxGeometry x="420" y="500" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="PaMXV6_IjdSjTMUUNi7L-12" value="Examples" style="whiteSpace=wrap;html=1;rounded=1;shadow=0;labelBackgroundColor=none;strokeWidth=1;fontFamily=Garamond;fontSize=17;align=center;sketch=1;curveFitting=1;jiggle=2;" vertex="1" parent="1">
<mxGeometry x="570" y="500" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="PaMXV6_IjdSjTMUUNi7L-13" value="Lib/Registry" style="whiteSpace=wrap;html=1;rounded=1;shadow=0;labelBackgroundColor=none;strokeWidth=1;fontFamily=Garamond;fontSize=17;align=center;sketch=1;curveFitting=1;jiggle=2;" vertex="1" parent="1">
<mxGeometry x="720" y="500" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="PaMXV6_IjdSjTMUUNi7L-14" value="" style="rounded=0;html=1;labelBackgroundColor=none;startArrow=none;startFill=0;startSize=5;endArrow=none;endFill=0;endSize=5;jettySize=auto;orthogonalLoop=1;strokeWidth=1;fontFamily=Garamond;fontSize=17;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitX=0.308;exitY=1.033;exitDx=0;exitDy=0;exitPerimeter=0;sketch=1;curveFitting=1;jiggle=2;shadow=0;" edge="1" parent="1" source="PaMXV6_IjdSjTMUUNi7L-4" target="PaMXV6_IjdSjTMUUNi7L-11">
<mxGeometry x="-0.2614" y="-13" relative="1" as="geometry">
<mxPoint as="offset" />
<mxPoint x="560" y="470" as="sourcePoint" />
<mxPoint x="507" y="529" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="PaMXV6_IjdSjTMUUNi7L-15" value="" style="rounded=0;html=1;labelBackgroundColor=none;startArrow=none;startFill=0;startSize=5;endArrow=none;endFill=0;endSize=5;jettySize=auto;orthogonalLoop=1;strokeWidth=1;fontFamily=Garamond;fontSize=17;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;sketch=1;curveFitting=1;jiggle=2;shadow=0;" edge="1" parent="1" source="PaMXV6_IjdSjTMUUNi7L-4" target="PaMXV6_IjdSjTMUUNi7L-12">
<mxGeometry x="-0.2614" y="-13" relative="1" as="geometry">
<mxPoint as="offset" />
<mxPoint x="657" y="442" as="sourcePoint" />
<mxPoint x="540" y="560" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="PaMXV6_IjdSjTMUUNi7L-16" value="" style="rounded=0;html=1;labelBackgroundColor=none;startArrow=none;startFill=0;startSize=5;endArrow=none;endFill=0;endSize=5;jettySize=auto;orthogonalLoop=1;strokeWidth=1;fontFamily=Garamond;fontSize=17;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitX=0.75;exitY=1;exitDx=0;exitDy=0;sketch=1;curveFitting=1;jiggle=2;shadow=0;" edge="1" parent="1" source="PaMXV6_IjdSjTMUUNi7L-4" target="PaMXV6_IjdSjTMUUNi7L-13">
<mxGeometry x="-0.2614" y="-13" relative="1" as="geometry">
<mxPoint as="offset" />
<mxPoint x="660" y="435" as="sourcePoint" />
<mxPoint x="660" y="555" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="PaMXV6_IjdSjTMUUNi7L-20" value="Default" style="rounded=1;whiteSpace=wrap;html=1;fontFamily=Garamond;fontSize=17;sketch=1;curveFitting=1;jiggle=2;shadow=0;" vertex="1" parent="1">
<mxGeometry x="650" y="630" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="PaMXV6_IjdSjTMUUNi7L-21" value="New York" style="rounded=1;whiteSpace=wrap;html=1;fontFamily=Garamond;fontSize=17;sketch=1;curveFitting=1;jiggle=2;shadow=0;" vertex="1" parent="1">
<mxGeometry x="800" y="630" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="PaMXV6_IjdSjTMUUNi7L-22" value="" style="rounded=0;html=1;labelBackgroundColor=none;startArrow=none;startFill=0;startSize=5;endArrow=none;endFill=0;endSize=5;jettySize=auto;orthogonalLoop=1;strokeWidth=1;fontFamily=Garamond;fontSize=17;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;sketch=1;curveFitting=1;jiggle=2;shadow=0;" edge="1" parent="1" source="PaMXV6_IjdSjTMUUNi7L-13" target="PaMXV6_IjdSjTMUUNi7L-20">
<mxGeometry x="-0.2614" y="-13" relative="1" as="geometry">
<mxPoint as="offset" />
<mxPoint x="730" y="670" as="sourcePoint" />
<mxPoint x="530" y="818" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="PaMXV6_IjdSjTMUUNi7L-23" value="" style="rounded=0;html=1;labelBackgroundColor=none;startArrow=none;startFill=0;startSize=5;endArrow=none;endFill=0;endSize=5;jettySize=auto;orthogonalLoop=1;strokeWidth=1;fontFamily=Garamond;fontSize=17;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;sketch=1;curveFitting=1;jiggle=2;shadow=0;" edge="1" parent="1" source="PaMXV6_IjdSjTMUUNi7L-13" target="PaMXV6_IjdSjTMUUNi7L-21">
<mxGeometry x="-0.2614" y="-13" relative="1" as="geometry">
<mxPoint as="offset" />
<mxPoint x="790" y="670" as="sourcePoint" />
<mxPoint x="750" y="820" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="PaMXV6_IjdSjTMUUNi7L-26" value="1" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;sketch=1;curveFitting=1;jiggle=2;fontFamily=Garamond;fontSize=21;" vertex="1" parent="1">
<mxGeometry x="140" y="230" width="40" height="40" as="geometry" />
</mxCell>
<mxCell id="PaMXV6_IjdSjTMUUNi7L-28" value="3" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;sketch=1;curveFitting=1;jiggle=2;fontFamily=Garamond;fontSize=21;" vertex="1" parent="1">
<mxGeometry x="400" y="350" width="40" height="40" as="geometry" />
</mxCell>
<mxCell id="PaMXV6_IjdSjTMUUNi7L-29" value="4" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;sketch=1;curveFitting=1;jiggle=2;fontFamily=Garamond;fontSize=21;" vertex="1" parent="1">
<mxGeometry x="550" y="350" width="40" height="40" as="geometry" />
</mxCell>
<mxCell id="PaMXV6_IjdSjTMUUNi7L-30" value="5" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;sketch=1;curveFitting=1;jiggle=2;fontFamily=Garamond;fontSize=21;" vertex="1" parent="1">
<mxGeometry x="700" y="350" width="40" height="40" as="geometry" />
</mxCell>
<mxCell id="PaMXV6_IjdSjTMUUNi7L-31" value="6" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;sketch=1;curveFitting=1;jiggle=2;fontFamily=Garamond;fontSize=21;" vertex="1" parent="1">
<mxGeometry x="850" y="350" width="40" height="40" as="geometry" />
</mxCell>
<mxCell id="PaMXV6_IjdSjTMUUNi7L-32" value="7" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;sketch=1;curveFitting=1;jiggle=2;fontFamily=Garamond;fontSize=21;" vertex="1" parent="1">
<mxGeometry x="400" y="480" width="40" height="40" as="geometry" />
</mxCell>
<mxCell id="PaMXV6_IjdSjTMUUNi7L-33" value="8" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;sketch=1;curveFitting=1;jiggle=2;fontFamily=Garamond;fontSize=21;" vertex="1" parent="1">
<mxGeometry x="550" y="480" width="40" height="40" as="geometry" />
</mxCell>
<mxCell id="PaMXV6_IjdSjTMUUNi7L-34" value="9" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;sketch=1;curveFitting=1;jiggle=2;fontFamily=Garamond;fontSize=21;" vertex="1" parent="1">
<mxGeometry x="700" y="480" width="40" height="40" as="geometry" />
</mxCell>
<mxCell id="PaMXV6_IjdSjTMUUNi7L-36" value="10" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;sketch=1;curveFitting=1;jiggle=2;fontFamily=Garamond;fontSize=21;" vertex="1" parent="1">
<mxGeometry x="630" y="610" width="40" height="40" as="geometry" />
</mxCell>
<mxCell id="PaMXV6_IjdSjTMUUNi7L-38" value="11" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;sketch=1;curveFitting=1;jiggle=2;fontFamily=Garamond;fontSize=21;" vertex="1" parent="1">
<mxGeometry x="780" y="610" width="40" height="40" as="geometry" />
</mxCell>
<mxCell id="PaMXV6_IjdSjTMUUNi7L-39" value="" style="rounded=0;html=1;labelBackgroundColor=none;startArrow=none;startFill=0;startSize=5;endArrow=none;endFill=0;endSize=5;jettySize=auto;orthogonalLoop=1;strokeWidth=1;fontFamily=Garamond;fontSize=17;sketch=1;curveFitting=1;jiggle=2;shadow=0;" edge="1" parent="1" source="62893188c0fa7362-1" target="PaMXV6_IjdSjTMUUNi7L-27">
<mxGeometry x="-0.3002" y="13" relative="1" as="geometry">
<mxPoint as="offset" />
<mxPoint x="500" y="189" as="sourcePoint" />
<mxPoint x="630" y="251" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="PaMXV6_IjdSjTMUUNi7L-27" value="2" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;sketch=1;curveFitting=1;jiggle=2;fontFamily=Garamond;fontSize=21;" vertex="1" parent="1">
<mxGeometry x="610" y="230" width="40" height="40" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

10
apps/www/src/components.d.ts vendored Normal file
View File

@ -0,0 +1,10 @@
/* eslint-disable */
// @ts-nocheck
export {}
/* prettier-ignore */
declare module 'vue' {
export interface GlobalComponents {
ComponentPreview: typeof import('../.vitepress/theme/components/ComponentPreview.vue')['default']
}
}

View File

@ -47,3 +47,5 @@ const { site, theme, page, frontmatter } = useData()
## More
Check out the documentation for the [full list of runtime APIs](https://vitepress.dev/reference/runtime-api#usedata).
kick

View File

@ -0,0 +1,9 @@
---
title: Building Blocks
---
<script setup>
import Blocks from "../../.vitepress/theme/components/Blocks.vue"
</script>
<Blocks />

View File

@ -0,0 +1,10 @@
---
title: Blocks - shadcn-vue
layout: false
---
<script setup>
import BlockPage from "../../../.vitepress/theme/components/BlockPage.vue"
</script>
<BlockPage />

View File

@ -5,7 +5,7 @@ description: Powered by amazing open source projects.
## About
[shadcn-vue](https://shadcn-vuee.com) is a port of [shadcn/ui](https://ui.shadcn.com) for Vue/Nuxt. It's maintained by [radix-vue](https://github.com/radix-vue).
[shadcn-vue](https://shadcn-vue.com) is a port of [shadcn/ui](https://ui.shadcn.com) for Vue/Nuxt. It's maintained by [radix-vue](https://github.com/radix-vue).
## Credits
@ -17,4 +17,4 @@ description: Powered by amazing open source projects.
## License
MIT © [shadcn](https://shadcn.com) & [radix-vue](https://github.com/radix-vue)
MIT © [shadcn](https://shadcn.com) & [radix-vue](https://github.com/radix-vue)

View File

@ -1,3 +1,139 @@
---
title: Changelog
description: Latest updates and announcements.
---
## June 2024
### New Component - Number Field
A new component has been added to the project [`NumberField`](/docs/components/number-field.html).
A number field allows a user to enter a number and increment or decrement the value using stepper buttons.
<ComponentPreview name="NumberFieldDemo" class="max-w-[180px]" />
## May 2024
### New Component - Charts
Several kinds of chart components has been added to the project.
Charts are versatile visualization tools, allowing users to represent data using various options for effective analysis.
1. [`Area Chart`](/docs/charts/area) - An area chart visually represents data over time, displaying trends and patterns through filled-in areas under a line graph.
<ComponentPreview name="AreaChartDemo" />
2. [`Bar Chart`](/docs/charts/bar) - A line chart visually represents data using rectangular bars of varying lengths to compare quantities across different categories or groups.
<ComponentPreview name="BarChartDemo" />
3. [`Donut Chart`](/docs/charts/donut) - A line chart visually represents data in a circular form, similar to a pie chart but with a central void, emphasizing proportions within categories.
<ComponentPreview name="DonutChartDemo" />
4. [`Line Chart`](/docs/charts/line) - A line chart visually displays data points connected by straight lines, illustrating trends or relationships over a continuous axis.
<ComponentPreview name="LineChartDemo" />
### New Component - Auto Form
[`Auto Form`](/docs/components/auto-form.html) is a drop-in form builder for your internal and low-priority forms with existing zod schemas.
For example, if you already have zod schemas for your API and want to create a simple admin panel to edit user profiles, simply pass the schema to AutoForm and you're done.
The following form has been created by passing a `zod` schema object to our `AutoForm` component.
<ComponentPreview name="AutoFormBasic" />
## April 2024
### Component Updated - Calendar
The [`Calendar`](/docs/components/calendar.html) component has been updated and is now built on top of the [RadixVue Calendar](https://www.radix-vue.com/components/calendar.html) component, which uses the [@internationalized/date](https://react-spectrum.adobe.com/internationalized/date/index.html) package to handle dates.
If you're looking for a range calendar, check out the [`Range Calendar`](/docs/components/range-calendar.html) component.
And if you're looking for a date picker input, check out the [`Date Picker`](/docs/components/date-picker.html) component.
<ComponentPreview name="CalendarDemo" />
<ComponentPreview name="RangeCalendarDemo" />
<ComponentPreview name="DatePickerDemo" />
### Building Blocks for the Web
[`Blocks`](/blocks) are composed of different components that can be used to build your apps, with each block being a standalone section of your application. These blocks are fully responsive, accessible, and composable, and are built using the same principles as the other components in `shadcn-vue`.
<div>
<image
src="/examples/block-dark.png"
:width="1280"
:height="727"
alt="Building Blocks"
class="hidden dark:block"
/>
<image
src="/examples/block-light.png"
:width="1280"
:height="727"
alt="Building Blocks"
class="block dark:hidden"
/>
</div>
## March 2024
### New Component - Breadcrumb
[`Breadcrumb`](/docs/components/breadcrumb.html) displays the path to the current resource using a hierarchy of links.
<ComponentPreview name="BreadcrumbDemo" />
### New Component - Pin Input (OTP Input)
[`Pin Input`](/docs/components/pin-input.html) allows users to input a sequence of one-character alphanumeric inputs.
<ComponentPreview name="PinInputDemo" />
### New Component - Resizable
[`Resizable`](/docs/components/resizable.html) - Accessible resizable panel groups and layouts with keyboard support.
<ComponentPreview name="ResizableDemo" />
### New Component - Drawer
[`Drawer`](/docs/components/drawer.html) - A drawer component for vue that is built on top of [Vaul Vue](https://github.com/radix-vue/vaul-vue).
<ComponentPreview name="DrawerDemo" />
## February 2024
### New Component - Tag Inputs
[`Tag inputs`](/docs/components/tags-input.html) render tags inside an input, followed by an actual text input.
<ComponentPreview name="TagsInputDemo" />
## January 2024
### New Component - Sonner
[`Sonner`](/docs/components/sonner.html) is an opinionated toast component for Vue.
The Sonner component is provided by [vue-sonner](https://vue-sonner.vercel.app/), which is a Vue port of Sonner, originally created by [Emil Kowalski](https://twitter.com/emilkowalski_) for React.
<ComponentPreview name="SonnerDemo" />
### New Component - Toggle Group
[`Toggle Group`](/docs/components/toggle-group.html) - A set of two-state buttons that can be toggled on or off.
<ComponentPreview name="ToggleGroupDemo" />
### New Component - Carousel
[`Carousel`](/docs/components/carousel.html) - A carousel with motion and swipe built using [Embla](https://www.embla-carousel.com/) library.
<ComponentPreview name="CarouselDemo" />

View File

@ -0,0 +1,107 @@
---
title: Charts
description: Versatile visualization tool, allowing users to represent data using various types of charts for effective analysis.
label: Alpha
---
<script setup>
import Area from '~icons/gravity-ui/chart-area-stacked'
import Bar from '~icons/gravity-ui/chart-column'
import Line from '~icons/gravity-ui/chart-line'
import Pie from '~icons/gravity-ui/chart-pie'
</script>
<Callout>
Only works with Vue >3.3
</Callout>
`Charts` components were built on top of [Unovis](https://unovis.dev/) (a modular data visualization framework), and inspired by [tremor](https://www.tremor.so).
## Chart type
<div class="grid gap-4 mt-8 sm:grid-cols-2 sm:gap-6 not-docs">
<LinkedCard href="/docs/charts/area">
<Area class="text-foreground/80 w-11 h-11" />
<p class="mt-2 font-medium">Area</p>
</LinkedCard>
<LinkedCard href="/docs/charts/line">
<Line class="text-foreground/80 w-11 h-11" />
<p class="mt-2 font-medium">Line</p>
</LinkedCard>
<LinkedCard href="/docs/charts/bar">
<Bar class="text-foreground/80 w-11 h-11" />
<p class="mt-2 font-medium">Bar</p>
</LinkedCard>
<LinkedCard href="/docs/charts/donut">
<Pie class="text-foreground/80 w-11 h-11" />
<p class="mt-2 font-medium">Donut</p>
</LinkedCard>
</div>
## Installation
<Steps>
### Update `css`
Add the following tooltip styling to your `tailwind.css` file:
```css
@layer base {
:root {
/* ... */
--vis-tooltip-background-color: none !important;
--vis-tooltip-border-color: none !important;
--vis-tooltip-text-color: none !important;
--vis-tooltip-shadow-color: none !important;
--vis-tooltip-backdrop-filter: none !important;
--vis-tooltip-padding: none !important;
--vis-primary-color: var(--primary);
/* change to any hsl value you want */
--vis-secondary-color: 160 81% 40%;
--vis-text-color: var(--muted-foreground);
}
```
If you are not using `css-variables` for your component, you need to update the `--vis-primary-color` and `--vis-text-color` to your desired hsl value.
You may use [this tool](https://redpixelthemes.com/blog/tailwindcss-colors-different-formats/) to help you find the hsl value for your primary color and text color. Be sure to provide `dark` mode styling as well.
</Steps>
## Colors
By default, we construct the primary theme color, and secondary (`--vis-secondary-color`) color with different opacity for the graph.
However, you can always pass in the desired `color` into each chart.
```vue
<template>
<AreaChart
:data="data"
:colors="['blue', 'pink', 'orange', 'red']"
/>
</template>
```
## Custom tooltip
If you want to customize the `Tooltip` for the chart, you can pass `customTooltip` prop with a custom Vue component.
The custom component would receive `title` and `data` props, check out [ChartTooltip.vue component](https://github.com/radix-vue/shadcn-vue/tree/dev/apps/www/src/lib/registry/default/ui/chart/ChartTooltip.vue) for example.
The expected prop definition would be:
```ts
defineProps<{
title?: string
data: {
name: string
color: string
value: any
}[]
}>()
```

View File

@ -0,0 +1,46 @@
---
title: Area
description: An area chart visually represents data over time, displaying trends and patterns through filled-in areas under a line graph.
source: apps/www/src/lib/registry/default/ui/chart-area
label: Alpha
---
<ComponentPreview name="AreaChartDemo" />
## Installation
<Callout>
Only works with Vue >3.3
</Callout>
<Steps>
### Run the following command
```bash
npx shadcn-vue@latest add chart-area
```
### Setup
Follow the [guide](/docs/charts.html#installation) to complete the setup.
</Steps>
## API
<!-- @include: @/content/meta/AreaChart.md -->
## Example
### Sparkline
We can turn the chart into sparkline chart by hiding axis, gridline and legends.
<ComponentPreview name="AreaChartSparkline" />
### Custom Tooltip
If you want to render custom tooltip, you can easily pass in a custom component. Refer to prop definition [here](/docs/charts.html#custom-tooltip).
<ComponentPreview name="AreaChartCustomTooltip" />

View File

@ -0,0 +1,50 @@
---
title: Bar
description: A line chart visually represents data using rectangular bars of varying lengths to compare quantities across different categories or groups.
source: apps/www/src/lib/registry/default/ui/chart-bar
label: Alpha
---
<ComponentPreview name="BarChartDemo" />
## Installation
<Callout>
Only works with Vue >3.3
</Callout>
<Steps>
### Run the following command
```bash
npx shadcn-vue@latest add chart-bar
```
### Setup
Follow the [guide](/docs/charts.html#installation) to complete the setup.
</Steps>
## API
<!-- @include: @/content/meta/BarChart.md -->
## Example
### Stacked
You can stack the bar chart by settings prop `type` to `stacked`.
<ComponentPreview name="BarChartStacked" />
### Rounded
<ComponentPreview name="BarChartRounded" />
### Custom Tooltip
If you want to render custom tooltip, you can easily pass in a custom component. Refer to prop definition [here](/docs/charts.html#custom-tooltip).
<ComponentPreview name="BarChartCustomTooltip" />

View File

@ -0,0 +1,52 @@
---
title: Donut
description: A line chart visually represents data in a circular form, similar to a pie chart but with a central void, emphasizing proportions within categories.
source: apps/www/src/lib/registry/default/ui/chart-donut
label: Alpha
---
<ComponentPreview name="DonutChartDemo" />
## Installation
<Callout>
Only works with Vue >3.3
</Callout>
<Steps>
### Run the following command
```bash
npx shadcn-vue@latest add chart-donut
```
### Setup
Follow the [guide](/docs/charts.html#installation) to complete the setup.
</Steps>
## API
<!-- @include: @/content/meta/DonutChart.md -->
## Example
### Pie Chart
If you want to render pie chart instead, pass `type` as `pie`.
<ComponentPreview name="DonutChartPie" />
### Color
We generate colors automatically based on the primary and secondary color and assigned them accordingly. Feel free to pass in your own array of colors.
<ComponentPreview name="DonutChartColor" />
### Custom Tooltip
If you want to render custom tooltip, you can easily pass in a custom component. Refer to prop definition [here](/docs/charts.html#custom-tooltip).
<ComponentPreview name="DonutChartCustomTooltip" />

View File

@ -0,0 +1,46 @@
---
title: Line
description: A line chart visually displays data points connected by straight lines, illustrating trends or relationships over a continuous axis.
source: apps/www/src/lib/registry/default/ui/chart-line
label: Alpha
---
<ComponentPreview name="LineChartDemo" />
## Installation
<Callout>
Only works with Vue >3.3
</Callout>
<Steps>
### Run the following command
```bash
npx shadcn-vue@latest add chart-line
```
### Setup
Follow the [guide](/docs/charts.html#installation) to complete the setup.
</Steps>
## API
<!-- @include: @/content/meta/LineChart.md -->
## Example
### Sparkline
We can turn the chart into sparkline chart by hiding axis, gridline and legends.
<ComponentPreview name="LineChartSparkline" />
### Custom Tooltip
If you want to render custom tooltip, you can easily pass in a custom component. Refer to prop definition [here](/docs/charts.html#custom-tooltip).
<ComponentPreview name="LineChartCustomTooltip" />

View File

@ -15,7 +15,7 @@ npx shadcn-vue@latest init
You will be asked a few questions to configure `components.json`:
```txt:line-numbers
```ansi:line-numbers
Would you like to use TypeScript (recommended)? no / yes
Which framework are you using? Vite / Nuxt / Laravel
Which style would you like to use? Default
@ -24,12 +24,12 @@ Where is your global CSS file? src/index.css
Do you want to use CSS variables for colors? no / yes
Where is your tailwind.config.js located? tailwind.config.js
Configure the import alias for components: @/components
Configure the import alias for utils: @/lib/utils
Configure the import alias for utils: @/lib/utils
```
### Options
```txt
```ansi
Usage: shadcn-vue init [options]
initialize your project and install dependencies
@ -50,7 +50,7 @@ npx shadcn-vue@latest add [component]
You will be presented with a list of components to choose from:
```txt
```ansi
Which components would you like to add? Space to select. Return to submit.
◯ accordion
@ -67,7 +67,7 @@ Which components would you like to add? Space to select. Return to submit.
### Options
```txt
```ansi
Usage: shadcn-vue add [options] [components...]
add components to your project
@ -90,7 +90,7 @@ Use the `update` command to update components in your project. This will overwri
We plan on improving this command in the future to improve the update experience.
```txt
```ansi
Usage: shadcn-vue update [options] [components...]
update components in your project
@ -101,4 +101,4 @@ Arguments:
Options:
-c, --cwd <cwd> the working directory. (default: the current directory)
-h, --help display help for command
```
```

View File

@ -89,7 +89,6 @@ This is used to generate the default color palette for your components. **This c
}
```
### tailwind.cssVariables
You can choose between using CSS variables or Tailwind CSS utility classes for theming.
@ -109,7 +108,6 @@ For more information, see the [theming docs](/docs/theming).
**This cannot be changed after initialization.** To switch between CSS variables and utility classes, you'll have to delete and re-install your components.
## aliases
The CLI uses these values and the `paths` config from your `tsconfig.json` or `jsconfig.json` file to place generated components in the correct location.
@ -117,7 +115,6 @@ The CLI uses these values and the `paths` config from your `tsconfig.json` or `j
Path aliases have to be set up in your `tsconfig.json` or `jsconfig.json` file.
> A fallback to `tsconfig.app.json` if no `paths` were found in `tsconfig.json`
<Callout class="mt-6">
@ -126,7 +123,6 @@ Path aliases have to be set up in your `tsconfig.json` or `jsconfig.json` file.
</Callout>
### aliases.utils
Import alias for your utility functions.
@ -150,3 +146,17 @@ Import alias for your components.
}
}
```
### aliases.ui
Import alias for `ui` components.
The CLI will use the `aliases.ui` value to determine where to place your `ui` components. Use this config if you want to customize the installation directory for your `ui` components.
```json title="components.json"
{
"aliases": {
"ui": "@/app/ui"
}
}
```

View File

@ -0,0 +1,6 @@
<script setup>
import { useRouter } from 'vitepress'
const router = useRouter()
router.go('/docs/components/accordion')
</script>

View File

@ -1,15 +1,13 @@
---
title: Accordion
description: A vertically stacked set of interactive headings that each reveal a section of content.
source: apps/www/src/lib/registry/default/ui/accordion
description: A vertically stacked set of interactive headings that each reveal a section of content.
source: apps/www/src/lib/registry/default/ui/accordion
primitive: https://www.radix-vue.com/components/accordion.html
---
<ComponentPreview name="AccordionDemo" class="sm:max-w-[70%]" />
## Installation
<Steps>
@ -46,9 +44,8 @@ module.exports = {
},
}
```
</Steps>
## Usage
@ -68,4 +65,3 @@ import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/
</Accordion>
</template>
```

View File

@ -1,21 +1,18 @@
---
title: Alert Dialog
description: A modal dialog that interrupts the user with important content and expects a response.
source: apps/www/src/lib/registry/default/ui/alert-dialog
source: apps/www/src/lib/registry/default/ui/alert-dialog
primitive: https://www.radix-vue.com/components/alert-dialog.html
---
<ComponentPreview name="AlertDialogDemo" />
## Installation
<ComponentPreview name="AlertDialogDemo" />
## Installation
```bash
npx shadcn-vue@latest add alert-dialog
```
## Usage
```vue
@ -51,4 +48,4 @@ import {
</AlertDialogContent>
</AlertDialog>
</template>
```
```

View File

@ -3,16 +3,14 @@ title: Alert
description: Displays a callout for user attention.
---
<ComponentPreview name="AlertDemo" />
<ComponentPreview name="AlertDemo" />
## Installation
```bash
npx shadcn-vue@latest add alert
```
## Usage
```vue
@ -34,11 +32,8 @@ import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'
### Default
<ComponentPreview name="AlertDemo" />
<ComponentPreview name="AlertDemo" />
### Destructive
<ComponentPreview name="AlertDestructiveDemo" />
<ComponentPreview name="AlertDestructiveDemo" />

View File

@ -1,11 +1,10 @@
---
title: Aspect Ratio
description: Displays content within a desired ratio.
source: apps/www/src/lib/registry/default/ui/aspect-ratio
source: apps/www/src/lib/registry/default/ui/aspect-ratio
primitive: https://www.radix-vue.com/components/aspect-ratio.html
---
<ComponentPreview name="AspectRatioDemo" />
## Installation
@ -47,8 +46,8 @@ import { AspectRatio } from '@/components/ui/aspect-ratio'
<template>
<div class="w-[450px]">
<AspectRatio :ratio="16 / 9">
<img src="..." alt="Image" class="rounded-md object-cover">
<img src="..." alt="Image" class="rounded-md object-cover w-full h-full">
</AspectRatio>
</div>
</template>
```
```

View File

@ -0,0 +1,549 @@
---
title: AutoForm
description: Automatically generate a form from Zod schema.
primitive: https://vee-validate.logaretm.com/v4/guide/overview/
---
<Callout class="mt-6">
Credit: Heavily inspired by [AutoForm](https://github.com/vantezzen/auto-form) by Vantezzen
</Callout>
## What is AutoForm
AutoForm is a drop-in form builder for your internal and low-priority forms with existing zod schemas. For example, if you already have zod schemas for your API and want to create a simple admin panel to edit user profiles, simply pass the schema to AutoForm and you're done.
## Installation
<Steps>
### Run the following command
```bash
npx shadcn-vue@latest update form
npx shadcn-vue@latest add auto-form
```
</Steps>
## Field types
Currently, these field types are supported out of the box:
- boolean (checkbox, switch)
- date (date picker)
- enum (select, radio group)
- number (input)
- string (input, textfield)
- file (file)
You can add support for other field types by adding them to the `INPUT_COMPONENTS` object in `auto-form/constants.ts`.
## Zod configuration
### Validations
Your form schema can use any of zod's validation methods including refine.
<Callout>
⚠️ However, there's a known issue with Zods `refine` and `superRefine` not executing whenever some object keys are missing.
[Read more](https://github.com/logaretm/vee-validate/issues/4338)
</Callout>
### Descriptions
You can use the `describe` method to set a label for each field. If no label is set, the field name will be used and un-camel-cased.
```ts
const formSchema = z.object({
username: z.string().describe('Your username'),
someValue: z.string(), // Will be "Some Value"
})
```
You can also configure the label with [`fieldConfig`](#label) too.
### Optional fields
By default, all fields are required. You can make a field optional by using the `optional` method.
```ts
const formSchema = z.object({
username: z.string().optional(),
})
```
### Default values
You can set a default value for a field using the `default` method.
```ts
const formSchema = z.object({
favouriteNumber: z.number().default(5),
})
```
If you want to set default value of date, convert it to Date first using `new Date(val)`.
### Sub-objects
You can nest objects to create accordion sections.
```ts
const formSchema = z.object({
address: z.object({
street: z.string(),
city: z.string(),
zip: z.string(),
// You can nest objects as deep as you want
nested: z.object({
foo: z.string(),
bar: z.string(),
nested: z.object({
foo: z.string(),
bar: z.string(),
}),
}),
}),
})
```
Like with normal objects, you can use the `describe` method to set a label and description for the section:
```ts
const formSchema = z.object({
address: z
.object({
street: z.string(),
city: z.string(),
zip: z.string(),
})
.describe('Your address'),
})
```
### Select/Enums
AutoForm supports `enum` and `nativeEnum` to create select fields.
```ts
const formSchema = z.object({
color: z.enum(['red', 'green', 'blue']),
})
enum BreadTypes {
// For native enums, you can alternatively define a backed enum to set a custom label
White = 'White bread',
Brown = 'Brown bread',
Wholegrain = 'Wholegrain bread',
Other,
}
// Keep in mind that zod will validate and return the enum labels, not the enum values!
const formSchema = z.object({
bread: z.nativeEnum(BreadTypes),
})
```
### Arrays
AutoForm supports arrays _of objects_. Because inferring things like field labels from arrays of strings/numbers/etc. is difficult, only objects are supported.
```ts
const formSchema = z.object({
guestListName: z.string(),
invitedGuests: z
.array(
// Define the fields for each item
z.object({
name: z.string(),
age: z.number(),
})
)
// Optionally set a custom label - otherwise this will be inferred from the field name
.describe('Guests invited to the party'),
})
```
Arrays are not supported as the root element of the form schema.
You also can set default value of an array using .default(), but please make sure the array element has same structure with the schema.
```ts
const formSchema = z.object({
guestListName: z.string(),
invitedGuests: z
.array(
// Define the fields for each item
z.object({
name: z.string(),
age: z.number(),
})
)
.describe('Guests invited to the party')
.default([
{ name: 'John', age: 24, },
{ name: 'Jane', age: 20, },
]),
})
```
## Field configuration
As zod doesn't allow adding other properties to the schema, you can use the `fieldConfig` prop to add additional configuration for the UI of each field.
```vue
<template>
<AutoForm
:field-config="{
username: {
// fieldConfig
},
}"
/>
</template>
```
### Label
You can use the `label` property to customize label if you want to overwrite the pre-defined label via [Zod's description](#descriptions).
```vue
<template>
<AutoForm
:field-config="{
username: {
label: 'Custom username',
},
}"
/>
</template>
```
### Description
You can use the `description` property to add a description below the field.
```vue
<template>
<AutoForm
:field-config="{
username: {
description: 'Enter a unique username. This will be shown to other users.',
},
}"
/>
</template>
```
### Input props
You can use the `inputProps` property to pass props to the input component. You can use any props that the HTML component accepts.
```vue
<template>
<AutoForm
:field-config="{
username: {
inputProps: {
type: 'text',
placeholder: 'Username',
},
},
}"
/>
</template>
// This will be rendered as:
<input type="text" placeholder="Username" />
```
Disabling the label of an input can be done by using the `showLabel` property in `inputProps`.
```vue
<template>
<AutoForm
:field-config="{
username: {
inputProps: {
type: 'text',
placeholder: 'Username',
showLabel: false,
},
},
}"
/>
</template>
```
### Component
By default, AutoForm will use the Zod type to determine which input component to use. You can override this by using the `component` property.
```vue
<template>
<AutoForm
:field-config="{
acceptTerms: {
// Booleans use a checkbox by default, use a switch instead
component: 'switch',
},
}"
/>
</template>
```
The complete list of supported field types is typed. Current supported types are:
- `checkbox` (default for booleans)
- `switch`
- `date` (default for dates)
- `select` (default for enums)
- `radio`
- `textarea`
Alternatively, you can pass a Vue component to the `component` property to use a custom component.
In `CustomField.vue`
```vue
<script setup lang="ts">
import type { FieldProps } from './interface'
import { AutoFormLabel } from '@/ui/auto-form'
import { FormControl, FormDescription, FormField, FormItem, FormMessage } from '@/ui/form'
import { Input } from '@/ui/input'
import { computed } from 'vue'
import AutoFormLabel from './AutoFormLabel.vue'
const props = defineProps<FieldProps>()
</script>
<template>
<FormField v-slot="slotProps" :name="fieldName">
<FormItem v-bind="$attrs">
<AutoFormLabel v-if="!config?.hideLabel" :required="required">
{{ config?.label }}
</AutoFormLabel>
<FormControl>
<CustomInput v-bind="slotProps" />
</FormControl>
<FormDescription v-if="config?.description">
{{ config.description }}
</FormDescription>
<FormMessage />
</FormItem>
</FormField>
</template>
```
Pass the above component in `fieldConfig`.
```vue
<template>
<AutoForm
:field-config="{
username: {
component: CustomField,
},
}"
/>
</template>
```
### Named slot
You can use Vue named slot to customize the rendered `AutoFormField`.
```vue
<template>
<AutoForm
:field-config="{
customParent: {
label: 'Wrapper',
},
}"
>
<template #customParent="slotProps">
<div class="flex items-end space-x-2">
<AutoFormField v-bind="slotProps" class="w-full" />
<Button type="button">
Check
</Button>
</div>
</template>
</AutoForm>
</template>
```
### Accessing the form data
There are two ways to access the form data:
### @submit
The preferred way is to use the `submit` emit. This will be called when the form is submitted and the data is valid.
```vue
<template>
<AutoForm
@submit="(data) => {
// Do something with the data
}"
/>
</template>
```
### Controlled form
By passing the `form` as props, you can control and use the method provided by `Form`.
```vue
<script setup lang="ts">
import { AutoForm } from '@/components/ui/auto-form'
import { toTypedSchema } from '@vee-validate/zod'
import { useForm } from 'vee-validate'
import * as z from 'zod'
const schema = z.object({
username: z.string(),
})
const form = useForm({
validationSchema: toTypedSchema(schema),
})
form.setFieldValue('username', 'bar')
</script>
<template>
<AutoForm :form="form" :schema="schema" />
</template>
```
### Submitting the form
You can use any `button` component to create a submit button. Most importantly is to add attributes `type="submit"`.
```vue
<template>
<AutoForm>
<CustomButton type="submit">
Send now
</CustomButton>
</AutoForm>
// or
<AutoForm>
<button type="submit">
Send now
</button>
</AutoForm>
</template>
```
### Adding other elements
All children passed to the `AutoForm` component will be rendered below the form.
```vue
<template>
<AutoForm>
<Button>Send now</Button>
<p class="text-gray-500 text-sm">
By submitting this form, you agree to our
<a href="#" class="text-primary underline">
terms and conditions
</a>.
</p>
</AutoForm>
</template>
```
### Dependencies
AutoForm allows you to add dependencies between fields to control fields based on the value of other fields. For this, a `dependencies` array can be passed to the `AutoForm` component.
```vue
<template>
<AutoForm
:dependencies="[
{
// 'age' hides 'parentsAllowed' when the age is 18 or older
sourceField: 'age',
type: DependencyType.HIDES,
targetField: 'parentsAllowed',
when: age => age >= 18,
},
{
// 'vegetarian' checkbox hides the 'Beef Wellington' option from 'mealOptions'
// if its not already selected
sourceField: 'vegetarian',
type: DependencyType.SETS_OPTIONS,
targetField: 'mealOptions',
when: (vegetarian, mealOption) =>
vegetarian && mealOption !== 'Beef Wellington',
options: ['Pasta', 'Salad'],
},
]"
/>
</template>
```
The following dependency types are supported:
- `DependencyType.HIDES`: Hides the target field when the `when` function returns true
- `DependencyType.DISABLES`: Disables the target field when the `when` function returns true
- `DependencyType.REQUIRES`: Sets the target field to required when the `when` function returns true
- `DependencyType.SETS_OPTIONS`: Sets the options of the target field to the `options` array when the `when` function returns true
The `when` function is called with the value of the source field and the value of the target field and should return a boolean to indicate if the dependency should be applied.
Please note that dependencies will not cause the inverse action when returning `false` - for example, if you mark a field as required in your zod schema (i.e. by not explicitly setting `optional`), returning `false` in your `REQURIES` dependency will not mark it as optional. You should instead use zod's `optional` method to mark as optional by default and use the `REQURIES` dependency to mark it as required when the dependency is met.
Please note that dependencies do not have any effect on the validation of the form. You should use zod's `refine` method to validate the form based on the value of other fields.
You can create multiple dependencies for the same field and dependency type - for example to hide a field based on multiple other fields. This will then hide the field when any of the dependencies are met.
## Example
### Basic
<ComponentPreview name="AutoFormBasic" />
### Input Without Label
This example shows how to use AutoForm input without label.
<ComponentPreview name="AutoFormInputWithoutLabel" />
### Sub Object
Automatically generate a form from a Zod schema.
<ComponentPreview name="AutoFormSubObject" />
### Controlled
This example shows how to use AutoForm in a controlled way.
<ComponentPreview name="AutoFormControlled" />
### Confirm Password
Refined schema to validate that two fields match.
<ComponentPreview name="AutoFormConfirmPassword" />
### API Example
The form select options are fetched from an API.
<ComponentPreview name="AutoFormApi" />
### Array support
You can use arrays in your schemas to create dynamic forms.
<ComponentPreview name="AutoFormArray" />
### Dependencies
Create dependencies between fields.
<ComponentPreview name="AutoFormDependencies" />

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