mirror of
https://github.com/ArthurDanjou/ui.git
synced 2026-01-20 23:11:43 +01:00
Compare commits
120 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c75c0152ce | ||
|
|
993bb89e02 | ||
|
|
9f01145bc6 | ||
|
|
c1d9e0ecd4 | ||
|
|
f610c96a0b | ||
|
|
8b546600db | ||
|
|
f08471ccda | ||
|
|
d19d7077e4 | ||
|
|
07a4d13c0f | ||
|
|
9e90d1768b | ||
|
|
91e5002050 | ||
|
|
eb68d0d453 | ||
|
|
2bdb5d2b42 | ||
|
|
b62cd7905d | ||
|
|
58faa1053b | ||
|
|
e909884d03 | ||
|
|
5e84fd0570 | ||
|
|
98c0f567fc | ||
|
|
379d20fc3c | ||
|
|
c12f94653e | ||
|
|
2392b4aa40 | ||
|
|
36055ba978 | ||
|
|
73541f2d4f | ||
|
|
03030ab1db | ||
|
|
c98d6e31c0 | ||
|
|
49b73aa024 | ||
|
|
bd8b737642 | ||
|
|
dd8a122933 | ||
|
|
0b799e4300 | ||
|
|
8517897c34 | ||
|
|
72889535e7 | ||
|
|
878f7078a2 | ||
|
|
bd8118c124 | ||
|
|
3d5ffe76ef | ||
|
|
c49f8999d3 | ||
|
|
cc62e345eb | ||
|
|
ae58d5c2b9 | ||
|
|
92e736213b | ||
|
|
7d6b5c358f | ||
|
|
f854746bd8 | ||
|
|
e1e05af0ba | ||
|
|
224ec3c1fb | ||
|
|
c3ac4badbf | ||
|
|
398c5d5dcd | ||
|
|
e7697595c8 | ||
|
|
b0ecac563c | ||
|
|
5cb45c52c2 | ||
|
|
0453af65fa | ||
|
|
53cfea40a4 | ||
|
|
386e51d159 | ||
|
|
eb8eec09c5 | ||
|
|
4a4ddbd5cb | ||
|
|
a563d8fed4 | ||
|
|
7658211537 | ||
|
|
e736ecafff | ||
|
|
cee3e126a4 | ||
|
|
f4a48f6016 | ||
|
|
877b22c294 | ||
|
|
8cc8e45b4f | ||
|
|
3f67b9209c | ||
|
|
a2b8b700df | ||
|
|
80cc59375f | ||
|
|
2bb911023c | ||
|
|
ab355a3576 | ||
|
|
6c02d1c704 | ||
|
|
66c3631b3d | ||
|
|
435ef30f26 | ||
|
|
17a96416f0 | ||
|
|
4378268117 | ||
|
|
622aef5ffe | ||
|
|
174af36000 | ||
|
|
2d64b50559 | ||
|
|
272c19de70 | ||
|
|
6a1142b403 | ||
|
|
9937951fb7 | ||
|
|
002129c299 | ||
|
|
0c2f655a27 | ||
|
|
fb16735dec | ||
|
|
aa6e523780 | ||
|
|
bfd15c1818 | ||
|
|
027d85402b | ||
|
|
99b9467dc2 | ||
|
|
70bf4a7392 | ||
|
|
b50fbcf760 | ||
|
|
b74bf9f666 | ||
|
|
c0feca136a | ||
|
|
0a4a9e3d2c | ||
|
|
0b29dd4ca5 | ||
|
|
9cce4456d0 | ||
|
|
ca4f06a313 | ||
|
|
7dd9ee528e | ||
|
|
cdf6ff7152 | ||
|
|
9c2104d947 | ||
|
|
d1c8026a1e | ||
|
|
14efa81986 | ||
|
|
b3314dc16b | ||
|
|
06135f38ae | ||
|
|
dbbab8ded0 | ||
|
|
6e77f1d514 | ||
|
|
4b6e80e364 | ||
|
|
8a1b112727 | ||
|
|
961f0ae27b | ||
|
|
e819656a34 | ||
|
|
024e03acc9 | ||
|
|
462d7729c9 | ||
|
|
df857fd541 | ||
|
|
9b208bf297 | ||
|
|
5af9da4d3c | ||
|
|
1d995136a5 | ||
|
|
ba15add4db | ||
|
|
0aca478c57 | ||
|
|
0ee4f2b75b | ||
|
|
6e53cb6281 | ||
|
|
de715304dc | ||
|
|
81854112e3 | ||
|
|
87526b9ec5 | ||
|
|
f79187825f | ||
|
|
0443ac2c9d | ||
|
|
d2c51e3667 | ||
|
|
d15d7fa01d |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -9,3 +9,4 @@ dist
|
|||||||
.vercel
|
.vercel
|
||||||
.idea
|
.idea
|
||||||
.env
|
.env
|
||||||
|
.data
|
||||||
|
|||||||
1
.nuxtrc
1
.nuxtrc
@@ -1,2 +1 @@
|
|||||||
imports.autoImport=false
|
|
||||||
typescript.includeWorkspace=true
|
typescript.includeWorkspace=true
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"git": {
|
"git": {
|
||||||
"commitMessage": "chore(release): ${version}"
|
"commitMessage": "chore(release): v${version}",
|
||||||
|
"tagName": "v${version}"
|
||||||
},
|
},
|
||||||
"npm": {
|
"npm": {
|
||||||
"publish": false
|
"publish": false
|
||||||
@@ -11,7 +12,7 @@
|
|||||||
"web": true
|
"web": true
|
||||||
},
|
},
|
||||||
"hooks": {
|
"hooks": {
|
||||||
"before:init": ["pnpm lint"]
|
"before:init": ["pnpm lint", "pnpm typecheck"]
|
||||||
},
|
},
|
||||||
"plugins": {
|
"plugins": {
|
||||||
"@release-it/conventional-changelog": {
|
"@release-it/conventional-changelog": {
|
||||||
|
|||||||
85
CHANGELOG.md
85
CHANGELOG.md
@@ -1,5 +1,90 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [2.15.2](https://github.com/nuxt/ui/compare/v2.15.1...v2.15.2) (2024-04-12)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **Accordion:** add `unmount` prop to allow lazy mounting for heavy components ([#1590](https://github.com/nuxt/ui/issues/1590)) ([91e5002](https://github.com/nuxt/ui/commit/91e50020507ac66992dfb52b3e0ad1a1ae5614b5))
|
||||||
|
* **Table:** add `checkbox` ui config ([#1409](https://github.com/nuxt/ui/issues/1409)) ([8b54660](https://github.com/nuxt/ui/commit/8b546600dbfbff187d9c5be1b35ea1772e94f83f))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **Breadcrumb:** missing `min-w-0` on wrapper to truncate ([9f01145](https://github.com/nuxt/ui/commit/9f01145bc674378371ff34d7110f3235b57d2459)), closes [#1650](https://github.com/nuxt/ui/issues/1650)
|
||||||
|
* **Carousel:** next and prev buttons disabled ([#1619](https://github.com/nuxt/ui/issues/1619)) ([e909884](https://github.com/nuxt/ui/commit/e909884d0327bfd7b4d5551382123f8998beff6a))
|
||||||
|
* **Popover/Dropdown:** prevent unintended closure on touchstart in mobile devices ([#1609](https://github.com/nuxt/ui/issues/1609)) ([2392b4a](https://github.com/nuxt/ui/commit/2392b4aa405430fc22766f130448a7cc5ced9a3a))
|
||||||
|
* **Slideover:** remove dynamic component when closing ([#1615](https://github.com/nuxt/ui/issues/1615)) ([58faa10](https://github.com/nuxt/ui/commit/58faa1053b9be3f627c3fcff1bcaa14850bb9e7f))
|
||||||
|
* **Slideover:** wait for transition to complete to reset state ([#1624](https://github.com/nuxt/ui/issues/1624)) ([07a4d13](https://github.com/nuxt/ui/commit/07a4d13c0fcb05c87fb42e02a3a2d6c5c52ccf09))
|
||||||
|
|
||||||
|
## [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)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **module:** revert tailwind config from [#1272](https://github.com/nuxt/ui/issues/1272) ([#1404](https://github.com/nuxt/ui/issues/1404)) ([ba15add](https://github.com/nuxt/ui/commit/ba15add4db5d2f84e987819628cbbf88edcbad57))
|
||||||
|
|
||||||
## [2.14.0](https://github.com/nuxt/ui/compare/v2.13.0...v2.14.0) (2024-02-22)
|
## [2.14.0](https://github.com/nuxt/ui/compare/v2.13.0...v2.14.0) (2024-02-22)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -27,14 +27,7 @@ Read more on [ui.nuxt.com](https://ui.nuxt.com)
|
|||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# npm
|
npx nuxi@latest module add ui
|
||||||
npm install @nuxt/ui
|
|
||||||
# yarn
|
|
||||||
yarn add @nuxt/ui
|
|
||||||
# pnpm
|
|
||||||
pnpm add @nuxt/ui
|
|
||||||
# bun
|
|
||||||
bun add @nuxt/ui
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Then, register the module in your `nuxt.config.ts`:
|
Then, register the module in your `nuxt.config.ts`:
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
imports.autoImport=true
|
|
||||||
@@ -3,6 +3,8 @@
|
|||||||
<div>
|
<div>
|
||||||
<NuxtLoadingIndicator />
|
<NuxtLoadingIndicator />
|
||||||
|
|
||||||
|
<Banner v-if="!$route.path.startsWith('/examples')" />
|
||||||
|
|
||||||
<Header v-if="!$route.path.startsWith('/examples')" :links="links" />
|
<Header v-if="!$route.path.startsWith('/examples')" :links="links" />
|
||||||
|
|
||||||
<NuxtLayout>
|
<NuxtLayout>
|
||||||
@@ -12,7 +14,7 @@
|
|||||||
<Footer v-if="!$route.path.startsWith('/examples')" />
|
<Footer v-if="!$route.path.startsWith('/examples')" />
|
||||||
|
|
||||||
<ClientOnly>
|
<ClientOnly>
|
||||||
<LazyUDocsSearch ref="searchRef" :files="files" :navigation="navigation" :links="links" :fuse="{ resultLimit: 1000 }" />
|
<LazyUContentSearch ref="searchRef" :files="files" :navigation="navigation" :links="links" :fuse="{ resultLimit: 42 }" />
|
||||||
</ClientOnly>
|
</ClientOnly>
|
||||||
|
|
||||||
<UNotifications>
|
<UNotifications>
|
||||||
@@ -21,6 +23,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</UNotifications>
|
</UNotifications>
|
||||||
<UModals />
|
<UModals />
|
||||||
|
<USlideovers />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -69,7 +72,7 @@ const links = computed(() => {
|
|||||||
active: route.path.startsWith('/pro/getting-started') || route.path.startsWith('/pro/components') || route.path.startsWith('/pro/prose')
|
active: route.path.startsWith('/pro/getting-started') || route.path.startsWith('/pro/components') || route.path.startsWith('/pro/prose')
|
||||||
}, {
|
}, {
|
||||||
label: 'Pricing',
|
label: 'Pricing',
|
||||||
icon: 'i-heroicons-credit-card',
|
icon: 'i-heroicons-ticket',
|
||||||
to: '/pro/pricing'
|
to: '/pro/pricing'
|
||||||
}, {
|
}, {
|
||||||
label: 'Templates',
|
label: 'Templates',
|
||||||
|
|||||||
58
docs/components/Banner.vue
Normal file
58
docs/components/Banner.vue
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const id = 'nuxt-ui-banner-1'
|
||||||
|
const to = '/pro/pricing'
|
||||||
|
|
||||||
|
const hideBanner = () => {
|
||||||
|
localStorage.setItem(id, 'true')
|
||||||
|
|
||||||
|
document.querySelector('html')?.classList.add('hide-banner')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.server) {
|
||||||
|
useHead({
|
||||||
|
script: [{
|
||||||
|
key: 'prehydrate-template-banner',
|
||||||
|
innerHTML: `
|
||||||
|
if (localStorage.getItem('${id}') === 'true') {
|
||||||
|
document.querySelector('html').classList.add('hide-banner')
|
||||||
|
}`.replace(/\s+/g, ' '),
|
||||||
|
type: 'text/javascript'
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</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" aria-label="Nuxt UI Pro pricing" tabindex="-1">
|
||||||
|
<span class="absolute inset-0 " aria-hidden="true" />
|
||||||
|
</NuxtLink>
|
||||||
|
|
||||||
|
<div class="flex items-center justify-between gap-2">
|
||||||
|
<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-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" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</UContainer>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.hide-banner .app-banner {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -18,8 +18,8 @@
|
|||||||
<template #right>
|
<template #right>
|
||||||
<ColorPicker />
|
<ColorPicker />
|
||||||
|
|
||||||
<UTooltip text="Search" :shortcuts="[metaSymbol, 'K']">
|
<UTooltip text="Search" :shortcuts="[metaSymbol, 'K']" :popper="{ strategy: 'absolute' }">
|
||||||
<UDocsSearchButton :label="null" />
|
<UContentSearchButton :label="null" />
|
||||||
</UTooltip>
|
</UTooltip>
|
||||||
|
|
||||||
<UColorModeButton />
|
<UColorModeButton />
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<UPopover mode="hover">
|
<UPopover mode="hover" :popper="{ strategy: 'absolute' }" :ui="{ width: 'w-[156px]' }">
|
||||||
<template #default="{ open }">
|
<template #default="{ open }">
|
||||||
<UButton color="gray" variant="ghost" square :class="[open && 'bg-gray-50 dark:bg-gray-800']" aria-label="Color picker">
|
<UButton color="gray" variant="ghost" square :class="[open && 'bg-gray-50 dark:bg-gray-800']" aria-label="Color picker">
|
||||||
<UIcon name="i-heroicons-swatch-20-solid" class="w-5 h-5 text-primary-500 dark:text-primary-400" />
|
<UIcon name="i-heroicons-swatch-20-solid" class="w-5 h-5 text-primary-500 dark:text-primary-400" />
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="[&>div>pre]:!rounded-t-none [&>div>pre]:!mt-0">
|
<div class="[&>div>pre]:!rounded-t-none [&>div>pre]:!mt-0">
|
||||||
<div
|
<div
|
||||||
|
v-if="hasPreview"
|
||||||
class="flex border border-gray-200 dark:border-gray-700 relative rounded-t-md"
|
class="flex border border-gray-200 dark:border-gray-700 relative rounded-t-md"
|
||||||
:class="[{ 'p-4': padding, 'rounded-b-md': !hasCode, 'border-b-0': hasCode, 'not-prose': !prose }, backgroundClass, extraClass]"
|
:class="[{ 'p-4': padding, 'rounded-b-md': !hasCode, 'border-b-0': hasCode, 'not-prose': !prose }, backgroundClass, extraClass]"
|
||||||
>
|
>
|
||||||
@@ -37,6 +38,10 @@ const props = defineProps({
|
|||||||
type: Object,
|
type: Object,
|
||||||
default: () => ({})
|
default: () => ({})
|
||||||
},
|
},
|
||||||
|
hiddenPreview: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
hiddenCode: {
|
hiddenCode: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
@@ -79,6 +84,7 @@ const data = await fetchContentExampleCode(camelName)
|
|||||||
const highlighter = useShikiHighlighter()
|
const highlighter = useShikiHighlighter()
|
||||||
|
|
||||||
const hasCode = computed(() => !props.hiddenCode && (data?.code || instance.slots.code))
|
const hasCode = computed(() => !props.hiddenCode && (data?.code || instance.slots.code))
|
||||||
|
const hasPreview = computed(() => !props.hiddenPreview && (props.component || instance.slots.default))
|
||||||
|
|
||||||
const { data: ast } = await useAsyncData(`content-example-${camelName}-ast`, () => transformContent('content:_markdown.md', `\`\`\`vue\n${data?.code ?? ''}\n\`\`\``, {
|
const { data: ast } = await useAsyncData(`content-example-${camelName}-ast`, () => transformContent('content:_markdown.md', `\`\`\`vue\n${data?.code ?? ''}\n\`\`\``, {
|
||||||
markdown: {
|
markdown: {
|
||||||
|
|||||||
@@ -61,9 +61,7 @@ const items = [{
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-col items-center">
|
<div class="flex flex-col items-center">
|
||||||
<code>$ npm i @nuxt/ui</code>
|
<code>$ npx nuxi@latest module add ui</code>
|
||||||
<code>$ yarn add @nuxt/ui</code>
|
|
||||||
<code>$ pnpm add @nuxt/ui</code>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</UAccordion>
|
</UAccordion>
|
||||||
|
|||||||
20
docs/components/content/examples/AlertExampleAvatar.vue
Normal file
20
docs/components/content/examples/AlertExampleAvatar.vue
Normal 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>
|
||||||
|
|
||||||
10
docs/components/content/examples/AlertExampleIcon.vue
Normal file
10
docs/components/content/examples/AlertExampleIcon.vue
Normal 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>
|
||||||
|
|
||||||
@@ -12,7 +12,7 @@ const links = [{
|
|||||||
<template>
|
<template>
|
||||||
<UBreadcrumb :links="links">
|
<UBreadcrumb :links="links">
|
||||||
<template #default="{ link, isActive, index }">
|
<template #default="{ link, isActive, index }">
|
||||||
<UBadge :color="isActive ? 'primary' : 'gray'" class="rounded-full">
|
<UBadge :color="isActive ? 'primary' : 'gray'" class="rounded-full truncate">
|
||||||
{{ index + 1 }}. {{ link.label }}
|
{{ index + 1 }}. {{ link.label }}
|
||||||
</UBadge>
|
</UBadge>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ const date = ref(new Date())
|
|||||||
<UButton icon="i-heroicons-calendar-days-20-solid" :label="format(date, 'd MMM, yyy')" />
|
<UButton icon="i-heroicons-calendar-days-20-solid" :label="format(date, 'd MMM, yyy')" />
|
||||||
|
|
||||||
<template #panel="{ close }">
|
<template #panel="{ close }">
|
||||||
<DatePicker v-model="date" @close="close" />
|
<DatePicker v-model="date" is-required @close="close" />
|
||||||
</template>
|
</template>
|
||||||
</UPopover>
|
</UPopover>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ function selectRange (duration: Duration) {
|
|||||||
</UButton>
|
</UButton>
|
||||||
|
|
||||||
<template #panel="{ close }">
|
<template #panel="{ close }">
|
||||||
<div class="flex items-center divide-x divide-gray-200 dark:divide-gray-800">
|
<div class="flex items-center sm:divide-x divide-gray-200 dark:divide-gray-800">
|
||||||
<div class="flex flex-col py-4">
|
<div class="hidden sm:flex flex-col py-4">
|
||||||
<UButton
|
<UButton
|
||||||
v-for="(range, index) in ranges"
|
v-for="(range, index) in ranges"
|
||||||
:key="index"
|
:key="index"
|
||||||
@@ -37,6 +37,7 @@ function selectRange (duration: Duration) {
|
|||||||
variant="ghost"
|
variant="ghost"
|
||||||
class="rounded-none px-6"
|
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']"
|
: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)"
|
@click="selectRange(range.duration)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -31,8 +31,8 @@ const selected = ref(people[0])
|
|||||||
<template>
|
<template>
|
||||||
<UInputMenu v-model="selected" :options="people">
|
<UInputMenu v-model="selected" :options="people">
|
||||||
<template #leading>
|
<template #leading>
|
||||||
<UIcon v-if="selected.icon" :name="(selected.icon as string)" class="w-4 h-4 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="3xs" class="mx-0.5" />
|
<UAvatar v-else-if="selected.avatar" v-bind="(selected.avatar as Avatar)" size="2xs" />
|
||||||
</template>
|
</template>
|
||||||
</UInputMenu>
|
</UInputMenu>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -5,13 +5,24 @@ defineProps({
|
|||||||
default: 0
|
default: 0
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['success'])
|
||||||
|
|
||||||
|
function onSuccess () {
|
||||||
|
emit('success')
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<UModal>
|
<UModal>
|
||||||
<UCard>
|
<UCard>
|
||||||
<p>This modal was opened programmatically !</p>
|
<div class="space-y-2">
|
||||||
<p>Count: {{ count }}</p>
|
<p>This modal was opened programmatically !</p>
|
||||||
|
<p>Count: {{ count }}</p>
|
||||||
|
<UButton @click="onSuccess">
|
||||||
|
Click to emit a success event
|
||||||
|
</UButton>
|
||||||
|
</div>
|
||||||
</UCard>
|
</UCard>
|
||||||
</UModal>
|
</UModal>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,13 +1,20 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ModalExampleComponent } from '#components'
|
import { ModalExampleComponent } from '#components'
|
||||||
|
|
||||||
|
const toast = useToast()
|
||||||
const modal = useModal()
|
const modal = useModal()
|
||||||
const count = ref(0)
|
const count = ref(0)
|
||||||
|
|
||||||
function openModal () {
|
function openModal () {
|
||||||
count.value += 1
|
count.value += 1
|
||||||
modal.open(ModalExampleComponent, {
|
modal.open(ModalExampleComponent, {
|
||||||
count: count.value
|
count: count.value,
|
||||||
|
onSuccess () {
|
||||||
|
toast.add({
|
||||||
|
title: 'Success !',
|
||||||
|
id: 'modal-success'
|
||||||
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -3,5 +3,5 @@ const value = ref(50)
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<URange v-model="value" name="range" />
|
<URange v-model="value" />
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -31,8 +31,8 @@ const selected = ref(people[0])
|
|||||||
<template>
|
<template>
|
||||||
<USelectMenu v-model="selected" :options="people">
|
<USelectMenu v-model="selected" :options="people">
|
||||||
<template #leading>
|
<template #leading>
|
||||||
<UIcon v-if="selected.icon" :name="(selected.icon as string)" class="w-4 h-4 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="3xs" class="mx-0.5" />
|
<UAvatar v-else-if="selected.avatar" v-bind="(selected.avatar as Avatar)" size="2xs" />
|
||||||
</template>
|
</template>
|
||||||
</USelectMenu>
|
</USelectMenu>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -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>
|
||||||
@@ -186,7 +186,7 @@ const { data: todos, pending } = await useLazyAsyncData<{
|
|||||||
sort-desc-icon="i-heroicons-arrow-down"
|
sort-desc-icon="i-heroicons-arrow-down"
|
||||||
sort-mode="manual"
|
sort-mode="manual"
|
||||||
class="w-full"
|
class="w-full"
|
||||||
:ui="{ td: { base: 'max-w-[0] truncate' } }"
|
:ui="{ td: { base: 'max-w-[0] truncate' }, default: { checkbox: { color: 'gray' } } }"
|
||||||
@select="select"
|
@select="select"
|
||||||
>
|
>
|
||||||
<template #completed-data="{ row }">
|
<template #completed-data="{ row }">
|
||||||
|
|||||||
@@ -32,13 +32,7 @@ const links = [{
|
|||||||
<template>
|
<template>
|
||||||
<UVerticalNavigation :links="links">
|
<UVerticalNavigation :links="links">
|
||||||
<template #avatar="{ link }">
|
<template #avatar="{ link }">
|
||||||
<UAvatar
|
<UAvatar v-bind="link.avatar" size="2xs" loading="lazy" />
|
||||||
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" />
|
|
||||||
</template>
|
</template>
|
||||||
</UVerticalNavigation>
|
</UVerticalNavigation>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { useBreakpoints, breakpointsTailwind } from '@vueuse/core'
|
||||||
import { DatePicker as VCalendarDatePicker } from 'v-calendar'
|
import { DatePicker as VCalendarDatePicker } from 'v-calendar'
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import type { DatePickerDate, DatePickerRangeObject } from 'v-calendar/dist/types/src/use/datePicker'
|
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 = {
|
const attrs = {
|
||||||
transparent: true,
|
transparent: true,
|
||||||
borderless: true,
|
borderless: true,
|
||||||
@@ -35,7 +40,7 @@ const attrs = {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<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 }" />
|
<VCalendarDatePicker v-else v-model="date" v-bind="{ ...attrs, ...$attrs }" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export async function fetchComponentMeta (name: string) {
|
|||||||
// Store promise to avoid multiple calls
|
// Store promise to avoid multiple calls
|
||||||
|
|
||||||
// add to nitro prerender
|
// add to nitro prerender
|
||||||
if (process.server) {
|
if (import.meta.server) {
|
||||||
const event = useRequestEvent()
|
const event = useRequestEvent()
|
||||||
event.node.res.setHeader(
|
event.node.res.setHeader(
|
||||||
'x-nitro-prerender',
|
'x-nitro-prerender',
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export async function fetchContentExampleCode (name?: string) {
|
|||||||
if (state.value[name]) { return state.value[name] }
|
if (state.value[name]) { return state.value[name] }
|
||||||
|
|
||||||
// add to nitro prerender
|
// add to nitro prerender
|
||||||
if (process.server) {
|
if (import.meta.server) {
|
||||||
const event = useRequestEvent()
|
const event = useRequestEvent()
|
||||||
event.node.res.setHeader(
|
event.node.res.setHeader(
|
||||||
'x-nitro-prerender',
|
'x-nitro-prerender',
|
||||||
|
|||||||
@@ -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.
|
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
|
## Features
|
||||||
|
|
||||||
|
|||||||
@@ -5,29 +5,15 @@ description: 'Learn how to install and configure Nuxt UI in your application.'
|
|||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
1. Install `@nuxt/ui` dependency to your project:
|
### Add to a Nuxt project
|
||||||
|
|
||||||
::code-group
|
1. Add `@nuxt/ui` module to your project:
|
||||||
|
|
||||||
```bash [pnpm]
|
```bash
|
||||||
pnpm add @nuxt/ui
|
npx nuxi@latest module add ui
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash [yarn]
|
2. Add it to the `modules` section in your `nuxt.config.ts`:
|
||||||
yarn add @nuxt/ui
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash [npm]
|
|
||||||
npm install @nuxt/ui
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash [bun]
|
|
||||||
bun add @nuxt/ui
|
|
||||||
```
|
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
2. Add it to your `modules` section in your `nuxt.config`:
|
|
||||||
|
|
||||||
```ts [nuxt.config.ts]
|
```ts [nuxt.config.ts]
|
||||||
export default defineNuxtConfig({
|
export default defineNuxtConfig({
|
||||||
@@ -37,6 +23,19 @@ export default defineNuxtConfig({
|
|||||||
|
|
||||||
That's it! You can now use all the components and composables in your Nuxt app ✨
|
That's it! You can now use all the components and composables in your Nuxt app ✨
|
||||||
|
|
||||||
|
### Use Nuxt starter
|
||||||
|
|
||||||
|
[Nuxt Starter](https://nuxt.new/) template makes it easy to get started with Nuxt UI.
|
||||||
|
The Nuxt Starter template is available from the `nuxi init` command.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx nuxi@latest init -t ui
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Please check [nuxt/starter](https://github.com/nuxt/starter/tree/ui) for details.
|
||||||
|
|
||||||
## Modules
|
## Modules
|
||||||
|
|
||||||
Nuxt UI will automatically install the [@nuxtjs/tailwindcss](https://tailwindcss.nuxtjs.org/), [@nuxtjs/color-mode](https://color-mode.nuxtjs.org/) and [nuxt-icon](https://github.com/nuxt-modules/icon) modules for you.
|
Nuxt UI will automatically install the [@nuxtjs/tailwindcss](https://tailwindcss.nuxtjs.org/), [@nuxtjs/color-mode](https://color-mode.nuxtjs.org/) and [nuxt-icon](https://github.com/nuxt-modules/icon) modules for you.
|
||||||
@@ -194,7 +193,7 @@ To enable these two features, you can add the following to your `.vscode/setting
|
|||||||
{
|
{
|
||||||
"tailwindCSS.experimental.classRegex": [
|
"tailwindCSS.experimental.classRegex": [
|
||||||
["ui:\\s*{([^)]*)\\s*}", "[\"'`]([^\"'`]*).*?[\"'`]"],
|
["ui:\\s*{([^)]*)\\s*}", "[\"'`]([^\"'`]*).*?[\"'`]"],
|
||||||
["/\\*ui\\*/\\s*{([^;]*)}", ":\\s*[\"'`]([^\"'`]*).*?[\"'`]"]
|
["/\\*\\s?ui\\s?\\*/\\s*{([^;]*)}", ":\\s*[\"'`]([^\"'`]*).*?[\"'`]"]
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -207,7 +206,7 @@ An example SFC using IntelliSense (note the `/*ui*/` prefix, also works with `re
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const ui = /*ui*/ {
|
const ui = /* ui */ {
|
||||||
background: 'bg-white dark:bg-slate-900'
|
background: 'bg-white dark:bg-slate-900'
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
---
|
---
|
||||||
description: Display togglable accordion panels.
|
description: Display togglable accordion panels.
|
||||||
links:
|
links:
|
||||||
- label: GitHub
|
|
||||||
icon: i-simple-icons-github
|
|
||||||
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/elements/Accordion.vue
|
|
||||||
- label: Disclosure
|
- label: Disclosure
|
||||||
icon: i-simple-icons-headlessui
|
icon: i-simple-icons-headlessui
|
||||||
to: 'https://headlessui.com/vue/disclosure'
|
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
|
## Usage
|
||||||
|
|||||||
@@ -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"}
|
: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
|
## Props
|
||||||
|
|
||||||
:component-props
|
:component-props
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
---
|
---
|
||||||
title: Breadcrumb
|
title: Breadcrumb
|
||||||
description: A list of links that indicate the current page's location within a navigational hierarchy.
|
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
|
## Usage
|
||||||
|
|||||||
@@ -336,7 +336,7 @@ Use the `#leading` slot to set the content of the leading icon.
|
|||||||
::component-card
|
::component-card
|
||||||
---
|
---
|
||||||
slots:
|
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:
|
baseProps:
|
||||||
color: 'gray'
|
color: 'gray'
|
||||||
props:
|
props:
|
||||||
@@ -347,7 +347,7 @@ excludedProps:
|
|||||||
---
|
---
|
||||||
|
|
||||||
#leading
|
#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`
|
### `trailing`
|
||||||
@@ -357,7 +357,7 @@ Use the `#trailing` slot to set the content of the trailing icon.
|
|||||||
::component-card
|
::component-card
|
||||||
---
|
---
|
||||||
slots:
|
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:
|
props:
|
||||||
label: Button
|
label: Button
|
||||||
color: 'gray'
|
color: 'gray'
|
||||||
@@ -366,7 +366,7 @@ excludedProps:
|
|||||||
---
|
---
|
||||||
|
|
||||||
#trailing
|
#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
|
## Props
|
||||||
|
|||||||
@@ -2,12 +2,12 @@
|
|||||||
title: CommandPalette
|
title: CommandPalette
|
||||||
description: Add a customizable command palette to your app.
|
description: Add a customizable command palette to your app.
|
||||||
links:
|
links:
|
||||||
- label: GitHub
|
|
||||||
icon: i-simple-icons-github
|
|
||||||
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/navigation/CommandPalette.vue
|
|
||||||
- label: 'Combobox'
|
- label: 'Combobox'
|
||||||
icon: i-simple-icons-headlessui
|
icon: i-simple-icons-headlessui
|
||||||
to: 'https://headlessui.com/vue/combobox'
|
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
|
## Usage
|
||||||
|
|||||||
@@ -16,15 +16,15 @@ Let's start by installing the `v-calendar` and `date-fns` dependency:
|
|||||||
::code-group
|
::code-group
|
||||||
|
|
||||||
```bash [pnpm]
|
```bash [pnpm]
|
||||||
pnpm add v-calendar@next
|
pnpm add v-calendar@next date-fns
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash [yarn]
|
```bash [yarn]
|
||||||
yarn add v-calendar@next
|
yarn add v-calendar@next date-fns
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash [npm]
|
```bash [npm]
|
||||||
npm install v-calendar@next
|
npm install v-calendar@next date-fns
|
||||||
```
|
```
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
---
|
---
|
||||||
description: Display a list of actions in a dropdown menu.
|
description: Display a list of actions in a dropdown menu.
|
||||||
links:
|
links:
|
||||||
- label: GitHub
|
|
||||||
icon: i-simple-icons-github
|
|
||||||
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/elements/Dropdown.vue
|
|
||||||
- label: Menu
|
- label: Menu
|
||||||
icon: i-simple-icons-headlessui
|
icon: i-simple-icons-headlessui
|
||||||
to: https://headlessui.com/vue/menu
|
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
|
## Usage
|
||||||
|
|||||||
@@ -236,7 +236,7 @@ Use the `#help` slot to set the custom content for help.
|
|||||||
::component-card
|
::component-card
|
||||||
---
|
---
|
||||||
slots:
|
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" />
|
default: <UInput model-value="benjamincanac" placeholder="you@example.com" />
|
||||||
props:
|
props:
|
||||||
label: 'Email'
|
label: 'Email'
|
||||||
|
|||||||
@@ -18,7 +18,11 @@ props:
|
|||||||
::
|
::
|
||||||
|
|
||||||
::callout{icon="i-heroicons-exclamation-triangle"}
|
::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
|
### Dynamic
|
||||||
|
|||||||
@@ -2,12 +2,12 @@
|
|||||||
title: InputMenu
|
title: InputMenu
|
||||||
description: Display an autocomplete input with real-time suggestions.
|
description: Display an autocomplete input with real-time suggestions.
|
||||||
links:
|
links:
|
||||||
- label: GitHub
|
|
||||||
icon: i-simple-icons-github
|
|
||||||
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/forms/InputMenu.vue
|
|
||||||
- label: 'Combobox'
|
- label: 'Combobox'
|
||||||
icon: i-simple-icons-headlessui
|
icon: i-simple-icons-headlessui
|
||||||
to: 'https://headlessui.com/vue/combobox'
|
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
|
## Usage
|
||||||
|
|||||||
@@ -180,13 +180,13 @@ Use the `#leading` slot to set the content of the leading icon.
|
|||||||
::component-card
|
::component-card
|
||||||
---
|
---
|
||||||
slots:
|
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:
|
baseProps:
|
||||||
placeholder: 'Search...'
|
placeholder: 'Search...'
|
||||||
---
|
---
|
||||||
|
|
||||||
#leading
|
#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`
|
### `trailing`
|
||||||
|
|||||||
@@ -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.
|
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
|
## Props
|
||||||
|
|
||||||
:component-props
|
:component-props
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
---
|
---
|
||||||
description: Display a modal within your application.
|
description: Display a modal within your application.
|
||||||
links:
|
links:
|
||||||
- label: GitHub
|
|
||||||
icon: i-simple-icons-github
|
|
||||||
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/overlays/Modal.vue
|
|
||||||
- label: 'Dialog'
|
- label: 'Dialog'
|
||||||
icon: i-simple-icons-headlessui
|
icon: i-simple-icons-headlessui
|
||||||
to: 'https://headlessui.com/vue/dialog'
|
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
|
## Usage
|
||||||
@@ -77,7 +77,15 @@ First of all, add the `Modals` component to your app, preferably inside `app.vue
|
|||||||
|
|
||||||
Then, you can use the `useModal` composable to control your modals within your app.
|
Then, you can use the `useModal` composable to control your modals within your app.
|
||||||
|
|
||||||
:component-example{component="modal-example-composable"}
|
<!-- For prerendering -->
|
||||||
|
:component-example{component="modal-example-component" hiddenCode hiddenPreview }
|
||||||
|
|
||||||
|
::code-group{class="[&>div:last-child>div:first-child]:!rounded-t-none"}
|
||||||
|
:component-example{component="modal-example-composable" label="app.vue" }
|
||||||
|
:component-example{component="modal-example-component" hiddenPreview label="modal.vue" }
|
||||||
|
::
|
||||||
|
|
||||||
|
Additionally, you can close the modal within the modal component by calling `modal.close()`.
|
||||||
|
|
||||||
## Props
|
## Props
|
||||||
|
|
||||||
|
|||||||
@@ -289,8 +289,14 @@ Slots defined in the `<UNotifications />` component are automatically passed dow
|
|||||||
|
|
||||||
## Props
|
## Props
|
||||||
|
|
||||||
:component-props
|
::tabs
|
||||||
|
:component-props{label="Notification"}
|
||||||
|
:component-props{label="Notifications" slug="UNotifications"}
|
||||||
|
::
|
||||||
|
|
||||||
## Config
|
## Config
|
||||||
|
|
||||||
:component-preset
|
::tabs
|
||||||
|
:component-preset{label="Notification"}
|
||||||
|
:component-preset{label="Notifications" slug="Notifications"}
|
||||||
|
::
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
---
|
---
|
||||||
description: Display a non-modal dialog that floats around a trigger element.
|
description: Display a non-modal dialog that floats around a trigger element.
|
||||||
links:
|
links:
|
||||||
- label: GitHub
|
|
||||||
icon: i-simple-icons-github
|
|
||||||
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/overlays/Popover.vue
|
|
||||||
- label: 'Popover'
|
- label: 'Popover'
|
||||||
icon: i-simple-icons-headlessui
|
icon: i-simple-icons-headlessui
|
||||||
to: 'https://headlessui.com/vue/popover'
|
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
|
## Usage
|
||||||
|
|||||||
@@ -2,12 +2,12 @@
|
|||||||
title: SelectMenu
|
title: SelectMenu
|
||||||
description: Display a select menu with advanced features.
|
description: Display a select menu with advanced features.
|
||||||
links:
|
links:
|
||||||
- label: GitHub
|
|
||||||
icon: i-simple-icons-github
|
|
||||||
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/forms/SelectMenu.vue
|
|
||||||
- label: 'Listbox'
|
- label: 'Listbox'
|
||||||
icon: i-simple-icons-headlessui
|
icon: i-simple-icons-headlessui
|
||||||
to: 'https://headlessui.com/vue/listbox'
|
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
|
## 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
|
::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.
|
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"}
|
::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`
|
### `empty`
|
||||||
|
|||||||
@@ -203,7 +203,7 @@ Use the `#leading` slot to set the content of the leading icon.
|
|||||||
::component-card
|
::component-card
|
||||||
---
|
---
|
||||||
slots:
|
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:
|
baseProps:
|
||||||
options:
|
options:
|
||||||
- 'United States'
|
- 'United States'
|
||||||
@@ -213,7 +213,7 @@ baseProps:
|
|||||||
---
|
---
|
||||||
|
|
||||||
#leading
|
#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`
|
### `trailing`
|
||||||
@@ -223,13 +223,13 @@ Use the `#trailing` slot to set the content of the trailing icon.
|
|||||||
::component-card
|
::component-card
|
||||||
---
|
---
|
||||||
slots:
|
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:
|
baseProps:
|
||||||
placeholder: 'Search...'
|
placeholder: 'Search...'
|
||||||
---
|
---
|
||||||
|
|
||||||
#trailing
|
#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
|
## Props
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
---
|
---
|
||||||
description: Display a dialog that slides in from the edge of the screen.
|
description: Display a dialog that slides in from the edge of the screen.
|
||||||
links:
|
links:
|
||||||
- label: GitHub
|
|
||||||
icon: i-simple-icons-github
|
|
||||||
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/overlays/Slideover.vue
|
|
||||||
- label: 'Dialog'
|
- label: 'Dialog'
|
||||||
icon: i-simple-icons-headlessui
|
icon: i-simple-icons-headlessui
|
||||||
to: 'https://headlessui.com/vue/dialog'
|
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
|
## Usage
|
||||||
@@ -33,7 +33,7 @@ Set the `transition` prop to `false` to disable it.
|
|||||||
|
|
||||||
### Prevent close
|
### 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"}
|
:component-example{component="slideover-example-prevent-close"}
|
||||||
|
|
||||||
@@ -53,6 +53,24 @@ defineShortcuts({
|
|||||||
</script>
|
</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
|
## Props
|
||||||
|
|
||||||
:component-props
|
:component-props
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
---
|
---
|
||||||
description: Display a toggle field.
|
description: Display a toggle field.
|
||||||
links:
|
links:
|
||||||
- label: GitHub
|
|
||||||
icon: i-simple-icons-github
|
|
||||||
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/forms/Toggle.vue
|
|
||||||
- label: 'Switch'
|
- label: 'Switch'
|
||||||
icon: i-simple-icons-headlessui
|
icon: i-simple-icons-headlessui
|
||||||
to: 'https://headlessui.com/vue/switch'
|
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
|
## 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
|
### Disabled
|
||||||
|
|
||||||
Use the `disabled` prop to disable the Toggle.
|
Use the `disabled` prop to disable the Toggle.
|
||||||
|
|||||||
@@ -79,10 +79,10 @@ pro:
|
|||||||
title: Upgrade to <span class="text-primary">Nuxt UI Pro</span>
|
title: Upgrade to <span class="text-primary">Nuxt UI Pro</span>
|
||||||
description: 'Nuxt UI Pro is a collection of premium Vue components built on top of Nuxt UI to create beautiful & responsive Nuxt applications in minutes.<br>It includes all primitives to build landing pages, documentations, blogs, dashboards or entire SaaS products.'
|
description: 'Nuxt UI Pro is a collection of premium Vue components built on top of Nuxt UI to create beautiful & responsive Nuxt applications in minutes.<br>It includes all primitives to build landing pages, documentations, blogs, dashboards or entire SaaS products.'
|
||||||
links:
|
links:
|
||||||
- label: View plans
|
- label: Buy now
|
||||||
to: /pro/pricing
|
to: /pro/pricing
|
||||||
color: gray
|
color: black
|
||||||
icon: i-heroicons-credit-card
|
trailingIcon: i-heroicons-arrow-right-20-solid
|
||||||
size: lg
|
size: lg
|
||||||
- label: Explore templates
|
- label: Explore templates
|
||||||
to: /pro/templates
|
to: /pro/templates
|
||||||
@@ -178,7 +178,7 @@ pro:
|
|||||||
</UPageBody>
|
</UPageBody>
|
||||||
|
|
||||||
<template #right>
|
<template #right>
|
||||||
<UDocsToc :links="page.body.toc.links" />
|
<UContentToc :links="page.body.toc.links" />
|
||||||
</template>
|
</template>
|
||||||
</UPage>
|
</UPage>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
<Footer />
|
<Footer />
|
||||||
|
|
||||||
<ClientOnly>
|
<ClientOnly>
|
||||||
<LazyUDocsSearch :files="files" :navigation="navigation" :links="links" :fuse="{ resultLimit: 1000 }" />
|
<LazyUContentSearch :files="files" :navigation="navigation" :links="links" :fuse="{ resultLimit: 42 }" />
|
||||||
</ClientOnly>
|
</ClientOnly>
|
||||||
|
|
||||||
<UNotifications>
|
<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')
|
active: route.path.startsWith('/pro/getting-started') || route.path.startsWith('/pro/components') || route.path.startsWith('/pro/prose')
|
||||||
}, {
|
}, {
|
||||||
label: 'Pricing',
|
label: 'Pricing',
|
||||||
icon: 'i-heroicons-credit-card',
|
icon: 'i-heroicons-ticket',
|
||||||
to: '/pro/pricing'
|
to: '/pro/pricing'
|
||||||
}, {
|
}, {
|
||||||
label: 'Templates',
|
label: 'Templates',
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { createResolver } from '@nuxt/kit'
|
import { createResolver } from '@nuxt/kit'
|
||||||
import colors from 'tailwindcss/colors'
|
import colors from 'tailwindcss/colors'
|
||||||
import module from '../src/module'
|
import module from '../src/module'
|
||||||
import { excludeColors } from '../src/runtime/utils/colors'
|
import { excludeColors } from '../src/colors'
|
||||||
import pkg from '../package.json'
|
import pkg from '../package.json'
|
||||||
|
|
||||||
const { resolve } = createResolver(import.meta.url)
|
const { resolve } = createResolver(import.meta.url)
|
||||||
@@ -17,11 +17,10 @@ export default defineNuxtConfig({
|
|||||||
].filter(Boolean),
|
].filter(Boolean),
|
||||||
modules: [
|
modules: [
|
||||||
'@nuxt/content',
|
'@nuxt/content',
|
||||||
|
'@nuxt/fonts',
|
||||||
'@nuxt/image',
|
'@nuxt/image',
|
||||||
'nuxt-og-image',
|
'nuxt-og-image',
|
||||||
module,
|
module,
|
||||||
'@nuxtjs/fontaine',
|
|
||||||
'@nuxtjs/google-fonts',
|
|
||||||
'@nuxtjs/plausible',
|
'@nuxtjs/plausible',
|
||||||
'@vueuse/nuxt',
|
'@vueuse/nuxt',
|
||||||
'nuxt-component-meta',
|
'nuxt-component-meta',
|
||||||
@@ -75,16 +74,6 @@ export default defineNuxtConfig({
|
|||||||
image: {
|
image: {
|
||||||
provider: 'ipx'
|
provider: 'ipx'
|
||||||
},
|
},
|
||||||
fontMetrics: {
|
|
||||||
fonts: ['DM Sans']
|
|
||||||
},
|
|
||||||
googleFonts: {
|
|
||||||
display: 'swap',
|
|
||||||
download: true,
|
|
||||||
families: {
|
|
||||||
'DM+Sans': [400, 500, 600, 700]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
nitro: {
|
nitro: {
|
||||||
prerender: {
|
prerender: {
|
||||||
routes: [
|
routes: [
|
||||||
|
|||||||
@@ -7,30 +7,28 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@iconify-json/heroicons": "^1.1.20",
|
"@iconify-json/heroicons": "^1.1.20",
|
||||||
"@iconify-json/simple-icons": "^1.1.92",
|
"@iconify-json/simple-icons": "^1.1.99",
|
||||||
"@nuxt/content": "^2.12.0",
|
"@nuxt/content": "^2.12.1",
|
||||||
"@nuxt/devtools": "^1.0.8",
|
"@nuxt/eslint-config": "^0.3.6",
|
||||||
"@nuxt/eslint-config": "^0.2.0",
|
"@nuxt/fonts": "^0.6.1",
|
||||||
"@nuxt/image": "^1.3.0",
|
"@nuxt/image": "^1.5.0",
|
||||||
"@nuxt/ui-pro": "npm:@nuxt/ui-pro-edge@0.7.5-28475621.09eb8fa",
|
"@nuxt/ui-pro": "npm:@nuxt/ui-pro-edge@1.1.0-28546155.4b9828b",
|
||||||
"@nuxtjs/fontaine": "^0.4.1",
|
"@nuxtjs/plausible": "^1.0.0",
|
||||||
"@nuxtjs/google-fonts": "^3.1.3",
|
"@octokit/rest": "^20.1.0",
|
||||||
"@nuxtjs/plausible": "^0.2.4",
|
"@vueuse/nuxt": "^10.9.0",
|
||||||
"@octokit/rest": "^20.0.2",
|
"date-fns": "^3.6.0",
|
||||||
"@vueuse/nuxt": "^10.8.0",
|
"eslint": "^8.57.0",
|
||||||
"date-fns": "^3.3.1",
|
"joi": "^17.12.3",
|
||||||
"eslint": "^8.56.0",
|
"nuxt": "^3.11.2",
|
||||||
"joi": "^17.12.1",
|
|
||||||
"nuxt": "^3.10.2",
|
|
||||||
"nuxt-cloudflare-analytics": "^1.0.8",
|
"nuxt-cloudflare-analytics": "^1.0.8",
|
||||||
"nuxt-component-meta": "^0.6.3",
|
"nuxt-component-meta": "^0.6.3",
|
||||||
"nuxt-og-image": "^2.2.4",
|
"nuxt-og-image": "^2.2.4",
|
||||||
"prettier": "^3.2.5",
|
"prettier": "^3.2.5",
|
||||||
"typescript": "^5.3.3",
|
"typescript": "^5.4.5",
|
||||||
"ufo": "^1.4.0",
|
"ufo": "^1.5.3",
|
||||||
"v-calendar": "^3.1.2",
|
"v-calendar": "^3.1.2",
|
||||||
"valibot": "^0.29.0",
|
"valibot": "^0.30.0",
|
||||||
"yup": "^1.3.3",
|
"yup": "^1.4.0",
|
||||||
"zod": "^3.22.4"
|
"zod": "^3.22.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,11 +7,11 @@
|
|||||||
|
|
||||||
<hr v-if="surround?.length">
|
<hr v-if="surround?.length">
|
||||||
|
|
||||||
<UDocsSurround :surround="surround" />
|
<UContentSurround :surround="surround" />
|
||||||
</UPageBody>
|
</UPageBody>
|
||||||
|
|
||||||
<template v-if="page?.body?.toc?.links?.length" #right>
|
<template v-if="page?.body?.toc?.links?.length" #right>
|
||||||
<UDocsToc :links="page.body.toc.links">
|
<UContentToc :links="page.body.toc.links">
|
||||||
<template #bottom>
|
<template #bottom>
|
||||||
<div class="hidden lg:block space-y-6 !mt-6">
|
<div class="hidden lg:block space-y-6 !mt-6">
|
||||||
<UDivider v-if="page.body?.toc?.links?.length" type="dashed" />
|
<UDivider v-if="page.body?.toc?.links?.length" type="dashed" />
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</UDocsToc>
|
</UContentToc>
|
||||||
</template>
|
</template>
|
||||||
</UPage>
|
</UPage>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<ULandingHero :ui="{ base: 'relative z-[1]', container: 'max-w-4xl' }" class="mb-[calc(var(--header-height)*2)]">
|
<ULandingHero :ui="{ base: 'relative z-[1]', container: 'max-w-4xl' }" class="mb-[calc(var(--header-height)*2)]">
|
||||||
<template #headline>
|
<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">
|
<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" />
|
<span class="absolute inset-0" aria-hidden="true" />
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
|
|
||||||
@@ -31,10 +31,11 @@
|
|||||||
readonly
|
readonly
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
icon="i-heroicons-command-line"
|
icon="i-heroicons-command-line"
|
||||||
input-class="select-none"
|
class="w-72"
|
||||||
|
input-class="focus:ring-1 focus:ring-gray-300 dark:focus:ring-gray-700"
|
||||||
aria-label="Install @nuxt/ui"
|
aria-label="Install @nuxt/ui"
|
||||||
size="lg"
|
size="lg"
|
||||||
:ui="{ base: 'disabled:cursor-default', icon: { trailing: { pointer: '' } } }"
|
:ui="{ icon: { trailing: { pointer: '' } } }"
|
||||||
>
|
>
|
||||||
<template #trailing>
|
<template #trailing>
|
||||||
<UButton
|
<UButton
|
||||||
@@ -90,6 +91,7 @@
|
|||||||
:width="card.image.width"
|
:width="card.image.width"
|
||||||
:height="card.image.height"
|
:height="card.image.height"
|
||||||
:alt="card.title"
|
:alt="card.title"
|
||||||
|
loading="lazy"
|
||||||
class="object-cover w-full"
|
class="object-cover w-full"
|
||||||
/>
|
/>
|
||||||
</ULandingCard>
|
</ULandingCard>
|
||||||
@@ -164,7 +166,13 @@
|
|||||||
</ULandingSection>
|
</ULandingSection>
|
||||||
|
|
||||||
<template v-if="navigation.find(item => item._path === '/pro')">
|
<template v-if="navigation.find(item => item._path === '/pro')">
|
||||||
<ULandingHero id="pro" :links="page.pro.links" :ui="{ title: 'sm:text-6xl' }">
|
<div class="relative">
|
||||||
|
<UDivider class="absolute inset-x-0" />
|
||||||
|
|
||||||
|
<div class="w-full relative overflow-hidden h-px bg-gradient-to-r from-gray-800 via-primary-400 to-gray-800 max-w-5xl mx-auto" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ULandingHero id="pro" :links="page.pro.links" :ui="{ title: 'sm:text-6xl' }" class="bg-gradient-to-b from-gray-50 dark:from-gray-950/50 to-white dark:to-gray-900 relative">
|
||||||
<template #title>
|
<template #title>
|
||||||
<span v-html="page.pro.title" />
|
<span v-html="page.pro.title" />
|
||||||
</template>
|
</template>
|
||||||
@@ -173,11 +181,13 @@
|
|||||||
<span v-html="page.pro.description" />
|
<span v-html="page.pro.description" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<video poster="https://res.cloudinary.com/nuxt/video/upload/so_14.8/v1708511800/ui-pro/video-nuxt-ui-pro_kwfbdh.jpg" controls class="rounded-lg">
|
<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">
|
||||||
<source src="https://res.cloudinary.com/nuxt/video/upload/v1708511800/ui-pro/video-nuxt-ui-pro_kwfbdh.webm" type="video/webm">
|
<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.mp4" type="video/mp4">
|
<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.ogg" type="video/ogg">
|
<source src="https://res.cloudinary.com/nuxt/video/upload/v1708511800/ui-pro/video-nuxt-ui-pro_kwfbdh.mp4" type="video/mp4">
|
||||||
</video>
|
<source src="https://res.cloudinary.com/nuxt/video/upload/v1708511800/ui-pro/video-nuxt-ui-pro_kwfbdh.ogg" type="video/ogg">
|
||||||
|
</video>
|
||||||
|
</div>
|
||||||
</ULandingHero>
|
</ULandingHero>
|
||||||
|
|
||||||
<ULandingSection v-for="(section, index) in page.pro.sections" :key="index" v-bind="section" class="!pt-0">
|
<ULandingSection v-for="(section, index) in page.pro.sections" :key="index" v-bind="section" class="!pt-0">
|
||||||
@@ -246,7 +256,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #aside-top>
|
<template #aside-top>
|
||||||
<UDocsSearchButton size="md" class="w-full" />
|
<UContentSearchButton size="md" class="w-full" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #aside-default>
|
<template #aside-default>
|
||||||
@@ -274,8 +284,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #docs-surround>
|
<template #content-surround>
|
||||||
<UDocsSurround
|
<UContentSurround
|
||||||
:surround="(surround as unknown as ParsedContent[])"
|
:surround="(surround as unknown as ParsedContent[])"
|
||||||
class="w-full gap-4"
|
class="w-full gap-4"
|
||||||
:ui="{
|
:ui="{
|
||||||
@@ -292,9 +302,9 @@
|
|||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #docs-toc>
|
<template #content-toc>
|
||||||
<div class="absolute top-0 left-0 right-0 space-y-3">
|
<div class="absolute top-0 left-0 right-0 space-y-3">
|
||||||
<UDocsToc :links="toc" class="bg-transparent relative max-h-full overflow-hidden top-0" :ui="({ container: { base: '!pt-0 !pb-4' } } as any)" />
|
<UContentToc :links="toc" class="bg-transparent relative max-h-full overflow-hidden top-0" :ui="({ container: { base: '!pt-0 !pb-4' } } as any)" />
|
||||||
|
|
||||||
<UDivider type="dashed" :ui="{ border: { base: 'border-gray-800/10 dark:border-gray-200/10' } }" />
|
<UDivider type="dashed" :ui="{ border: { base: 'border-gray-800/10 dark:border-gray-200/10' } }" />
|
||||||
|
|
||||||
@@ -372,7 +382,7 @@
|
|||||||
<template #description>
|
<template #description>
|
||||||
<span v-html="page.pro.landing?.description" />
|
<span v-html="page.pro.landing?.description" />
|
||||||
</template>
|
</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.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.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">
|
<source src="https://res.cloudinary.com/nuxt/video/upload/v1698923423/ui-pro/nuxt-ui-pro-landing-demo_yrh6nr.ogg" type="video/ogg">
|
||||||
@@ -385,7 +395,7 @@
|
|||||||
<template #description>
|
<template #description>
|
||||||
<span v-html="page.pro.docs?.description" />
|
<span v-html="page.pro.docs?.description" />
|
||||||
</template>
|
</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.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.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">
|
<source src="https://res.cloudinary.com/nuxt/video/upload/v1698923398/ui-pro/nuxt-ui-pro-docs-demo_jm6ubr.ogg" type="video/ogg">
|
||||||
@@ -426,7 +436,7 @@ useSeoMeta({
|
|||||||
twitterImage: 'https://ui.nuxt.com/social-card.png'
|
twitterImage: 'https://ui.nuxt.com/social-card.png'
|
||||||
})
|
})
|
||||||
|
|
||||||
const source = ref('npm i @nuxt/ui')
|
const source = ref('npx nuxi@latest module add ui')
|
||||||
const sectionRef = ref()
|
const sectionRef = ref()
|
||||||
const demoRef = ref()
|
const demoRef = ref()
|
||||||
const start = ref(0)
|
const start = ref(0)
|
||||||
@@ -609,11 +619,11 @@ const docsBlocks = computed(() => [isAfterStep(steps.docs) && {
|
|||||||
slot: 'page-body',
|
slot: 'page-body',
|
||||||
class: 'inset-x-4 top-4 justify-start'
|
class: 'inset-x-4 top-4 justify-start'
|
||||||
}, isAfterStep(steps.docs + 12) ? {
|
}, isAfterStep(steps.docs + 12) ? {
|
||||||
slot: 'docs-surround',
|
slot: 'content-surround',
|
||||||
class: 'bottom-4 inset-x-4 h-28'
|
class: 'bottom-4 inset-x-4 h-28'
|
||||||
} : {
|
} : {
|
||||||
name: 'UDocsSurround',
|
name: 'UContentSurround',
|
||||||
to: '/pro/components/docs-surround',
|
to: '/pro/components/content-surround',
|
||||||
class: 'bottom-4 inset-x-4 h-28',
|
class: 'bottom-4 inset-x-4 h-28',
|
||||||
inactive: false
|
inactive: false
|
||||||
}]
|
}]
|
||||||
@@ -621,12 +631,12 @@ const docsBlocks = computed(() => [isAfterStep(steps.docs) && {
|
|||||||
name: '#default',
|
name: '#default',
|
||||||
class: 'left-4 right-72 inset-y-4'
|
class: 'left-4 right-72 inset-y-4'
|
||||||
}]), isAfterStep(steps.docs + 13) ? {
|
}]), isAfterStep(steps.docs + 13) ? {
|
||||||
name: 'UDocsToc',
|
name: 'UContentToc',
|
||||||
to: '/pro/components/docs-toc',
|
to: '/pro/components/content-toc',
|
||||||
class: 'right-4 inset-y-4 w-64',
|
class: 'right-4 inset-y-4 w-64',
|
||||||
inactive: isAfterStep(steps.docs + 14),
|
inactive: isAfterStep(steps.docs + 14),
|
||||||
children: [{
|
children: [{
|
||||||
slot: 'docs-toc',
|
slot: 'content-toc',
|
||||||
class: 'inset-4 overflow-y-auto'
|
class: 'inset-4 overflow-y-auto'
|
||||||
}]
|
}]
|
||||||
} : {
|
} : {
|
||||||
@@ -755,10 +765,12 @@ const navigationLinks = [{
|
|||||||
|
|
||||||
const surround = [{
|
const surround = [{
|
||||||
title: 'Introduction',
|
title: 'Introduction',
|
||||||
description: 'Fully styled and customizable components for Nuxt.'
|
description: 'Fully styled and customizable components for Nuxt.',
|
||||||
|
_path: '/'
|
||||||
}, {
|
}, {
|
||||||
title: 'Theming',
|
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 = `
|
const md = `
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ function createPrettierWorkerApi (worker: Worker): SimplePrettier {
|
|||||||
export default defineNuxtPlugin({
|
export default defineNuxtPlugin({
|
||||||
async setup () {
|
async setup () {
|
||||||
let prettier: SimplePrettier
|
let prettier: SimplePrettier
|
||||||
if (process.server) {
|
if (import.meta.server) {
|
||||||
const prettierModule = await import('prettier')
|
const prettierModule = await import('prettier')
|
||||||
prettier = {
|
prettier = {
|
||||||
format (source, options = {
|
format (source, options = {
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export default defineNuxtPlugin({
|
|||||||
`
|
`
|
||||||
})
|
})
|
||||||
|
|
||||||
if (process.client) {
|
if (import.meta.client) {
|
||||||
watch(root, () => {
|
watch(root, () => {
|
||||||
window.localStorage.setItem('nuxt-ui-root', root.value)
|
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.primary = window.localStorage.getItem('nuxt-ui-primary') || appConfig.ui.primary
|
||||||
appConfig.ui.gray = window.localStorage.getItem('nuxt-ui-gray') || appConfig.ui.gray
|
appConfig.ui.gray = window.localStorage.getItem('nuxt-ui-gray') || appConfig.ui.gray
|
||||||
}
|
}
|
||||||
if (process.server) {
|
if (import.meta.server) {
|
||||||
useHead({
|
useHead({
|
||||||
script: [
|
script: [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ export default <Partial<Config>>{
|
|||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
fontFamily: {
|
fontFamily: {
|
||||||
sans: ['DM Sans', 'DM Sans fallback', ...defaultTheme.fontFamily.sans]
|
sans: ['DM Sans', ...defaultTheme.fontFamily.sans]
|
||||||
},
|
},
|
||||||
colors: {
|
colors: {
|
||||||
green: {
|
green: {
|
||||||
|
|||||||
56
package.json
56
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@nuxt/ui",
|
"name": "@nuxt/ui",
|
||||||
"version": "2.14.0",
|
"version": "2.15.2",
|
||||||
"repository": "nuxt/ui",
|
"repository": "nuxt/ui",
|
||||||
"homepage": "https://ui.nuxt.com",
|
"homepage": "https://ui.nuxt.com",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@@ -37,51 +37,51 @@
|
|||||||
"@headlessui/tailwindcss": "^0.2.0",
|
"@headlessui/tailwindcss": "^0.2.0",
|
||||||
"@headlessui/vue": "^1.7.19",
|
"@headlessui/vue": "^1.7.19",
|
||||||
"@iconify-json/heroicons": "^1.1.20",
|
"@iconify-json/heroicons": "^1.1.20",
|
||||||
"@nuxt/kit": "^3.10.2",
|
"@nuxt/kit": "^3.11.2",
|
||||||
"@nuxtjs/color-mode": "^3.3.2",
|
"@nuxtjs/color-mode": "^3.4.0",
|
||||||
"@nuxtjs/tailwindcss": "^6.11.4",
|
"@nuxtjs/tailwindcss": "^6.11.4",
|
||||||
"@popperjs/core": "^2.11.8",
|
"@popperjs/core": "^2.11.8",
|
||||||
"@tailwindcss/aspect-ratio": "^0.4.2",
|
"@tailwindcss/aspect-ratio": "^0.4.2",
|
||||||
"@tailwindcss/container-queries": "^0.1.1",
|
"@tailwindcss/container-queries": "^0.1.1",
|
||||||
"@tailwindcss/forms": "^0.5.7",
|
"@tailwindcss/forms": "^0.5.7",
|
||||||
"@tailwindcss/typography": "^0.5.10",
|
"@tailwindcss/typography": "^0.5.12",
|
||||||
"@vueuse/core": "^10.8.0",
|
"@vueuse/core": "^10.9.0",
|
||||||
"@vueuse/integrations": "^10.8.0",
|
"@vueuse/integrations": "^10.9.0",
|
||||||
"@vueuse/math": "^10.8.0",
|
"@vueuse/math": "^10.9.0",
|
||||||
"defu": "^6.1.4",
|
"defu": "^6.1.4",
|
||||||
"fuse.js": "^6.6.2",
|
"fuse.js": "^6.6.2",
|
||||||
"nuxt-icon": "^0.6.8",
|
"nuxt-icon": "^0.6.10",
|
||||||
"ohash": "^1.1.3",
|
"ohash": "^1.1.3",
|
||||||
"pathe": "^1.1.2",
|
"pathe": "^1.1.2",
|
||||||
"scule": "^1.3.0",
|
"scule": "^1.3.0",
|
||||||
"tailwind-merge": "^2.2.1",
|
"tailwind-merge": "^2.2.2",
|
||||||
"tailwindcss": "^3.4.1"
|
"tailwindcss": "^3.4.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@nuxt/eslint-config": "^0.2.0",
|
"@nuxt/eslint-config": "^0.3.6",
|
||||||
"@nuxt/module-builder": "^0.5.5",
|
"@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",
|
"@release-it/conventional-changelog": "^8.0.1",
|
||||||
"@vue/test-utils": "^2.4.4",
|
"@vue/test-utils": "^2.4.5",
|
||||||
"eslint": "^8.56.0",
|
"eslint": "^8.57.0",
|
||||||
"happy-dom": "^13.3.8",
|
"happy-dom": "^14.7.1",
|
||||||
"joi": "^17.12.1",
|
"joi": "^17.12.3",
|
||||||
"nuxt": "^3.10.2",
|
"nuxt": "^3.11.2",
|
||||||
"release-it": "^17.1.1",
|
"release-it": "^17.2.0",
|
||||||
"typescript": "^5.3.3",
|
"typescript": "^5.4.5",
|
||||||
"unbuild": "^2.0.0",
|
"unbuild": "^2.0.0",
|
||||||
"valibot": "^0.29.0",
|
"valibot": "^0.30.0",
|
||||||
"vitest": "^1.3.1",
|
"vitest": "^1.5.0",
|
||||||
"vitest-environment-nuxt": "^1.0.0",
|
"vitest-environment-nuxt": "^1.0.0",
|
||||||
"vue-tsc": "^1.8.27",
|
"vue-tsc": "^2.0.13",
|
||||||
"yup": "^1.3.3",
|
"yup": "^1.4.0",
|
||||||
"zod": "^3.22.4"
|
"zod": "^3.22.4"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"@nuxt/kit": "^3.10.2",
|
"@nuxt/kit": "^3.11.2",
|
||||||
"@nuxt/schema": "3.10.2",
|
"@nuxt/schema": "3.11.2",
|
||||||
"tailwindcss": "3.4.1",
|
"tailwindcss": "^3.4.3",
|
||||||
"@headlessui/vue": "^1.7.19",
|
"@headlessui/vue": "1.7.19",
|
||||||
"vue": "3.4.19"
|
"vue": "3.4.21"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
imports.autoImport=true
|
|
||||||
4991
pnpm-lock.yaml
generated
4991
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"extends": [
|
"extends": [
|
||||||
"@nuxtjs"
|
"github>nuxt/renovate-config-nuxt"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { omit } from './lodash'
|
import { omit } from './runtime/utils/lodash'
|
||||||
import { kebabCase, camelCase, upperFirst } from 'scule'
|
import { kebabCase, camelCase, upperFirst } from 'scule'
|
||||||
|
|
||||||
const colorsToExclude = [
|
const colorsToExclude = [
|
||||||
116
src/module.ts
116
src/module.ts
@@ -1,10 +1,22 @@
|
|||||||
|
import { createRequire } from 'node:module'
|
||||||
import { defineNuxtModule, installModule, addComponentsDir, addImportsDir, createResolver, addPlugin } from '@nuxt/kit'
|
import { defineNuxtModule, installModule, addComponentsDir, addImportsDir, createResolver, addPlugin } from '@nuxt/kit'
|
||||||
import type { CollectionNames, IconsPluginOptions } from '@egoist/tailwindcss-icons'
|
import { defaultExtractor as createDefaultExtractor } from 'tailwindcss/lib/lib/defaultExtractor.js'
|
||||||
|
import { iconsPlugin, getIconCollections, type CollectionNames, type IconsPluginOptions } from '@egoist/tailwindcss-icons'
|
||||||
import { name, version } from '../package.json'
|
import { name, version } from '../package.json'
|
||||||
import createTemplates from './templates'
|
import createTemplates from './templates'
|
||||||
|
import { generateSafelist, excludeColors, customSafelistExtractor } from './colors'
|
||||||
import * as config from './runtime/ui.config'
|
import * as config from './runtime/ui.config'
|
||||||
import type { DeepPartial, Strategy } from './runtime/types/utils'
|
import type { DeepPartial, Strategy } from './runtime/types/utils'
|
||||||
import installTailwind from './tailwind'
|
|
||||||
|
const defaultExtractor = createDefaultExtractor({ tailwindConfig: { separator: ':' } })
|
||||||
|
const _require = createRequire(import.meta.url)
|
||||||
|
const defaultColors = _require('tailwindcss/colors.js')
|
||||||
|
|
||||||
|
delete defaultColors.lightBlue
|
||||||
|
delete defaultColors.warmGray
|
||||||
|
delete defaultColors.trueGray
|
||||||
|
delete defaultColors.coolGray
|
||||||
|
delete defaultColors.blueGray
|
||||||
|
|
||||||
type UI = {
|
type UI = {
|
||||||
primary?: string
|
primary?: string
|
||||||
@@ -76,13 +88,107 @@ export default defineNuxtModule<ModuleOptions>({
|
|||||||
nuxt.options.css.push(resolve(runtimeDir, 'ui.css'))
|
nuxt.options.css.push(resolve(runtimeDir, 'ui.css'))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
nuxt.hook('tailwindcss:config', function (tailwindConfig) {
|
||||||
|
tailwindConfig.theme = tailwindConfig.theme || {}
|
||||||
|
tailwindConfig.theme.extend = tailwindConfig.theme.extend || {}
|
||||||
|
tailwindConfig.theme.extend.colors = tailwindConfig.theme.extend.colors || {}
|
||||||
|
|
||||||
|
const globalColors: any = {
|
||||||
|
...(tailwindConfig.theme.colors || defaultColors),
|
||||||
|
...tailwindConfig.theme.extend?.colors
|
||||||
|
}
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
globalColors.primary = tailwindConfig.theme.extend.colors.primary = {
|
||||||
|
50: 'rgb(var(--color-primary-50) / <alpha-value>)',
|
||||||
|
100: 'rgb(var(--color-primary-100) / <alpha-value>)',
|
||||||
|
200: 'rgb(var(--color-primary-200) / <alpha-value>)',
|
||||||
|
300: 'rgb(var(--color-primary-300) / <alpha-value>)',
|
||||||
|
400: 'rgb(var(--color-primary-400) / <alpha-value>)',
|
||||||
|
500: 'rgb(var(--color-primary-500) / <alpha-value>)',
|
||||||
|
600: 'rgb(var(--color-primary-600) / <alpha-value>)',
|
||||||
|
700: 'rgb(var(--color-primary-700) / <alpha-value>)',
|
||||||
|
800: 'rgb(var(--color-primary-800) / <alpha-value>)',
|
||||||
|
900: 'rgb(var(--color-primary-900) / <alpha-value>)',
|
||||||
|
950: 'rgb(var(--color-primary-950) / <alpha-value>)',
|
||||||
|
DEFAULT: 'rgb(var(--color-primary-DEFAULT) / <alpha-value>)'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (globalColors.gray) {
|
||||||
|
// @ts-ignore
|
||||||
|
globalColors.cool = tailwindConfig.theme.extend.colors.cool = defaultColors.gray
|
||||||
|
}
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
globalColors.gray = tailwindConfig.theme.extend.colors.gray = {
|
||||||
|
50: 'rgb(var(--color-gray-50) / <alpha-value>)',
|
||||||
|
100: 'rgb(var(--color-gray-100) / <alpha-value>)',
|
||||||
|
200: 'rgb(var(--color-gray-200) / <alpha-value>)',
|
||||||
|
300: 'rgb(var(--color-gray-300) / <alpha-value>)',
|
||||||
|
400: 'rgb(var(--color-gray-400) / <alpha-value>)',
|
||||||
|
500: 'rgb(var(--color-gray-500) / <alpha-value>)',
|
||||||
|
600: 'rgb(var(--color-gray-600) / <alpha-value>)',
|
||||||
|
700: 'rgb(var(--color-gray-700) / <alpha-value>)',
|
||||||
|
800: 'rgb(var(--color-gray-800) / <alpha-value>)',
|
||||||
|
900: 'rgb(var(--color-gray-900) / <alpha-value>)',
|
||||||
|
950: 'rgb(var(--color-gray-950) / <alpha-value>)'
|
||||||
|
}
|
||||||
|
|
||||||
|
const colors = excludeColors(globalColors)
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
nuxt.options.appConfig.ui = {
|
||||||
|
primary: 'green',
|
||||||
|
gray: 'cool',
|
||||||
|
colors,
|
||||||
|
strategy: 'merge'
|
||||||
|
}
|
||||||
|
|
||||||
|
tailwindConfig.safelist = tailwindConfig.safelist || []
|
||||||
|
tailwindConfig.safelist.push(...generateSafelist(options.safelistColors || [], colors))
|
||||||
|
})
|
||||||
|
|
||||||
createTemplates(nuxt)
|
createTemplates(nuxt)
|
||||||
|
|
||||||
// Modules
|
// Modules
|
||||||
|
|
||||||
await installModule('nuxt-icon')
|
await installModule('nuxt-icon')
|
||||||
await installModule('@nuxtjs/color-mode', { classSuffix: '' })
|
await installModule('@nuxtjs/color-mode', { classSuffix: '' })
|
||||||
await installTailwind(options, nuxt, { resolve, runtimeDir })
|
await installModule('@nuxtjs/tailwindcss', {
|
||||||
|
exposeConfig: true,
|
||||||
|
config: {
|
||||||
|
darkMode: 'class',
|
||||||
|
plugins: [
|
||||||
|
require('@tailwindcss/forms')({ strategy: 'class' }),
|
||||||
|
require('@tailwindcss/aspect-ratio'),
|
||||||
|
require('@tailwindcss/typography'),
|
||||||
|
require('@tailwindcss/container-queries'),
|
||||||
|
require('@headlessui/tailwindcss'),
|
||||||
|
iconsPlugin(Array.isArray(options.icons) || options.icons === 'all' ? { collections: getIconCollections(options.icons) } : typeof options.icons === 'object' ? options.icons as IconsPluginOptions : {})
|
||||||
|
],
|
||||||
|
content: {
|
||||||
|
files: [
|
||||||
|
resolve(runtimeDir, 'components/**/*.{vue,mjs,ts}'),
|
||||||
|
resolve(runtimeDir, 'ui.config/**/*.{mjs,js,ts}')
|
||||||
|
],
|
||||||
|
transform: {
|
||||||
|
vue: (content) => {
|
||||||
|
return content.replaceAll(/(?:\r\n|\r|\n)/g, ' ')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
extract: {
|
||||||
|
vue: (content) => {
|
||||||
|
return [
|
||||||
|
...defaultExtractor(content),
|
||||||
|
// @ts-ignore
|
||||||
|
...customSafelistExtractor(options.prefix, content, nuxt.options.appConfig.ui.colors, options.safelistColors)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// Plugins
|
// Plugins
|
||||||
|
|
||||||
@@ -94,6 +200,10 @@ export default defineNuxtModule<ModuleOptions>({
|
|||||||
src: resolve(runtimeDir, 'plugins', 'modals')
|
src: resolve(runtimeDir, 'plugins', 'modals')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
addPlugin({
|
||||||
|
src: resolve(runtimeDir, 'plugins', 'slideovers')
|
||||||
|
})
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
|
|
||||||
addComponentsDir({
|
addComponentsDir({
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<thead :class="ui.thead">
|
<thead :class="ui.thead">
|
||||||
<tr :class="ui.tr.base">
|
<tr :class="ui.tr.base">
|
||||||
<th v-if="modelValue" scope="col" :class="ui.checkbox.padding">
|
<th v-if="modelValue" scope="col" :class="ui.checkbox.padding">
|
||||||
<UCheckbox :model-value="indeterminate || selected.length === rows.length" :indeterminate="indeterminate" aria-label="Select all" @change="onChange" />
|
<UCheckbox :model-value="indeterminate || selected.length === rows.length" :indeterminate="indeterminate" v-bind="ui.default.checkbox" aria-label="Select all" @change="onChange" />
|
||||||
</th>
|
</th>
|
||||||
|
|
||||||
<th v-for="(column, index) in columns" :key="index" scope="col" :class="[ui.th.base, ui.th.padding, ui.th.color, ui.th.font, ui.th.size, column.class]">
|
<th v-for="(column, index) in columns" :key="index" scope="col" :class="[ui.th.base, ui.th.padding, ui.th.color, ui.th.font, ui.th.size, column.class]">
|
||||||
@@ -57,7 +57,7 @@
|
|||||||
<template v-else>
|
<template v-else>
|
||||||
<tr v-for="(row, index) in rows" :key="index" :class="[ui.tr.base, isSelected(row) && ui.tr.selected, $attrs.onSelect && ui.tr.active, row?.class]" @click="() => onSelect(row)">
|
<tr v-for="(row, index) in rows" :key="index" :class="[ui.tr.base, isSelected(row) && ui.tr.selected, $attrs.onSelect && ui.tr.active, row?.class]" @click="() => onSelect(row)">
|
||||||
<td v-if="modelValue" :class="ui.checkbox.padding">
|
<td v-if="modelValue" :class="ui.checkbox.padding">
|
||||||
<UCheckbox v-model="selected" :value="row" aria-label="Select row" @click.stop />
|
<UCheckbox v-model="selected" :value="row" v-bind="ui.default.checkbox" aria-label="Select row" @click.stop />
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td v-for="(column, subIndex) in columns" :key="subIndex" :class="[ui.td.base, ui.td.padding, ui.td.color, ui.td.font, ui.td.size, row[column.key]?.class]">
|
<td v-for="(column, subIndex) in columns" :key="subIndex" :class="[ui.td.base, ui.td.padding, ui.td.color, ui.td.font, ui.td.size, row[column.key]?.class]">
|
||||||
@@ -280,8 +280,8 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function onChange (event: any) {
|
function onChange (checked: boolean) {
|
||||||
if (event.target.checked) {
|
if (checked) {
|
||||||
selectAllRows()
|
selectAllRows()
|
||||||
} else {
|
} else {
|
||||||
selected.value = []
|
selected.value = []
|
||||||
|
|||||||
@@ -39,20 +39,34 @@
|
|||||||
@before-leave="onBeforeLeave"
|
@before-leave="onBeforeLeave"
|
||||||
@leave="onLeave"
|
@leave="onLeave"
|
||||||
>
|
>
|
||||||
<div v-show="open">
|
<HDisclosurePanel
|
||||||
<HDisclosurePanel :class="[ui.item.base, ui.item.size, ui.item.color, ui.item.padding]" static>
|
v-if="unmount"
|
||||||
<slot :name="item.slot || 'item'" :item="item" :index="index" :open="open" :close="close">
|
:class="[ui.item.base, ui.item.size, ui.item.color, ui.item.padding]"
|
||||||
{{ item.content }}
|
unmount
|
||||||
</slot>
|
>
|
||||||
</HDisclosurePanel>
|
<slot :name="item.slot || 'item'" :item="item" :index="index" :open="open" :close="close">
|
||||||
</div>
|
{{ 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>
|
</Transition>
|
||||||
</HDisclosure>
|
</HDisclosure>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { ref, computed, toRef, defineComponent } from 'vue'
|
import { ref, computed, toRef, defineComponent, watch } from 'vue'
|
||||||
import type { PropType } from 'vue'
|
import type { PropType } from 'vue'
|
||||||
import { Disclosure as HDisclosure, DisclosureButton as HDisclosureButton, DisclosurePanel as HDisclosurePanel, provideUseId } from '@headlessui/vue'
|
import { Disclosure as HDisclosure, DisclosureButton as HDisclosureButton, DisclosurePanel as HDisclosurePanel, provideUseId } from '@headlessui/vue'
|
||||||
import UIcon from '../elements/Icon.vue'
|
import UIcon from '../elements/Icon.vue'
|
||||||
@@ -91,6 +105,10 @@ export default defineComponent({
|
|||||||
type: String,
|
type: String,
|
||||||
default: () => config.default.openIcon
|
default: () => config.default.openIcon
|
||||||
},
|
},
|
||||||
|
unmount: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
closeIcon: {
|
closeIcon: {
|
||||||
type: String,
|
type: String,
|
||||||
default: () => config.default.closeIcon
|
default: () => config.default.closeIcon
|
||||||
@@ -108,12 +126,25 @@ export default defineComponent({
|
|||||||
default: () => ({})
|
default: () => ({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup (props) {
|
emits: ['open'],
|
||||||
|
setup (props, { emit }) {
|
||||||
const { ui, attrs } = useUI('accordion', toRef(props, 'ui'), config, toRef(props, 'class'))
|
const { ui, attrs } = useUI('accordion', toRef(props, 'ui'), config, toRef(props, 'class'))
|
||||||
|
|
||||||
const uiButton = computed<typeof configButton>(() => configButton)
|
const uiButton = computed<typeof configButton>(() => configButton)
|
||||||
|
|
||||||
const buttonRefs = ref<{ open: boolean, close: (e: EventTarget) => {} }[]>([])
|
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) {
|
function closeOthers (currentIndex: number, e: Event) {
|
||||||
if (!props.items[currentIndex].closeOthers && props.multiple) {
|
if (!props.items[currentIndex].closeOthers && props.multiple) {
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :class="alertClass" v-bind="attrs">
|
<div :class="alertClass" v-bind="attrs">
|
||||||
<div class="flex" :class="[ui.gap, { 'items-start': (description || $slots.description), 'items-center': !description && !$slots.description }]">
|
<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" />
|
<slot name="icon" :icon="icon">
|
||||||
<UAvatar v-if="avatar" v-bind="{ size: ui.avatar.size, ...avatar }" :class="ui.avatar.base" />
|
<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">
|
<div :class="ui.inner">
|
||||||
<p v-if="(title || $slots.title)" :class="ui.title">
|
<p v-if="(title || $slots.title)" :class="ui.title">
|
||||||
@@ -10,7 +14,7 @@
|
|||||||
{{ title }}
|
{{ title }}
|
||||||
</slot>
|
</slot>
|
||||||
</p>
|
</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">
|
<slot name="description" :description="description">
|
||||||
{{ description }}
|
{{ description }}
|
||||||
</slot>
|
</slot>
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<span :class="wrapperClass">
|
<span :class="wrapperClass">
|
||||||
<img
|
<component
|
||||||
|
:is="as"
|
||||||
v-if="url && !error"
|
v-if="url && !error"
|
||||||
:class="imgClass"
|
:class="imgClass"
|
||||||
:alt="alt"
|
:alt="alt"
|
||||||
:src="url"
|
:src="url"
|
||||||
v-bind="attrs"
|
v-bind="attrs"
|
||||||
@error="onError"
|
@error="onError"
|
||||||
>
|
/>
|
||||||
<span v-else-if="text" :class="ui.text">{{ text }}</span>
|
<span v-else-if="text" :class="ui.text">{{ text }}</span>
|
||||||
<UIcon v-else-if="icon" :name="icon" :class="iconClass" />
|
<UIcon v-else-if="icon" :name="icon" :class="iconClass" />
|
||||||
<span v-else-if="placeholder" :class="ui.placeholder">{{ placeholder }}</span>
|
<span v-else-if="placeholder" :class="ui.placeholder">{{ placeholder }}</span>
|
||||||
@@ -39,6 +40,10 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
props: {
|
props: {
|
||||||
|
as: {
|
||||||
|
type: [String, Object],
|
||||||
|
default: 'img'
|
||||||
|
},
|
||||||
src: {
|
src: {
|
||||||
type: [String, Boolean],
|
type: [String, Boolean],
|
||||||
default: null
|
default: null
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
v-for="(item, index) in items"
|
v-for="(item, index) in items"
|
||||||
:key="index"
|
:key="index"
|
||||||
:class="ui.item"
|
:class="ui.item"
|
||||||
|
:role="indicators ? 'tabpanel' : null"
|
||||||
>
|
>
|
||||||
<slot :item="item" :index="index" />
|
<slot :item="item" :index="index" />
|
||||||
</div>
|
</div>
|
||||||
@@ -34,11 +35,13 @@
|
|||||||
</slot>
|
</slot>
|
||||||
</div>
|
</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">
|
<template v-for="page in pages" :key="page">
|
||||||
<slot name="indicator" :on-click="onClick" :active="page === currentPage" :page="page">
|
<slot name="indicator" :on-click="onClick" :active="page === currentPage" :page="page">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
role="tab"
|
||||||
|
:aria-selected="page === currentPage"
|
||||||
:class="[
|
:class="[
|
||||||
ui.indicators.base,
|
ui.indicators.base,
|
||||||
page === currentPage ? ui.indicators.active : ui.indicators.inactive
|
page === currentPage ? ui.indicators.active : ui.indicators.inactive
|
||||||
@@ -53,7 +56,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { ref, toRef, toRefs, computed, defineComponent } from 'vue'
|
import { ref, toRef, computed, defineComponent } from 'vue'
|
||||||
import type { PropType } from 'vue'
|
import type { PropType } from 'vue'
|
||||||
import { twMerge } from 'tailwind-merge'
|
import { twMerge } from 'tailwind-merge'
|
||||||
import { mergeConfig } from '../../utils'
|
import { mergeConfig } from '../../utils'
|
||||||
@@ -109,10 +112,9 @@ export default defineComponent({
|
|||||||
const carouselRef = ref<HTMLElement>()
|
const carouselRef = ref<HTMLElement>()
|
||||||
const itemWidth = ref(0)
|
const itemWidth = ref(0)
|
||||||
|
|
||||||
const { x, arrivedState } = useScroll(carouselRef, { behavior: 'smooth' })
|
const { x } = useScroll(carouselRef, { behavior: 'smooth' })
|
||||||
const { width: carouselWidth } = useElementSize(carouselRef)
|
|
||||||
|
|
||||||
const { left: isFirst, right: isLast } = toRefs(arrivedState)
|
const { width: carouselWidth } = useElementSize(carouselRef)
|
||||||
|
|
||||||
useCarouselScroll(carouselRef)
|
useCarouselScroll(carouselRef)
|
||||||
|
|
||||||
@@ -122,7 +124,13 @@ export default defineComponent({
|
|||||||
itemWidth.value = entry?.target?.firstElementChild?.clientWidth || 0
|
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(() => {
|
const pages = computed(() => {
|
||||||
if (!itemWidth.value) {
|
if (!itemWidth.value) {
|
||||||
@@ -132,6 +140,9 @@ export default defineComponent({
|
|||||||
return props.items.length - Math.round(carouselWidth.value / itemWidth.value) + 1
|
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 () {
|
function onClickNext () {
|
||||||
x.value += itemWidth.value
|
x.value += itemWidth.value
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,8 @@
|
|||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
:class="ui.trigger"
|
:class="ui.trigger"
|
||||||
role="button"
|
role="button"
|
||||||
@mouseover="onMouseOver"
|
@mouseenter="onMouseEnter"
|
||||||
|
@touchstart.passive="onTouchStart"
|
||||||
>
|
>
|
||||||
<slot :open="open" :disabled="disabled">
|
<slot :open="open" :disabled="disabled">
|
||||||
<button :disabled="disabled">
|
<button :disabled="disabled">
|
||||||
@@ -16,25 +17,25 @@
|
|||||||
</slot>
|
</slot>
|
||||||
</HMenuButton>
|
</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">
|
<Transition appear v-bind="ui.transition">
|
||||||
<div>
|
<div>
|
||||||
<div v-if="popper.arrow" data-popper-arrow :class="Object.values(ui.arrow)" />
|
<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>
|
<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">
|
<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">
|
<HMenuItem v-slot="{ active, disabled: itemDisabled, close }" :disabled="item.disabled">
|
||||||
<component
|
<component
|
||||||
:is="!!href ? 'a' : 'button'"
|
:is="!!href ? 'a' : 'button'"
|
||||||
:href="!itemDisabled ? href : undefined"
|
:href="!itemDisabled ? href : undefined"
|
||||||
:rel="rel"
|
:rel="rel"
|
||||||
:target="target"
|
: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 })"
|
@click="onClick($event, item, { href, navigate, close, isExternal })"
|
||||||
>
|
>
|
||||||
<slot :name="item.slot || 'item'" :item="item">
|
<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" />
|
<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>
|
<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) {
|
if (props.mode !== 'hover' || !menuApi.value) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -263,7 +276,8 @@ export default defineComponent({
|
|||||||
trigger,
|
trigger,
|
||||||
container,
|
container,
|
||||||
containerStyle,
|
containerStyle,
|
||||||
onMouseOver,
|
onTouchStart,
|
||||||
|
onMouseEnter,
|
||||||
onMouseLeave,
|
onMouseLeave,
|
||||||
onClick,
|
onClick,
|
||||||
getNuxtLinkProps,
|
getNuxtLinkProps,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :class="ui.wrapper">
|
<div :class="ui.wrapper" :data-n-ids="attrs['data-n-ids']">
|
||||||
<div :class="ui.container">
|
<div :class="ui.container">
|
||||||
<input
|
<input
|
||||||
:id="inputId"
|
:id="inputId"
|
||||||
@@ -39,6 +39,7 @@ import type { Strategy } from '../../types'
|
|||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
import { checkbox } from '#ui/ui.config'
|
import { checkbox } from '#ui/ui.config'
|
||||||
import colors from '#ui-colors'
|
import colors from '#ui-colors'
|
||||||
|
import { useId } from '#app'
|
||||||
|
|
||||||
const config = mergeConfig<typeof checkbox>(appConfig.ui.strategy, appConfig.ui.checkbox, checkbox)
|
const config = mergeConfig<typeof checkbox>(appConfig.ui.strategy, appConfig.ui.checkbox, checkbox)
|
||||||
|
|
||||||
@@ -105,7 +106,8 @@ export default defineComponent({
|
|||||||
setup (props, { emit }) {
|
setup (props, { emit }) {
|
||||||
const { ui, attrs } = useUI('checkbox', toRef(props, 'ui'), config, toRef(props, 'class'))
|
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({
|
const toggle = computed({
|
||||||
get () {
|
get () {
|
||||||
@@ -117,7 +119,7 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const onChange = (event: Event) => {
|
const onChange = (event: Event) => {
|
||||||
emit('change', event)
|
emit('change', (event.target as HTMLInputElement).checked)
|
||||||
emitFormChange()
|
emitFormChange()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ export default defineComponent({
|
|||||||
default: () => ({})
|
default: () => ({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: ['update:modelValue', 'blur'],
|
emits: ['update:modelValue', 'blur', 'change'],
|
||||||
setup (props, { emit, slots }) {
|
setup (props, { emit, slots }) {
|
||||||
const { ui, attrs } = useUI('input', toRef(props, 'ui'), config, toRef(props, 'class'))
|
const { ui, attrs } = useUI('input', toRef(props, 'ui'), config, toRef(props, 'class'))
|
||||||
|
|
||||||
@@ -205,15 +205,19 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const onChange = (event: Event) => {
|
const onChange = (event: Event) => {
|
||||||
const value = (event.target as HTMLInputElement).value
|
if (props.type === 'file') {
|
||||||
|
const value = (event.target as HTMLInputElement).files
|
||||||
if (modelModifiers.value.lazy) {
|
emit('change', value)
|
||||||
updateInput(value)
|
} else {
|
||||||
}
|
const value = (event.target as HTMLInputElement).value
|
||||||
|
emit('change', 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.lazy) {
|
||||||
if (modelModifiers.value.trim) {
|
updateInput(value)
|
||||||
(event.target as HTMLInputElement).value = value.trim()
|
}
|
||||||
|
// 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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
v-bind="attrs"
|
v-bind="attrs"
|
||||||
:display-value="() => query ? query : label"
|
:display-value="() => query ? query : label"
|
||||||
@change="onChange"
|
@change="onQueryChange"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<span v-if="(isLeading && leadingIconName) || $slots.leading" :class="leadingWrapperIconClass">
|
<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 = ''
|
query.value = ''
|
||||||
emit('update:modelValue', event)
|
emit('update:modelValue', value)
|
||||||
emit('change', event)
|
emit('change', value)
|
||||||
|
|
||||||
emitFormChange()
|
emitFormChange()
|
||||||
}
|
}
|
||||||
|
|
||||||
function onChange (event: any) {
|
function onQueryChange (event: any) {
|
||||||
query.value = event.target.value
|
query.value = event.target.value
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -459,7 +460,7 @@ export default defineComponent({
|
|||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
query,
|
query,
|
||||||
onUpdate,
|
onUpdate,
|
||||||
onChange
|
onQueryChange
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :class="ui.wrapper">
|
<div :class="ui.wrapper" :data-n-ids="attrs['data-n-ids']">
|
||||||
<div :class="ui.container">
|
<div :class="ui.container">
|
||||||
<input
|
<input
|
||||||
:id="inputId"
|
:id="inputId"
|
||||||
@@ -11,6 +11,7 @@
|
|||||||
type="radio"
|
type="radio"
|
||||||
:class="inputClass"
|
:class="inputClass"
|
||||||
v-bind="attrs"
|
v-bind="attrs"
|
||||||
|
@change="onChange"
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="label || $slots.label" :class="ui.inner">
|
<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)
|
const config = mergeConfig<typeof radio>(appConfig.ui.strategy, appConfig.ui.radio, radio)
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
inheritAttrs: false,
|
||||||
props: {
|
props: {
|
||||||
id: {
|
id: {
|
||||||
type: String,
|
type: String,
|
||||||
@@ -110,14 +112,16 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
set (value) {
|
set (value) {
|
||||||
emit('update:modelValue', value)
|
emit('update:modelValue', value)
|
||||||
emit('change', value)
|
|
||||||
|
|
||||||
if (!radioGroup) {
|
if (!radioGroup) {
|
||||||
emitFormChange()
|
emitFormChange()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
function onChange (event: Event) {
|
||||||
|
emit('change', (event.target as HTMLInputElement).value)
|
||||||
|
}
|
||||||
|
|
||||||
const inputClass = computed(() => {
|
const inputClass = computed(() => {
|
||||||
return twMerge(twJoin(
|
return twMerge(twJoin(
|
||||||
ui.value.base,
|
ui.value.base,
|
||||||
@@ -138,7 +142,8 @@ export default defineComponent({
|
|||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
name,
|
name,
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
inputClass
|
inputClass,
|
||||||
|
onChange
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :class="ui.wrapper">
|
<div :class="ui.wrapper">
|
||||||
<fieldset v-bind="attrs">
|
<fieldset v-bind="attrs" :class="ui.fieldset">
|
||||||
<legend v-if="legend || $slots.legend" :class="ui.legend">
|
<legend v-if="legend || $slots.legend" :class="ui.legend">
|
||||||
<slot name="legend">
|
<slot name="legend">
|
||||||
{{ legend }}
|
{{ legend }}
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const onChange = (event: Event) => {
|
const onChange = (event: Event) => {
|
||||||
emit('change', event)
|
emit('change', (event.target as HTMLInputElement).value)
|
||||||
emitFormChange()
|
emitFormChange()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -194,8 +194,8 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const onChange = (event: Event) => {
|
const onChange = (event: Event) => {
|
||||||
|
emit('change', (event.target as HTMLInputElement).value)
|
||||||
emitFormChange()
|
emitFormChange()
|
||||||
emit('change', event)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const guessOptionValue = (option: any) => {
|
const guessOptionValue = (option: any) => {
|
||||||
|
|||||||
@@ -63,7 +63,7 @@
|
|||||||
autofocus
|
autofocus
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
:class="uiMenu.input"
|
:class="uiMenu.input"
|
||||||
@change="onChange"
|
@change="onQueryChange"
|
||||||
/>
|
/>
|
||||||
<component
|
<component
|
||||||
:is="searchable ? 'HComboboxOption' : 'HListboxOption'"
|
:is="searchable ? 'HComboboxOption' : 'HListboxOption'"
|
||||||
@@ -105,12 +105,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
</component>
|
</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">
|
<slot name="option-empty" :query="query">
|
||||||
No results for "{{ query }}".
|
No results for "{{ query }}".
|
||||||
</slot>
|
</slot>
|
||||||
</p>
|
</p>
|
||||||
<p v-else-if="!filteredOptions.length" :class="uiMenu.empty">
|
<p v-else-if="!filteredOptions?.length" :class="uiMenu.empty">
|
||||||
<slot name="empty" :query="query">
|
<slot name="empty" :query="query">
|
||||||
No options.
|
No options.
|
||||||
</slot>
|
</slot>
|
||||||
@@ -174,7 +174,7 @@ export default defineComponent({
|
|||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
props: {
|
props: {
|
||||||
modelValue: {
|
modelValue: {
|
||||||
type: [String, Number, Object, Array],
|
type: [String, Number, Object, Array, Boolean],
|
||||||
default: ''
|
default: ''
|
||||||
},
|
},
|
||||||
query: {
|
query: {
|
||||||
@@ -362,7 +362,7 @@ export default defineComponent({
|
|||||||
} else {
|
} else {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
} else {
|
} else if (props.modelValue !== undefined && props.modelValue !== null) {
|
||||||
if (props.valueAttribute) {
|
if (props.valueAttribute) {
|
||||||
const option = props.options.find(option => option[props.valueAttribute] === props.modelValue)
|
const option = props.options.find(option => option[props.valueAttribute] === props.modelValue)
|
||||||
return option ? option[props.optionAttribute] : null
|
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 ['string', 'number'].includes(typeof props.modelValue) ? props.modelValue : props.modelValue[props.optionAttribute]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
})
|
})
|
||||||
|
|
||||||
const selectClass = computed(() => {
|
const selectClass = computed(() => {
|
||||||
@@ -385,7 +387,7 @@ export default defineComponent({
|
|||||||
variant?.replaceAll('{color}', color.value),
|
variant?.replaceAll('{color}', color.value),
|
||||||
(isLeading.value || slots.leading) && ui.value.leading.padding[size.value],
|
(isLeading.value || slots.leading) && ui.value.leading.padding[size.value],
|
||||||
(isTrailing.value || slots.trailing) && ui.value.trailing.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(() => {
|
const isLeading = computed(() => {
|
||||||
@@ -503,13 +505,13 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
function onUpdate (event: any) {
|
function onUpdate (value: any) {
|
||||||
emit('update:modelValue', event)
|
emit('update:modelValue', value)
|
||||||
emit('change', event)
|
emit('change', value)
|
||||||
emitFormChange()
|
emitFormChange()
|
||||||
}
|
}
|
||||||
|
|
||||||
function onChange (event: any) {
|
function onQueryChange (event: any) {
|
||||||
query.value = event.target.value
|
query.value = event.target.value
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -544,7 +546,7 @@ export default defineComponent({
|
|||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
query,
|
query,
|
||||||
onUpdate,
|
onUpdate,
|
||||||
onChange
|
onQueryChange
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ export default defineComponent({
|
|||||||
default: () => ({})
|
default: () => ({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: ['update:modelValue', 'blur'],
|
emits: ['update:modelValue', 'blur', 'change'],
|
||||||
setup (props, { emit }) {
|
setup (props, { emit }) {
|
||||||
const { ui, attrs } = useUI('textarea', toRef(props, 'ui'), config, toRef(props, 'class'))
|
const { ui, attrs } = useUI('textarea', toRef(props, 'ui'), config, toRef(props, 'class'))
|
||||||
|
|
||||||
@@ -192,6 +192,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
const onChange = (event: Event) => {
|
const onChange = (event: Event) => {
|
||||||
const value = (event.target as HTMLInputElement).value
|
const value = (event.target as HTMLInputElement).value
|
||||||
|
emit('change', value)
|
||||||
|
|
||||||
if (modelModifiers.value.lazy) {
|
if (modelModifiers.value.lazy) {
|
||||||
updateInput(value)
|
updateInput(value)
|
||||||
|
|||||||
@@ -3,15 +3,26 @@
|
|||||||
:id="inputId"
|
:id="inputId"
|
||||||
v-model="active"
|
v-model="active"
|
||||||
:name="name"
|
:name="name"
|
||||||
:disabled="disabled"
|
:disabled="disabled || loading"
|
||||||
:class="switchClass"
|
:class="switchClass"
|
||||||
v-bind="attrs"
|
v-bind="attrs"
|
||||||
>
|
>
|
||||||
<span :class="containerClass">
|
<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" />
|
<UIcon :name="onIcon" :class="onIconClass" />
|
||||||
</span>
|
</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" />
|
<UIcon :name="offIcon" :class="offIconClass" />
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
@@ -58,6 +69,10 @@ export default defineComponent({
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
},
|
},
|
||||||
|
loading: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
onIcon: {
|
onIcon: {
|
||||||
type: String,
|
type: String,
|
||||||
default: () => config.default.onIcon
|
default: () => config.default.onIcon
|
||||||
@@ -66,6 +81,10 @@ export default defineComponent({
|
|||||||
type: String,
|
type: String,
|
||||||
default: () => config.default.offIcon
|
default: () => config.default.offIcon
|
||||||
},
|
},
|
||||||
|
loadingIcon: {
|
||||||
|
type: String,
|
||||||
|
default: () => config.default.loadingIcon
|
||||||
|
},
|
||||||
color: {
|
color: {
|
||||||
type: String as PropType<ToggleColor>,
|
type: String as PropType<ToggleColor>,
|
||||||
default: () => config.default.color,
|
default: () => config.default.color,
|
||||||
@@ -101,6 +120,8 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
set (value) {
|
set (value) {
|
||||||
emit('update:modelValue', value)
|
emit('update:modelValue', value)
|
||||||
|
emit('change', value)
|
||||||
|
|
||||||
emitFormChange()
|
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())
|
provideUseId(() => useId())
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -150,7 +178,8 @@ export default defineComponent({
|
|||||||
switchClass,
|
switchClass,
|
||||||
containerClass,
|
containerClass,
|
||||||
onIconClass,
|
onIconClass,
|
||||||
offIconClass
|
offIconClass,
|
||||||
|
loadingIconClass
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :class="ui.group.wrapper" role="option">
|
<div :class="ui.group.wrapper">
|
||||||
<h2 v-if="label" :class="ui.group.label">
|
<h2 v-if="label" :class="ui.group.label">
|
||||||
{{ label }}
|
{{ label }}
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<div :class="ui.group.container" role="listbox" :aria-label="group[groupAttribute]">
|
<div :class="ui.group.container" :aria-label="group[groupAttribute]">
|
||||||
<HComboboxOption
|
<HComboboxOption
|
||||||
v-for="(command, index) of group.commands"
|
v-for="(command, index) of group.commands"
|
||||||
:key="`${group.key}-${index}`"
|
:key="`${group.key}-${index}`"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<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">
|
<HDialog :class="ui.wrapper" v-bind="attrs" @close="close">
|
||||||
<TransitionChild v-if="overlay" as="template" :appear="appear" v-bind="ui.overlay.transition">
|
<TransitionChild v-if="overlay" as="template" :appear="appear" v-bind="ui.overlay.transition">
|
||||||
<div :class="[ui.overlay.base, ui.overlay.background]" />
|
<div :class="[ui.overlay.base, ui.overlay.background]" />
|
||||||
@@ -82,7 +82,7 @@ export default defineComponent({
|
|||||||
default: () => ({})
|
default: () => ({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: ['update:modelValue', 'close', 'close-prevented'],
|
emits: ['update:modelValue', 'close', 'close-prevented', 'after-leave'],
|
||||||
setup (props, { emit }) {
|
setup (props, { emit }) {
|
||||||
const { ui, attrs } = useUI('modal', toRef(props, 'ui'), config, toRef(props, 'class'))
|
const { ui, attrs } = useUI('modal', toRef(props, 'ui'), config, toRef(props, 'class'))
|
||||||
|
|
||||||
@@ -117,6 +117,10 @@ export default defineComponent({
|
|||||||
emit('close')
|
emit('close')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const onAfterLeave = () => {
|
||||||
|
emit('after-leave')
|
||||||
|
}
|
||||||
|
|
||||||
provideUseId(() => useId())
|
provideUseId(() => useId())
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -125,6 +129,7 @@ export default defineComponent({
|
|||||||
attrs,
|
attrs,
|
||||||
isOpen,
|
isOpen,
|
||||||
transitionClass,
|
transitionClass,
|
||||||
|
onAfterLeave,
|
||||||
close
|
close
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
<template>
|
<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>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
@@ -8,5 +14,5 @@ import { useModal, modalInjectionKey } from '../../composables/useModal'
|
|||||||
|
|
||||||
const modalState = inject(modalInjectionKey)
|
const modalState = inject(modalInjectionKey)
|
||||||
|
|
||||||
const { isOpen } = useModal()
|
const { isOpen, reset } = useModal()
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
{{ title }}
|
{{ title }}
|
||||||
</slot>
|
</slot>
|
||||||
</p>
|
</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">
|
<slot name="description" :description="description">
|
||||||
{{ description }}
|
{{ description }}
|
||||||
</slot>
|
</slot>
|
||||||
|
|||||||
@@ -7,7 +7,8 @@
|
|||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
:class="ui.trigger"
|
:class="ui.trigger"
|
||||||
role="button"
|
role="button"
|
||||||
@mouseover="onMouseOver"
|
@mouseenter="onMouseEnter"
|
||||||
|
@touchstart.passive="onTouchStart"
|
||||||
>
|
>
|
||||||
<slot :open="open" :close="close">
|
<slot :open="open" :close="close">
|
||||||
<button :disabled="disabled">
|
<button :disabled="disabled">
|
||||||
@@ -20,7 +21,7 @@
|
|||||||
<div v-if="open" :class="[ui.overlay.base, ui.overlay.background]" />
|
<div v-if="open" :class="[ui.overlay.base, ui.overlay.background]" />
|
||||||
</Transition>
|
</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">
|
<Transition appear v-bind="ui.transition">
|
||||||
<div>
|
<div>
|
||||||
<div v-if="popper.arrow" data-popper-arrow :class="Object.values(ui.arrow)" />
|
<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) {
|
if (props.mode !== 'hover' || !popoverApi.value) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -223,7 +236,8 @@ export default defineComponent({
|
|||||||
trigger,
|
trigger,
|
||||||
container,
|
container,
|
||||||
containerStyle,
|
containerStyle,
|
||||||
onMouseOver,
|
onTouchStart,
|
||||||
|
onMouseEnter,
|
||||||
onMouseLeave
|
onMouseLeave
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<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">
|
<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">
|
<TransitionChild v-if="overlay" as="template" :appear="appear" v-bind="ui.overlay.transition">
|
||||||
<div :class="[ui.overlay.base, ui.overlay.background]" />
|
<div :class="[ui.overlay.base, ui.overlay.background]" />
|
||||||
@@ -71,7 +71,7 @@ export default defineComponent({
|
|||||||
default: () => ({})
|
default: () => ({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: ['update:modelValue', 'close', 'close-prevented'],
|
emits: ['update:modelValue', 'close', 'close-prevented', 'after-leave'],
|
||||||
setup (props, { emit }) {
|
setup (props, { emit }) {
|
||||||
const { ui, attrs } = useUI('slideover', toRef(props, 'ui'), config, toRef(props, 'class'))
|
const { ui, attrs } = useUI('slideover', toRef(props, 'ui'), config, toRef(props, 'class'))
|
||||||
|
|
||||||
@@ -109,6 +109,10 @@ export default defineComponent({
|
|||||||
emit('close')
|
emit('close')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const onAfterLeave = () => {
|
||||||
|
emit('after-leave')
|
||||||
|
}
|
||||||
|
|
||||||
provideUseId(() => useId())
|
provideUseId(() => useId())
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -117,6 +121,7 @@ export default defineComponent({
|
|||||||
attrs,
|
attrs,
|
||||||
isOpen,
|
isOpen,
|
||||||
transitionClass,
|
transitionClass,
|
||||||
|
onAfterLeave,
|
||||||
close
|
close
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
18
src/runtime/components/overlays/Slideovers.client.vue
Normal file
18
src/runtime/components/overlays/Slideovers.client.vue
Normal 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>
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<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">
|
<slot :open="open">
|
||||||
Hover
|
Hover
|
||||||
</slot>
|
</slot>
|
||||||
@@ -96,7 +96,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
// Methods
|
// Methods
|
||||||
|
|
||||||
function onMouseOver () {
|
function onMouseEnter () {
|
||||||
// cancel programmed closing
|
// cancel programmed closing
|
||||||
if (closeTimeout) {
|
if (closeTimeout) {
|
||||||
clearTimeout(closeTimeout)
|
clearTimeout(closeTimeout)
|
||||||
@@ -137,7 +137,7 @@ export default defineComponent({
|
|||||||
trigger,
|
trigger,
|
||||||
container,
|
container,
|
||||||
open,
|
open,
|
||||||
onMouseOver,
|
onMouseEnter,
|
||||||
onMouseLeave
|
onMouseLeave
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,17 @@ export function useProvideButtonGroup (buttonGroupProps: ButtonGroupProps) {
|
|||||||
export function useInjectButtonGroup ({ ui, props }: { ui: any, props: any }) {
|
export function useInjectButtonGroup ({ ui, props }: { ui: any, props: any }) {
|
||||||
const instance = getCurrentInstance()
|
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 parent = instance.parent
|
||||||
let groupContext: Ref<ButtonGroupContext> | undefined
|
let groupContext: Ref<ButtonGroupContext> | undefined
|
||||||
|
|
||||||
|
|||||||
@@ -8,19 +8,29 @@ export const modalInjectionKey: InjectionKey<ShallowRef<ModalState>> = Symbol('n
|
|||||||
|
|
||||||
function _useModal () {
|
function _useModal () {
|
||||||
const modalState = inject(modalInjectionKey)
|
const modalState = inject(modalInjectionKey)
|
||||||
|
|
||||||
const isOpen = ref(false)
|
const isOpen = ref(false)
|
||||||
|
|
||||||
function open<T extends Component> (component: T, props?: Modal & ComponentProps<T>) {
|
function open<T extends Component> (component: T, props?: Modal & ComponentProps<T>) {
|
||||||
|
if (!modalState) {
|
||||||
|
throw new Error('useModal() is called without provider')
|
||||||
|
}
|
||||||
|
|
||||||
modalState.value = {
|
modalState.value = {
|
||||||
component,
|
component,
|
||||||
props: props ?? {}
|
props: props ?? {}
|
||||||
}
|
}
|
||||||
|
|
||||||
isOpen.value = true
|
isOpen.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
function close () {
|
async function close () {
|
||||||
|
if (!modalState) return
|
||||||
|
|
||||||
isOpen.value = false
|
isOpen.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
function reset () {
|
||||||
modalState.value = {
|
modalState.value = {
|
||||||
component: 'div',
|
component: 'div',
|
||||||
props: {}
|
props: {}
|
||||||
@@ -31,6 +41,8 @@ function _useModal () {
|
|||||||
* Allows updating the modal props
|
* Allows updating the modal props
|
||||||
*/
|
*/
|
||||||
function patch <T extends Component = {}> (props: Partial<Modal & ComponentProps<T>>) {
|
function patch <T extends Component = {}> (props: Partial<Modal & ComponentProps<T>>) {
|
||||||
|
if (!modalState) return
|
||||||
|
|
||||||
modalState.value = {
|
modalState.value = {
|
||||||
...modalState.value,
|
...modalState.value,
|
||||||
props: {
|
props: {
|
||||||
@@ -41,11 +53,12 @@ function _useModal () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isOpen,
|
|
||||||
open,
|
open,
|
||||||
close,
|
close,
|
||||||
patch
|
reset,
|
||||||
|
patch,
|
||||||
|
isOpen
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useModal = createSharedComposable(_useModal)
|
export const useModal = createSharedComposable(_useModal)
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { ref, computed, onMounted } from 'vue'
|
|||||||
import type {} from '@vueuse/shared'
|
import type {} from '@vueuse/shared'
|
||||||
|
|
||||||
export const _useShortcuts = () => {
|
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(' ')
|
const metaSymbol = ref(' ')
|
||||||
|
|
||||||
|
|||||||
64
src/runtime/composables/useSlideover.ts
Normal file
64
src/runtime/composables/useSlideover.ts
Normal 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)
|
||||||
@@ -41,7 +41,7 @@ ${Object.entries(gray || colors.cool).map(([key, value]) => `--color-gray-${key}
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SPA mode
|
// SPA mode
|
||||||
if (process.client && nuxtApp.isHydrating && !nuxtApp.payload.serverRendered) {
|
if (import.meta.client && nuxtApp.isHydrating && !nuxtApp.payload.serverRendered) {
|
||||||
const style = document.createElement('style')
|
const style = document.createElement('style')
|
||||||
|
|
||||||
style.innerHTML = root.value
|
style.innerHTML = root.value
|
||||||
|
|||||||
13
src/runtime/plugins/slideovers.ts
Normal file
13
src/runtime/plugins/slideovers.ts
Normal 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)
|
||||||
|
})
|
||||||
1
src/runtime/types/index.d.ts
vendored
1
src/runtime/types/index.d.ts
vendored
@@ -17,6 +17,7 @@ export * from './kbd'
|
|||||||
export * from './link'
|
export * from './link'
|
||||||
export * from './meter'
|
export * from './meter'
|
||||||
export * from './modal'
|
export * from './modal'
|
||||||
|
export * from './slideover'
|
||||||
export * from './notification'
|
export * from './notification'
|
||||||
export * from './popper'
|
export * from './popper'
|
||||||
export * from './progress'
|
export * from './progress'
|
||||||
|
|||||||
17
src/runtime/types/slideover.d.ts
vendored
Normal file
17
src/runtime/types/slideover.d.ts
vendored
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -50,6 +50,9 @@ export default {
|
|||||||
variant: 'ghost' as const,
|
variant: 'ghost' as const,
|
||||||
class: '-m-1.5'
|
class: '-m-1.5'
|
||||||
},
|
},
|
||||||
|
checkbox: {
|
||||||
|
color: 'primary' as const
|
||||||
|
},
|
||||||
progress: {
|
progress: {
|
||||||
color: 'primary' as const,
|
color: 'primary' as const,
|
||||||
animation: 'carousel' as const
|
animation: 'carousel' as const
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { arrow } from '../popper'
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
container: 'z-20 group',
|
container: 'z-20 group',
|
||||||
trigger: 'inline-flex w-full',
|
trigger: 'flex items-center w-full',
|
||||||
width: 'w-full',
|
width: 'w-full',
|
||||||
height: 'max-h-60',
|
height: 'max-h-60',
|
||||||
base: 'relative focus:outline-none overflow-y-auto scroll-py-1',
|
base: 'relative focus:outline-none overflow-y-auto scroll-py-1',
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
export default {
|
export default {
|
||||||
wrapper: 'relative flex items-start',
|
wrapper: 'relative flex items-start',
|
||||||
|
fieldset: '',
|
||||||
legend: 'text-sm font-medium text-gray-700 dark:text-gray-200 mb-1',
|
legend: 'text-sm font-medium text-gray-700 dark:text-gray-200 mb-1',
|
||||||
default: {
|
default: {
|
||||||
color: 'primary'
|
color: 'primary'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user