Compare commits

...

171 Commits

Author SHA1 Message Date
Benjamin Canac
02015e5803 Merge branch 'v3' into pr/1945 2025-07-21 17:03:34 +02:00
J-Michalek
62ab01655c feat(Tabs): add badge on items (#4553)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2025-07-21 16:55:27 +02:00
Benjamin Canac
42b6c96df2 up 2025-07-21 15:36:19 +02:00
Benjamin Canac
5989229744 up 2025-07-21 15:33:58 +02:00
Benjamin Canac
fe1cf02c51 up 2025-07-21 15:19:05 +02:00
Benjamin Canac
66b08fdf82 Merge branch 'v3' into pr/1945 2025-07-21 14:29:54 +02:00
Benjamin Canac
0da34cd8e5 up 2025-07-21 14:27:28 +02:00
Alex
f33660035f feat(Kbd): add color prop & soft variant (#4549) 2025-07-21 14:22:07 +02:00
Benjamin Canac
891d66cdb1 up 2025-07-21 13:10:10 +02:00
renovate[bot]
657ec228b5 chore(deps): update nuxt framework to ^4.0.1 (v3) (#4562)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-21 11:51:01 +02:00
Benjamin Canac
90660c97c3 up 2025-07-21 11:26:42 +02:00
Hugo Richard
e9d515cb85 docs(migration): update form validation error property name (#4557) 2025-07-21 11:24:49 +02:00
Benjamin Canac
20a1240015 Merge branch 'v3' into pr/1945 2025-07-21 11:03:03 +02:00
renovate[bot]
f32cfeef9e chore(deps): update all non-major dependencies (v3) (#4527)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-21 10:25:24 +02:00
Alex
6b6ec8cb2c fix(ColorPicker): update color conversion logic (#4550) 2025-07-19 19:49:18 +02:00
Benjamin Canac
1d692fb524 up 2025-07-18 18:38:27 +02:00
Benjamin Canac
3e836dfe4d Merge branch 'v3' into pr/1945 2025-07-18 17:28:20 +02:00
Benjamin Canac
e2695ee7e4 feat(Drawer): add nested prop
Resolves #4320
2025-07-18 15:55:24 +02:00
Benjamin Canac
8dca270965 up 2025-07-18 15:11:22 +02:00
Benjamin Canac
bfa6460613 up 2025-07-18 14:38:05 +02:00
Benjamin Canac
91a3f311b1 up 2025-07-17 16:06:42 +02:00
Benjamin Canac
c577df7eb1 Merge branch 'v3' into pr/1945 2025-07-17 12:31:24 +02:00
Benjamin Canac
cad7c45c08 chore: remove future.compatibilityVersion 2025-07-17 11:20:59 +02:00
Benjamin Canac
a2b84d7f62 Merge branch 'v3' into pr/1945 2025-07-17 10:56:15 +02:00
Benjamin Canac
5db3b0f98c docs(icons): add app prefix to assets 2025-07-17 10:56:08 +02:00
J-Michalek
6ca7c8b7bf feat(InputMenu): emit remove-tag event (#4511) 2025-07-16 22:28:45 +02:00
kyyy
bb99345f5b fix(RadioGroup): improve type safety for normalizeItem function (#4535)
Co-authored-by: Sandro Circi <sandro.circi@digitoolmedia.com>
2025-07-16 21:58:05 +02:00
Thilo Hettmer
c64c4cdea0 fix(FormField): resolve minor accessibility and rendering issues (#4515)
Co-authored-by: Romain Hamel <rom.hml@gmail.com>
2025-07-16 21:55:57 +02:00
Benjamin Canac
d12160927e Merge branch 'v3' into pr/1945 2025-07-16 21:52:54 +02:00
renovate[bot]
8b42365bf4 chore(deps): lock file maintenance (v3) (#4539)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-16 15:37:18 +02:00
Benjamin Canac
cb160e6971 fix(InputMenu): reset search term on mounted
Resolves #3993
2025-07-16 12:52:58 +02:00
Benjamin Canac
4d4234d2f8 fix(InputMenu/SelectMenu): improve display value without valueKey
Resolves #4528
2025-07-16 12:43:38 +02:00
renovate[bot]
6f38d3ea8a chore(deps): update nuxt framework to v4 (v3) (major) (#4532)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2025-07-16 12:09:41 +02:00
Benjamin Canac
1b14b5dcd9 cli(templates): fix component type 2025-07-16 10:22:17 +02:00
Benjamin Canac
01c8f3bf5e feat(FileUpload): new component
Co-Authored-By: Vachmara <55046446+vachmara@users.noreply.github.com>
2025-07-16 10:22:06 +02:00
Benjamin Canac
10450c537d cli(templates): fix component type 2025-07-14 14:49:23 +02:00
Artem Alesenko
7ef19333f0 feat(Table): add support for colspan and rowspan (#4460)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2025-07-14 12:18:17 +02:00
renovate[bot]
d983af93b3 chore(deps): update dependency zod to v4 (v3) (#4523)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2025-07-14 11:58:02 +02:00
J-Michalek
1db21d1b00 feat(Table): add style to table and column meta (#4513)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2025-07-14 10:56:28 +02:00
J-Michalek
6f2ce5c610 refactor(components): unite syntax for emits declaration (#4512) 2025-07-14 10:46:47 +02:00
Benjamin Canac
488707e148 fix(InputMenu/SelectMenu): filter null items in search 2025-07-14 10:40:29 +02:00
J-Michalek
ef473c3848 fix(defineShortcuts): always pass event to shotcut handler (#4516) 2025-07-14 10:34:20 +02:00
renovate[bot]
93dff3264f chore(deps): update all non-major dependencies (v3) (#4479)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-14 10:23:04 +02:00
renovate[bot]
5da9084da3 chore(deps): update nuxt framework to ^3.17.7 (v3) (#4518)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-14 09:58:50 +02:00
Benjamin Canac
c92f908b8d fix(InputMenu/SelectMenu): only filter non-null fields
Resolves #4509
2025-07-12 12:15:29 +02:00
Benjamin Canac
45553dc3fe chore(github): update stale workflow 2025-07-12 12:05:24 +02:00
Benjamin Canac
55e06e97e7 fix(Carousel): improve accessibility
Resolves #4494
2025-07-10 14:18:36 +02:00
Eugen Istoc
a813ea700e test(useOverlay): add composable tests (#4482)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2025-07-10 12:04:18 +02:00
Benjamin Canac
a4d0ca7396 fix(FormField): improve error type with boolean
Resolves #4496
2025-07-10 12:04:06 +02:00
Eugen Istoc
5ad7dabbdc fix(useOverlay): don't use patch when passing props to open (#4497) 2025-07-10 10:45:55 +02:00
Benjamin Canac
d8160ba6ef fix(vue): stub clearError 2025-07-09 17:48:30 +02:00
Alex
fc24e03cc4 fix(Carousel/Tree): add type to button elements for accessibility (#4493) 2025-07-09 17:43:49 +02:00
Benjamin Canac
1902492cf2 docs(pricing): add banner 2025-07-09 16:17:24 +02:00
Benjamin Canac
0c525638d7 chore(deps): update @nuxt/ui-pro 2025-07-09 16:17:09 +02:00
Benjamin Canac
35f90b9920 feat(module): add theme.defaultVariants option (#4400) 2025-07-09 14:37:49 +02:00
Benjamin Canac
836f74849b fix(NavigationMenu/Tabs): proxy fallthrough attributes 2025-07-09 12:58:35 +02:00
Benjamin Canac
78f92a24f8 fix(module): merge user's options when installing modules 2025-07-09 12:24:18 +02:00
Benjamin Canac
52908c19f1 docs(app): update banner 2025-07-08 15:38:00 +02:00
J-Michalek
513cca25f6 docs(input-menu/select-menu/select): add example with full item width (#4419)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2025-07-08 12:43:49 +02:00
Benjamin Canac
c1427a3264 docs(ComponentSlots): hide ui slot prop content 2025-07-08 11:48:05 +02:00
Eugen Istoc
6519a74de4 fix(useOverlay): improve props handling by merging existing and new (#4478) 2025-07-08 10:51:34 +02:00
Benjamin Canac
da05c37ffe docs(input): hide password reveal button in edge
Resolves #4484
2025-07-08 10:39:25 +02:00
Benjamin Canac
ec569e427b feat(Toast): progress bar with Progress component 2025-07-07 17:10:05 +02:00
Benjamin Canac
1d052ec565 fix(Toast): only show progress when open
Resolves #4464
2025-07-07 16:40:59 +02:00
J-Michalek
1ba8a55bcb fix(Carousel): add aria-current attribute to active dot (#4447)
Co-authored-by: Jakub <jakub.michalek@freelo.io>
2025-07-07 12:09:57 +02:00
Hugo Richard
63730d684b feat(CommandPalette): add footer slot (#4457)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2025-07-07 12:01:26 +02:00
renovate[bot]
9ab184cc24 chore(deps): lock file maintenance (v3) (#4472)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-07 11:11:01 +02:00
renovate[bot]
ad0e4ddbf4 chore(deps): update vue-tsc to ^3.0.1 (v3) (#4470)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-07 10:43:27 +02:00
renovate[bot]
6a93556aed chore(deps): update all non-major dependencies (v3) (#4469)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-07 10:29:55 +02:00
TonyWeb
9debce737c fix(Button/Link): merge active-class / inactive-class with app config (#4446)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2025-07-03 16:18:27 +02:00
Sylvain Marroufin
772631cde9 fix(defineShortcuts): allow extra keys to be combined with shift (#4456) 2025-07-03 15:51:50 +02:00
Teages
d7aefa53b2 fix(useOverlay): support infering close argument from complex emits (#4414)
Co-authored-by: Eugen Istoc <eugenistoc@gmail.com>
2025-07-03 10:45:19 +02:00
renovate[bot]
8922c7388e chore(deps): update all non-major dependencies (v3) (#4448)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2025-07-02 17:31:53 +02:00
Igor G
c355cacd43 feat(Table): add footer support to display column summary (#4194)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2025-07-02 16:57:21 +02:00
renovate[bot]
a0e71d9e29 chore(deps): update vue-tsc to v3 (v3) (major) (#4445)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2025-07-02 14:25:37 +02:00
renovate[bot]
127e06ae83 chore(deps): update all non-major dependencies (v3) (#4443)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2025-07-02 11:39:23 +02:00
Benjamin Canac
09c1ed8bf4 chore(renovate): group vue-tsc & vue-component-type-helpers 2025-07-02 11:20:57 +02:00
renovate[bot]
a05102fab3 chore(deps): update dependency reka-ui to v2.3.2 (v3) (#4439)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-02 11:07:08 +02:00
renovate[bot]
09caf44d0d chore(deps): update nuxt framework to ^3.17.6 (v3) (#4437)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-02 10:13:13 +02:00
Estéban
15482aae76 docs(icons): add warning about dash collections in vue (#4307)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2025-07-01 18:00:12 +02:00
Benjamin Canac
f903ec396f feat(Table): add row hover event
Resolves #2435
2025-07-01 16:32:07 +02:00
Benjamin Canac
b00e07f13d feat(Popover): add reference prop 2025-07-01 16:13:31 +02:00
Benjamin Canac
5c573b37b6 docs(prettier): upgrade version 2025-07-01 13:16:38 +02:00
Benjamin Canac
f62c5ec20c feat(Table): add support for context menu
Resolves #4259
2025-07-01 13:15:00 +02:00
Benjamin Canac
b96a1ccbab feat(InputTags): add max-length prop
Resolves #4405
2025-07-01 10:35:24 +02:00
VALERIY SINEVICH
4ce654076c fix(Table): handle reactive columns (#4412) 2025-06-30 15:20:35 +02:00
Romain Hamel
fb9e7bb856 feat(Input/Textarea): add default-value prop (#4404)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2025-06-30 15:04:52 +02:00
Benjamin Canac
69a7b957d5 feat(Tooltip): add reference prop
Resolves #4430
2025-06-30 15:01:59 +02:00
Mike Newbon
3b67d54833 fix(Carousel): resolve plugins with page transitions (#4380)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2025-06-30 14:55:07 +02:00
Benjamin Canac
df8f20232f fix(Button): add active styles to behave like hover on mobile
Resolves #991
2025-06-30 12:35:36 +02:00
J-Michalek
347694b4b5 fix(Table): add scope attribute to headers (#4417) 2025-06-30 12:33:11 +02:00
renovate[bot]
021880328b chore(deps): lock file maintenance (v3) (#4429)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-30 11:39:01 +02:00
renovate[bot]
9c1f423555 chore(deps): lock file maintenance (v3) (#4428)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-30 10:51:22 +02:00
renovate[bot]
6cb737e038 chore(deps): update all non-major dependencies (v3) (#4391)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-30 10:20:52 +02:00
renovate[bot]
231b82fe4c chore(deps): update tailwindcss to ^4.1.11 (v3) (#4425)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-30 10:05:49 +02:00
Benjamin Canac
57a5037b13 fix(vue): handle override when importing from @nuxt/ui 2025-06-26 16:40:35 +02:00
Benjamin Canac
752e2b69bd fix(theme): colors autocomplete in app config 2025-06-26 12:45:57 +02:00
Daniel Roe
6237663a01 chore: update dev:prepare command too (#4399) 2025-06-26 12:20:05 +02:00
Daniel Roe
44cfa00e4d chore: use workspace to run vite commands (#4398) 2025-06-26 10:16:40 +02:00
Benjamin Canac
8cbbab9a6b chore(deps): update @nuxt/ui-pro 2025-06-25 17:48:48 +02:00
Benjamin Canac
2d51e20939 docs(content): update badges 2025-06-25 17:48:37 +02:00
Benjamin Canac
268e29b041 chore(release): v3.2.0 2025-06-25 17:00:05 +02:00
Benjamin Canac
b0364b96b7 fix(SelectMenu): dynamic input size 2025-06-25 16:04:04 +02:00
Benjamin Canac
ba3c6e8788 fix(InputMenu/SelectMenu): dynamic empty size
Resolves #4377
2025-06-25 16:01:04 +02:00
Benjamin Canac
01da3cbf31 docs(components): add illustrations 2025-06-25 11:46:58 +02:00
Estéban
595fc64515 feat(Table): add body-top / body-bottom slots (#4354)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2025-06-25 11:17:02 +02:00
Estéban
81569713e9 feat(Modal/Slideover): add actions slot (#4358)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2025-06-24 18:03:53 +02:00
Jack Bobakanoosh
1a8feb751e fix(Form): expose reactive fields (#4386) 2025-06-24 17:56:12 +02:00
Maxime Pauvert
1d281e915a docs(app): use findPageBreadcrumb from @nuxt/content (#4359) 2025-06-24 17:45:52 +02:00
Artea
c3adc381c9 fix(Card/Drawer/Modal): prevent scrollbars overflow (#4368)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2025-06-24 17:16:13 +02:00
Lars Eberhardt
edca3bcb74 fix(Table): use tr as separator (#4083)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2025-06-24 17:09:04 +02:00
Benjamin Canac
8f32ee3d24 chore(deps): update @nuxt/ui-pro 2025-06-24 16:42:56 +02:00
Hugo Richard
9172bb7dc2 docs(app): add copy markdown button (#4369) 2025-06-24 12:11:23 +02:00
renovate[bot]
32dae2e002 chore(deps): update dependency better-sqlite3 to v12 (v3) (#4385)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-23 14:39:02 +02:00
Estéban
be41aed1f3 fix(components): remove default md size on buttons (#4357) 2025-06-23 12:47:30 +02:00
renovate[bot]
bf678412ca chore(deps): lock file maintenance (v3) (#4389)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-23 12:39:12 +02:00
renovate[bot]
a999600e9f chore(deps): update all non-major dependencies (v3) (#4362)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-23 12:05:11 +02:00
Benjamin Canac
04f12adc5b docs(examples): use useClipboard instead of navigator.clipboard 2025-06-18 14:16:54 +02:00
Sigve Hansen
abfd0ede03 fix(Toaster): smoother visibility transition for stacked toasts (#4367) 2025-06-17 16:56:33 +02:00
Vachmara
2fa8db64dd fix(NavigationMenu): nested accordion context at every level (#4363)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2025-06-17 10:49:07 +02:00
Benjamin Canac
52f1963833 chore(cli): improve templates 2025-06-17 10:10:08 +02:00
renovate[bot]
9a83c9c7f4 chore(deps): update devdependency happy-dom to v18 (v3) (#4353)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-16 23:03:17 +02:00
renovate[bot]
f2510cb342 chore(deps): lock file maintenance (v3) (#4352)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2025-06-16 12:52:42 +02:00
Estéban
4dd56c8111 fix(Pagination): match default button size (#4350) 2025-06-16 11:46:59 +02:00
renovate[bot]
6e3ec6a077 chore(deps): update all non-major dependencies (v3) (#4349)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-16 11:24:09 +02:00
Hugo Richard
59c26ec123 feat(CommandPalette): handle children in items (#4226)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2025-06-13 14:49:43 +02:00
Julien Augugliaro
67ef866a40 docs(input): fix typo in mask example (#4334) 2025-06-12 23:48:01 +02:00
J-Michalek
5170cfd7eb feat(Timeline): add reverse prop (#4316)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2025-06-12 17:06:51 +02:00
Benjamin Canac
9bcf1ad92f docs(input-tags): add illustration 2025-06-12 16:40:08 +02:00
Benjamin Canac
7a2bd4e617 feat(Select/SelectMenu/Tabs): expose trigger refs
Resolves #4292
2025-06-12 15:38:43 +02:00
Benjamin Canac
8781a07909 fix(InputTags): extend emits interface 2025-06-12 15:35:20 +02:00
Benjamin Canac
2492526d7c docs(showcase): improve file parse 2025-06-12 12:40:57 +02:00
zhong666
54bb2282c5 feat(InputTags): new component (#4261)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2025-06-12 12:10:48 +02:00
renovate[bot]
2a2495a652 chore(deps): update tailwindcss to ^4.1.10 (v3) (#4332)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-12 10:53:49 +02:00
renovate[bot]
f17b15ed1e chore(deps): update tailwindcss to ^4.1.9 (v3) (#4326)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-11 17:19:05 +02:00
Eugen Istoc
66355ba301 fix(useOverlay): set props to original props when defaultOpen is set (#4308)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2025-06-11 17:05:55 +02:00
Benjamin Canac
4873b3a043 chore(deps): update @nuxt/ui-pro 2025-06-11 17:02:44 +02:00
Benjamin Canac
0d4baf7851 docs(app): improve header dropdown items 2025-06-11 16:55:25 +02:00
syvixor
04333cd8cb docs(app): update v2 versions in header (#4325)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2025-06-11 15:27:19 +02:00
renovate[bot]
cb3522ed18 chore(deps): update dependency colortranslator to v5 (v3) (#4314)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-11 12:56:00 +02:00
Benjamin Canac
1a4de49c16 feat(Select/SelectMenu): handle dynamic autofocus
Resolves #4324
2025-06-11 12:53:57 +02:00
Benjamin Canac
3eb7812f2d chore(github): update stale workflow 2025-06-11 12:17:19 +02:00
Benjamin Canac
080aed7225 chore(github): handle workflow_dispatch 2025-06-11 12:11:55 +02:00
Benjamin Canac
d77fd6102a chore(github): update stale workflow 2025-06-11 12:04:30 +02:00
Benjamin Canac
0f558fc0d0 feat(extendLocale): new composable
Resolves #3729
2025-06-11 10:58:20 +02:00
J-Michalek
1841e13b32 docs(timeline): fix duplicate separator field on item.ui (#4313)
Co-authored-by: Jakub <jakub.michalek@freelo.io>
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2025-06-10 18:21:14 +02:00
Hugo Richard
4e7c1c9c30 fix(defineShortcuts): allow meta_- shortcut (#4321) 2025-06-10 17:54:15 +02:00
renovate[bot]
4b3dd48778 chore(deps): update nuxt-hub/action action to v2 (v3) (#4315)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2025-06-10 17:44:59 +02:00
renovate[bot]
a9e8ea9231 chore(deps): update all non-major dependencies (v3) (#4277)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-10 17:17:33 +02:00
Benjamin Canac
4dd9344ff9 chore(deps): use workspace:* syntax 2025-06-10 16:53:07 +02:00
renovate[bot]
180c150e0f chore(deps): update dependency reka-ui to v2.3.1 (v3) (#4310)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-10 16:16:01 +02:00
Estéban
145cae798c docs(installation): add tip to improve types in vue (#4318)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2025-06-10 15:58:09 +02:00
Benjamin Canac
9aea54267a chore(deps): add @internationalized/date in playground 2025-06-10 15:49:17 +02:00
Benjamin Canac
9400552491 chore(github): improve sha retrieval 2025-06-10 14:52:35 +02:00
Benjamin Canac
89753fc337 chore(github): update workflows permissions 2025-06-10 13:02:32 +02:00
Benjamin Canac
f8a6bd3bf6 chore(github): add stale workflow 2025-06-06 12:29:23 +02:00
Benjamin Canac
228d4c9835 docs(input): add mask example 2025-06-06 11:37:26 +02:00
Benjamin Canac
150b334b1d fix(Modal/Slideover): don't emit close:prevent on closeAutoFocus 2025-06-05 17:22:42 +02:00
Eugen Istoc
bf56e15a2e fix(useOverlay): use original props when not provided to open (#4269) 2025-06-05 15:48:47 +02:00
Benjamin Canac
09151df170 chore(renovate): add vaul-vue to group 2025-06-05 12:47:08 +02:00
Benjamin Canac
22b917a0f7 chore(deps): pin reka-ui & vaul-vue dependencies
Related to https://github.com/nuxt/ui/issues/4257
2025-06-05 12:47:08 +02:00
adjabaev
43cbb94ee2 feat(locale): add Luxembourgish language (#4264) 2025-06-05 12:34:38 +02:00
Benjamin Canac
3bf5acb683 fix(Toast): calc height on next tick
Resolves #4265
2025-06-05 12:13:35 +02:00
Eugen Istoc
d37315cc83 docs(use-overlay): add caveats section regarding provide/inject limit (#4287)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2025-06-04 16:01:02 +02:00
renovate[bot]
326bb9a31e chore(deps): update nuxt framework to ^3.17.5 (v3) (#4284)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-04 10:34:41 +02:00
Benjamin Canac
4157260a02 docs(timeline): improve responsive 2025-06-03 18:09:36 +02:00
Benjamin Canac
03b20fdb26 chore: prefer nuxt over nuxi 2025-06-02 12:51:40 +02:00
renovate[bot]
004c93bfa2 chore(deps): lock file maintenance (v3) (#4278)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-02 12:46:59 +02:00
renovate[bot]
18eb5e6b97 chore(deps): update tailwindcss to ^4.1.8 (v3) (#4275)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-02 10:55:39 +02:00
renovate[bot]
42f7f94521 chore(deps): update all non-major dependencies (v3) (#4273)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-02 10:22:38 +02:00
dan hale
ea0c459306 feat(Form): expose loading state to default slot (#4247)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2025-06-01 16:48:16 +02:00
358 changed files with 14584 additions and 6181 deletions

View File

@@ -10,7 +10,7 @@ body:
id: env
attributes:
label: Environment
description: You can use `npx nuxi info` to fill this section
description: You can use `npx nuxt info` to fill this section
placeholder: |
- Operating System: `Darwin`
- Node Version: `v18.16.0`

View File

@@ -10,7 +10,7 @@ body:
id: env
attributes:
label: Environment
description: You can use `npx nuxi info` to fill this section
description: You can use `npx nuxt info` to fill this section
placeholder: |
- Operating System: `Darwin`
- Node Version: `v18.16.0`

View File

@@ -6,10 +6,6 @@ jobs:
deploy:
runs-on: ${{ matrix.os }}
environment:
name: ${{ github.ref == 'refs/heads/v3' && 'production' || 'preview' }}
url: ${{ steps.deploy.outputs.deployment-url }}
permissions:
contents: read
id-token: write
@@ -40,14 +36,10 @@ jobs:
- name: Prepare build
run: pnpm run dev:prepare
- name: Build application
run: pnpm run docs:build
- name: Deploy to NuxtHub
uses: nuxt-hub/action@v2
env:
NODE_OPTIONS: '--max-old-space-size=8192'
- name: Deploy to NuxtHub
uses: nuxt-hub/action@v1
id: deploy
with:
project-key: ui-7eg3
directory: docs/dist
directory: docs

View File

@@ -93,7 +93,7 @@ jobs:
- name: Store commit SHA
run: |
echo "COMMIT_SHA=$(echo ${{ github.workflow_sha }} | cut -c1-7)" >> $GITHUB_ENV
echo "COMMIT_SHA=$(echo ${{ github.event.pull_request.head.sha || github.sha }} | cut -c1-7)" >> $GITHUB_ENV
- name: Install pnpm
uses: pnpm/action-setup@v4
@@ -111,7 +111,7 @@ jobs:
run: pnpm install --ignore-workspace
- name: Prepare
run: pnpm nuxi prepare
run: pnpm nuxt prepare
- name: Typecheck
run: pnpm run typecheck
@@ -138,7 +138,7 @@ jobs:
- name: Store commit SHA
run: |
echo "COMMIT_SHA=$(echo ${{ github.workflow_sha }} | cut -c1-7)" >> $GITHUB_ENV
echo "COMMIT_SHA=$(echo ${{ github.event.pull_request.head.sha || github.sha }} | cut -c1-7)" >> $GITHUB_ENV
- name: Install pnpm
uses: pnpm/action-setup@v4
@@ -183,7 +183,7 @@ jobs:
- name: Store commit SHA
run: |
echo "COMMIT_SHA=$(echo ${{ github.workflow_sha }} | cut -c1-7)" >> $GITHUB_ENV
echo "COMMIT_SHA=$(echo ${{ github.event.pull_request.head.sha || github.sha }} | cut -c1-7)" >> $GITHUB_ENV
- name: Install pnpm
uses: pnpm/action-setup@v4
@@ -235,7 +235,7 @@ jobs:
- name: Store commit SHA
run: |
echo "COMMIT_SHA=$(echo ${{ github.workflow_sha }} | cut -c1-7)" >> $GITHUB_ENV
echo "COMMIT_SHA=$(echo ${{ github.event.pull_request.head.sha || github.sha }} | cut -c1-7)" >> $GITHUB_ENV
- name: Install pnpm
uses: pnpm/action-setup@v4

View File

@@ -9,10 +9,6 @@ jobs:
deploy:
runs-on: ${{ matrix.os }}
environment:
name: ${{ github.ref == 'refs/heads/v3' && 'production' || 'preview' }}
url: ${{ steps.deploy.outputs.deployment-url }}
permissions:
contents: read
id-token: write
@@ -40,14 +36,10 @@ jobs:
- name: Prepare build
run: pnpm run dev:prepare
- name: Build application
run: pnpm run dev:build
env:
NITRO_PRESET: cloudflare-pages
- name: Deploy to NuxtHub
uses: nuxt-hub/action@v1
id: deploy
uses: nuxt-hub/action@v2
env:
NODE_OPTIONS: '--max-old-space-size=8192'
with:
project-key: ui3-playground-pb9b
directory: playground/dist
directory: playground

27
.github/workflows/reproduction.yml vendored Normal file
View File

@@ -0,0 +1,27 @@
name: reproduction
on:
workflow_dispatch:
schedule:
- cron: '30 1 * * *'
jobs:
reproduction:
runs-on: ubuntu-latest
permissions:
actions: write
issues: write
steps:
- uses: actions/stale@v9
with:
days-before-stale: -1 # Issues and PR will never be flagged stale automatically.
stale-issue-label: 'needs reproduction' # Label that flags an issue as stale.
only-labels: 'needs reproduction' # Only process these issues
days-before-issue-close: 7
ignore-updates: true
remove-stale-when-updated: false
close-issue-message: This issue was closed because it was open for 7 days without a reproduction.
close-issue-label: closed-by-bot
operations-per-run: 300 #default 30

View File

@@ -1,6 +1,7 @@
name: stale
on:
workflow_dispatch:
schedule:
- cron: '30 1 * * *'
@@ -9,17 +10,28 @@ jobs:
runs-on: ubuntu-latest
permissions:
actions: write
issues: write
steps:
- uses: actions/stale@v9
- uses: actions/stale@4c023f01d613e60293d8004f251a18bfb9bbd71d
with:
days-before-stale: -1 # Issues and PR will never be flagged stale automatically.
stale-issue-label: 'needs reproduction' # Label that flags an issue as stale.
only-labels: 'needs reproduction' # Only process these issues
days-before-issue-close: 7
ignore-updates: true
remove-stale-when-updated: false
close-issue-message: This issue was closed because it was open for 7 days without a reproduction.
close-issue-label: closed-by-bot
operations-per-run: 300 #default 30
days-before-pr-stale: -1
days-before-stale: 60
days-before-close: 7
stale-issue-label: 'stale'
close-issue-label: 'closed-by-bot'
close-issue-message: |
Hi! 👋
This issue has been automatically **closed** due to prolonged inactivity.
We're a small team and can't address every report, but we appreciate your feedback and contributions.
If this issue is still relevant with the latest version of Nuxt UI, please feel free to reopen or create a new issue with updated details.
Thank you for your understanding and support!
— Nuxt UI Team
exempt-issue-labels: 'feature,announcement,release,reka-ui,upstream'
operations-per-run: 300

1
.nuxtrc Normal file
View File

@@ -0,0 +1 @@
experimental.normalizeComponentNames=false

View File

@@ -1,5 +1,51 @@
# Changelog
## [3.2.0](https://github.com/nuxt/ui/compare/v3.1.3...v3.2.0) (2025-06-25)
### ⚠ BREAKING CHANGES
* **useOverlay:** correct spelling of `unmount` function (#4051)
### Features
* **Avatar:** add `chip` prop ([#4224](https://github.com/nuxt/ui/issues/4224)) ([03ac395](https://github.com/nuxt/ui/commit/03ac395164c02c964361c68743268b1bc90aae59))
* **Carousel:** allow customization of active dot color ([#4229](https://github.com/nuxt/ui/issues/4229)) ([2ee1c5a](https://github.com/nuxt/ui/commit/2ee1c5ac2e20ab9ce2f4037a8e8c64e561b0428b))
* **CommandPalette:** handle `children` in items ([#4226](https://github.com/nuxt/ui/issues/4226)) ([59c26ec](https://github.com/nuxt/ui/commit/59c26ec1230375a24fbaf8a630a696ae854700c7))
* **extendLocale:** new composable ([0f558fc](https://github.com/nuxt/ui/commit/0f558fc0d014d51549222accfc50286d1770d1aa)), closes [#3729](https://github.com/nuxt/ui/issues/3729)
* **Form:** expose loading state to default slot ([#4247](https://github.com/nuxt/ui/issues/4247)) ([ea0c459](https://github.com/nuxt/ui/commit/ea0c459306be585bacaaf5b433114d072550c824))
* **InputTags:** new component ([#4261](https://github.com/nuxt/ui/issues/4261)) ([54bb228](https://github.com/nuxt/ui/commit/54bb2282c58d3bf5a7dde4cdee687c68efd934a0))
* **locale:** add Luxembourgish language ([#4264](https://github.com/nuxt/ui/issues/4264)) ([43cbb94](https://github.com/nuxt/ui/commit/43cbb94ee25106b414fc8fe979fa65ebaa9ccc76))
* **Modal/Slideover:** add `actions` slot ([#4358](https://github.com/nuxt/ui/issues/4358)) ([8156971](https://github.com/nuxt/ui/commit/81569713e9da9d5531ecdf4614660b84c686fa81))
* **Modal/Slideover:** add `close` method in slots ([#4219](https://github.com/nuxt/ui/issues/4219)) ([5835eb5](https://github.com/nuxt/ui/commit/5835eb5f0f835b5f03646dec78f85b2f556a109b))
* **Select/SelectMenu/Tabs:** expose trigger refs ([7a2bd4e](https://github.com/nuxt/ui/commit/7a2bd4e6179373902ba6f285903ea896fd1d378f)), closes [#4292](https://github.com/nuxt/ui/issues/4292)
* **Select/SelectMenu:** handle dynamic `autofocus` ([1a4de49](https://github.com/nuxt/ui/commit/1a4de49c1665c9ef65279315be0393d6272447b9)), closes [#4324](https://github.com/nuxt/ui/issues/4324)
* **Table:** add `body-top` / `body-bottom` slots ([#4354](https://github.com/nuxt/ui/issues/4354)) ([595fc64](https://github.com/nuxt/ui/commit/595fc64515613fe82c3a56fc5518f2e3fcce6e19))
* **Timeline:** add `reverse` prop ([#4316](https://github.com/nuxt/ui/issues/4316)) ([5170cfd](https://github.com/nuxt/ui/commit/5170cfd7eb44a25c64673cf12979f9ca1049695f))
* **Timeline:** new component ([#4215](https://github.com/nuxt/ui/issues/4215)) ([8017767](https://github.com/nuxt/ui/commit/80177679f2aa0d7f0e39e639a02d527a06e6172c))
### Bug Fixes
* **Card/Drawer/Modal:** prevent scrollbars overflow ([#4368](https://github.com/nuxt/ui/issues/4368)) ([c3adc38](https://github.com/nuxt/ui/commit/c3adc381c90dad7152e27fc303ee678efc7c4c94))
* **components:** remove default `md` size on buttons ([#4357](https://github.com/nuxt/ui/issues/4357)) ([be41aed](https://github.com/nuxt/ui/commit/be41aed1f3d3476801e1840dbb8766926bc93c05))
* **defineShortcuts:** allow `meta_-` shortcut ([#4321](https://github.com/nuxt/ui/issues/4321)) ([4e7c1c9](https://github.com/nuxt/ui/commit/4e7c1c9c305b45dd76d4c238e70a6aeedae78c8b))
* **Form:** conditionally type form data via `transform` prop ([#4188](https://github.com/nuxt/ui/issues/4188)) ([37abcc6](https://github.com/nuxt/ui/commit/37abcc6a5b0a678be626673af5067956657a50d6))
* **Form:** expose reactive fields ([#4386](https://github.com/nuxt/ui/issues/4386)) ([1a8feb7](https://github.com/nuxt/ui/commit/1a8feb751e6827c414ef82fe9fb259ba7dcc7e08))
* **InputMenu/SelectMenu:** dynamic `empty` size ([ba3c6e8](https://github.com/nuxt/ui/commit/ba3c6e8788ed75d86d4406749797da52d7816b84)), closes [#4377](https://github.com/nuxt/ui/issues/4377)
* **InputTags:** extend emits interface ([8781a07](https://github.com/nuxt/ui/commit/8781a079096def0d3bae5b8d896db0df6ce37e23))
* **Modal/Slideover:** don't emit `close:prevent` on `closeAutoFocus` ([150b334](https://github.com/nuxt/ui/commit/150b334b1d242c6dc132193e23359c03e6f35666))
* **NavigationMenu:** nested accordion context at every level ([#4363](https://github.com/nuxt/ui/issues/4363)) ([2fa8db6](https://github.com/nuxt/ui/commit/2fa8db64ddf4c92a19e73774143518d87d001b72))
* **NavigationMenu:** set content `max-height` in `horizontal` orientation ([62bc7b2](https://github.com/nuxt/ui/commit/62bc7b25a2d205d8dffb47a109196f91ff3e823a)), closes [#4208](https://github.com/nuxt/ui/issues/4208)
* **Pagination:** match default button `size` ([#4350](https://github.com/nuxt/ui/issues/4350)) ([4dd56c8](https://github.com/nuxt/ui/commit/4dd56c8111e5a224105b82d541b7742b46abb34a))
* **Select/SelectMenu:** display falsy values ([7df7ee3](https://github.com/nuxt/ui/commit/7df7ee336a925d7ee07f866551dad9350785c9fc))
* **Select/SelectMenu:** prevent empty string display when multiple ([483e473](https://github.com/nuxt/ui/commit/483e473e3f5681cc97c3766ea47283dc95f76345))
* **SelectMenu:** dynamic input size ([b0364b9](https://github.com/nuxt/ui/commit/b0364b96b73b9e543781a35962c03b5a983352c4))
* **Table:** use `tr` as separator ([#4083](https://github.com/nuxt/ui/issues/4083)) ([edca3bc](https://github.com/nuxt/ui/commit/edca3bcb743c7eb63e6abbaa801d3858342a8777))
* **Toast:** calc height on next tick ([3bf5acb](https://github.com/nuxt/ui/commit/3bf5acb683f0ad09735b2417d265d6fcfd901b11)), closes [#4265](https://github.com/nuxt/ui/issues/4265)
* **Toaster:** smoother visibility transition for stacked toasts ([#4367](https://github.com/nuxt/ui/issues/4367)) ([abfd0ed](https://github.com/nuxt/ui/commit/abfd0ede036fa2953f9abc841d77ac71bbd3bba9))
* **useOverlay:** correct spelling of `unmount` function ([#4051](https://github.com/nuxt/ui/issues/4051)) ([546df57](https://github.com/nuxt/ui/commit/546df572fca60325315bed17c9be3367052fb7a9))
* **useOverlay:** set props to original props when `defaultOpen` is set ([#4308](https://github.com/nuxt/ui/issues/4308)) ([66355ba](https://github.com/nuxt/ui/commit/66355ba301d569b9f44527bafc5f8f09bcda63c0))
* **useOverlay:** use original props when not provided to `open` ([#4269](https://github.com/nuxt/ui/issues/4269)) ([bf56e15](https://github.com/nuxt/ui/commit/bf56e15a2eed7d51199d5641649a822e91ca41ba))
## [3.1.3](https://github.com/nuxt/ui/compare/v3.1.2...v3.1.3) (2025-05-26)
### ⚠ BREAKING CHANGES

View File

@@ -31,10 +31,11 @@ const component = ({ name, primitive, pro, prose, content }) => {
? `
<script lang="ts">
import type { AppConfig } from '@nuxt/schema'
${pro ? `import type { ComponentConfig } from '@nuxt/ui'` : ''}
import theme from '#build/${path}/${prose ? 'prose/' : ''}${content ? 'content/' : ''}${kebabName}'
import type { ComponentConfig } from '../types/utils'
${!pro ? `import type { ComponentConfig } from '../types/utils'` : ''}
type ${upperName} = ComponentConfig<typeof theme, AppConfig, ${upperName}${pro ? `, '${key}'` : ''}>
type ${upperName} = ComponentConfig<typeof theme, AppConfig, '${camelName}'${pro ? `, '${key}'` : ''}>
export interface ${upperName}Props {
/**
@@ -62,7 +63,7 @@ defineSlots<${upperName}Slots>()
const appConfig = useAppConfig() as ${upperName}['AppConfig']
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.${camelName} || {}) })())
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.${pro ? 'uiPro' : 'ui'}?.${camelName} || {}) })())
</script>
<template>
@@ -75,10 +76,11 @@ const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.${camelName}
<script lang="ts">
import type { ${upperName}RootProps, ${upperName}RootEmits } from 'reka-ui'
import type { AppConfig } from '@nuxt/schema'
${pro ? `import type { ComponentConfig } from '@nuxt/ui'` : ''}
import theme from '#build/${path}/${prose ? 'prose/' : ''}${content ? 'content/' : ''}${kebabName}'
import type { ComponentConfig } from '../types/utils'
${!pro ? `import type { ComponentConfig } from '../types/utils'` : ''}
type ${upperName} = ComponentConfig<typeof theme, AppConfig, ${upperName}${pro ? `, '${key}'` : ''}>
type ${upperName} = ComponentConfig<typeof theme, AppConfig, '${camelName}'${pro ? `, '${key}'` : ''}>
export interface ${upperName}Props extends Pick<${upperName}RootProps> {
class?: any
@@ -105,7 +107,7 @@ const appConfig = useAppConfig() as ${upperName}['AppConfig']
const rootProps = useForwardPropsEmits(reactivePick(props), emits)
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.${camelName} || {}) })())
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.${pro ? 'uiPro' : 'ui'}?.${camelName} || {}) })())
</script>
<template>
@@ -145,7 +147,8 @@ const test = ({ name, prose, content }) => {
? undefined
: `
import { describe, it, expect } from 'vitest'
import ${upperName}, { type ${upperName}Props, type ${upperName}Slots } from '../../${content ? '../' : ''}src/runtime/components/${content ? 'content/' : ''}${upperName}.vue'
import ${upperName} from '../../${content ? '../' : ''}src/runtime/components/${content ? 'content/' : ''}${upperName}.vue'
import type { ${upperName}Props, ${upperName}Slots } from '../../${content ? '../' : ''}src/runtime/components/${content ? 'content/' : ''}${upperName}.vue'
import ComponentRender from '../${content ? '../' : ''}component-render'
describe('${upperName}', () => {
@@ -186,6 +189,7 @@ links:${primitive
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxt/${pro ? 'ui-pro' : 'ui'}/tree/v3/src/runtime/components/${upperName}.vue
navigation.badge: Soon
---
## Usage

View File

@@ -53,7 +53,7 @@ provide('navigation', mappedNavigation)
<NuxtLoadingIndicator color="var(--ui-primary)" :height="2" />
<template v-if="!route.path.startsWith('/examples')">
<!-- <Banner /> -->
<Banner />
<Header :links="links" />
</template>

View File

@@ -1,18 +1,19 @@
<template>
<UBanner
id="ui3-launch"
icon="i-lucide-rocket"
:actions="[
{
label: 'Discover Nuxt UI Pro',
to: '/pro/pricing',
trailingIcon: 'i-lucide-arrow-right'
}
]"
id="nuxtlabs-join-vercel"
title="NuxtLabs is joining Vercel"
icon="i-simple-icons-vercel"
to="https://nuxtlabs.com/?utm_source=nuxt-ui&utm_medium=banner&utm_campaign=nuxtlabs-vercel"
target="_blank"
close
>
<template #title>
<span class="font-semibold">Nuxt UI v3</span> is officially released.
</template>
</UBanner>
:actions="[{
label: 'Read the announcement',
color: 'neutral',
variant: 'outline',
trailingIcon: 'i-lucide-arrow-right',
to: 'https://nuxtlabs.com/?utm_source=nuxt-ui&utm_medium=banner&utm_campaign=nuxtlabs-vercel',
target: '_blank',
class: 'ring-0'
}]"
/>
</template>

View File

@@ -22,9 +22,7 @@ onMounted(() => {
const navigation = inject<Ref<ContentNavigationItem[]>>('navigation')
const githubLink = computed(() => {
return `https://github.com/nuxt/${value.value}`
})
const githubLink = computed(() => `https://github.com/nuxt/${value.value}`)
const desktopLinks = computed(() => props.links.map(({ icon, ...link }) => link))
const mobileLinks = computed(() => [
@@ -36,6 +34,16 @@ const mobileLinks = computed(() => [
target: '_blank'
}
])
const items = computed(() => {
const ui2 = { label: 'v2.22.0', to: 'https://ui2.nuxt.com' }
const uiPro1 = { label: 'v1.8.0', to: 'https://ui2.nuxt.com/pro' }
return [
{ label: `v${config.version}`, active: true, color: 'primary' as const, checked: true, type: 'checkbox' as const },
route.path === '/' ? ui2 : route.path.startsWith('/pro') ? uiPro1 : module.value === 'ui-pro' ? uiPro1 : ui2
]
})
</script>
<template>
@@ -53,7 +61,7 @@ const mobileLinks = computed(() => [
<UDropdownMenu
v-slot="{ open }"
:modal="false"
:items="[{ label: `v${config.version}`, active: true, color: 'primary', checked: true, type: 'checkbox' }, { label: module === 'ui-pro' ? 'v1.7.1' : 'v2.21.1', to: module === 'ui-pro' ? 'https://ui2.nuxt.com/pro' : 'https://ui2.nuxt.com' }]"
:items="items"
:ui="{ content: 'w-(--reka-dropdown-menu-trigger-width) min-w-0' }"
size="xs"
>

View File

@@ -0,0 +1,77 @@
<script setup lang="ts">
const route = useRoute()
const toast = useToast()
const { copy, copied } = useClipboard()
const site = useSiteConfig()
const mdPath = computed(() => `${site.url}/raw${route.path}.md`)
const items = [
{
label: 'Copy Markdown link',
icon: 'i-lucide-link',
onSelect() {
copy(mdPath.value)
toast.add({
title: 'Copied to clipboard',
icon: 'i-lucide-check-circle'
})
}
},
{
label: 'View as Markdown',
icon: 'i-simple-icons:markdown',
target: '_blank',
to: `/raw${route.path}.md`
},
{
label: 'Open in ChatGPT',
icon: 'i-simple-icons:openai',
target: '_blank',
to: `https://chatgpt.com/?hints=search&q=${encodeURIComponent(`Read ${mdPath.value} so I can ask questions about it.`)}`
},
{
label: 'Open in Claude',
icon: 'i-simple-icons:anthropic',
target: '_blank',
to: `https://claude.ai/new?q=${encodeURIComponent(`Read ${mdPath.value} so I can ask questions about it.`)}`
}
]
async function copyPage() {
copy(await $fetch<string>(`/raw${route.path}.md`))
}
</script>
<template>
<UButtonGroup>
<UButton
label="Copy page"
:icon="copied ? 'i-lucide-copy-check' : 'i-lucide-copy'"
color="neutral"
variant="outline"
:ui="{
leadingIcon: [copied ? 'text-primary' : 'text-neutral', 'size-3.5']
}"
@click="copyPage"
/>
<UDropdownMenu
:items="items"
:content="{
align: 'end',
side: 'bottom',
sideOffset: 8
}"
:ui="{
content: 'w-48'
}"
>
<UButton
icon="i-lucide-chevron-down"
size="sm"
color="neutral"
variant="outline"
/>
</UDropdownMenu>
</UButtonGroup>
</template>

View File

@@ -34,7 +34,7 @@ const meta = await fetchComponentMeta(name as any)
</ProseCode>
</ProseTd>
<ProseTd>
<HighlightInlineType v-if="slot.type" :type="slot.type" />
<HighlightInlineType v-if="slot.type" :type="slot.type.replace(/ui:\s*\{[^}]*\}/g, 'ui: {}')" />
<MDC v-if="slot.description" :value="slot.description" class="text-toned mt-1" :cache-key="`${kebabCase(route.path)}-${slot.name}-description`" />
</ProseTd>

View File

@@ -26,6 +26,7 @@ function getEmojiFlag(locale: string): string {
km: 'kh', // Khmer -> Cambodia
ko: 'kr', // Korean -> South Korea
ky: 'kg', // Kyrgyz -> Kyrgyzstan
lb: 'lu', // Luxembourgish -> Luxembourg
ms: 'my', // Malay -> Malaysia
nb: 'no', // Norwegian Bokmål -> Norway
sl: 'si', // Slovenian -> Slovenia

View File

@@ -14,8 +14,8 @@ const items = [
v-slot="{ item }"
orientation="vertical"
:items="items"
class="w-full max-w-xs mx-auto"
:ui="{ container: 'h-[336px]' }"
class="w-full max-w-xs mx-auto"
>
<img :src="item" width="320" height="320" class="rounded-lg">
</UCarousel>

View File

@@ -83,7 +83,7 @@ const groups = [
</template>
<template #billing-label="{ item }">
{{ item.label }}
<span class="font-medium text-primary">{{ item.label }}</span>
<UBadge variant="subtle" size="sm">
50% off

View File

@@ -0,0 +1,78 @@
<script setup lang="ts">
const groups = [
{
id: 'actions',
items: [
{
label: 'Add new file',
suffix: 'Create a new file in the current directory',
icon: 'i-lucide-file-plus',
kbds: ['meta', 'N']
},
{
label: 'Add new folder',
suffix: 'Create a new folder in the current directory',
icon: 'i-lucide-folder-plus',
kbds: ['meta', 'F']
},
{
label: 'Search files',
suffix: 'Search across all files in the project',
icon: 'i-lucide-search',
kbds: ['meta', 'P']
},
{
label: 'Settings',
suffix: 'Open application settings',
icon: 'i-lucide-settings',
kbds: ['meta', ',']
}
]
},
{
id: 'recent',
label: 'Recent',
items: [
{
label: 'project.vue',
suffix: 'components/',
icon: 'i-vscode-icons-file-type-vue'
},
{
label: 'readme.md',
suffix: 'docs/',
icon: 'i-vscode-icons-file-type-markdown'
},
{
label: 'package.json',
suffix: 'root/',
icon: 'i-vscode-icons-file-type-node'
}
]
}
]
</script>
<template>
<UCommandPalette :groups="groups" class="flex-1 h-80">
<template #footer>
<div class="flex items-center justify-between gap-2">
<UIcon name="i-simple-icons-nuxtdotjs" class="size-5 text-dimmed ml-1" />
<div class="flex items-center gap-1">
<UButton color="neutral" variant="ghost" label="Open Command" class="text-dimmed" size="xs">
<template #trailing>
<UKbd value="enter" />
</template>
</UButton>
<USeparator orientation="vertical" class="h-4" />
<UButton color="neutral" variant="ghost" label="Actions" class="text-dimmed" size="xs">
<template #trailing>
<UKbd value="meta" />
<UKbd value="k" />
</template>
</UButton>
</div>
</div>
</template>
</UCommandPalette>
</template>

View File

@@ -0,0 +1,119 @@
<script setup lang="ts">
const toast = useToast()
const groups = [{
id: 'actions',
label: 'Actions',
items: [{
label: 'Create new',
icon: 'i-lucide-plus',
children: [{
label: 'New file',
icon: 'i-lucide-file-plus',
suffix: 'Create a new file in the current directory',
onSelect(e: Event) {
e.preventDefault()
toast.add({ title: 'New file created!' })
},
kbds: ['meta', 'N']
}, {
label: 'New folder',
icon: 'i-lucide-folder-plus',
suffix: 'Create a new folder in the current directory',
onSelect(e: Event) {
e.preventDefault()
toast.add({ title: 'New folder created!' })
},
kbds: ['meta', 'F']
}, {
label: 'New project',
icon: 'i-lucide-folder-git',
suffix: 'Create a new project from a template',
onSelect(e: Event) {
e.preventDefault()
toast.add({ title: 'New project created!' })
},
kbds: ['meta', 'P']
}]
}, {
label: 'Share',
icon: 'i-lucide-share',
children: [{
label: 'Copy link',
icon: 'i-lucide-link',
suffix: 'Copy a link to the current item',
onSelect(e: Event) {
e.preventDefault()
toast.add({ title: 'Link copied to clipboard!' })
},
kbds: ['meta', 'L']
}, {
label: 'Share via email',
icon: 'i-lucide-mail',
suffix: 'Share the current item via email',
onSelect(e: Event) {
e.preventDefault()
toast.add({ title: 'Share via email dialog opened!' })
}
}, {
label: 'Share on social',
icon: 'i-lucide-share-2',
suffix: 'Share the current item on social media',
children: [{
label: 'Twitter',
icon: 'i-simple-icons-twitter',
onSelect(e: Event) {
e.preventDefault()
toast.add({ title: 'Shared on Twitter!' })
}
}, {
label: 'LinkedIn',
icon: 'i-simple-icons-linkedin',
onSelect(e: Event) {
e.preventDefault()
toast.add({ title: 'Shared on LinkedIn!' })
}
}, {
label: 'Facebook',
icon: 'i-simple-icons-facebook',
onSelect(e: Event) {
e.preventDefault()
toast.add({ title: 'Shared on Facebook!' })
}
}]
}]
}, {
label: 'Settings',
icon: 'i-lucide-settings',
children: [{
label: 'General',
icon: 'i-lucide-sliders',
suffix: 'Configure general settings',
onSelect(e: Event) {
e.preventDefault()
toast.add({ title: 'General settings opened!' })
}
}, {
label: 'Appearance',
icon: 'i-lucide-palette',
suffix: 'Customize the appearance',
onSelect(e: Event) {
e.preventDefault()
toast.add({ title: 'Appearance settings opened!' })
}
}, {
label: 'Security',
icon: 'i-lucide-shield',
suffix: 'Manage security settings',
onSelect(e: Event) {
e.preventDefault()
toast.add({ title: 'Security settings opened!' })
}
}]
}]
}]
</script>
<template>
<UCommandPalette :groups="groups" class="flex-1" />
</template>

View File

@@ -0,0 +1,15 @@
<template>
<UDrawer :ui="{ content: 'h-full', overlay: 'bg-inverted/30' }">
<UButton label="Open" color="neutral" variant="subtle" trailing-icon="i-lucide-chevron-up" />
<template #footer>
<UDrawer nested :ui="{ content: 'h-full', overlay: 'bg-inverted/30' }">
<UButton color="neutral" variant="outline" label="Open nested" />
<template #content>
<Placeholder class="flex-1 m-4" />
</template>
</UDrawer>
</template>
</UDrawer>
</template>

View File

@@ -1,5 +1,6 @@
<script setup lang="ts">
import { object, string, nonempty, refine, type Infer } from 'superstruct'
import { object, string, nonempty, refine } from 'superstruct'
import type { Infer } from 'superstruct'
import type { FormSubmitEvent } from '@nuxt/ui'
const schema = object({

View File

@@ -1,5 +1,6 @@
<script setup lang="ts">
import { object, string, type InferType } from 'yup'
import { object, string } from 'yup'
import type { InferType } from 'yup'
import type { FormSubmitEvent } from '@nuxt/ui'
const schema = object({

View File

@@ -0,0 +1,31 @@
<script setup lang="ts">
const { data: users } = await useFetch('https://jsonplaceholder.typicode.com/users', {
key: 'typicode-users-email',
transform: (data: { id: number, name: string, email: string }[]) => {
return data?.map(user => ({
label: user.name,
value: String(user.id),
email: user.email,
avatar: { src: `https://i.pravatar.cc/120?img=${user.id}` }
}))
},
lazy: true
})
</script>
<template>
<UInputMenu
:items="users"
icon="i-lucide-user"
placeholder="Select user"
:ui="{ content: 'min-w-fit' }"
>
<template #item-label="{ item }">
{{ item.label }}
<span class="text-muted">
{{ item.email }}
</span>
</template>
</UInputMenu>
</template>

View File

@@ -35,6 +35,7 @@ const items = ref([
}
}
] satisfies InputMenuItem[])
const value = ref(items.value[0])
</script>

View File

@@ -0,0 +1,9 @@
<script setup lang="ts">
const tags = ref(['Vue'])
</script>
<template>
<UFormField label="Tags" required>
<UInputTags v-model="tags" placeholder="Enter tags..." />
</UFormField>
</template>

View File

@@ -1,15 +1,9 @@
<script setup lang="ts">
const value = ref('npx nuxi module add ui')
const copied = ref(false)
import { useClipboard } from '@vueuse/core'
function copy() {
navigator.clipboard.writeText(value.value)
copied.value = true
const value = ref('npx nuxt module add ui')
setTimeout(() => {
copied.value = false
}, 2000)
}
const { copy, copied } = useClipboard()
</script>
<template>
@@ -25,7 +19,7 @@ function copy() {
size="sm"
:icon="copied ? 'i-lucide-copy-check' : 'i-lucide-copy'"
aria-label="Copy to clipboard"
@click="copy"
@click="copy(value)"
/>
</UTooltip>
</template>

View File

@@ -0,0 +1,14 @@
<script setup lang="ts">
import { vMaska } from 'maska/vue'
</script>
<template>
<div class="flex flex-col gap-2">
<UInput v-maska="'#### #### #### ####'" placeholder="4242 4242 4242 4242" icon="i-lucide-credit-card" />
<div class="flex items-center gap-2">
<UInput v-maska="'##/##'" placeholder="MM/YY" icon="i-lucide-calendar" />
<UInput v-maska="'###'" placeholder="CVC" />
</div>
</div>
</template>

View File

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

View File

@@ -24,3 +24,10 @@ const password = ref('')
</template>
</UInput>
</template>
<style>
/* Hide the password reveal button in Edge */
::-ms-reveal {
display: none;
}
</style>

View File

@@ -62,13 +62,13 @@ const items = [
<template>
<UNavigationMenu
:items="items"
class="w-full justify-center"
:ui="{
viewport: 'sm:w-(--reka-navigation-menu-viewport-width)',
content: 'sm:w-auto',
childList: 'sm:w-96',
childLinkDescription: 'text-balance line-clamp-2'
}"
class="w-full justify-center"
>
<template #docs-content="{ item }">
<ul class="grid gap-2 p-4 lg:w-[500px] lg:grid-cols-[minmax(0,.75fr)_minmax(0,1fr)]">

View File

@@ -0,0 +1,43 @@
<script setup lang="ts">
const open = ref(false)
const anchor = ref({ x: 0, y: 0 })
const reference = computed(() => ({
getBoundingClientRect: () =>
({
width: 0,
height: 0,
left: anchor.value.x,
right: anchor.value.x,
top: anchor.value.y,
bottom: anchor.value.y,
...anchor.value
} as DOMRect)
}))
</script>
<template>
<UPopover
:open="open"
:reference="reference"
:content="{ side: 'top', sideOffset: 16, updatePositionStrategy: 'always' }"
>
<div
class="flex items-center justify-center rounded-md border border-dashed border-accented text-sm aspect-video w-72"
@pointerenter="open = true"
@pointerleave="open = false"
@pointermove="(ev) => {
anchor.x = ev.clientX
anchor.y = ev.clientY
}"
>
Hover me
</div>
<template #content>
<div class="p-4">
{{ anchor.x.toFixed(0) }} - {{ anchor.y.toFixed(0) }}
</div>
</template>
</UPopover>
</template>

View File

@@ -0,0 +1,32 @@
<script setup lang="ts">
const { data: users } = await useFetch('https://jsonplaceholder.typicode.com/users', {
key: 'typicode-users-email',
transform: (data: { id: number, name: string, email: string }[]) => {
return data?.map(user => ({
label: user.name,
value: String(user.id),
email: user.email,
avatar: { src: `https://i.pravatar.cc/120?img=${user.id}` }
}))
},
lazy: true
})
</script>
<template>
<USelectMenu
:items="users"
icon="i-lucide-user"
placeholder="Select user"
:ui="{ content: 'min-w-fit' }"
class="w-48"
>
<template #item-label="{ item }">
{{ item.label }}
<span class="text-muted">
{{ item.email }}
</span>
</template>
</USelectMenu>
</template>

View File

@@ -35,6 +35,7 @@ const items = ref([
}
}
] satisfies SelectMenuItem[])
const value = ref(items.value[0])
</script>

View File

@@ -24,6 +24,7 @@ const items = ref([
}
}
] satisfies SelectMenuItem[])
const value = ref(items.value[0])
</script>

View File

@@ -23,6 +23,7 @@ const items = ref([
icon: 'i-lucide-circle-check'
}
] satisfies SelectMenuItem[])
const value = ref(items.value[0])
</script>

View File

@@ -0,0 +1,35 @@
<script setup lang="ts">
const value = ref<string>()
const { data: users } = await useFetch('https://jsonplaceholder.typicode.com/users', {
key: 'typicode-users-email',
transform: (data: { id: number, name: string, email: string }[]) => {
return data?.map(user => ({
label: user.name,
email: user.email,
value: String(user.id),
avatar: { src: `https://i.pravatar.cc/120?img=${user.id}` }
}))
},
lazy: true
})
</script>
<template>
<USelect
v-model="value"
:items="users"
placeholder="Select user"
value-key="value"
:ui="{ content: 'min-w-fit' }"
class="w-48"
>
<template #item-label="{ item }">
{{ item.label }}
<span class="text-muted">
{{ item.email }}
</span>
</template>
</USelect>
</template>

View File

@@ -24,8 +24,8 @@ function getUserAvatar(value: string) {
:loading="status === 'pending'"
icon="i-lucide-user"
placeholder="Select user"
class="w-48"
value-key="value"
class="w-48"
>
<template #leading="{ modelValue, ui }">
<UAvatar

View File

@@ -35,6 +35,7 @@ const items = ref([
}
}
] satisfies SelectItem[])
const value = ref(items.value[0]?.value)
const avatar = computed(() => items.value.find(item => item.value === value.value)?.avatar)

View File

@@ -23,6 +23,7 @@ const items = ref([
icon: 'i-lucide-circle-check'
}
] satisfies SelectItem[])
const value = ref(items.value[0]?.value)
const icon = computed(() => items.value.find(item => item.value === value.value)?.icon)

View File

@@ -0,0 +1,106 @@
<script setup lang="ts">
import { h, resolveComponent } from 'vue'
import type { TableColumn, TableRow } from '@nuxt/ui'
const UBadge = resolveComponent('UBadge')
type Payment = {
id: string
date: string
status: 'paid' | 'failed' | 'refunded'
email: string
amount: number
}
const data = ref<Payment[]>([{
id: '4600',
date: '2024-03-11T15:30:00',
status: 'paid',
email: 'james.anderson@example.com',
amount: 594
}, {
id: '4599',
date: '2024-03-11T10:10:00',
status: 'failed',
email: 'mia.white@example.com',
amount: 276
}, {
id: '4598',
date: '2024-03-11T08:50:00',
status: 'refunded',
email: 'william.brown@example.com',
amount: 315
}, {
id: '4597',
date: '2024-03-10T19:45:00',
status: 'paid',
email: 'emma.davis@example.com',
amount: 529
}, {
id: '4596',
date: '2024-03-10T15:55:00',
status: 'paid',
email: 'ethan.harris@example.com',
amount: 639
}])
const columns: TableColumn<Payment>[] = [{
accessorKey: 'id',
header: '#',
cell: ({ row }) => `#${row.getValue('id')}`
}, {
accessorKey: 'date',
header: 'Date',
cell: ({ row }) => {
return new Date(row.getValue('date')).toLocaleString('en-US', {
day: 'numeric',
month: 'short',
hour: '2-digit',
minute: '2-digit',
hour12: false
})
}
}, {
accessorKey: 'status',
header: 'Status',
cell: ({ row }) => {
const color = ({
paid: 'success' as const,
failed: 'error' as const,
refunded: 'neutral' as const
})[row.getValue('status') as string]
return h(UBadge, { class: 'capitalize', variant: 'subtle', color }, () => row.getValue('status'))
}
}, {
accessorKey: 'email',
header: 'Email'
}, {
accessorKey: 'amount',
header: () => h('div', { class: 'text-right' }, 'Amount'),
footer: ({ column }) => {
const total = column.getFacetedRowModel().rows.reduce((acc: number, row: TableRow<Payment>) => acc + Number.parseFloat(row.getValue('amount')), 0)
const formatted = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'EUR'
}).format(total)
return h('div', { class: 'text-right font-medium' }, `Total: ${formatted}`)
},
cell: ({ row }) => {
const amount = Number.parseFloat(row.getValue('amount'))
const formatted = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'EUR'
}).format(amount)
return h('div', { class: 'text-right font-medium' }, formatted)
}
}]
</script>
<template>
<UTable :data="data" :columns="columns" class="flex-1" />
</template>

View File

@@ -2,6 +2,7 @@
import { h, resolveComponent } from 'vue'
import { upperFirst } from 'scule'
import type { TableColumn } from '@nuxt/ui'
import { useClipboard } from '@vueuse/core'
const UButton = resolveComponent('UButton')
const UCheckbox = resolveComponent('UCheckbox')
@@ -9,6 +10,7 @@ const UBadge = resolveComponent('UBadge')
const UDropdownMenu = resolveComponent('UDropdownMenu')
const toast = useToast()
const { copy } = useClipboard()
type Payment = {
id: string
@@ -220,7 +222,7 @@ const columns: TableColumn<Payment>[] = [{
}, {
label: 'Copy payment ID',
onSelect() {
navigator.clipboard.writeText(row.original.id)
copy(row.original.id)
toast.add({
title: 'Payment ID copied to clipboard!',

View File

@@ -1,7 +1,8 @@
<script setup lang="ts">
import { h, resolveComponent } from 'vue'
import type { TableColumn } from '@nuxt/ui'
import { getGroupedRowModel, type GroupingOptions } from '@tanstack/vue-table'
import { getGroupedRowModel } from '@tanstack/vue-table'
import type { GroupingOptions } from '@tanstack/vue-table'
const UBadge = resolveComponent('UBadge')

View File

@@ -2,12 +2,14 @@
import { h, resolveComponent } from 'vue'
import type { TableColumn } from '@nuxt/ui'
import type { Row } from '@tanstack/vue-table'
import { useClipboard } from '@vueuse/core'
const UButton = resolveComponent('UButton')
const UBadge = resolveComponent('UBadge')
const UDropdownMenu = resolveComponent('UDropdownMenu')
const toast = useToast()
const { copy } = useClipboard()
type Payment = {
id: string
@@ -119,7 +121,7 @@ function getRowItems(row: Row<Payment>) {
}, {
label: 'Copy payment ID',
onSelect() {
navigator.clipboard.writeText(row.original.id)
copy(row.original.id)
toast.add({
title: 'Payment ID copied to clipboard!',

View File

@@ -0,0 +1,159 @@
<script setup lang="ts">
import { h, resolveComponent } from 'vue'
import type { ContextMenuItem, TableColumn, TableRow } from '@nuxt/ui'
import { useClipboard } from '@vueuse/core'
const UBadge = resolveComponent('UBadge')
const UCheckbox = resolveComponent('UCheckbox')
const toast = useToast()
const { copy } = useClipboard()
type Payment = {
id: string
date: string
status: 'paid' | 'failed' | 'refunded'
email: string
amount: number
}
const data = ref<Payment[]>([{
id: '4600',
date: '2024-03-11T15:30:00',
status: 'paid',
email: 'james.anderson@example.com',
amount: 594
}, {
id: '4599',
date: '2024-03-11T10:10:00',
status: 'failed',
email: 'mia.white@example.com',
amount: 276
}, {
id: '4598',
date: '2024-03-11T08:50:00',
status: 'refunded',
email: 'william.brown@example.com',
amount: 315
}, {
id: '4597',
date: '2024-03-10T19:45:00',
status: 'paid',
email: 'emma.davis@example.com',
amount: 529
}, {
id: '4596',
date: '2024-03-10T15:55:00',
status: 'paid',
email: 'ethan.harris@example.com',
amount: 639
}])
const columns: TableColumn<Payment>[] = [{
id: 'select',
header: ({ table }) => h(UCheckbox, {
'modelValue': table.getIsSomePageRowsSelected() ? 'indeterminate' : table.getIsAllPageRowsSelected(),
'onUpdate:modelValue': (value: boolean | 'indeterminate') => table.toggleAllPageRowsSelected(!!value),
'aria-label': 'Select all'
}),
cell: ({ row }) => h(UCheckbox, {
'modelValue': row.getIsSelected(),
'onUpdate:modelValue': (value: boolean | 'indeterminate') => row.toggleSelected(!!value),
'aria-label': 'Select row'
})
}, {
accessorKey: 'id',
header: '#',
cell: ({ row }) => `#${row.getValue('id')}`
}, {
accessorKey: 'date',
header: 'Date',
cell: ({ row }) => {
return new Date(row.getValue('date')).toLocaleString('en-US', {
day: 'numeric',
month: 'short',
hour: '2-digit',
minute: '2-digit',
hour12: false
})
}
}, {
accessorKey: 'status',
header: 'Status',
cell: ({ row }) => {
const color = ({
paid: 'success' as const,
failed: 'error' as const,
refunded: 'neutral' as const
})[row.getValue('status') as string]
return h(UBadge, { class: 'capitalize', variant: 'subtle', color }, () => row.getValue('status'))
}
}, {
accessorKey: 'email',
header: 'Email'
}, {
accessorKey: 'amount',
header: () => h('div', { class: 'text-right' }, 'Amount'),
cell: ({ row }) => {
const amount = Number.parseFloat(row.getValue('amount'))
const formatted = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'EUR'
}).format(amount)
return h('div', { class: 'text-right font-medium' }, formatted)
}
}]
const items = ref<ContextMenuItem[]>([])
function getRowItems(row: TableRow<Payment>) {
return [{
type: 'label' as const,
label: 'Actions'
}, {
label: 'Copy payment ID',
onSelect() {
copy(row.original.id)
toast.add({
title: 'Payment ID copied to clipboard!',
color: 'success',
icon: 'i-lucide-circle-check'
})
}
}, {
label: row.getIsExpanded() ? 'Collapse' : 'Expand',
onSelect() {
row.toggleExpanded()
}
}, {
type: 'separator' as const
}, {
label: 'View customer'
}, {
label: 'View payment details'
}]
}
function onContextmenu(_e: Event, row: TableRow<Payment>) {
items.value = getRowItems(row)
}
</script>
<template>
<UContextMenu :items="items">
<UTable
:data="data"
:columns="columns"
class="flex-1"
@contextmenu="onContextmenu"
>
<template #expanded="{ row }">
<pre>{{ row.original }}</pre>
</template>
</UTable>
</UContextMenu>
</template>

View File

@@ -0,0 +1,157 @@
<script setup lang="ts">
import { h, resolveComponent } from 'vue'
import type { TableColumn, TableRow } from '@nuxt/ui'
const UBadge = resolveComponent('UBadge')
const UCheckbox = resolveComponent('UCheckbox')
type Payment = {
id: string
date: string
status: 'paid' | 'failed' | 'refunded'
email: string
amount: number
}
const data = ref<Payment[]>([{
id: '4600',
date: '2024-03-11T15:30:00',
status: 'paid',
email: 'james.anderson@example.com',
amount: 594
}, {
id: '4599',
date: '2024-03-11T10:10:00',
status: 'failed',
email: 'mia.white@example.com',
amount: 276
}, {
id: '4598',
date: '2024-03-11T08:50:00',
status: 'refunded',
email: 'william.brown@example.com',
amount: 315
}, {
id: '4597',
date: '2024-03-10T19:45:00',
status: 'paid',
email: 'emma.davis@example.com',
amount: 529
}, {
id: '4596',
date: '2024-03-10T15:55:00',
status: 'paid',
email: 'ethan.harris@example.com',
amount: 639
}])
const columns: TableColumn<Payment>[] = [{
id: 'select',
header: ({ table }) => h(UCheckbox, {
'modelValue': table.getIsSomePageRowsSelected() ? 'indeterminate' : table.getIsAllPageRowsSelected(),
'onUpdate:modelValue': (value: boolean | 'indeterminate') => table.toggleAllPageRowsSelected(!!value),
'aria-label': 'Select all'
}),
cell: ({ row }) => h(UCheckbox, {
'modelValue': row.getIsSelected(),
'onUpdate:modelValue': (value: boolean | 'indeterminate') => row.toggleSelected(!!value),
'aria-label': 'Select row'
})
}, {
accessorKey: 'id',
header: '#',
cell: ({ row }) => `#${row.getValue('id')}`
}, {
accessorKey: 'date',
header: 'Date',
cell: ({ row }) => {
return new Date(row.getValue('date')).toLocaleString('en-US', {
day: 'numeric',
month: 'short',
hour: '2-digit',
minute: '2-digit',
hour12: false
})
}
}, {
accessorKey: 'status',
header: 'Status',
cell: ({ row }) => {
const color = ({
paid: 'success' as const,
failed: 'error' as const,
refunded: 'neutral' as const
})[row.getValue('status') as string]
return h(UBadge, { class: 'capitalize', variant: 'subtle', color }, () => row.getValue('status'))
}
}, {
accessorKey: 'email',
header: 'Email'
}, {
accessorKey: 'amount',
header: () => h('div', { class: 'text-right' }, 'Amount'),
cell: ({ row }) => {
const amount = Number.parseFloat(row.getValue('amount'))
const formatted = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'EUR'
}).format(amount)
return h('div', { class: 'text-right font-medium' }, formatted)
}
}]
const anchor = ref({ x: 0, y: 0 })
const reference = computed(() => ({
getBoundingClientRect: () =>
({
width: 0,
height: 0,
left: anchor.value.x,
right: anchor.value.x,
top: anchor.value.y,
bottom: anchor.value.y,
...anchor.value
} as DOMRect)
}))
const open = ref(false)
const openDebounced = refDebounced(open, 10)
const selectedRow = ref<TableRow<Payment> | null>(null)
function onHover(_e: Event, row: TableRow<Payment> | null) {
selectedRow.value = row
open.value = !!row
}
</script>
<template>
<div class="flex w-full flex-1 gap-1">
<UTable
:data="data"
:columns="columns"
class="flex-1"
@pointermove="(ev: PointerEvent) => {
anchor.x = ev.clientX
anchor.y = ev.clientY
}"
@hover="onHover"
/>
<UPopover
:content="{ side: 'top', sideOffset: 16, updatePositionStrategy: 'always' }"
:open="openDebounced"
:reference="reference"
>
<template #content>
<div class="p-4">
{{ selectedRow?.original?.id }}
</div>
</template>
</UPopover>
</div>
</template>

View File

@@ -112,7 +112,7 @@ function onSelect(row: TableRow<Payment>, e?: Event) {
</script>
<template>
<div class=" flex w-full flex-1 gap-1">
<div class="flex w-full flex-1 gap-1">
<div class="flex-1">
<UTable
ref="table"

View File

@@ -1,5 +1,6 @@
<script setup lang="ts">
import type { TableColumn, DropdownMenuItem } from '@nuxt/ui'
import { useClipboard } from '@vueuse/core'
interface User {
id: number
@@ -10,6 +11,7 @@ interface User {
}
const toast = useToast()
const { copy } = useClipboard()
const data = ref<User[]>([{
id: 1,
@@ -71,7 +73,8 @@ function getDropdownActions(user: User): DropdownMenuItem[][] {
label: 'Copy user Id',
icon: 'i-lucide-copy',
onSelect: () => {
navigator.clipboard.writeText(user.id.toString())
copy(user.id.toString())
toast.add({
title: 'User ID copied to clipboard!',
color: 'success',

View File

@@ -26,7 +26,7 @@ const state = reactive({
</script>
<template>
<UTabs :items="items" variant="link" class="gap-4 w-full" :ui="{ trigger: 'grow' }">
<UTabs :items="items" variant="link" :ui="{ trigger: 'grow' }" class="gap-4 w-full">
<template #account="{ item }">
<p class="text-muted mb-4">
{{ item.description }}

View File

@@ -27,8 +27,8 @@ const items: TimelineItem[] = [{
<template>
<UTimeline
:items="items"
:ui="{ item: 'even:flex-row-reverse even:-translate-x-[calc(100%-2rem)] even:text-right' }"
:default-value="2"
class="w-full translate-x-[calc(50%-2rem)]"
:ui="{ item: 'even:flex-row-reverse even:-translate-x-[calc(100%-2rem)] even:text-right' }"
class="translate-x-[calc(50%-1rem)]"
/>
</template>

View File

@@ -42,11 +42,11 @@ const items = [{
<UTimeline
:items="items"
size="xs"
class="w-96"
:ui="{
date: 'float-end ms-1',
description: 'px-3 py-2 ring ring-default mt-2 rounded-md text-default'
}"
class="w-96"
>
<template #title="{ item }">
<span>{{ item.username }}</span>

View File

@@ -0,0 +1,16 @@
<script setup lang="ts">
const toast = useToast()
function showToast() {
toast.add({
title: 'Uh oh! Something went wrong.',
description: 'There was a problem with your request.',
icon: 'i-lucide-wifi',
progress: false
})
}
</script>
<template>
<UButton label="Show toast" color="neutral" variant="outline" @click="showToast" />
</template>

View File

@@ -7,12 +7,12 @@ const appConfig = useAppConfig()
<UFormField
label="toaster.duration"
size="sm"
class="inline-flex ring ring-accented rounded-sm"
:ui="{
wrapper: 'bg-elevated/50 rounded-l-sm flex border-r border-accented',
label: 'text-muted px-2 py-1.5',
container: 'mt-0'
}"
class="inline-flex ring ring-accented rounded-sm"
>
<UInput
v-model="appConfig.toaster.duration"

View File

@@ -7,12 +7,12 @@ const appConfig = useAppConfig()
<UFormField
label="toaster.expand"
size="sm"
class="inline-flex ring ring-accented rounded-sm"
:ui="{
wrapper: 'bg-elevated/50 rounded-l-sm flex border-r border-accented',
label: 'text-muted px-2 py-1.5',
container: 'mt-0'
}"
class="inline-flex ring ring-accented rounded-sm"
>
<USelectMenu
v-model="appConfig.toaster.expand"

View File

@@ -10,12 +10,12 @@ const appConfig = useAppConfig()
<UFormField
label="toaster.position"
size="sm"
class="inline-flex ring ring-accented rounded-sm"
:ui="{
wrapper: 'bg-elevated/50 rounded-l-sm flex border-r border-accented',
label: 'text-muted px-2 py-1.5',
container: 'mt-0'
}"
class="inline-flex ring ring-accented rounded-sm"
>
<USelectMenu
v-model="appConfig.toaster.position"

View File

@@ -0,0 +1,41 @@
<script setup lang="ts">
const open = ref(false)
const anchor = ref({ x: 0, y: 0 })
const reference = computed(() => ({
getBoundingClientRect: () =>
({
width: 0,
height: 0,
left: anchor.value.x,
right: anchor.value.x,
top: anchor.value.y,
bottom: anchor.value.y,
...anchor.value
} as DOMRect)
}))
</script>
<template>
<UTooltip
:open="open"
:reference="reference"
:content="{ side: 'top', sideOffset: 16, updatePositionStrategy: 'always' }"
>
<div
class="flex items-center justify-center rounded-md border border-dashed border-accented text-sm aspect-video w-72"
@pointerenter="open = true"
@pointerleave="open = false"
@pointermove="(ev) => {
anchor.x = ev.clientX
anchor.y = ev.clientY
}"
>
Hover me
</div>
<template #content>
{{ anchor.x.toFixed(0) }} - {{ anchor.y.toFixed(0) }}
</template>
</UTooltip>
</template>

View File

@@ -1,5 +1,5 @@
import { onMounted, watch } from 'vue'
import FaviconSvg from 'public/icon.svg?raw'
import FaviconSvg from '../../public/icon.svg?raw'
export function useFaviconFromTheme() {
const colorMode = useColorMode()

View File

@@ -59,7 +59,7 @@ provide('navigation', mappedNavigation)
<UApp>
<NuxtLoadingIndicator color="#FFF" />
<!-- <Banner /> -->
<Banner />
<Header :links="links" />

View File

@@ -2,7 +2,8 @@
import { kebabCase } from 'scule'
import type { ContentNavigationItem } from '@nuxt/content'
import type { PageLink } from '@nuxt/ui-pro'
import { findPageBreadcrumb, mapContentNavigation } from '@nuxt/ui-pro/utils/content'
import { mapContentNavigation } from '@nuxt/ui-pro/utils/content'
import { findPageBreadcrumb } from '@nuxt/content/utils'
const route = useRoute()
const { framework, module } = useSharedData()
@@ -37,7 +38,7 @@ const { data: surround } = await useAsyncData(`${kebabCase(route.path)}-surround
const navigation = inject<Ref<ContentNavigationItem[]>>('navigation')
const breadcrumb = computed(() => mapContentNavigation(findPageBreadcrumb(navigation?.value, page.value)).map(({ icon, ...link }) => link))
const breadcrumb = computed(() => mapContentNavigation(findPageBreadcrumb(navigation?.value, page.value?.path, { indexAsChild: true })).map(({ icon, ...link }) => link))
if (!import.meta.prerender) {
// Redirect to the correct framework version if the page is not the current framework
@@ -141,7 +142,7 @@ const communityLinks = computed(() => [{
<MDC v-if="page.description" :value="page.description" unwrap="p" :cache-key="`${kebabCase(route.path)}-description`" />
</template>
<template v-if="page.links?.length" #links>
<template #links>
<UButton
v-for="link in page.links"
:key="link.label"
@@ -154,6 +155,7 @@ const communityLinks = computed(() => [{
<UAvatar v-bind="link.avatar" size="2xs" :alt="`${link.label} avatar`" />
</template>
</UButton>
<PageHeaderLinks />
</template>
</UPageHeader>

View File

@@ -5,6 +5,17 @@ pricing:
title: Upgrade to Nuxt UI [Pro]{class="text-primary"}.
description: On top of 40+ open source components from Nuxt UI, Pro gives you access to 50+ premium Vue components to create beautiful & responsive Nuxt applications in minutes. It includes all primitives to build landing pages, documentations, blogs, dashboards or entire SaaS products.
freePlan:
description: "**NuxtLabs is joining Vercel** :tada: As part of this transition, Nuxt UI is becoming even more accessible.<br><br> **In September, we're launching Nuxt UI v4**: a free, open-source library that unifies Nuxt UI and Nuxt UI Pro, offering 100+ components and a complete free Figma Kit for everyone."
orientation: horizontal
button:
label: Read the announcement
to: 'https://nuxtlabs.com/?utm_source=nuxt-ui&utm_medium=banner&utm_campaign=nuxtlabs-vercel'
target: _blank
color: 'neutral'
trailingIcon: 'i-lucide-arrow-right'
ui:
trailingIcon: 'ms-0'
devPlan:
title: Free in development
description: Try Nuxt UI Pro for free in development, no credit card required. Upgrade when ready to deploy.
orientation: horizontal
@@ -13,6 +24,9 @@ pricing:
to: '/getting-started/installation/pro/nuxt'
color: 'neutral'
variant: 'subtle'
trailingIcon: 'i-lucide-arrow-right'
ui:
trailingIcon: 'ms-0'
figma:
title: Figma Kit Pro
description: Get all Nuxt UI Pro components in a Figma kit to design your next application before coding. Everything you need, from wire-framing to high-fidelity web integration.

View File

@@ -34,10 +34,19 @@ useSeoMeta({
<div class="flex flex-col bg-default gap-8 lg:gap-0">
<UPricingPlan
v-bind="page.pricing.freePlan"
variant="naked"
class="lg:rounded-none border-x border-default border-t border-b lg:border-b-0"
class="lg:rounded-none ring-primary/15 ring-inset -mb-px bg-primary/5 z-[1]"
:ui="{ description: 'mt-0 text-primary' }"
>
<template #description>
<MDC :value="page.pricing.freePlan.description" unwrap="p" />
</template>
</UPricingPlan>
<UPricingPlan
v-bind="page.pricing.devPlan"
class="lg:rounded-none ring-inset -mb-px"
/>
<UPricingPlans compact>
<UPricingPlans compact class="-space-x-px">
<UPricingPlan
v-for="(plan, index) in page.pricing.plans"
:key="index"
@@ -47,18 +56,17 @@ useSeoMeta({
:discount="plan.discount"
:billing-period="plan.billing_period"
:billing-cycle="plan.billing_cycle"
:variant="plan.highlight ? 'soft' : 'outline'"
:class="['lg:rounded-none', { 'border-2 lg:border lg:border-x-0 border-primary lg:border-default': plan.highlight }]"
:variant="plan.highlight ? 'subtle' : 'outline'"
class="lg:rounded-none ring-inset -mb-px"
:features="plan.features"
:button="plan.button"
/>
</UPricingPlans>
<UPricingPlan
v-bind="page.pricing.figma"
variant="naked"
:billing-period="page.pricing.figma.billing_period"
:billing-cycle="page.pricing.figma.billing_cycle"
class="lg:rounded-none border lg:border-y-0 border-default"
class="lg:rounded-none ring-inset -mb-px"
>
<template #features>
<li v-for="(feature, index) in page.pricing.figma.features" :key="index" class="flex items-center gap-2 min-w-0">

View File

@@ -16,12 +16,12 @@ function handleMessage(message) {
async function handleFormatMessage(message) {
if (!globalThis.prettier) {
await Promise.all([
import('https://cdn.jsdelivr.net/npm/prettier@3.5.2/standalone.js'),
import('https://cdn.jsdelivr.net/npm/prettier@3.5.2/plugins/babel.js'),
import('https://cdn.jsdelivr.net/npm/prettier@3.5.2/plugins/estree.js'),
import('https://cdn.jsdelivr.net/npm/prettier@3.5.2/plugins/html.js'),
import('https://cdn.jsdelivr.net/npm/prettier@3.5.2/plugins/markdown.js'),
import('https://cdn.jsdelivr.net/npm/prettier@3.5.2/plugins/typescript.js')
import('https://cdn.jsdelivr.net/npm/prettier@3.6.2/standalone.js'),
import('https://cdn.jsdelivr.net/npm/prettier@3.6.2/plugins/babel.js'),
import('https://cdn.jsdelivr.net/npm/prettier@3.6.2/plugins/estree.js'),
import('https://cdn.jsdelivr.net/npm/prettier@3.6.2/plugins/html.js'),
import('https://cdn.jsdelivr.net/npm/prettier@3.6.2/plugins/markdown.js'),
import('https://cdn.jsdelivr.net/npm/prettier@3.6.2/plugins/typescript.js')
])
}

View File

@@ -84,7 +84,7 @@ You can play with Nuxt UI components as well as your app components directly fro
Install the module to your Nuxt application with one command:
```bash [Terminal]
npx nuxi module add compodium
npx nuxt module add compodium
```
::

View File

@@ -115,7 +115,7 @@ Start your project using the [nuxt/starter#ui](https://github.com/nuxt/starter/t
Create a new project locally by running the following command:
```bash [Terminal]
npx nuxi init -t ui <my-app>
npm create nuxt@latest -- -t ui
```
::note
@@ -225,6 +225,27 @@ export default defineNuxtConfig({
This option adds the `transition-colors` class on components with hover or active states.
::
### `theme.defaultVariants` :badge{label="Soon" class="align-text-top"}
Use the `theme.defaultVariants` option to override the default `color` and `size` variants for components.
- Default: `{ color: 'primary', size: 'md' }`{lang="ts-type"}
```ts [nuxt.config.ts]
export default defineNuxtConfig({
modules: ['@nuxt/ui'],
css: ['~/assets/css/main.css'],
ui: {
theme: {
defaultVariants: {
color: 'neutral',
size: 'sm'
}
}
}
})
```
## Continuous Releases
Nuxt UI uses [pkg.pr.new](https://github.com/stackblitz-labs/pkg.pr.new) for continuous preview releases, providing developers with instant access to the latest features and bug fixes without waiting for official releases.

View File

@@ -78,6 +78,22 @@ components.d.ts
::
::tip
Internally, Nuxt UI relies on custom alias to resolve the theme types. If you're using TypeScript, you should add an alias to your `tsconfig` to enable auto-completion in your `vite.config.ts`.
```json [tsconfig.node.json]
{
"compilerOptions": {
"paths": {
"#build/ui": [
"./node_modules/@nuxt/ui/.nuxt/ui"
]
}
}
}
```
::
#### Use the Nuxt UI Vue plugin in your `main.ts`
```ts [main.ts]{3,14}
@@ -179,7 +195,7 @@ Start your project using the [nuxtlabs/nuxt-ui-vue-starter](https://github.com/n
Create a new project locally by running the following command:
```bash [Terminal]
npx nuxi init -t github:nuxtlabs/nuxt-ui-vue-starter <my-app>
npm create nuxt@latest -- -t github:nuxtlabs/nuxt-ui-vue-starter
```
::note
@@ -317,6 +333,32 @@ export default defineConfig({
This option adds the `transition-colors` class on components with hover or active states.
::
### `theme.defaultVariants` :badge{label="Soon" class="align-text-top"}
Use the `theme.defaultVariants` option to override the default `color` and `size` variants for components.
- Default: `{ color: 'primary', size: 'md' }`{lang="ts-type"}
```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({
theme: {
defaultVariants: {
color: 'neutral',
size: 'sm'
}
}
})
]
})
```
### `inertia`
Use the `inertia` option to enable compatibility with [Inertia.js](https://inertiajs.com/).

View File

@@ -536,6 +536,33 @@ import { ModalExampleComponent } from '#components'
</script>
```
### Changed form validation
- The error object property for targeting form fields has been renamed from `path` to `name`:
```diff
<script setup lang="ts">
const validate = (state: any): FormError[] => {
const errors = []
if (!state.email) {
errors.push({
- path: 'email',
+ name: 'email',
message: 'Required'
})
}
if (!state.password) {
errors.push({
- path: 'password',
+ name: 'password',
message: 'Required'
})
}
return errors
}
</script>
```
---
::warning

View File

@@ -87,7 +87,7 @@ Read more about this in the `@nuxt/icon` documentation.
You can use local SVG files to create a custom Iconify collection.
For example, place your icons' SVG files under a folder of your choice, for example, `./assets/icons`:
For example, place your icons' SVG files under a folder of your choice, for example, `./app/assets/icons`:
```bash
assets/icons
@@ -104,7 +104,7 @@ export default defineNuxtConfig({
icon: {
customCollections: [{
prefix: 'custom',
dir: './assets/icons'
dir: './app/assets/icons'
}]
}
})

View File

@@ -32,6 +32,12 @@ props:
You can use any name from the <https://icones.js.org> collection.
::
::warning
When using collections with a dash (`-`), you need to separate the icon name from the collection name with a colon (`:`) as `@iconify/vue` does not handle this case like `@nuxt/icon`. For example, instead of `i-simple-icons-github` you need to write `i-simple-icons:github` or `simple-icons:github`.
Learn more about the [Iconify naming convention](https://iconify.design/docs/icon-components/vue/#icon).
::
### Component Props
Some components also have an `icon` prop to display an icon, like the [Button](/components/button) for example:

View File

@@ -60,7 +60,7 @@ import { fr } from '@nuxt/ui-pro/locale'
### Custom locale
You also have the option to add your own locale using `defineLocale`:
You can create your own locale using the `defineLocale` composable:
::module-only
@@ -125,6 +125,65 @@ Look at the `code` parameter, there you need to pass the iso code of the languag
::
### Extend locale :badge{label="New" class="align-text-top"}
You can customize an existing locale by overriding its `messages` or `code` using the `extendLocale` composable:
::module-only
#ui
:::div
```vue [app.vue]
<script setup lang="ts">
import { en } from '@nuxt/ui/locale'
const locale = extendLocale(en, {
code: 'en-GB',
messages: {
commandPalette: {
placeholder: 'Search a component...'
}
}
})
</script>
<template>
<UApp :locale="locale">
<NuxtPage />
</UApp>
</template>
```
:::
#ui-pro
:::div
```vue [app.vue]
<script setup lang="ts">
import { en } from '@nuxt/ui-pro/locale'
const locale = extendLocale(en, {
code: 'en-GB',
messages: {
commandPalette: {
placeholder: 'Search a component...'
}
}
})
</script>
<template>
<UApp :locale="locale">
<NuxtPage />
</UApp>
</template>
```
:::
::
### Dynamic locale
To dynamically switch between languages, you can use the [Nuxt I18n](https://i18n.nuxtjs.org/) module.

View File

@@ -60,7 +60,7 @@ import { fr } from '@nuxt/ui-pro/locale'
### Custom locale
You also have the option to add your locale using `defineLocale`:
You can create your own locale using the `defineLocale` composable:
::module-only
@@ -127,6 +127,67 @@ Look at the `code` parameter, there you need to pass the iso code of the languag
::
### Extend locale :badge{label="New" class="align-text-top"}
You can customize an existing locale by overriding its `messages` or `code` using the `extendLocale` composable:
::module-only
#ui
:::div
```vue [App.vue]
<script setup lang="ts">
import { en } from '@nuxt/ui/locale'
import { extendLocale } from '@nuxt/ui/composables/defineLocale.js'
const locale = extendLocale(en, {
code: 'en-GB',
messages: {
commandPalette: {
placeholder: 'Search a component...'
}
}
})
</script>
<template>
<UApp :locale="locale">
<RouterView />
</UApp>
</template>
```
:::
#ui-pro
:::div
```vue [App.vue]
<script setup lang="ts">
import { en } from '@nuxt/ui-pro/locale'
import { extendLocale } from '@nuxt/ui/composables/defineLocale.js'
const locale = extendLocale(en, {
code: 'en-GB',
messages: {
commandPalette: {
placeholder: 'Search a component...'
}
}
})
</script>
<template>
<UApp :locale="locale">
<RouterView />
</UApp>
</template>
```
:::
::
### Dynamic locale
To dynamically switch between languages, you can use the [Vue I18n](https://vue-i18n.intlify.dev/) plugin.

View File

@@ -1,6 +1,6 @@
---
title: useOverlay
description: "A composable to programmatically control overlays."
description: 'A composable to programmatically control overlays.'
---
## Usage
@@ -9,9 +9,11 @@ Use the auto-imported `useOverlay` composable to programmatically control [Modal
```vue
<script setup lang="ts">
import { LazyModalExample } from '#components'
const overlay = useOverlay()
const modal = overlay.create(MyModal)
const modal = overlay.create(LazyModalExample)
async function openModal() {
modal.open()
@@ -29,71 +31,73 @@ In order to return a value from the overlay, the `overlay.open().instance.result
### `create(component: T, options: OverlayOptions): OverlayInstance`
Creates an overlay, and returns a factory instance
Create an overlay, and return a factory instance.
- Parameters:
- `component`: The overlay component
- `options` The overlay options
- `defaultOpen?: boolean` Opens the overlay immediately after being created `default: false`
- `component`: The overlay component.
- `options`:
- `defaultOpen?: boolean` Open the overlay immediately after being created. Defaults to `false`.
- `props?: ComponentProps`: An optional object of props to pass to the rendered component.
- `destroyOnClose?: boolean` Removes the overlay from memory when closed `default: false`
- `destroyOnClose?: boolean` Removes the overlay from memory when closed. Defaults to `false`.
### `open(id: symbol, props?: ComponentProps<T>): OpenedOverlay<T>`
Opens the overlay using its `id`
Open an overlay by its `id`.
- Parameters:
- `id`: The identifier of the overlay
- `id`: The identifier of the overlay.
- `props`: An optional object of props to pass to the rendered component.
### `close(id: symbol, value?: any): void`
Close an overlay using its `id`
Close an overlay by its `id`.
- Parameters:
- `id`: The identifier of the overlay
- `value`: A value to resolve the overlay promise with
- `id`: The identifier of the overlay.
- `value`: A value to resolve the overlay promise with.
### `patch(id: symbol, props: ComponentProps<T>): void`
Update an overlay using its `id`
Update an overlay by its `id`.
- Parameters:
- `id`: The identifier of the overlay
- `id`: The identifier of the overlay.
- `props`: An object of props to update on the rendered component.
### `unmount(id: symbol): void`
Removes the overlay from the DOM using its `id`
Remove an overlay from the DOM by its `id`.
- Parameters:
- `id`: The identifier of the overlay
- `id`: The identifier of the overlay.
### `isOpen(id: symbol): boolean`
Checks if an overlay its open using its `id`
Check if an overlay is open using its `id`.
- Parameters:
- `id`: The identifier of the overlay
- `id`: The identifier of the overlay.
### `overlays: Overlay[]`
In-memory list of overlays that were created
In-memory list of all overlays that were created.
## Overlay Instance API
## Instance API
### `open(props?: ComponentProps<T>): Promise<OpenedOverlay<T>>`
Opens the overlay
Open the overlay.
- Parameters:
- `props`: An optional object of props to pass to the rendered component.
```vue
<script setup lang="ts">
import { LazyModalExample } from '#components'
const overlay = useOverlay()
const modal = overlay.create(MyModalContent)
const modal = overlay.create(LazyModalExample)
function openModal() {
modal.open({
@@ -105,23 +109,25 @@ function openModal() {
### `close(value?: any): void`
Close the overlay
Close the overlay.
- Parameters:
- `value`: A value to resolve the overlay promise with
- `value`: A value to resolve the overlay promise with.
### `patch(props: ComponentProps<T>)`
Updates the props of the overlay.
Update the props of the overlay.
- Parameters:
- `props`: An object of props to update on the rendered component.
```vue
<script setup lang="ts">
import { LazyModalExample } from '#components'
const overlay = useOverlay()
const modal = overlay.create(MyModal, {
const modal = overlay.create(LazyModalExample, {
title: 'Welcome'
})
@@ -141,6 +147,8 @@ Here's a complete example of how to use the `useOverlay` composable:
```vue
<script setup lang="ts">
import { ModalA, ModalB, SlideoverA } from '#components'
const overlay = useOverlay()
// Create with default props
@@ -150,7 +158,7 @@ const modalB = overlay.create(ModalB)
const slideoverA = overlay.create(SlideoverA)
const openModalA = () => {
// Open Modal A, but override the title prop
// Open modalA, but override the title prop
modalA.open({ title: 'Hello' })
}
@@ -160,16 +168,37 @@ const openModalB = async () => {
const input = await modalBInstance.result
// Pass the result from modalB to the slideover, and open it.
// Pass the result from modalB to the slideover, and open it
slideoverA.open({ input })
}
</script>
<template>
<div>
<button @click="openModal">Open Modal</button>
</div>
<button @click="openModalA">Open Modal</button>
</template>
```
In this example, we're using the `useOverlay` composable to control multiple modals and slideovers.
## Caveats
### Provide / Inject
When opening overlays programmatically (e.g. modals, slideovers, etc), the overlay component can only access injected values from the component containing `UApp` (typically `app.vue` or layout components). This is because overlays are mounted outside of the page context by the `UApp` component.
As such, using `provide()` in pages or parent components isn't supported directly. To pass provided values to overlays, the recommended approach is to use props instead:
```vue
<script setup lang="ts">
import { LazyModalExample } from '#components'
const providedValue = inject('valueProvidedInPage')
const modal = overlay.create(LazyModalExample, {
props: {
providedValue,
otherData: someValue
}
})
</script>
```

View File

@@ -9,7 +9,6 @@ links:
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/CheckboxGroup.vue
navigation.badge: New
---

View File

@@ -156,7 +156,7 @@ props:
---
::
### Variant :badge{label="New" class="align-text-top"}
### Variant
Use the `variant` prop to change the variant of the Checkbox.
@@ -190,7 +190,7 @@ props:
---
::
### Indicator :badge{label="New" class="align-text-top"}
### Indicator
Use the `indicator` prop to change the position or hide the indicator. Defaults to `start`.

View File

@@ -52,9 +52,11 @@ Each group contains an `items` array of objects that define the commands. Each i
- `loading?: boolean`{lang="ts-type"}
- `disabled?: boolean`{lang="ts-type"}
- [`slot?: string`{lang="ts-type"}](#with-custom-slot)
- `placeholder?: string`{lang="ts-type"}
- `children?: CommandPaletteItem[]`{lang="ts-type"}
- `onSelect?(e?: Event): void`{lang="ts-type"}
- `class?: any`{lang="ts-type"}
- `ui?: { item?: ClassNameValue, itemLeadingIcon?: ClassNameValue, itemLeadingAvatarSize?: ClassNameValue, itemLeadingAvatar?: ClassNameValue, itemLeadingChipSize?: ClassNameValue, itemLeadingChip?: ClassNameValue, itemLabel?: ClassNameValue, itemLabelPrefix?: ClassNameValue, itemLabelBase?: ClassNameValue, itemLabelSuffix?: ClassNameValue, itemTrailing?: ClassNameValue, itemTrailingKbds?: ClassNameValue, itemTrailingKbdsSize?: ClassNameValue, itemTrailingHighlightedIcon?: ClassNameValue, itemTrailingIcon?: ClassNameValue,}`{lang="ts-type"}
- `ui?: { item?: ClassNameValue, itemLeadingIcon?: ClassNameValue, itemLeadingAvatarSize?: ClassNameValue, itemLeadingAvatar?: ClassNameValue, itemLeadingChipSize?: ClassNameValue, itemLeadingChip?: ClassNameValue, itemLabel?: ClassNameValue, itemLabelPrefix?: ClassNameValue, itemLabelBase?: ClassNameValue, itemLabelSuffix?: ClassNameValue, itemTrailing?: ClassNameValue, itemTrailingKbds?: ClassNameValue, itemTrailingKbdsSize?: ClassNameValue, itemTrailingHighlightedIcon?: ClassNameValue, itemTrailingIcon?: ClassNameValue }`{lang="ts-type"}
You can pass any property from the [Link](/components/link#props) component such as `to`, `target`, etc.
@@ -110,6 +112,10 @@ props:
---
::
::tip{to="#with-children-in-items"}
Each item can take a `children` array of objects with the following properties to create submenus:
::
### Multiple
Use the `multiple` prop to allow multiple selections.
@@ -246,6 +252,128 @@ You can customize this icon globally in your `vite.config.ts` under `ui.icons.se
:::
::
### Selected Icon
Use the `selected-icon` prop to customize the selected item [Icon](/components/icon). Defaults to `i-lucide-check`.
::component-code
---
collapse: true
hide:
- autofocus
ignore:
- groups
- modelValue
- multiple
- class
external:
- groups
- modelValue
class: '!p-0'
props:
multiple: true
autofocus: false
modelValue:
- label: 'Benjamin Canac'
suffix: 'benjamincanac'
avatar:
src: 'https://github.com/benjamincanac.png'
selectedIcon: 'i-lucide-circle-check'
groups:
- id: 'users'
label: 'Users'
items:
- label: 'Benjamin Canac'
suffix: 'benjamincanac'
avatar:
src: 'https://github.com/benjamincanac.png'
- label: 'Sylvain Marroufin'
suffix: 'smarroufin'
avatar:
src: 'https://github.com/smarroufin.png'
- label: 'Sébastien Chopin'
suffix: 'atinux'
avatar:
src: 'https://github.com/atinux.png'
- label: 'Romain Hamel'
suffix: 'romhml'
avatar:
src: 'https://github.com/romhml.png'
- label: 'Haytham A. Salama'
suffix: 'Haythamasalama'
avatar:
src: 'https://github.com/Haythamasalama.png'
- label: 'Daniel Roe'
suffix: 'danielroe'
avatar:
src: 'https://github.com/danielroe.png'
- label: 'Neil Richter'
suffix: 'noook'
avatar:
src: 'https://github.com/noook.png'
class: 'flex-1'
---
::
::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.
:::
::
### Trailing Icon :badge{label="New" class="align-text-top"}
Use the `trailing-icon` prop to customize the trailing [Icon](/components/icon) when an item has children. Defaults to `i-lucide-chevron-right`.
::component-code
---
collapse: true
prettier: true
hide:
- autofocus
ignore:
- groups
- class
external:
- groups
class: '!p-0'
props:
autofocus: false
trailingIcon: 'i-lucide-arrow-right'
groups:
- id: 'actions'
items:
- label: 'Share'
icon: 'i-lucide-share'
children:
- label: 'Email'
icon: 'i-lucide-mail'
- label: 'Copy'
icon: 'i-lucide-copy'
- label: 'Link'
icon: 'i-lucide-link'
class: 'flex-1'
---
::
::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.
:::
::
### Loading
Use the `loading` prop to show a loading icon on the CommandPalette.
@@ -321,37 +449,6 @@ You can customize this icon globally in your `vite.config.ts` under `ui.icons.lo
:::
::
### Disabled
Use the `disabled` prop to disable the CommandPalette.
::component-code
---
collapse: true
hide:
- autofocus
ignore:
- groups
- class
external:
- groups
class: '!p-0'
props:
autofocus: false
disabled: true
groups:
- id: 'apps'
items:
- label: 'Calendar'
icon: 'i-lucide-calendar'
- label: 'Music'
icon: 'i-lucide-music'
- label: 'Maps'
icon: 'i-lucide-map'
class: 'flex-1'
---
::
### Close
Use the `close` prop to display a [Button](/components/button) to dismiss the CommandPalette.
@@ -468,6 +565,124 @@ You can customize this icon globally in your `vite.config.ts` under `ui.icons.cl
:::
::
### Back :badge{label="New" class="align-text-top"}
Use the `back` prop to customize or hide the back button (with `false` value) displayed when navigating into a submenu.
You can pass any property from the [Button](/components/button) component to customize it.
::component-code
---
collapse: true
prettier: true
hide:
- autofocus
ignore:
- back.color
- groups
- class
external:
- groups
class: '!p-0'
props:
autofocus: false
back:
color: primary
groups:
- id: 'actions'
items:
- label: 'Share'
icon: 'i-lucide-share'
children:
- label: 'Email'
icon: 'i-lucide-mail'
- label: 'Copy'
icon: 'i-lucide-copy'
- label: 'Link'
icon: 'i-lucide-link'
class: 'flex-1'
---
::
### Back Icon :badge{label="New" class="align-text-top"}
Use the `back-icon` prop to customize the back button [Icon](/components/icon). Defaults to `i-lucide-arrow-left`.
::component-code
---
collapse: true
hide:
- autofocus
ignore:
- class
- groups
- back
external:
- groups
class: '!p-0'
props:
autofocus: false
back: true
backIcon: 'i-lucide-house'
groups:
- id: 'actions'
items:
- label: 'Share'
icon: 'i-lucide-share'
children:
- label: 'Email'
icon: 'i-lucide-mail'
- label: 'Copy'
icon: 'i-lucide-copy'
- label: 'Link'
icon: 'i-lucide-link'
class: 'flex-1'
---
::
::framework-only
#nuxt
:::tip{to="/getting-started/icons/nuxt#theme"}
You can customize this icon globally in your `app.config.ts` under `ui.icons.arrowLeft` key.
:::
#vue
:::tip{to="/getting-started/icons/vue#theme"}
You can customize this icon globally in your `vite.config.ts` under `ui.icons.arrowLeft` key.
:::
::
### Disabled
Use the `disabled` prop to disable the CommandPalette.
::component-code
---
collapse: true
hide:
- autofocus
ignore:
- groups
- class
external:
- groups
class: '!p-0'
props:
autofocus: false
disabled: true
groups:
- id: 'apps'
items:
- label: 'Calendar'
icon: 'i-lucide-calendar'
- label: 'Music'
icon: 'i-lucide-music'
- label: 'Maps'
icon: 'i-lucide-map'
class: 'flex-1'
---
::
## Examples
### Control selected item(s)
@@ -502,6 +717,28 @@ props:
This example uses the `@update:model-value` event to reset the search term when an item is selected.
::
### With children in items :badge{label="New" class="align-text-top"}
You can create hierarchical menus by using the `children` property in items. When an item has children, it will automatically display a chevron icon and enable navigation into a submenu.
::component-example
---
collapse: true
prettier: true
name: 'command-palette-items-children-example'
class: '!p-0'
props:
autofocus: false
---
::
::note
When navigating into a submenu:
- The search term is reset
- A back button appears in the input
- You can go back to the previous group by pressing the :kbd{value="backspace"} key
::
### With fetched items
You can fetch items from an API and use them in the CommandPalette.
@@ -640,6 +877,20 @@ props:
This can be useful when using the CommandPalette inside a [`Modal`](/components/modal) for example.
::
### With footer slot :badge{label="Soon" class="align-text-top"}
Use the `#footer` slot to add custom content at the bottom of the CommandPalette, such as keyboard shortcuts help or additional actions.
::component-example
---
collapse: true
name: 'command-palette-footer-slot-example'
class: '!p-0'
props:
autofocus: false
---
::
### With custom slot
Use the `slot` property to customize a specific item or group.
@@ -658,6 +909,7 @@ You will have access to the following slots:
::component-example
---
collapse: true
name: 'command-palette-custom-slot-example'
class: '!p-0'
props:

View File

@@ -328,6 +328,17 @@ name: 'drawer-responsive-example'
---
::
### Nested drawers :badge{label="Soon" class="align-text-top"}
You can nest drawers within each other by using the `nested` prop.
::component-example
---
prettier: true
name: 'drawer-nested-example'
---
::
### With footer slot
Use the `#footer` slot to add content after the Drawer's body.

View File

@@ -0,0 +1,31 @@
---
title: FileUpload
description: ''
links:
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/FileUpload.vue
navigation.badge: Soon
---
## Usage
## Examples
## API
### Props
:component-props
### Slots
:component-slots
### Emits
:component-emits
## Theme
:component-theme

View File

@@ -135,7 +135,7 @@ props:
### Multiple
Use the `multiple` prop to allow multiple selections, the selected items will be displayed as badges.
Use the `multiple` prop to allow multiple selections, the selected items will be displayed as tags.
::component-code
---
@@ -166,7 +166,7 @@ Ensure to pass an array to the `default-value` prop or the `v-model` directive.
### Delete Icon
With `multiple`, use the `delete-icon` prop to customize the delete [Icon](/components/icon) in the badges. Defaults to `i-lucide-x`.
With `multiple`, use the `delete-icon` prop to customize the delete [Icon](/components/icon) in the tags. Defaults to `i-lucide-x`.
::component-code
---
@@ -757,6 +757,33 @@ name: 'input-menu-filter-fields-example'
---
::
### With full content width
You can expand the content to the full width of its items by using the `ui.content` key.
::component-example
---
name: 'input-menu-content-width-example'
collapse: true
---
::
::tip
You can also change the content width globally in your `app.config.ts`:
```
export default defineAppConfig({
ui: {
inputMenu: {
slots: {
content: 'min-w-fit'
}
}
}
})
```
::
### 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.
@@ -782,6 +809,14 @@ name: 'input-menu-countries-example'
:component-emits
### Expose
When accessing the component via a template ref, you can use the following:
| Name | Type |
| ---- | ---- |
| `inputRef`{lang="ts-type"} | `Ref<InstanceType<typeof ComboboxTrigger> \| null>`{lang="ts-type"} |
## Theme
:component-theme

View File

@@ -1,6 +1,6 @@
---
title: InputNumber
description: Input numerical values with a customizable range.
description: An input for numerical values with a customizable range.
category: form
links:
- label: NumberField
@@ -287,8 +287,8 @@ name: 'input-number-slots-example'
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"} |
| -------------------------- | ----------------------------------------------- |
| `inputRef`{lang="ts-type"} | `Ref<InstanceType<typeof NumberFieldInput> \| null>`{lang="ts-type"} |
## Theme

View File

@@ -0,0 +1,296 @@
---
title: InputTags
description: An input element that displays interactive tags.
category: form
links:
- label: InputTags
icon: i-custom-reka-ui
to: https://reka-ui.com/docs/components/tags-input
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/InputTags.vue
navigation.badge: New
---
## Usage
Use the `v-model` directive to control the value of the InputTags.
::component-code
---
prettier: true
ignore:
- modelValue
external:
- modelValue
props:
modelValue: ['Vue']
---
::
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: ['Vue']
---
::
### Placeholder
Use the `placeholder` prop to set a placeholder text.
::component-code
---
props:
placeholder: 'Enter tags...'
---
::
### Max Length :badge{label="Soon" class="align-text-top"}
Use the `max-length` prop to set the maximum number of characters allowed in a tag.
::component-code
---
props:
maxLength: 4
---
::
### Color
Use the `color` prop to change the ring color when the InputTags is focused.
::component-code
---
prettier: true
ignore:
- modelValue
external:
- modelValue
props:
modelValue: ['Vue']
color: neutral
highlight: true
---
::
::note
The `highlight` prop is used here to show the focus state. It's used internally when a validation error occurs.
::
### Variants
Use the `variant` prop to change the appearance of the InputTags.
::component-code
---
prettier: true
ignore:
- modelValue
external:
- modelValue
props:
modelValue: ['Vue']
variant: subtle
color: neutral
highlight: false
---
::
### Sizes
Use the `size` prop to adjust the size of the InputTags.
::component-code
---
prettier: true
ignore:
- modelValue
external:
- modelValue
props:
modelValue: ['Vue']
size: xl
---
::
### Icon
Use the `icon` prop to show an [Icon](/components/icon) inside the InputTags.
::component-code
---
prettier: true
ignore:
- modelValue
external:
- modelValue
props:
modelValue: ['Vue']
icon: 'i-lucide-search'
size: md
variant: outline
---
::
::note
Use the `leading` and `trailing` props to set the icon position or the `leading-icon` and `trailing-icon` props to set a different icon for each position.
::
### Avatar
Use the `avatar` prop to show an [Avatar](/components/avatar) inside the InputTags.
::component-code
---
prettier: true
ignore:
- modelValue
external:
- modelValue
props:
modelValue: ['Vue']
avatar:
src: 'https://github.com/vuejs.png'
size: md
variant: outline
---
::
### Delete Icon
Use the `delete-icon` prop to customize the delete [Icon](/components/icon) in the tags. Defaults to `i-lucide-x`.
::component-code
---
prettier: true
ignore:
- modelValue
external:
- modelValue
props:
modelValue: ['Vue']
deleteIcon: 'i-lucide-trash'
---
::
::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.
:::
::
### Loading
Use the `loading` prop to show a loading icon on the InputTags.
::component-code
---
prettier: true
ignore:
- modelValue
external:
- modelValue
props:
modelValue: ['Vue']
loading: true
trailing: false
---
::
### Loading Icon
Use the `loading-icon` prop to customize the loading icon. Defaults to `i-lucide-loader-circle`.
::component-code
---
prettier: true
ignore:
- modelValue
external:
- modelValue
props:
modelValue: ['Vue']
loading: true
loadingIcon: 'i-lucide-loader'
---
::
::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
Use the `disabled` prop to disable the InputTags.
::component-code
---
prettier: true
ignore:
- modelValue
external:
- modelValue
props:
modelValue: ['Vue']
disabled: true
---
::
## Examples
### Within a FormField
You can use the InputTags within a [FormField](/components/form-field) component to display a label, help text, required indicator, etc.
::component-example
---
name: 'input-tags-form-field-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<InstanceType<typeof TagsInputInput> \| null>`{lang="ts-type"} |
## Theme
:component-theme

View File

@@ -278,6 +278,16 @@ name: 'input-kbd-example'
This example uses the `defineShortcuts` composable to focus the Input when the :kbd{value="/"} key is pressed.
::
### With mask
There's no built-in support for masks, but you can use libraries like [maska](https://github.com/beholdr/maska) to mask the Input.
::component-example
---
name: 'input-mask-example'
---
::
### With floating label
You can use the `#default` slot to add a floating label to the Input.

View File

@@ -62,6 +62,19 @@ items:
---
::
### Color :badge{label="Soon" class="align-text-top"}
Use the `color` prop to change the color of the Kbd.
::component-code
---
props:
color: neutral
slots:
default: K
---
::
### Variant
Use the `variant` prop to change the variant of the Kbd.
@@ -69,6 +82,7 @@ Use the `variant` prop to change the variant of the Kbd.
::component-code
---
props:
color: neutral
variant: solid
slots:
default: K

View File

@@ -889,7 +889,7 @@ You can inspect the DOM to see each item's content being rendered.
## Examples
### With tooltip in items :badge{label="New" class="align-text-top"}
### With tooltip in items
When orientation is `vertical` and the menu is `collapsed`, you can set the `tooltip` prop to `true` to display a [Tooltip](/components/tooltip) around items with their label but you can also use the `tooltip` property on each item to override the default tooltip.
@@ -994,7 +994,7 @@ props:
---
::
### With popover in items :badge{label="New" class="align-text-top"}
### With popover in items
When orientation is `vertical` and the menu is `collapsed`, you can set the `popover` prop to `true` to display a [Popover](/components/popover) around items with their children but you can also use the `popover` property on each item to override the default popover.

View File

@@ -180,6 +180,8 @@ props:
:component-emits
### Expose
When accessing the component via a template ref, you can use the following:
| Name | Type |

View File

@@ -202,7 +202,17 @@ name: 'popover-command-palette-example'
---
::
### With anchor slot :badge{label="New" class="align-text-top"}
### With following cursor :badge{label="Soon" class="align-text-top"}
You can make the Popover follow the cursor when hovering over an element using the [`reference`](https://reka-ui.com/docs/components/tooltip#trigger) prop:
::component-example
---
name: 'popover-cursor-example'
---
::
### With anchor slot
You can use the `#anchor` slot to position the Popover against a custom element.

View File

@@ -159,7 +159,7 @@ props:
---
::
### Variant :badge{label="New" class="align-text-top"}
### Variant
Use the `variant` prop to change the variant of the RadioGroup.
@@ -240,7 +240,7 @@ props:
---
::
### Indicator :badge{label="New" class="align-text-top"}
### Indicator
Use the `indicator` prop to change the position or hide the indicator. Defaults to `start`.

View File

@@ -790,6 +790,33 @@ name: 'select-menu-filter-fields-example'
---
::
### With full content width
You can expand the content to the full width of its items by using the `ui.content` key.
::component-example
---
name: 'select-menu-content-width-example'
collapse: true
---
::
::tip
You can also change the content width globally in your `app.config.ts`:
```
export default defineAppConfig({
ui: {
selectMenu: {
slots: {
content: 'min-w-fit'
}
}
}
})
```
::
### 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.
@@ -801,6 +828,8 @@ name: 'select-menu-countries-example'
---
::
## API
### Props
@@ -815,6 +844,14 @@ name: 'select-menu-countries-example'
:component-emits
### Expose
When accessing the component via a template ref, you can use the following:
| Name | Type |
| ---- | ---- |
| `triggerRef`{lang="ts-type"} | `Ref<InstanceType<typeof ComboboxTrigger> \| null>`{lang="ts-type"} |
## Theme
:component-theme

View File

@@ -695,6 +695,33 @@ collapse: true
---
::
### With full content width
You can expand the content to the full width of its items by using the `ui.content` key.
::component-example
---
name: 'select-content-width-example'
collapse: true
---
::
::tip
You can also change the content width globally in your `app.config.ts`:
```
export default defineAppConfig({
ui: {
select: {
slots: {
content: 'min-w-fit'
}
}
}
})
```
::
## API
### Props
@@ -709,6 +736,14 @@ collapse: true
:component-emits
### Expose
When accessing the component via a template ref, you can use the following:
| Name | Type |
| ---- | ---- |
| `triggerRef`{lang="ts-type"} | `Ref<InstanceType<typeof SelectTrigger> \| null>`{lang="ts-type"} |
## Theme
:component-theme

View File

@@ -136,7 +136,7 @@ props:
---
::
### Tooltip :badge{label="New" class="align-text-top"}
### Tooltip
Use the `tooltip` prop to display a [Tooltip](/components/tooltip) around the Slider thumbs with the current value. You can set it to `true` for default behavior or pass an object to customize it with any property from the [Tooltip](/components/tooltip#props) component.

View File

@@ -77,11 +77,15 @@ Use the `columns` prop as an array of [ColumnDef](https://tanstack.com/table/lat
- `accessorKey`: [The key of the row object to use when extracting the value for the column.]{class="text-muted"}
- `header`: [The header to display for the column. If a string is passed, it can be used as a default for the column ID. If a function is passed, it will be passed a props object for the header and should return the rendered header value (the exact type depends on the adapter being used).]{class="text-muted"}
- `footer`: [The footer to display for the column. Works exactly like header, but is displayed under the table.]{class="text-muted"}
- `cell`: [The cell to display each row for the column. If a function is passed, it will be passed a props object for the cell and should return the rendered cell value (the exact type depends on the adapter being used).]{class="text-muted"}
- `meta`: [Extra properties for the column.]{class="text-muted"}
- `class`:
- `td`: [The classes to apply to the `td` element.]{class="text-muted"}
- `th`: [The classes to apply to the `th` element.]{class="text-muted"}
- `style`:
- `td`: [The style to apply to the `td` element.]{class="text-muted"}
- `th`: [The style to apply to the `th` element.]{class="text-muted"}
In order to render components or other HTML elements, you will need to use the Vue [`h` function](https://vuejs.org/api/render-function.html#h) inside the `header` and `cell` props. This is different from other components that use slots but allows for more flexibility.
@@ -111,6 +115,8 @@ Use the `meta` prop as an object ([TableMeta](https://tanstack.com/table/latest/
- `class`:
- `tr`: [The classes to apply to the `tr` element.]{class="text-muted"}
- `style`:
- `tr`: [The style to apply to the `tr` element.]{class="text-muted"}
### Loading
@@ -161,7 +167,7 @@ props:
### Sticky
Use the `sticky` prop to make the header sticky.
Use the `sticky` prop to make the header or footer sticky.
::component-code
---
@@ -172,6 +178,10 @@ ignore:
- class
external:
- data
items:
sticky:
- true
- false
props:
sticky: true
data:
@@ -266,8 +276,8 @@ You can group rows based on a given column value and show/hide sub rows via some
#### Important parts:
* Add prop `grouping` to `UTable` component with an array of column ids you want to group by.
* Add prop `grouping-options` to `UTable`. It must include `getGroupedRowModel`, you can import it from `@tanstack/vue-table` or implement your own.
* Add `grouping` prop with an array of column ids you want to group by.
* Add `grouping-options` prop. It must include `getGroupedRowModel`, you can import it from `@tanstack/vue-table` or implement your own.
* Expand rows via `row.toggleExpanded()` method on any cell of the row. Keep in mind, it also toggles `#expanded` slot.
* Use `aggregateFn` on column definition to define how to aggregate the rows.
* `agregatedCell` renderer on column definition only works if there is no `cell` renderer.
@@ -304,19 +314,19 @@ class: '!p-0'
You can use the `row-selection` prop to control the selection state of the rows (can be binded with `v-model`).
::
### With `@select` event
### With row select event
You can add a `@select` listener to make rows clickable. The handler function receives the `TableRow` instance as the first argument and an optional `Event` as the second argument.
You can add a `@select` listener to make rows clickable with or without a checkbox column.
::note
You can use this to navigate to a page, open a modal or even to select the row manually.
The handler function receives the `TableRow` instance as the first argument and an optional `Event` as the second argument.
::
::component-example
---
prettier: true
collapse: true
name: 'table-row-selection-event-example'
name: 'table-row-select-event-example'
highlights:
- 123
- 130
@@ -324,6 +334,70 @@ class: '!p-0'
---
::
::tip
You can use this to navigate to a page, open a modal or even to select the row manually.
::
### With row context menu event :badge{label="Soon" class="align-text-top"}
You can add a `@contextmenu` listener to make rows right clickable and wrap the Table in a [ContextMenu](/components/context-menu) component to display row actions for example.
::note
The handler function receives the `Event` and `TableRow` instance as the first and second arguments respectively.
::
::component-example
---
prettier: true
collapse: true
name: 'table-row-context-menu-event-example'
highlights:
- 130
- 170
class: '!p-0'
---
::
### With row hover event :badge{label="Soon" class="align-text-top"}
You can add a `@hover` listener to make rows hoverable and use a [Popover](/components/popover) or a [Tooltip](/components/tooltip) component to display row details for example.
::note
The handler function receives the `Event` and `TableRow` instance as the first and second arguments respectively.
::
::component-example
---
prettier: true
collapse: true
name: 'table-row-hover-event-example'
highlights:
- 126
- 149
class: '!p-0'
---
::
::note
This example is similar as the Popover [with following cursor example](/components/popover#with-following-cursor) and uses a [`refDebounced`](https://vueuse.org/shared/refDebounced/#refdebounced) to prevent the Popover from opening and closing too quickly when moving the cursor from one row to another.
::
### With column footer :badge{label="Soon" class="align-text-top"}
You can add a `footer` property to the column definition to render a footer for the column.
::component-example
---
prettier: true
collapse: true
name: 'table-column-footer-example'
highlights:
- 94
- 108
class: '!p-0'
---
::
### With column sorting
You can update a column `header` to render a [Button](/components/button) component inside the `header` to toggle the sorting state using the TanStack Table [Sorting APIs](https://tanstack.com/table/latest/docs/api/features/sorting).

View File

@@ -19,12 +19,13 @@ Use the `items` prop as an array of objects with the following properties:
- `label?: string`{lang="ts-type"}
- `icon?: string`{lang="ts-type"}
- `avatar?: AvatarProps`{lang="ts-type"}
- `badge?: string | number | BadgeProps`{lang="ts-type"}
- `content?: string`{lang="ts-type"}
- `value?: string | number`{lang="ts-type"}
- `disabled?: boolean`{lang="ts-type"}
- [`slot?: string`{lang="ts-type"}](#with-custom-slot)
- `class?: any`{lang="ts-type"}
- `ui?: { trigger?: ClassNameValue, leadingIcon?: ClassNameValue, leadingAvatar?: ClassNameValue, label?: ClassNameValue, content?: ClassNameValue }`{lang="ts-type"}
- `ui?: { trigger?: ClassNameValue, leadingIcon?: ClassNameValue, leadingAvatar?: ClassNameValue, leadingAvatarSize?: ClassNameValue, label?: ClassNameValue, trailingBadge?: ClassNameValue, trailingBadgeSize?: ClassNameValue, content?: ClassNameValue }`{lang="ts-type"}
::component-code
---
@@ -242,6 +243,14 @@ You will have access to the following slots:
:component-emits
### Expose
When accessing the component via a template ref, you can use the following:
| Name | Type |
| ---- | ---- |
| `triggersRef`{lang="ts-type"} | `Ref<ComponentPublicInstance[]>`{lang="ts-type"} |
## Theme
:component-theme

View File

@@ -124,7 +124,7 @@ props:
---
::
### Icon :badge{label="New" class="align-text-top"}
### Icon
Use the `icon` prop to show an [Icon](/components/icon) inside the Textarea.
@@ -157,7 +157,7 @@ props:
---
::
### Avatar :badge{label="New" class="align-text-top"}
### Avatar
Use the `avatar` prop to show an [Avatar](/components/avatar) inside the Textarea.
@@ -176,7 +176,7 @@ props:
---
::
### Loading :badge{label="New" class="align-text-top"}
### Loading
Use the `loading` prop to show a loading icon on the Textarea.
@@ -192,7 +192,7 @@ props:
---
::
### Loading Icon :badge{label="New" class="align-text-top"}
### Loading Icon
Use the `loading-icon` prop to customize the loading icon. Defaults to `i-lucide-loader-circle`.

View File

@@ -6,7 +6,7 @@ links:
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/Timeline.vue
navigation.badge: Soon
navigation.badge: New
---
## Usage
@@ -23,7 +23,7 @@ Use the `items` prop as an array of objects with the following properties:
- `value?: string | number`{lang="ts-type"}
- [`slot?: string`{lang="ts-type"}](#with-custom-slot)
- `class?: any`{lang="ts-type"}
- `ui?: { item?: ClassNameValue, container?: ClassNameValue, indicator?: ClassNameValue, separator?: ClassNameValue, wrapper?: ClassNameValue, separator?: ClassNameValue, date?: ClassNameValue, title?: ClassNameValue, description?: ClassNameValue }`{lang="ts-type"}
- `ui?: { item?: ClassNameValue, container?: ClassNameValue, indicator?: ClassNameValue, separator?: ClassNameValue, wrapper?: ClassNameValue, date?: ClassNameValue, title?: ClassNameValue, description?: ClassNameValue }`{lang="ts-type"}
::component-code
---
@@ -169,6 +169,47 @@ props:
description: 'QA testing and performance optimization.'
icon: 'i-lucide-check-circle'
class: 'w-full'
class: 'overflow-x-auto'
---
::
### Reverse
Use the reverse prop to reverse the direction of the Timeline.
::component-code
---
ignore:
- items
- class
- defaultValue
external:
- items
externalTypes:
- TimelineItem[]
props:
reverse: true
modelValue: 2
orientation: 'vertical'
items:
- date: 'Mar 15, 2025'
title: 'Project Kickoff'
description: 'Kicked off the project with team alignment.'
icon: 'i-lucide-rocket'
- date: 'Mar 22 2025'
title: 'Design Phase'
description: 'User research and design workshops.'
icon: 'i-lucide-palette'
- date: 'Mar 29 2025'
title: 'Development Sprint'
description: 'Frontend and backend development.'
icon: 'i-lucide-code'
- date: 'Apr 5 2025'
title: 'Testing & Deployment'
description: 'QA testing and performance optimization.'
icon: 'i-lucide-check-circle'
class: 'w-full'
class: 'overflow-x-auto'
---
::

View File

@@ -107,7 +107,7 @@ name: 'toast-color-example'
### Close
Pass a `close` field to customize or hide the close button (with `false` value).
Pass a `close` field to customize or hide the close [Button](/components/button) (with `false` value).
::component-example
---
@@ -143,7 +143,7 @@ You can customize this icon globally in your `vite.config.ts` under `ui.icons.cl
### Actions
Pass an `actions` field to add some [Button](/components/button) actions to the Alert.
Pass an `actions` field to add some [Button](/components/button) actions to the Toast.
::component-example
---
@@ -155,9 +155,23 @@ name: 'toast-actions-example'
---
::
### Progress :badge{label="Soon" class="align-text-top"}
Pass a `progress` field to customize or hide the [Progress](/components/progress) bar (with `false` value).
::tip
The Progress bar inherits the Toast color by default, but you can override it using the `progress.color` field.
::
::component-example
---
name: 'toast-progress-example'
---
::
### Orientation
Use the `orientation` prop to change the orientation of the Toast.
Pass an `orientation` field to the `toast.add` method to change the orientation of the Toast.
::component-example
---

View File

@@ -186,6 +186,16 @@ name: 'tooltip-open-example'
In this example, leveraging [`defineShortcuts`](/composables/define-shortcuts), you can toggle the Tooltip by pressing :kbd{value="O"}.
::
### With following cursor :badge{label="Soon" class="align-text-top"}
You can make the Tooltip follow the cursor when hovering over an element using the [`reference`](https://reka-ui.com/docs/components/tooltip#trigger) prop:
::component-example
---
name: 'tooltip-cursor-example'
---
::
## API
### Props

View File

@@ -33,7 +33,7 @@ export default defineNuxtModule((_, nuxt) => {
}
const name = template.name.toLowerCase().replace(/\s/g, '-')
const filename = join(process.cwd(), 'docs/public/assets/showcase', `${name}.png`)
const filename = join(nuxt.options.rootDir, 'public/assets/showcase', `${name}.png`)
if (existsSync(filename)) {
continue

View File

@@ -143,10 +143,6 @@ export default defineNuxtConfig({
'/releases': { redirect: 'https://github.com/nuxt/ui/releases', prerender: false }
},
future: {
compatibilityVersion: 4
},
compatibilityDate: '2024-07-09',
nitro: {

View File

@@ -2,41 +2,49 @@
"private": true,
"name": "@nuxt/ui-docs",
"type": "module",
"scripts": {
"dev": "nuxt dev",
"build": "nuxt build",
"generate": "nuxt generate",
"typecheck": "nuxt typecheck"
},
"dependencies": {
"@ai-sdk/vue": "^1.2.12",
"@iconify-json/logos": "^1.2.4",
"@iconify-json/lucide": "^1.2.44",
"@iconify-json/simple-icons": "^1.2.35",
"@iconify-json/vscode-icons": "^1.2.21",
"@nuxt/content": "^3.5.1",
"@iconify-json/lucide": "^1.2.57",
"@iconify-json/simple-icons": "^1.2.44",
"@iconify-json/vscode-icons": "^1.2.23",
"@nuxt/content": "^3.6.3",
"@nuxt/image": "^1.10.0",
"@nuxt/ui": "latest",
"@nuxt/ui-pro": "https://pkg.pr.new/@nuxt/ui-pro@f06b49c",
"@nuxthub/core": "^0.8.27",
"@nuxt/ui": "workspace:*",
"@nuxt/ui-pro": "https://pkg.pr.new/@nuxt/ui-pro@17684e4",
"@nuxthub/core": "^0.9.0",
"@nuxtjs/plausible": "^1.2.0",
"@octokit/rest": "^22.0.0",
"@rollup/plugin-yaml": "^4.1.2",
"@vueuse/integrations": "^13.2.0",
"@vueuse/nuxt": "^13.2.0",
"ai": "^4.3.16",
"@vueuse/integrations": "^13.5.0",
"@vueuse/nuxt": "^13.5.0",
"ai": "^4.3.19",
"better-sqlite3": "^12.2.0",
"capture-website": "^4.2.0",
"joi": "^17.13.3",
"motion-v": "^1.1.1",
"nuxt": "^3.17.4",
"nuxt-component-meta": "^0.11.0",
"nuxt-llms": "^0.1.2",
"nuxt-og-image": "^5.1.4",
"prettier": "^3.5.3",
"maska": "^3.2.0",
"motion-v": "^1.5.0",
"nuxt": "^4.0.1",
"nuxt-component-meta": "^0.12.1",
"nuxt-llms": "^0.1.3",
"nuxt-og-image": "^5.1.9",
"prettier": "^3.6.2",
"shiki-transformer-color-highlight": "^1.0.0",
"sortablejs": "^1.15.6",
"superstruct": "^2.0.2",
"ufo": "^1.6.1",
"valibot": "^1.1.0",
"workers-ai-provider": "^0.5.2",
"workers-ai-provider": "^0.7.2",
"yup": "^1.6.1",
"zod": "^3.25.28"
"zod": "^4.0.5"
},
"devDependencies": {
"wrangler": "^4.16.1"
"wrangler": "^4.25.0"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

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