Compare commits

...

100 Commits

Author SHA1 Message Date
Romain Hamel
75a0ac84c9 fix(Form): extend Form<...> type with HTMLFormElement 2024-04-07 13:09:55 +02:00
Eugen Istoc
07a4d13c0f fix(Slideover): wait for transition to complete to reset state (#1624) 2024-04-05 19:31:29 +02:00
renovate[bot]
9e90d1768b chore(deps): update devdependency @nuxt/eslint-config to ^0.3.0 (#1623)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-05 17:51:40 +02:00
Neil Richter
91e5002050 feat(Accordion): add unmount prop to allow lazy mounting for heavy components (#1590) 2024-04-05 14:11:31 +02:00
renovate[bot]
eb68d0d453 chore(deps): update devdependency @nuxt/ui-pro to v1.1.0-28538540.a353e68 (#1622)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-05 14:08:08 +02:00
Neil Richter
2bdb5d2b42 fix(Modal): wait for transition to complete to reset state (#1618) 2024-04-05 14:07:51 +02:00
renovate[bot]
b62cd7905d chore(deps): update devdependency @nuxt/ui-pro to v1.1.0-28538504.d4106a4 (#1620)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-05 12:44:03 +02:00
Eugen Istoc
58faa1053b fix(Slideover): remove dynamic component when closing (#1615)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2024-04-05 12:43:50 +02:00
Kshitij Subedi
e909884d03 fix(Carousel): next and prev buttons disabled (#1619) 2024-04-05 12:20:50 +02:00
renovate[bot]
5e84fd0570 chore(deps): update devdependency @nuxtjs/plausible to v1 (#1610)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-05 12:18:39 +02:00
Benjamin Canac
98c0f567fc docs: replace i-heroicons-credit-card with i-heroicons-ticket 2024-04-05 11:57:47 +02:00
renovate[bot]
379d20fc3c chore(deps): update all non-major dependencies (#1602)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-05 11:28:22 +02:00
renovate[bot]
c12f94653e chore(deps): update nuxt framework to ^3.11.2 (#1613)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-05 11:06:04 +02:00
vahid bagheri
2392b4aa40 fix(Popover/Dropdown): prevent unintended closure on touchstart in mobile devices (#1609) 2024-04-04 00:18:40 +02:00
Benjamin Canac
36055ba978 chore(release): v2.15.1 2024-04-02 13:08:11 +02:00
renovate[bot]
73541f2d4f chore(deps): update all non-major dependencies (#1562)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-02 12:02:39 +02:00
Benjamin Canac
03030ab1db docs(nuxt.config): remove @nuxtjs/google-fonts and @nuxtjs/fontaine config 2024-03-29 10:57:31 +01:00
Cardona Simon
c98d6e31c0 fix(Checkbox): @change event value (#1580)
Co-authored-by: Romain Hamel <rom.hml@gmail.com>
2024-03-28 21:29:29 +01:00
Benjamin Canac
49b73aa024 feat(Avatar): add as prop to use NuxtImg underneath
Resolves #1577
2024-03-28 11:04:20 +01:00
Mahdi Shah Abbasian
bd8b737642 fix(Divider): add w-full only on horizontal wrapper (#1565) 2024-03-27 13:57:09 +01:00
Neil Richter
dd8a122933 docs(installation): update regex to match @nuxt/eslint rules (#1572) 2024-03-27 13:55:23 +01:00
Qin Guan
0b799e4300 docs(icon): add link to theming icons section (#1571)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2024-03-27 13:54:38 +01:00
Benjamin Canac
8517897c34 fix(Popover): missing mouseenter event on container
Resolves #1564
2024-03-27 12:04:49 +01:00
Benjamin Canac
72889535e7 fix(Dropdown): missing mouseenter event on container 2024-03-27 12:04:31 +01:00
Romain Hamel
878f7078a2 fix(Input/SelectMenu): handle file type and change events (#1570) 2024-03-27 11:57:31 +01:00
Benjamin Canac
bd8118c124 docs(deps): update @nuxt/ui-pro 2024-03-26 14:18:21 +01:00
Benjamin Canac
3d5ffe76ef chore(release): v2.15.0 2024-03-26 13:53:25 +01:00
chenying
c49f8999d3 fix(SelectMenu): handle Boolean type as model value (#1550)
Co-authored-by: chenying <chenying@addcn.com>
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2024-03-26 12:15:25 +01:00
Daniel Roe
cc62e345eb fix: opt in to import.meta.* properties (#1561)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2024-03-25 17:57:58 +01:00
renovate[bot]
ae58d5c2b9 chore(deps): update all non-major dependencies (#1547)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-25 16:01:48 +01:00
Romain Hamel
92e736213b fix(forms)!: normalize input emits (#1560) 2024-03-25 15:36:36 +01:00
Benjamin Canac
7d6b5c358f docs(Slideover): missing New badge 2024-03-25 12:45:26 +01:00
Benjamin Canac
f854746bd8 docs(alert): missing New badges 2024-03-25 12:45:18 +01:00
Mohammad Amin Mokhtari
e1e05af0ba feat(Toggle): add loading prop (#1546)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2024-03-25 12:43:15 +01:00
Neil Richter
224ec3c1fb feat(Accordion): emit open event with index (#1559) 2024-03-25 12:27:06 +01:00
chenying
c3ac4badbf docs(DatePicker): add is-required prop (#1549)
Co-authored-by: chenying <chenying@addcn.com>
2024-03-25 12:09:19 +01:00
renovate[bot]
398c5d5dcd chore(deps): update all non-major dependencies (#1543)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-21 18:16:48 +01:00
Eugen Istoc
e7697595c8 feat(Slideover): open programmatically (#1465)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2024-03-21 17:48:35 +01:00
Stanley
b0ecac563c fix(SelectMenu): filteredOptions might be undefined (#1541)
Co-authored-by: Sedana Yoga <55230513+sedanayoga@users.noreply.github.com>
2024-03-20 10:55:13 +01:00
renovate[bot]
5cb45c52c2 chore(deps): update all non-major dependencies (#1537)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-19 18:05:32 +01:00
renovate[bot]
0453af65fa chore(deps): update devdependency ufo to ^1.5.2 (#1533)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-19 11:12:19 +01:00
renovate[bot]
53cfea40a4 chore(deps): update devdependency happy-dom to v14 (#1536)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-19 10:35:59 +01:00
renovate[bot]
386e51d159 chore(deps): update nuxt framework to ^3.11.1 (#1535)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-19 10:28:55 +01:00
renovate[bot]
eb8eec09c5 chore(deps): update nuxt framework (#1528)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-18 11:45:57 +01:00
renovate[bot]
4a4ddbd5cb chore(deps): update all non-major dependencies (#1504)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-18 11:24:21 +01:00
Daniel Öhling
a563d8fed4 fix(Popover/Dropdown): use @touchstart.passive instead of @touchstart.prevent (#1520) 2024-03-17 18:48:00 +01:00
Conner Blanton
7658211537 fix(ButtonGroup): nested group elements (#1530) 2024-03-17 18:47:10 +01:00
Noah Gregory
e736ecafff fix(Carousel): add tab-based ARIA roles (#1516) 2024-03-14 10:32:39 +01:00
Alex Thorwaldson
cee3e126a4 feat(Alert): add icon & avatar slots (#1401)
Co-authored-by: gangan <44604921+shinGangan@users.noreply.github.com>
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-13 21:49:57 +01:00
Benjamin Canac
f4a48f6016 fix(InputMenu): trigger alignement on safari
Resolves #1505
2024-03-12 22:39:14 +01:00
Benjamin Canac
877b22c294 docs: consistent icons and avatars across examples 2024-03-12 22:20:44 +01:00
renovate[bot]
8cc8e45b4f chore(deps): update all non-major dependencies (#1490)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-10 11:51:11 +01:00
Daniel Roe
3f67b9209c chore: check playground types separately (#1497) 2024-03-09 19:25:09 +01:00
Romain Hamel
a2b8b700df fix(Checkbox): bind data-n-ids to root element (#1495) 2024-03-09 15:58:54 +01:00
Daniel Roe
80cc59375f chore: remove auto-import override (#1492) 2024-03-09 14:36:05 +01:00
renovate[bot]
2bb911023c chore(deps): update all non-major dependencies (#1483)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-08 12:10:58 +01:00
renovate[bot]
ab355a3576 chore(deps): update all non-major dependencies (#1481)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-07 15:07:59 +01:00
Shoshana Connack
6c02d1c704 docs(getting-started): grammar correction (#1482) 2024-03-07 11:51:46 +01:00
renovate[bot]
66c3631b3d chore(deps): update all non-major dependencies (#1474)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2024-03-07 00:18:08 +01:00
Benjamin Canac
435ef30f26 chore(release-it): prefix version commit message 2024-03-05 12:48:45 +01:00
Benjamin Canac
17a96416f0 chore(release): 2.14.2 2024-03-05 12:21:34 +01:00
renovate[bot]
4378268117 chore(deps): update devdependency @nuxt/ui-pro to v1.0.1-28492961.4d49b9c (#1471)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-04 21:38:46 +01:00
Benjamin Canac
622aef5ffe docs: limit search results to 42 2024-03-04 19:27:09 +01:00
Benjamin Canac
174af36000 docs: switch to @nuxt/fonts 2024-03-04 18:21:41 +01:00
Benjamin Canac
2d64b50559 fix(RadioGroup): add missing fieldset config
Resolves #1472
2024-03-04 18:06:49 +01:00
Benjamin Canac
272c19de70 fix(Tooltip): arrow not hidden on mobile
Resolves #1470
2024-03-04 16:27:01 +01:00
Alex Thorwaldson
6a1142b403 fix(Dropdown): active/inactive dropdown links (#1407)
Co-authored-by: gangan <44604921+shinGangan@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2024-03-04 16:11:41 +01:00
renovate[bot]
9937951fb7 chore(deps): update all non-major dependencies (#1440)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-04 16:11:05 +01:00
kmilogp
002129c299 fix(Modal): remove overflow-hidden (#1460)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2024-03-04 12:58:56 +01:00
Benjamin Canac
0c2f655a27 docs(Range): revert example as it shows in the docs 2024-03-04 12:47:51 +01:00
renovate[bot]
fb16735dec chore(deps): update devdependency vue-tsc to v2 (#1454)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-04 12:33:38 +01:00
Mukund Shah
aa6e523780 docs(Notification): add Notifications props and config (#1449) 2024-03-04 11:56:18 +01:00
David Parys
bfd15c1818 docs(Link): add IntelliSense section (#1442) 2024-03-04 11:55:17 +01:00
Rodion
027d85402b docs(FormGroup): wrong icon name in #help slot (#1451) 2024-03-04 11:54:44 +01:00
roiLeo
99b9467dc2 docs(SelectMenu): invalid anchor to creatable (#1453) 2024-03-04 11:53:52 +01:00
Benjamin Canac
70bf4a7392 fix(Dropdown): improve hover mode on touch devices 2024-03-04 11:47:39 +01:00
Benjamin Canac
b50fbcf760 fix(Popover): improve hover mode on touch devices 2024-03-04 11:47:34 +01:00
Benjamin Canac
b74bf9f666 chore(Tooltip): use mouseenter instead of mouseover 2024-03-04 11:45:01 +01:00
renovate[bot]
c0feca136a chore(deps): update all non-major dependencies (#1430)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-28 17:21:00 +01:00
Benjamin Canac
0a4a9e3d2c fix(HorizontalNavigation): add relative class to icon 2024-02-28 16:26:16 +01:00
Benjamin Canac
0b29dd4ca5 fix(VerticalNavigation): add relative class to icon
Resolves #1245
2024-02-28 16:25:20 +01:00
Benjamin Canac
9cce4456d0 fix(Notification): improve description alignment when no title provided 2024-02-28 12:52:53 +01:00
Benjamin Canac
ca4f06a313 fix(Alert): improve description alignment when no title provided
Resolves #1408
2024-02-28 12:52:43 +01:00
Benjamin Canac
7dd9ee528e docs(date-picker): improve examples responsive 2024-02-28 11:25:59 +01:00
Shoshana Connack
cdf6ff7152 docs(date-picker): include date-fns in installation (#1434) 2024-02-27 14:26:25 +01:00
Benjamin Canac
9c2104d947 docs: remove banner on examples 2024-02-27 09:18:53 +01:00
Benjamin Canac
d1c8026a1e docs(deps): bump @nuxt/ui-pro 2024-02-27 09:18:53 +01:00
Benjamin Canac
14efa81986 chore(deps): refresh lock 2024-02-27 09:18:53 +01:00
renovate[bot]
b3314dc16b chore(deps): update all non-major dependencies (#1406)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-27 09:18:53 +01:00
Benjamin Canac
06135f38ae docs(home): improve lighthouse 2024-02-26 12:56:40 +01:00
Benjamin Canac
dbbab8ded0 chore(CommandPaletteGroup): remove useless role attributes 2024-02-26 12:56:40 +01:00
Romain Hamel
6e77f1d514 fix(Checkbox): label interaction without FormGroup (#1427) 2024-02-26 12:55:30 +01:00
Benjamin Canac
4b6e80e364 fix(SelectMenu): check null model value
Resolves #1421
2024-02-26 11:37:32 +01:00
Benjamin Canac
8a1b112727 docs(deps): bump @nuxt/ui-pro 2024-02-26 11:37:32 +01:00
Benjamin Canac
961f0ae27b docs: update banner 2024-02-26 11:37:32 +01:00
Benjamin Canac
e819656a34 docs: move headlessui links before github 2024-02-26 11:37:32 +01:00
Sébastien Chopin
024e03acc9 docs(breadcrumb): add link to github (#1414) 2024-02-26 11:37:32 +01:00
renovate[bot]
462d7729c9 chore(deps): update devdependency @nuxt/ui-pro to v1.0.0-28478433.ed477f1 (#1405)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-26 11:37:31 +01:00
gangan
df857fd541 chore(renovate): update extends (#1380) 2024-02-26 11:37:31 +01:00
sdezza
9b208bf297 docs(modal): mention how to programatically close (#1423) 2024-02-26 11:24:02 +01:00
95 changed files with 3893 additions and 1802 deletions

1
.gitignore vendored
View File

@@ -9,3 +9,4 @@ dist
.vercel
.idea
.env
.data

View File

@@ -1,2 +1 @@
imports.autoImport=false
typescript.includeWorkspace=true

View File

@@ -1,6 +1,6 @@
{
"git": {
"commitMessage": "chore(release): ${version}",
"commitMessage": "chore(release): v${version}",
"tagName": "v${version}"
},
"npm": {

View File

@@ -1,5 +1,66 @@
# Changelog
## [2.15.1](https://github.com/nuxt/ui/compare/v2.15.0...v2.15.1) (2024-04-02)
### Features
* **Avatar:** add `as` prop to use `NuxtImg` underneath ([49b73aa](https://github.com/nuxt/ui/commit/49b73aa024be14a9aa150a2804f4dcb18542fa49)), closes [#1577](https://github.com/nuxt/ui/issues/1577)
### Bug Fixes
* **Checkbox:** `[@change](https://github.com/change)` event value ([#1580](https://github.com/nuxt/ui/issues/1580)) ([c98d6e3](https://github.com/nuxt/ui/commit/c98d6e31c0e3f46b97957d5cf3de7f9da1f70c58))
* **Divider:** add `w-full` only on horizontal wrapper ([#1565](https://github.com/nuxt/ui/issues/1565)) ([bd8b737](https://github.com/nuxt/ui/commit/bd8b737642280e6a83b67f9a27dd7a823a77e963))
* **Dropdown:** missing `mouseenter` event on container ([7288953](https://github.com/nuxt/ui/commit/72889535e7e9763e7ebf59498f22c39bf09d6477))
* **Input/SelectMenu:** handle `file` type and `change` events ([#1570](https://github.com/nuxt/ui/issues/1570)) ([878f707](https://github.com/nuxt/ui/commit/878f7078a28c5e70a662682d1293db466d518c7d))
* **Popover:** missing `mouseenter` event on container ([8517897](https://github.com/nuxt/ui/commit/8517897c34adaa9e3624f867b43106deb59fcbe8)), closes [#1564](https://github.com/nuxt/ui/issues/1564)
## [2.15.0](https://github.com/nuxt/ui/compare/v2.14.2...v2.15.0) (2024-03-26)
### ⚠ BREAKING CHANGES
* **forms:** normalize input emits (#1560)
### Features
* **Accordion:** emit `open` event with index ([#1559](https://github.com/nuxt/ui/issues/1559)) ([224ec3c](https://github.com/nuxt/ui/commit/224ec3c1fbfb9875398d3af60e5fe49e47ce55b1))
* **Alert:** add `icon` & `avatar` slots ([#1401](https://github.com/nuxt/ui/issues/1401)) ([cee3e12](https://github.com/nuxt/ui/commit/cee3e126a472735c0e484de315868bb28287164f))
* **Slideover:** open programmatically ([#1465](https://github.com/nuxt/ui/issues/1465)) ([e769759](https://github.com/nuxt/ui/commit/e7697595c8769ceea61690f6c2f294206de50972))
* **Toggle:** add `loading` prop ([#1546](https://github.com/nuxt/ui/issues/1546)) ([e1e05af](https://github.com/nuxt/ui/commit/e1e05af0bafd1e5d1b91f374562ed8d389fb0cae))
### Bug Fixes
* **ButtonGroup:** nested group elements ([#1530](https://github.com/nuxt/ui/issues/1530)) ([7658211](https://github.com/nuxt/ui/commit/765821153753d1a49276421511224336aebcdd2f))
* **Carousel:** add tab-based ARIA roles ([#1516](https://github.com/nuxt/ui/issues/1516)) ([e736eca](https://github.com/nuxt/ui/commit/e736ecafff59f9d4eb88b366ef1e9d26449b8ca3))
* **Checkbox:** bind `data-n-ids` to root element ([#1495](https://github.com/nuxt/ui/issues/1495)) ([a2b8b70](https://github.com/nuxt/ui/commit/a2b8b700df6ad0907a5d4d622d178d1345b55b83))
* **forms:** normalize input emits ([#1560](https://github.com/nuxt/ui/issues/1560)) ([92e7362](https://github.com/nuxt/ui/commit/92e736213b221d5ec8cfb8881fda4fc65ce7dfa0))
* **InputMenu:** trigger alignement on safari ([f4a48f6](https://github.com/nuxt/ui/commit/f4a48f6016ede664e4f46741e7811b0dbe0acfbe)), closes [#1505](https://github.com/nuxt/ui/issues/1505)
* opt in to `import.meta.*` properties ([#1561](https://github.com/nuxt/ui/issues/1561)) ([cc62e34](https://github.com/nuxt/ui/commit/cc62e345eb96a632730bed796c77afe7ecdadf2a))
* **Popover/Dropdown:** use `[@touchstart](https://github.com/touchstart).passive` instead of `[@touchstart](https://github.com/touchstart).prevent` ([#1520](https://github.com/nuxt/ui/issues/1520)) ([a563d8f](https://github.com/nuxt/ui/commit/a563d8fed44535107080fee094995d87ca5dc2b6))
* **SelectMenu:** `filteredOptions` might be undefined ([#1541](https://github.com/nuxt/ui/issues/1541)) ([b0ecac5](https://github.com/nuxt/ui/commit/b0ecac563c5702fe40cf42a8861b1d2d1366d423))
* **SelectMenu:** handle `Boolean` type as model value ([#1550](https://github.com/nuxt/ui/issues/1550)) ([c49f899](https://github.com/nuxt/ui/commit/c49f8999d319ec487672ebd68e8b3f0031843cd6))
## [2.14.2](https://github.com/nuxt/ui/compare/v2.14.1...v2.14.2) (2024-03-05)
### Bug Fixes
* **Alert:** improve `description` alignment when no title provided ([ca4f06a](https://github.com/nuxt/ui/commit/ca4f06a313314af5813007878a9b6c8f1003c6d1)), closes [#1408](https://github.com/nuxt/ui/issues/1408)
* **Checkbox:** label interaction without `FormGroup` ([#1427](https://github.com/nuxt/ui/issues/1427)) ([6e77f1d](https://github.com/nuxt/ui/commit/6e77f1d5144d7d87b0c76b43ecf3d731166c808b))
* **Dropdown:** active/inactive dropdown links ([#1407](https://github.com/nuxt/ui/issues/1407)) ([6a1142b](https://github.com/nuxt/ui/commit/6a1142b4032150def78c69080df455f7d2a25e7b))
* **Dropdown:** improve `hover` mode on touch devices ([70bf4a7](https://github.com/nuxt/ui/commit/70bf4a73921f47fcd41599874b595a6eed947f5a))
* **HorizontalNavigation:** add `relative` class to icon ([0a4a9e3](https://github.com/nuxt/ui/commit/0a4a9e3d2c4a7584570e4ab7ae6fe8265c960a33))
* **Modal:** remove `overflow-hidden` ([#1460](https://github.com/nuxt/ui/issues/1460)) ([002129c](https://github.com/nuxt/ui/commit/002129c29926df5a816288b916194ab28cf4c8a4))
* **Notification:** improve `description` alignment when no title provided ([9cce445](https://github.com/nuxt/ui/commit/9cce4456d03c52daca4d7347e60cbcd7f501317a))
* **Popover:** improve `hover` mode on touch devices ([b50fbcf](https://github.com/nuxt/ui/commit/b50fbcf760e908579e81f6e57234f2080e2bf035))
* **RadioGroup:** add missing `fieldset` config ([2d64b50](https://github.com/nuxt/ui/commit/2d64b50559946b166c02cfe921e63d746cdc09d4)), closes [#1472](https://github.com/nuxt/ui/issues/1472)
* **SelectMenu:** check `null` model value ([4b6e80e](https://github.com/nuxt/ui/commit/4b6e80e3646e263a83614830d9ec6adb0edf2ede)), closes [#1421](https://github.com/nuxt/ui/issues/1421)
* **Tooltip:** arrow not hidden on mobile ([272c19d](https://github.com/nuxt/ui/commit/272c19de708144b1b132b98a7287254974f4e144)), closes [#1470](https://github.com/nuxt/ui/issues/1470)
* **VerticalNavigation:** add `relative` class to icon ([0b29dd4](https://github.com/nuxt/ui/commit/0b29dd4ca560cac7d151132e086eab17c0498a5c)), closes [#1245](https://github.com/nuxt/ui/issues/1245)
## [2.14.1](https://github.com/nuxt/ui/compare/v2.14.0...v2.14.1) (2024-02-23)

View File

@@ -1 +0,0 @@
imports.autoImport=true

View File

@@ -3,7 +3,7 @@
<div>
<NuxtLoadingIndicator />
<Banner />
<Banner v-if="!$route.path.startsWith('/examples')" />
<Header v-if="!$route.path.startsWith('/examples')" :links="links" />
@@ -14,7 +14,7 @@
<Footer v-if="!$route.path.startsWith('/examples')" />
<ClientOnly>
<LazyUContentSearch ref="searchRef" :files="files" :navigation="navigation" :links="links" :fuse="{ resultLimit: 1000 }" />
<LazyUContentSearch ref="searchRef" :files="files" :navigation="navigation" :links="links" :fuse="{ resultLimit: 42 }" />
</ClientOnly>
<UNotifications>
@@ -23,6 +23,7 @@
</template>
</UNotifications>
<UModals />
<USlideovers />
</div>
</template>
@@ -71,7 +72,7 @@ const links = computed(() => {
active: route.path.startsWith('/pro/getting-started') || route.path.startsWith('/pro/components') || route.path.startsWith('/pro/prose')
}, {
label: 'Pricing',
icon: 'i-heroicons-credit-card',
icon: 'i-heroicons-ticket',
to: '/pro/pricing'
}, {
label: 'Templates',

View File

@@ -1,11 +1,6 @@
<script setup lang="ts">
import { isAfter } from 'date-fns'
const id = 'nuxt-ui-banner-1'
const to = '/pro/pricing'
const date = new Date('2024-02-25T20:00:00Z')
const timeAgo = useTimeAgo(date)
const hideBanner = () => {
localStorage.setItem(id, 'true')
@@ -25,26 +20,12 @@ if (process.server) {
}]
})
}
onMounted(() => {
if (isAfter(new Date(), date)) {
hideBanner()
return
}
const interval = setInterval(() => {
if (isAfter(new Date(), date)) {
hideBanner()
clearInterval(interval)
}
}, 1000)
})
</script>
<template>
<div class="relative bg-primary hover:bg-primary/90 transition-[background] backdrop-blur z-50 app-banner">
<UContainer class="py-2">
<NuxtLink v-if="to" :to="to" class="focus:outline-none" tabindex="-1">
<NuxtLink v-if="to" :to="to" class="focus:outline-none" aria-label="Nuxt UI Pro pricing" tabindex="-1">
<span class="absolute inset-0 " aria-hidden="true" />
</NuxtLink>
@@ -52,13 +33,14 @@ onMounted(() => {
<div class="lg:flex-1 hidden lg:flex items-center" />
<p class="text-sm font-medium text-white dark:text-gray-900">
<UIcon name="i-heroicons-gift" class="w-5 h-5 align-top flex-shrink-0 pointer-events-none mr-2" />
<span class="font-semibold">Nuxt UI Pro v1.0</span> is out with dashboard components! Discount ends <span class="font-semibold">{{ timeAgo }}</span>.
<UIcon name="i-heroicons-rocket-launch" class="w-5 h-5 align-top flex-shrink-0 pointer-events-none mr-2" />
<span class="font-semibold">Nuxt UI Pro v1.0</span> is out with dashboard components!
</p>
<div class="flex items-center justify-end lg:flex-1">
<button
class="p-1.5 rounded-md inline-flex hover:bg-primary/90"
aria-label="Close banner"
@click.prevent="hideBanner"
>
<UIcon name="i-heroicons-x-mark-20-solid" class="w-5 h-5 text-white dark:text-gray-900" />

View File

@@ -0,0 +1,20 @@
<template>
<UAlert
title="Customize Alert Avatar"
description="Insert custom content into the avatar slot!"
:avatar="{
src: 'https://avatars.githubusercontent.com/u/739984?v=4',
alt: 'Avatar'
}"
>
<template #avatar="{ avatar }">
<UAvatar
v-bind="avatar"
chip-color="primary"
chip-text=""
chip-position="top-right"
/>
</template>
</UAlert>
</template>

View File

@@ -0,0 +1,10 @@
<template>
<UAlert title="Customize Alert Icon" description="Insert custom content into the icon slot!" icon="i-heroicons-command-line">
<template #icon="{ icon }">
<UBadge size="sm">
<UIcon :name="icon" />
</UBadge>
</template>
</UAlert>
</template>

View File

@@ -9,7 +9,7 @@ const date = ref(new Date())
<UButton icon="i-heroicons-calendar-days-20-solid" :label="format(date, 'd MMM, yyy')" />
<template #panel="{ close }">
<DatePicker v-model="date" @close="close" />
<DatePicker v-model="date" is-required @close="close" />
</template>
</UPopover>
</template>

View File

@@ -27,8 +27,8 @@ function selectRange (duration: Duration) {
</UButton>
<template #panel="{ close }">
<div class="flex items-center divide-x divide-gray-200 dark:divide-gray-800">
<div class="flex flex-col py-4">
<div class="flex items-center sm:divide-x divide-gray-200 dark:divide-gray-800">
<div class="hidden sm:flex flex-col py-4">
<UButton
v-for="(range, index) in ranges"
:key="index"
@@ -37,6 +37,7 @@ function selectRange (duration: Duration) {
variant="ghost"
class="rounded-none px-6"
:class="[isRangeSelected(range.duration) ? 'bg-gray-100 dark:bg-gray-800' : 'hover:bg-gray-50 dark:hover:bg-gray-800/50']"
truncate
@click="selectRange(range.duration)"
/>
</div>

View File

@@ -31,8 +31,8 @@ const selected = ref(people[0])
<template>
<UInputMenu v-model="selected" :options="people">
<template #leading>
<UIcon v-if="selected.icon" :name="(selected.icon as string)" class="w-4 h-4 mx-0.5" />
<UAvatar v-else-if="selected.avatar" v-bind="(selected.avatar as Avatar)" size="3xs" class="mx-0.5" />
<UIcon v-if="selected.icon" :name="(selected.icon as string)" class="w-5 h-5" />
<UAvatar v-else-if="selected.avatar" v-bind="(selected.avatar as Avatar)" size="2xs" />
</template>
</UInputMenu>
</template>

View File

@@ -3,5 +3,5 @@ const value = ref(50)
</script>
<template>
<URange v-model="value" name="range" />
<URange v-model="value" />
</template>

View File

@@ -31,8 +31,8 @@ const selected = ref(people[0])
<template>
<USelectMenu v-model="selected" :options="people">
<template #leading>
<UIcon v-if="selected.icon" :name="(selected.icon as string)" class="w-4 h-4 mx-0.5" />
<UAvatar v-else-if="selected.avatar" v-bind="(selected.avatar as Avatar)" size="3xs" class="mx-0.5" />
<UIcon v-if="selected.icon" :name="(selected.icon as string)" class="w-5 h-5" />
<UAvatar v-else-if="selected.avatar" v-bind="(selected.avatar as Avatar)" size="2xs" />
</template>
</USelectMenu>
</template>

View File

@@ -0,0 +1,30 @@
<script lang="ts" setup>
const props = defineProps({
count: {
type: Number,
default: 0
}
})
const emits = defineEmits<{
close: [];
}>()
</script>
<template>
<USlideover>
<UCard class="flex flex-col flex-1" :ui="{ body: { base: 'flex-1' }, ring: '', divide: 'divide-y divide-gray-100 dark:divide-gray-800' }">
<template #header>
<div class="flex items-center justify-between">
<h3 class="text-base font-semibold leading-6 text-gray-900 dark:text-white">
Opened programmatically: {{ props.count }} times
</h3>
<UButton color="gray" variant="ghost" icon="i-heroicons-x-mark-20-solid" class="-my-1" @click="emits('close')" />
</div>
</template>
<Placeholder class="h-full" />
</UCard>
</USlideover>
</template>

View File

@@ -0,0 +1,16 @@
<script setup lang="ts">
import { SlideoverExampleComponent } from '#components'
const slideover = useSlideover()
const count = ref(0)
function openSlideover () {
count.value += 1
slideover.open(SlideoverExampleComponent, {
count: count.value,
onClose: slideover.close
})
}
</script>
<template>
<UButton label="Reveal slideover" @click="openSlideover" />
</template>

View File

@@ -32,13 +32,7 @@ const links = [{
<template>
<UVerticalNavigation :links="links">
<template #avatar="{ link }">
<UAvatar
v-if="link.avatar"
v-bind="link.avatar"
size="3xs"
loading="lazy"
/>
<UIcon v-else name="i-heroicons-user-circle-20-solid" class="text-lg" />
<UAvatar v-bind="link.avatar" size="2xs" loading="lazy" />
</template>
</UVerticalNavigation>
</template>

View File

@@ -1,4 +1,5 @@
<script setup lang="ts">
import { useBreakpoints, breakpointsTailwind } from '@vueuse/core'
import { DatePicker as VCalendarDatePicker } from 'v-calendar'
// @ts-ignore
import type { DatePickerDate, DatePickerRangeObject } from 'v-calendar/dist/types/src/use/datePicker'
@@ -25,6 +26,10 @@ const date = computed({
}
})
const breakpoints = useBreakpoints(breakpointsTailwind)
const smallerThanSm = breakpoints.smaller('sm')
const attrs = {
transparent: true,
borderless: true,
@@ -35,7 +40,7 @@ const attrs = {
</script>
<template>
<VCalendarDatePicker v-if="date && (date as DatePickerRangeObject)?.start && (date as DatePickerRangeObject)?.end" v-model.range="date" :columns="2" v-bind="{ ...attrs, ...$attrs }" />
<VCalendarDatePicker v-if="date && (date as DatePickerRangeObject)?.start && (date as DatePickerRangeObject)?.end" v-model.range="date" :columns="smallerThanSm ? 1 : 2" :rows="smallerThanSm ? 2 : 1" v-bind="{ ...attrs, ...$attrs }" />
<VCalendarDatePicker v-else v-model="date" v-bind="{ ...attrs, ...$attrs }" />
</template>

View File

@@ -12,7 +12,7 @@ export async function fetchComponentMeta (name: string) {
// Store promise to avoid multiple calls
// add to nitro prerender
if (process.server) {
if (import.meta.server) {
const event = useRequestEvent()
event.node.res.setHeader(
'x-nitro-prerender',

View File

@@ -11,7 +11,7 @@ export async function fetchContentExampleCode (name?: string) {
if (state.value[name]) { return state.value[name] }
// add to nitro prerender
if (process.server) {
if (import.meta.server) {
const event = useRequestEvent()
event.node.res.setHeader(
'x-nitro-prerender',

View File

@@ -5,7 +5,7 @@ description: 'Fully styled and customizable components for Nuxt.'
Nuxt UI is a module that provides a set of Vue components and composables built with [Tailwind CSS](https://tailwindcss.com/) and [Headless UI](https://headlessui.dev/) to help you build beautiful and accessible user interfaces.
Its goal is to provide everything related to UI when building a Nuxt app. This includes components, icons, colors, dark mode but also keyboard shortcuts.
Its goal is to provide everything related to UI when building a Nuxt app. This includes components, icons, colors, dark mode as well as keyboard shortcuts.
## Features

View File

@@ -194,7 +194,7 @@ To enable these two features, you can add the following to your `.vscode/setting
{
"tailwindCSS.experimental.classRegex": [
["ui:\\s*{([^)]*)\\s*}", "[\"'`]([^\"'`]*).*?[\"'`]"],
["/\\*ui\\*/\\s*{([^;]*)}", ":\\s*[\"'`]([^\"'`]*).*?[\"'`]"]
["/\\*\\s?ui\\s?\\*/\\s*{([^;]*)}", ":\\s*[\"'`]([^\"'`]*).*?[\"'`]"]
]
}
```
@@ -207,7 +207,7 @@ An example SFC using IntelliSense (note the `/*ui*/` prefix, also works with `re
</template>
<script setup lang="ts">
const ui = /*ui*/ {
const ui = /* ui */ {
background: 'bg-white dark:bg-slate-900'
}
</script>

View File

@@ -1,12 +1,12 @@
---
description: Display togglable accordion panels.
links:
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/elements/Accordion.vue
- label: Disclosure
icon: i-simple-icons-headlessui
to: 'https://headlessui.com/vue/disclosure'
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/elements/Accordion.vue
---
## Usage

View File

@@ -163,6 +163,18 @@ This can be handy when you want to display HTML content. To achieve this, you ca
:component-example{component="alert-example-html"}
### `icon` :u-badge{label="New" class="align-middle ml-2 !rounded-full" variant="subtle"}
Use the `#icon` slot to customize the displayed icon.
:component-example{component="alert-example-icon"}
### `avatar` :u-badge{label="New" class="align-middle ml-2 !rounded-full" variant="subtle"}
Use the `#avatar` slot to customize the displayable avatar.
:component-example{component="alert-example-avatar"}
## Props
:component-props

View File

@@ -1,6 +1,10 @@
---
title: Breadcrumb
description: A list of links that indicate the current page's location within a navigational hierarchy.
links:
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/navigation/Breadcrumb.vue
---
## Usage

View File

@@ -336,7 +336,7 @@ Use the `#leading` slot to set the content of the leading icon.
::component-card
---
slots:
leading: <UAvatar src="https://avatars.githubusercontent.com/u/739984?v=4" size="3xs" />
leading: <UAvatar src="https://avatars.githubusercontent.com/u/739984?v=4" size="2xs" />
baseProps:
color: 'gray'
props:
@@ -347,7 +347,7 @@ excludedProps:
---
#leading
:u-avatar{src="https://avatars.githubusercontent.com/u/739984?v=4" size="3xs"}
:u-avatar{src="https://avatars.githubusercontent.com/u/739984?v=4" size="2xs"}
::
### `trailing`
@@ -357,7 +357,7 @@ Use the `#trailing` slot to set the content of the trailing icon.
::component-card
---
slots:
trailing: <UIcon name="i-heroicons-arrow-right-20-solid" />
trailing: <UIcon name="i-heroicons-arrow-right-20-solid" class="w-5 h-5" />
props:
label: Button
color: 'gray'
@@ -366,7 +366,7 @@ excludedProps:
---
#trailing
:u-icon{name="i-heroicons-arrow-right-20-solid"}
:u-icon{name="i-heroicons-arrow-right-20-solid" class="w-5 h-5"}
::
## Props

View File

@@ -2,12 +2,12 @@
title: CommandPalette
description: Add a customizable command palette to your app.
links:
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/navigation/CommandPalette.vue
- label: 'Combobox'
icon: i-simple-icons-headlessui
to: 'https://headlessui.com/vue/combobox'
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/navigation/CommandPalette.vue
---
## Usage

View File

@@ -16,15 +16,15 @@ Let's start by installing the `v-calendar` and `date-fns` dependency:
::code-group
```bash [pnpm]
pnpm add v-calendar@next
pnpm add v-calendar@next date-fns
```
```bash [yarn]
yarn add v-calendar@next
yarn add v-calendar@next date-fns
```
```bash [npm]
npm install v-calendar@next
npm install v-calendar@next date-fns
```
::

View File

@@ -1,12 +1,12 @@
---
description: Display a list of actions in a dropdown menu.
links:
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/elements/Dropdown.vue
- label: Menu
icon: i-simple-icons-headlessui
to: https://headlessui.com/vue/menu
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/elements/Dropdown.vue
---
## Usage

View File

@@ -236,7 +236,7 @@ Use the `#help` slot to set the custom content for help.
::component-card
---
slots:
help: Here are some examples <UIcon name="i-heroicons-arrow-right-20-solid" />
help: Here are some examples <UIcon name="i-heroicons-information-circle" />
default: <UInput model-value="benjamincanac" placeholder="you@example.com" />
props:
label: 'Email'

View File

@@ -18,7 +18,11 @@ props:
::
::callout{icon="i-heroicons-exclamation-triangle"}
You won't be able to use any icon in the `name` prop here as icons are bundled using [egoist/tailwindcss-icons](https://github.com/egoist/tailwindcss-icons), read more about this in [Theming](/getting-started/theming#icons).
You won't be able to use all icons in the `name` prop here as icons are bundled using [egoist/tailwindcss-icons](https://github.com/egoist/tailwindcss-icons).
::
::callout{icon="i-heroicons-light-bulb"}
Don't forget to install and specify the icon collections you need in your `nuxt.config.ts`, read more about this in [Theming](/getting-started/theming#icons).
::
### Dynamic

View File

@@ -2,12 +2,12 @@
title: InputMenu
description: Display an autocomplete input with real-time suggestions.
links:
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/forms/InputMenu.vue
- label: 'Combobox'
icon: i-simple-icons-headlessui
to: 'https://headlessui.com/vue/combobox'
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/forms/InputMenu.vue
---
## Usage

View File

@@ -180,13 +180,13 @@ Use the `#leading` slot to set the content of the leading icon.
::component-card
---
slots:
leading: <UAvatar src="https://avatars.githubusercontent.com/u/739984?v=4" size="3xs" class="mx-0.5" />
leading: <UAvatar src="https://avatars.githubusercontent.com/u/739984?v=4" size="2xs" />
baseProps:
placeholder: 'Search...'
---
#leading
:u-avatar{src="https://avatars.githubusercontent.com/u/739984?v=4" size="3xs" class="mx-0.5"}
:u-avatar{src="https://avatars.githubusercontent.com/u/739984?v=4" size="2xs"}
::
### `trailing`

View File

@@ -33,6 +33,18 @@ It also renders an `<a>` tag when a `to` prop is provided, otherwise it defaults
It is used underneath by the [Button](/components/button), [Dropdown](/components/dropdown) and [VerticalNavigation](/components/vertical-navigation) components.
## IntelliSense
If you're using VSCode and wish to get autocompletion for the classes `active-class` and `inactive-class`, you can add the following settings to your `.vscode/settings.json`:
```json [.vscode/settings.json]
{
"tailwindCSS.classAttributes": [
"active-class",
"inactive-class"
],
}
```
## Props
:component-props

View File

@@ -1,12 +1,12 @@
---
description: Display a modal within your application.
links:
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/overlays/Modal.vue
- label: 'Dialog'
icon: i-simple-icons-headlessui
to: 'https://headlessui.com/vue/dialog'
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/overlays/Modal.vue
---
## Usage
@@ -79,6 +79,8 @@ Then, you can use the `useModal` composable to control your modals within your a
:component-example{component="modal-example-composable"}
Additionally, you can close the modal within the modal component by calling `modal.close()`.
## Props
:component-props

View File

@@ -289,8 +289,14 @@ Slots defined in the `<UNotifications />` component are automatically passed dow
## Props
:component-props
::tabs
:component-props{label="Notification"}
:component-props{label="Notifications" slug="UNotifications"}
::
## Config
:component-preset
::tabs
:component-preset{label="Notification"}
:component-preset{label="Notifications" slug="Notifications"}
::

View File

@@ -1,12 +1,12 @@
---
description: Display a non-modal dialog that floats around a trigger element.
links:
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/overlays/Popover.vue
- label: 'Popover'
icon: i-simple-icons-headlessui
to: 'https://headlessui.com/vue/popover'
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/overlays/Popover.vue
---
## Usage

View File

@@ -2,12 +2,12 @@
title: SelectMenu
description: Display a select menu with advanced features.
links:
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/forms/SelectMenu.vue
- label: 'Listbox'
icon: i-simple-icons-headlessui
to: 'https://headlessui.com/vue/listbox'
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/forms/SelectMenu.vue
---
## Usage
@@ -50,7 +50,7 @@ componentProps:
---
::
If you only want to select a single object property rather than the whole object as value, you can set the `value-attribute` property. This prop defaults to `null`.
If you only want to select a single object property rather than the whole object as value, you can set the `value-attribute` property. This prop defaults to `null`.The value of the `value-attribute` field in options must be unique.
::component-example
---
@@ -257,7 +257,7 @@ componentProps:
Use the `#option-create` slot to customize the content displayed when the `creatable` prop is `true` and there is no options. You will have access to the `query` property in the slot scope.
::callout{icon="i-heroicons-light-bulb"}
An example is available in the [Create option](#create-option) section.
An example is available in the [Creatable](#creatable) section.
::
### `empty`

View File

@@ -203,7 +203,7 @@ Use the `#leading` slot to set the content of the leading icon.
::component-card
---
slots:
leading: <UAvatar src="https://avatars.githubusercontent.com/u/739984?v=4" size="3xs" class="mx-0.5" />
leading: <UIcon name="i-heroicons-flag" class="w-5 h-5" />
baseProps:
options:
- 'United States'
@@ -213,7 +213,7 @@ baseProps:
---
#leading
:u-avatar{src="https://avatars.githubusercontent.com/u/739984?v=4" size="3xs" class="mx-0.5"}
:u-icon{name="i-heroicons-flag" class="w-5 h-5"}
::
### `trailing`
@@ -223,13 +223,13 @@ Use the `#trailing` slot to set the content of the trailing icon.
::component-card
---
slots:
trailing: <UIcon name="i-heroicons-arrows-up-down-20-solid" />
trailing: <UIcon name="i-heroicons-arrows-up-down-20-solid" class="w-5 h-5" />
baseProps:
placeholder: 'Search...'
---
#trailing
:u-icon{name="i-heroicons-arrows-up-down-20-solid"}
:u-icon{name="i-heroicons-arrows-up-down-20-solid" class="w-5 h-5"}
::
## Props

View File

@@ -1,12 +1,12 @@
---
description: Display a dialog that slides in from the edge of the screen.
links:
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/overlays/Slideover.vue
- label: 'Dialog'
icon: i-simple-icons-headlessui
to: 'https://headlessui.com/vue/dialog'
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/overlays/Slideover.vue
---
## Usage
@@ -33,7 +33,7 @@ Set the `transition` prop to `false` to disable it.
### Prevent close
Use the `prevent-close` prop to disable the outside click alongside the `esc` keyboard shortcut. A `close-prevented` event will be emitted when the user tries to close the modal.
Use the `prevent-close` prop to disable the outside click alongside the `esc` keyboard shortcut. A `close-prevented` event will be emitted when the user tries to close the slideover.
:component-example{component="slideover-example-prevent-close"}
@@ -53,6 +53,24 @@ defineShortcuts({
</script>
```
### Control programmatically :u-badge{label="New" class="align-middle ml-2 !rounded-full" variant="subtle"}
First of all, add the `USlideovers` component to your app, preferably inside `app.vue`.
```vue [app.vue]
<template>
<div>
<UContainer>
<NuxtPage />
</UContainer>
<USlideovers />
</div>
</template>
```
Then, you can use the `useSlideover` composable to control your slideovers within your app.
:component-example{component="slideover-example-composable"}
## Props
:component-props

View File

@@ -1,12 +1,12 @@
---
description: Display a toggle field.
links:
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/forms/Toggle.vue
- label: 'Switch'
icon: i-simple-icons-headlessui
to: 'https://headlessui.com/vue/switch'
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/forms/Toggle.vue
---
## Usage
@@ -52,6 +52,19 @@ excludedProps:
---
::
### Loading :u-badge{label="New" class="align-middle ml-2 !rounded-full" variant="subtle"}
Use the `loading` prop to show a loading icon and disable the Toggle.
Use the `loading-icon` prop to set a different icon or change it globally in `ui.toggle.default.loadingIcon`. Defaults to `i-heroicons-arrow-path-20-solid`.
::component-card
---
props:
loading: true
---
::
### Disabled
Use the `disabled` prop to disable the Toggle.

View File

@@ -16,7 +16,7 @@
<Footer />
<ClientOnly>
<LazyUContentSearch :files="files" :navigation="navigation" :links="links" :fuse="{ resultLimit: 1000 }" />
<LazyUContentSearch :files="files" :navigation="navigation" :links="links" :fuse="{ resultLimit: 42 }" />
</ClientOnly>
<UNotifications>
@@ -79,7 +79,7 @@ const links = computed(() => {
active: route.path.startsWith('/pro/getting-started') || route.path.startsWith('/pro/components') || route.path.startsWith('/pro/prose')
}, {
label: 'Pricing',
icon: 'i-heroicons-credit-card',
icon: 'i-heroicons-ticket',
to: '/pro/pricing'
}, {
label: 'Templates',

View File

@@ -17,11 +17,10 @@ export default defineNuxtConfig({
].filter(Boolean),
modules: [
'@nuxt/content',
'@nuxt/fonts',
'@nuxt/image',
'nuxt-og-image',
module,
'@nuxtjs/fontaine',
'@nuxtjs/google-fonts',
'@nuxtjs/plausible',
'@vueuse/nuxt',
'nuxt-component-meta',
@@ -75,16 +74,6 @@ export default defineNuxtConfig({
image: {
provider: 'ipx'
},
fontMetrics: {
fonts: ['DM Sans']
},
googleFonts: {
display: 'swap',
download: true,
families: {
'DM+Sans': [400, 500, 600, 700]
}
},
nitro: {
prerender: {
routes: [

View File

@@ -7,30 +7,28 @@
},
"devDependencies": {
"@iconify-json/heroicons": "^1.1.20",
"@iconify-json/simple-icons": "^1.1.92",
"@nuxt/content": "^2.12.0",
"@nuxt/devtools": "^1.0.8",
"@nuxt/eslint-config": "^0.2.0",
"@nuxt/image": "^1.3.0",
"@nuxt/ui-pro": "npm:@nuxt/ui-pro-edge@1.0.0-28476869.809f447",
"@nuxtjs/fontaine": "^0.4.1",
"@nuxtjs/google-fonts": "^3.1.3",
"@nuxtjs/plausible": "^0.2.4",
"@octokit/rest": "^20.0.2",
"@vueuse/nuxt": "^10.8.0",
"date-fns": "^3.3.1",
"eslint": "^8.56.0",
"joi": "^17.12.2",
"nuxt": "^3.10.3",
"@iconify-json/simple-icons": "^1.1.98",
"@nuxt/content": "^2.12.1",
"@nuxt/eslint-config": "^0.3.0",
"@nuxt/fonts": "^0.6.1",
"@nuxt/image": "^1.5.0",
"@nuxt/ui-pro": "npm:@nuxt/ui-pro-edge@1.1.0-28538540.a353e68",
"@nuxtjs/plausible": "^1.0.0",
"@octokit/rest": "^20.1.0",
"@vueuse/nuxt": "^10.9.0",
"date-fns": "^3.6.0",
"eslint": "^8.57.0",
"joi": "^17.12.3",
"nuxt": "^3.11.2",
"nuxt-cloudflare-analytics": "^1.0.8",
"nuxt-component-meta": "^0.6.3",
"nuxt-og-image": "^2.2.4",
"prettier": "^3.2.5",
"typescript": "^5.3.3",
"ufo": "^1.4.0",
"typescript": "^5.4.4",
"ufo": "^1.5.3",
"v-calendar": "^3.1.2",
"valibot": "^0.29.0",
"yup": "^1.3.3",
"valibot": "^0.30.0",
"yup": "^1.4.0",
"zod": "^3.22.4"
}
}

View File

@@ -4,7 +4,7 @@
<ULandingHero :ui="{ base: 'relative z-[1]', container: 'max-w-4xl' }" class="mb-[calc(var(--header-height)*2)]">
<template #headline>
<UBadge variant="subtle" size="md" class="hover:bg-primary-100 dark:bg-primary-950/100 dark:hover:bg-primary-900 transition-color relative font-medium rounded-full shadow-none">
<NuxtLink :to="`https://github.com/nuxt/ui/releases/tag/v${config.version.split('.').slice(0, -1).join('.')}.0`" target="_blank" class="focus:outline-none" tabindex="-1">
<NuxtLink :to="`https://github.com/nuxt/ui/releases/tag/v${config.version.split('.').slice(0, -1).join('.')}.0`" target="_blank" class="focus:outline-none" aria-label="Go to last relase" tabindex="-1">
<span class="absolute inset-0" aria-hidden="true" />
</NuxtLink>
@@ -90,6 +90,7 @@
:width="card.image.width"
:height="card.image.height"
:alt="card.title"
loading="lazy"
class="object-cover w-full"
/>
</ULandingCard>
@@ -180,7 +181,7 @@
</template>
<div class="bg-gray-900/5 dark:bg-white/5 ring-1 ring-inset ring-gray-900/10 dark:ring-white/10 rounded-xl lg:-m-4 p-4">
<video poster="https://res.cloudinary.com/nuxt/video/upload/so_3.3/v1708511800/ui-pro/video-nuxt-ui-pro_kwfbdh.jpg" controls class="rounded-lg">
<video preload="none" poster="https://res.cloudinary.com/nuxt/video/upload/so_3.3/v1708511800/ui-pro/video-nuxt-ui-pro_kwfbdh.jpg" controls class="rounded-lg">
<source src="https://res.cloudinary.com/nuxt/video/upload/v1708511800/ui-pro/video-nuxt-ui-pro_kwfbdh.webm" type="video/webm">
<source src="https://res.cloudinary.com/nuxt/video/upload/v1708511800/ui-pro/video-nuxt-ui-pro_kwfbdh.mp4" type="video/mp4">
<source src="https://res.cloudinary.com/nuxt/video/upload/v1708511800/ui-pro/video-nuxt-ui-pro_kwfbdh.ogg" type="video/ogg">
@@ -282,7 +283,7 @@
</div>
</template>
<template #docs-surround>
<template #content-surround>
<UContentSurround
:surround="(surround as unknown as ParsedContent[])"
class="w-full gap-4"
@@ -300,7 +301,7 @@
/>
</template>
<template #docs-toc>
<template #content-toc>
<div class="absolute top-0 left-0 right-0 space-y-3">
<UContentToc :links="toc" class="bg-transparent relative max-h-full overflow-hidden top-0" :ui="({ container: { base: '!pt-0 !pb-4' } } as any)" />
@@ -380,7 +381,7 @@
<template #description>
<span v-html="page.pro.landing?.description" />
</template>
<video poster="https://res.cloudinary.com/nuxt/video/upload/so_14.4/v1698923423/ui-pro/nuxt-ui-pro-landing-demo_yrh6nr.jpg" controls>
<video preload="none" poster="https://res.cloudinary.com/nuxt/video/upload/so_14.4/v1698923423/ui-pro/nuxt-ui-pro-landing-demo_yrh6nr.jpg" controls>
<source src="https://res.cloudinary.com/nuxt/video/upload/v1698923423/ui-pro/nuxt-ui-pro-landing-demo_yrh6nr.webm" type="video/webm">
<source src="https://res.cloudinary.com/nuxt/video/upload/v1698923423/ui-pro/nuxt-ui-pro-landing-demo_yrh6nr.mp4" type="video/mp4">
<source src="https://res.cloudinary.com/nuxt/video/upload/v1698923423/ui-pro/nuxt-ui-pro-landing-demo_yrh6nr.ogg" type="video/ogg">
@@ -393,7 +394,7 @@
<template #description>
<span v-html="page.pro.docs?.description" />
</template>
<video poster="https://res.cloudinary.com/nuxt/video/upload/v1698923398/ui-pro/nuxt-ui-pro-docs-demo_jm6ubr.jpg" controls>
<video preload="none" poster="https://res.cloudinary.com/nuxt/video/upload/v1698923398/ui-pro/nuxt-ui-pro-docs-demo_jm6ubr.jpg" controls>
<source src="https://res.cloudinary.com/nuxt/video/upload/v1698923398/ui-pro/nuxt-ui-pro-docs-demo_jm6ubr.webm" type="video/webm">
<source src="https://res.cloudinary.com/nuxt/video/upload/v1698923398/ui-pro/nuxt-ui-pro-docs-demo_jm6ubr.mp4" type="video/mp4">
<source src="https://res.cloudinary.com/nuxt/video/upload/v1698923398/ui-pro/nuxt-ui-pro-docs-demo_jm6ubr.ogg" type="video/ogg">
@@ -617,7 +618,7 @@ const docsBlocks = computed(() => [isAfterStep(steps.docs) && {
slot: 'page-body',
class: 'inset-x-4 top-4 justify-start'
}, isAfterStep(steps.docs + 12) ? {
slot: 'docs-surround',
slot: 'content-surround',
class: 'bottom-4 inset-x-4 h-28'
} : {
name: 'UContentSurround',
@@ -634,7 +635,7 @@ const docsBlocks = computed(() => [isAfterStep(steps.docs) && {
class: 'right-4 inset-y-4 w-64',
inactive: isAfterStep(steps.docs + 14),
children: [{
slot: 'docs-toc',
slot: 'content-toc',
class: 'inset-4 overflow-y-auto'
}]
} : {
@@ -763,10 +764,12 @@ const navigationLinks = [{
const surround = [{
title: 'Introduction',
description: 'Fully styled and customizable components for Nuxt.'
description: 'Fully styled and customizable components for Nuxt.',
_path: '/'
}, {
title: 'Theming',
description: 'Learn how to customize the look and feel of the components.'
description: 'Learn how to customize the look and feel of the components.',
_path: '/'
}]
const md = `

View File

@@ -44,7 +44,7 @@ function createPrettierWorkerApi (worker: Worker): SimplePrettier {
export default defineNuxtPlugin({
async setup () {
let prettier: SimplePrettier
if (process.server) {
if (import.meta.server) {
const prettierModule = await import('prettier')
prettier = {
format (source, options = {

View File

@@ -23,7 +23,7 @@ export default defineNuxtPlugin({
`
})
if (process.client) {
if (import.meta.client) {
watch(root, () => {
window.localStorage.setItem('nuxt-ui-root', root.value)
})
@@ -31,7 +31,7 @@ export default defineNuxtPlugin({
appConfig.ui.primary = window.localStorage.getItem('nuxt-ui-primary') || appConfig.ui.primary
appConfig.ui.gray = window.localStorage.getItem('nuxt-ui-gray') || appConfig.ui.gray
}
if (process.server) {
if (import.meta.server) {
useHead({
script: [
{

View File

@@ -5,7 +5,7 @@ export default <Partial<Config>>{
theme: {
extend: {
fontFamily: {
sans: ['DM Sans', 'DM Sans fallback', ...defaultTheme.fontFamily.sans]
sans: ['DM Sans', ...defaultTheme.fontFamily.sans]
},
colors: {
green: {

View File

@@ -1,6 +1,6 @@
{
"name": "@nuxt/ui",
"version": "2.14.1",
"version": "2.15.1",
"repository": "nuxt/ui",
"homepage": "https://ui.nuxt.com",
"type": "module",
@@ -37,51 +37,51 @@
"@headlessui/tailwindcss": "^0.2.0",
"@headlessui/vue": "^1.7.19",
"@iconify-json/heroicons": "^1.1.20",
"@nuxt/kit": "^3.10.3",
"@nuxtjs/color-mode": "^3.3.2",
"@nuxt/kit": "^3.11.2",
"@nuxtjs/color-mode": "^3.3.3",
"@nuxtjs/tailwindcss": "^6.11.4",
"@popperjs/core": "^2.11.8",
"@tailwindcss/aspect-ratio": "^0.4.2",
"@tailwindcss/container-queries": "^0.1.1",
"@tailwindcss/forms": "^0.5.7",
"@tailwindcss/typography": "^0.5.10",
"@vueuse/core": "^10.8.0",
"@vueuse/integrations": "^10.8.0",
"@vueuse/math": "^10.8.0",
"@tailwindcss/typography": "^0.5.12",
"@vueuse/core": "^10.9.0",
"@vueuse/integrations": "^10.9.0",
"@vueuse/math": "^10.9.0",
"defu": "^6.1.4",
"fuse.js": "^6.6.2",
"nuxt-icon": "^0.6.8",
"nuxt-icon": "^0.6.10",
"ohash": "^1.1.3",
"pathe": "^1.1.2",
"scule": "^1.3.0",
"tailwind-merge": "^2.2.1",
"tailwindcss": "^3.4.1"
"tailwind-merge": "^2.2.2",
"tailwindcss": "^3.4.3"
},
"devDependencies": {
"@nuxt/eslint-config": "^0.2.0",
"@nuxt/eslint-config": "^0.3.0",
"@nuxt/module-builder": "^0.5.5",
"@nuxt/test-utils": "^3.11.0",
"@nuxt/test-utils": "^3.12.0",
"@release-it/conventional-changelog": "^8.0.1",
"@vue/test-utils": "^2.4.4",
"eslint": "^8.56.0",
"happy-dom": "^13.4.1",
"joi": "^17.12.2",
"nuxt": "^3.10.3",
"@vue/test-utils": "^2.4.5",
"eslint": "^8.57.0",
"happy-dom": "^14.5.1",
"joi": "^17.12.3",
"nuxt": "^3.11.2",
"release-it": "^17.1.1",
"typescript": "^5.3.3",
"typescript": "^5.4.4",
"unbuild": "^2.0.0",
"valibot": "^0.29.0",
"vitest": "^1.3.1",
"valibot": "^0.30.0",
"vitest": "^1.4.0",
"vitest-environment-nuxt": "^1.0.0",
"vue-tsc": "^1.8.27",
"yup": "^1.3.3",
"vue-tsc": "^2.0.10",
"yup": "^1.4.0",
"zod": "^3.22.4"
},
"resolutions": {
"@nuxt/kit": "3.10.3",
"@nuxt/schema": "3.10.3",
"tailwindcss": "3.4.1",
"@nuxt/kit": "^3.11.2",
"@nuxt/schema": "3.11.2",
"tailwindcss": "^3.4.3",
"@headlessui/vue": "1.7.19",
"vue": "3.4.19"
"vue": "3.4.21"
}
}

View File

@@ -1 +0,0 @@
imports.autoImport=true

4676
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
{
"extends": [
"@nuxtjs"
"github>nuxt/renovate-config-nuxt"
]
}

View File

@@ -200,6 +200,10 @@ export default defineNuxtModule<ModuleOptions>({
src: resolve(runtimeDir, 'plugins', 'modals')
})
addPlugin({
src: resolve(runtimeDir, 'plugins', 'slideovers')
})
// Components
addComponentsDir({

View File

@@ -280,8 +280,8 @@ export default defineComponent({
})
}
function onChange (event: any) {
if (event.target.checked) {
function onChange (checked: boolean) {
if (checked) {
selectAllRows()
} else {
selected.value = []

View File

@@ -39,20 +39,34 @@
@before-leave="onBeforeLeave"
@leave="onLeave"
>
<div v-show="open">
<HDisclosurePanel :class="[ui.item.base, ui.item.size, ui.item.color, ui.item.padding]" static>
<slot :name="item.slot || 'item'" :item="item" :index="index" :open="open" :close="close">
{{ item.content }}
</slot>
</HDisclosurePanel>
</div>
<HDisclosurePanel
v-if="unmount"
:class="[ui.item.base, ui.item.size, ui.item.color, ui.item.padding]"
unmount
>
<slot :name="item.slot || 'item'" :item="item" :index="index" :open="open" :close="close">
{{ item.content }}
</slot>
</HDisclosurePanel>
<template v-else>
<div v-show="open">
<HDisclosurePanel
:class="[ui.item.base, ui.item.size, ui.item.color, ui.item.padding]"
static
>
<slot :name="item.slot || 'item'" :item="item" :index="index" :open="open" :close="close">
{{ item.content }}
</slot>
</HDisclosurePanel>
</div>
</template>
</Transition>
</HDisclosure>
</div>
</template>
<script lang="ts">
import { ref, computed, toRef, defineComponent } from 'vue'
import { ref, computed, toRef, defineComponent, watch } from 'vue'
import type { PropType } from 'vue'
import { Disclosure as HDisclosure, DisclosureButton as HDisclosureButton, DisclosurePanel as HDisclosurePanel, provideUseId } from '@headlessui/vue'
import UIcon from '../elements/Icon.vue'
@@ -91,6 +105,10 @@ export default defineComponent({
type: String,
default: () => config.default.openIcon
},
unmount: {
type: Boolean,
default: false
},
closeIcon: {
type: String,
default: () => config.default.closeIcon
@@ -108,12 +126,25 @@ export default defineComponent({
default: () => ({})
}
},
setup (props) {
emits: ['open'],
setup (props, { emit }) {
const { ui, attrs } = useUI('accordion', toRef(props, 'ui'), config, toRef(props, 'class'))
const uiButton = computed<typeof configButton>(() => configButton)
const buttonRefs = ref<{ open: boolean, close: (e: EventTarget) => {} }[]>([])
const openedStates = computed(() => buttonRefs.value.map(({ open }) => open))
watch(openedStates, (newValue, oldValue) => {
for (const index in newValue) {
const isOpenBefore = oldValue[index]
const isOpenAfter = newValue[index]
if (!isOpenBefore && isOpenAfter) {
emit('open', index)
}
}
}, { immediate: true })
function closeOthers (currentIndex: number, e: Event) {
if (!props.items[currentIndex].closeOthers && props.multiple) {

View File

@@ -1,8 +1,12 @@
<template>
<div :class="alertClass" v-bind="attrs">
<div class="flex" :class="[ui.gap, { 'items-start': (description || $slots.description), 'items-center': !description && !$slots.description }]">
<UIcon v-if="icon" :name="icon" :class="ui.icon.base" />
<UAvatar v-if="avatar" v-bind="{ size: ui.avatar.size, ...avatar }" :class="ui.avatar.base" />
<slot name="icon" :icon="icon">
<UIcon v-if="icon" :name="icon" :ui="ui.icon.base" />
</slot>
<slot name="avatar" :avatar="avatar">
<UAvatar v-if="avatar" v-bind="{ size: ui.avatar.size, ...avatar }" :class="ui.avatar.base" />
</slot>
<div :class="ui.inner">
<p v-if="(title || $slots.title)" :class="ui.title">
@@ -10,7 +14,7 @@
{{ title }}
</slot>
</p>
<p v-if="description || $slots.description" :class="ui.description">
<p v-if="description || $slots.description" :class="twMerge(ui.description, !(title && $slots.title) && 'mt-0 leading-5')">
<slot name="description" :description="description">
{{ description }}
</slot>

View File

@@ -1,13 +1,14 @@
<template>
<span :class="wrapperClass">
<img
<component
:is="as"
v-if="url && !error"
:class="imgClass"
:alt="alt"
:src="url"
v-bind="attrs"
@error="onError"
>
/>
<span v-else-if="text" :class="ui.text">{{ text }}</span>
<UIcon v-else-if="icon" :name="icon" :class="iconClass" />
<span v-else-if="placeholder" :class="ui.placeholder">{{ placeholder }}</span>
@@ -39,6 +40,10 @@ export default defineComponent({
},
inheritAttrs: false,
props: {
as: {
type: [String, Object],
default: 'img'
},
src: {
type: [String, Boolean],
default: null

View File

@@ -5,6 +5,7 @@
v-for="(item, index) in items"
:key="index"
:class="ui.item"
:role="indicators ? 'tabpanel' : null"
>
<slot :item="item" :index="index" />
</div>
@@ -34,11 +35,13 @@
</slot>
</div>
<div v-if="indicators" :class="ui.indicators.wrapper">
<div v-if="indicators" role="tablist" :class="ui.indicators.wrapper">
<template v-for="page in pages" :key="page">
<slot name="indicator" :on-click="onClick" :active="page === currentPage" :page="page">
<button
type="button"
role="tab"
:aria-selected="page === currentPage"
:class="[
ui.indicators.base,
page === currentPage ? ui.indicators.active : ui.indicators.inactive
@@ -53,7 +56,7 @@
</template>
<script lang="ts">
import { ref, toRef, toRefs, computed, defineComponent } from 'vue'
import { ref, toRef, computed, defineComponent } from 'vue'
import type { PropType } from 'vue'
import { twMerge } from 'tailwind-merge'
import { mergeConfig } from '../../utils'
@@ -109,10 +112,9 @@ export default defineComponent({
const carouselRef = ref<HTMLElement>()
const itemWidth = ref(0)
const { x, arrivedState } = useScroll(carouselRef, { behavior: 'smooth' })
const { width: carouselWidth } = useElementSize(carouselRef)
const { x } = useScroll(carouselRef, { behavior: 'smooth' })
const { left: isFirst, right: isLast } = toRefs(arrivedState)
const { width: carouselWidth } = useElementSize(carouselRef)
useCarouselScroll(carouselRef)
@@ -122,7 +124,13 @@ export default defineComponent({
itemWidth.value = entry?.target?.firstElementChild?.clientWidth || 0
})
const currentPage = computed(() => Math.round(x.value / itemWidth.value) + 1)
const currentPage = computed(() => {
if (!itemWidth.value) {
return 0
}
return Math.round(x.value / itemWidth.value) + 1
})
const pages = computed(() => {
if (!itemWidth.value) {
@@ -132,6 +140,9 @@ export default defineComponent({
return props.items.length - Math.round(carouselWidth.value / itemWidth.value) + 1
})
const isFirst = computed(() => currentPage.value <= 1)
const isLast = computed(() => currentPage.value === pages.value)
function onClickNext () {
x.value += itemWidth.value
}

View File

@@ -7,7 +7,8 @@
:disabled="disabled"
:class="ui.trigger"
role="button"
@mouseover="onMouseOver"
@mouseenter="onMouseEnter"
@touchstart.passive="onTouchStart"
>
<slot :open="open" :disabled="disabled">
<button :disabled="disabled">
@@ -16,25 +17,25 @@
</slot>
</HMenuButton>
<div v-if="open && items.length" ref="container" :class="[ui.container, ui.width]" :style="containerStyle" @mouseover="onMouseOver">
<div v-if="open && items.length" ref="container" :class="[ui.container, ui.width]" :style="containerStyle" @mouseenter="onMouseEnter">
<Transition appear v-bind="ui.transition">
<div>
<div v-if="popper.arrow" data-popper-arrow :class="Object.values(ui.arrow)" />
<HMenuItems :class="[ui.base, ui.divide, ui.ring, ui.rounded, ui.shadow, ui.background, ui.height]" static>
<div v-for="(subItems, index) of items" :key="index" :class="ui.padding">
<NuxtLink v-for="(item, subIndex) of subItems" :key="subIndex" v-slot="{ href, target, rel, navigate, isExternal }" v-bind="getNuxtLinkProps(item)" custom>
<NuxtLink v-for="(item, subIndex) of subItems" :key="subIndex" v-slot="{ href, target, rel, navigate, isExternal, isActive }" v-bind="getNuxtLinkProps(item)" custom>
<HMenuItem v-slot="{ active, disabled: itemDisabled, close }" :disabled="item.disabled">
<component
:is="!!href ? 'a' : 'button'"
:href="!itemDisabled ? href : undefined"
:rel="rel"
:target="target"
:class="twMerge(twJoin(ui.item.base, ui.item.padding, ui.item.size, ui.item.rounded, active ? ui.item.active : ui.item.inactive, itemDisabled && ui.item.disabled), item.class)"
:class="twMerge(twJoin(ui.item.base, ui.item.padding, ui.item.size, ui.item.rounded, active || isActive ? ui.item.active : ui.item.inactive, itemDisabled && ui.item.disabled), item.class)"
@click="onClick($event, item, { href, navigate, close, isExternal })"
>
<slot :name="item.slot || 'item'" :item="item">
<UIcon v-if="item.icon" :name="item.icon" :class="twMerge(twJoin(ui.item.icon.base, active ? ui.item.icon.active : ui.item.icon.inactive), item.iconClass)" />
<UIcon v-if="item.icon" :name="item.icon" :class="twMerge(twJoin(ui.item.icon.base, active || isActive ? ui.item.icon.active : ui.item.icon.inactive), item.iconClass)" />
<UAvatar v-else-if="item.avatar" v-bind="{ size: ui.item.avatar.size, ...item.avatar }" :class="ui.item.avatar.base" />
<span :class="twMerge(ui.item.label, item.labelClass)">{{ item.label }}</span>
@@ -181,7 +182,19 @@ export default defineComponent({
}
})
function onMouseOver () {
function onTouchStart (event: TouchEvent) {
if (!event.cancelable || !menuApi.value) {
return
}
if (menuApi.value.menuState === 0) {
menuApi.value.closeMenu()
} else {
menuApi.value.openMenu()
}
}
function onMouseEnter () {
if (props.mode !== 'hover' || !menuApi.value) {
return
}
@@ -263,7 +276,8 @@ export default defineComponent({
trigger,
container,
containerStyle,
onMouseOver,
onTouchStart,
onMouseEnter,
onMouseLeave,
onClick,
getNuxtLinkProps,

View File

@@ -1,5 +1,5 @@
<template>
<div :class="ui.wrapper">
<div :class="ui.wrapper" :data-n-ids="attrs['data-n-ids']">
<div :class="ui.container">
<input
:id="inputId"
@@ -39,6 +39,7 @@ import type { Strategy } from '../../types'
import appConfig from '#build/app.config'
import { checkbox } from '#ui/ui.config'
import colors from '#ui-colors'
import { useId } from '#app'
const config = mergeConfig<typeof checkbox>(appConfig.ui.strategy, appConfig.ui.checkbox, checkbox)
@@ -105,7 +106,8 @@ export default defineComponent({
setup (props, { emit }) {
const { ui, attrs } = useUI('checkbox', toRef(props, 'ui'), config, toRef(props, 'class'))
const { emitFormChange, color, name, inputId } = useFormGroup(props)
const { emitFormChange, color, name, inputId: _inputId } = useFormGroup(props)
const inputId = _inputId.value ?? useId()
const toggle = computed({
get () {
@@ -117,7 +119,7 @@ export default defineComponent({
})
const onChange = (event: Event) => {
emit('change', event)
emit('change', (event.target as HTMLInputElement).checked)
emitFormChange()
}

View File

@@ -163,7 +163,7 @@ export default defineComponent({
default: () => ({})
}
},
emits: ['update:modelValue', 'blur'],
emits: ['update:modelValue', 'blur', 'change'],
setup (props, { emit, slots }) {
const { ui, attrs } = useUI('input', toRef(props, 'ui'), config, toRef(props, 'class'))
@@ -205,15 +205,19 @@ export default defineComponent({
}
const onChange = (event: Event) => {
const value = (event.target as HTMLInputElement).value
if (modelModifiers.value.lazy) {
updateInput(value)
}
// Update trimmed input so that it has same behavior as native input https://github.com/vuejs/core/blob/5ea8a8a4fab4e19a71e123e4d27d051f5e927172/packages/runtime-dom/src/directives/vModel.ts#L63
if (modelModifiers.value.trim) {
(event.target as HTMLInputElement).value = value.trim()
if (props.type === 'file') {
const value = (event.target as HTMLInputElement).files
emit('change', value)
} else {
const value = (event.target as HTMLInputElement).value
emit('change', value)
if (modelModifiers.value.lazy) {
updateInput(value)
}
// Update trimmed input so that it has same behavior as native input https://github.com/vuejs/core/blob/5ea8a8a4fab4e19a71e123e4d27d051f5e927172/packages/runtime-dom/src/directives/vModel.ts#L63
if (modelModifiers.value.trim) {
(event.target as HTMLInputElement).value = value.trim()
}
}
}

View File

@@ -21,7 +21,7 @@
autocomplete="off"
v-bind="attrs"
:display-value="() => query ? query : label"
@change="onChange"
@change="onQueryChange"
/>
<span v-if="(isLeading && leadingIconName) || $slots.leading" :class="leadingWrapperIconClass">
@@ -418,14 +418,15 @@ export default defineComponent({
}
})
function onUpdate (event: any) {
function onUpdate (value: any) {
query.value = ''
emit('update:modelValue', event)
emit('change', event)
emit('update:modelValue', value)
emit('change', value)
emitFormChange()
}
function onChange (event: any) {
function onQueryChange (event: any) {
query.value = event.target.value
}
@@ -459,7 +460,7 @@ export default defineComponent({
// eslint-disable-next-line vue/no-dupe-keys
query,
onUpdate,
onChange
onQueryChange
}
}
})

View File

@@ -1,5 +1,5 @@
<template>
<div :class="ui.wrapper">
<div :class="ui.wrapper" :data-n-ids="attrs['data-n-ids']">
<div :class="ui.container">
<input
:id="inputId"
@@ -11,6 +11,7 @@
type="radio"
:class="inputClass"
v-bind="attrs"
@change="onChange"
>
</div>
<div v-if="label || $slots.label" :class="ui.inner">
@@ -42,6 +43,7 @@ import { useId } from '#imports'
const config = mergeConfig<typeof radio>(appConfig.ui.strategy, appConfig.ui.radio, radio)
export default defineComponent({
inheritAttrs: false,
props: {
id: {
type: String,
@@ -110,14 +112,16 @@ export default defineComponent({
},
set (value) {
emit('update:modelValue', value)
emit('change', value)
if (!radioGroup) {
emitFormChange()
}
}
})
function onChange (event: Event) {
emit('change', (event.target as HTMLInputElement).value)
}
const inputClass = computed(() => {
return twMerge(twJoin(
ui.value.base,
@@ -138,7 +142,8 @@ export default defineComponent({
// eslint-disable-next-line vue/no-dupe-keys
name,
// eslint-disable-next-line vue/no-dupe-keys
inputClass
inputClass,
onChange
}
}
})

View File

@@ -1,6 +1,6 @@
<template>
<div :class="ui.wrapper">
<fieldset v-bind="attrs">
<fieldset v-bind="attrs" :class="ui.fieldset">
<legend v-if="legend || $slots.legend" :class="ui.legend">
<slot name="legend">
{{ legend }}

View File

@@ -107,7 +107,7 @@ export default defineComponent({
})
const onChange = (event: Event) => {
emit('change', event)
emit('change', (event.target as HTMLInputElement).value)
emitFormChange()
}

View File

@@ -194,8 +194,8 @@ export default defineComponent({
}
const onChange = (event: Event) => {
emit('change', (event.target as HTMLInputElement).value)
emitFormChange()
emit('change', event)
}
const guessOptionValue = (option: any) => {

View File

@@ -63,7 +63,7 @@
autofocus
autocomplete="off"
:class="uiMenu.input"
@change="onChange"
@change="onQueryChange"
/>
<component
:is="searchable ? 'HComboboxOption' : 'HListboxOption'"
@@ -105,12 +105,12 @@
</div>
</li>
</component>
<p v-else-if="searchable && query && !filteredOptions.length" :class="uiMenu.option.empty">
<p v-else-if="searchable && query && !filteredOptions?.length" :class="uiMenu.option.empty">
<slot name="option-empty" :query="query">
No results for "{{ query }}".
</slot>
</p>
<p v-else-if="!filteredOptions.length" :class="uiMenu.empty">
<p v-else-if="!filteredOptions?.length" :class="uiMenu.empty">
<slot name="empty" :query="query">
No options.
</slot>
@@ -174,7 +174,7 @@ export default defineComponent({
inheritAttrs: false,
props: {
modelValue: {
type: [String, Number, Object, Array],
type: [String, Number, Object, Array, Boolean],
default: ''
},
query: {
@@ -362,7 +362,7 @@ export default defineComponent({
} else {
return null
}
} else {
} else if (props.modelValue !== undefined && props.modelValue !== null) {
if (props.valueAttribute) {
const option = props.options.find(option => option[props.valueAttribute] === props.modelValue)
return option ? option[props.optionAttribute] : null
@@ -370,6 +370,8 @@ export default defineComponent({
return ['string', 'number'].includes(typeof props.modelValue) ? props.modelValue : props.modelValue[props.optionAttribute]
}
}
return null
})
const selectClass = computed(() => {
@@ -385,7 +387,7 @@ export default defineComponent({
variant?.replaceAll('{color}', color.value),
(isLeading.value || slots.leading) && ui.value.leading.padding[size.value],
(isTrailing.value || slots.trailing) && ui.value.trailing.padding[size.value]
), props.placeholder && !props.modelValue && ui.value.placeholder, props.selectClass)
), props.placeholder && (props.modelValue === undefined && props.modelValue === null) && ui.value.placeholder, props.selectClass)
})
const isLeading = computed(() => {
@@ -503,13 +505,13 @@ export default defineComponent({
}
})
function onUpdate (event: any) {
emit('update:modelValue', event)
emit('change', event)
function onUpdate (value: any) {
emit('update:modelValue', value)
emit('change', value)
emitFormChange()
}
function onChange (event: any) {
function onQueryChange (event: any) {
query.value = event.target.value
}
@@ -544,7 +546,7 @@ export default defineComponent({
// eslint-disable-next-line vue/no-dupe-keys
query,
onUpdate,
onChange
onQueryChange
}
}
})

View File

@@ -131,7 +131,7 @@ export default defineComponent({
default: () => ({})
}
},
emits: ['update:modelValue', 'blur'],
emits: ['update:modelValue', 'blur', 'change'],
setup (props, { emit }) {
const { ui, attrs } = useUI('textarea', toRef(props, 'ui'), config, toRef(props, 'class'))
@@ -192,6 +192,7 @@ export default defineComponent({
const onChange = (event: Event) => {
const value = (event.target as HTMLInputElement).value
emit('change', value)
if (modelModifiers.value.lazy) {
updateInput(value)

View File

@@ -3,15 +3,26 @@
:id="inputId"
v-model="active"
:name="name"
:disabled="disabled"
:disabled="disabled || loading"
:class="switchClass"
v-bind="attrs"
>
<span :class="containerClass">
<span v-if="onIcon" :class="[active ? ui.icon.active : ui.icon.inactive, ui.icon.base]" aria-hidden="true">
<span v-if="loading" :class="[ui.icon.active, ui.icon.base]" aria-hidden="true">
<UIcon :name="loadingIcon" :class="loadingIconClass" />
</span>
<span
v-if="!loading && onIcon"
:class="[active ? ui.icon.active : ui.icon.inactive, ui.icon.base]"
aria-hidden="true"
>
<UIcon :name="onIcon" :class="onIconClass" />
</span>
<span v-if="offIcon" :class="[active ? ui.icon.inactive : ui.icon.active, ui.icon.base]" aria-hidden="true">
<span
v-if="!loading && offIcon"
:class="[active ? ui.icon.inactive : ui.icon.active, ui.icon.base]"
aria-hidden="true"
>
<UIcon :name="offIcon" :class="offIconClass" />
</span>
</span>
@@ -58,6 +69,10 @@ export default defineComponent({
type: Boolean,
default: false
},
loading: {
type: Boolean,
default: false
},
onIcon: {
type: String,
default: () => config.default.onIcon
@@ -66,6 +81,10 @@ export default defineComponent({
type: String,
default: () => config.default.offIcon
},
loadingIcon: {
type: String,
default: () => config.default.loadingIcon
},
color: {
type: String as PropType<ToggleColor>,
default: () => config.default.color,
@@ -101,6 +120,8 @@ export default defineComponent({
},
set (value) {
emit('update:modelValue', value)
emit('change', value)
emitFormChange()
}
})
@@ -137,6 +158,13 @@ export default defineComponent({
)
})
const loadingIconClass = computed(() => {
return twJoin(
ui.value.icon.size[props.size],
color.value && ui.value.icon.loading.replaceAll('{color}', color.value)
)
})
provideUseId(() => useId())
return {
@@ -150,7 +178,8 @@ export default defineComponent({
switchClass,
containerClass,
onIconClass,
offIconClass
offIconClass,
loadingIconClass
}
}
})

View File

@@ -1,10 +1,10 @@
<template>
<div :class="ui.group.wrapper" role="option">
<div :class="ui.group.wrapper">
<h2 v-if="label" :class="ui.group.label">
{{ label }}
</h2>
<div :class="ui.group.container" role="listbox" :aria-label="group[groupAttribute]">
<div :class="ui.group.container" :aria-label="group[groupAttribute]">
<HComboboxOption
v-for="(command, index) of group.commands"
:key="`${group.key}-${index}`"

View File

@@ -1,5 +1,5 @@
<template>
<TransitionRoot :appear="appear" :show="isOpen" as="template">
<TransitionRoot :appear="appear" :show="isOpen" as="template" @after-leave="onAfterLeave">
<HDialog :class="ui.wrapper" v-bind="attrs" @close="close">
<TransitionChild v-if="overlay" as="template" :appear="appear" v-bind="ui.overlay.transition">
<div :class="[ui.overlay.base, ui.overlay.background]" />
@@ -82,7 +82,7 @@ export default defineComponent({
default: () => ({})
}
},
emits: ['update:modelValue', 'close', 'close-prevented'],
emits: ['update:modelValue', 'close', 'close-prevented', 'after-leave'],
setup (props, { emit }) {
const { ui, attrs } = useUI('modal', toRef(props, 'ui'), config, toRef(props, 'class'))
@@ -117,6 +117,10 @@ export default defineComponent({
emit('close')
}
const onAfterLeave = () => {
emit('after-leave')
}
provideUseId(() => useId())
return {
@@ -125,6 +129,7 @@ export default defineComponent({
attrs,
isOpen,
transitionClass,
onAfterLeave,
close
}
}

View File

@@ -1,5 +1,11 @@
<template>
<component :is="modalState.component" v-if="modalState" v-bind="modalState.props" v-model="isOpen" />
<component
:is="modalState.component"
v-if="modalState"
v-bind="modalState.props"
v-model="isOpen"
@after-leave="reset"
/>
</template>
<script lang="ts" setup>
@@ -8,5 +14,5 @@ import { useModal, modalInjectionKey } from '../../composables/useModal'
const modalState = inject(modalInjectionKey)
const { isOpen } = useModal()
const { isOpen, reset } = useModal()
</script>

View File

@@ -18,7 +18,7 @@
{{ title }}
</slot>
</p>
<p v-if="(description || $slots.description)" :class="ui.description">
<p v-if="(description || $slots.description)" :class="twMerge(ui.description, !(title && $slots.title) && 'mt-0 leading-5')">
<slot name="description" :description="description">
{{ description }}
</slot>

View File

@@ -7,7 +7,8 @@
:disabled="disabled"
:class="ui.trigger"
role="button"
@mouseover="onMouseOver"
@mouseenter="onMouseEnter"
@touchstart.passive="onTouchStart"
>
<slot :open="open" :close="close">
<button :disabled="disabled">
@@ -20,7 +21,7 @@
<div v-if="open" :class="[ui.overlay.base, ui.overlay.background]" />
</Transition>
<div v-if="open" ref="container" :class="[ui.container, ui.width]" :style="containerStyle" @mouseover="onMouseOver">
<div v-if="open" ref="container" :class="[ui.container, ui.width]" :style="containerStyle" @mouseenter="onMouseEnter">
<Transition appear v-bind="ui.transition">
<div>
<div v-if="popper.arrow" data-popper-arrow :class="Object.values(ui.arrow)" />
@@ -153,7 +154,19 @@ export default defineComponent({
}
})
function onMouseOver () {
function onTouchStart (event: TouchEvent) {
if (!event.cancelable || !popoverApi.value) {
return
}
if (popoverApi.value.popoverState === 0) {
popoverApi.value.closePopover()
} else {
popoverApi.value.togglePopover()
}
}
function onMouseEnter () {
if (props.mode !== 'hover' || !popoverApi.value) {
return
}
@@ -223,7 +236,8 @@ export default defineComponent({
trigger,
container,
containerStyle,
onMouseOver,
onTouchStart,
onMouseEnter,
onMouseLeave
}
}

View File

@@ -1,5 +1,5 @@
<template>
<TransitionRoot as="template" :appear="appear" :show="isOpen">
<TransitionRoot as="template" :appear="appear" :show="isOpen" @after-leave="onAfterLeave">
<HDialog :class="[ui.wrapper, { 'justify-end': side === 'right' }]" v-bind="attrs" @close="close">
<TransitionChild v-if="overlay" as="template" :appear="appear" v-bind="ui.overlay.transition">
<div :class="[ui.overlay.base, ui.overlay.background]" />
@@ -71,7 +71,7 @@ export default defineComponent({
default: () => ({})
}
},
emits: ['update:modelValue', 'close', 'close-prevented'],
emits: ['update:modelValue', 'close', 'close-prevented', 'after-leave'],
setup (props, { emit }) {
const { ui, attrs } = useUI('slideover', toRef(props, 'ui'), config, toRef(props, 'class'))
@@ -109,6 +109,10 @@ export default defineComponent({
emit('close')
}
const onAfterLeave = () => {
emit('after-leave')
}
provideUseId(() => useId())
return {
@@ -117,6 +121,7 @@ export default defineComponent({
attrs,
isOpen,
transitionClass,
onAfterLeave,
close
}
}

View File

@@ -0,0 +1,18 @@
<template>
<component
:is="slideoverState.component"
v-if="slideoverState"
v-bind="slideoverState.props"
v-model="isOpen"
@after-leave="reset"
/>
</template>
<script lang="ts" setup>
import { inject } from 'vue'
import { useSlideover, slidOverInjectionKey } from '../../composables/useSlideover'
const slideoverState = inject(slidOverInjectionKey)
const { isOpen, reset } = useSlideover()
</script>

View File

@@ -1,5 +1,5 @@
<template>
<div ref="trigger" :class="ui.wrapper" v-bind="attrs" @mouseover="onMouseOver" @mouseleave="onMouseLeave">
<div ref="trigger" :class="ui.wrapper" v-bind="attrs" @mouseenter="onMouseEnter" @mouseleave="onMouseLeave">
<slot :open="open">
Hover
</slot>
@@ -96,7 +96,7 @@ export default defineComponent({
// Methods
function onMouseOver () {
function onMouseEnter () {
// cancel programmed closing
if (closeTimeout) {
clearTimeout(closeTimeout)
@@ -137,7 +137,7 @@ export default defineComponent({
trigger,
container,
open,
onMouseOver,
onMouseEnter,
onMouseLeave
}
}

View File

@@ -42,6 +42,17 @@ export function useProvideButtonGroup (buttonGroupProps: ButtonGroupProps) {
export function useInjectButtonGroup ({ ui, props }: { ui: any, props: any }) {
const instance = getCurrentInstance()
provide('ButtonGroupContextConsumer', true)
const isParentPartOfGroup = inject('ButtonGroupContextConsumer', false)
// early return if a parent is already part of the group
if (isParentPartOfGroup) {
return {
size: computed(() => props.size),
rounded: computed(() => ui.value.rounded)
}
}
let parent = instance.parent
let groupContext: Ref<ButtonGroupContext> | undefined

View File

@@ -8,19 +8,29 @@ export const modalInjectionKey: InjectionKey<ShallowRef<ModalState>> = Symbol('n
function _useModal () {
const modalState = inject(modalInjectionKey)
const isOpen = ref(false)
function open<T extends Component> (component: T, props?: Modal & ComponentProps<T>) {
if (!modalState) {
throw new Error('useModal() is called without provider')
}
modalState.value = {
component,
props: props ?? {}
}
isOpen.value = true
}
function close () {
async function close () {
if (!modalState) return
isOpen.value = false
}
function reset () {
modalState.value = {
component: 'div',
props: {}
@@ -31,6 +41,8 @@ function _useModal () {
* Allows updating the modal props
*/
function patch <T extends Component = {}> (props: Partial<Modal & ComponentProps<T>>) {
if (!modalState) return
modalState.value = {
...modalState.value,
props: {
@@ -41,11 +53,12 @@ function _useModal () {
}
return {
isOpen,
open,
close,
patch
reset,
patch,
isOpen
}
}
export const useModal = createSharedComposable(_useModal)
export const useModal = createSharedComposable(_useModal)

View File

@@ -3,7 +3,7 @@ import { ref, computed, onMounted } from 'vue'
import type {} from '@vueuse/shared'
export const _useShortcuts = () => {
const macOS = computed(() => process.client && navigator && navigator.userAgent && navigator.userAgent.match(/Macintosh;/))
const macOS = computed(() => import.meta.client && navigator && navigator.userAgent && navigator.userAgent.match(/Macintosh;/))
const metaSymbol = ref(' ')

View File

@@ -0,0 +1,64 @@
import { ref, inject } from 'vue'
import type { ShallowRef, Component, InjectionKey } from 'vue'
import { createSharedComposable } from '@vueuse/core'
import type { ComponentProps } from '../types/component'
import type { Slideover, SlideoverState } from '../types/slideover'
export const slidOverInjectionKey: InjectionKey<ShallowRef<SlideoverState>> = Symbol('nuxt-ui.slideover')
function _useSlideover () {
const slideoverState = inject(slidOverInjectionKey)
const isOpen = ref(false)
function open<T extends Component> (component: T, props?: Slideover & ComponentProps<T>) {
if (!slideoverState) {
throw new Error('useSlideover() is called without provider')
}
slideoverState.value = {
component,
props: props ?? {}
}
isOpen.value = true
}
async function close () {
if (!slideoverState) return
isOpen.value = false
}
function reset () {
slideoverState.value = {
component: 'div',
props: {}
}
}
/**
* Allows updating the slideover props
*/
function patch<T extends Component = {}> (props: Partial<Slideover & ComponentProps<T>>) {
if (!slideoverState) return
slideoverState.value = {
...slideoverState.value,
props: {
...slideoverState.value.props,
...props
}
}
}
return {
open,
close,
reset,
patch,
isOpen
}
}
export const useSlideover = createSharedComposable(_useSlideover)

View File

@@ -41,7 +41,7 @@ ${Object.entries(gray || colors.cool).map(([key, value]) => `--color-gray-${key}
}
// SPA mode
if (process.client && nuxtApp.isHydrating && !nuxtApp.payload.serverRendered) {
if (import.meta.client && nuxtApp.isHydrating && !nuxtApp.payload.serverRendered) {
const style = document.createElement('style')
style.innerHTML = root.value

View File

@@ -0,0 +1,13 @@
import { defineNuxtPlugin } from '#imports'
import { shallowRef } from 'vue'
import { slidOverInjectionKey } from '../composables/useSlideover'
import type { SlideoverState } from '../types/slideover'
export default defineNuxtPlugin((nuxtApp) => {
const slideoverState = shallowRef<SlideoverState>({
component: 'div',
props: {}
})
nuxtApp.vueApp.provide(slidOverInjectionKey, slideoverState)
})

View File

@@ -9,7 +9,7 @@ export interface FormErrorWithId extends FormError {
id: string
}
export interface Form<T> {
export interface Form<T> extends HTMLFormElement {
validate(path?: string | string[], opts?: { silent?: true }): Promise<T | false>;
validate(path?: string | string[], opts?: { silent?: false }): Promise<T>;
clear(path?: string): void

View File

@@ -17,6 +17,7 @@ export * from './kbd'
export * from './link'
export * from './meter'
export * from './modal'
export * from './slideover'
export * from './notification'
export * from './popper'
export * from './progress'

17
src/runtime/types/slideover.d.ts vendored Normal file
View File

@@ -0,0 +1,17 @@
import type { Component } from 'vue'
interface Slideover {
ui?: any;
side?: 'right' | 'left';
transition?: boolean;
appear?: boolean;
overlay?: boolean;
preventClose?: boolean;
modelValue?: boolean;
}
interface SlideoverState {
component: Component | string;
props: Slideover;
}

View File

@@ -2,7 +2,7 @@ import { arrow } from '../popper'
export default {
container: 'z-20 group',
trigger: 'inline-flex w-full',
trigger: 'flex items-center w-full',
width: 'w-full',
height: 'max-h-60',
base: 'relative focus:outline-none overflow-y-auto scroll-py-1',

View File

@@ -1,7 +1,8 @@
export default {
wrapper: 'relative flex items-start',
fieldset: '',
legend: 'text-sm font-medium text-gray-700 dark:text-gray-200 mb-1',
default: {
color: 'primary'
}
}
}

View File

@@ -49,12 +49,14 @@ export default {
'2xl': 'h-6 w-6'
},
on: 'text-{color}-500 dark:text-{color}-400',
off: 'text-gray-400 dark:text-gray-500'
off: 'text-gray-400 dark:text-gray-500',
loading: 'animate-spin text-{color}-500 dark:text-{color}-400'
},
default: {
onIcon: null,
offIcon: null,
loadingIcon: 'i-heroicons-arrow-path-20-solid',
color: 'primary',
size: 'md'
}
}
}

View File

@@ -1,7 +1,7 @@
export default {
wrapper: {
base: 'flex items-center align-center text-center w-full',
horizontal: 'flex-row',
base: 'flex items-center align-center text-center',
horizontal: 'w-full flex-row',
vertical: 'flex-col'
},
container: {

View File

@@ -9,7 +9,7 @@ export default {
inactive: 'text-gray-500 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white',
label: 'truncate relative',
icon: {
base: 'flex-shrink-0 w-5 h-5',
base: 'flex-shrink-0 w-5 h-5 relative',
active: 'text-gray-700 dark:text-gray-200',
inactive: 'text-gray-400 dark:text-gray-500 group-hover:text-gray-700 dark:group-hover:text-gray-200'
},

View File

@@ -11,7 +11,7 @@ export default {
inactive: 'text-gray-500 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white hover:before:bg-gray-50 dark:hover:before:bg-gray-800/50',
label: 'truncate relative',
icon: {
base: 'flex-shrink-0 w-5 h-5',
base: 'flex-shrink-0 w-5 h-5 relative',
active: 'text-gray-700 dark:text-gray-200',
inactive: 'text-gray-400 dark:text-gray-500 group-hover:text-gray-700 dark:group-hover:text-gray-200'
},

View File

@@ -4,7 +4,7 @@ export default {
container: 'flex min-h-full items-end sm:items-center justify-center text-center',
padding: 'p-4 sm:p-0',
margin: 'sm:my-8',
base: 'relative text-left rtl:text-right overflow-hidden flex flex-col',
base: 'relative text-left rtl:text-right flex flex-col',
overlay: {
base: 'fixed inset-0 transition-opacity',
background: 'bg-gray-200/75 dark:bg-gray-800/75',

View File

@@ -28,5 +28,8 @@ export default {
openDelay: 0,
closeDelay: 0
},
arrow
arrow: {
...arrow,
base: '[@media(pointer:coarse)]:hidden invisible before:visible before:block before:rotate-45 before:z-[-1] before:w-2 before:h-2'
}
}

View File

@@ -1,6 +1,6 @@
{
"extends": "./.nuxt/tsconfig.json",
"exclude": ["docs", "dist"],
"exclude": ["docs", "dist", "playground", "node_modules"],
"compilerOptions": {
"noImplicitAny": false,
"strictNullChecks": false