Compare commits

...

97 Commits

Author SHA1 Message Date
Sébastien Chopin
84fc8c7c88 chore: remove all exclude 2024-11-26 18:34:14 +01:00
Benjamin Canac
1becead74e up
Co-Authored-By: Sébastien Chopin <seb@nuxt.com>
2024-11-26 18:24:02 +01:00
Benjamin Canac
ebaf4c530b up
Co-Authored-By: Sébastien Chopin <seb@nuxt.com>
2024-11-26 18:04:24 +01:00
Benjamin Canac
a7a4824ece docs(nuxt.config): disable prerender 2024-11-26 17:48:12 +01:00
Alex
15ca2f5701 docs(ComponentCode): add cast prop (#2773) 2024-11-26 16:13:47 +01:00
Benjamin Canac
08b9e4bff0 chore(README): update 2024-11-26 15:18:21 +01:00
Benjamin Canac
f49b49fd2c docs(app): update header github link 2024-11-26 15:10:17 +01:00
Benjamin Canac
a74e8c4444 docs(robots): update 2024-11-26 15:10:08 +01:00
Benjamin Canac
781081132d chore(README): update github links 2024-11-26 15:10:00 +01:00
Malik-Jouda
b1550d58ad fix(Table): handle loading animation in RTL mode (#2771) 2024-11-26 14:45:03 +01:00
Malik-Jouda
e7b69b7d6f fix(Calendar): handle icons in RTL mode (#2770) 2024-11-26 13:17:06 +01:00
Benjamin Canac
9478fc0768 fix(Calendar): omit as / asChild props 2024-11-26 13:12:11 +01:00
Alex
2e9aeb5f05 feat(Calendar): implement component (#2618)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2024-11-26 12:24:20 +01:00
Benjamin Canac
86f2b4856c fix(NavigationMenu): wrong badge class
Resolves #2766
2024-11-26 12:23:01 +01:00
Benjamin Canac
ba874c9191 docs(app): framework select global (#2719)
Co-authored-by: harlan <harlan@harlanzw.com>
2024-11-25 15:47:52 +01:00
Benjamin Canac
ffc81cc950 chore(CommandPalette): pass active to children 2024-11-25 14:39:15 +01:00
Benjamin Canac
d783387ed3 test(CommandPalette): improve 2024-11-25 14:26:58 +01:00
Benjamin Canac
37655377e9 feat(CommandPalette): add active field on items for consistency 2024-11-25 14:22:39 +01:00
renovate[bot]
7ab88d30b2 chore(deps): lock file maintenance (v3) (#2752)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-25 12:48:44 +01:00
renovate[bot]
ccb79b7ee4 chore(deps): update all non-major dependencies (v3) (#2705)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2024-11-25 12:37:53 +01:00
kyyy
c9806da6d8 fix(Form)!: resolve async validation in yup & issue directly mutate state (#2702)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2024-11-23 19:30:20 +01:00
Dafitra
3bccb6782a fix(useLocale): update locale import to enable tree shaking (#2735) 2024-11-22 23:03:44 +01:00
renovate[bot]
5a01a81577 chore(deps): update tailwindcss to v4.0.0-beta.2 (v3) (#2736)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2024-11-22 23:03:00 +01:00
Arcitezz
3baddfd121 feat(i18n): add Dutch locale (#2728) 2024-11-22 12:44:27 +01:00
Benjamin Canac
a7a1227c93 fix(Breadcrumb): missing aria-hidden on presentation items
Resolves #2725
2024-11-22 09:53:26 +01:00
Benjamin Canac
b259ddf271 docs(app): update @source usage 2024-11-21 23:41:29 +01:00
Benjamin Canac
c47ffc1cd5 docs: update links to tailwind v4 beta docs 2024-11-21 23:23:45 +01:00
Malik-Jouda
0baa3a06d4 fix(Progress): handle horizontal animation in RTL mode (#2723) 2024-11-21 23:07:59 +01:00
renovate[bot]
b140dbfe63 chore(deps): update tailwindcss to v4.0.0-beta.1 (v3) (#2721)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-21 22:32:52 +01:00
Benjamin Canac
6d917baac0 chore(css): update reset styles
https://github.com/tailwindlabs/tailwindcss/pull/15064
2024-11-21 22:31:37 +01:00
renovate[bot]
c511c95537 chore(deps): update tailwindcss to v4.0.0-alpha.36 (v3) (#2718)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Sandro Circi <sandro.circi@digitoolmedia.com>
2024-11-21 22:18:50 +01:00
Hasan Mumin
de8228e504 feat(i18n): add Turkish locale (#2716) 2024-11-21 17:27:41 +01:00
Benjamin Canac
29d2acf564 docs(getting-started): update faq 2024-11-21 16:51:28 +01:00
Benjamin Canac
f5452ba0c5 docs(icon): add missing props 2024-11-21 15:54:36 +01:00
Alex
b6a841e975 docs(i18n): display supported languages in a cards (#2709) 2024-11-21 11:21:35 +01:00
renovate[bot]
7bf85e9a09 chore(deps): update tailwindcss to v4.0.0-alpha.35 (v3) (#2707)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-21 10:56:00 +01:00
Benjamin Canac
9e8d50b2b8 chore(deps): dedupe 2024-11-20 18:54:15 +01:00
Benjamin Canac
b13b9e3ec0 docs(deps): update @nuxt/content to 3.0.0-alpha.7 2024-11-20 18:53:53 +01:00
Benjamin Canac
126c893635 docs(deps): update @nuxt/ui-pro 2024-11-20 17:56:34 +01:00
Farnabaz
7d8b721bdd docs(deps): update @nuxt/content (#2706) 2024-11-20 13:44:37 +01:00
renovate[bot]
b120e8d998 chore(deps): update all non-major dependencies (v3) (#2694)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-20 10:21:21 +01:00
renovate[bot]
2c4634a58f chore(deps): update nuxt framework to ^3.14.1592 (v3) (#2700)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-20 10:20:55 +01:00
Dewdew
2cbf83eb84 feat(locale): translate Korean (#2703)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2024-11-20 09:58:52 +01:00
Benjamin Canac
da1b0bac04 docs(dropdown-menu/context-menu): move class to ui.content
After a6ecef0f fix
2024-11-20 09:51:19 +01:00
Benjamin Canac
7cc51d2efa test: update snapshots 2024-11-20 09:35:04 +01:00
Benjamin Canac
c163ed8187 docs: improve titles 2024-11-19 22:52:23 +01:00
Benjamin Canac
a6ecef0f0d fix(components): apply class on trigger instead of content when present
Resolves #2132
2024-11-19 22:10:27 +01:00
Benjamin Canac
faec8260a4 test(Popover/Tooltip): add class / ui props 2024-11-19 22:09:33 +01:00
Malik-Jouda
7a02bfeba6 playground: improve responsive (#2675) 2024-11-19 19:26:09 +01:00
Benjamin Canac
9dd525ca26 docs(deps): update @nuxt/content to alpha.6 (#2692) 2024-11-19 18:41:04 +01:00
Benjamin Canac
21d8c352a9 chore(release): v3.0.0-alpha.9 2024-11-19 16:03:52 +01:00
Benjamin Canac
5deadc7096 Revert "docs(ComponentCode/ComponentExample): use relative imports"
This reverts commit d75f47419d.
2024-11-19 15:54:51 +01:00
Benjamin Canac
fa9f0a7e2a chore(Toaster): use ToastPortal from radix-vue 2024-11-19 15:41:26 +01:00
Alex
143c015bbd docs(i18n): display supported languages in a table (#2684)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2024-11-19 15:33:01 +01:00
Benjamin Canac
d75f47419d docs(ComponentCode/ComponentExample): use relative imports 2024-11-19 15:18:06 +01:00
Alex
7b148daf1f cli: fix line break doc template (#2687) 2024-11-19 14:06:48 +01:00
Benjamin Canac
30e0c7fddd docs(deps): update @nuxt/ui-pro 2024-11-19 12:24:40 +01:00
Mohet
14fb21be00 feat(locale): add Persian language (#2682)
Co-authored-by: Ali Zemani <ali.ze@arianatech.ir>
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2024-11-19 12:10:09 +01:00
renovate[bot]
25091bad48 chore(deps): lock file maintenance (v3) (#2672)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-19 11:59:54 +01:00
renovate[bot]
b75ed29068 chore(deps): update all non-major dependencies (v3) (#2679)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2024-11-19 11:40:06 +01:00
Thomas
b2fa65734b fix(css): --font-family-sans renamed to --font-sans (#2680)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2024-11-19 10:07:01 +01:00
Dewdew
d3a079a644 fix(Textarea): autoresize does not work when initializing modelValue (#2681) 2024-11-19 10:02:19 +01:00
renovate[bot]
8c6a8c283f chore(deps): update all non-major dependencies (v3) (#2642)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-18 23:18:35 +01:00
kicaj
2fc36c878c feat(locale): add Polish language (#2678) 2024-11-18 22:49:05 +01:00
Aaron Dewes
992be91823 fix(locale): Improve German translation (#2676) 2024-11-18 21:59:27 +01:00
Alex
bd2f077fe8 feat(InputNumber): implement component (#2577)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2024-11-18 10:08:57 +01:00
Sandro Circi
7329900ae5 feat(Link): allow partial query match for its active state (#2664)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2024-11-17 12:16:19 +01:00
Benjamin Canac
8d85498ee1 fix(Button): improve neutral solid variant hover 2024-11-16 21:59:27 +01:00
Benjamin Canac
5c292cf620 chore(Alert/Toast): improve tsdoc 2024-11-16 14:27:16 +01:00
Benjamin Canac
c0837059a9 playground: add color mode button 2024-11-15 12:58:06 +01:00
Benjamin Canac
f5ea2411dc chore(package): add dev:vue script 2024-11-15 12:57:55 +01:00
Malik-Jouda
1fbbfe8df0 fix(Carousel): use dir from locale (#2647) 2024-11-15 12:00:51 +01:00
Sandro Circi
0daac5bafb fix(Toast): unreachable behind overlays (#2650)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2024-11-15 11:59:08 +01:00
Benjamin Canac
756f791a1a fix(Breadcrumb): render as nav
Resolves #2649
2024-11-15 09:52:44 +01:00
Eduard Aymerich
8ed434c105 feat(locale): translate Spanish (#2644) 2024-11-15 09:48:11 +01:00
renovate[bot]
190a2c9799 chore(deps): update tailwindcss to v4.0.0-alpha.34 (v3) (#2645)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-15 09:47:16 +01:00
Alex
e55c0e2594 feat(locale): typing dir (#2643) 2024-11-14 19:53:35 +01:00
Benjamin Canac
4312ca4702 docs(deps): update @nuxt/ui-pro 2024-11-14 18:26:50 +01:00
renovate[bot]
2289742656 chore(deps): update dependency @nuxt/icon to ^1.7.5 (v3) (#2639)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-14 16:45:20 +01:00
Benjamin Canac
601f4b2cd2 fix(PinInput): missing useFormField import 2024-11-14 15:57:21 +01:00
Benjamin Canac
cd080541a0 docs(deps): add @iconify-json/logos 2024-11-14 15:45:22 +01:00
Benjamin Canac
5722e0802d playground(deps): add @iconify-json/simple-icons 2024-11-14 15:44:35 +01:00
Benjamin Canac
8d0026558a fix(css): remove useless spacing override 2024-11-14 11:47:58 +01:00
Malik-Jouda
e5119a9ca4 fix(Breadcrumb/Carousel/Pagination): handle icons in RTL mode (#2633) 2024-11-14 10:17:55 +01:00
renovate[bot]
976dd2a386 chore(deps): update all non-major dependencies (v3) (#2629)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-14 10:16:27 +01:00
Benjamin Canac
1d95eb7246 docs(input): add more examples 2024-11-13 16:58:21 +01:00
Benjamin Canac
7cc26d098d fix(App): remove dir prop (#2630) 2024-11-13 16:23:08 +01:00
Guillaume Chau
9241ba1230 fix(FormField): missing conditions to apply container classes (#2631)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2024-11-13 15:57:35 +01:00
Daniel Roe
3396d5cebe ci: run vite build to test playground (#2624) 2024-11-13 12:22:46 +01:00
Alex
937585cb3f feat(locale): provide dir on defineLocale (#2620)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2024-11-13 12:19:21 +01:00
renovate[bot]
9c00f7c7b7 chore(deps): update all non-major dependencies (v3) (#2626)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-13 11:58:02 +01:00
Sandro Circi
73e25ed235 fix(locale): it translation (#2623) 2024-11-12 21:25:25 +01:00
kyyy
75c5e87724 feat(Form): apply transformations (#2550)
Co-authored-by: Romain Hamel <rom.hml@gmail.com>
2024-11-12 16:43:40 +01:00
max
95aa6f68b3 feat(PinInput): implement component (#2570)
Co-authored-by: Max Steinwand <msteinwand@kues.de>
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
Co-authored-by: Romain Hamel <rom.hml@gmail.com>
2024-11-12 16:11:06 +01:00
Inesh Bose
f516d7b36d feat(InputMenu/SelectMenu): add create-item prop (#2472)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2024-11-12 15:28:18 +01:00
renovate[bot]
300ccc4885 chore(deps): update all non-major dependencies (v3) (#2605)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-12 14:29:28 +01:00
Alex
e48b416e3b cli: add doc template (#2616)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2024-11-12 14:19:32 +01:00
223 changed files with 20130 additions and 3436 deletions

View File

@@ -61,5 +61,8 @@ jobs:
- name: Build
run: pnpm run build
- name: Build vue fixture
run: pnpm run test:vue:build
- name: Publish
run: pnpx pkg-pr-new publish --compact --no-template --pnpm

View File

@@ -1,5 +1,67 @@
# Changelog
## [3.0.0-alpha.9](https://github.com/nuxt/ui/compare/v3.0.0-alpha.8...v3.0.0-alpha.9) (2024-11-19)
### Features
* **cli:** add locale command ([#2586](https://github.com/nuxt/ui/issues/2586)) ([824ba56](https://github.com/nuxt/ui/commit/824ba5629183bc4cd59321213558770db211f6e5))
* **css:** add `--ui-bg-muted` / `--ui-border-muted` variables ([7f6db45](https://github.com/nuxt/ui/commit/7f6db45f1e15ef39cda9b732cb601c552f29570a))
* **Form:** apply transformations ([#2550](https://github.com/nuxt/ui/issues/2550)) ([75c5e87](https://github.com/nuxt/ui/commit/75c5e87724e7abdf0a6751d7a1dbbafb947f373f))
* **FormField:** add `error-pattern` prop ([#2601](https://github.com/nuxt/ui/issues/2601)) ([143612e](https://github.com/nuxt/ui/commit/143612ec737d1c7571398601c3222f2eed37996e))
* **InputMenu/SelectMenu:** add `create-item` prop ([#2472](https://github.com/nuxt/ui/issues/2472)) ([f516d7b](https://github.com/nuxt/ui/commit/f516d7b36da51565f4ab05a4c9cfe5e5b4015124))
* **InputNumber:** implement component ([#2577](https://github.com/nuxt/ui/issues/2577)) ([bd2f077](https://github.com/nuxt/ui/commit/bd2f077fe8e645d5fce8b1eb5a6eb1068b3e8f7c))
* **Link:** allow partial query match for its active state ([#2664](https://github.com/nuxt/ui/issues/2664)) ([7329900](https://github.com/nuxt/ui/commit/7329900ae549430b88567a09cbb585d3cf0a6d32))
* **locale:** add Persian language ([#2682](https://github.com/nuxt/ui/issues/2682)) ([14fb21b](https://github.com/nuxt/ui/commit/14fb21be0034ffc0ba5d213734c00f12e0d6bea8))
* **locale:** add Polish language ([#2678](https://github.com/nuxt/ui/issues/2678)) ([2fc36c8](https://github.com/nuxt/ui/commit/2fc36c878c67967ec91e4f6999575bad45521d44))
* **locale:** add support for Arabic ([#2582](https://github.com/nuxt/ui/issues/2582)) ([602a667](https://github.com/nuxt/ui/commit/602a667343be22b72383ab3cf42f36ec9e135082))
* **locale:** add support for Czech translation ([#2593](https://github.com/nuxt/ui/issues/2593)) ([4889d30](https://github.com/nuxt/ui/commit/4889d30b448296de42e146dc5771738837c31f8c))
* **locale:** add support for Italian ([#2583](https://github.com/nuxt/ui/issues/2583)) ([4fbbb25](https://github.com/nuxt/ui/commit/4fbbb25f68b0b5ee76e50f2da776a74d54acc041))
* **locale:** provide `code` ([#2611](https://github.com/nuxt/ui/issues/2611)) ([8a8b1ee](https://github.com/nuxt/ui/commit/8a8b1ee2e1628bc5439ef109d3c68b69bf631f81))
* **locale:** provide `dir` on `defineLocale` ([#2620](https://github.com/nuxt/ui/issues/2620)) ([937585c](https://github.com/nuxt/ui/commit/937585cb3f8bc902d60a4b5904711598204aee2d))
* **locale:** translate chinese ([#2580](https://github.com/nuxt/ui/issues/2580)) ([febda5c](https://github.com/nuxt/ui/commit/febda5c2b67374d1358a66694543b77037d239c6))
* **locale:** translate Spanish ([#2644](https://github.com/nuxt/ui/issues/2644)) ([8ed434c](https://github.com/nuxt/ui/commit/8ed434c105b75ae02aa7493a235cebb64d518d09))
* **locale:** typing `dir` ([#2643](https://github.com/nuxt/ui/issues/2643)) ([e55c0e2](https://github.com/nuxt/ui/commit/e55c0e25947e7bcef931b26dafaad120f488a627))
* **module:** support i18n in components ([#2553](https://github.com/nuxt/ui/issues/2553)) ([2636240](https://github.com/nuxt/ui/commit/26362408b161108487b889ff001bec9166059c79))
* **NavigationMenu:** control items `open` & `defaultOpen` on vertical ([30218f1](https://github.com/nuxt/ui/commit/30218f1b5b0a56207fd4db224ffa0401ac194a04)), closes [#2608](https://github.com/nuxt/ui/issues/2608)
* **PinInput:** implement component ([#2570](https://github.com/nuxt/ui/issues/2570)) ([95aa6f6](https://github.com/nuxt/ui/commit/95aa6f68b316d02c28d1124d9a826bca55cde81f))
* **Popover:** add `prevent-close` prop ([ea97759](https://github.com/nuxt/ui/commit/ea97759c2c219bdf5c48b652b47d293ddf513a00)), closes [#2245](https://github.com/nuxt/ui/issues/2245)
* **SelectMenu:** use `UInput` in search to handle props like icon ([ff1e079](https://github.com/nuxt/ui/commit/ff1e0798d384d40ad82a95fe5faa16acb080efe3)), closes [#2021](https://github.com/nuxt/ui/issues/2021)
* **Table:** add `caption` prop ([446f9c1](https://github.com/nuxt/ui/commit/446f9c1085e96187afdc5c1d7ce3450f8df1a2e1))
### Bug Fixes
* **App:** missing `vue` imports ([ddb4690](https://github.com/nuxt/ui/commit/ddb46905e7e3480ab578bcd8a478f25dff60081a))
* **App:** remove `dir` prop ([#2630](https://github.com/nuxt/ui/issues/2630)) ([7cc26d0](https://github.com/nuxt/ui/commit/7cc26d098dff70923bcd9d414d675018951b1967))
* **Breadcrumb/Carousel/Pagination:** handle icons in RTL mode ([#2633](https://github.com/nuxt/ui/issues/2633)) ([e5119a9](https://github.com/nuxt/ui/commit/e5119a9ca4e217ef769904323c16bd8c0cbc02d9))
* **Breadcrumb:** render as `nav` ([756f791](https://github.com/nuxt/ui/commit/756f791a1a8dd3a4a88c212b4e4f775584decb55)), closes [#2649](https://github.com/nuxt/ui/issues/2649)
* **Button:** improve neutral solid variant hover ([8d85498](https://github.com/nuxt/ui/commit/8d85498ee197ec0b26cdd7c4b08f84fec45ddd8f))
* **Carousel:** use `dir` from locale ([#2647](https://github.com/nuxt/ui/issues/2647)) ([1fbbfe8](https://github.com/nuxt/ui/commit/1fbbfe8df06b3b8b294615ac328d582c5230aa8b))
* **ContextMenu/DropdownMenu:** relative imports with prefix ([47f58f5](https://github.com/nuxt/ui/commit/47f58f52ef2d03176a184a3ca2154f5cea655edb))
* **css:** `--font-family-sans` renamed to `--font-sans` ([#2680](https://github.com/nuxt/ui/issues/2680)) ([b2fa657](https://github.com/nuxt/ui/commit/b2fa65734bb59186520c985f7c73fc34a0cb8b37))
* **css:** remove useless spacing override ([8d00265](https://github.com/nuxt/ui/commit/8d0026558a21efbbca08e9939844f7479a0d6cce))
* **FormField:** missing conditions to apply container classes ([#2631](https://github.com/nuxt/ui/issues/2631)) ([9241ba1](https://github.com/nuxt/ui/commit/9241ba1230b0fde41595634362d83c92c66b7699))
* **Form:** match `error-pattern` on input validation ([#2606](https://github.com/nuxt/ui/issues/2606)) ([3584a33](https://github.com/nuxt/ui/commit/3584a3328b8588f024557c9908242bc374853419))
* **InputMenu/SelectMenu:** init `filter` with `labelKey` ([18931ac](https://github.com/nuxt/ui/commit/18931acdb316bc72a3e5ed6d20985688ad5c8d99))
* **InputMenu/SelectMenu:** look in `items` only with `value-attribute` ([0ceafe1](https://github.com/nuxt/ui/commit/0ceafe1d54000f3eb49562b00c188d82fa23c4ee)), closes [#2464](https://github.com/nuxt/ui/issues/2464)
* **InputMenu/SelectMenu:** multiple not working with generic boolean casting ([503f701](https://github.com/nuxt/ui/commit/503f701c7ecdfe27e9057e5ddebfc7e03889d61b)), closes [#2541](https://github.com/nuxt/ui/issues/2541)
* **InputMenu/SelectMenu:** use `isEqual` from `ohash` ([f943f88](https://github.com/nuxt/ui/commit/f943f88fcc9f4678d8f7bd224799e289a0c57dd8))
* **Link:** missing relative import ([#2588](https://github.com/nuxt/ui/issues/2588)) ([95a0bbc](https://github.com/nuxt/ui/commit/95a0bbc581a40677f620eed3170f9a423976214b))
* **locale:** Improve German translation ([#2676](https://github.com/nuxt/ui/issues/2676)) ([992be91](https://github.com/nuxt/ui/commit/992be91823fe1877254ccd092c71c77dd3ff42f7))
* **locale:** it translation ([#2623](https://github.com/nuxt/ui/issues/2623)) ([73e25ed](https://github.com/nuxt/ui/commit/73e25ed23562f755ea4c66e6c5fb06dd18caac1e))
* **locale:** Italian translation ([#2584](https://github.com/nuxt/ui/issues/2584)) ([d167c9b](https://github.com/nuxt/ui/commit/d167c9b807a82fdf0fd280ce8417a66f86d7ed72))
* **Modal/Slideover:** prevent `esc` with `prevent-close` prop ([9e2cc5b](https://github.com/nuxt/ui/commit/9e2cc5b12567472044726924a3896b4b0e7993a1)), closes [#2501](https://github.com/nuxt/ui/issues/2501)
* **module:** remove `fast-deep-equal` in favor of custom `isEqual` ([37a3597](https://github.com/nuxt/ui/commit/37a359701f4b2ce4a9b0727b64c0e3eea6be00b4))
* **module:** skip devtools renderer page injection if router integration is disabled ([#2571](https://github.com/nuxt/ui/issues/2571)) ([afe4003](https://github.com/nuxt/ui/commit/afe40033b088d8aedb73fa8387a0284ef78444e4))
* **PinInput:** missing `useFormField` import ([601f4b2](https://github.com/nuxt/ui/commit/601f4b2cd2027817b935e02a0b4584dc3dce655f))
* **Textarea:** `autoresize` does not work when initializing `modelValue` ([#2681](https://github.com/nuxt/ui/issues/2681)) ([d3a079a](https://github.com/nuxt/ui/commit/d3a079a644db3dfe2f4e9567973550d74b3ba905))
* **Toaster:** teleport to `body` ([b0be26d](https://github.com/nuxt/ui/commit/b0be26d67feab467ac5862edd82e52df03a5091c)), closes [#2404](https://github.com/nuxt/ui/issues/2404)
* **Toast:** unreachable behind overlays ([#2650](https://github.com/nuxt/ui/issues/2650)) ([0daac5b](https://github.com/nuxt/ui/commit/0daac5bafb756c3a2dfaf2bf166c30c0eb476e08))
* **useLocale:** missing import in various components ([#2603](https://github.com/nuxt/ui/issues/2603)) ([df7a61a](https://github.com/nuxt/ui/commit/df7a61a97a14b3d7943baee6a74686134dfdb10b))
### Reverts
* Revert "docs(ComponentCode/ComponentExample): use relative imports" ([5deadc7](https://github.com/nuxt/ui/commit/5deadc709640bbfd3ec14c1c9363deb55e765d6a))
## [3.0.0-alpha.8](https://github.com/nuxt/ui/compare/v3.0.0-alpha.7...v3.0.0-alpha.8) (2024-11-07)
### ⚠ BREAKING CHANGES

View File

@@ -1,4 +1,4 @@
[![nuxt-ui.png](https://repository-images.githubusercontent.com/428329515/43fec891-9030-4601-8233-5d45ba5c6013)](https://ui.nuxt.com)
[![nuxt-ui.png](https://volta.s3.fr-par.scw.cloud/nuxt_ui_social_card_531d133fa2.png)](https://ui.nuxt.com)
# Nuxt UI
@@ -7,7 +7,7 @@
[![License][license-src]][license-href]
[![Nuxt][nuxt-src]][nuxt-href]
We're thrilled to introduce Nuxt UI v3, a significant upgrade to our UI library that delivers extensive improvements and robust new capabilities. This major update harnesses the combined strengths of [Radix Vue](https://www.radix-vue.com/), [Tailwind CSS v4](https://tailwindcss.com/blog/tailwindcss-v4-alpha), and [Tailwind Variants](https://www.tailwind-variants.org/) to offer developers an unparalleled set of tools for creating sophisticated, accessible, and highly performant user interfaces.
We're thrilled to introduce Nuxt UI v3, a significant upgrade to our UI library that delivers extensive improvements and robust new capabilities. This major update harnesses the combined strengths of [Radix Vue](https://www.radix-vue.com/), [Tailwind CSS v4](https://tailwindcss.com/docs/v4-beta), and [Tailwind Variants](https://www.tailwind-variants.org/) to offer developers an unparalleled set of tools for creating sophisticated, accessible, and highly performant user interfaces.
> [!NOTE]
> You are on the `v3` development branch, check out the [dev branch](https://github.com/nuxt/ui) for Nuxt UI v2.
@@ -105,7 +105,7 @@ Learn more in the [installation guide](https://ui3.nuxt.dev/getting-started/inst
## License
Licensed under the [MIT license](https://github.com/nuxt/ui/blob/dev/LICENSE.md).
Licensed under the [MIT license](https://github.com/nuxt/ui/blob/v3/LICENSE.md).
<!-- Badges -->
[npm-version-src]: https://img.shields.io/npm/v/@nuxt/ui/next.svg?style=flat&colorA=18181B&colorB=28CF8D
@@ -115,7 +115,7 @@ Licensed under the [MIT license](https://github.com/nuxt/ui/blob/dev/LICENSE.md)
[npm-downloads-href]: https://npm.chart.dev/@nuxt/ui
[license-src]: https://img.shields.io/github/license/nuxt/ui.svg?style=flat&colorA=18181B&colorB=28CF8D
[license-href]: https://github.com/nuxt/ui/blob/main/LICENSE.md
[license-href]: https://github.com/nuxt/ui/blob/v3/LICENSE.md
[nuxt-src]: https://img.shields.io/badge/Nuxt-18181B?logo=nuxt.js
[nuxt-href]: https://nuxt.com

View File

@@ -17,6 +17,10 @@ export default defineCommand({
name: {
description: 'Locale name to create. For example: English.',
required: true
},
dir: {
description: 'Locale direction. For example: rtl.',
default: 'ltr'
}
},
async setup({ args }) {
@@ -32,6 +36,11 @@ export default defineCommand({
process.exit(1)
}
if (!['ltr', 'rtl'].includes(args.dir)) {
consola.error(`🚨 Direction ${args.dir} not supported!`)
process.exit(1)
}
if (!args.code.match(/^[a-z]{2}(?:_[a-z]{2,4})?$/)) {
consola.error(`🚨 ${args.code} is not a valid locale code!\nExample: en or en_us`)
process.exit(1)
@@ -45,7 +54,9 @@ export default defineCommand({
// Create new locale file
await fsp.copyFile(originLocaleFilePath, newLocaleFilePath)
const localeFile = await fsp.readFile(newLocaleFilePath, 'utf-8')
const rewrittenLocaleFile = localeFile.replace(/defineLocale\('(.*)'/, `defineLocale('${args.name}', '${normalizeLocale(args.code)}'`)
const rewrittenLocaleFile = localeFile
.replace(/name: '(.*)',/, `name: '${args.name}',`)
.replace(/code: '(.*)',/, `code: '${normalizeLocale(args.code)}',${(args.dir && args.dir !== 'ltr') ? `\n dir: '${args.dir}',` : ''}`)
await fsp.writeFile(newLocaleFilePath, rewrittenLocaleFile)
consola.success(`🪄 Generated ${newLocaleFilePath}`)

View File

@@ -163,9 +163,54 @@ describe('${upperName}', () => {
}
}
const doc = ({ name, pro }) => {
const kebabName = kebabCase(name)
const upperName = splitByCase(name).map(p => upperFirst(p)).join('')
return {
filename: `docs/content/${pro ? 'pro' : '3.components'}/${kebabName}.md`,
contents: `---
description:
links: ${pro
? ''
: `
- label: ${upperName}
icon: i-custom-radix-vue
to: https://www.radix-vue.com/components/${kebabName}.html`}
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxt/${pro ? 'ui-pro' : 'ui'}/tree/v3/src/runtime/components/${upperName}.vue
---
## Usage
## Examples
## API
### Props
:component-props
### Slots
:component-slots
### Emits
:component-emits
## Theme
:component-theme
`
}
}
export default {
playground,
component,
theme,
test
test,
doc
}

View File

@@ -168,7 +168,7 @@ const isDark = computed({
@import '@nuxt/ui';
@theme {
--font-family-sans: 'DM Sans', sans-serif;
--font-sans: 'DM Sans', sans-serif;
--color-primary-50: var(--ui-color-primary-50);
--color-primary-100: var(--ui-color-primary-100);

View File

@@ -12,7 +12,7 @@
"dependencies": {
"@nuxt/ui": "latest",
"knitwork": "^1.1.0",
"nuxt": "^3.14.159",
"nuxt": "^3.14.1592",
"prettier": "^3.3.3",
"zod": "^3.23.8"
}

View File

@@ -73,24 +73,41 @@ useServerSeoMeta({
twitterCard: 'summary_large_image'
})
const updatedNavigation = computed(() => navigation.value?.map(item => ({
...item,
children: item.children?.map((child: typeof item) => ({
...child,
...(child.path === '/getting-started/installation' && {
title: 'Installation',
active: route.path.startsWith('/getting-started/installation'),
children: []
}),
...(child.path === '/getting-started/i18n' && {
title: 'I18n',
active: route.path.startsWith('/getting-started/i18n'),
children: []
})
})) || []
})))
const { framework, frameworks } = useSharedData()
provide('navigation', updatedNavigation)
const groups = computed(() => {
return [{
id: 'framework',
label: 'Framework',
items: frameworks.value
}]
})
function filterFrameworkItems(items: any[]) {
return items?.filter(item => !item.framework || item.framework === framework.value)
}
function processNavigationItem(item: any): any {
if (item.shadow) {
const matchingChild = filterFrameworkItems(item.children)?.[0]
return matchingChild
? {
...matchingChild,
title: item.title,
children: matchingChild.children ? processNavigationItem(matchingChild) : undefined
}
: item
}
return {
...item,
children: item.children?.length ? filterFrameworkItems(item.children)?.map(processNavigationItem) : undefined
}
}
const filteredNavigation = computed(() => navigation.value?.map(processNavigationItem))
provide('navigation', filteredNavigation)
</script>
<template>
@@ -111,7 +128,13 @@ provide('navigation', updatedNavigation)
<Footer />
<ClientOnly>
<LazyUContentSearch v-model:search-term="searchTerm" :files="files" :navigation="navigation" :fuse="{ resultLimit: 42 }" />
<LazyUContentSearch
v-model:search-term="searchTerm"
:files="files"
:groups="groups"
:navigation="filteredNavigation"
:fuse="{ resultLimit: 42 }"
/>
</ClientOnly>
</template>
</UApp>
@@ -121,12 +144,12 @@ provide('navigation', updatedNavigation)
@import "tailwindcss";
@import "@nuxt/ui-pro";
@source "../content/**/*.md";
@source "../content";
@theme {
--container-8xl: 90rem;
--font-family-sans: 'Public Sans', sans-serif;
--font-sans: 'Public Sans', sans-serif;
--color-green-50: #EFFDF5;
--color-green-100: #D9FBE8;

View File

@@ -0,0 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 283.46 283.46">
<defs>
<style>
.cls-1{fill:#231815;}
@media (prefers-color-scheme: dark) { .cls-1{fill:#ffffff;} }
</style>
</defs>
<path class="cls-1" d="M144.89,89.86c-33.46,0-54.44,14.56-66.14,26.76a86,86,0,0,0-23.69,58.94c0,22.64,8.81,43.48,24.8,58.67,15.7,14.92,36.9,23.14,59.68,23.14,23.81,0,46-8.49,62.49-23.91,17-15.9,26.37-37.93,26.37-62C228.4,120.37,185.94,89.86,144.89,89.86Zm.49,153.67a61.49,61.49,0,0,1-46.45-20.4c-12.33-13.76-18.85-32.64-18.85-54.62,0-20.7,6.07-37.67,17.57-49.07,10.11-10,24.39-15.62,40.19-15.74,19,0,35.22,6.56,46.76,19,12.6,13.58,19.27,34,19.27,58.95C203.87,224.39,174.49,243.53,145.38,243.53Z"/>
<polygon class="cls-1" points="198.75 74.96 179.45 74.96 142.09 37.83 104.51 74.96 86.14 74.96 138.09 24.25 146.81 24.25 198.75 74.96"/>
</svg>

After

Width:  |  Height:  |  Size: 855 B

View File

@@ -0,0 +1,25 @@
<script setup lang="ts">
const { framework, frameworks } = useSharedData()
</script>
<template>
<UDropdownMenu
v-slot="{ open }"
:modal="false"
:items="frameworks"
:ui="{ content: 'w-(--radix-dropdown-menu-trigger-width)' }"
>
<UButton
color="neutral"
variant="outline"
v-bind="frameworks.find(f => f.value === framework)"
block
trailing-icon="i-lucide-chevron-down"
:class="[open && 'bg-[var(--ui-bg-elevated)]']"
:ui="{
trailingIcon: ['transition-transform duration-200', open ? 'rotate-180' : undefined].filter(Boolean).join(' ')
}"
class="-mx-2 w-[calc(100%+1rem)]"
/>
</UDropdownMenu>
</template>

View File

@@ -42,7 +42,7 @@ defineShortcuts({
<UButton
color="neutral"
variant="ghost"
to="https://github.com/nuxt/ui/tree/v3"
to="https://github.com/nuxt/ui"
target="_blank"
icon="i-simple-icons-github"
aria-label="GitHub"
@@ -55,6 +55,8 @@ defineShortcuts({
<USeparator type="dashed" class="my-4" />
<FrameworkSelect class="mb-4" />
<UContentNavigation :navigation="navigation" highlight />
</template>
</UHeader>

View File

@@ -3,9 +3,42 @@
import json5 from 'json5'
import { upperFirst, camelCase, kebabCase } from 'scule'
import { hash } from 'ohash'
import { CalendarDate } from '@internationalized/date'
import * as theme from '#build/ui'
import { get, set } from '#ui/utils'
interface Cast {
get: (args: any) => any
template: (args: any) => string
}
type CastDateValue = [number, number, number]
const castMap: Record<string, Cast> = {
'DateValue': {
get: (args: CastDateValue) => new CalendarDate(...args),
template: (value: CalendarDate) => {
return value ? `new CalendarDate(${value.year}, ${value.month}, ${value.day})` : 'null'
}
},
'DateValue[]': {
get: (args: CastDateValue[]) => args.map(date => new CalendarDate(...date)),
template: (value: CalendarDate[]) => {
return value ? `[${value.map(date => `new CalendarDate(${date.year}, ${date.month}, ${date.day})`).join(', ')}]` : '[]'
}
},
'DateRange': {
get: (args: { start: CastDateValue, end: CastDateValue }) => ({ start: new CalendarDate(...args.start), end: new CalendarDate(...args.end) }),
template: (value: { start: CalendarDate, end: CalendarDate }) => {
if (!value.start || !value.end) {
return `{ start: null, end: null }`
}
return `{ start: new CalendarDate(${value.start.year}, ${value.start.month}, ${value.start.day}), end: new CalendarDate(${value.end.year}, ${value.end.month}, ${value.end.day}) }`
}
}
}
const props = defineProps<{
/** Override the slug taken from the route */
slug?: string
@@ -18,6 +51,8 @@ const props = defineProps<{
external?: string[]
/** List of props to use with `v-model` */
model?: string[]
/** List of props to cast from code and selection */
cast?: { [key: string]: string }
/** List of items for each prop */
items?: { [key: string]: string[] }
props?: { [key: string]: any }
@@ -45,7 +80,17 @@ const camelName = camelCase(props.slug ?? route.params.slug?.[route.params.slug.
const name = `U${upperFirst(camelName)}`
const component = defineAsyncComponent(() => import(`#ui/components/${upperFirst(camelName)}.vue`))
const componentProps = reactive({ ...(props.props || {}) })
const componentProps = reactive({
...Object.fromEntries(Object.entries(props.props || {}).map(([key, value]) => {
const cast = props.cast?.[key]
if (cast && !castMap[cast]) {
throw new Error(`Unknown cast: ${cast}`)
}
return [key, cast ? castMap[cast]!.get(value) : value]
}))
})
const componentEvents = reactive({
...Object.fromEntries((props.model || []).map(key => [`onUpdate:${key}`, (e: any) => setComponentProp(key, e)])),
...(componentProps.modelValue ? { [`onUpdate:modelValue`]: (e: any) => setComponentProp('modelValue', e) } : {})
@@ -96,7 +141,7 @@ const options = computed(() => {
return {
name: key,
label: key,
type: prop?.type,
type: props?.cast?.[key] ?? prop?.type,
items
}
})
@@ -117,7 +162,10 @@ const code = computed(() => {
<script setup lang="ts">
`
for (const key of props.external) {
code += `const ${key === 'modelValue' ? 'value' : key} = ref(${json5.stringify(componentProps[key], null, 2).replace(/,([ |\t\n]+[}|\]])/g, '$1')})
const cast = props.cast?.[key]
const value = cast ? castMap[cast]!.template(componentProps[key]) : json5.stringify(componentProps[key], null, 2).replace(/,([ |\t\n]+[}|\]])/g, '$1')
code += `const ${key === 'modelValue' ? 'value' : key} = ref(${value})
`
}
code += `<\/script>
@@ -164,7 +212,7 @@ const code = computed(() => {
continue
}
code += ` ${prop?.type.includes('number') ? ':' : ''}${name}="${value}"`
code += ` ${typeof value === 'number' ? ':' : ''}${name}="${value}"`
}
}
@@ -220,7 +268,7 @@ const { data: ast } = await useAsyncData(`component-code-${name}-${hash({ props:
<template>
<div class="my-5">
<div>
<div v-if="options.length" class="flex items-center gap-2.5 border border-[var(--ui-color-neutral-200)] dark:border-[var(--ui-color-neutral-700)] border-b-0 relative rounded-t-[calc(var(--ui-radius)*1.5)] px-4 py-2.5 overflow-x-auto">
<div v-if="options.length" class="flex items-center gap-2.5 border border-[var(--ui-border-muted)] border-b-0 relative rounded-t-[calc(var(--ui-radius)*1.5)] px-4 py-2.5 overflow-x-auto">
<template v-for="option in options" :key="option.name">
<UFormField
:label="option.label"
@@ -269,7 +317,7 @@ const { data: ast } = await useAsyncData(`component-code-${name}-${hash({ props:
</template>
</div>
<div v-if="component" class="flex justify-center border border-b-0 border-[var(--ui-color-neutral-200)] dark:border-[var(--ui-color-neutral-700)] relative p-4 z-[1]" :class="[!options.length && 'rounded-t-[calc(var(--ui-radius)*1.5)]', props.class]">
<div v-if="component" class="flex justify-center border border-b-0 border-[var(--ui-border-muted)] relative p-4 z-[1]" :class="[!options.length && 'rounded-t-[calc(var(--ui-radius)*1.5)]', props.class]">
<component :is="component" v-bind="{ ...componentProps, ...componentEvents }">
<template v-for="slot in Object.keys(slots || {})" :key="slot" #[slot]>
<MDCSlot :name="slot" unwrap="p">

View File

@@ -117,8 +117,8 @@ const optionsValues = ref(props.options?.reduce((acc, option) => {
<template>
<div class="my-5">
<template v-if="preview">
<div class="border border-[var(--ui-color-neutral-200)] dark:border-[var(--ui-color-neutral-700)] relative z-[1]" :class="[{ 'border-b-0 rounded-t-[calc(var(--ui-radius)*1.5)]': props.source, 'rounded-[calc(var(--ui-radius)*1.5)]': !props.source }]">
<div v-if="props.options?.length || !!slots.options" class="flex gap-4 p-4 border-b border-[var(--ui-color-neutral-200)] dark:border-[var(--ui-color-neutral-700)]">
<div class="border border-[var(--ui-border-muted)] relative z-[1]" :class="[{ 'border-b-0 rounded-t-[calc(var(--ui-radius)*1.5)]': props.source, 'rounded-[calc(var(--ui-radius)*1.5)]': !props.source }]">
<div v-if="props.options?.length || !!slots.options" class="flex gap-4 p-4 border-b border-[var(--ui-border-muted)]">
<slot name="options" />
<UFormField

View File

@@ -35,7 +35,7 @@ const schemaProps = computed(() => {
</script>
<template>
<Collapsible v-if="schemaProps?.length" class="mt-1">
<ProseCollapsible v-if="schemaProps?.length" class="mt-1">
<ProseUl>
<ProseLi v-for="schemaProp in schemaProps" :key="schemaProp.name">
<HighlightInlineType :type="`${schemaProp.name}${schemaProp.required === false ? '?' : ''}: ${schemaProp.type}`" />
@@ -43,5 +43,5 @@ const schemaProps = computed(() => {
<MDC v-if="schemaProp.description" :value="schemaProp.description" class="text-[var(--ui-text-muted)] my-1" />
</ProseLi>
</ProseUl>
</Collapsible>
</ProseCollapsible>
</template>

View File

@@ -4,14 +4,19 @@ import { camelCase } from 'scule'
import * as theme from '#build/ui'
const route = useRoute()
const { framework } = useSharedData()
const name = camelCase(route.params.slug?.[route.params.slug.length - 1] ?? '')
const strippedCompoundVariants = ref(false)
function stripCompoundVariants(component?: any) {
if (component?.compoundVariants) {
component.compoundVariants = component.compoundVariants.filter((compoundVariant: any) => {
const strippedTheme = computed(() => {
const strippedTheme = {
...(theme as any)[name]
}
if (strippedTheme?.compoundVariants) {
strippedTheme.compoundVariants = strippedTheme.compoundVariants.filter((compoundVariant: any) => {
if (compoundVariant.color) {
if (!['primary', 'neutral'].includes(compoundVariant.color)) {
strippedCompoundVariants.value = true
@@ -40,24 +45,43 @@ function stripCompoundVariants(component?: any) {
})
}
return component
}
return strippedTheme
})
const component = computed(() => {
return {
ui: {
[name]: stripCompoundVariants((theme as any)[name])
[name]: strippedTheme.value
}
}
})
const { data: ast } = await useAsyncData(`component-theme-${name}`, async () => {
const { data: ast } = await useAsyncData(`component-theme-${name}-${framework.value}`, async () => {
const md = `
::code-collapse
${framework.value === 'nuxt'
? `
\`\`\`ts [app.config.ts]
export default defineAppConfig(${json5.stringify(component.value, null, 2).replace(/,([ |\t\n]+[}|\])])/g, '$1')})
\`\`\`\
`
: `
\`\`\`ts [vite.config.ts]
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'
export default defineConfig({
plugins: [
vue(),
ui(${json5.stringify(component.value, null, 2).replace(/,([ |\t\n]+[}|\])])/g, '$1')
.split('\n')
.map((line, i) => i === 0 ? line : ` ${line}`)
.join('\n')})
]
})
\`\`\`
`}
::
${strippedCompoundVariants.value
@@ -69,7 +93,7 @@ Some colors in \`compoundVariants\` are omitted for readability. Check out the s
`
return parseMarkdown(md)
})
}, { watch: [framework] })
</script>
<template>

View File

@@ -0,0 +1,7 @@
<script setup lang="ts">
const { framework } = useSharedData()
</script>
<template>
<slot :name="framework" />
</template>

View File

@@ -2,8 +2,13 @@
import json5 from 'json5'
import icons from '../../../../src/theme/icons'
const { data: ast } = await useAsyncData(`icons-theme`, async () => {
const { framework } = useSharedData()
const { data: ast } = await useAsyncData(`icons-theme-${framework.value}`, async () => {
const md = `
::code-collapse
${framework.value === 'nuxt'
? `
\`\`\`ts [app.config.ts]
export default defineAppConfig(${json5.stringify({
ui: {
@@ -11,10 +16,33 @@ export default defineAppConfig(${json5.stringify({
}
}, null, 2).replace(/,([ |\t\n]+[}|\])])/g, '$1')})
\`\`\`\
`
: `
\`\`\`ts [vite.config.ts]
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'
export default defineConfig({
plugins: [
vue(),
ui(${json5.stringify({
ui: {
icons
}
}, null, 2).replace(/,([ |\t\n]+[}|\])])/g, '$1')
.split('\n')
.map((line, i) => i === 0 ? line : ` ${line}`)
.join('\n')})
]
})
\`\`\`
`}
::
`
return parseMarkdown(md)
})
}, { watch: [framework] })
</script>
<template>

View File

@@ -1,34 +1,53 @@
<script setup lang="ts">
import type { Locale } from '@nuxt/ui'
import * as locales from '@nuxt/ui/locale'
type LocaleKey = keyof typeof locales
type LocaleComputed = Locale & { flag: string }
const props = withDefaults(defineProps<{
default?: string
}>(), {
default: 'en'
})
const getLocaleKeys = () => Object.keys(locales) as Array<keyof typeof locales>
const localesList = getLocaleKeys().map(locale => [locale, locales[locale].name])
const countries = await $fetch('/api/locales.json')
const getLocaleKeys = Object.keys(locales) as LocaleKey[]
const localesList = getLocaleKeys.map<LocaleComputed>((code) => {
const locale: Locale = locales[code]
return {
...locale,
flag: countries[locale.code] || ''
}
})
</script>
<!-- eslint-disable vue/singleline-html-element-content-newline -->
<template>
<div>
<ProseUl>
<ProseLi v-for="[key, label] in localesList" :key="key">
<ProseCode>{{ key }}</ProseCode> - {{ label }}
<template v-if="key === props.default">
(default)
</template>
</ProseLi>
</ProseUl>
<Note to="https://github.com/nuxt/ui/tree/v3/src/runtime/locale" target="_blank">
<ProseP>
By default, the <ProseCode>{{ props.default }}</ProseCode> locale is used.
</ProseP>
<div class="grid gap-6 grid-cols-2 md:grid-cols-3">
<div v-for="locale in localesList" :key="locale.code">
<div class="flex gap-3 items-center">
<UAvatar :text="locale.flag" size="xl" />
<div class="text-sm">
<div class="font-semibold">{{ locale.name }}</div>
<div class="mt-1">Code: <ProseCode class="text-xs">{{ locale.code }}</ProseCode></div>
</div>
</div>
</div>
</div>
<ProseNote to="https://github.com/nuxt/ui/tree/v3/src/runtime/locale" target="_blank">
If you need additional languages, you can contribute by creating a PR to add a new locale in <ProseCode>src/runtime/locale/</ProseCode>.
</Note>
<Tip>
</ProseNote>
<ProseTip>
You can use the <ProseCode>nuxt-ui</ProseCode> CLI to create a new locale:
<ProsePre language="bash">nuxt-ui make locale --code "en" --name "English"</ProsePre>
</Tip>
</ProseTip>
</div>
</template>

View File

@@ -0,0 +1,21 @@
<script setup lang="ts">
import { CalendarDate, DateFormatter, getLocalTimeZone } from '@internationalized/date'
const df = new DateFormatter('en-US', {
dateStyle: 'medium'
})
const modelValue = shallowRef(new CalendarDate(2022, 1, 10))
</script>
<template>
<UPopover>
<UButton color="neutral" variant="subtle" icon="i-lucide-calendar">
{{ df.format(modelValue.toDate(getLocalTimeZone())) }}
</UButton>
<template #content>
<UCalendar v-model="modelValue" class="p-2" />
</template>
</UPopover>
</template>

View File

@@ -0,0 +1,35 @@
<script setup lang="ts">
import { CalendarDate, DateFormatter, getLocalTimeZone } from '@internationalized/date'
const df = new DateFormatter('en-US', {
dateStyle: 'medium'
})
const modelValue = shallowRef({
start: new CalendarDate(2022, 1, 20),
end: new CalendarDate(2022, 2, 10)
})
</script>
<template>
<UPopover>
<UButton color="neutral" variant="subtle" icon="i-lucide-calendar">
<template v-if="modelValue.start">
<template v-if="modelValue.end">
{{ df.format(modelValue.start.toDate(getLocalTimeZone())) }} - {{ df.format(modelValue.end.toDate(getLocalTimeZone())) }}
</template>
<template v-else>
{{ df.format(modelValue.start.toDate(getLocalTimeZone())) }}
</template>
</template>
<template v-else>
Pick a date
</template>
</UButton>
<template #content>
<UCalendar v-model="modelValue" class="p-2" :number-of-months="2" range />
</template>
</UPopover>
</template>

View File

@@ -0,0 +1,17 @@
<script setup lang="ts">
import { CalendarDate } from '@internationalized/date'
import type { Matcher } from 'radix-vue/date'
const modelValue = shallowRef({
start: new CalendarDate(2022, 1, 1),
end: new CalendarDate(2022, 1, 9)
})
const isDateDisabled: Matcher = (date) => {
return date.day >= 10 && date.day <= 16
}
</script>
<template>
<UCalendar v-model="modelValue" :is-date-disabled="isDateDisabled" range />
</template>

View File

@@ -0,0 +1,30 @@
<script setup lang="ts">
import { CalendarDate } from '@internationalized/date'
const modelValue = shallowRef(new CalendarDate(2022, 1, 10))
function getColorByDate(date: Date) {
const isWeekend = date.getDay() % 6 == 0
const isDayMeeting = date.getDay() % 3 == 0
if (isWeekend) {
return undefined
}
if (isDayMeeting) {
return 'error'
}
return 'success'
}
</script>
<template>
<UCalendar v-model="modelValue">
<template #day="{ day }">
<UChip :show="!!getColorByDate(day.toDate('UTC'))" :color="getColorByDate(day.toDate('UTC'))" size="2xs">
{{ day.day }}
</UChip>
</template>
</UCalendar>
</template>

View File

@@ -0,0 +1,11 @@
<script setup lang="ts">
import { CalendarDate } from '@internationalized/date'
const modelValue = shallowRef(new CalendarDate(2023, 9, 10))
const minDate = new CalendarDate(2023, 9, 1)
const maxDate = new CalendarDate(2023, 9, 30)
</script>
<template>
<UCalendar v-model="modelValue" :min-value="minDate" :max-value="maxDate" />
</template>

View File

@@ -0,0 +1,17 @@
<script setup lang="ts">
import { CalendarDate } from '@internationalized/date'
import type { Matcher } from 'radix-vue/date'
const modelValue = shallowRef({
start: new CalendarDate(2022, 1, 1),
end: new CalendarDate(2022, 1, 9)
})
const isDateUnavailable: Matcher = (date) => {
return date.day >= 10 && date.day <= 16
}
</script>
<template>
<UCalendar v-model="modelValue" :is-date-unavailable="isDateUnavailable" range />
</template>

View File

@@ -32,7 +32,7 @@ const items = computed(() => [{
</script>
<template>
<UContextMenu :items="items" class="w-48">
<UContextMenu :items="items" :ui="{ content: 'w-48' }">
<div class="flex items-center justify-center rounded-md border border-dashed border-[var(--ui-border-accented)] text-sm aspect-video w-72">
Right click here
</div>

View File

@@ -25,7 +25,7 @@ const items = [
</script>
<template>
<UContextMenu :items="items" class="w-48">
<UContextMenu :items="items" :ui="{ content: 'w-48' }">
<div class="flex items-center justify-center rounded-md border border-dashed border-[var(--ui-border-accented)] text-sm aspect-video w-72">
Right click here
</div>

View File

@@ -12,7 +12,7 @@ const items = [{
</script>
<template>
<UContextMenu :items="items" class="w-48">
<UContextMenu :items="items" :ui="{ content: 'w-48' }">
<div class="flex items-center justify-center rounded-md border border-dashed border-[var(--ui-border-accented)] text-sm aspect-video w-72">
Right click here
</div>

View File

@@ -40,7 +40,7 @@ const items = computed(() => [{
</script>
<template>
<UDropdownMenu :items="items" :content="{ align: 'start' }" class="w-48">
<UDropdownMenu :items="items" :content="{ align: 'start' }" :ui="{ content: 'w-48' }">
<UButton label="Open" color="neutral" variant="outline" icon="i-lucide-menu" />
</UDropdownMenu>
</template>

View File

@@ -25,7 +25,7 @@ const items = [
</script>
<template>
<UDropdownMenu :items="items" class="w-48">
<UDropdownMenu :items="items" :ui="{ content: 'w-48' }">
<UButton label="Open" color="neutral" variant="outline" icon="i-lucide-menu" />
<template #profile-trailing>

View File

@@ -13,7 +13,7 @@ const items = [{
</script>
<template>
<UDropdownMenu :items="items" class="w-48">
<UDropdownMenu :items="items" :ui="{ content: 'w-48' }">
<UButton label="Open" color="neutral" variant="outline" icon="i-lucide-menu" />
<template #profile-trailing>

View File

@@ -18,7 +18,7 @@ const items = [{
</script>
<template>
<UDropdownMenu v-model:open="open" :items="items" class="w-48">
<UDropdownMenu v-model:open="open" :items="items" :ui="{ content: 'w-48' }">
<UButton label="Open" color="neutral" variant="outline" icon="i-lucide-menu" />
</UDropdownMenu>
</template>

View File

@@ -4,6 +4,7 @@ import type { FormSubmitEvent } from '@nuxt/ui'
const schema = z.object({
input: z.string().min(10),
inputNumber: z.number().min(10),
inputMenu: z.any().refine(option => option?.value === 'option-2', {
message: 'Select Option 2'
}),
@@ -29,10 +30,11 @@ const schema = z.object({
radioGroup: z.string().refine(value => value === 'option-2', {
message: 'Select Option 2'
}),
slider: z.number().max(20, { message: 'Must be less than 20' })
slider: z.number().max(20, { message: 'Must be less than 20' }),
pin: z.string().regex(/^\d$/).array().length(5)
})
type Schema = z.output<typeof schema>
type Schema = z.input<typeof schema>
const state = reactive<Partial<Schema>>({})
@@ -52,10 +54,10 @@ async function onSubmit(event: FormSubmitEvent<any>) {
</script>
<template>
<UForm ref="form" :state="state" :schema="schema" @submit="onSubmit">
<UForm ref="form" :state="state" :schema="schema" class="w-full" @submit="onSubmit">
<div class="grid grid-cols-3 gap-4">
<UFormField label="Input" name="input">
<UInput v-model="state.input" placeholder="john@lennon.com" class="w-40" />
<UInput v-model="state.input" placeholder="john@lennon.com" class="w-full" />
</UFormField>
<div class="flex flex-col gap-4">
@@ -73,34 +75,40 @@ async function onSubmit(event: FormSubmitEvent<any>) {
</UFormField>
<UFormField name="select" label="Select">
<USelect v-model="state.select" :items="items" class="w-48" />
<USelect v-model="state.select" :items="items" class="w-full" />
</UFormField>
<UFormField name="selectMenu" label="Select Menu">
<USelectMenu v-model="state.selectMenu" :items="items" class="w-48" />
<USelectMenu v-model="state.selectMenu" :items="items" class="w-full" />
</UFormField>
<UFormField name="selectMenuMultiple" label="Select Menu (Multiple)">
<USelectMenu v-model="state.selectMenuMultiple" multiple :items="items" class="w-48" />
<USelectMenu v-model="state.selectMenuMultiple" multiple :items="items" class="w-full" />
</UFormField>
<UFormField name="inputMenu" label="Input Menu">
<UInputMenu v-model="state.inputMenu" :items="items" />
<UInputMenu v-model="state.inputMenu" :items="items" class="w-full" />
</UFormField>
<UFormField name="inputMenuMultiple" label="Input Menu (Multiple)">
<UInputMenu v-model="state.inputMenuMultiple" multiple :items="items" />
<UInputMenu v-model="state.inputMenuMultiple" multiple :items="items" class="w-full" />
</UFormField>
<span />
<UFormField name="inputNumber" label="Input Number">
<UInputNumber v-model="state.inputNumber" class="w-full" />
</UFormField>
<UFormField label="Textarea" name="textarea">
<UTextarea v-model="state.textarea" />
<UTextarea v-model="state.textarea" class="w-full" />
</UFormField>
<UFormField name="radioGroup">
<URadioGroup v-model="state.radioGroup" legend="Radio group" :items="items" />
</UFormField>
<UFormField name="pin" label="Pin Input" :error-pattern="/(pin)\..*/">
<UPinInput v-model="state.pin" />
</UFormField>
</div>
<div class="flex gap-2 mt-8">

View File

@@ -0,0 +1,15 @@
<script setup lang="ts">
const value = ref(1500)
</script>
<template>
<UInputNumber
v-model="value"
:format-options="{
style: 'currency',
currency: 'EUR',
currencyDisplay: 'code',
currencySign: 'accounting'
}"
/>
</template>

View File

@@ -0,0 +1,13 @@
<script setup lang="ts">
const value = ref(5)
</script>
<template>
<UInputNumber
v-model="value"
:format-options="{
signDisplay: 'exceptZero',
minimumFractionDigits: 1
}"
/>
</template>

View File

@@ -0,0 +1,9 @@
<script setup lang="ts">
const retries = ref(0)
</script>
<template>
<UFormField label="Retries" help="Specify number of attempts" required>
<UInputNumber v-model="retries" placeholder="Enter retries" />
</UFormField>
</template>

View File

@@ -0,0 +1,13 @@
<script setup lang="ts">
const value = ref(0.05)
</script>
<template>
<UInputNumber
v-model="value"
:step="0.01"
:format-options="{
style: 'percent'
}"
/>
</template>

View File

@@ -0,0 +1,15 @@
<script setup lang="ts">
const value = ref(5)
</script>
<template>
<UInputNumber v-model="value">
<template #decrement>
<UButton size="xs" icon="i-lucide-minus" />
</template>
<template #increment>
<UButton size="xs" icon="i-lucide-plus" />
</template>
</UInputNumber>
</template>

View File

@@ -6,7 +6,7 @@ const value = ref('Click to clear')
<UInput
v-model="value"
placeholder="Type something..."
:ui="{ trailing: 'pr-0.5' }"
:ui="{ trailing: 'pe-1' }"
>
<template v-if="value?.length" #trailing>
<UButton

View File

@@ -0,0 +1,33 @@
<script setup lang="ts">
const value = ref('npx nuxi module add ui')
const copied = ref(false)
function copy() {
navigator.clipboard.writeText(value.value)
copied.value = true
setTimeout(() => {
copied.value = false
}, 2000)
}
</script>
<template>
<UInput
v-model="value"
:ui="{ trailing: 'pr-0.5' }"
>
<template v-if="value?.length" #trailing>
<UTooltip text="Copy to clipboard" :content="{ side: 'right' }">
<UButton
:color="copied ? 'success' : 'neutral'"
variant="link"
size="sm"
:icon="copied ? 'i-lucide-copy-check' : 'i-lucide-copy'"
aria-label="Copy to clipboard"
@click="copy"
/>
</UTooltip>
</template>
</UInput>
</template>

View File

@@ -0,0 +1,21 @@
<script setup lang="ts">
const input = useTemplateRef('input')
defineShortcuts({
'/': () => {
input.value?.inputRef?.focus()
}
})
</script>
<template>
<UInput
ref="input"
icon="i-lucide-search"
placeholder="Search..."
>
<template #trailing>
<UKbd value="/" />
</template>
</UInput>
</template>

View File

@@ -40,7 +40,7 @@ const text = computed(() => {
placeholder="Password"
:color="color"
:type="show ? 'text' : 'password'"
:ui="{ trailing: 'pr-0.5' }"
:ui="{ trailing: 'pe-1' }"
:aria-invalid="score < 4"
aria-describedby="password-strength"
class="w-full"

View File

@@ -8,7 +8,7 @@ const password = ref('password')
v-model="password"
placeholder="Password"
:type="show ? 'text' : 'password'"
:ui="{ trailing: 'pr-0.5' }"
:ui="{ trailing: 'pe-1' }"
>
<template #trailing>
<UButton

View File

@@ -56,7 +56,7 @@ defineProps({
<h1 class="m-0 text-[75px] font-semibold mb-2 text-white flex items-center">
<span>{{ title }}</span>
</h1>
<p v-if="description" class="text-[32px] text-[#94a3b8] leading-tight">
<p v-if="description" class="text-[32px] text-[#94a3b8] leading-tight text-balance">
{{ description.slice(0, 200) }}
</p>
</div>

View File

@@ -0,0 +1,23 @@
import { createSharedComposable } from '@vueuse/core'
function _useSharedData() {
const framework = useCookie('nuxt-ui-framework', { default: () => 'nuxt' })
const frameworks = computed(() => [{
label: 'Nuxt',
icon: 'i-logos-nuxt-icon',
value: 'nuxt',
onSelect: () => framework.value = 'nuxt'
}, {
label: 'Vue',
icon: 'i-logos-vue',
value: 'vue',
onSelect: () => framework.value = 'vue'
}].map(f => ({ ...f, active: framework.value === f.value })))
return {
framework,
frameworks
}
}
export const useSharedData = createSharedComposable(_useSharedData)

View File

@@ -10,6 +10,10 @@ const navigation = inject<Ref<ContentNavigationItem[]>>('navigation')
<UPage>
<template #left>
<UPageAside>
<template #top>
<FrameworkSelect />
</template>
<UContentNavigation :navigation="navigation" highlight />
</UPageAside>
</template>

View File

@@ -21,10 +21,32 @@ const navigation = inject<Ref<ContentNavigationItem[]>>('navigation')
const breadcrumb = computed(() => mapContentNavigation(findPageBreadcrumb(navigation?.value, page.value)).map(({ icon, ...link }) => link))
const { framework } = useSharedData()
// Redirect to the correct framework version if the page is not the current framework
if (!import.meta.prerender) {
watch(framework, () => {
if (page.value?.navigation?.framework && page.value?.navigation?.framework !== framework.value) {
if (route.path.endsWith(`/${page.value?.navigation?.framework}`)) {
navigateTo(`${route.path.split('/').slice(0, -1).join('/')}/${framework.value}`)
} else {
navigateTo(`/getting-started`)
}
}
})
}
// Update the framework if the page has a different framework
watch(page, () => {
if (page.value?.navigation?.framework && page.value?.navigation?.framework !== framework.value) {
framework.value = page.value?.navigation?.framework as string
}
}, { immediate: true })
useSeoMeta({
titleTemplate: '%s - Nuxt UI v3',
title: typeof page.value.navigation === 'object' ? page.value.navigation.title : page.value.title,
ogTitle: `${typeof page.value.navigation === 'object' ? page.value.navigation.title : page.value.title} - Nuxt UI v3`,
title: typeof page.value.navigation === 'object' && page.value.navigation.title ? page.value.navigation.title : page.value.title,
ogTitle: `${typeof page.value.navigation === 'object' && page.value.navigation.title ? page.value.navigation.title : page.value.title} - Nuxt UI v3`,
description: page.value.description,
ogDescription: page.value.description
})
@@ -75,21 +97,6 @@ const communityLinks = computed(() => [{
</template>
<template #links>
<UDropdownMenu v-if="page.select" v-slot="{ open }" :items="page.select.items" :content="{ align: 'end' }">
<UButton
color="neutral"
variant="subtle"
v-bind="page.select.items.find((item: any) => item.to === route.path)"
block
trailing-icon="i-lucide-chevron-down"
:class="[open && 'bg-[var(--ui-bg-accented)]/75']"
:ui="{
trailingIcon: ['transition-transform duration-200', open ? 'rotate-180' : undefined].filter(Boolean).join(' ')
}"
class="w-[128px]"
/>
</UDropdownMenu>
<UButton
v-for="link in page.links"
:key="link.label"

View File

@@ -5,6 +5,10 @@ export const collections = {
type: 'page',
source: '**/*',
schema: z.object({
navigation: z.object({
title: z.string().optional(),
framework: z.string().optional()
}),
links: z.array(z.object({
label: z.string(),
icon: z.string(),
@@ -14,14 +18,7 @@ export const collections = {
}).optional(),
to: z.string(),
target: z.string().optional()
})),
select: z.object({
items: z.array(z.object({
label: z.string(),
icon: z.string(),
to: z.string()
}))
})
}))
})
})
}

View File

@@ -21,15 +21,15 @@ This transition empowers Nuxt UI to become a more comprehensive and flexible UI
### Tailwind CSS v4
Nuxt UI v3 integrates the latest Tailwind CSS v4 alpha (announced March 6, 2024), bringing significant improvements:
Nuxt UI v3 integrates the latest Tailwind CSS v4 beta (released Nov 21, 2024), bringing significant improvements:
- **Faster Builds**: Up to 10x faster, especially for larger projects.
- **Unified Toolchain**: Built-in features like vendor prefixing, nesting support, and modern CSS transforms.
- **CSS-First Configuration**: New `@theme` directive for easy customization without JavaScript.
- **Optimized Performance**: Smaller engine footprint and more efficient processing.
- **Built for performance**: Full builds in the new engine are up to 5x faster, and incremental builds are over 100x faster — and measured in microseconds.
- **Unified toolchain**: Built-in import handling, vendor prefixing, and syntax transforms, with no additional tooling required.
- **CSS-first configuration**: A reimagined developer experience where you customize and extend the framework directly in CSS instead of a JavaScript configuration file.
- **Designed for the modern web**: Built on native cascade layers, wide-gamut colors, and including first-class support for modern CSS features like container queries, @starting-style, popovers, and more.
::note
For a comprehensive overview of Tailwind CSS v4 alpha features, visit the [official announcement](https://tailwindcss.com/blog/tailwindcss-v4-alpha).
For a comprehensive overview of Tailwind CSS v4 beta features, read the [prerelease documentation](https://tailwindcss.com/docs/v4-beta).
::
### Tailwind Variants
@@ -71,7 +71,7 @@ You can now use Nuxt UI in any Vue project without Nuxt by adding the Vite and V
- **Developer Experience**: Complete TypeScript support with IntelliSense and auto-completion
::tip{to="/getting-started/installation/vue"}
Learn how to install and configure Nuxt UI in a Vue project in the [Vue installation guide](/getting-started/installation/vue).
Learn how to install and configure Nuxt UI in a Vue project in the **Vue installation guide**.
::
## Migration
@@ -90,6 +90,10 @@ Key points to consider:
The transition to v3 involves significant changes, including new component structures, updated theming approaches, and revised TypeScript definitions. We recommend a careful, incremental upgrade process, starting with thorough testing in a development environment.
::
::accordion-item{label="Is Nuxt UI v3 compatible with standalone Vue projects?"}
Nuxt UI is now compatible with Vue! You can follow the [installation guide](/getting-started/installation/vue) to get started.
::
::accordion-item{label="What about Nuxt UI Pro?"}
We've also rebuilt Nuxt UI Pro from scratch and released a `v3.0.0-alpha.x` package but it only contains the components to build this documentation yet. This will be a free update, so the license you buy now will be valid for v3. We're actively working to finish the rewrite of all Nuxt UI Pro components.
::
@@ -98,16 +102,12 @@ Key points to consider:
Nuxt UI v3 is currently designed to work exclusively with Tailwind CSS. While there's interest in UnoCSS support, implementing it would require significant changes to the theme structure due to differences in class naming conventions. As a result, we don't have plans to add UnoCSS support in v3.
::
::accordion-item{label="Is Nuxt UI v3 compatible with standalone Vue projects?"}
We're planning to add Vue support in the near future. For now, Nuxt UI v3 is only available for Nuxt. Track progress on Vue compatibility [in this issue](https://github.com/nuxt/ui/issues/2129).
::
::accordion-item{label="How does Nuxt UI v3 handle accessibility?"}
Nuxt UI v3 enhances accessibility through Radix Vue integration. This provides automatic ARIA attributes, keyboard navigation support, intelligent focus management, and screen reader announcements. While offering a strong foundation, proper implementation and testing in your specific use case remains crucial for full accessibility compliance. For more detailed information, refer to [Radix Vue's accessibility documentation](https://www.radix-vue.com/overview/accessibility.html).
::
::accordion-item{label="What is the testing approach for Nuxt UI v3?"}
Nuxt UI v3 ensures reliability with 800+ Vitest tests, covering core functionality and accessibility. This robust testing suite supports the library's stability and serves as a reference for developers.
Nuxt UI v3 ensures reliability with 1000+ Vitest tests, covering core functionality and accessibility. This robust testing suite supports the library's stability and serves as a reference for developers.
::
::accordion-item{label="Is this version stable and suitable for production use?"}

View File

@@ -0,0 +1 @@
shadow: true

View File

@@ -1,17 +1,13 @@
---
title: Install in a Nuxt app
navigation.title: Nuxt
title: Installation
description: 'Learn how to install and configure Nuxt UI in your Nuxt application.'
select:
items:
- label: Nuxt
icon: i-logos-nuxt-icon
to: /getting-started/installation/nuxt
- label: Vue
icon: i-logos-vue
to: /getting-started/installation/vue
navigation.framework: nuxt
---
::callout{to="/getting-started/installation/vue" icon="i-logos-vue" class="hidden"}
Looking for the **Vue** version?
::
## Setup
::steps{level="4"}

View File

@@ -1,17 +1,13 @@
---
navigation.title: Vue
title: Install in a Vue app
title: Installation
description: 'Learn how to install and configure Nuxt UI in your Vue application.'
select:
items:
- label: Nuxt
icon: i-logos-nuxt-icon
to: /getting-started/installation/nuxt
- label: Vue
icon: i-logos-vue
to: /getting-started/installation/vue
navigation.framework: vue
---
::callout{to="/getting-started/installation/nuxt" icon="i-logos-nuxt-icon" class="hidden"}
Looking for the **Nuxt** version?
::
## Setup
::steps{level="4"}
@@ -156,10 +152,6 @@ export default defineConfig({
Use the `ui` option to provide configuration for Nuxt UI.
::warning
In the rest of the docs, there are references to the `app.config.ts` file (which is a Nuxt feature). When using Nuxt UI in a project without Nuxt, this configuration is passed in this `ui` option instead.
::
```ts [vite.config.ts]
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

View File

@@ -5,7 +5,7 @@ description: 'Learn how to customize Nuxt UI components using Tailwind CSS v4, C
## Tailwind CSS
Nuxt UI v3 uses Tailwind CSS v4 alpha which doesn't have a documentation yet, let's have a look on how to use it.
Nuxt UI v3 uses Tailwind CSS v4 beta, you can read the [prerelease documentation](https://tailwindcss.com/docs/v4-beta) for more information.
### `@theme`
@@ -16,7 +16,7 @@ Tailwind CSS v4 takes a CSS-first configuration approach, you now customize your
@import "@nuxt/ui";
@theme {
--font-family-sans: 'Public Sans', sans-serif;
--font-sans: 'Public Sans', sans-serif;
--breakpoint-3xl: 1920px;
@@ -36,8 +36,8 @@ Tailwind CSS v4 takes a CSS-first configuration approach, you now customize your
The `@theme` directive tells Tailwind to make new utilities and variants available based on these variables. It's the equivalent of the `theme.extend` key in Tailwind CSS v3 `tailwind.config.ts` file.
::note
You can learn more about this on [https://tailwindcss.com/blog/tailwindcss-v4-alpha](https://tailwindcss.com/blog/tailwindcss-v4-alpha#css-first-configuration).
::note{to="https://tailwindcss.com/docs/v4-beta#css-first-configuration" target="_blank"}
Learn more about Tailwind CSS v4 CSS-first configuration approach.
::
### `@source`
@@ -50,11 +50,11 @@ This can be useful when writing Tailwind classes in markdown files with [`@nuxt/
@import "tailwindcss";
@import "@nuxt/ui";
@source "../content/**/*.md";
@source "../content";
```
::note{to="https://github.com/tailwindlabs/tailwindcss/pull/14078"}
You can learn more about the `@source` directive in this pull request.
::note{to="https://tailwindcss.com/docs/v4-beta#adding-content-sources"}
Learn how to add content sources in Tailwind CSS v4.
::
### `@plugin`
@@ -68,8 +68,8 @@ You can use the `@plugin` directive to import Tailwind CSS plugins.
@plugin "@tailwindcss/typography";
```
::note{to="https://github.com/tailwindlabs/tailwindcss/pull/14264"}
You can learn more about the `@plugin` directive in this pull request.
::note{to="https://tailwindcss.com/docs/v4-beta#using-plugins"}
Learn more about using plugins in Tailwind CSS v4.
::
## Design system
@@ -78,8 +78,14 @@ Nuxt UI extends Tailwind CSS's theming capabilities, providing a flexible design
### Colors
::framework-only
#nuxt
Nuxt UI leverages Nuxt [App Config](https://nuxt.com/docs/guide/directory-structure/app-config#app-config-file) to provide customizable color aliases based on [Tailwind CSS colors](https://tailwindcss.com/docs/customizing-colors#color-palette-reference):
#vue
Nuxt UI leverages Vite config to provide customizable color aliases based on [Tailwind CSS colors](https://tailwindcss.com/docs/customizing-colors#color-palette-reference):
::
| Color | Default | Description |
| --- | --- | --- |
| `primary`{color="primary"} | `green` | Main brand color, used as the default color for components. |
@@ -90,6 +96,8 @@ Nuxt UI leverages Nuxt [App Config](https://nuxt.com/docs/guide/directory-struct
| `error`{color="error"} | `red` | Used for form error validation states. |
| `neutral` | `slate` | Neutral color for backgrounds, text, etc. |
::framework-only
#nuxt
You can configure these color aliases at runtime in your `app.config.ts` file under the `ui.colors` key, allowing for dynamic theme customization without requiring an application rebuild:
```ts [app.config.ts]
@@ -103,6 +111,30 @@ export default defineAppConfig({
})
```
#vue
You can configure these color aliases at runtime in your `vite.config.ts` file under the `ui.colors` key:
```ts [vite.config.ts]
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'
export default defineConfig({
plugins: [
vue(),
ui({
ui: {
colors: {
primary: 'blue',
neutral: 'zinc'
}
}
})
]
})
```
::
::note
Try the :prose-icon{name="i-lucide-swatch-book" class="text-[var(--ui-primary)]"} theme picker in the header above to change `primary` and `neutral` colors.
::
@@ -118,8 +150,10 @@ slots:
---
::
::tip
You can add you own dynamic color aliases in your `app.config.ts`, you just have to make sure to define them in the [`ui.theme.colors`](/getting-started/installation#themecolors) option in your `nuxt.config.ts` file.
::framework-only
#nuxt
:::tip
You can add you own dynamic color aliases in your `app.config.ts`, you just have to make sure to define them in the [`ui.theme.colors`](/getting-started/installation/nuxt#themecolors) option in your `nuxt.config.ts` file.
```ts [app.config.ts]
export default defineAppConfig({
@@ -140,7 +174,34 @@ export default defineNuxtConfig({
}
})
```
:::
#vue
:::tip
You can add you own dynamic color aliases in your `vite.config.ts`, you just have to make sure to also define them in the [`theme.colors`](/getting-started/installation/vue#themecolors) option of the `ui` plugin.
```ts [vite.config.ts]
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'
export default defineConfig({
plugins: [
vue(),
ui({
ui: {
colors: {
tertiary: 'indigo'
}
},
theme: {
colors: ['primary', 'secondary', 'tertiary', 'info', 'success', 'warning', 'error']
}
})
]
})
```
:::
::
::warning
@@ -490,7 +551,19 @@ props:
---
::
The `defaultVariants` property specifies the default values for each variant. It determines how a component looks and behaves when no prop is provided. These default values can be customized in your [`app.config.ts`](#appconfigts) to adjust the standard appearance of components throughout your application.
The `defaultVariants` property specifies the default values for each variant. It determines how a component looks and behaves when no prop is provided.
::framework-only
#nuxt
:::tip
These default values can be customized in your [`app.config.ts`](#config) to adjust the standard appearance of components throughout your application.
:::
#vue
:::tip
These default values can be customized in your [`vite.config.ts`](#config) to adjust the standard appearance of components throughout your application.
:::
::
## Customize theme
@@ -507,9 +580,11 @@ You can explore the theme for each component in two ways:
- Browse the source code directly in the GitHub repository at https://github.com/nuxt/ui/tree/v3/src/theme.
::
### `app.config.ts`
### Config
You can override the theme of components inside your `app.config.ts` by using the exact same structure as the theme object.
::framework-only
#nuxt
You can override the theme of components globally inside your `app.config.ts` by using the exact same structure as the theme object.
Let's say you want to change the font weight of all your buttons, you can do it like this:
@@ -525,13 +600,42 @@ export default defineAppConfig({
})
```
#vue
You can override the theme of components globally inside your `vite.config.ts` by using the exact same structure as the theme object.
Let's say you want to change the font weight of all your buttons, you can do it like this:
```ts [vite.config.ts]
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'
export default defineConfig({
plugins: [
vue(),
ui({
ui: {
button: {
slots: {
base: 'font-bold'
}
}
}
})
]
})
```
::
::note
In this example, the `font-bold` class will override the default `font-medium` class on all buttons.
::
### `ui` prop
### Props
You can also override a component's **slots** using the `ui` prop. This has priority over the `app.config.ts` configuration and `variants` resolution.
#### `ui` prop
You can also override a component's **slots** using the `ui` prop. This has priority over the global configuration and `variants` resolution.
::component-code{slug="button"}
---
@@ -560,9 +664,9 @@ slots:
In this example, the `trailingIcon` slot is overwritten with `size-3` even though the `md` size variant would apply a `size-5` class to it.
::
### `class` prop
#### `class` prop
The `class` prop allows you to override the classes of the `root` or `base` slot. This has priority over the `app.config.ts` configuration and `variants` resolution.
The `class` prop allows you to override the classes of the `root` or `base` slot. This has priority over the global configuration and `variants` resolution.
::component-code{slug="button"}
---

View File

@@ -0,0 +1 @@
shadow: true

View File

@@ -1,6 +1,7 @@
---
title: Icons
description: 'Nuxt UI integrates with Nuxt Icon to access over 200,000+ icons from Iconify.'
navigation.framework: nuxt
links:
- label: 'Iconify'
to: https://iconify.design/
@@ -12,14 +13,14 @@ links:
icon: i-simple-icons-github
---
::callout{to="/getting-started/icons/vue" icon="i-logos-vue" class="hidden"}
Looking for the **Vue** version?
::
## Usage
Nuxt UI automatically registers the [`@nuxt/icon`](https://github.com/nuxt/icon) module for you, so there's no additional setup required.
::note
You can use any name from the https://icones.js.org collection.
::
### Icon Component
You can use the [Icon](/components/icon) component with a `name` prop to display an icon:
@@ -32,6 +33,10 @@ props:
---
::
::note
You can use any name from the https://icones.js.org collection.
::
### Component Props
Some components also have an `icon` prop to display an icon, like the [Button](/components/button) for example:

View File

@@ -0,0 +1,55 @@
---
title: Icons
description: 'Nuxt UI integrates with Iconify to access over 200,000+ icons.'
navigation.framework: vue
links:
- label: 'Iconify'
to: https://iconify.design/
target: _blank
icon: i-simple-icons-iconify
---
::callout{to="/getting-started/icons/nuxt" icon="i-logos-nuxt-icon" class="hidden"}
Looking for the **Nuxt** version?
::
## Usage
### Icon Component
You can use the [Icon](/components/icon) component with a `name` prop to display an icon:
::component-code{slug="icon"}
---
props:
name: 'i-lucide-lightbulb'
class: 'size-5'
---
::
::note
You can use any name from the https://icones.js.org collection.
::
### Component Props
Some components also have an `icon` prop to display an icon, like the [Button](/components/button) for example:
::component-code{slug="button"}
---
ignore:
- color
- variant
props:
icon: i-lucide-sun
variant: subtle
slots:
default: Button
---
::
## Theme
You can change the default icons used by Nuxt UI components in your `vite.config.ts`:
:icons-theme

View File

@@ -1,6 +1,7 @@
---
title: Fonts
description: 'Nuxt UI integrates with Nuxt Fonts to provide plug-and-play font optimization.'
navigation.framework: nuxt
links:
- label: 'nuxt/fonts'
to: https://github.com/nuxt/fonts
@@ -10,19 +11,23 @@ links:
## Usage
Nuxt UI automatically registers the [`@nuxt/fonts`](https://github.com/nuxt/fonts) module for you, so there's no additional setup required. To use a font in your Nuxt UI application, you can simply declare it in your CSS:
Nuxt UI automatically registers the [`@nuxt/fonts`](https://github.com/nuxt/fonts) module for you, so there's no additional setup required. To use a font in your Nuxt UI application, you can simply declare it in your CSS. It will be automatically loaded and optimized for you.
```css [main.css]
@import "tailwindcss";
@import "@nuxt/ui";
@theme {
--font-family-sans: 'Public Sans', sans-serif;
--font-sans: 'Public Sans', sans-serif;
}
```
That's it! Nuxt Fonts will detect this and you should immediately see the web font loaded in your browser.
You can disable the `@nuxt/fonts` module with the `ui.fonts` option in your `nuxt.config.ts`:
::note{to="https://fonts.nuxt.com/advanced" target="_blank"}
Read more about how `@nuxt/fonts` work behind the scenes to optimize your fonts.
::
```ts [nuxt.config.ts]
export default defineNuxtConfig({
ui: {
fonts: false
}
})
```

View File

@@ -1,27 +0,0 @@
---
title: Color Mode
description: 'Nuxt UI integrates with Nuxt Color Mode to allow for easy switching between light and dark themes.'
links:
- label: 'nuxtjs/color-mode'
to: https://github.com/nuxt-modules/color-mode
target: _blank
icon: i-simple-icons-github
---
## Usage
Nuxt UI automatically registers the [`@nuxtjs/color-mode`](https://github.com/nuxt-modules/color-mode) module for you, so there's no additional setup required.
You can disable dark mode by setting the `preference` to `light` instead of `system` in your `nuxt.config.ts`.
```ts [nuxt.config.ts]
export default defineNuxtConfig({
colorMode: {
preference: 'light'
}
})
```
::tip
If you're stuck in dark mode even after changing this setting, you might need to remove the `nuxt-color-mode` entry from your browser's local storage.
::

View File

@@ -0,0 +1 @@
shadow: true

View File

@@ -0,0 +1,58 @@
---
title: Color Mode
description: 'Nuxt UI integrates with Nuxt Color Mode to allow for easy switching between light and dark themes.'
navigation.framework: nuxt
links:
- label: 'nuxtjs/color-mode'
to: https://github.com/nuxt-modules/color-mode
target: _blank
icon: i-simple-icons-github
---
::callout{to="/getting-started/color-mode/vue" icon="i-logos-vue" class="hidden"}
Looking for the **Vue** version?
::
## Usage
Nuxt UI automatically registers the [`@nuxtjs/color-mode`](https://github.com/nuxt-modules/color-mode) module for you, so there's no additional setup required. You can simply use the `useColorMode` composable to switch between light and dark modes:
```vue [ColorModeButton.vue]
<script setup>
const colorMode = useColorMode()
const isDark = computed({
get() {
return colorMode.value === 'dark'
},
set() {
colorMode.preference = colorMode.value === 'dark' ? 'light' : 'dark'
}
})
</script>
<template>
<ClientOnly v-if="!colorMode?.forced">
<UButton
:icon="isDark ? 'i-lucide-moon' : 'i-lucide-sun'"
color="neutral"
variant="ghost"
@click="isDark = !isDark"
/>
<template #fallback>
<div class="size-8" />
</template>
</ClientOnly>
</template>
```
You can disable the `@nuxtjs/color-mode` module with the `ui.colorMode` option in your `nuxt.config.ts`:
```ts [nuxt.config.ts]
export default defineNuxtConfig({
ui: {
colorMode: false
}
})
```

View File

@@ -0,0 +1,47 @@
---
title: Color Mode
description: 'Nuxt UI integrates with VueUse to allow for easy switching between light and dark themes.'
navigation.framework: vue
---
::callout{to="/getting-started/color-mode/nuxt" icon="i-logos-nuxt-icon" class="hidden"}
Looking for the **Nuxt** version?
::
## Usage
Nuxt UI automatically registers the [useDark](https://vueuse.org/core/useDark) composable as a Vue plugin, so there's no additional setup required. You can simply use it to switch between light and dark modes:
```vue [ColorModeButton.vue]
<script setup>
import { useColorMode } from '@vueuse/core'
const mode = useColorMode()
</script>
<template>
<UButton
:icon="mode === 'dark' ? 'i-lucide-moon' : 'i-lucide-sun'"
color="neutral"
variant="ghost"
@click="mode = mode === 'dark' ? 'light' : 'dark'"
/>
</template>
```
You can disable this plugin with the `colorMode` option in your `vite.config.ts`:
```ts [vite.config.ts]
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'
export default defineConfig({
plugins: [
vue(),
ui({
colorMode: false
})
]
})
```

View File

@@ -0,0 +1,2 @@
badge: New
shadow: true

View File

@@ -1,22 +1,20 @@
---
navigation.title: Nuxt
title: Internationalization (i18n) in a Nuxt app
description: 'Learn how to internationalize your Nuxt app and support multi-directional support (LTR/RTL).'
select:
items:
- label: Nuxt
icon: i-logos-nuxt-icon
to: /getting-started/i18n/nuxt
- label: Vue
icon: i-logos-vue
to: /getting-started/i18n/vue
title: Internationalization (i18n)
description: 'Learn how to internationalize your Nuxt app with multi-directional support (LTR/RTL).'
navigation.framework: nuxt
---
::note{to="/components/app"}
Nuxt UI provides an [App](/components/app) component that wraps your app to provide global configurations.
::callout{to="/getting-started/i18n/vue" icon="i-logos-vue" class="hidden"}
Looking for the **Vue** version?
::
## Locale
## Usage
::note{to="/components/app"}
Nuxt UI provides an **App** component that wraps your app to provide global configurations.
::
### Locale
Use the `locale` prop with the locale you want to use from `@nuxt/ui/locale`:
@@ -38,8 +36,13 @@ You also have the option to add your own locale using `defineLocale`:
```vue [app.vue]
<script setup lang="ts">
const locale = defineLocale('My custom locale', 'en', {
// implement pairs
const locale = defineLocale({
name: 'My custom locale',
code: 'en',
dir: 'ltr',
messages: {
// implement pairs
}
})
</script>
@@ -51,7 +54,7 @@ const locale = defineLocale('My custom locale', 'en', {
```
::tip
Look at the second parameter, there you need to pass the iso code of the language. Example:
Look at the `code` parameter, there you need to pass the iso code of the language. Example:
* `hi` Hindi (language)
* `de-AT`: German (language) as used in Austria (region)
::
@@ -125,65 +128,36 @@ const { locale } = useI18n()
::
### Supported languages
:supported-languages
## Direction
Use the `dir` prop with `ltr` or `rtl` to set the global reading direction of your app:
```vue [app.vue]
<template>
<UApp dir="rtl">
<NuxtPage />
</UApp>
</template>
```
### Dynamic direction
To dynamically change the global reading direction of your app, you can use VueUse's [useTextDirection](https://vueuse.org/core/useTextDirection/) composable to detect and switch between LTR and RTL text directions.
Each locale has a `dir` property which will be used by the `App` component to set the directionality of all components.
::steps{level="4"}
#### Install the `@vueuse/core` package
::code-group{sync="pm"}
```bash [pnpm]
pnpm add @vueuse/core
```
```bash [yarn]
yarn add @vueuse/core
```
```bash [npm]
npm install @vueuse/core
```
```bash [bun]
bun add @vueuse/core
```
::
#### Set the `dir` prop using `useTextDirection`
In a multilingual application, you might want to set the `lang` and `dir` attributes on the `<html>` element dynamically based on the user's locale, which you can do with the [useHead](https://nuxt.com/docs/api/composables/use-head) composable:
```vue [app.vue]
<script setup lang="ts">
import { useTextDirection } from '@vueuse/core'
import * as locales from '@nuxt/ui/locale'
const textDirection = useTextDirection({ initialValue: 'ltr' })
const dir = computed(() => textDirection.value === 'rtl' ? 'rtl' : 'ltr')
const { locale } = useI18n()
const lang = computed(() => locales[locale.value].code)
const dir = computed(() => locales[locale.value].dir)
useHead({
htmlAttrs: {
lang,
dir
}
})
</script>
<template>
<UApp :dir="dir">
<UApp :locale="locales[locale]">
<NuxtPage />
</UApp>
</template>
```
::
## Supported languages
:supported-languages

View File

@@ -1,22 +1,20 @@
---
navigation.title: Vue
title: Internationalization (i18n) in a Vue app
description: 'Learn how to internationalize your Vue app and support multi-directional support (LTR/RTL).'
select:
items:
- label: Nuxt
icon: i-logos-nuxt-icon
to: /getting-started/i18n/nuxt
- label: Vue
icon: i-logos-vue
to: /getting-started/i18n/vue
title: Internationalization (i18n)
description: 'Learn how to internationalize your Vue app with multi-directional support (LTR/RTL).'
navigation.framework: vue
---
::note{to="/components/app"}
Nuxt UI provides an [App](/components/app) component that wraps your app to provide global configurations.
::callout{to="/getting-started/i18n/nuxt" icon="i-logos-nuxt-icon" class="hidden"}
Looking for the **Nuxt** version?
::
## Locale
## Usage
::note{to="/components/app"}
Nuxt UI provides an **App** component that wraps your app to provide global configurations.
::
### Locale
Use the `locale` prop with the locale you want to use from `@nuxt/ui/locale`:
@@ -40,8 +38,13 @@ You also have the option to add your locale using `defineLocale`:
<script setup lang="ts">
import { defineLocale } from '@nuxt/ui/runtime/composables/defineLocale'
const locale = defineLocale('My custom locale', 'en', {
// implement pairs
const locale = defineLocale({
name: 'My custom locale',
code: 'en',
dir: 'ltr',
messages: {
// implement pairs
}
})
</script>
@@ -53,7 +56,7 @@ const locale = defineLocale('My custom locale', 'en', {
```
::tip
Look at the second parameter, there you need to pass the iso code of the language. Example:
Look at the `code` parameter, there you need to pass the iso code of the language. Example:
* `hi` Hindi (language)
* `de-AT`: German (language) as used in Austria (region)
::
@@ -135,64 +138,39 @@ const { locale } = useI18n()
::
## Supported languages
:supported-languages
## Direction
Use the `dir` prop with `ltr` or `rtl` to set the global reading direction of your app:
```vue [App.vue]
<template>
<UApp dir="rtl">
<NuxtPage />
</UApp>
</template>
```
### Dynamic direction
To dynamically change the global reading direction of your app, you can use VueUse's [useTextDirection](https://vueuse.org/core/useTextDirection/) composable to detect and switch between LTR and RTL text directions.
Each locale has a `dir` property which will be used by the `App` component to set the directionality of all components.
::steps{level="4"}
#### Install the `@vueuse/core` package
::code-group{sync="pm"}
```bash [pnpm]
pnpm add @vueuse/core
```
```bash [yarn]
yarn add @vueuse/core
```
```bash [npm]
npm install @vueuse/core
```
```bash [bun]
bun add @vueuse/core
```
::
#### Set the `dir` prop using `useTextDirection`
In a multilingual application, you might want to set the `lang` and `dir` attributes on the `<html>` element dynamically based on the user's locale, which you can do with the [useHead](https://unhead.unjs.io/usage/composables/use-head) composable:
```vue [App.vue]
<script setup lang="ts">
import { computed } from 'vue'
import { useTextDirection } from '@vueuse/core'
import { useI18n } from 'vue-i18n'
import { useHead } from '@unhead/vue'
import * as locales from '@nuxt/ui/locale'
const textDirection = useTextDirection()
const dir = computed(() => textDirection.value === 'rtl' ? 'rtl' : 'ltr')
const { locale } = useI18n()
const lang = computed(() => locales[locale.value].code)
const dir = computed(() => locales[locale.value].dir)
useHead({
htmlAttrs: {
lang,
dir
}
})
</script>
<template>
<UApp :dir="dir">
<UApp :locale="locales[locale]">
<RouterView />
</UApp>
</template>
```
## Supported languages
:supported-languages

View File

@@ -27,12 +27,16 @@ Use it as at the root of your app:
</template>
```
::tip{to="/getting-started/i18n/nuxt#locale"}
::framework-only
#nuxt
:::tip{to="/getting-started/i18n/nuxt#locale"}
Learn how to use the `locale` prop to change the locale of your app.
::
:::
::tip{to="/getting-started/i18n/nuxt#direction"}
Learn how to use the `dir` prop to change the global reading direction of your app.
#vue
:::tip{to="/getting-started/i18n/vue#locale"}
Learn how to use the `locale` prop to change the locale of your app.
:::
::
## API

View File

@@ -168,8 +168,16 @@ props:
---
::
::tip{to="/getting-started/icons#theme"}
::framework-only
#nuxt
:::tip{to="/getting-started/icons/nuxt#theme"}
You can customize this icon globally in your `app.config.ts` under `ui.icons.chevronDown` key.
:::
#vue
:::tip{to="/getting-started/icons/vue#theme"}
You can customize this icon globally in your `vite.config.ts` under `ui.icons.chevronDown` key.
:::
::
## Examples

View File

@@ -178,8 +178,16 @@ props:
---
::
::tip{to="/getting-started/icons#theme"}
::framework-only
#nuxt
:::tip{to="/getting-started/icons/nuxt#theme"}
You can customize this icon globally in your `app.config.ts` under `ui.icons.close` key.
:::
#vue
:::tip{to="/getting-started/icons/vue#theme"}
You can customize this icon globally in your `vite.config.ts` under `ui.icons.close` key.
:::
::
### Actions

View File

@@ -67,8 +67,16 @@ props:
---
::
::tip{to="/getting-started/icons#theme"}
::framework-only
#nuxt
:::tip{to="/getting-started/icons/nuxt#theme"}
You can customize this icon globally in your `app.config.ts` under `ui.icons.chevronRight` key.
:::
#vue
:::tip{to="/getting-started/icons/vue#theme"}
You can customize this icon globally in your `vite.config.ts` under `ui.icons.chevronRight` key.
:::
::
## Examples

View File

@@ -197,8 +197,16 @@ slots:
Button
::
::tip{to="/getting-started/icons#theme"}
::framework-only
#nuxt
:::tip{to="/getting-started/icons/nuxt#theme"}
You can customize this icon globally in your `app.config.ts` under `ui.icons.loading` key.
:::
#vue
:::tip{to="/getting-started/icons/vue#theme"}
You can customize this icon globally in your `vite.config.ts` under `ui.icons.loading` key.
:::
::
### Disabled

View File

@@ -0,0 +1,248 @@
---
title: Calendar
description: A calendar component for selecting single dates, multiple dates or date ranges.
links:
- label: Calendar
icon: i-custom-radix-vue
to: https://www.radix-vue.com/components/calendar.html
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/Calendar.vue
navigation.badge: New
---
::note
This component relies on the [@internationalized/date](https://react-spectrum.adobe.com/internationalized/date/index.html) package which provides objects and functions for representing and manipulating dates and times in a locale-aware manner.
::
## Usage
Use the `v-model` directive to control the selected date.
::component-code
---
cast:
modelValue: DateValue
ignore:
- modelValue
external:
- modelValue
props:
modelValue: [2022, 2, 3]
---
::
Use the `default-value` prop to set the initial value when you do not need to control its state.
::component-code
---
cast:
defaultValue: DateValue
ignore:
- defaultValue
external:
- defaultValue
props:
defaultValue: [2022, 2, 6]
---
::
### Multiple
Use the `multiple` prop to allow multiple selections.
::component-code
---
prettier: true
cast:
modelValue: DateValue[]
ignore:
- multiple
- modelValue
external:
- modelValue
props:
multiple: true
modelValue: [[2022, 2, 4], [2022, 2, 6], [2022, 2, 8]]
---
::
### Range
Use the `range` prop to select a range of dates.
::component-code
---
prettier: true
cast:
modelValue: DateRange
ignore:
- range
- modelValue.start
- modelValue.end
external:
- modelValue
props:
range: true
modelValue:
start: [2022, 2, 3]
end: [2022, 2, 20]
---
::
### Color
Use the `color` prop to change the color of the calendar.
::component-code
---
props:
color: neutral
---
::
### Size
Use the `size` prop to change the size of the calendar.
::component-code
---
props:
size: xl
---
::
### Disabled
Use the `disabled` prop to disable the calendar.
::component-code
---
props:
disabled: true
---
::
### Number Of Months
Use the `numberOfMonths` prop to change the number of months in the calendar.
::component-code
---
props:
numberOfMonths: 3
---
::
### Month Controls
Use the `month-controls` prop to show the month controls. Defaults to `true`.
::component-code
---
props:
monthControls: false
---
::
### Year Controls
Use the `year-controls` prop to show the year controls. Defaults to `true`.
::component-code
---
props:
yearControls: false
---
::
### Fixed Weeks
Use the `fixed-weeks` prop to display the calendar with fixed weeks.
::component-code
---
props:
fixedWeeks: false
---
::
## Examples
### With chip events
Use the [Chip](/components/chip) component to add events to specific days.
::component-example
---
name: 'calendar-events-example'
---
::
### With disabled dates
Use the `is-date-disabled` prop with a function to mark specific dates as disabled.
::component-example
---
name: 'calendar-disabled-dates-example'
---
::
### With unavailable dates
Use the `is-date-unavailable` prop with a function to mark specific dates as unavailable.
::component-example
---
name: 'calendar-unavailable-dates-example'
---
::
### With min/max dates
Use the `min-value` and `max-value` props to limit the dates.
::component-example
---
name: 'calendar-min-max-dates-example'
---
::
### As a DatePicker
Use a [Button](/components/button) and a [Popover](/components/popover) component to create a date picker.
::component-example
---
name: 'calendar-date-picker-example'
---
::
### As a DateRangePicker
Use a [Button](/components/button) and a [Popover](/components/popover) component to create a date range picker.
::component-example
---
name: 'calendar-date-range-picker-example'
---
::
## API
### Props
:component-props
### Slots
:component-slots
### Emits
:component-emits
## Theme
:component-theme

View File

@@ -67,7 +67,7 @@ class: 'p-8'
### Prev / Next
Use the `prev` and `next` props to customize the prev and next buttons.
Use the `prev` and `next` props to customize the prev and next buttons with any [Button](/components/button) props.
::component-example
---
@@ -76,7 +76,7 @@ class: 'p-8'
---
::
### Prev Icon / Next Icon
### Prev / Next Icons
Use the `prev-icon` and `next-icon` props to customize the buttons [Icon](/components/icon). Defaults to `i-lucide-arrow-left` / `i-lucide-arrow-right`.
@@ -94,8 +94,16 @@ options:
---
::
::tip{to="/getting-started/icons#theme"}
::framework-only
#nuxt
:::tip{to="/getting-started/icons/nuxt#theme"}
You can customize these icons globally in your `app.config.ts` under `ui.icons.arrowLeft` / `ui.icons.arrowRight` key.
:::
#vue
:::tip{to="/getting-started/icons/vue#theme"}
You can customize these icons globally in your `vite.config.ts` under `ui.icons.arrowLeft` / `ui.icons.arrowRight` key.
:::
::
### Dots

View File

@@ -58,8 +58,16 @@ props:
---
::
::tip{to="/getting-started/icons#theme"}
::framework-only
#nuxt
:::tip{to="/getting-started/icons/nuxt#theme"}
You can customize this icon globally in your `app.config.ts` under `ui.icons.minus` key.
:::
#vue
:::tip{to="/getting-started/icons/vue#theme"}
You can customize this icon globally in your `vite.config.ts` under `ui.icons.minus` key.
:::
::
### Label
@@ -115,8 +123,16 @@ props:
---
::
::tip{to="/getting-started/icons#theme"}
::framework-only
#nuxt
:::tip{to="/getting-started/icons/nuxt#theme"}
You can customize this icon globally in your `app.config.ts` under `ui.icons.check` key.
:::
#vue
:::tip{to="/getting-started/icons/vue#theme"}
You can customize this icon globally in your `vite.config.ts` under `ui.icons.check` key.
:::
::
### Color

View File

@@ -42,6 +42,7 @@ Each group takes some `items` as an array of objects with the following properti
- `avatar?: AvatarProps`{lang="ts-type"}
- `chip?: ChipProps`{lang="ts-type"}
- `kbds?: string[] | KbdProps[]`{lang="ts-type"}
- `active?: boolean`{lang="ts-type"}
- `loading?: boolean`{lang="ts-type"}
- `disabled?: boolean`{lang="ts-type"}
- [`slot?: string`{lang="ts-type"}](#with-custom-slot)
@@ -215,8 +216,16 @@ props:
---
::
::tip{to="/getting-started/icons#theme"}
::framework-only
#nuxt
:::tip{to="/getting-started/icons/nuxt#theme"}
You can customize this icon globally in your `app.config.ts` under `ui.icons.search` key.
:::
#vue
:::tip{to="/getting-started/icons/vue#theme"}
You can customize this icon globally in your `vite.config.ts` under `ui.icons.search` key.
:::
::
### Loading
@@ -276,8 +285,16 @@ props:
---
::
::tip{to="/getting-started/icons#theme"}
::framework-only
#nuxt
:::tip{to="/getting-started/icons/nuxt#theme"}
You can customize this icon globally in your `app.config.ts` under `ui.icons.loading` key.
:::
#vue
:::tip{to="/getting-started/icons/vue#theme"}
You can customize this icon globally in your `vite.config.ts` under `ui.icons.loading` key.
:::
::
### Disabled
@@ -403,8 +420,16 @@ props:
---
::
::tip{to="/getting-started/icons#theme"}
::framework-only
#nuxt
:::tip{to="/getting-started/icons/nuxt#theme"}
You can customize this icon globally in your `app.config.ts` under `ui.icons.close` key.
:::
#vue
:::tip{to="/getting-started/icons/vue#theme"}
You can customize this icon globally in your `vite.config.ts` under `ui.icons.close` key.
:::
::
## Examples

View File

@@ -40,7 +40,7 @@ prettier: true
collapse: true
ignore:
- items
- class
- ui.content
external:
- items
props:
@@ -90,7 +90,8 @@ props:
- option
- meta
- j
class: 'w-48'
ui:
content: 'w-48'
slots:
default: |
@@ -119,7 +120,7 @@ Use the `size` prop to change the size of the ContextMenu.
prettier: true
ignore:
- items
- class
- ui.content
external:
- items
props:
@@ -131,7 +132,8 @@ props:
icon: i-lucide-sun
- label: Dark
icon: i-lucide-moon
class: 'w-48'
ui:
content: 'w-48'
slots:
default: |
@@ -152,7 +154,7 @@ Use the `disabled` prop to disable the ContextMenu.
prettier: true
ignore:
- items
- class
- ui.content
external:
- items
props:
@@ -164,7 +166,8 @@ props:
icon: i-lucide-sun
- label: Dark
icon: i-lucide-moon
class: 'w-48'
ui:
content: 'w-48'
slots:
default: |

View File

@@ -40,7 +40,7 @@ prettier: true
collapse: true
ignore:
- items
- class
- ui.content
external:
- items
props:
@@ -91,7 +91,8 @@ props:
- shift
- meta
- q
class: 'w-48'
ui:
content: 'w-48'
slots:
default: |
@@ -118,7 +119,7 @@ Use the `content` prop to control how the DropdownMenu content is rendered, like
prettier: true
ignore:
- items
- class
- ui.content
external:
- items
items:
@@ -143,7 +144,8 @@ props:
align: start
side: bottom
sideOffset: 8
class: 'w-48'
ui:
content: 'w-48'
slots:
default: |
@@ -163,7 +165,7 @@ prettier: true
ignore:
- arrow
- items
- class
- ui.content
external:
- items
props:
@@ -175,7 +177,8 @@ props:
icon: i-lucide-credit-card
- label: Settings
icon: i-lucide-cog
class: 'w-48'
ui:
content: 'w-48'
slots:
default: |
@@ -194,8 +197,8 @@ Use the `size` prop to control the size of the DropdownMenu.
prettier: true
ignore:
- items
- class
- content.align
- ui.content
external:
- items
props:
@@ -209,7 +212,8 @@ props:
icon: i-lucide-cog
content:
align: start
class: 'w-48'
ui:
content: 'w-48'
slots:
default: |
@@ -236,7 +240,7 @@ Use the `disabled` prop to disable the DropdownMenu.
prettier: true
ignore:
- items
- class
- ui.content
external:
- items
props:
@@ -248,7 +252,8 @@ props:
icon: i-lucide-credit-card
- label: Settings
icon: i-lucide-cog
class: 'w-48'
ui:
content: 'w-48'
slots:
default: |

View File

@@ -1,14 +1,15 @@
---
description: A wrapper around Nuxt Icon component to display icons.
description: A component to display any icon from Iconify.
links:
- label: Nuxt Icon
icon: i-simple-icons-github
to: https://github.com/nuxt/icon
- label: Icônes
to: https://icones.js.org/
target: _blank
icon: i-custom-icones-js
---
## Usage
You can use any name from the https://icones.js.org collection:
Use the `name` prop to display an icon:
::component-code
---
@@ -18,6 +19,15 @@ props:
---
::
::tip
It's highly recommended to install the icons collections you need, read more about this in [Icons](/getting-started/icons#collections).
::framework-only
#nuxt
:::caution{to="/getting-started/icons/nuxt#collections"}
It's highly recommended to install the icons collections you need, read more about this.
:::
::
## API
### Props
:component-props

View File

@@ -189,8 +189,16 @@ props:
---
::
::tip{to="/getting-started/icons#theme"}
::framework-only
#nuxt
:::tip{to="/getting-started/icons/nuxt#theme"}
You can customize this icon globally in your `app.config.ts` under `ui.icons.close` key.
:::
#vue
:::tip{to="/getting-started/icons/vue#theme"}
You can customize this icon globally in your `vite.config.ts` under `ui.icons.close` key.
:::
::
### Placeholder
@@ -214,6 +222,42 @@ props:
---
::
### Create Item
Use the `create-item` prop to allow user input.
::component-code
---
prettier: true
ignore:
- modelValue
- items
external:
- items
- modelValue
items:
createItem:
- true
- 'always'
props:
modelValue: 'Backlog'
items:
- Backlog
- Todo
- In Progress
- Done
createItem: true
---
::
::note
The create option shows when no match is found by default. Set it to `always` to show it even when similar values exist.
::
::tip{to="#emits"}
Use the `@create` event to handle the creation of the item. You will receive the event and the item as arguments.
::
### Content
Use the `content` prop to control how the InputMenu content is rendered, like its `align` or `side` for example.
@@ -405,8 +449,16 @@ props:
---
::
::tip{to="/getting-started/icons#theme"}
::framework-only
#nuxt
:::tip{to="/getting-started/icons/nuxt#theme"}
You can customize this icon globally in your `app.config.ts` under `ui.icons.chevronDown` key.
:::
#vue
:::tip{to="/getting-started/icons/vue#theme"}
You can customize this icon globally in your `vite.config.ts` under `ui.icons.chevronDown` key.
:::
::
### Selected Icon
@@ -434,8 +486,16 @@ props:
---
::
::tip{to="/getting-started/icons#theme"}
::framework-only
#nuxt
:::tip{to="/getting-started/icons/nuxt#theme"}
You can customize this icon globally in your `app.config.ts` under `ui.icons.check` key.
:::
#vue
:::tip{to="/getting-started/icons/vue#theme"}
You can customize this icon globally in your `vite.config.ts` under `ui.icons.check` key.
:::
::
### Avatar
@@ -514,8 +574,16 @@ props:
---
::
::tip{to="/getting-started/icons#theme"}
::framework-only
#nuxt
:::tip{to="/getting-started/icons/nuxt#theme"}
You can customize this icon globally in your `app.config.ts` under `ui.icons.loading` key.
:::
#vue
:::tip{to="/getting-started/icons/vue#theme"}
You can customize this icon globally in your `vite.config.ts` under `ui.icons.loading` key.
:::
::
### Disabled
@@ -703,7 +771,7 @@ name: 'input-menu-filter-fields-example'
---
::
### As a country picker
### As a CountryPicker
This example demonstrates using the InputMenu as a country picker with lazy loading - countries are only fetched when the menu is opened.

View File

@@ -0,0 +1,295 @@
---
title: InputNumber
description: Input numerical values with a customizable range.
links:
- label: Number Field
icon: i-custom-radix-vue
to: https://www.radix-vue.com/components/number-field
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/InputNumber.vue
navigation.badge: New
---
::note
This component relies on the [@internationalized/number](https://react-spectrum.adobe.com/internationalized/number/index.html) package which provides utilities for formatting and parsing numbers across locales and numbering systems.
::
## Usage
Use the `v-model` directive to control the value of the InputNumber.
::component-code
---
ignore:
- modelValue
external:
- modelValue
props:
modelValue: 5
---
::
Use the `default-value` prop to set the initial value when you do not need to control its state.
::component-code
---
ignore:
- defaultValue
props:
defaultValue: 5
---
::
### Min / Max
Use the `min` and `max` props to set the minimum and maximum values of the InputNumber.
::component-code
---
ignore:
- modelValue
external:
- modelValue
props:
modelValue: 5
min: 0
max: 10
---
::
### Step
Use the `step` prop to set the step value of the InputNumber.
::component-code
---
ignore:
- modelValue
external:
- modelValue
props:
modelValue: 5
step: 2
---
::
### Orientation
Use the `orientation` prop to change the orientation of the InputNumber.
::component-code
---
ignore:
- modelValue
external:
- modelValue
props:
modelValue: 5
orientation: vertical
---
::
### Placeholder
Use the `placeholder` prop to set a placeholder text.
::component-code
---
props:
placeholder: 'Enter a number'
---
::
### Color
Use the `color` prop to change the ring color when the InputNumber is focused.
::component-code
---
ignore:
- modelValue
external:
- modelValue
props:
modelValue: 5
color: neutral
highlight: true
---
::
### Variant
Use the `variant` prop to change the variant of the InputNumber.
::component-code
---
ignore:
- modelValue
external:
- modelValue
props:
modelValue: 5
variant: subtle
color: neutral
highlight: false
---
::
### Size
Use the `size` prop to change the size of the InputNumber.
::component-code
---
ignore:
- modelValue
external:
- modelValue
props:
modelValue: 5
size: xl
---
::
### Disabled
Use the `disabled` prop to disable the InputNumber.
::component-code
---
ignore:
- modelValue
external:
- modelValue
props:
modelValue: 5
disabled: true
---
::
### Increment / Decrement
Use the `increment` and `decrement` props to customize the increment and decrement buttons with any [Button](/components/button) props. Defaults to `{ variant: 'link' }`{lang="ts-type"}.
::component-code
---
prettier: true
ignore:
- modelValue
- increment.size
- increment.color
- increment.variant
- decrement.size
- decrement.color
- decrement.variant
external:
- modelValue
props:
modelValue: 5
increment:
color: neutral
variant: solid
size: xs
decrement:
color: neutral
variant: solid
size: xs
---
::
### Increment / Decrement Icons
Use the `increment-icon` and `decrement-icon` props to customize the buttons [Icon](/components/icon). Defaults to `i-lucide-plus` / `i-lucide-minus`.
::component-code
---
prettier: true
ignore:
- modelValue
external:
- modelValue
props:
modelValue: 5
incrementIcon: 'i-lucide-arrow-right'
decrementIcon: 'i-lucide-arrow-left'
---
::
## Examples
### With decimal format
Use the `format-options` prop to customize the format of the value.
::component-example
---
name: 'input-number-decimal-example'
---
::
### With percentage format
Use the `format-options` prop with `style: 'percent'` to customize the format of the value.
::component-example
---
name: 'input-number-percentage-example'
---
::
### With currency format
Use the `format-options` prop with `style: 'currency'` to customize the format of the value.
::component-example
---
name: 'input-number-currency-example'
---
::
### Within a FormField
You can use the InputNumber within a [FormField](/components/form-field) component to display a label, help text, required indicator, etc.
::component-example
---
name: 'input-number-form-field-example'
---
::
### With slots
Use the `#increment` and `#decrement` slots to customize the buttons.
::component-example
---
name: 'input-number-slots-example'
---
::
## API
### Props
:component-props
### Slots
:component-slots
### Emits
:component-emits
### Expose
When accessing the component via a template ref, you can use the following:
| Name | Type |
|----------------------------|-------------------------------------------------|
| `inputRef`{lang="ts-type"} | `Ref<HTMLInputElement \| null>`{lang="ts-type"} |
## Theme
:component-theme

View File

@@ -25,15 +25,15 @@ props:
Use the `type` prop to change the input type. Defaults to `text`.
Some types have been implemented in their own components such as [Checkbox](/components/checkbox), [Radio](/components/radio-group), etc. and others have been styled like `file` for example.
Some types have been implemented in their own components such as [Checkbox](/components/checkbox), [Radio](/components/radio-group), [InputNumber](/components/input-number) etc. and others have been styled like `file` for example.
::component-code
---
items:
type:
- text
- password
- number
- password
- search
- file
props:
@@ -184,8 +184,16 @@ props:
---
::
::tip{to="/getting-started/icons#theme"}
::framework-only
#nuxt
:::tip{to="/getting-started/icons/nuxt#theme"}
You can customize this icon globally in your `app.config.ts` under `ui.icons.loading` key.
:::
#vue
:::tip{to="/getting-started/icons/vue#theme"}
You can customize this icon globally in your `vite.config.ts` under `ui.icons.loading` key.
:::
::
### Disabled
@@ -214,6 +222,16 @@ name: 'input-clear-button-example'
---
::
### With copy button
You can put a [Button](/components/button) inside the `#trailing` slot to copy the value to the clipboard.
::component-example
---
name: 'input-copy-button-example'
---
::
### With password toggle
You can put a [Button](/components/button) inside the `#trailing` slot to toggle the password visibility.
@@ -245,6 +263,20 @@ name: 'input-character-limit-example'
---
::
### With keyboard shortcut
You can use the [Kbd](/components/kbd) component inside the `#trailing` slot to add a keyboard shortcut to the Input.
::component-example
---
name: 'input-kbd-example'
---
::
::note{to="/composables/define-shortcuts"}
This example uses the `defineShortcuts` composable to focus the Input when the :kbd{value="/"} key is pressed.
::
### With floating label
You can use the `#default` slot to add a floating label to the Input.
@@ -266,7 +298,7 @@ name: 'input-form-field-example'
::
::tip{to="/components/form"}
It also provides validation and error handling when used within a [Form](/components/form) component.
It also provides validation and error handling when used within a **Form** component.
::
### Within a ButtonGroup

View File

@@ -13,6 +13,7 @@ The Link component is a wrapper around [`<NuxtLink>`](https://nuxt.com/docs/api/
- `inactive-class` prop to set a class when the link is inactive, `active-class` is used when active.
- `exact` prop to style with `active-class` when the link is active and the route is exactly the same as the current route.
- `exact-query` and `exact-hash` props to style with `active-class` when the link is active and the query or hash is exactly the same as the current query or hash.
- use `exact-query="partial"` to style with `active-class` when the link is active and the query partially match the current query.
The incentive behind this is to provide the same API as NuxtLink back in Nuxt 2 / Vue 2. You can read more about it in the Vue Router [migration from Vue 2](https://router.vuejs.org/guide/migration/#removal-of-the-exact-prop-in-router-link) guide.

View File

@@ -156,8 +156,16 @@ slots:
:placeholder{class="h-48"}
::
::tip{to="/getting-started/icons#theme"}
::framework-only
#nuxt
:::tip{to="/getting-started/icons/nuxt#theme"}
You can customize this icon globally in your `app.config.ts` under `ui.icons.close` key.
:::
#vue
:::tip{to="/getting-started/icons/vue#theme"}
You can customize this icon globally in your `vite.config.ts` under `ui.icons.close` key.
:::
::
### Overlay

View File

@@ -434,8 +434,16 @@ props:
---
::
::tip{to="/getting-started/icons#theme"}
::framework-only
#nuxt
:::tip{to="/getting-started/icons/nuxt#theme"}
You can customize this icon globally in your `app.config.ts` under `ui.icons.chevronDown` key.
:::
#vue
:::tip{to="/getting-started/icons/vue#theme"}
You can customize this icon globally in your `vite.config.ts` under `ui.icons.chevronDown` key.
:::
::
### Arrow

View File

@@ -0,0 +1,182 @@
---
title: PinInput
description: An input element to enter a pin.
links:
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/PinInput.vue
navigation.badge: New
---
## Usage
Use the `v-model` directive to control the value of the PinInput.
::component-code
---
prettier: true
ignore:
- modelValue
external:
- modelValue
props:
modelValue: []
---
::
Use the `default-value` prop to set the initial value when you do not need to control its state.
::component-code
---
prettier: true
ignore:
- defaultValue
props:
defaultValue: ['1','2','3']
---
::
### Type
Use the `type` prop to change the input type. Defaults to `text`.
::component-code
---
items:
type:
- text
- number
props:
type: 'number'
---
::
::note
When `type` is set to `number`, it will only accept numeric characters.
::
### Mask
Use the `mask` prop to treat the input like a password.
::component-code
---
prettier: true
ignore:
- placeholder
- defaultValue
props:
mask: true
defaultValue: ['1','2','3','4','5']
---
::
### OTP
Use the `otp` prop to enable One-Time Password functionality. When enabled, mobile devices can automatically detect and fill OTP codes from SMS messages or clipboard content, with autocomplete support.
::component-code
---
props:
otp: true
---
::
### Length
Use the `length` prop to change the amount of inputs.
::component-code
---
props:
length: 6
---
::
### Placeholder
Use the `placeholder` prop to set a placeholder text.
::component-code
---
props:
placeholder: '○'
---
::
### Color
Use the `color` prop to change the ring color when the PinInput is focused.
::component-code
---
ignore:
- placeholder
props:
color: neutral
highlight: true
placeholder: '○'
---
::
::note
The `highlight` prop is used here to show the focus state. It's used internally when a validation error occurs.
::
### Variant
Use the `variant` prop to change the variant of the PinInput.
::component-code
---
ignore:
- placeholder
props:
color: neutral
variant: subtle
highlight: false
placeholder: '○'
---
::
### Size
Use the `size` prop to change the size of the PinInput.
::component-code
---
ignore:
- placeholder
props:
size: xl
placeholder: '○'
---
::
### Disabled
Use the `disabled` prop to disable the PinInput.
::component-code
---
ignore:
- placeholder
props:
disabled: true
placeholder: '○'
---
::
## API
### Props
:component-props
### Emits
:component-emits
## Theme
:component-theme

View File

@@ -235,6 +235,48 @@ props:
---
::
::tip
You can set the `search-input` prop to `false` to hide the search input.
::
### Create Item
Use the `create-item` prop to allow user input.
::component-code
---
prettier: true
ignore:
- modelValue
- items
- class
external:
- items
- modelValue
items:
createItem:
- true
- 'always'
props:
modelValue: 'Backlog'
createItem: true
items:
- Backlog
- Todo
- In Progress
- Done
class: 'w-48'
---
::
::note
The create option shows when no match is found by default. Set it to `always` to show it even when similar values exist.
::
::tip{to="#emits"}
Use the `@create` event to handle the creation of the item. You will receive the event and the item as arguments.
::
### Content
Use the `content` prop to control how the SelectMenu content is rendered, like its `align` or `side` for example.
@@ -440,8 +482,16 @@ props:
---
::
::tip{to="/getting-started/icons#theme"}
::framework-only
#nuxt
:::tip{to="/getting-started/icons/nuxt#theme"}
You can customize this icon globally in your `app.config.ts` under `ui.icons.chevronDown` key.
:::
#vue
:::tip{to="/getting-started/icons/vue#theme"}
You can customize this icon globally in your `vite.config.ts` under `ui.icons.chevronDown` key.
:::
::
### Selected Icon
@@ -471,8 +521,16 @@ props:
---
::
::tip{to="/getting-started/icons#theme"}
::framework-only
#nuxt
:::tip{to="/getting-started/icons/nuxt#theme"}
You can customize this icon globally in your `app.config.ts` under `ui.icons.check` key.
:::
#vue
:::tip{to="/getting-started/icons/vue#theme"}
You can customize this icon globally in your `vite.config.ts` under `ui.icons.check` key.
:::
::
### Avatar
@@ -557,8 +615,16 @@ props:
---
::
::tip{to="/getting-started/icons#theme"}
::framework-only
#nuxt
:::tip{to="/getting-started/icons/nuxt#theme"}
You can customize this icon globally in your `app.config.ts` under `ui.icons.loading` key.
:::
#vue
:::tip{to="/getting-started/icons/vue#theme"}
You can customize this icon globally in your `vite.config.ts` under `ui.icons.loading` key.
:::
::
### Disabled
@@ -740,7 +806,7 @@ name: 'select-menu-filter-fields-example'
---
::
### As a country picker
### As a CountryPicker
This example demonstrates using the SelectMenu as a country picker with lazy loading - countries are only fetched when the menu is opened.

View File

@@ -206,7 +206,7 @@ props:
::
::note{to="https://www.radix-vue.com/components/select.html#change-the-positioning-mode"}
Read more about the `content.position` prop in the [Radix Vue documentation](https://www.radix-vue.com/components/select.html#change-the-positioning-mode).
Read more about the `content.position` prop in the **Radix Vue** documentation.
::
<!--
@@ -378,8 +378,16 @@ props:
---
::
::tip{to="/getting-started/icons#theme"}
::framework-only
#nuxt
:::tip{to="/getting-started/icons/nuxt#theme"}
You can customize this icon globally in your `app.config.ts` under `ui.icons.chevronDown` key.
:::
#vue
:::tip{to="/getting-started/icons/vue#theme"}
You can customize this icon globally in your `vite.config.ts` under `ui.icons.chevronDown` key.
:::
::
### Selected Icon
@@ -409,8 +417,16 @@ props:
---
::
::tip{to="/getting-started/icons#theme"}
::framework-only
#nuxt
:::tip{to="/getting-started/icons/nuxt#theme"}
You can customize this icon globally in your `app.config.ts` under `ui.icons.check` key.
:::
#vue
:::tip{to="/getting-started/icons/vue#theme"}
You can customize this icon globally in your `vite.config.ts` under `ui.icons.check` key.
:::
::
### Avatar
@@ -495,8 +511,16 @@ props:
---
::
::tip{to="/getting-started/icons#theme"}
::framework-only
#nuxt
:::tip{to="/getting-started/icons/nuxt#theme"}
You can customize this icon globally in your `app.config.ts` under `ui.icons.loading` key.
:::
#vue
:::tip{to="/getting-started/icons/vue#theme"}
You can customize this icon globally in your `vite.config.ts` under `ui.icons.loading` key.
:::
::
### Disabled

View File

@@ -156,8 +156,16 @@ slots:
:placeholder{class="h-full"}
::
::tip{to="/getting-started/icons#theme"}
::framework-only
#nuxt
:::tip{to="/getting-started/icons/nuxt#theme"}
You can customize this icon globally in your `app.config.ts` under `ui.icons.close` key.
:::
#vue
:::tip{to="/getting-started/icons/vue#theme"}
You can customize this icon globally in your `vite.config.ts` under `ui.icons.close` key.
:::
::
### Side

View File

@@ -123,8 +123,16 @@ props:
---
::
::tip{to="/getting-started/icons#theme"}
::framework-only
#nuxt
:::tip{to="/getting-started/icons/nuxt#theme"}
You can customize this icon globally in your `app.config.ts` under `ui.icons.loading` key.
:::
#vue
:::tip{to="/getting-started/icons/vue#theme"}
You can customize this icon globally in your `vite.config.ts` under `ui.icons.loading` key.
:::
::
### Color

View File

@@ -128,8 +128,16 @@ name: 'toast-close-icon-example'
---
::
::tip{to="/getting-started/icons#theme"}
::framework-only
#nuxt
:::tip{to="/getting-started/icons/nuxt#theme"}
You can customize this icon globally in your `app.config.ts` under `ui.icons.close` key.
:::
#vue
:::tip{to="/getting-started/icons/vue#theme"}
You can customize this icon globally in your `vite.config.ts` under `ui.icons.close` key.
:::
::
### Actions

View File

@@ -17,7 +17,12 @@ export default defineNuxtConfig({
'@nuxtjs/plausible',
'@vueuse/nuxt',
'nuxt-component-meta',
'nuxt-og-image'
'nuxt-og-image',
(_, nuxt) => {
nuxt.hook('components:dirs', (dirs) => {
dirs.unshift({ path: resolve('./app/components/content/examples'), pathPrefix: false, prefix: '', global: true })
})
}
],
app: {
@@ -32,10 +37,24 @@ export default defineNuxtConfig({
},
content: {
database: {
type: 'd1',
binding: 'DB'
},
build: {
markdown: {
highlight: {
langs: ['bash', 'ts', 'typescript', 'diff', 'vue', 'json', 'yml', 'css', 'mdc']
},
remarkPlugins: {
'remark-mdc': {
options: {
experimental: {
autoUnwrap: false
}
}
}
}
}
}
@@ -56,6 +75,8 @@ export default defineNuxtConfig({
routeRules: {
'/': { redirect: '/getting-started', prerender: false },
'/getting-started/installation': { redirect: '/getting-started/installation/nuxt', prerender: false },
'/getting-started/icons': { redirect: '/getting-started/icons/nuxt', prerender: false },
'/getting-started/color-mode': { redirect: '/getting-started/color-mode/nuxt', prerender: false },
'/getting-started/i18n': { redirect: '/getting-started/i18n/nuxt', prerender: false },
'/composables': { redirect: '/composables/define-shortcuts', prerender: false },
'/components': { redirect: '/components/app', prerender: false }
@@ -70,31 +91,28 @@ export default defineNuxtConfig({
nitro: {
prerender: {
routes: [
'/getting-started',
'/api/countries.json'
// '/getting-started',
'/api/countries.json',
'/api/locales.json'
// '/api/releases.json',
// '/api/pulls.json'
],
crawlLinks: true,
autoSubfolderIndex: false
]
// crawlLinks: true,
// autoSubfolderIndex: false
// ignore: !process.env.NUXT_GITHUB_TOKEN ? ['/pro'] : []
},
cloudflare: {
pages: {
routes: {
exclude: [
'/components/*',
'/getting-started/*',
'/composables/*',
'/api/*'
]
exclude: []
}
}
}
},
hub: {
cache: true
cache: true,
database: true
},
componentMeta: {

View File

@@ -3,21 +3,22 @@
"name": "@nuxt/ui-docs",
"type": "module",
"dependencies": {
"@iconify-json/lucide": "^1.2.13",
"@iconify-json/simple-icons": "^1.2.11",
"@iconify-json/logos": "^1.2.3",
"@iconify-json/lucide": "^1.2.16",
"@iconify-json/simple-icons": "^1.2.13",
"@iconify-json/vscode-icons": "^1.2.2",
"@nuxt/content": "3.0.0-alpha.6",
"@nuxt/content": "3.0.0-alpha.7",
"@nuxt/image": "^1.8.1",
"@nuxt/ui": "latest",
"@nuxt/ui-pro": "https://pkg.pr.new/@nuxt/ui-pro@7c62edd",
"@nuxthub/core": "^0.8.6",
"@nuxtjs/plausible": "^1.0.3",
"@nuxt/ui-pro": "https://pkg.pr.new/@nuxt/ui-pro@1408077",
"@nuxthub/core": "^0.8.7",
"@nuxtjs/plausible": "^1.1.1",
"@octokit/rest": "^21.0.2",
"@vueuse/nuxt": "^11.2.0",
"@vueuse/nuxt": "^11.3.0",
"joi": "^17.13.3",
"nuxt": "^3.14.159",
"nuxt": "^3.14.1592",
"nuxt-component-meta": "^0.9.0",
"nuxt-og-image": "^3.0.8",
"nuxt-og-image": "^3.1.1",
"prettier": "^3.3.3",
"shiki-transformer-color-highlight": "^0.2.0",
"superstruct": "^2.0.2",
@@ -27,6 +28,6 @@
"zod": "^3.23.8"
},
"devDependencies": {
"wrangler": "^3.86.1"
"wrangler": "^3.90.0"
}
}

View File

@@ -1,2 +1 @@
user-agent: *
disallow: /dev/*

View File

@@ -0,0 +1,505 @@
const locales: Record<string, string> = {
'aa': '🇪🇷',
'aa-ER': '🇪🇷',
'af': '🇳🇦',
'af-NA': '🇳🇦',
'af-ZA': '🇿🇦',
'am': '🇪🇹',
'am-ET': '🇪🇹',
'ar': '🇪🇬',
'ar-AE': '🇦🇪',
'ar-BH': '🇧🇭',
'ar-DJ': '🇩🇯',
'ar-DZ': '🇩🇿',
'ar-EG': '🇪🇬',
'ar-ER': '🇪🇷',
'ar-IL': '🇮🇱',
'ar-IQ': '🇮🇶',
'ar-JO': '🇯🇴',
'ar-KM': '🇰🇲',
'ar-KW': '🇰🇼',
'ar-LB': '🇱🇧',
'ar-LY': '🇱🇾',
'ar-MA': '🇲🇦',
'ar-MR': '🇲🇷',
'ar-OM': '🇴🇲',
'ar-PS': '🇵🇸',
'ar-QA': '🇶🇦',
'ar-SA': '🇸🇦',
'ar-SD': '🇸🇩',
'ar-SO': '🇸🇴',
'ar-SY': '🇸🇾',
'ar-TD': '🇹🇩',
'ar-TN': '🇹🇳',
'ar-YE': '🇾🇪',
'ay': '🇧🇴',
'ay-BO': '🇧🇴',
'az': '🇦🇿',
'az-AZ': '🇦🇿',
'be': '🇧🇾',
'be-BY': '🇧🇾',
'bg': '🇧🇬',
'bg-BG': '🇧🇬',
'bi': '🇻🇺',
'bi-VU': '🇻🇺',
'bn': '🇧🇩',
'bn-BD': '🇧🇩',
'bs': '🇧🇦',
'bs-BA': '🇧🇦',
'bs-ME': '🇲🇪',
'byn': '🇪🇷',
'byn-ER': '🇪🇷',
'ca': '🇦🇩',
'ca-AD': '🇦🇩',
'ch': '🇬🇺',
'ch-GU': '🇬🇺',
'ch-MP': '🇲🇵',
'cs': '🇨🇿',
'cs-CZ': '🇨🇿',
'da': '🇩🇰',
'da-DK': '🇩🇰',
'de': '🇩🇪',
'de-AT': '🇦🇹',
'de-BE': '🇧🇪',
'de-CH': '🇨🇭',
'de-DE': '🇩🇪',
'de-LI': '🇱🇮',
'de-LU': '🇱🇺',
'de-VA': '🇻🇦',
'dv': '🇲🇻',
'dv-MV': '🇲🇻',
'dz': '🇧🇹',
'dz-BT': '🇧🇹',
'el': '🇬🇷',
'el-CY': '🇨🇾',
'el-GR': '🇬🇷',
'en': '🇬🇧',
'en-AG': '🇦🇬',
'en-AI': '🇦🇮',
'en-AQ': '🇦🇶',
'en-AS': '🇦🇸',
'en-AU': '🇦🇺',
'en-BB': '🇧🇧',
'en-BM': '🇧🇲',
'en-BS': '🇧🇸',
'en-BW': '🇧🇼',
'en-BZ': '🇧🇿',
'en-CA': '🇨🇦',
'en-CC': '🇨🇨',
'en-CK': '🇨🇰',
'en-CM': '🇨🇲',
'en-CW': '🇨🇼',
'en-CX': '🇨🇽',
'en-DM': '🇩🇲',
'en-ER': '🇪🇷',
'en-FJ': '🇫🇯',
'en-FK': '🇫🇰',
'en-FM': '🇫🇲',
'en-GB': '🇬🇧',
'en-GD': '🇬🇩',
'en-GG': '🇬🇬',
'en-GH': '🇬🇭',
'en-GI': '🇬🇮',
'en-GM': '🇬🇲',
'en-GS': '🇬🇸',
'en-GU': '🇬🇺',
'en-GY': '🇬🇾',
'en-HK': '🇭🇰',
'en-HM': '🇭🇲',
'en-IE': '🇮🇪',
'en-IM': '🇮🇲',
'en-IN': '🇮🇳',
'en-IO': '🇮🇴',
'en-JE': '🇯🇪',
'en-JM': '🇯🇲',
'en-KE': '🇰🇪',
'en-KI': '🇰🇮',
'en-KN': '🇰🇳',
'en-KY': '🇰🇾',
'en-LC': '🇱🇨',
'en-LR': '🇱🇷',
'en-LS': '🇱🇸',
'en-MF': '🇲🇫',
'en-MH': '🇲🇭',
'en-MP': '🇲🇵',
'en-MS': '🇲🇸',
'en-MT': '🇲🇹',
'en-MU': '🇲🇺',
'en-MW': '🇲🇼',
'en-NA': '🇳🇦',
'en-NF': '🇳🇫',
'en-NG': '🇳🇬',
'en-NR': '🇳🇷',
'en-NU': '🇳🇺',
'en-NZ': '🇳🇿',
'en-PG': '🇵🇬',
'en-PH': '🇵🇭',
'en-PK': '🇵🇰',
'en-PN': '🇵🇳',
'en-PR': '🇵🇷',
'en-PW': '🇵🇼',
'en-RW': '🇷🇼',
'en-SB': '🇸🇧',
'en-SC': '🇸🇨',
'en-SD': '🇸🇩',
'en-SG': '🇸🇬',
'en-SH': '🇸🇭',
'en-SL': '🇸🇱',
'en-SS': '🇸🇸',
'en-SX': '🇸🇽',
'en-SZ': '🇸🇿',
'en-TC': '🇹🇨',
'en-TK': '🇹🇰',
'en-TO': '🇹🇴',
'en-TT': '🇹🇹',
'en-TV': '🇹🇻',
'en-TZ': '🇹🇿',
'en-UG': '🇺🇬',
'en-UM': '🇺🇲',
'en-US': '🇺🇸',
'en-VC': '🇻🇨',
'en-VG': '🇻🇬',
'en-VI': '🇻🇮',
'en-VU': '🇻🇺',
'en-WS': '🇼🇸',
'en-ZA': '🇿🇦',
'en-ZM': '🇿🇲',
'en-ZW': '🇿🇼',
'es': '🇪🇸',
'es-AR': '🇦🇷',
'es-BO': '🇧🇴',
'es-BZ': '🇧🇿',
'es-CL': '🇨🇱',
'es-CO': '🇨🇴',
'es-CR': '🇨🇷',
'es-CU': '🇨🇺',
'es-DO': '🇩🇴',
'es-EC': '🇪🇨',
'es-EH': '🇪🇭',
'es-ES': '🇪🇸',
'es-GQ': '🇬🇶',
'es-GT': '🇬🇹',
'es-GU': '🇬🇺',
'es-HN': '🇭🇳',
'es-MX': '🇲🇽',
'es-NI': '🇳🇮',
'es-PA': '🇵🇦',
'es-PE': '🇵🇪',
'es-PR': '🇵🇷',
'es-PY': '🇵🇾',
'es-SV': '🇸🇻',
'es-UY': '🇺🇾',
'es-VE': '🇻🇪',
'et': '🇪🇪',
'et-EE': '🇪🇪',
'fa': '🇮🇷',
'fa-IR': '🇮🇷',
'fan': '🇬🇶',
'fan-GQ': '🇬🇶',
'ff': '🇧🇫',
'ff-BF': '🇧🇫',
'ff-GN': '🇬🇳',
'fi': '🇫🇮',
'fi-FI': '🇫🇮',
'fj': '🇫🇯',
'fj-FJ': '🇫🇯',
'fo': '🇫🇴',
'fo-FO': '🇫🇴',
'fr': '🇫🇷',
'fr-BE': '🇧🇪',
'fr-BF': '🇧🇫',
'fr-BI': '🇧🇮',
'fr-BJ': '🇧🇯',
'fr-BL': '🇧🇱',
'fr-CA': '🇨🇦',
'fr-CD': '🇨🇩',
'fr-CF': '🇨🇫',
'fr-CG': '🇨🇬',
'fr-CH': '🇨🇭',
'fr-CI': '🇨🇮',
'fr-CM': '🇨🇲',
'fr-DJ': '🇩🇯',
'fr-FR': '🇫🇷',
'fr-GA': '🇬🇦',
'fr-GF': '🇬🇫',
'fr-GG': '🇬🇬',
'fr-GN': '🇬🇳',
'fr-GP': '🇬🇵',
'fr-GQ': '🇬🇶',
'fr-HT': '🇭🇹',
'fr-JE': '🇯🇪',
'fr-KM': '🇰🇲',
'fr-LB': '🇱🇧',
'fr-LU': '🇱🇺',
'fr-MC': '🇲🇨',
'fr-MF': '🇲🇫',
'fr-MG': '🇲🇬',
'fr-ML': '🇲🇱',
'fr-MQ': '🇲🇶',
'fr-NC': '🇳🇨',
'fr-NE': '🇳🇪',
'fr-PF': '🇵🇫',
'fr-PM': '🇵🇲',
'fr-RE': '🇷🇪',
'fr-RW': '🇷🇼',
'fr-SC': '🇸🇨',
'fr-SN': '🇸🇳',
'fr-TD': '🇹🇩',
'fr-TF': '🇹🇫',
'fr-TG': '🇹🇬',
'fr-VA': '🇻🇦',
'fr-VU': '🇻🇺',
'fr-WF': '🇼🇫',
'fr-YT': '🇾🇹',
'ga': '🇮🇪',
'ga-IE': '🇮🇪',
'gn': '🇦🇷',
'gn-AR': '🇦🇷',
'gn-PY': '🇵🇾',
'gv': '🇮🇲',
'gv-IM': '🇮🇲',
'he': '🇮🇱',
'he-IL': '🇮🇱',
'hi': '🇮🇳',
'hi-IN': '🇮🇳',
'hif': '🇫🇯',
'hif-FJ': '🇫🇯',
'hr': '🇭🇷',
'hr-BA': '🇧🇦',
'hr-HR': '🇭🇷',
'hr-ME': '🇲🇪',
'ht': '🇭🇹',
'ht-HT': '🇭🇹',
'hu': '🇭🇺',
'hu-HU': '🇭🇺',
'hy': '🇦🇲',
'hy-AM': '🇦🇲',
'hy-CY': '🇨🇾',
'id': '🇮🇩',
'id-ID': '🇮🇩',
'is': '🇮🇸',
'is-IS': '🇮🇸',
'it': '🇮🇹',
'it-CH': '🇨🇭',
'it-IT': '🇮🇹',
'it-SM': '🇸🇲',
'it-VA': '🇻🇦',
'ja': '🇯🇵',
'ja-JP': '🇯🇵',
'ka': '🇬🇪',
'ka-GE': '🇬🇪',
'kg': '🇨🇩',
'kg-CD': '🇨🇩',
'kk': '🇰🇿',
'kk-KZ': '🇰🇿',
'kl': '🇬🇱',
'kl-GL': '🇬🇱',
'km': '🇰🇭',
'km-KH': '🇰🇭',
'ko': '🇰🇵',
'ko-KP': '🇰🇵',
'ko-KR': '🇰🇷',
'ku': '🇮🇶',
'ku-IQ': '🇮🇶',
'kun': '🇪🇷',
'kun-ER': '🇪🇷',
'ky': '🇰🇬',
'ky-KG': '🇰🇬',
'la': '🇻🇦',
'la-VA': '🇻🇦',
'lb': '🇱🇺',
'lb-LU': '🇱🇺',
'ln': '🇨🇩',
'ln-CD': '🇨🇩',
'ln-CG': '🇨🇬',
'lo': '🇱🇦',
'lo-LA': '🇱🇦',
'lt': '🇱🇹',
'lt-LT': '🇱🇹',
'lu': '🇨🇩',
'lu-CD': '🇨🇩',
'lv': '🇱🇻',
'lv-LV': '🇱🇻',
'mg': '🇲🇬',
'mg-MG': '🇲🇬',
'mh': '🇲🇭',
'mh-MH': '🇲🇭',
'mi': '🇳🇿',
'mi-NZ': '🇳🇿',
'mk': '🇲🇰',
'mk-MK': '🇲🇰',
'mn': '🇲🇳',
'mn-MN': '🇲🇳',
'ms': '🇧🇳',
'ms-BN': '🇧🇳',
'ms-MY': '🇲🇾',
'ms-SG': '🇸🇬',
'mt': '🇲🇹',
'mt-MT': '🇲🇹',
'my': '🇲🇲',
'my-MM': '🇲🇲',
'na': '🇳🇷',
'na-NR': '🇳🇷',
'nb': '🇧🇻',
'nb-BV': '🇧🇻',
'nb-NO': '🇳🇴',
'nd': '🇿🇼',
'nd-ZW': '🇿🇼',
'ne': '🇳🇵',
'ne-NP': '🇳🇵',
'nl': '🇳🇱',
'nl-AW': '🇦🇼',
'nl-BE': '🇧🇪',
'nl-BQ': '🇧🇶',
'nl-CW': '🇨🇼',
'nl-MF': '🇲🇫',
'nl-NL': '🇳🇱',
'nl-SR': '🇸🇷',
'nl-SX': '🇸🇽',
'nn': '🇧🇻',
'nn-BV': '🇧🇻',
'nn-NO': '🇳🇴',
'no': '🇳🇴',
'no-BV': '🇧🇻',
'no-NO': '🇳🇴',
'no-SJ': '🇸🇯',
'nr': '🇿🇦',
'nr-ZA': '🇿🇦',
'nrb': '🇪🇷',
'nrb-ER': '🇪🇷',
'ny': '🇲🇼',
'ny-MW': '🇲🇼',
'pa': '🇦🇼',
'pa-AW': '🇦🇼',
'pa-CW': '🇨🇼',
'pl': '🇵🇱',
'pl-PL': '🇵🇱',
'ps': '🇦🇫',
'ps-AF': '🇦🇫',
'pt': '🇵🇹',
'pt-AO': '🇦🇴',
'pt-BR': '🇧🇷',
'pt-CV': '🇨🇻',
'pt-GQ': '🇬🇶',
'pt-GW': '🇬🇼',
'pt-MO': '🇲🇴',
'pt-MZ': '🇲🇿',
'pt-PT': '🇵🇹',
'pt-ST': '🇸🇹',
'pt-TL': '🇹🇱',
'qu': '🇧🇴',
'qu-BO': '🇧🇴',
'rar': '🇨🇰',
'rar-CK': '🇨🇰',
'rm': '🇨🇭',
'rm-CH': '🇨🇭',
'rn': '🇧🇮',
'rn-BI': '🇧🇮',
'ro': '🇷🇴',
'ro-MD': '🇲🇩',
'ro-RO': '🇷🇴',
'rtm': '🇫🇯',
'rtm-FJ': '🇫🇯',
'ru': '🇷🇺',
'ru-AQ': '🇦🇶',
'ru-BY': '🇧🇾',
'ru-KG': '🇰🇬',
'ru-KZ': '🇰🇿',
'ru-RU': '🇷🇺',
'ru-TJ': '🇹🇯',
'ru-TM': '🇹🇲',
'ru-UZ': '🇺🇿',
'rw': '🇷🇼',
'rw-RW': '🇷🇼',
'sg': '🇨🇫',
'sg-CF': '🇨🇫',
'si': '🇱🇰',
'si-LK': '🇱🇰',
'sk': '🇸🇰',
'sk-CZ': '🇨🇿',
'sk-SK': '🇸🇰',
'sl': '🇸🇮',
'sl-SI': '🇸🇮',
'sm': '🇦🇸',
'sm-AS': '🇦🇸',
'sm-WS': '🇼🇸',
'sn': '🇿🇼',
'sn-ZW': '🇿🇼',
'so': '🇸🇴',
'so-SO': '🇸🇴',
'sq': '🇦🇱',
'sq-AL': '🇦🇱',
'sq-ME': '🇲🇪',
'sq-XK': '🇽🇰',
'sr': '🇧🇦',
'sr-BA': '🇧🇦',
'sr-ME': '🇲🇪',
'sr-RS': '🇷🇸',
'sr-XK': '🇽🇰',
'ss': '🇸🇿',
'ss-SZ': '🇸🇿',
'ss-ZA': '🇿🇦',
'ssy': '🇪🇷',
'ssy-ER': '🇪🇷',
'st': '🇱🇸',
'st-LS': '🇱🇸',
'st-ZA': '🇿🇦',
'sv': '🇦🇽',
'sv-AX': '🇦🇽',
'sv-FI': '🇫🇮',
'sv-SE': '🇸🇪',
'sw': '🇨🇩',
'sw-CD': '🇨🇩',
'sw-KE': '🇰🇪',
'sw-TZ': '🇹🇿',
'sw-UG': '🇺🇬',
'ta': '🇸🇬',
'ta-LK': '🇱🇰',
'ta-SG': '🇸🇬',
'tg': '🇹🇯',
'tg-TJ': '🇹🇯',
'th': '🇹🇭',
'th-TH': '🇹🇭',
'ti': '🇪🇷',
'ti-ER': '🇪🇷',
'tig': '🇪🇷',
'tig-ER': '🇪🇷',
'tk': '🇦🇫',
'tk-AF': '🇦🇫',
'tk-TM': '🇹🇲',
'tn': '🇧🇼',
'tn-BW': '🇧🇼',
'tn-ZA': '🇿🇦',
'to': '🇹🇴',
'to-TO': '🇹🇴',
'tr': '🇹🇷',
'tr-CY': '🇨🇾',
'tr-TR': '🇹🇷',
'ts': '🇿🇦',
'ts-ZA': '🇿🇦',
'uk': '🇺🇦',
'uk-UA': '🇺🇦',
'ur': '🇵🇰',
'ur-PK': '🇵🇰',
'uz': '🇺🇿',
'uz-AF': '🇦🇫',
'uz-UZ': '🇺🇿',
've': '🇿🇦',
've-ZA': '🇿🇦',
'vi': '🇻🇳',
'vi-VN': '🇻🇳',
'xh': '🇿🇦',
'xh-ZA': '🇿🇦',
'zh': '🇨🇳',
'zh-CN': '🇨🇳',
'zh-HK': '🇭🇰',
'zh-Hans': '🇨🇳',
'zh-Hant': '🇨🇳',
'zh-MO': '🇲🇴',
'zh-SG': '🇸🇬',
'zh-TW': '🇹🇼',
'zu': '🇿🇦',
'zu-ZA': '🇿🇦'
}
export default eventHandler(async () => locales)

View File

@@ -1,8 +1,8 @@
{
"name": "@nuxt/ui",
"description": "A UI Library for Modern Web Apps, powered by Vue & Tailwind CSS.",
"version": "3.0.0-alpha.8",
"packageManager": "pnpm@9.12.3",
"version": "3.0.0-alpha.9",
"packageManager": "pnpm@9.14.2",
"repository": {
"type": "git",
"url": "git+https://github.com/nuxt/ui.git"
@@ -55,6 +55,7 @@
"build": "nuxt-module-build build && pnpm devtools:build",
"prepack": "pnpm build",
"dev": "DEV=true nuxi dev playground",
"dev:vue": "DEV=true vite playground-vue",
"dev:build": "nuxi build playground",
"dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxi prepare playground && nuxi prepare docs && nuxi prepare devtools && vite build playground-vue",
"devtools": "NUXT_UI_DEVTOOLS_LOCAL=true nuxi dev playground",
@@ -68,67 +69,70 @@
"typecheck": "vue-tsc --noEmit && nuxi typecheck playground && nuxi typecheck docs && nuxi typecheck devtools && cd playground-vue && vue-tsc --noEmit",
"test": "vitest",
"test:vue": "vitest -c vitest.vue.config.ts",
"test:vue:build": "vite build playground-vue",
"release": "release-it --preRelease=alpha --npm.tag=next"
},
"dependencies": {
"@iconify/vue": "^4.1.2",
"@nuxt/devtools-kit": "^1.6.0",
"@internationalized/date": "^3.6.0",
"@internationalized/number": "^3.6.0",
"@nuxt/devtools-kit": "^1.6.1",
"@nuxt/fonts": "^0.10.2",
"@nuxt/icon": "^1.7.2",
"@nuxt/kit": "^3.14.159",
"@nuxt/schema": "^3.14.159",
"@nuxt/icon": "^1.8.2",
"@nuxt/kit": "^3.14.1592",
"@nuxt/schema": "^3.14.1592",
"@nuxtjs/color-mode": "^3.5.2",
"@tailwindcss/postcss": "4.0.0-alpha.33",
"@tailwindcss/vite": "4.0.0-alpha.33",
"@tailwindcss/postcss": "4.0.0-beta.2",
"@tailwindcss/vite": "4.0.0-beta.2",
"@tanstack/vue-table": "^8.20.5",
"@unhead/vue": "^1.11.11",
"@vueuse/core": "^11.2.0",
"@vueuse/integrations": "^11.2.0",
"@vueuse/core": "^11.3.0",
"@vueuse/integrations": "^11.3.0",
"consola": "^3.2.3",
"defu": "^6.1.4",
"embla-carousel-auto-height": "^8.3.1",
"embla-carousel-auto-scroll": "^8.3.1",
"embla-carousel-autoplay": "^8.3.1",
"embla-carousel-class-names": "^8.3.1",
"embla-carousel-fade": "^8.3.1",
"embla-carousel-vue": "^8.3.1",
"embla-carousel-auto-height": "^8.5.1",
"embla-carousel-auto-scroll": "^8.5.1",
"embla-carousel-autoplay": "^8.5.1",
"embla-carousel-class-names": "^8.5.1",
"embla-carousel-fade": "^8.5.1",
"embla-carousel-vue": "^8.5.1",
"embla-carousel-wheel-gestures": "^8.0.1",
"fuse.js": "^7.0.0",
"get-port-please": "^3.1.2",
"knitwork": "^1.1.0",
"magic-string": "^0.30.12",
"mlly": "^1.7.2",
"magic-string": "^0.30.13",
"mlly": "^1.7.3",
"ohash": "^1.1.4",
"pathe": "^1.1.2",
"radix-vue": "^1.9.9",
"radix-vue": "^1.9.10",
"scule": "^1.3.0",
"sirv": "^3.0.0",
"tailwind-variants": "^0.2.1",
"tailwindcss": "4.0.0-alpha.33",
"tailwind-variants": "^0.3.0",
"tailwindcss": "4.0.0-beta.2",
"tinyglobby": "^0.2.10",
"unplugin": "^1.15.0",
"unplugin-auto-import": "^0.18.3",
"unplugin": "^1.16.0",
"unplugin-auto-import": "^0.18.5",
"unplugin-vue-components": "^0.27.4",
"vaul-vue": "^0.2.0"
},
"devDependencies": {
"@nuxt/eslint-config": "^0.6.1",
"@nuxt/eslint-config": "^0.7.1",
"@nuxt/module-builder": "^0.8.4",
"@nuxt/test-utils": "^3.14.4",
"@release-it/conventional-changelog": "^9.0.2",
"@release-it/conventional-changelog": "^9.0.3",
"@standard-schema/spec": "1.0.0-beta.3",
"@vue/test-utils": "^2.4.6",
"embla-carousel": "^8.3.1",
"eslint": "^9.14.0",
"embla-carousel": "^8.5.1",
"eslint": "^9.15.0",
"happy-dom": "^15.7.4",
"joi": "^17.13.3",
"knitwork": "^1.1.0",
"nuxt": "^3.14.159",
"nuxt": "^3.14.1592",
"nuxt-component-meta": "^0.9.0",
"release-it": "^17.10.0",
"superstruct": "^2.0.2",
"valibot": "^0.42.1",
"vitest": "^2.1.4",
"vitest": "^2.1.5",
"vitest-environment-nuxt": "^1.0.1",
"vue-tsc": "^2.1.10",
"yup": "^1.4.0",
@@ -139,8 +143,8 @@
},
"resolutions": {
"@nuxt/ui": "workspace:*",
"@nuxt/content": "3.0.0-alpha.5",
"happy-dom": "14.12.3",
"rollup": "^4.24.0"
"rollup": "^4.24.0",
"typescript": "5.6.3"
}
}

View File

@@ -10,13 +10,13 @@
},
"dependencies": {
"@nuxt/ui": "latest",
"vue": "^3.5.12",
"vue": "^3.5.13",
"vue-router": "^4.4.5"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.1.5",
"typescript": "^5.6.3",
"unplugin-auto-import": "^0.18.3",
"@vitejs/plugin-vue": "^5.2.0",
"typescript": "^5.7.2",
"unplugin-auto-import": "^0.18.5",
"unplugin-vue-components": "^0.27.4",
"vite": "^5.4.11",
"vue-tsc": "^2.1.10"

View File

@@ -2,12 +2,14 @@
import { splitByCase, upperFirst } from 'scule'
import { useRouter } from 'vue-router'
import { reactive, ref } from 'vue'
import { useColorMode } from '@vueuse/core'
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore included for compatibility with Nuxt playground
import { useAppConfig } from '#imports'
const appConfig = useAppConfig()
const mode = useColorMode()
appConfig.toaster = reactive({
position: 'bottom-right' as const,
@@ -43,6 +45,7 @@ const components = [
'modal',
'navigation-menu',
'pagination',
'pin-input',
'popover',
'progress',
'radio-group',
@@ -81,9 +84,19 @@ defineShortcuts({
<UApp :toaster="(appConfig.toaster as any)">
<div class="h-screen w-screen overflow-hidden flex min-h-0 bg-[var(--ui-bg)]" vaul-drawer-wrapper>
<UNavigationMenu :items="items" orientation="vertical" class="hidden lg:flex border-e border-[var(--ui-border)] overflow-y-auto w-48 p-4" />
<UNavigationMenu :items="items" orientation="horizontal" class="lg:hidden border-b border-[var(--ui-border)] overflow-x-auto" />
<UNavigationMenu :items="items" orientation="horizontal" class="lg:hidden border-b border-[var(--ui-border)] [&>div]:min-w-min overflow-x-auto" />
<div class="flex-1 flex flex-col items-center justify-around overflow-y-auto w-full py-12 px-4">
<div class="fixed top-15 lg:top-3 right-4 flex items-center gap-2">
<UButton
:icon="mode === 'dark' ? 'i-lucide-moon' : 'i-lucide-sun'"
color="neutral"
variant="ghost"
:aria-label="`Switch to ${mode === 'dark' ? 'light' : 'dark'} mode`"
@click="mode = mode === 'dark' ? 'light' : 'dark'"
/>
</div>
<div class="flex-1 flex flex-col items-center justify-around overflow-y-auto w-full py-14 px-4">
<Suspense>
<RouterView />
</Suspense>
@@ -103,7 +116,7 @@ defineShortcuts({
@import "@nuxt/ui";
@theme {
--font-family-sans: 'Public Sans', sans-serif;
--font-sans: 'Public Sans', sans-serif;
--color-green-50: #EFFDF5;
--color-green-100: #D9FBE8;

View File

@@ -1,8 +1,19 @@
<script setup lang="ts">
import { splitByCase, upperFirst } from 'scule'
import { useColorMode } from '#imports'
const appConfig = useAppConfig()
const router = useRouter()
const appConfig = useAppConfig()
const colorMode = useColorMode()
const isDark = computed({
get() {
return colorMode.value === 'dark'
},
set() {
colorMode.preference = colorMode.value === 'dark' ? 'light' : 'dark'
}
})
const components = [
'accordion',
@@ -13,6 +24,7 @@ const components = [
'button',
'button-group',
'card',
'calendar',
'carousel',
'checkbox',
'chip',
@@ -25,11 +37,13 @@ const components = [
'form-field',
'input',
'input-menu',
'input-number',
'kbd',
'link',
'modal',
'navigation-menu',
'pagination',
'pin-input',
'popover',
'progress',
'radio-group',
@@ -70,9 +84,25 @@ defineShortcuts({
<UApp :toaster="appConfig.toaster">
<div class="h-screen w-screen overflow-hidden flex flex-col lg:flex-row min-h-0 bg-[var(--ui-bg)]" vaul-drawer-wrapper>
<UNavigationMenu :items="items" orientation="vertical" class="hidden lg:flex border-e border-[var(--ui-border)] overflow-y-auto w-48 p-4" />
<UNavigationMenu :items="items" orientation="horizontal" class="lg:hidden border-b border-[var(--ui-border)] overflow-x-auto" />
<UNavigationMenu :items="items" orientation="horizontal" class="lg:hidden border-b border-[var(--ui-border)] [&>div]:min-w-min overflow-x-auto" />
<div class="flex-1 flex flex-col items-center justify-around overflow-y-auto w-full py-12 px-4">
<div class="fixed top-15 lg:top-3 right-4 flex items-center gap-2">
<ClientOnly v-if="!colorMode?.forced">
<UButton
:icon="isDark ? 'i-lucide-moon' : 'i-lucide-sun'"
color="neutral"
variant="ghost"
:aria-label="`Switch to ${isDark ? 'light' : 'dark'} mode`"
@click="isDark = !isDark"
/>
<template #fallback>
<div class="size-8" />
</template>
</ClientOnly>
</div>
<div class="flex-1 flex flex-col items-center justify-around overflow-y-auto w-full py-14 px-4">
<NuxtPage />
</div>
@@ -94,7 +124,7 @@ defineShortcuts({
@import "@nuxt/ui";
@theme {
--font-family-sans: 'Public Sans', sans-serif;
--font-sans: 'Public Sans', sans-serif;
--color-green-50: #EFFDF5;
--color-green-100: #D9FBE8;

View File

@@ -0,0 +1,20 @@
<script setup lang="ts">
import { CalendarDate } from '@internationalized/date'
const singleValue = shallowRef(new CalendarDate(2022, 1, 10))
const multipleValue = shallowRef({
start: new CalendarDate(2022, 1, 10),
end: new CalendarDate(2022, 1, 20)
})
</script>
<template>
<div class="flex flex-col gap-4">
<div class="flex justify-center gap-2">
<UCalendar v-model="singleValue" />
</div>
<div class="flex justify-center gap-2">
<UCalendar v-model="multipleValue" range />
</div>
</div>
</template>

View File

@@ -105,13 +105,13 @@ defineShortcuts(extractShortcuts(items.value))
<USelect v-model="size" :items="sizes" placeholder="Size" />
</div>
<UContextMenu :items="items" class="min-w-48" :size="size">
<UContextMenu :items="items" :ui="{ content: 'w-48' }" :size="size">
<div class="flex items-center justify-center rounded-md border border-dashed border-[var(--ui-border-accented)] text-sm aspect-video w-72">
Right click here
</div>
</UContextMenu>
<UContextMenu :items="itemsWithColor" class="min-w-48" :size="size">
<UContextMenu :items="itemsWithColor" :ui="{ content: 'w-48' }" :size="size">
<div class="flex items-center justify-center rounded-md border border-dashed border-[var(--ui-border-accented)] text-sm aspect-video w-72">
Color right click here
</div>

View File

@@ -140,7 +140,7 @@ defineShortcuts(extractShortcuts(items.value))
<div class="flex flex-col items-center gap-8">
<USelectMenu v-model="size" :items="sizes" placeholder="Size" />
<UDropdownMenu :items="items" :size="size" arrow :content="{ side: 'bottom', align: 'start' }" class="min-w-48">
<UDropdownMenu :items="items" :size="size" arrow :content="{ side: 'bottom', align: 'start' }" :ui="{ content: 'w-48' }">
<UButton label="Open" color="neutral" variant="outline" icon="i-lucide-menu" />
<template #custom-trailing>
@@ -148,7 +148,7 @@ defineShortcuts(extractShortcuts(items.value))
</template>
</UDropdownMenu>
<UDropdownMenu :items="itemsWithColor" :size="size" arrow :content="{ side: 'bottom', align: 'start' }" class="min-w-48">
<UDropdownMenu :items="itemsWithColor" :size="size" arrow :content="{ side: 'bottom', align: 'start' }" :ui="{ content: 'w-48' }">
<UButton label="Color" color="neutral" variant="outline" icon="i-lucide-menu" />
<template #custom-trailing>

View File

@@ -0,0 +1,68 @@
<template>
<div class="flex flex-col items-center gap-4">
<div class="flex flex-col gap-4 w-48">
<UInputNumber />
</div>
<div class="flex items-center gap-2">
<UInputNumber
v-for="variant in variants"
:key="variant"
:placeholder="upperFirst(variant)"
:variant="variant"
class="w-48"
/>
</div>
<div class="flex items-center gap-2">
<UInputNumber
v-for="variant in variants"
:key="variant"
:placeholder="upperFirst(variant)"
:variant="variant"
color="neutral"
class="w-48"
/>
</div>
<div class="flex items-center gap-2">
<UInputNumber
v-for="variant in variants"
:key="variant"
:placeholder="upperFirst(variant)"
:variant="variant"
color="error"
highlight
class="w-48"
/>
</div>
<div class="flex flex-col gap-4 w-48">
<UInputNumber placeholder="Disabled" disabled />
<UInputNumber placeholder="Required" required />
</div>
<div class="flex items-center gap-4">
<UInputNumber
v-for="size in sizes"
:key="size"
:size="size"
:placeholder="`Horizontal ${size}`"
class="w-48"
/>
</div>
<div class="flex items-center gap-4">
<UInputNumber
v-for="size in sizes"
:key="size"
:size="size"
class="w-48"
:placeholder="`Vertical ${size}`"
orientation="vertical"
/>
</div>
</div>
</template>
<script setup lang="ts">
import { upperFirst } from 'scule'
import theme from '#build/ui/input-number'
const sizes = Object.keys(theme.variants.size) as Array<keyof typeof theme.variants.size>
const variants = Object.keys(theme.variants.variant) as Array<keyof typeof theme.variants.variant>
</script>

View File

@@ -0,0 +1,52 @@
<script setup lang="ts">
import theme from '#build/ui/pin-input'
const sizes = Object.keys(theme.variants.size) as Array<keyof typeof theme.variants.size>
const variants = Object.keys(theme.variants.variant) as Array<keyof typeof theme.variants.variant>
const onComplete = (e: string[]) => {
alert(e.join(''))
}
</script>
<template>
<div class="flex flex-col items-center gap-4">
<div class="flex gap-4">
<UPinInput placeholder="○" autofocus @complete="onComplete" />
</div>
<div class="flex items-center gap-4">
<UPinInput v-for="variant in variants" :key="variant" placeholder="○" :variant="variant" />
</div>
<div class="flex items-center gap-4">
<UPinInput
v-for="variant in variants"
:key="variant"
placeholder="○"
:variant="variant"
color="neutral"
/>
</div>
<div class="flex items-center gap-4">
<UPinInput
v-for="variant in variants"
:key="variant"
placeholder="○"
:variant="variant"
color="error"
highlight
/>
</div>
<div class="flex flex-col gap-4">
<UPinInput placeholder="○" disabled />
<UPinInput placeholder="○" required />
</div>
<div class="flex items-center gap-4">
<UPinInput
v-for="size in sizes"
:key="size"
placeholder="○"
:size="size"
/>
</div>
</div>
</template>

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