mirror of
https://github.com/ArthurDanjou/ui.git
synced 2026-01-23 00:15:05 +01:00
Compare commits
74 Commits
issue-1057
...
v2.21.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f2d387622a | ||
|
|
b02dc4d5b7 | ||
|
|
6dddadc370 | ||
|
|
d89ecce472 | ||
|
|
efb74668bd | ||
|
|
e065734d58 | ||
|
|
0c5bea5f11 | ||
|
|
f6d4dd3b88 | ||
|
|
d9d4f1915a | ||
|
|
c70d29702e | ||
|
|
a0d8935f64 | ||
|
|
04aefcf81f | ||
|
|
e68b9795be | ||
|
|
b8c8718560 | ||
|
|
2a33a8171d | ||
|
|
23cfc046e7 | ||
|
|
e68cb53ab6 | ||
|
|
109b857472 | ||
|
|
ea15e21cdc | ||
|
|
b7153cd879 | ||
|
|
5047d448ed | ||
|
|
a0fee0fa73 | ||
|
|
b762d29220 | ||
|
|
98c19be71a | ||
|
|
8cf9f27d53 | ||
|
|
c0455c831f | ||
|
|
0360ea7a3c | ||
|
|
711539f3ce | ||
|
|
80d6d89467 | ||
|
|
d573fb636f | ||
|
|
1d08d319a7 | ||
|
|
b654c93e93 | ||
|
|
b7e04db645 | ||
|
|
e6034a2765 | ||
|
|
a8c38224c6 | ||
|
|
a9ef6406ea | ||
|
|
96e846ddee | ||
|
|
16dbc1b536 | ||
|
|
c6b2ae45e5 | ||
|
|
547c657ee7 | ||
|
|
b16b434041 | ||
|
|
fb12323304 | ||
|
|
0a404615ff | ||
|
|
cbf0f22efd | ||
|
|
4cde571e38 | ||
|
|
023497d144 | ||
|
|
56d4ca3b74 | ||
|
|
11b8c3d9db | ||
|
|
419a24f703 | ||
|
|
854bb81295 | ||
|
|
bf8e3954a4 | ||
|
|
637ec4d27b | ||
|
|
f3632ddee5 | ||
|
|
dbd2aed20b | ||
|
|
51c8b8e3e5 | ||
|
|
588a908358 | ||
|
|
d692a81b1e | ||
|
|
ec98d415b4 | ||
|
|
c80d2e6c12 | ||
|
|
ce61a2b6db | ||
|
|
eee5bb9939 | ||
|
|
d3804157ec | ||
|
|
03e24f4583 | ||
|
|
d0e626c551 | ||
|
|
670d8bfbac | ||
|
|
64b703df8d | ||
|
|
976b03f241 | ||
|
|
35e3b8c720 | ||
|
|
07ef771b17 | ||
|
|
5c75b5c490 | ||
|
|
53df9d9a8c | ||
|
|
0d1a76e3c6 | ||
|
|
b2ed4662af | ||
|
|
423c48879d |
15
.github/ISSUE_TEMPLATE/bug-v3.yml
vendored
15
.github/ISSUE_TEMPLATE/bug-v3.yml
vendored
@@ -5,8 +5,8 @@ body:
|
|||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: |
|
value: |
|
||||||
> [!IMPORTANT]
|
> [!IMPORTANT]
|
||||||
> As Nuxt UI v3 is currently in alpha, we recommend thorough testing before using it in production environments. We're actively working on stabilization and welcome feedback from early adopters to improve the library.
|
> As Nuxt UI v3 is currently in alpha, we recommend thorough testing before using it in production environments. We're actively working on stabilization and welcome feedback from early adopters to improve the library.
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: |
|
value: |
|
||||||
@@ -29,11 +29,20 @@ body:
|
|||||||
- Build Modules: `-`
|
- Build Modules: `-`
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
- type: dropdown
|
||||||
|
id: package
|
||||||
|
attributes:
|
||||||
|
label: Is this bug related to Nuxt or Vue?
|
||||||
|
options:
|
||||||
|
- Nuxt
|
||||||
|
- Vue
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
- type: input
|
- type: input
|
||||||
id: version
|
id: version
|
||||||
attributes:
|
attributes:
|
||||||
label: Version
|
label: Version
|
||||||
placeholder: v3.0.0-alpha.5
|
placeholder: v3.0.0-alpha.x
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
|
|||||||
2
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
2
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
@@ -12,7 +12,7 @@ body:
|
|||||||
label: For what version of Nuxt UI are you suggesting this?
|
label: For what version of Nuxt UI are you suggesting this?
|
||||||
options:
|
options:
|
||||||
- v2.x
|
- v2.x
|
||||||
- v3-alpha
|
- v3.0.0-alpha.x
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
|
|||||||
2
.github/ISSUE_TEMPLATE/question.yml
vendored
2
.github/ISSUE_TEMPLATE/question.yml
vendored
@@ -12,7 +12,7 @@ body:
|
|||||||
label: For what version of Nuxt UI are you asking this question?
|
label: For what version of Nuxt UI are you asking this question?
|
||||||
options:
|
options:
|
||||||
- v2.x
|
- v2.x
|
||||||
- v3-alpha
|
- v3.0.0-alpha.x
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
|
|||||||
45
CHANGELOG.md
45
CHANGELOG.md
@@ -1,5 +1,50 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [2.21.0](https://github.com/nuxt/ui/compare/v2.20.0...v2.21.0) (2025-01-14)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **module:** handle `tailwindMerge` config from `app.config` ([#2902](https://github.com/nuxt/ui/issues/2902)) ([ea15e21](https://github.com/nuxt/ui/commit/ea15e21cdcba00e21302415829113e8c6def8a6e))
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **Table:** `v-model` causing first column missing ([#2890](https://github.com/nuxt/ui/issues/2890)) ([d573fb6](https://github.com/nuxt/ui/commit/d573fb636f7f749ce95b93c5fb1ae2a053eeeeb0))
|
||||||
|
* **Table:** remove `[@select](https://github.com/select)` event on checkbox ([#3042](https://github.com/nuxt/ui/issues/3042)) ([d9d4f19](https://github.com/nuxt/ui/commit/d9d4f1915aac586ae1abf3ebe67ca9aff65b9be0))
|
||||||
|
* **tailwind:** use mjs template ([#2945](https://github.com/nuxt/ui/issues/2945)) ([8cf9f27](https://github.com/nuxt/ui/commit/8cf9f27d537bad5ffe4e136f52ff71548a451c5f))
|
||||||
|
|
||||||
|
## [2.20.0](https://github.com/nuxt/ui/compare/v2.19.2...v2.20.0) (2024-12-09)
|
||||||
|
|
||||||
|
### ⚠ BREAKING CHANGES
|
||||||
|
|
||||||
|
* **Form:** resolve async validation in yup & issue directly mutate state (#2701)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **Accordion:** add `close` event ([#2750](https://github.com/nuxt/ui/issues/2750)) ([419a24f](https://github.com/nuxt/ui/commit/419a24f7034cefda2c6669f3c26742552e500f63))
|
||||||
|
* **Badge:** handle `icon` prop ([#2594](https://github.com/nuxt/ui/issues/2594)) ([0d1a76e](https://github.com/nuxt/ui/commit/0d1a76e3c69e08534abb295b96548e67cfbea00c))
|
||||||
|
* **InputMenu/SelectMenu:** add support for `dot notation` in `by` prop ([#2607](https://github.com/nuxt/ui/issues/2607)) ([53df9d9](https://github.com/nuxt/ui/commit/53df9d9a8cd6850803bdafc7ef6efe4e7404d334))
|
||||||
|
* **Link:** allow partial query match for `activeClass` ([#2663](https://github.com/nuxt/ui/issues/2663)) ([03e24f4](https://github.com/nuxt/ui/commit/03e24f45836bdddd94b30cbaecc2288a78b56b0b))
|
||||||
|
* **Notification:** add `pauseTimeoutOnHover` prop ([#2661](https://github.com/nuxt/ui/issues/2661)) ([11b8c3d](https://github.com/nuxt/ui/commit/11b8c3d9db1ec62b1c3557703c7ab5c99cb42df5))
|
||||||
|
* **Table:** add contextmenu handling to table rows ([#2283](https://github.com/nuxt/ui/issues/2283)) ([c9e6256](https://github.com/nuxt/ui/commit/c9e6256e7f2c06da8bfda13700f56f6994e76eab))
|
||||||
|
* **Table:** add custom `[@select](https://github.com/select):all` event ([#2581](https://github.com/nuxt/ui/issues/2581)) ([ac323c4](https://github.com/nuxt/ui/commit/ac323c4cccd930f2cd8c1f54b325bd509acd40bf))
|
||||||
|
* **Table:** allow dynamically render `checkbox` ([#2549](https://github.com/nuxt/ui/issues/2549)) ([d6daf46](https://github.com/nuxt/ui/commit/d6daf466ace42b828151c45b18cd47179e85d66d))
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **AvatarGroup/ButtonGroup/MeterGroup:** allow deeply partial `ui` config ([#2542](https://github.com/nuxt/ui/issues/2542)) ([bf58086](https://github.com/nuxt/ui/commit/bf580863af11d6a1a4c6c6774b44ec37b082e933))
|
||||||
|
* **Carousel:** wrong `ui` type with `strategy` ([07ef771](https://github.com/nuxt/ui/commit/07ef771b17c72e275508a273371454a5e8a62257))
|
||||||
|
* **components:** replace `as const` with correct type in config ([#2652](https://github.com/nuxt/ui/issues/2652)) ([51c8b8e](https://github.com/nuxt/ui/commit/51c8b8e3e59d7eceff72625650a199fcf7c6feca))
|
||||||
|
* **date-picker:** undefined `dayIndex` ([#2545](https://github.com/nuxt/ui/issues/2545)) ([ce955d2](https://github.com/nuxt/ui/commit/ce955d24f1dfd222e87ce88428c0612c3f13cd50))
|
||||||
|
* **Form:** resolve async validation in yup & issue directly mutate state ([#2701](https://github.com/nuxt/ui/issues/2701)) ([f3632dd](https://github.com/nuxt/ui/commit/f3632ddee511f0fccb24d4fc37403421e84ffdae))
|
||||||
|
* **Form:** use parsed value from `joi` instead of original state ([#2587](https://github.com/nuxt/ui/issues/2587)) ([acecff4](https://github.com/nuxt/ui/commit/acecff40ec0156e45b4934c5d10c4dfa7c135f8e))
|
||||||
|
* **InputMenu/SelectMenu:** use `by` prop to compare objects & support dot notation in `value-attribute` ([#2566](https://github.com/nuxt/ui/issues/2566)) ([7154254](https://github.com/nuxt/ui/commit/7154254ac22830f651ec200f7f3af2f5577f2de0))
|
||||||
|
* **Link:** `exactQuery` prop type ([#2781](https://github.com/nuxt/ui/issues/2781)) ([4cde571](https://github.com/nuxt/ui/commit/4cde571e387775a9b12759f6f8c99117c84cbcff))
|
||||||
|
* **Notification:** element renders even when no `notification` is present ([#2561](https://github.com/nuxt/ui/issues/2561)) ([d4e408c](https://github.com/nuxt/ui/commit/d4e408cfd8e2ef26021519f2f30f57e9120e1939))
|
||||||
|
* **Table:** data outdated when rows change ([#2600](https://github.com/nuxt/ui/issues/2600)) ([b23f2de](https://github.com/nuxt/ui/commit/b23f2decfc9607555a315d0d087d0a042f03a938))
|
||||||
|
* **Table:** missing type on props `loadingState` ([#2551](https://github.com/nuxt/ui/issues/2551)) ([6e66990](https://github.com/nuxt/ui/commit/6e66990372ef6bd7c109a64c753d9b50e96a450b))
|
||||||
|
* **Table:** prevent `onClick` while blocking element ([#2592](https://github.com/nuxt/ui/issues/2592)) ([9703786](https://github.com/nuxt/ui/commit/97037864b39749db228fa5f51981f19e4a9c29dd))
|
||||||
|
* **types:** improve `DeepPartial` type for App Config ([#2621](https://github.com/nuxt/ui/issues/2621)) ([976b03f](https://github.com/nuxt/ui/commit/976b03f241ef9626a6338685e43c844a8b3953fd))
|
||||||
|
|
||||||
## [2.19.2](https://github.com/nuxt/ui/compare/v2.19.1...v2.19.2) (2024-11-05)
|
## [2.19.2](https://github.com/nuxt/ui/compare/v2.19.1...v2.19.2) (2024-11-05)
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
[](https://ui.nuxt.com)
|
[](https://ui.nuxt.com)
|
||||||
|
|
||||||
# Nuxt UI
|
# Nuxt UI
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<div>
|
<div>
|
||||||
<NuxtLoadingIndicator />
|
<NuxtLoadingIndicator />
|
||||||
|
|
||||||
<Banner v-if="!$route.path.startsWith('/examples')" />
|
<!-- <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" />
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const id = 'nuxt-ui-banner-2'
|
const id = 'nuxt-ui-banner-3'
|
||||||
const to = 'https://ui3.nuxt.dev'
|
const to = '/pro/pricing'
|
||||||
|
|
||||||
const hideBanner = () => {
|
const hideBanner = () => {
|
||||||
localStorage.setItem(id, 'true')
|
localStorage.setItem(id, 'true')
|
||||||
@@ -28,9 +28,8 @@ if (import.meta.server) {
|
|||||||
<NuxtLink
|
<NuxtLink
|
||||||
v-if="to"
|
v-if="to"
|
||||||
:to="to"
|
:to="to"
|
||||||
target="_blank"
|
|
||||||
class="focus:outline-none"
|
class="focus:outline-none"
|
||||||
aria-label="Nuxt UI Pro pricing"
|
aria-label="20% off on all Nuxt UI Pro products for Black Friday week"
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
>
|
>
|
||||||
<span class="absolute inset-0 " aria-hidden="true" />
|
<span class="absolute inset-0 " aria-hidden="true" />
|
||||||
@@ -40,19 +39,19 @@ if (import.meta.server) {
|
|||||||
<div class="lg:flex-1 hidden lg:flex items-center" />
|
<div class="lg:flex-1 hidden lg:flex items-center" />
|
||||||
|
|
||||||
<p class="text-sm font-medium text-white dark:text-gray-900 truncate">
|
<p class="text-sm font-medium text-white dark:text-gray-900 truncate">
|
||||||
<UIcon name="i-heroicons-rocket-launch" class="w-5 h-5 align-top flex-shrink-0 pointer-events-none mr-2" />
|
<UIcon name="i-ri-discount-percent-fill" class="size-5 align-top flex-shrink-0 pointer-events-none mr-2" />
|
||||||
<span class="font-semibold">Nuxt UI v3-alpha</span> has been released!
|
<span class="font-bold">Black Friday Week</span>: <UBadge label="20% off" color="white" class="ring-0 font-semibold" /> on all Nuxt UI Pro products from <span class="font-semibold">Nov 25</span> to <span class="font-semibold">Dec 2</span>!
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<UButton
|
<!-- <UButton
|
||||||
to="https://ui3.nuxt.dev"
|
:to="to"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
label="Try it out"
|
label="Buy now"
|
||||||
color="black"
|
color="black"
|
||||||
variant="solid"
|
variant="solid"
|
||||||
size="2xs"
|
size="2xs"
|
||||||
trailing-icon="i-heroicons-arrow-right-20-solid"
|
trailing-icon="i-heroicons-arrow-right-20-solid"
|
||||||
/>
|
/> -->
|
||||||
|
|
||||||
<div class="flex items-center justify-end lg:flex-1">
|
<div class="flex items-center justify-end lg:flex-1">
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -10,12 +10,34 @@
|
|||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<template #left>
|
<template #left>
|
||||||
<NuxtLink to="/" class="flex items-end gap-2 font-bold text-xl text-gray-900 dark:text-white min-w-0" aria-label="Nuxt UI">
|
<NuxtLink to="/" class="flex items-end gap-2 text-xl text-gray-900 dark:text-white min-w-0 shrink-0" aria-label="Nuxt UI">
|
||||||
<LogoPro v-if="$route.path.startsWith('/pro')" class="w-auto h-6 shrink-0" />
|
<LogoPro v-if="$route.path.startsWith('/pro')" class="w-auto h-6 shrink-0" />
|
||||||
<Logo v-else class="w-auto h-6 shrink-0" />
|
<Logo v-else class="w-auto h-6 shrink-0" />
|
||||||
|
|
||||||
<UBadge :label="$route.path.startsWith('/pro') ? `v${pkg.version.split('-')[0]}` : `v${config.version}`" variant="subtle" size="xs" class="-mb-[2px] rounded font-semibold truncate hidden sm:inline-flex" />
|
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
|
|
||||||
|
<UDropdown
|
||||||
|
:items="[[{ label: $route.path.startsWith('/pro') ? `v${pkg.version.split('-')[0]}` : `v${config.version}`, class: 'text-primary-500 dark:text-primary-400' }, { label: 'v3.0.0-alpha.x', to: 'https://ui3.nuxt.dev' }]]"
|
||||||
|
:popper="{ strategy: 'absolute', offsetDistance: 11, placement: 'bottom-start' }"
|
||||||
|
:ui="{
|
||||||
|
background: 'dark:bg-gray-900',
|
||||||
|
ring: 'dark:ring-gray-800',
|
||||||
|
width: 'w-auto',
|
||||||
|
item: {
|
||||||
|
padding: 'p-1',
|
||||||
|
size: 'text-xs',
|
||||||
|
active: 'dark:bg-gray-800/50'
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<UButton
|
||||||
|
:label="$route.path.startsWith('/pro') ? `v${pkg.version.split('-')[0]}` : `v${config.version}`"
|
||||||
|
trailing-icon="i-lucide-chevron-down"
|
||||||
|
variant="outline"
|
||||||
|
size="2xs"
|
||||||
|
truncate
|
||||||
|
class="-mb-[6px] font-semibold rounded-full truncate ring-primary-500/25 dark:ring-primary-400/25 bg-primary-500/10 dark:bg-primary-400/10 hover:bg-primary-500/15 dark:hover:bg-primary-400/15 transition-colors"
|
||||||
|
/>
|
||||||
|
</UDropdown>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #right>
|
<template #right>
|
||||||
@@ -25,10 +47,10 @@
|
|||||||
<UContentSearchButton :label="null" />
|
<UContentSearchButton :label="null" />
|
||||||
</UTooltip>
|
</UTooltip>
|
||||||
|
|
||||||
<UColorModeButton />
|
<UColorModeButton class="hidden lg:inline-flex" />
|
||||||
|
|
||||||
<UButton
|
<UButton
|
||||||
to="https://github.com/nuxt/ui"
|
to="https://github.com/nuxt/ui/tree/dev"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
icon="i-simple-icons-github"
|
icon="i-simple-icons-github"
|
||||||
aria-label="GitHub"
|
aria-label="GitHub"
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ const ui = {
|
|||||||
inactive: 'text-gray-400 dark:text-gray-500'
|
inactive: 'text-gray-400 dark:text-gray-500'
|
||||||
},
|
},
|
||||||
avatar: {
|
avatar: {
|
||||||
size: '2xs' as const
|
size: '2xs'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,102 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
const options = ref([
|
|
||||||
{ id: 1, name: 'bug', color: 'd73a4a' },
|
|
||||||
{ id: 2, name: 'documentation', color: '0075ca' },
|
|
||||||
{ id: 3, name: 'duplicate', color: 'cfd3d7' },
|
|
||||||
{ id: 4, name: 'enhancement', color: 'a2eeef' },
|
|
||||||
{ id: 5, name: 'good first issue', color: '7057ff' },
|
|
||||||
{ id: 6, name: 'help wanted', color: '008672' },
|
|
||||||
{ id: 7, name: 'invalid', color: 'e4e669' },
|
|
||||||
{ id: 8, name: 'question', color: 'd876e3' },
|
|
||||||
{ id: 9, name: 'wontfix', color: 'ffffff' }
|
|
||||||
])
|
|
||||||
|
|
||||||
const selected = ref([])
|
|
||||||
|
|
||||||
const labels = computed({
|
|
||||||
get: () => selected.value,
|
|
||||||
set: async (labels) => {
|
|
||||||
const promises = labels.map(async (label) => {
|
|
||||||
if (label.id) {
|
|
||||||
return label
|
|
||||||
}
|
|
||||||
|
|
||||||
// In a real app, you would make an API call to create the label
|
|
||||||
const response = {
|
|
||||||
id: options.value.length + 1,
|
|
||||||
name: label.name,
|
|
||||||
color: generateColorFromString(label.name)
|
|
||||||
}
|
|
||||||
|
|
||||||
options.value.push(response)
|
|
||||||
|
|
||||||
return response
|
|
||||||
})
|
|
||||||
|
|
||||||
selected.value = await Promise.all(promises)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
function hashCode(str) {
|
|
||||||
let hash = 0
|
|
||||||
for (let i = 0; i < str.length; i++) {
|
|
||||||
hash = str.charCodeAt(i) + ((hash << 5) - hash)
|
|
||||||
}
|
|
||||||
return hash
|
|
||||||
}
|
|
||||||
|
|
||||||
function intToRGB(i) {
|
|
||||||
const c = (i & 0x00FFFFFF)
|
|
||||||
.toString(16)
|
|
||||||
.toUpperCase()
|
|
||||||
|
|
||||||
return '00000'.substring(0, 6 - c.length) + c
|
|
||||||
}
|
|
||||||
|
|
||||||
function generateColorFromString(str) {
|
|
||||||
return intToRGB(hashCode(str))
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<USelectMenu
|
|
||||||
v-model="labels"
|
|
||||||
by="id"
|
|
||||||
name="labels"
|
|
||||||
:options="options"
|
|
||||||
option-attribute="name"
|
|
||||||
clearable
|
|
||||||
multiple
|
|
||||||
searchable
|
|
||||||
creatable
|
|
||||||
>
|
|
||||||
<template #label>
|
|
||||||
<template v-if="labels.length">
|
|
||||||
<span class="flex items-center -space-x-1">
|
|
||||||
<span v-for="label of labels" :key="label.id" class="flex-shrink-0 w-2 h-2 mt-px rounded-full" :style="{ background: `#${label.color}` }" />
|
|
||||||
</span>
|
|
||||||
<span>{{ labels.length }} label{{ labels.length > 1 ? 's' : '' }}</span>
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<span class="text-gray-500 dark:text-gray-400 truncate">Select labels</span>
|
|
||||||
</template>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template #option="{ option }">
|
|
||||||
<span
|
|
||||||
class="flex-shrink-0 w-2 h-2 mt-px rounded-full"
|
|
||||||
:style="{ background: `#${option.color}` }"
|
|
||||||
/>
|
|
||||||
<span class="truncate">{{ option.name }}</span>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template #option-create="{ option }">
|
|
||||||
<span class="flex-shrink-0">New label:</span>
|
|
||||||
<span
|
|
||||||
class="flex-shrink-0 w-2 h-2 mt-px rounded-full -mx-1"
|
|
||||||
:style="{ background: `#${generateColorFromString(option.name)}` }"
|
|
||||||
/>
|
|
||||||
<span class="block truncate">{{ option.name }}</span>
|
|
||||||
</template>
|
|
||||||
</USelectMenu>
|
|
||||||
</template>
|
|
||||||
@@ -1,105 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
const options = ref([
|
|
||||||
{ id: 1, name: 'bug', color: 'd73a4a' },
|
|
||||||
{ id: 2, name: 'documentation', color: '0075ca' },
|
|
||||||
{ id: 3, name: 'duplicate', color: 'cfd3d7' },
|
|
||||||
{ id: 4, name: 'enhancement', color: 'a2eeef' },
|
|
||||||
{ id: 5, name: 'good first issue', color: '7057ff' },
|
|
||||||
{ id: 6, name: 'help wanted', color: '008672' },
|
|
||||||
{ id: 7, name: 'invalid', color: 'e4e669' },
|
|
||||||
{ id: 8, name: 'question', color: 'd876e3' },
|
|
||||||
{ id: 9, name: 'wontfix', color: 'ffffff' }
|
|
||||||
])
|
|
||||||
|
|
||||||
const selected = ref([])
|
|
||||||
|
|
||||||
const labels = computed({
|
|
||||||
get: () => selected.value,
|
|
||||||
set: async (labels) => {
|
|
||||||
const promises = labels.map(async (label) => {
|
|
||||||
if (label.id) {
|
|
||||||
return label
|
|
||||||
}
|
|
||||||
|
|
||||||
// In a real app, you would make an API call to create the label
|
|
||||||
const response = {
|
|
||||||
id: options.value.length + 1,
|
|
||||||
name: label.name,
|
|
||||||
color: generateColorFromString(label.name)
|
|
||||||
}
|
|
||||||
|
|
||||||
options.value.push(response)
|
|
||||||
|
|
||||||
return response
|
|
||||||
})
|
|
||||||
|
|
||||||
selected.value = await Promise.all(promises)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
function hashCode(str) {
|
|
||||||
let hash = 0
|
|
||||||
for (let i = 0; i < str.length; i++) {
|
|
||||||
hash = str.charCodeAt(i) + ((hash << 5) - hash)
|
|
||||||
}
|
|
||||||
return hash
|
|
||||||
}
|
|
||||||
|
|
||||||
function intToRGB(i) {
|
|
||||||
const c = (i & 0x00FFFFFF)
|
|
||||||
.toString(16)
|
|
||||||
.toUpperCase()
|
|
||||||
|
|
||||||
return '00000'.substring(0, 6 - c.length) + c
|
|
||||||
}
|
|
||||||
|
|
||||||
function generateColorFromString(str) {
|
|
||||||
return intToRGB(hashCode(str))
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<USelectMenu
|
|
||||||
v-model="labels"
|
|
||||||
by="id"
|
|
||||||
name="labels"
|
|
||||||
:options="options"
|
|
||||||
option-attribute="name"
|
|
||||||
clearable
|
|
||||||
multiple
|
|
||||||
searchable
|
|
||||||
creatable
|
|
||||||
>
|
|
||||||
<template #label>
|
|
||||||
<template v-if="labels.length">
|
|
||||||
<span class="flex items-center -space-x-1">
|
|
||||||
<span v-for="label of labels" :key="label.id" class="flex-shrink-0 w-2 h-2 mt-px rounded-full" :style="{ background: `#${label.color}` }" />
|
|
||||||
</span>
|
|
||||||
<span>{{ labels.length }} label{{ labels.length > 1 ? 's' : '' }}</span>
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<span class="text-gray-500 dark:text-gray-400 truncate">Select labels</span>
|
|
||||||
</template>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template #option="{ option }">
|
|
||||||
<span
|
|
||||||
class="flex-shrink-0 w-2 h-2 mt-px rounded-full"
|
|
||||||
:style="{ background: `#${option.color}` }"
|
|
||||||
/>
|
|
||||||
<span class="truncate">{{ option.name }}</span>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template #option-create="{ option }">
|
|
||||||
<span class="flex-shrink-0">New label:</span>
|
|
||||||
<span
|
|
||||||
class="flex-shrink-0 w-2 h-2 mt-px rounded-full -mx-1"
|
|
||||||
:style="{ background: `#${generateColorFromString(option.name)}` }"
|
|
||||||
/>
|
|
||||||
<span class="block truncate">{{ option.name }}</span>
|
|
||||||
</template>
|
|
||||||
<template #clearable="{ onClear }">
|
|
||||||
<UButton icon="i-heroicons-trash-20-solid" size="xs" class="text-gray-400 dark:text-gray-500" variant="ghost" @click.capture.stop="onClear" />
|
|
||||||
</template>
|
|
||||||
</USelectMenu>
|
|
||||||
</template>
|
|
||||||
@@ -221,6 +221,52 @@ export default defineAppConfig({
|
|||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Extend Tailwind Merge
|
||||||
|
|
||||||
|
Tailwind Merge is a library that allows you to efficiently merge Tailwind CSS classes. It is used by this module to merge the classes from the `ui` prop, the `class` attribute, and the default classes.
|
||||||
|
|
||||||
|
::callout{icon="i-heroicons-light-bulb" to="https://github.com/dcastil/tailwind-merge" target="_blank"}
|
||||||
|
Learn more about Tailwind Merge.
|
||||||
|
::
|
||||||
|
|
||||||
|
By default, Tailwind Merge doesn't handle custom Tailwind CSS configuration like custom colors, spacing, or other utilities you may have defined. You'll need to extend it to handle your custom configuration.
|
||||||
|
|
||||||
|
You can extend Tailwind Merge by using the `tailwindMerge` option in your `app.config.ts`:
|
||||||
|
|
||||||
|
::code-group
|
||||||
|
```ts [app.config.ts]
|
||||||
|
export default defineAppConfig({
|
||||||
|
ui: {
|
||||||
|
tailwindMerge: {
|
||||||
|
extend: {
|
||||||
|
theme: {
|
||||||
|
spacing: ['sm', 'md', 'lg', 'xl', '2xl']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
```ts [tailwind.config.ts]
|
||||||
|
import type { Config } from 'tailwindcss'
|
||||||
|
|
||||||
|
export default <Partial<Config>>{
|
||||||
|
theme: {
|
||||||
|
extend: {
|
||||||
|
spacing: {
|
||||||
|
sm: '0.5rem',
|
||||||
|
md: '1rem',
|
||||||
|
lg: '1.5rem',
|
||||||
|
xl: '2rem',
|
||||||
|
'2xl': '2.5rem'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
::
|
||||||
|
|
||||||
## Dark mode
|
## Dark mode
|
||||||
|
|
||||||
All the components are styled with dark mode in mind.
|
All the components are styled with dark mode in mind.
|
||||||
|
|||||||
@@ -141,6 +141,74 @@ Badge
|
|||||||
You can customize the whole [preset](#preset) by using the `ui` prop.
|
You can customize the whole [preset](#preset) by using the `ui` prop.
|
||||||
::
|
::
|
||||||
|
|
||||||
|
### Icon
|
||||||
|
|
||||||
|
Use any icon from [Iconify](https://icones.js.org) by setting the `icon` prop by using this pattern: `i-{collection_name}-{icon_name}`.
|
||||||
|
|
||||||
|
Use the `leading` and `trailing` props to set the icon position or the `leading-icon` and `trailing-icon` props to set a different icon for each position.
|
||||||
|
|
||||||
|
::component-card
|
||||||
|
---
|
||||||
|
props:
|
||||||
|
icon: 'i-heroicons-rocket-launch'
|
||||||
|
size: 'sm'
|
||||||
|
color: 'primary'
|
||||||
|
variant: 'solid'
|
||||||
|
label: Badge
|
||||||
|
trailing: false
|
||||||
|
options:
|
||||||
|
- name: variant
|
||||||
|
restriction: only
|
||||||
|
values:
|
||||||
|
- solid
|
||||||
|
excludedProps:
|
||||||
|
- icon
|
||||||
|
- label
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
## Slots
|
||||||
|
|
||||||
|
### `leading`
|
||||||
|
|
||||||
|
Use the `#leading` slot to set the content of the leading icon.
|
||||||
|
|
||||||
|
::component-card
|
||||||
|
---
|
||||||
|
slots:
|
||||||
|
leading: <UAvatar src="https://avatars.githubusercontent.com/u/739984?v=4" size="3xs" />
|
||||||
|
baseProps:
|
||||||
|
color: 'gray'
|
||||||
|
props:
|
||||||
|
label: Badge
|
||||||
|
color: 'gray'
|
||||||
|
excludedProps:
|
||||||
|
- color
|
||||||
|
---
|
||||||
|
|
||||||
|
#leading
|
||||||
|
:u-avatar{src="https://avatars.githubusercontent.com/u/739984?v=4" size="3xs"}
|
||||||
|
::
|
||||||
|
|
||||||
|
### `trailing`
|
||||||
|
|
||||||
|
Use the `#trailing` slot to set the content of the trailing icon.
|
||||||
|
|
||||||
|
::component-card
|
||||||
|
---
|
||||||
|
slots:
|
||||||
|
trailing: <UIcon name="i-heroicons-rocket-launch" class="w-4 h-4" />
|
||||||
|
props:
|
||||||
|
label: Badge
|
||||||
|
color: 'gray'
|
||||||
|
excludedProps:
|
||||||
|
- color
|
||||||
|
---
|
||||||
|
|
||||||
|
#trailing
|
||||||
|
:u-icon{name="i-heroicons-rocket-launch" class="w-4 h-4"}
|
||||||
|
::
|
||||||
|
|
||||||
## Props
|
## Props
|
||||||
|
|
||||||
:component-props
|
:component-props
|
||||||
|
|||||||
@@ -142,7 +142,7 @@ props:
|
|||||||
|
|
||||||
### Loading
|
### Loading
|
||||||
|
|
||||||
Use the `loading` prop to show a loading icon and disable the Input.
|
Use the `loading` prop to show a loading icon in the Input.
|
||||||
|
|
||||||
Use the `loading-icon` prop to set a different icon or change it globally in `ui.input.default.loadingIcon`. Defaults to `i-heroicons-arrow-path-20-solid`.
|
Use the `loading-icon` prop to set a different icon or change it globally in `ui.input.default.loadingIcon`. Defaults to `i-heroicons-arrow-path-20-solid`.
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ The Link component is a wrapper around [`<NuxtLink>`](https://nuxt.com/docs/api/
|
|||||||
- `inactive-class` prop to set a class when the link is inactive, `active-class` is used when active.
|
- `inactive-class` prop to set a class when the link is inactive, `active-class` is used when active.
|
||||||
- `exact` prop to style with `active-class` when the link is active and the route is exactly the same as the current route.
|
- `exact` prop to style with `active-class` when the link is active and the route is exactly the same as the current route.
|
||||||
- `exact-query` and `exact-hash` props to style with `active-class` when the link is active and the query or hash is exactly the same as the current query or hash.
|
- `exact-query` and `exact-hash` props to style with `active-class` when the link is active and the query or hash is exactly the same as the current query or hash.
|
||||||
|
- use `exact-query="partial"` to style with `active-class` when the link is active and the query partially match the current query.
|
||||||
|
|
||||||
The incentive behind this is to provide the same API as NuxtLink back in Nuxt 2 / Vue 2. You can read more about it in the Vue Router [migration from Vue 2](https://router.vuejs.org/guide/migration/#removal-of-the-exact-prop-in-router-link) guide.
|
The incentive behind this is to provide the same API as NuxtLink back in Nuxt 2 / Vue 2. You can read more about it in the Vue Router [migration from Vue 2](https://router.vuejs.org/guide/migration/#removal-of-the-exact-prop-in-router-link) guide.
|
||||||
|
|
||||||
|
|||||||
@@ -137,9 +137,9 @@ excludedProps:
|
|||||||
|
|
||||||
### Timeout
|
### Timeout
|
||||||
|
|
||||||
Use the `timeout` prop to configure how long the Notification will remain. The default value is `5000`, set it to `0` to disable the timeout.
|
Use the `timeout` prop to configure how long the Notification will remain. The default value is `5000`, set it to `0` to disable the timeout. The `pauseTimeoutOnHover` prop (`true` by default) controls whether hovering the notification should pause the timeout.
|
||||||
|
|
||||||
You will see a progress bar at the bottom of the Notification which will indicate the remaining time. When hovering the Notification, the progress bar will be paused.
|
You will see a progress bar at the bottom of the Notification which will indicate the remaining time. When hovering the Notification, the progress bar will be paused if `pauseTimeoutOnHover` is enabled; otherwise, it won't stop.
|
||||||
|
|
||||||
::component-card
|
::component-card
|
||||||
---
|
---
|
||||||
@@ -149,6 +149,7 @@ baseProps:
|
|||||||
description: 'This is a notification.'
|
description: 'This is a notification.'
|
||||||
props:
|
props:
|
||||||
timeout: 60000
|
timeout: 60000
|
||||||
|
pauseTimeoutOnHover: true
|
||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
|
|||||||
@@ -156,38 +156,7 @@ Use the `searchableLazy` prop to control the immediacy of data requests.
|
|||||||
---
|
---
|
||||||
component: 'select-menu-example-search-async'
|
component: 'select-menu-example-search-async'
|
||||||
componentProps:
|
componentProps:
|
||||||
class: 'w-full lg:w-48'
|
class: 'w-full lg:w-48'
|
||||||
---
|
|
||||||
::
|
|
||||||
|
|
||||||
## Clearable
|
|
||||||
The `clearable` prop allows users to easily remove their selected option(s) with a clear button.
|
|
||||||
|
|
||||||
::component-example
|
|
||||||
---
|
|
||||||
component: 'select-menu-example-clearable'
|
|
||||||
componentProps:
|
|
||||||
class: 'w-full lg:w-52'
|
|
||||||
---
|
|
||||||
::
|
|
||||||
|
|
||||||
|
|
||||||
### Customization
|
|
||||||
#### Slot Props
|
|
||||||
The slot provides four key props:
|
|
||||||
|
|
||||||
| Prop | Type | Description |
|
|
||||||
|------|------|-------------|
|
|
||||||
| `selected` | `Object` | The currently selected value/item in the component |
|
|
||||||
| `disabled` | `Boolean` | Whether the component is in a disabled state |
|
|
||||||
| `loading` | `Boolean` | Whether the component is in a loading state |
|
|
||||||
| `onClear` | `Function` | Callback function to clear the selected value when the clear button is clicked |
|
|
||||||
|
|
||||||
::component-example
|
|
||||||
---
|
|
||||||
component: 'select-menu-example-clearable-customization'
|
|
||||||
componentProps:
|
|
||||||
class: 'w-full lg:w-52'
|
|
||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ extraClass: 'overflow-hidden'
|
|||||||
padding: false
|
padding: false
|
||||||
component: 'table-example-columns-selectable'
|
component: 'table-example-columns-selectable'
|
||||||
componentProps:
|
componentProps:
|
||||||
class: 'flex-1 flex-col overflow-hidden'
|
class: 'flex-1 flex-col overflow-hidden min-h-[230px]'
|
||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
@@ -346,7 +346,7 @@ componentProps:
|
|||||||
|
|
||||||
### Contextmenu
|
### Contextmenu
|
||||||
|
|
||||||
Use the `contextmenu` listener on your Table to make the rows righ-clickable. The function will receive the original event as the first argument and the row as the second argument.
|
Use the `contextmenu` listener on your Table to make the rows right-clickable. The function will receive the original event as the first argument and the row as the second argument.
|
||||||
|
|
||||||
You can use this to open a [ContextMenu](/components/context-menu) for that row.
|
You can use this to open a [ContextMenu](/components/context-menu) for that row.
|
||||||
|
|
||||||
|
|||||||
@@ -27,8 +27,7 @@ export default defineNuxtConfig({
|
|||||||
'@nuxtjs/plausible',
|
'@nuxtjs/plausible',
|
||||||
'@vueuse/nuxt',
|
'@vueuse/nuxt',
|
||||||
'nuxt-component-meta',
|
'nuxt-component-meta',
|
||||||
'nuxt-cloudflare-analytics',
|
'nuxt-cloudflare-analytics'
|
||||||
'modules/content-examples-code'
|
|
||||||
],
|
],
|
||||||
|
|
||||||
site: {
|
site: {
|
||||||
|
|||||||
@@ -3,28 +3,29 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@iconify-json/heroicons": "^1.2.1",
|
"@iconify-json/heroicons": "^1.2.2",
|
||||||
"@iconify-json/simple-icons": "^1.2.11",
|
"@iconify-json/lucide": "^1.2.23",
|
||||||
"@iconify-json/vscode-icons": "^1.2.2",
|
"@iconify-json/simple-icons": "^1.2.19",
|
||||||
|
"@iconify-json/vscode-icons": "^1.2.10",
|
||||||
"@nuxt/content": "^2.13.4",
|
"@nuxt/content": "^2.13.4",
|
||||||
"@nuxt/fonts": "^0.10.2",
|
"@nuxt/fonts": "^0.10.3",
|
||||||
"@nuxt/image": "^1.8.1",
|
"@nuxt/image": "^1.9.0",
|
||||||
"@nuxt/ui": "latest",
|
"@nuxt/ui": "latest",
|
||||||
"@nuxt/ui-pro": "^1.5.0",
|
"@nuxt/ui-pro": "^1.6.0",
|
||||||
"@nuxtjs/plausible": "^1.0.3",
|
"@nuxtjs/plausible": "^1.2.0",
|
||||||
"@octokit/rest": "^21.0.2",
|
"@octokit/rest": "^21.1.0",
|
||||||
"@vueuse/nuxt": "^11.2.0",
|
"@vueuse/nuxt": "^12.4.0",
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
"joi": "^17.13.3",
|
"joi": "^17.13.3",
|
||||||
"nuxt": "^3.14.159",
|
"nuxt": "^3.15.1",
|
||||||
"nuxt-cloudflare-analytics": "^1.0.8",
|
"nuxt-cloudflare-analytics": "^1.0.8",
|
||||||
"nuxt-component-meta": "^0.9.0",
|
"nuxt-component-meta": "^0.9.0",
|
||||||
"nuxt-og-image": "^3.0.8",
|
"nuxt-og-image": "^4.0.2",
|
||||||
"prettier": "^3.3.3",
|
"prettier": "^3.4.2",
|
||||||
"ufo": "^1.5.4",
|
"ufo": "^1.5.4",
|
||||||
"v-calendar": "^3.1.2",
|
"v-calendar": "^3.1.2",
|
||||||
"valibot": "^0.42.1",
|
"valibot": "^0.42.1",
|
||||||
"yup": "^1.4.0",
|
"yup": "^1.6.1",
|
||||||
"zod": "^3.23.8"
|
"zod": "^3.24.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
53
package.json
53
package.json
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"name": "@nuxt/ui",
|
"name": "@nuxt/ui",
|
||||||
"description": "A UI Library for Modern Web Apps, powered by Vue & Tailwind CSS.",
|
"description": "A UI Library for Modern Web Apps, powered by Vue & Tailwind CSS.",
|
||||||
"version": "2.19.2",
|
"version": "2.21.0",
|
||||||
"packageManager": "pnpm@9.12.3",
|
"packageManager": "pnpm@9.15.3",
|
||||||
"repository": "nuxt/ui",
|
"repository": "nuxt/ui",
|
||||||
"homepage": "https://ui.nuxt.com",
|
"homepage": "https://ui.nuxt.com",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@@ -34,54 +34,55 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@headlessui/tailwindcss": "^0.2.1",
|
"@headlessui/tailwindcss": "^0.2.1",
|
||||||
"@headlessui/vue": "^1.7.23",
|
"@headlessui/vue": "^1.7.23",
|
||||||
"@iconify-json/heroicons": "^1.2.1",
|
"@iconify-json/heroicons": "^1.2.2",
|
||||||
"@nuxt/icon": "^1.7.2",
|
"@nuxt/icon": "^1.10.3",
|
||||||
"@nuxt/kit": "^3.14.159",
|
"@nuxt/kit": "^3.15.1",
|
||||||
"@nuxtjs/color-mode": "^3.5.2",
|
"@nuxtjs/color-mode": "^3.5.2",
|
||||||
"@nuxtjs/tailwindcss": "^6.12.2",
|
"@nuxtjs/tailwindcss": "^6.13.1",
|
||||||
"@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.9",
|
"@tailwindcss/forms": "^0.5.10",
|
||||||
"@tailwindcss/typography": "^0.5.15",
|
"@tailwindcss/typography": "^0.5.16",
|
||||||
"@vueuse/core": "^11.2.0",
|
"@vueuse/core": "^12.4.0",
|
||||||
"@vueuse/integrations": "^11.2.0",
|
"@vueuse/integrations": "^12.4.0",
|
||||||
"@vueuse/math": "^11.2.0",
|
"@vueuse/math": "^12.4.0",
|
||||||
"defu": "^6.1.4",
|
"defu": "^6.1.4",
|
||||||
"fuse.js": "^7.0.0",
|
"fuse.js": "^7.0.0",
|
||||||
"ohash": "^1.1.4",
|
"ohash": "^1.1.4",
|
||||||
"pathe": "^1.1.2",
|
"pathe": "^2.0.1",
|
||||||
"scule": "^1.3.0",
|
"scule": "^1.3.0",
|
||||||
"tailwind-merge": "^2.5.4",
|
"tailwind-merge": "^2.6.0",
|
||||||
"tailwindcss": "^3.4.14"
|
"tailwindcss": "^3.4.17"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@nuxt/eslint-config": "^0.6.1",
|
"@nuxt/eslint-config": "^0.7.5",
|
||||||
"@nuxt/module-builder": "^0.8.4",
|
"@nuxt/module-builder": "^0.8.4",
|
||||||
"@nuxt/test-utils": "^3.14.4",
|
"@nuxt/test-utils": "^3.15.4",
|
||||||
"@release-it/conventional-changelog": "^9.0.2",
|
"@release-it/conventional-changelog": "^10.0.0",
|
||||||
"@vue/test-utils": "^2.4.6",
|
"@vue/test-utils": "^2.4.6",
|
||||||
"eslint": "^9.14.0",
|
"eslint": "^9.18.0",
|
||||||
"happy-dom": "^14.12.3",
|
"happy-dom": "^14.12.3",
|
||||||
"joi": "^17.13.3",
|
"joi": "^17.13.3",
|
||||||
"nuxt": "^3.14.159",
|
"nuxt": "^3.15.1",
|
||||||
"release-it": "^17.10.0",
|
"release-it": "^18.1.1",
|
||||||
"superstruct": "^2.0.2",
|
"superstruct": "^2.0.2",
|
||||||
"unbuild": "^2.0.0",
|
"typescript": "^5.6.3",
|
||||||
"valibot": "^0.42.1",
|
"valibot": "^0.42.1",
|
||||||
"valibot30": "npm:valibot@0.30.0",
|
"valibot30": "npm:valibot@0.30.0",
|
||||||
"valibot31": "npm:valibot@0.31.0",
|
"valibot31": "npm:valibot@0.31.0",
|
||||||
"vitest": "^2.1.4",
|
"vitest": "^2.1.8",
|
||||||
"vitest-environment-nuxt": "^1.0.1",
|
"vitest-environment-nuxt": "^1.0.1",
|
||||||
"vue-tsc": "^2.1.10",
|
"vue-tsc": "^2.1.10",
|
||||||
"yup": "^1.4.0",
|
"yup": "^1.6.1",
|
||||||
"zod": "^3.23.8"
|
"zod": "^3.24.1"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"@nuxt/ui": "workspace:*",
|
"@nuxt/ui": "workspace:*",
|
||||||
"@nuxt/content": "2.13.2",
|
"@nuxt/content": "2.13.2",
|
||||||
"@nuxtjs/mdc": "0.9.0",
|
"@nuxtjs/mdc": "0.9.0",
|
||||||
"nuxt": "3.13.2",
|
"chokidar": "3.6.0",
|
||||||
"@nuxt/kit": "3.13.2"
|
"vue-tsc": "2.1.10",
|
||||||
|
"typescript": "5.6.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,6 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nuxt/ui": "latest",
|
"@nuxt/ui": "latest",
|
||||||
"nuxt": "^3.14.159"
|
"nuxt": "^3.15.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
6853
pnpm-lock.yaml
generated
6853
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,6 @@
|
|||||||
import { createRequire } from 'node:module'
|
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 { ConfigExtension, DefaultClassGroupIds, DefaultThemeGroupIds } from 'tailwind-merge'
|
||||||
import { name, version } from '../package.json'
|
import { name, version } from '../package.json'
|
||||||
import createTemplates from './templates'
|
import createTemplates from './templates'
|
||||||
import type * as config from './runtime/ui.config'
|
import type * as config from './runtime/ui.config'
|
||||||
@@ -20,6 +21,7 @@ type UI = {
|
|||||||
gray?: string
|
gray?: string
|
||||||
colors?: string[]
|
colors?: string[]
|
||||||
strategy?: Strategy
|
strategy?: Strategy
|
||||||
|
tailwindMerge?: ConfigExtension<DefaultClassGroupIds, DefaultThemeGroupIds>
|
||||||
[key: string]: any
|
[key: string]: any
|
||||||
} & DeepPartial<typeof config, string | number | boolean>
|
} & DeepPartial<typeof config, string | number | boolean>
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
:class="[ui.th.base, ui.th.padding, ui.th.color, ui.th.font, ui.th.size, column.key === 'select' && ui.checkbox.padding, column.class]"
|
:class="[ui.th.base, ui.th.padding, ui.th.color, ui.th.font, ui.th.size, column.key === 'select' && ui.checkbox.padding, column.class]"
|
||||||
:aria-sort="getAriaSort(column)"
|
:aria-sort="getAriaSort(column)"
|
||||||
>
|
>
|
||||||
<slot v-if="!singleSelect && modelValue && (column.key === 'select' || shouldRenderColumnInFirstPlace(index, 'select'))" name="select-header" :indeterminate="indeterminate" :checked="isAllRowChecked" :change="onChange">
|
<slot v-if="!singleSelect && modelValue && column.key === 'select'" name="select-header" :indeterminate="indeterminate" :checked="isAllRowChecked" :change="onChange">
|
||||||
<UCheckbox
|
<UCheckbox
|
||||||
:model-value="isAllRowChecked"
|
:model-value="isAllRowChecked"
|
||||||
:indeterminate="indeterminate"
|
:indeterminate="indeterminate"
|
||||||
@@ -93,13 +93,12 @@
|
|||||||
/>
|
/>
|
||||||
</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, column?.rowClass, row[column.key]?.class, column.key === 'select' && ui.checkbox.padding]">
|
<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, column?.rowClass, row[column.key]?.class, column.key === 'select' && ui.checkbox.padding]">
|
||||||
<slot v-if="modelValue && (column.key === 'select' || shouldRenderColumnInFirstPlace(subIndex, 'select')) " name="select-data" :checked="isSelected(row)" :change="(ev: boolean) => onChangeCheckbox(ev, row)">
|
<slot v-if="modelValue && column.key === 'select' " name="select-data" :checked="isSelected(row)" :change="(ev: boolean) => onChangeCheckbox(ev, row)">
|
||||||
<UCheckbox
|
<UCheckbox
|
||||||
:model-value="isSelected(row)"
|
:model-value="isSelected(row)"
|
||||||
v-bind="ui.default.checkbox"
|
v-bind="ui.default.checkbox"
|
||||||
aria-label="Select row"
|
aria-label="Select row"
|
||||||
@change="onChangeCheckbox($event, row)"
|
@change="onChangeCheckbox($event, row)"
|
||||||
@click.capture.stop="() => onSelect(row)"
|
|
||||||
/>
|
/>
|
||||||
</slot>
|
</slot>
|
||||||
|
|
||||||
@@ -144,7 +143,7 @@ import UButton from '../elements/Button.vue'
|
|||||||
import UProgress from '../elements/Progress.vue'
|
import UProgress from '../elements/Progress.vue'
|
||||||
import UCheckbox from '../forms/Checkbox.vue'
|
import UCheckbox from '../forms/Checkbox.vue'
|
||||||
import { useUI } from '../../composables/useUI'
|
import { useUI } from '../../composables/useUI'
|
||||||
import { mergeConfig, get } from '../../utils'
|
import { get, mergeConfig } from '../../utils'
|
||||||
import type { TableRow, TableColumn, Strategy, Button, ProgressColor, ProgressAnimation, DeepPartial, Expanded } from '../../types/index'
|
import type { TableRow, TableColumn, Strategy, Button, ProgressColor, ProgressAnimation, DeepPartial, Expanded } from '../../types/index'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
@@ -274,7 +273,30 @@ export default defineComponent({
|
|||||||
setup(props, { emit, attrs: $attrs }) {
|
setup(props, { emit, attrs: $attrs }) {
|
||||||
const { ui, attrs } = useUI('table', toRef(props, 'ui'), config, toRef(props, 'class'))
|
const { ui, attrs } = useUI('table', toRef(props, 'ui'), config, toRef(props, 'class'))
|
||||||
|
|
||||||
const columns = computed(() => props.columns ?? Object.keys(props.rows[0] ?? {}).map(key => ({ key, label: upperFirst(key), sortable: false, class: undefined, sort: defaultSort }) as TableColumn))
|
const columns = computed(() => {
|
||||||
|
const defaultColumns = props.columns ?? (
|
||||||
|
Object.keys(props.rows[0]).map(key => ({
|
||||||
|
key,
|
||||||
|
label: upperFirst(key),
|
||||||
|
sortable: false,
|
||||||
|
class: undefined,
|
||||||
|
sort: defaultSort
|
||||||
|
}))
|
||||||
|
) as TableColumn[]
|
||||||
|
|
||||||
|
const hasColumnSelect = defaultColumns.find(v => v.key === 'select')
|
||||||
|
|
||||||
|
if (hasColumnSelect || !props.modelValue) {
|
||||||
|
return defaultColumns
|
||||||
|
}
|
||||||
|
|
||||||
|
return [{
|
||||||
|
key: 'select',
|
||||||
|
sortable: false,
|
||||||
|
class: undefined,
|
||||||
|
sort: defaultSort
|
||||||
|
}, ...defaultColumns]
|
||||||
|
})
|
||||||
|
|
||||||
const sort = useVModel(props, 'sort', emit, { passive: true, defaultValue: defu({}, props.sort, { column: null, direction: 'asc' }) })
|
const sort = useVModel(props, 'sort', emit, { passive: true, defaultValue: defu({}, props.sort, { column: null, direction: 'asc' }) })
|
||||||
const expand = useVModel(props, 'expand', emit, {
|
const expand = useVModel(props, 'expand', emit, {
|
||||||
@@ -422,8 +444,7 @@ export default defineComponent({
|
|||||||
if (checked) {
|
if (checked) {
|
||||||
selected.value = props.singleSelect ? [row] : [...selected.value, row]
|
selected.value = props.singleSelect ? [row] : [...selected.value, row]
|
||||||
} else {
|
} else {
|
||||||
const index = selected.value.findIndex(item => compare(item, row))
|
selected.value = selected.value.filter(value => !compare(toRaw(value), toRaw(row)))
|
||||||
selected.value.splice(index, 1)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -435,13 +456,6 @@ export default defineComponent({
|
|||||||
return expand.value?.openedRows ? expand.value.openedRows.some(openedRow => compare(openedRow, row)) : false
|
return expand.value?.openedRows ? expand.value.openedRows.some(openedRow => compare(openedRow, row)) : false
|
||||||
}
|
}
|
||||||
|
|
||||||
function shouldRenderColumnInFirstPlace(index: number, key: string) {
|
|
||||||
if (!props.columns) {
|
|
||||||
return index === 0
|
|
||||||
}
|
|
||||||
return index === 0 && !props.columns.find(col => col.key === key)
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleOpened(row: TableRow) {
|
function toggleOpened(row: TableRow) {
|
||||||
expand.value = {
|
expand.value = {
|
||||||
openedRows: isExpanded(row) ? expand.value.openedRows.filter(v => !compare(v, row)) : props.multipleExpand ? [...expand.value.openedRows, row] : [row],
|
openedRows: isExpanded(row) ? expand.value.openedRows.filter(v => !compare(v, row)) : props.multipleExpand ? [...expand.value.openedRows, row] : [row],
|
||||||
@@ -502,7 +516,6 @@ export default defineComponent({
|
|||||||
toggleOpened,
|
toggleOpened,
|
||||||
getAriaSort,
|
getAriaSort,
|
||||||
isExpanded,
|
isExpanded,
|
||||||
shouldRenderColumnInFirstPlace,
|
|
||||||
retriggerSlot
|
retriggerSlot
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ export default defineComponent({
|
|||||||
default: () => ({})
|
default: () => ({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: ['open'],
|
emits: ['open', 'close'],
|
||||||
setup(props, { emit }) {
|
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'))
|
||||||
|
|
||||||
@@ -142,6 +142,8 @@ export default defineComponent({
|
|||||||
|
|
||||||
if (!isOpenBefore && isOpenAfter) {
|
if (!isOpenBefore && isOpenAfter) {
|
||||||
emit('open', index)
|
emit('open', index)
|
||||||
|
} else if (isOpenBefore && !isOpenAfter) {
|
||||||
|
emit('close', index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, { immediate: true })
|
}, { immediate: true })
|
||||||
|
|||||||
@@ -42,13 +42,13 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, toRef, defineComponent } from 'vue'
|
import { computed, toRef, defineComponent } from 'vue'
|
||||||
import type { PropType } from 'vue'
|
import type { PropType } from 'vue'
|
||||||
import { twMerge, twJoin } from 'tailwind-merge'
|
import { twJoin } from 'tailwind-merge'
|
||||||
import UIcon from '../elements/Icon.vue'
|
import UIcon from '../elements/Icon.vue'
|
||||||
import UAvatar from '../elements/Avatar.vue'
|
import UAvatar from '../elements/Avatar.vue'
|
||||||
import UButton from '../elements/Button.vue'
|
import UButton from '../elements/Button.vue'
|
||||||
import { useUI } from '../../composables/useUI'
|
import { useUI } from '../../composables/useUI'
|
||||||
import type { Avatar, Button, AlertColor, AlertVariant, AlertAction, Strategy, DeepPartial } from '../../types/index'
|
import type { Avatar, Button, AlertColor, AlertVariant, AlertAction, Strategy, DeepPartial } from '../../types/index'
|
||||||
import { mergeConfig } from '../../utils'
|
import { mergeConfig, twMerge } from '../../utils'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
import { alert } from '#ui/ui.config'
|
import { alert } from '#ui/ui.config'
|
||||||
|
|||||||
@@ -23,10 +23,10 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, ref, computed, toRef, watch } from 'vue'
|
import { defineComponent, ref, computed, toRef, watch } from 'vue'
|
||||||
import type { PropType } from 'vue'
|
import type { PropType } from 'vue'
|
||||||
import { twMerge, twJoin } from 'tailwind-merge'
|
import { twJoin } from 'tailwind-merge'
|
||||||
import UIcon from '../elements/Icon.vue'
|
import UIcon from '../elements/Icon.vue'
|
||||||
import { useUI } from '../../composables/useUI'
|
import { useUI } from '../../composables/useUI'
|
||||||
import { mergeConfig } from '../../utils'
|
import { mergeConfig, twMerge } from '../../utils'
|
||||||
import type { AvatarSize, AvatarChipColor, AvatarChipPosition, Strategy, DeepPartial } from '../../types/index'
|
import type { AvatarSize, AvatarChipColor, AvatarChipPosition, Strategy, DeepPartial } from '../../types/index'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { h, cloneVNode, computed, toRef, defineComponent } from 'vue'
|
import { h, cloneVNode, computed, toRef, defineComponent } from 'vue'
|
||||||
import type { PropType } from 'vue'
|
import type { PropType } from 'vue'
|
||||||
import { twMerge, twJoin } from 'tailwind-merge'
|
import { twJoin } from 'tailwind-merge'
|
||||||
import { useUI } from '../../composables/useUI'
|
import { useUI } from '../../composables/useUI'
|
||||||
import { mergeConfig, getSlotsChildren } from '../../utils'
|
import { getSlotsChildren, mergeConfig, twMerge } from '../../utils'
|
||||||
import type { AvatarSize, DeepPartial, Strategy } from '../../types/index'
|
import type { AvatarSize, DeepPartial, Strategy } from '../../types/index'
|
||||||
import UAvatar from './Avatar.vue'
|
import UAvatar from './Avatar.vue'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
|
|||||||
@@ -1,15 +1,28 @@
|
|||||||
<template>
|
<template>
|
||||||
<span :class="badgeClass" v-bind="attrs">
|
<span :class="badgeClass" v-bind="attrs">
|
||||||
<slot>{{ label }}</slot>
|
<slot name="leading">
|
||||||
|
<UIcon v-if="isLeading && leadingIconName" :name="leadingIconName" :class="leadingIconClass" aria-hidden="true" />
|
||||||
|
</slot>
|
||||||
|
|
||||||
|
<slot>
|
||||||
|
<span v-if="label">
|
||||||
|
{{ label }}
|
||||||
|
</span>
|
||||||
|
</slot>
|
||||||
|
|
||||||
|
<slot name="trailing">
|
||||||
|
<UIcon v-if="isTrailing && trailingIconName" :name="trailingIconName" :class="trailingIconClass" aria-hidden="true" />
|
||||||
|
</slot>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, toRef, defineComponent } from 'vue'
|
import { computed, toRef, defineComponent } from 'vue'
|
||||||
import type { PropType } from 'vue'
|
import type { PropType } from 'vue'
|
||||||
import { twMerge, twJoin } from 'tailwind-merge'
|
import { twJoin } from 'tailwind-merge'
|
||||||
|
import UIcon from '../elements/Icon.vue'
|
||||||
import { useUI } from '../../composables/useUI'
|
import { useUI } from '../../composables/useUI'
|
||||||
import { mergeConfig } from '../../utils'
|
import { mergeConfig, twMerge } from '../../utils'
|
||||||
import { useInjectButtonGroup } from '../../composables/useButtonGroup'
|
import { useInjectButtonGroup } from '../../composables/useButtonGroup'
|
||||||
import type { BadgeColor, BadgeSize, BadgeVariant, DeepPartial, Strategy } from '../../types/index'
|
import type { BadgeColor, BadgeSize, BadgeVariant, DeepPartial, Strategy } from '../../types/index'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
@@ -19,6 +32,9 @@ import { badge } from '#ui/ui.config'
|
|||||||
const config = mergeConfig<typeof badge>(appConfig.ui.strategy, appConfig.ui.badge, badge)
|
const config = mergeConfig<typeof badge>(appConfig.ui.strategy, appConfig.ui.badge, badge)
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
components: {
|
||||||
|
UIcon
|
||||||
|
},
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
props: {
|
props: {
|
||||||
size: {
|
size: {
|
||||||
@@ -49,6 +65,26 @@ export default defineComponent({
|
|||||||
type: [String, Number],
|
type: [String, Number],
|
||||||
default: null
|
default: null
|
||||||
},
|
},
|
||||||
|
icon: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
leadingIcon: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
trailingIcon: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
trailing: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
leading: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
class: {
|
class: {
|
||||||
type: [String, Object, Array] as PropType<any>,
|
type: [String, Object, Array] as PropType<any>,
|
||||||
default: () => ''
|
default: () => ''
|
||||||
@@ -63,6 +99,14 @@ export default defineComponent({
|
|||||||
|
|
||||||
const { size, rounded } = useInjectButtonGroup({ ui, props })
|
const { size, rounded } = useInjectButtonGroup({ ui, props })
|
||||||
|
|
||||||
|
const isLeading = computed(() => {
|
||||||
|
return (props.icon && props.leading) || (props.icon && !props.trailing) || !props.trailing || props.leadingIcon
|
||||||
|
})
|
||||||
|
|
||||||
|
const isTrailing = computed(() => {
|
||||||
|
return (props.icon && props.trailing) || props.trailing || props.trailingIcon
|
||||||
|
})
|
||||||
|
|
||||||
const badgeClass = computed(() => {
|
const badgeClass = computed(() => {
|
||||||
const variant = ui.value.color?.[props.color as string]?.[props.variant as string] || ui.value.variant[props.variant]
|
const variant = ui.value.color?.[props.color as string]?.[props.variant as string] || ui.value.variant[props.variant]
|
||||||
|
|
||||||
@@ -71,13 +115,42 @@ export default defineComponent({
|
|||||||
ui.value.font,
|
ui.value.font,
|
||||||
rounded.value,
|
rounded.value,
|
||||||
ui.value.size[size.value],
|
ui.value.size[size.value],
|
||||||
|
ui.value.gap[size.value],
|
||||||
variant?.replaceAll('{color}', props.color)
|
variant?.replaceAll('{color}', props.color)
|
||||||
), props.class)
|
), props.class)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const leadingIconName = computed(() => {
|
||||||
|
return props.leadingIcon || props.icon
|
||||||
|
})
|
||||||
|
|
||||||
|
const trailingIconName = computed(() => {
|
||||||
|
return props.trailingIcon || props.icon
|
||||||
|
})
|
||||||
|
|
||||||
|
const leadingIconClass = computed(() => {
|
||||||
|
return twJoin(
|
||||||
|
ui.value.icon.base,
|
||||||
|
ui.value.icon.size[size.value]
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
const trailingIconClass = computed(() => {
|
||||||
|
return twJoin(
|
||||||
|
ui.value.icon.base,
|
||||||
|
ui.value.icon.size[size.value]
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
attrs,
|
attrs,
|
||||||
badgeClass
|
isLeading,
|
||||||
|
isTrailing,
|
||||||
|
badgeClass,
|
||||||
|
leadingIconName,
|
||||||
|
trailingIconName,
|
||||||
|
leadingIconClass,
|
||||||
|
trailingIconClass
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -19,11 +19,11 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, defineComponent, toRef } from 'vue'
|
import { computed, defineComponent, toRef } from 'vue'
|
||||||
import type { PropType } from 'vue'
|
import type { PropType } from 'vue'
|
||||||
import { twMerge, twJoin } from 'tailwind-merge'
|
import { twJoin } from 'tailwind-merge'
|
||||||
import UIcon from '../elements/Icon.vue'
|
import UIcon from '../elements/Icon.vue'
|
||||||
import ULink from '../elements/Link.vue'
|
import ULink from '../elements/Link.vue'
|
||||||
import { useUI } from '../../composables/useUI'
|
import { useUI } from '../../composables/useUI'
|
||||||
import { mergeConfig, nuxtLinkProps, getNuxtLinkProps } from '../../utils'
|
import { getNuxtLinkProps, mergeConfig, nuxtLinkProps, twMerge } from '../../utils'
|
||||||
import { useInjectButtonGroup } from '../../composables/useButtonGroup'
|
import { useInjectButtonGroup } from '../../composables/useButtonGroup'
|
||||||
import type { ButtonColor, ButtonSize, ButtonVariant, DeepPartial, Strategy } from '../../types/index'
|
import type { ButtonColor, ButtonSize, ButtonVariant, DeepPartial, Strategy } from '../../types/index'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { h, computed, toRef, defineComponent } from 'vue'
|
import { h, computed, toRef, defineComponent } from 'vue'
|
||||||
import type { PropType } from 'vue'
|
import type { PropType } from 'vue'
|
||||||
import { twMerge, twJoin } from 'tailwind-merge'
|
import { twJoin } from 'tailwind-merge'
|
||||||
import { useUI } from '../../composables/useUI'
|
import { useUI } from '../../composables/useUI'
|
||||||
import { mergeConfig, getSlotsChildren } from '../../utils'
|
import { getSlotsChildren, mergeConfig, twMerge } from '../../utils'
|
||||||
import { useProvideButtonGroup } from '../../composables/useButtonGroup'
|
import { useProvideButtonGroup } from '../../composables/useButtonGroup'
|
||||||
import type { ButtonSize, DeepPartial, Strategy } from '../../types/index'
|
import type { ButtonSize, DeepPartial, Strategy } from '../../types/index'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
|
|||||||
@@ -58,9 +58,8 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { ref, toRef, 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 { useScroll, useResizeObserver, useElementSize } from '@vueuse/core'
|
import { useScroll, useResizeObserver, useElementSize } from '@vueuse/core'
|
||||||
import { mergeConfig } from '../../utils'
|
import { mergeConfig, twMerge } from '../../utils'
|
||||||
import UButton from '../elements/Button.vue'
|
import UButton from '../elements/Button.vue'
|
||||||
import type { Strategy, Button, DeepPartial } from '../../types/index'
|
import type { Strategy, Button, DeepPartial } from '../../types/index'
|
||||||
import { useUI } from '../../composables/useUI'
|
import { useUI } from '../../composables/useUI'
|
||||||
@@ -106,7 +105,7 @@ export default defineComponent({
|
|||||||
default: () => ''
|
default: () => ''
|
||||||
},
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<DeepPartial<typeof config & { strategy?: Strategy }>>,
|
type: Object as PropType<DeepPartial<typeof config> & { strategy?: Strategy }>,
|
||||||
default: undefined
|
default: undefined
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -60,13 +60,13 @@ import { defineComponent, ref, computed, watch, toRef, onMounted, resolveCompone
|
|||||||
import type { PropType } from 'vue'
|
import type { PropType } from 'vue'
|
||||||
import { Menu as HMenu, MenuButton as HMenuButton, MenuItems as HMenuItems, MenuItem as HMenuItem, provideUseId } from '@headlessui/vue'
|
import { Menu as HMenu, MenuButton as HMenuButton, MenuItems as HMenuItems, MenuItem as HMenuItem, provideUseId } from '@headlessui/vue'
|
||||||
import { defu } from 'defu'
|
import { defu } from 'defu'
|
||||||
import { twMerge, twJoin } from 'tailwind-merge'
|
import { twJoin } from 'tailwind-merge'
|
||||||
import UIcon from '../elements/Icon.vue'
|
import UIcon from '../elements/Icon.vue'
|
||||||
import UAvatar from '../elements/Avatar.vue'
|
import UAvatar from '../elements/Avatar.vue'
|
||||||
import UKbd from '../elements/Kbd.vue'
|
import UKbd from '../elements/Kbd.vue'
|
||||||
import { useUI } from '../../composables/useUI'
|
import { useUI } from '../../composables/useUI'
|
||||||
import { usePopper } from '../../composables/usePopper'
|
import { usePopper } from '../../composables/usePopper'
|
||||||
import { mergeConfig, getNuxtLinkProps } from '../../utils'
|
import { getNuxtLinkProps, mergeConfig, twMerge } from '../../utils'
|
||||||
import type { DeepPartial, DropdownItem, PopperOptions, Strategy } from '../../types/index'
|
import type { DeepPartial, DropdownItem, PopperOptions, Strategy } from '../../types/index'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
|
|||||||
@@ -7,9 +7,9 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { toRef, defineComponent, computed } from 'vue'
|
import { toRef, defineComponent, computed } from 'vue'
|
||||||
import type { PropType } from 'vue'
|
import type { PropType } from 'vue'
|
||||||
import { twMerge, twJoin } from 'tailwind-merge'
|
import { twJoin } from 'tailwind-merge'
|
||||||
import { useUI } from '../../composables/useUI'
|
import { useUI } from '../../composables/useUI'
|
||||||
import { mergeConfig } from '../../utils'
|
import { mergeConfig, twMerge } from '../../utils'
|
||||||
import type { DeepPartial, KbdSize, Strategy } from '../../types/index'
|
import type { DeepPartial, KbdSize, Strategy } from '../../types/index'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
|
|||||||
@@ -32,8 +32,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { isEqual } from 'ohash'
|
import { isEqual, diff } from 'ohash'
|
||||||
import { defineComponent } from 'vue'
|
import { type PropType, defineComponent } from 'vue'
|
||||||
import { nuxtLinkProps } from '../../utils'
|
import { nuxtLinkProps } from '../../utils'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
@@ -61,7 +61,7 @@ export default defineComponent({
|
|||||||
default: false
|
default: false
|
||||||
},
|
},
|
||||||
exactQuery: {
|
exactQuery: {
|
||||||
type: Boolean,
|
type: [Boolean, String] as PropType<boolean | 'partial'>,
|
||||||
default: false
|
default: false
|
||||||
},
|
},
|
||||||
exactHash: {
|
exactHash: {
|
||||||
@@ -74,9 +74,21 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
|
function isPartiallyEqual(item1, item2) {
|
||||||
|
const diffedKeys = diff(item1, item2).reduce((filtered, q) => {
|
||||||
|
if (q.type === 'added') {
|
||||||
|
filtered.push(q.key)
|
||||||
|
}
|
||||||
|
return filtered
|
||||||
|
}, [])
|
||||||
|
return isEqual(item1, item2, { excludeKeys: key => diffedKeys.includes(key) })
|
||||||
|
}
|
||||||
|
|
||||||
function resolveLinkClass(route, $route, { isActive, isExactActive }: { isActive: boolean, isExactActive: boolean }) {
|
function resolveLinkClass(route, $route, { isActive, isExactActive }: { isActive: boolean, isExactActive: boolean }) {
|
||||||
if (props.exactQuery && !isEqual(route.query, $route.query)) {
|
if (props.exactQuery === 'partial') {
|
||||||
return props.inactiveClass
|
if (!isPartiallyEqual(route.query, $route.query)) return props.inactiveClass
|
||||||
|
} else if (props.exactQuery === true) {
|
||||||
|
if (!isEqual(route.query, $route.query)) return props.inactiveClass
|
||||||
}
|
}
|
||||||
if (props.exactHash && route.hash !== $route.hash) {
|
if (props.exactHash && route.hash !== $route.hash) {
|
||||||
return props.inactiveClass
|
return props.inactiveClass
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import type { ComputedRef, VNode, SlotsType, PropType } from 'vue'
|
|||||||
import { twJoin } from 'tailwind-merge'
|
import { twJoin } from 'tailwind-merge'
|
||||||
import UIcon from '../elements/Icon.vue'
|
import UIcon from '../elements/Icon.vue'
|
||||||
import { useUI } from '../../composables/useUI'
|
import { useUI } from '../../composables/useUI'
|
||||||
import { mergeConfig, getSlotsChildren } from '../../utils'
|
import { getSlotsChildren, mergeConfig } from '../../utils'
|
||||||
import type { DeepPartial, Strategy, MeterSize } from '../../types/index'
|
import type { DeepPartial, Strategy, MeterSize } from '../../types/index'
|
||||||
import type Meter from './Meter.vue'
|
import type Meter from './Meter.vue'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
|
|||||||
@@ -32,10 +32,10 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, toRef, defineComponent } from 'vue'
|
import { computed, toRef, defineComponent } from 'vue'
|
||||||
import type { PropType } from 'vue'
|
import type { PropType } from 'vue'
|
||||||
import { twMerge, twJoin } from 'tailwind-merge'
|
import { twJoin } from 'tailwind-merge'
|
||||||
import { useUI } from '../../composables/useUI'
|
import { useUI } from '../../composables/useUI'
|
||||||
import { useFormGroup } from '../../composables/useFormGroup'
|
import { useFormGroup } from '../../composables/useFormGroup'
|
||||||
import { mergeConfig } from '../../utils'
|
import { mergeConfig, twMerge } from '../../utils'
|
||||||
import type { DeepPartial, Strategy } from '../../types/index'
|
import type { DeepPartial, Strategy } from '../../types/index'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
|
|||||||
@@ -60,6 +60,8 @@ export default defineComponent({
|
|||||||
const formId = useId()
|
const formId = useId()
|
||||||
const bus = useEventBus<FormEvent>(`form-${formId}`)
|
const bus = useEventBus<FormEvent>(`form-${formId}`)
|
||||||
|
|
||||||
|
const parsedValue = ref(null)
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
bus.on(async (event) => {
|
bus.on(async (event) => {
|
||||||
if (event.type !== 'submit' && props.validateOn?.includes(event.type)) {
|
if (event.type !== 'submit' && props.validateOn?.includes(event.type)) {
|
||||||
@@ -87,7 +89,7 @@ export default defineComponent({
|
|||||||
if (errors) {
|
if (errors) {
|
||||||
errs = errs.concat(errors)
|
errs = errs.concat(errors)
|
||||||
} else {
|
} else {
|
||||||
Object.assign(props.state, result)
|
parsedValue.value = result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,7 +132,7 @@ export default defineComponent({
|
|||||||
if (props.validateOn?.includes('submit')) {
|
if (props.validateOn?.includes('submit')) {
|
||||||
await validate()
|
await validate()
|
||||||
}
|
}
|
||||||
event.data = props.state
|
event.data = props.schema ? parsedValue.value : props.state
|
||||||
emit('submit', event)
|
emit('submit', event)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (!(error instanceof FormException)) {
|
if (!(error instanceof FormException)) {
|
||||||
@@ -321,7 +323,7 @@ async function validateYupSchema(
|
|||||||
schema: YupObjectSchema<any>
|
schema: YupObjectSchema<any>
|
||||||
): Promise<ValidateReturnSchema<typeof state>> {
|
): Promise<ValidateReturnSchema<typeof state>> {
|
||||||
try {
|
try {
|
||||||
const result = schema.validateSync(state, { abortEarly: false })
|
const result = await schema.validate(state, { abortEarly: false })
|
||||||
return {
|
return {
|
||||||
errors: null,
|
errors: null,
|
||||||
result
|
result
|
||||||
|
|||||||
@@ -33,12 +33,12 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { ref, computed, toRef, onMounted, defineComponent } from 'vue'
|
import { ref, computed, toRef, onMounted, defineComponent } from 'vue'
|
||||||
import type { PropType } from 'vue'
|
import type { PropType } from 'vue'
|
||||||
import { twMerge, twJoin } from 'tailwind-merge'
|
import { twJoin } from 'tailwind-merge'
|
||||||
import { defu } from 'defu'
|
import { defu } from 'defu'
|
||||||
import UIcon from '../elements/Icon.vue'
|
import UIcon from '../elements/Icon.vue'
|
||||||
import { useUI } from '../../composables/useUI'
|
import { useUI } from '../../composables/useUI'
|
||||||
import { useFormGroup } from '../../composables/useFormGroup'
|
import { useFormGroup } from '../../composables/useFormGroup'
|
||||||
import { mergeConfig, looseToNumber } from '../../utils'
|
import { looseToNumber, mergeConfig, twMerge } from '../../utils'
|
||||||
import { useInjectButtonGroup } from '../../composables/useButtonGroup'
|
import { useInjectButtonGroup } from '../../composables/useButtonGroup'
|
||||||
import type { InputSize, InputColor, InputVariant, Strategy, DeepPartial } from '../../types/index'
|
import type { InputSize, InputColor, InputVariant, Strategy, DeepPartial } from '../../types/index'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
|
|||||||
@@ -103,14 +103,14 @@ import {
|
|||||||
} from '@headlessui/vue'
|
} from '@headlessui/vue'
|
||||||
import { computedAsync, useDebounceFn } from '@vueuse/core'
|
import { computedAsync, useDebounceFn } from '@vueuse/core'
|
||||||
import { defu } from 'defu'
|
import { defu } from 'defu'
|
||||||
import { twMerge, twJoin } from 'tailwind-merge'
|
import { twJoin } from 'tailwind-merge'
|
||||||
import { isEqual } from 'ohash'
|
import { isEqual } from 'ohash'
|
||||||
import UIcon from '../elements/Icon.vue'
|
import UIcon from '../elements/Icon.vue'
|
||||||
import UAvatar from '../elements/Avatar.vue'
|
import UAvatar from '../elements/Avatar.vue'
|
||||||
import { useUI } from '../../composables/useUI'
|
import { useUI } from '../../composables/useUI'
|
||||||
import { usePopper } from '../../composables/usePopper'
|
import { usePopper } from '../../composables/usePopper'
|
||||||
import { useFormGroup } from '../../composables/useFormGroup'
|
import { useFormGroup } from '../../composables/useFormGroup'
|
||||||
import { get, mergeConfig } from '../../utils'
|
import { get, mergeConfig, twMerge } from '../../utils'
|
||||||
import { useInjectButtonGroup } from '../../composables/useButtonGroup'
|
import { useInjectButtonGroup } from '../../composables/useButtonGroup'
|
||||||
import type { InputSize, InputColor, InputVariant, PopperOptions, Strategy, DeepPartial } from '../../types/index'
|
import type { InputSize, InputColor, InputVariant, PopperOptions, Strategy, DeepPartial } from '../../types/index'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
@@ -293,6 +293,24 @@ export default defineComponent({
|
|||||||
|
|
||||||
const size = computed(() => sizeButtonGroup.value ?? sizeFormGroup.value)
|
const size = computed(() => sizeButtonGroup.value ?? sizeFormGroup.value)
|
||||||
|
|
||||||
|
const by = computed(() => {
|
||||||
|
if (!props.by) return undefined
|
||||||
|
|
||||||
|
if (typeof props.by === 'function') {
|
||||||
|
return props.by
|
||||||
|
}
|
||||||
|
|
||||||
|
const key = props.by
|
||||||
|
const hasDot = key.indexOf('.')
|
||||||
|
if (hasDot > 0) {
|
||||||
|
return (a: any, z: any) => {
|
||||||
|
return accessor(a, key) === accessor(z, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return key
|
||||||
|
})
|
||||||
|
|
||||||
const internalQuery = ref('')
|
const internalQuery = ref('')
|
||||||
const query = computed({
|
const query = computed({
|
||||||
get() {
|
get() {
|
||||||
@@ -305,9 +323,7 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const label = computed(() => {
|
const label = computed(() => {
|
||||||
if (!props.modelValue) {
|
if (!props.modelValue) return null
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
function getValue(value: any) {
|
function getValue(value: any) {
|
||||||
if (props.valueAttribute) {
|
if (props.valueAttribute) {
|
||||||
@@ -318,7 +334,7 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
function compareValues(value1: any, value2: any) {
|
function compareValues(value1: any, value2: any) {
|
||||||
if (props.by && typeof value1 === 'object' && typeof value2 === 'object') {
|
if (by.value && typeof by.value !== 'function' && typeof value1 === 'object' && typeof value2 === 'object') {
|
||||||
return isEqual(value1[props.by], value2[props.by])
|
return isEqual(value1[props.by], value2[props.by])
|
||||||
}
|
}
|
||||||
return isEqual(value1, value2)
|
return isEqual(value1, value2)
|
||||||
@@ -507,7 +523,9 @@ export default defineComponent({
|
|||||||
query,
|
query,
|
||||||
accessor,
|
accessor,
|
||||||
onUpdate,
|
onUpdate,
|
||||||
onQueryChange
|
onQueryChange,
|
||||||
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
|
by
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -31,10 +31,10 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, defineComponent, inject, toRef } from 'vue'
|
import { computed, defineComponent, inject, toRef } from 'vue'
|
||||||
import type { PropType } from 'vue'
|
import type { PropType } from 'vue'
|
||||||
import { twMerge, twJoin } from 'tailwind-merge'
|
import { twJoin } from 'tailwind-merge'
|
||||||
import { useUI } from '../../composables/useUI'
|
import { useUI } from '../../composables/useUI'
|
||||||
import { useFormGroup } from '../../composables/useFormGroup'
|
import { useFormGroup } from '../../composables/useFormGroup'
|
||||||
import { mergeConfig } from '../../utils'
|
import { mergeConfig, twMerge } from '../../utils'
|
||||||
import type { DeepPartial, Strategy } from '../../types/index'
|
import type { DeepPartial, Strategy } from '../../types/index'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ import { computed, defineComponent, provide, toRef } from 'vue'
|
|||||||
import type { PropType } from 'vue'
|
import type { PropType } from 'vue'
|
||||||
import { useUI } from '../../composables/useUI'
|
import { useUI } from '../../composables/useUI'
|
||||||
import { useFormGroup } from '../../composables/useFormGroup'
|
import { useFormGroup } from '../../composables/useFormGroup'
|
||||||
import { mergeConfig, get } from '../../utils'
|
import { get, mergeConfig } from '../../utils'
|
||||||
import type { DeepPartial, Strategy } from '../../types/index'
|
import type { DeepPartial, Strategy } from '../../types/index'
|
||||||
import URadio from './Radio.vue'
|
import URadio from './Radio.vue'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
|
|||||||
@@ -22,10 +22,10 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, toRef, defineComponent } from 'vue'
|
import { computed, toRef, defineComponent } from 'vue'
|
||||||
import type { PropType } from 'vue'
|
import type { PropType } from 'vue'
|
||||||
import { twMerge, twJoin } from 'tailwind-merge'
|
import { twJoin } from 'tailwind-merge'
|
||||||
import { useUI } from '../../composables/useUI'
|
import { useUI } from '../../composables/useUI'
|
||||||
import { useFormGroup } from '../../composables/useFormGroup'
|
import { useFormGroup } from '../../composables/useFormGroup'
|
||||||
import { mergeConfig } from '../../utils'
|
import { mergeConfig, twMerge } from '../../utils'
|
||||||
import type { RangeSize, RangeColor, Strategy, DeepPartial } from '../../types/index'
|
import type { RangeSize, RangeColor, Strategy, DeepPartial } from '../../types/index'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
|
|||||||
@@ -55,11 +55,11 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, toRef, defineComponent } from 'vue'
|
import { computed, toRef, defineComponent } from 'vue'
|
||||||
import type { PropType, ComputedRef } from 'vue'
|
import type { PropType, ComputedRef } from 'vue'
|
||||||
import { twMerge, twJoin } from 'tailwind-merge'
|
import { twJoin } from 'tailwind-merge'
|
||||||
import UIcon from '../elements/Icon.vue'
|
import UIcon from '../elements/Icon.vue'
|
||||||
import { useUI } from '../../composables/useUI'
|
import { useUI } from '../../composables/useUI'
|
||||||
import { useFormGroup } from '../../composables/useFormGroup'
|
import { useFormGroup } from '../../composables/useFormGroup'
|
||||||
import { mergeConfig, get } from '../../utils'
|
import { get, mergeConfig, twMerge } from '../../utils'
|
||||||
import { useInjectButtonGroup } from '../../composables/useButtonGroup'
|
import { useInjectButtonGroup } from '../../composables/useButtonGroup'
|
||||||
import type { SelectSize, SelectColor, SelectVariant, Strategy, DeepPartial } from '../../types/index'
|
import type { SelectSize, SelectColor, SelectVariant, Strategy, DeepPartial } from '../../types/index'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
|
|||||||
@@ -39,18 +39,6 @@
|
|||||||
<span v-if="label" :class="uiMenu.label">{{ label }}</span>
|
<span v-if="label" :class="uiMenu.label">{{ label }}</span>
|
||||||
<span v-else :class="uiMenu.label">{{ placeholder || ' ' }}</span>
|
<span v-else :class="uiMenu.label">{{ placeholder || ' ' }}</span>
|
||||||
</slot>
|
</slot>
|
||||||
<span v-if="canClearValue" :class="clearableWrapperClass">
|
|
||||||
<slot name="clearable" :selected="selected" :disabled="disabled" :loading="loading" @clear="onClear">
|
|
||||||
<UButton
|
|
||||||
:icon="clearableIcon"
|
|
||||||
size="xs"
|
|
||||||
class="p-0"
|
|
||||||
:class="clearableButtonClass"
|
|
||||||
variant="ghost"
|
|
||||||
@click.capture.stop="onClear"
|
|
||||||
/>
|
|
||||||
</slot>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span v-if="(isTrailing && trailingIconName) || $slots.trailing" :class="trailingWrapperIconClass">
|
<span v-if="(isTrailing && trailingIconName) || $slots.trailing" :class="trailingWrapperIconClass">
|
||||||
<slot name="trailing" :selected="selected" :disabled="disabled" :loading="loading">
|
<slot name="trailing" :selected="selected" :disabled="disabled" :loading="loading">
|
||||||
@@ -151,17 +139,16 @@ import {
|
|||||||
} from '@headlessui/vue'
|
} from '@headlessui/vue'
|
||||||
import { computedAsync, useDebounceFn } from '@vueuse/core'
|
import { computedAsync, useDebounceFn } from '@vueuse/core'
|
||||||
import { defu } from 'defu'
|
import { defu } from 'defu'
|
||||||
import { twMerge, twJoin } from 'tailwind-merge'
|
import { twJoin } from 'tailwind-merge'
|
||||||
import { isEqual } from 'ohash'
|
import { isEqual } from 'ohash'
|
||||||
import UIcon from '../elements/Icon.vue'
|
import UIcon from '../elements/Icon.vue'
|
||||||
import UAvatar from '../elements/Avatar.vue'
|
import UAvatar from '../elements/Avatar.vue'
|
||||||
import { useUI } from '../../composables/useUI'
|
import { useUI } from '../../composables/useUI'
|
||||||
import { usePopper } from '../../composables/usePopper'
|
import { usePopper } from '../../composables/usePopper'
|
||||||
import { useFormGroup } from '../../composables/useFormGroup'
|
import { useFormGroup } from '../../composables/useFormGroup'
|
||||||
import { get, mergeConfig } from '../../utils'
|
import { get, mergeConfig, twMerge } from '../../utils'
|
||||||
import { useInjectButtonGroup } from '../../composables/useButtonGroup'
|
import { useInjectButtonGroup } from '../../composables/useButtonGroup'
|
||||||
import type { SelectSize, SelectColor, SelectVariant, PopperOptions, Strategy, DeepPartial } from '../../types/index'
|
import type { SelectSize, SelectColor, SelectVariant, PopperOptions, Strategy, DeepPartial } from '../../types/index'
|
||||||
import type { Button } from '../../types/button'
|
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
import { select, selectMenu } from '#ui/ui.config'
|
import { select, selectMenu } from '#ui/ui.config'
|
||||||
@@ -346,18 +333,9 @@ export default defineComponent({
|
|||||||
uiMenu: {
|
uiMenu: {
|
||||||
type: Object as PropType<DeepPartial<typeof configMenu> & { strategy?: Strategy }>,
|
type: Object as PropType<DeepPartial<typeof configMenu> & { strategy?: Strategy }>,
|
||||||
default: () => ({})
|
default: () => ({})
|
||||||
},
|
|
||||||
clearable: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
clearableIcon: {
|
|
||||||
type: String,
|
|
||||||
default: () => config.default.clerableIcon
|
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
emits: ['update:modelValue', 'update:query', 'open', 'close', 'change', 'clear'],
|
emits: ['update:modelValue', 'update:query', 'open', 'close', 'change'],
|
||||||
setup(props, { emit, slots }) {
|
setup(props, { emit, slots }) {
|
||||||
if (import.meta.dev && props.multiple && !Array.isArray(props.modelValue)) {
|
if (import.meta.dev && props.multiple && !Array.isArray(props.modelValue)) {
|
||||||
console.warn(`[@nuxt/ui] The USelectMenu components needs to have a modelValue of type Array when using the multiple prop. Got '${typeof props.modelValue}' instead.`, props.modelValue)
|
console.warn(`[@nuxt/ui] The USelectMenu components needs to have a modelValue of type Array when using the multiple prop. Got '${typeof props.modelValue}' instead.`, props.modelValue)
|
||||||
@@ -370,6 +348,24 @@ export default defineComponent({
|
|||||||
|
|
||||||
const [trigger, container] = usePopper(popper.value)
|
const [trigger, container] = usePopper(popper.value)
|
||||||
|
|
||||||
|
const by = computed(() => {
|
||||||
|
if (!props.by) return undefined
|
||||||
|
|
||||||
|
if (typeof props.by === 'function') {
|
||||||
|
return props.by
|
||||||
|
}
|
||||||
|
|
||||||
|
const key = props.by
|
||||||
|
const hasDot = key.indexOf('.')
|
||||||
|
if (hasDot > 0) {
|
||||||
|
return (a: any, z: any) => {
|
||||||
|
return accessor(a, key) === accessor(z, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return key
|
||||||
|
})
|
||||||
|
|
||||||
const { size: sizeButtonGroup, rounded } = useInjectButtonGroup({ ui, props })
|
const { size: sizeButtonGroup, rounded } = useInjectButtonGroup({ ui, props })
|
||||||
const { emitFormBlur, emitFormChange, inputId, color, size: sizeFormGroup, name } = useFormGroup(props, config)
|
const { emitFormBlur, emitFormChange, inputId, color, size: sizeFormGroup, name } = useFormGroup(props, config)
|
||||||
|
|
||||||
@@ -388,8 +384,8 @@ export default defineComponent({
|
|||||||
|
|
||||||
const selected = computed(() => {
|
const selected = computed(() => {
|
||||||
function compareValues(value1: any, value2: any) {
|
function compareValues(value1: any, value2: any) {
|
||||||
if (props.by && typeof value1 === 'object' && typeof value2 === 'object') {
|
if (by.value && typeof by.value !== 'function' && typeof value1 === 'object' && typeof value2 === 'object') {
|
||||||
return isEqual(value1[props.by], value2[props.by])
|
return isEqual(value1[by.value], value2[by.value])
|
||||||
}
|
}
|
||||||
return isEqual(value1, value2)
|
return isEqual(value1, value2)
|
||||||
}
|
}
|
||||||
@@ -421,16 +417,12 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const label = computed(() => {
|
const label = computed(() => {
|
||||||
if (!selected.value) return null
|
if (!props.modelValue) return null
|
||||||
|
|
||||||
if (props.valueAttribute) {
|
|
||||||
return accessor(selected.value as Record<string, any>, props.optionAttribute)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Array.isArray(props.modelValue) && props.modelValue.length) {
|
if (Array.isArray(props.modelValue) && props.modelValue.length) {
|
||||||
return `${props.modelValue.length} selected`
|
return `${props.modelValue.length} selected`
|
||||||
} else if (['string', 'number'].includes(typeof props.modelValue)) {
|
} else if (['string', 'number'].includes(typeof props.modelValue)) {
|
||||||
return props.modelValue
|
return props.valueAttribute ? accessor(selected.value, props.optionAttribute) : props.modelValue
|
||||||
}
|
}
|
||||||
|
|
||||||
return accessor(props.modelValue as Record<string, any>, props.optionAttribute)
|
return accessor(props.modelValue as Record<string, any>, props.optionAttribute)
|
||||||
@@ -468,23 +460,6 @@ export default defineComponent({
|
|||||||
return props.leadingIcon || props.icon
|
return props.leadingIcon || props.icon
|
||||||
})
|
})
|
||||||
|
|
||||||
const canClearValue = computed(() => props.clearable && (Array.isArray(selected.value) ? selected.value.length > 0 : !!selected.value))
|
|
||||||
|
|
||||||
const clearableWrapperClass = computed(() => {
|
|
||||||
return twJoin(
|
|
||||||
ui.value.icon.clearable.wrapper,
|
|
||||||
ui.value.icon.clearable.padding[size.value]
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
const clearableButtonClass = computed(() => {
|
|
||||||
return twJoin(
|
|
||||||
ui.value.icon.base,
|
|
||||||
color.value && appConfig.ui.colors.includes(color.value) && ui.value.icon.color.replaceAll('{color}', color.value),
|
|
||||||
props.loading && ui.value.icon.loading
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
const trailingIconName = computed(() => {
|
const trailingIconName = computed(() => {
|
||||||
if (props.loading && !isLeading.value) {
|
if (props.loading && !isLeading.value) {
|
||||||
return props.loadingIcon
|
return props.loadingIcon
|
||||||
@@ -513,6 +488,7 @@ export default defineComponent({
|
|||||||
const trailingWrapperIconClass = computed(() => {
|
const trailingWrapperIconClass = computed(() => {
|
||||||
return twJoin(
|
return twJoin(
|
||||||
ui.value.icon.trailing.wrapper,
|
ui.value.icon.trailing.wrapper,
|
||||||
|
ui.value.icon.trailing.pointer,
|
||||||
ui.value.icon.trailing.padding[size.value]
|
ui.value.icon.trailing.padding[size.value]
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@@ -587,7 +563,7 @@ export default defineComponent({
|
|||||||
return ['string', 'number'].includes(typeof props.modelValue) ? query.value : { [props.optionAttribute]: query.value }
|
return ['string', 'number'].includes(typeof props.modelValue) ? query.value : { [props.optionAttribute]: query.value }
|
||||||
})
|
})
|
||||||
|
|
||||||
function handleClearSearchOnClose() {
|
function clearOnClose() {
|
||||||
if (props.clearSearchOnClose) {
|
if (props.clearSearchOnClose) {
|
||||||
query.value = ''
|
query.value = ''
|
||||||
}
|
}
|
||||||
@@ -597,7 +573,7 @@ export default defineComponent({
|
|||||||
if (value) {
|
if (value) {
|
||||||
emit('open')
|
emit('open')
|
||||||
} else {
|
} else {
|
||||||
handleClearSearchOnClose()
|
clearOnClose()
|
||||||
emit('close')
|
emit('close')
|
||||||
emitFormBlur()
|
emitFormBlur()
|
||||||
}
|
}
|
||||||
@@ -617,28 +593,6 @@ export default defineComponent({
|
|||||||
query.value = event.target.value
|
query.value = event.target.value
|
||||||
}
|
}
|
||||||
|
|
||||||
function onClear() {
|
|
||||||
if (canClearValue.value) {
|
|
||||||
emit('update:modelValue', props.multiple ? [] : null)
|
|
||||||
emit('clear')
|
|
||||||
emitFormChange()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function trailingSlotProps() {
|
|
||||||
const slotProps: Record<string, any> = {
|
|
||||||
selected: selected.value,
|
|
||||||
loading: props.loading,
|
|
||||||
disabled: props.disabled
|
|
||||||
}
|
|
||||||
|
|
||||||
if (props.clearable) {
|
|
||||||
slotProps.onClear = onClear
|
|
||||||
}
|
|
||||||
|
|
||||||
return slotProps
|
|
||||||
}
|
|
||||||
|
|
||||||
provideUseId(() => useId())
|
provideUseId(() => useId())
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -658,7 +612,6 @@ export default defineComponent({
|
|||||||
label,
|
label,
|
||||||
accessor,
|
accessor,
|
||||||
isLeading,
|
isLeading,
|
||||||
onClear,
|
|
||||||
isTrailing,
|
isTrailing,
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
selectClass,
|
selectClass,
|
||||||
@@ -674,10 +627,8 @@ export default defineComponent({
|
|||||||
query,
|
query,
|
||||||
onUpdate,
|
onUpdate,
|
||||||
onQueryChange,
|
onQueryChange,
|
||||||
trailingSlotProps,
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
canClearValue,
|
by
|
||||||
clearableWrapperClass,
|
|
||||||
clearableButtonClass
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -23,11 +23,11 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { ref, computed, toRef, watch, onMounted, nextTick, defineComponent } from 'vue'
|
import { ref, computed, toRef, watch, onMounted, nextTick, defineComponent } from 'vue'
|
||||||
import type { PropType } from 'vue'
|
import type { PropType } from 'vue'
|
||||||
import { twMerge, twJoin } from 'tailwind-merge'
|
import { twJoin } from 'tailwind-merge'
|
||||||
import { defu } from 'defu'
|
import { defu } from 'defu'
|
||||||
import { useUI } from '../../composables/useUI'
|
import { useUI } from '../../composables/useUI'
|
||||||
import { useFormGroup } from '../../composables/useFormGroup'
|
import { useFormGroup } from '../../composables/useFormGroup'
|
||||||
import { mergeConfig, looseToNumber } from '../../utils'
|
import { looseToNumber, mergeConfig, twMerge } from '../../utils'
|
||||||
import type { TextareaSize, TextareaColor, TextareaVariant, Strategy, DeepPartial } from '../../types/index'
|
import type { TextareaSize, TextareaColor, TextareaVariant, Strategy, DeepPartial } from '../../types/index'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
|
|||||||
@@ -33,11 +33,11 @@
|
|||||||
import { computed, toRef, defineComponent } from 'vue'
|
import { computed, toRef, defineComponent } from 'vue'
|
||||||
import type { PropType } from 'vue'
|
import type { PropType } from 'vue'
|
||||||
import { Switch as HSwitch, provideUseId } from '@headlessui/vue'
|
import { Switch as HSwitch, provideUseId } from '@headlessui/vue'
|
||||||
import { twMerge, twJoin } from 'tailwind-merge'
|
import { twJoin } from 'tailwind-merge'
|
||||||
import UIcon from '../elements/Icon.vue'
|
import UIcon from '../elements/Icon.vue'
|
||||||
import { useUI } from '../../composables/useUI'
|
import { useUI } from '../../composables/useUI'
|
||||||
import { useFormGroup } from '../../composables/useFormGroup'
|
import { useFormGroup } from '../../composables/useFormGroup'
|
||||||
import { mergeConfig } from '../../utils'
|
import { mergeConfig, twMerge } from '../../utils'
|
||||||
import type { ToggleSize, ToggleColor, Strategy, DeepPartial } from '../../types/index'
|
import type { ToggleSize, ToggleColor, Strategy, DeepPartial } from '../../types/index'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
|
|||||||
@@ -19,9 +19,9 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, toRef, defineComponent } from 'vue'
|
import { computed, toRef, defineComponent } from 'vue'
|
||||||
import type { PropType } from 'vue'
|
import type { PropType } from 'vue'
|
||||||
import { twMerge, twJoin } from 'tailwind-merge'
|
import { twJoin } from 'tailwind-merge'
|
||||||
import { useUI } from '../../composables/useUI'
|
import { useUI } from '../../composables/useUI'
|
||||||
import { mergeConfig } from '../../utils'
|
import { mergeConfig, twMerge } from '../../utils'
|
||||||
import type { DeepPartial, Strategy } from '../../types/index'
|
import type { DeepPartial, Strategy } from '../../types/index'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
|
|||||||
@@ -7,9 +7,9 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, toRef, defineComponent } from 'vue'
|
import { computed, toRef, defineComponent } from 'vue'
|
||||||
import type { PropType } from 'vue'
|
import type { PropType } from 'vue'
|
||||||
import { twMerge, twJoin } from 'tailwind-merge'
|
import { twJoin } from 'tailwind-merge'
|
||||||
import { useUI } from '../../composables/useUI'
|
import { useUI } from '../../composables/useUI'
|
||||||
import { mergeConfig } from '../../utils'
|
import { mergeConfig, twMerge } from '../../utils'
|
||||||
import type { DeepPartial, Strategy } from '../../types/index'
|
import type { DeepPartial, Strategy } from '../../types/index'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
|
|||||||
@@ -21,11 +21,11 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { toRef, computed, defineComponent } from 'vue'
|
import { toRef, computed, defineComponent } from 'vue'
|
||||||
import type { PropType } from 'vue'
|
import type { PropType } from 'vue'
|
||||||
import { twMerge, twJoin } from 'tailwind-merge'
|
import { twJoin } from 'tailwind-merge'
|
||||||
import UIcon from '../elements/Icon.vue'
|
import UIcon from '../elements/Icon.vue'
|
||||||
import UAvatar from '../elements/Avatar.vue'
|
import UAvatar from '../elements/Avatar.vue'
|
||||||
import { useUI } from '../../composables/useUI'
|
import { useUI } from '../../composables/useUI'
|
||||||
import { mergeConfig } from '../../utils'
|
import { mergeConfig, twMerge } from '../../utils'
|
||||||
import type { Avatar, DeepPartial, DividerSize, Strategy } from '../../types/index'
|
import type { Avatar, DeepPartial, DividerSize, Strategy } from '../../types/index'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
|
|||||||
@@ -5,9 +5,9 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, toRef, defineComponent } from 'vue'
|
import { computed, toRef, defineComponent } from 'vue'
|
||||||
import type { PropType } from 'vue'
|
import type { PropType } from 'vue'
|
||||||
import { twMerge, twJoin } from 'tailwind-merge'
|
import { twJoin } from 'tailwind-merge'
|
||||||
import { useUI } from '../../composables/useUI'
|
import { useUI } from '../../composables/useUI'
|
||||||
import { mergeConfig } from '../../utils'
|
import { mergeConfig, twMerge } from '../../utils'
|
||||||
import type { DeepPartial, Strategy } from '../../types/index'
|
import type { DeepPartial, Strategy } from '../../types/index'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
|
|||||||
@@ -36,11 +36,11 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, toRef } from 'vue'
|
import { defineComponent, toRef } from 'vue'
|
||||||
import type { PropType } from 'vue'
|
import type { PropType } from 'vue'
|
||||||
import { twMerge, twJoin } from 'tailwind-merge'
|
import { twJoin } from 'tailwind-merge'
|
||||||
import UIcon from '../elements/Icon.vue'
|
import UIcon from '../elements/Icon.vue'
|
||||||
import ULink from '../elements/Link.vue'
|
import ULink from '../elements/Link.vue'
|
||||||
import { useUI } from '../../composables/useUI'
|
import { useUI } from '../../composables/useUI'
|
||||||
import { mergeConfig, getULinkProps } from '../../utils'
|
import { getULinkProps, mergeConfig, twMerge } from '../../utils'
|
||||||
import type { BreadcrumbLink, DeepPartial, Strategy } from '../../types/index'
|
import type { BreadcrumbLink, DeepPartial, Strategy } from '../../types/index'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
|
|||||||
@@ -54,13 +54,13 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { toRef, defineComponent, computed } from 'vue'
|
import { toRef, defineComponent, computed } from 'vue'
|
||||||
import type { PropType } from 'vue'
|
import type { PropType } from 'vue'
|
||||||
import { twMerge, twJoin } from 'tailwind-merge'
|
import { twJoin } from 'tailwind-merge'
|
||||||
import UIcon from '../elements/Icon.vue'
|
import UIcon from '../elements/Icon.vue'
|
||||||
import UAvatar from '../elements/Avatar.vue'
|
import UAvatar from '../elements/Avatar.vue'
|
||||||
import UBadge from '../elements/Badge.vue'
|
import UBadge from '../elements/Badge.vue'
|
||||||
import ULink from '../elements/Link.vue'
|
import ULink from '../elements/Link.vue'
|
||||||
import { useUI } from '../../composables/useUI'
|
import { useUI } from '../../composables/useUI'
|
||||||
import { mergeConfig, getULinkProps } from '../../utils'
|
import { getULinkProps, mergeConfig, twMerge } from '../../utils'
|
||||||
import type { DeepPartial, HorizontalNavigationLink, Strategy } from '../../types/index'
|
import type { DeepPartial, HorizontalNavigationLink, Strategy } from '../../types/index'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
|
|||||||
@@ -55,14 +55,14 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { toRef, defineComponent, computed } from 'vue'
|
import { toRef, defineComponent, computed } from 'vue'
|
||||||
import type { PropType } from 'vue'
|
import type { PropType } from 'vue'
|
||||||
import { twMerge, twJoin } from 'tailwind-merge'
|
import { twJoin } from 'tailwind-merge'
|
||||||
import UIcon from '../elements/Icon.vue'
|
import UIcon from '../elements/Icon.vue'
|
||||||
import UAvatar from '../elements/Avatar.vue'
|
import UAvatar from '../elements/Avatar.vue'
|
||||||
import UBadge from '../elements/Badge.vue'
|
import UBadge from '../elements/Badge.vue'
|
||||||
import ULink from '../elements/Link.vue'
|
import ULink from '../elements/Link.vue'
|
||||||
import UDivider from '../layout/Divider.vue'
|
import UDivider from '../layout/Divider.vue'
|
||||||
import { useUI } from '../../composables/useUI'
|
import { useUI } from '../../composables/useUI'
|
||||||
import { mergeConfig, getULinkProps } from '../../utils'
|
import { getULinkProps, mergeConfig, twMerge } from '../../utils'
|
||||||
import type { VerticalNavigationLink, Strategy, DeepPartial } from '../../types/index'
|
import type { VerticalNavigationLink, Strategy, DeepPartial } from '../../types/index'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
|
|||||||
@@ -18,10 +18,10 @@ import type { PropType, Ref } from 'vue'
|
|||||||
import { defu } from 'defu'
|
import { defu } from 'defu'
|
||||||
import { onClickOutside } from '@vueuse/core'
|
import { onClickOutside } from '@vueuse/core'
|
||||||
import type { VirtualElement } from '@popperjs/core'
|
import type { VirtualElement } from '@popperjs/core'
|
||||||
import { twMerge, twJoin } from 'tailwind-merge'
|
import { twJoin } from 'tailwind-merge'
|
||||||
import { useUI } from '../../composables/useUI'
|
import { useUI } from '../../composables/useUI'
|
||||||
import { usePopper } from '../../composables/usePopper'
|
import { usePopper } from '../../composables/usePopper'
|
||||||
import { mergeConfig } from '../../utils'
|
import { mergeConfig, twMerge } from '../../utils'
|
||||||
import type { DeepPartial, PopperOptions, Strategy } from '../../types/index'
|
import type { DeepPartial, PopperOptions, Strategy } from '../../types/index'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
|
|||||||
@@ -45,13 +45,13 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { ref, computed, toRef, onMounted, onUnmounted, watch, watchEffect, defineComponent } from 'vue'
|
import { ref, computed, toRef, onMounted, onUnmounted, watch, watchEffect, defineComponent } from 'vue'
|
||||||
import type { PropType } from 'vue'
|
import type { PropType } from 'vue'
|
||||||
import { twMerge, twJoin } from 'tailwind-merge'
|
import { twJoin } from 'tailwind-merge'
|
||||||
import UIcon from '../elements/Icon.vue'
|
import UIcon from '../elements/Icon.vue'
|
||||||
import UAvatar from '../elements/Avatar.vue'
|
import UAvatar from '../elements/Avatar.vue'
|
||||||
import UButton from '../elements/Button.vue'
|
import UButton from '../elements/Button.vue'
|
||||||
import { useUI } from '../../composables/useUI'
|
import { useUI } from '../../composables/useUI'
|
||||||
import { useTimer } from '../../composables/useTimer'
|
import { useTimer } from '../../composables/useTimer'
|
||||||
import { mergeConfig } from '../../utils'
|
import { mergeConfig, twMerge } from '../../utils'
|
||||||
import type { Avatar, Button, NotificationColor, NotificationAction, Strategy, DeepPartial } from '../../types/index'
|
import type { Avatar, Button, NotificationColor, NotificationAction, Strategy, DeepPartial } from '../../types/index'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
@@ -117,6 +117,10 @@ export default defineComponent({
|
|||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<DeepPartial<typeof config> & { strategy?: Strategy }>,
|
type: Object as PropType<DeepPartial<typeof config> & { strategy?: Strategy }>,
|
||||||
default: () => ({})
|
default: () => ({})
|
||||||
|
},
|
||||||
|
pauseTimeoutOnHover: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: ['close'],
|
emits: ['close'],
|
||||||
@@ -157,13 +161,13 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
|
|
||||||
function onMouseover() {
|
function onMouseover() {
|
||||||
if (timer) {
|
if (props.pauseTimeoutOnHover && timer) {
|
||||||
timer.pause()
|
timer.pause()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onMouseleave() {
|
function onMouseleave() {
|
||||||
if (timer) {
|
if (props.pauseTimeoutOnHover && timer) {
|
||||||
timer.resume()
|
timer.resume()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,10 +22,10 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, toRef, defineComponent } from 'vue'
|
import { computed, toRef, defineComponent } from 'vue'
|
||||||
import type { PropType } from 'vue'
|
import type { PropType } from 'vue'
|
||||||
import { twMerge, twJoin } from 'tailwind-merge'
|
import { twJoin } from 'tailwind-merge'
|
||||||
import { useUI } from '../../composables/useUI'
|
import { useUI } from '../../composables/useUI'
|
||||||
import { useToast } from '../../composables/useToast'
|
import { useToast } from '../../composables/useToast'
|
||||||
import { mergeConfig } from '../../utils'
|
import { mergeConfig, twMerge } from '../../utils'
|
||||||
import type { DeepPartial, Notification, Strategy } from '../../types/index'
|
import type { DeepPartial, Notification, Strategy } from '../../types/index'
|
||||||
import UNotification from './Notification.vue'
|
import UNotification from './Notification.vue'
|
||||||
import { useState } from '#imports'
|
import { useState } from '#imports'
|
||||||
|
|||||||
3
src/runtime/types/checkbox.d.ts
vendored
Normal file
3
src/runtime/types/checkbox.d.ts
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import type colors from '#ui-colors'
|
||||||
|
|
||||||
|
export type CheckboxColor = typeof colors[number]
|
||||||
2
src/runtime/types/divider.d.ts
vendored
2
src/runtime/types/divider.d.ts
vendored
@@ -1,3 +1,3 @@
|
|||||||
import type { divider } from '#ui/ui.config'
|
import type { divider } from '../ui.config'
|
||||||
|
|
||||||
export type DividerSize = keyof typeof divider.border.size.horizontal | keyof typeof divider.border.size.vertical
|
export type DividerSize = keyof typeof divider.border.size.horizontal | keyof typeof divider.border.size.vertical
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ export * from './avatar'
|
|||||||
export * from './badge'
|
export * from './badge'
|
||||||
export * from './breadcrumb'
|
export * from './breadcrumb'
|
||||||
export * from './button'
|
export * from './button'
|
||||||
|
export * from './checkbox'
|
||||||
export * from './chip'
|
export * from './chip'
|
||||||
export * from './clipboard'
|
export * from './clipboard'
|
||||||
export * from './command-palette'
|
export * from './command-palette'
|
||||||
|
|||||||
2
src/runtime/types/link.d.ts
vendored
2
src/runtime/types/link.d.ts
vendored
@@ -6,7 +6,7 @@ export interface Link extends NuxtLinkProps {
|
|||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
active?: boolean
|
active?: boolean
|
||||||
exact?: boolean
|
exact?: boolean
|
||||||
exactQuery?: boolean
|
exactQuery?: boolean | 'partial'
|
||||||
exactHash?: boolean
|
exactHash?: boolean
|
||||||
inactiveClass?: string
|
inactiveClass?: string
|
||||||
}
|
}
|
||||||
|
|||||||
2
src/runtime/types/utils.d.ts
vendored
2
src/runtime/types/utils.d.ts
vendored
@@ -7,7 +7,7 @@ export interface TightMap<O = any> {
|
|||||||
export type DeepPartial<T, O = any> = {
|
export type DeepPartial<T, O = any> = {
|
||||||
[P in keyof T]?: T[P] extends object
|
[P in keyof T]?: T[P] extends object
|
||||||
? DeepPartial<T[P], O>
|
? DeepPartial<T[P], O>
|
||||||
: T[P];
|
: T[P] extends string ? string : T[P];
|
||||||
} & {
|
} & {
|
||||||
[key: string]: O | TightMap<O>
|
[key: string]: O | TightMap<O>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import type { ButtonColor, ButtonSize, ButtonVariant, CheckboxColor, ProgressAnimation, ProgressColor } from '../../types'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
wrapper: 'relative overflow-x-auto',
|
wrapper: 'relative overflow-x-auto',
|
||||||
base: 'min-w-full table-fixed',
|
base: 'min-w-full table-fixed',
|
||||||
@@ -51,23 +53,23 @@ export default {
|
|||||||
icon: 'i-heroicons-arrows-up-down-20-solid',
|
icon: 'i-heroicons-arrows-up-down-20-solid',
|
||||||
trailing: true,
|
trailing: true,
|
||||||
square: true,
|
square: true,
|
||||||
color: 'gray' as const,
|
color: 'gray' as ButtonColor,
|
||||||
variant: 'ghost' as const,
|
variant: 'ghost' as ButtonVariant,
|
||||||
class: '-m-1.5'
|
class: '-m-1.5'
|
||||||
},
|
},
|
||||||
expandButton: {
|
expandButton: {
|
||||||
icon: 'i-heroicons-chevron-down',
|
icon: 'i-heroicons-chevron-down',
|
||||||
color: 'gray' as const,
|
color: 'gray' as ButtonColor,
|
||||||
variant: 'ghost' as const,
|
variant: 'ghost' as ButtonVariant,
|
||||||
size: 'xs' as const,
|
size: 'xs' as ButtonSize,
|
||||||
class: '-my-1.5 align-middle'
|
class: '-my-1.5 align-middle'
|
||||||
},
|
},
|
||||||
checkbox: {
|
checkbox: {
|
||||||
color: 'primary' as const
|
color: 'primary' as CheckboxColor
|
||||||
},
|
},
|
||||||
progress: {
|
progress: {
|
||||||
color: 'primary' as const,
|
color: 'primary' as ProgressColor,
|
||||||
animation: 'carousel' as const
|
animation: 'carousel' as ProgressAnimation
|
||||||
},
|
},
|
||||||
loadingState: {
|
loadingState: {
|
||||||
icon: 'i-heroicons-arrow-path-20-solid',
|
icon: 'i-heroicons-arrow-path-20-solid',
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export default {
|
|||||||
openIcon: 'i-heroicons-chevron-down-20-solid',
|
openIcon: 'i-heroicons-chevron-down-20-solid',
|
||||||
closeIcon: '',
|
closeIcon: '',
|
||||||
class: 'mb-1.5 w-full',
|
class: 'mb-1.5 w-full',
|
||||||
variant: 'soft' as const,
|
variant: 'soft',
|
||||||
truncate: true
|
truncate: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import type { AvatarSize, ButtonColor, ButtonSize, ButtonVariant } from '../../types'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
wrapper: 'w-full relative overflow-hidden',
|
wrapper: 'w-full relative overflow-hidden',
|
||||||
inner: 'w-0 flex-1',
|
inner: 'w-0 flex-1',
|
||||||
@@ -13,7 +15,7 @@ export default {
|
|||||||
},
|
},
|
||||||
avatar: {
|
avatar: {
|
||||||
base: 'flex-shrink-0 self-center',
|
base: 'flex-shrink-0 self-center',
|
||||||
size: 'md' as const
|
size: 'md' as AvatarSize
|
||||||
},
|
},
|
||||||
color: {
|
color: {
|
||||||
white: {
|
white: {
|
||||||
@@ -32,9 +34,9 @@ export default {
|
|||||||
icon: null,
|
icon: null,
|
||||||
closeButton: null,
|
closeButton: null,
|
||||||
actionButton: {
|
actionButton: {
|
||||||
size: 'xs' as const,
|
size: 'xs' as ButtonSize,
|
||||||
color: 'primary' as const,
|
color: 'primary' as ButtonColor,
|
||||||
variant: 'link' as const
|
variant: 'link' as ButtonVariant
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,12 @@ export default {
|
|||||||
md: 'text-sm px-2 py-1',
|
md: 'text-sm px-2 py-1',
|
||||||
lg: 'text-sm px-2.5 py-1.5'
|
lg: 'text-sm px-2.5 py-1.5'
|
||||||
},
|
},
|
||||||
|
gap: {
|
||||||
|
xs: 'gap-0.5',
|
||||||
|
sm: 'gap-1',
|
||||||
|
md: 'gap-1',
|
||||||
|
lg: 'gap-1.5'
|
||||||
|
},
|
||||||
color: {
|
color: {
|
||||||
white: {
|
white: {
|
||||||
solid: 'ring-1 ring-inset ring-gray-300 dark:ring-gray-700 text-gray-900 dark:text-white bg-white dark:bg-gray-900'
|
solid: 'ring-1 ring-inset ring-gray-300 dark:ring-gray-700 text-gray-900 dark:text-white bg-white dark:bg-gray-900'
|
||||||
@@ -25,6 +31,15 @@ export default {
|
|||||||
soft: 'bg-{color}-50 dark:bg-{color}-400 dark:bg-opacity-10 text-{color}-500 dark:text-{color}-400',
|
soft: 'bg-{color}-50 dark:bg-{color}-400 dark:bg-opacity-10 text-{color}-500 dark:text-{color}-400',
|
||||||
subtle: 'bg-{color}-50 dark:bg-{color}-400 dark:bg-opacity-10 text-{color}-500 dark:text-{color}-400 ring-1 ring-inset ring-{color}-500 dark:ring-{color}-400 ring-opacity-25 dark:ring-opacity-25'
|
subtle: 'bg-{color}-50 dark:bg-{color}-400 dark:bg-opacity-10 text-{color}-500 dark:text-{color}-400 ring-1 ring-inset ring-{color}-500 dark:ring-{color}-400 ring-opacity-25 dark:ring-opacity-25'
|
||||||
},
|
},
|
||||||
|
icon: {
|
||||||
|
base: 'flex-shrink-0',
|
||||||
|
size: {
|
||||||
|
xs: 'h-4 w-4',
|
||||||
|
sm: 'h-4 w-4',
|
||||||
|
md: 'h-5 w-5',
|
||||||
|
lg: 'h-5 w-5'
|
||||||
|
}
|
||||||
|
},
|
||||||
default: {
|
default: {
|
||||||
size: 'sm',
|
size: 'sm',
|
||||||
variant: 'solid',
|
variant: 'solid',
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import type { ButtonColor } from '../../types'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
wrapper: 'relative',
|
wrapper: 'relative',
|
||||||
container: 'relative w-full flex overflow-x-auto snap-x snap-mandatory scroll-smooth',
|
container: 'relative w-full flex overflow-x-auto snap-x snap-mandatory scroll-smooth',
|
||||||
@@ -13,12 +15,12 @@ export default {
|
|||||||
},
|
},
|
||||||
default: {
|
default: {
|
||||||
prevButton: {
|
prevButton: {
|
||||||
color: 'black' as const,
|
color: 'black' as ButtonColor,
|
||||||
class: 'rtl:[&_span:first-child]:rotate-180 absolute start-4 top-1/2 transform -translate-y-1/2 rounded-full',
|
class: 'rtl:[&_span:first-child]:rotate-180 absolute start-4 top-1/2 transform -translate-y-1/2 rounded-full',
|
||||||
icon: 'i-heroicons-chevron-left-20-solid'
|
icon: 'i-heroicons-chevron-left-20-solid'
|
||||||
},
|
},
|
||||||
nextButton: {
|
nextButton: {
|
||||||
color: 'black' as const,
|
color: 'black' as ButtonColor,
|
||||||
class: 'rtl:[&_span:last-child]:rotate-180 absolute end-4 top-1/2 transform -translate-y-1/2 rounded-full',
|
class: 'rtl:[&_span:last-child]:rotate-180 absolute end-4 top-1/2 transform -translate-y-1/2 rounded-full',
|
||||||
icon: 'i-heroicons-chevron-right-20-solid'
|
icon: 'i-heroicons-chevron-right-20-solid'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import type { AvatarSize } from '../../types'
|
||||||
import { arrow } from '../popper'
|
import { arrow } from '../popper'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -28,7 +29,7 @@ export default {
|
|||||||
},
|
},
|
||||||
avatar: {
|
avatar: {
|
||||||
base: 'flex-shrink-0',
|
base: 'flex-shrink-0',
|
||||||
size: '2xs' as const
|
size: '2xs' as AvatarSize
|
||||||
},
|
},
|
||||||
label: 'truncate',
|
label: 'truncate',
|
||||||
shortcuts: 'hidden md:inline-flex flex-shrink-0 gap-0.5 ms-auto'
|
shortcuts: 'hidden md:inline-flex flex-shrink-0 gap-0.5 ms-auto'
|
||||||
|
|||||||
@@ -98,18 +98,6 @@ export default {
|
|||||||
'lg': 'px-3.5',
|
'lg': 'px-3.5',
|
||||||
'xl': 'px-3.5'
|
'xl': 'px-3.5'
|
||||||
}
|
}
|
||||||
},
|
|
||||||
clearable: {
|
|
||||||
wrapper: 'absolute inset-y-0 end-6 flex items-center',
|
|
||||||
pointer: 'pointer-events-auto',
|
|
||||||
padding: {
|
|
||||||
'2xs': 'px-2',
|
|
||||||
'xs': 'px-2.5',
|
|
||||||
'sm': 'px-2.5',
|
|
||||||
'md': 'px-3',
|
|
||||||
'lg': 'px-3.5',
|
|
||||||
'xl': 'px-3.5'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
default: {
|
default: {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import type { AvatarSize } from '../../types'
|
||||||
import { arrow } from '../popper'
|
import { arrow } from '../popper'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -36,7 +37,7 @@ export default {
|
|||||||
},
|
},
|
||||||
avatar: {
|
avatar: {
|
||||||
base: 'flex-shrink-0',
|
base: 'flex-shrink-0',
|
||||||
size: '2xs' as const
|
size: '2xs' as AvatarSize
|
||||||
},
|
},
|
||||||
chip: {
|
chip: {
|
||||||
base: 'flex-shrink-0 w-2 h-2 mx-1 rounded-full'
|
base: 'flex-shrink-0 w-2 h-2 mx-1 rounded-full'
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ export default {
|
|||||||
color: 'white',
|
color: 'white',
|
||||||
variant: 'outline',
|
variant: 'outline',
|
||||||
loadingIcon: 'i-heroicons-arrow-path-20-solid',
|
loadingIcon: 'i-heroicons-arrow-path-20-solid',
|
||||||
trailingIcon: 'i-heroicons-chevron-down-20-solid',
|
trailingIcon: 'i-heroicons-chevron-down-20-solid'
|
||||||
clerableIcon: 'i-heroicons-x-mark-20-solid'
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import type { AvatarSize } from '../../types'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
wrapper: {
|
wrapper: {
|
||||||
base: 'flex items-center align-center text-center',
|
base: 'flex items-center align-center text-center',
|
||||||
@@ -42,11 +44,11 @@ export default {
|
|||||||
},
|
},
|
||||||
avatar: {
|
avatar: {
|
||||||
base: 'flex-shrink-0',
|
base: 'flex-shrink-0',
|
||||||
size: '2xs' as const
|
size: '2xs' as AvatarSize
|
||||||
},
|
},
|
||||||
label: 'text-sm',
|
label: 'text-sm',
|
||||||
default: {
|
default: {
|
||||||
size: '2xs' as const,
|
size: '2xs',
|
||||||
type: 'solid' as const
|
type: 'solid'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import type { AvatarSize } from '../../types'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
wrapper: 'flex flex-col flex-1 min-h-0 divide-y divide-gray-100 dark:divide-gray-800',
|
wrapper: 'flex flex-col flex-1 min-h-0 divide-y divide-gray-100 dark:divide-gray-800',
|
||||||
container: 'relative flex-1 overflow-y-auto divide-y divide-gray-100 dark:divide-gray-800 scroll-py-2',
|
container: 'relative flex-1 overflow-y-auto divide-y divide-gray-100 dark:divide-gray-800 scroll-py-2',
|
||||||
@@ -46,7 +48,7 @@ export default {
|
|||||||
},
|
},
|
||||||
avatar: {
|
avatar: {
|
||||||
base: 'flex-shrink-0',
|
base: 'flex-shrink-0',
|
||||||
size: '2xs' as const
|
size: '2xs' as AvatarSize
|
||||||
},
|
},
|
||||||
chip: {
|
chip: {
|
||||||
base: 'flex-shrink-0 w-2 h-2 mx-1 rounded-full'
|
base: 'flex-shrink-0 w-2 h-2 mx-1 rounded-full'
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import type { AvatarSize, BadgeColor, BadgeSize, BadgeVariant } from '../../types'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
wrapper: 'relative w-full flex items-center justify-between',
|
wrapper: 'relative w-full flex items-center justify-between',
|
||||||
container: 'flex items-center min-w-0',
|
container: 'flex items-center min-w-0',
|
||||||
@@ -15,12 +17,12 @@ export default {
|
|||||||
},
|
},
|
||||||
avatar: {
|
avatar: {
|
||||||
base: 'flex-shrink-0',
|
base: 'flex-shrink-0',
|
||||||
size: '2xs' as const
|
size: '2xs' as AvatarSize
|
||||||
},
|
},
|
||||||
badge: {
|
badge: {
|
||||||
base: 'flex-shrink-0 ms-auto relative rounded',
|
base: 'flex-shrink-0 ms-auto relative rounded',
|
||||||
color: 'gray' as const,
|
color: 'gray' as BadgeColor,
|
||||||
variant: 'solid' as const,
|
variant: 'solid' as BadgeVariant,
|
||||||
size: 'xs' as const
|
size: 'xs' as BadgeSize
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import type { ButtonColor } from '../../types'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
wrapper: 'flex items-center -space-x-px',
|
wrapper: 'flex items-center -space-x-px',
|
||||||
base: '',
|
base: '',
|
||||||
@@ -5,28 +7,28 @@ export default {
|
|||||||
default: {
|
default: {
|
||||||
size: 'sm',
|
size: 'sm',
|
||||||
activeButton: {
|
activeButton: {
|
||||||
color: 'primary' as const
|
color: 'primary' as ButtonColor
|
||||||
},
|
},
|
||||||
inactiveButton: {
|
inactiveButton: {
|
||||||
color: 'white' as const
|
color: 'white' as ButtonColor
|
||||||
},
|
},
|
||||||
firstButton: {
|
firstButton: {
|
||||||
color: 'white' as const,
|
color: 'white' as ButtonColor,
|
||||||
class: 'rtl:[&_span:first-child]:rotate-180',
|
class: 'rtl:[&_span:first-child]:rotate-180',
|
||||||
icon: 'i-heroicons-chevron-double-left-20-solid'
|
icon: 'i-heroicons-chevron-double-left-20-solid'
|
||||||
},
|
},
|
||||||
lastButton: {
|
lastButton: {
|
||||||
color: 'white' as const,
|
color: 'white' as ButtonColor,
|
||||||
class: 'rtl:[&_span:last-child]:rotate-180',
|
class: 'rtl:[&_span:last-child]:rotate-180',
|
||||||
icon: 'i-heroicons-chevron-double-right-20-solid'
|
icon: 'i-heroicons-chevron-double-right-20-solid'
|
||||||
},
|
},
|
||||||
prevButton: {
|
prevButton: {
|
||||||
color: 'white' as const,
|
color: 'white' as ButtonColor,
|
||||||
class: 'rtl:[&_span:first-child]:rotate-180',
|
class: 'rtl:[&_span:first-child]:rotate-180',
|
||||||
icon: 'i-heroicons-chevron-left-20-solid'
|
icon: 'i-heroicons-chevron-left-20-solid'
|
||||||
},
|
},
|
||||||
nextButton: {
|
nextButton: {
|
||||||
color: 'white' as const,
|
color: 'white' as ButtonColor,
|
||||||
class: 'rtl:[&_span:last-child]:rotate-180',
|
class: 'rtl:[&_span:last-child]:rotate-180',
|
||||||
icon: 'i-heroicons-chevron-right-20-solid'
|
icon: 'i-heroicons-chevron-right-20-solid'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import type { AvatarSize, BadgeColor, BadgeSize, BadgeVariant } from '../../types'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
wrapper: 'relative',
|
wrapper: 'relative',
|
||||||
base: 'group relative flex items-center gap-1.5 focus:outline-none focus-visible:outline-none dark:focus-visible:outline-none focus-visible:before:ring-inset focus-visible:before:ring-1 focus-visible:before:ring-primary-500 dark:focus-visible:before:ring-primary-400 before:absolute before:inset-px before:rounded-md disabled:cursor-not-allowed disabled:opacity-75',
|
base: 'group relative flex items-center gap-1.5 focus:outline-none focus-visible:outline-none dark:focus-visible:outline-none focus-visible:before:ring-inset focus-visible:before:ring-1 focus-visible:before:ring-primary-500 dark:focus-visible:before:ring-primary-400 before:absolute before:inset-px before:rounded-md disabled:cursor-not-allowed disabled:opacity-75',
|
||||||
@@ -17,13 +19,13 @@ export default {
|
|||||||
},
|
},
|
||||||
avatar: {
|
avatar: {
|
||||||
base: 'flex-shrink-0',
|
base: 'flex-shrink-0',
|
||||||
size: '2xs' as const
|
size: '2xs' as AvatarSize
|
||||||
},
|
},
|
||||||
badge: {
|
badge: {
|
||||||
base: 'flex-shrink-0 ms-auto relative rounded',
|
base: 'flex-shrink-0 ms-auto relative rounded',
|
||||||
color: 'gray' as const,
|
color: 'gray' as BadgeColor,
|
||||||
variant: 'solid' as const,
|
variant: 'solid' as BadgeVariant,
|
||||||
size: 'xs' as const
|
size: 'xs' as BadgeSize
|
||||||
},
|
},
|
||||||
divider: {
|
divider: {
|
||||||
wrapper: {
|
wrapper: {
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import type { AvatarSize, ButtonColor, ButtonSize, ButtonVariant } from '../../types'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
wrapper: 'w-full pointer-events-auto',
|
wrapper: 'w-full pointer-events-auto',
|
||||||
container: 'relative overflow-hidden',
|
container: 'relative overflow-hidden',
|
||||||
@@ -17,7 +19,7 @@ export default {
|
|||||||
},
|
},
|
||||||
avatar: {
|
avatar: {
|
||||||
base: 'flex-shrink-0 self-center',
|
base: 'flex-shrink-0 self-center',
|
||||||
size: 'md' as const
|
size: 'md' as AvatarSize
|
||||||
},
|
},
|
||||||
progress: {
|
progress: {
|
||||||
base: 'absolute bottom-0 end-0 start-0 h-1',
|
base: 'absolute bottom-0 end-0 start-0 h-1',
|
||||||
@@ -38,13 +40,13 @@ export default {
|
|||||||
timeout: 5000,
|
timeout: 5000,
|
||||||
closeButton: {
|
closeButton: {
|
||||||
icon: 'i-heroicons-x-mark-20-solid',
|
icon: 'i-heroicons-x-mark-20-solid',
|
||||||
color: 'gray' as const,
|
color: 'gray' as ButtonColor,
|
||||||
variant: 'link' as const,
|
variant: 'link' as ButtonVariant,
|
||||||
padded: false
|
padded: false
|
||||||
},
|
},
|
||||||
actionButton: {
|
actionButton: {
|
||||||
size: 'xs' as const,
|
size: 'xs' as ButtonSize,
|
||||||
color: 'white' as const
|
color: 'white' as ButtonColor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
import { defu, createDefu } from 'defu'
|
import { defu, createDefu } from 'defu'
|
||||||
import { extendTailwindMerge } from 'tailwind-merge'
|
import { extendTailwindMerge } from 'tailwind-merge'
|
||||||
import type { Strategy } from '../types/index'
|
import type { Strategy } from '../types/index'
|
||||||
|
// @ts-ignore
|
||||||
|
import appConfig from '#build/app.config'
|
||||||
|
|
||||||
const customTwMerge = extendTailwindMerge<string, string>({
|
export const twMerge = extendTailwindMerge<string, string>(defu({
|
||||||
extend: {
|
extend: {
|
||||||
classGroups: {
|
classGroups: {
|
||||||
icons: [(classPart: string) => classPart.startsWith('i-')]
|
icons: [(classPart: string) => classPart.startsWith('i-')]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
}, appConfig.ui?.tailwindMerge))
|
||||||
|
|
||||||
const defuTwMerge = createDefu((obj, key, value, namespace) => {
|
const defuTwMerge = createDefu((obj, key, value, namespace) => {
|
||||||
if (namespace === 'default' || namespace.startsWith('default.')) {
|
if (namespace === 'default' || namespace.startsWith('default.')) {
|
||||||
@@ -28,7 +30,7 @@ const defuTwMerge = createDefu((obj, key, value, namespace) => {
|
|||||||
}
|
}
|
||||||
if (typeof obj[key] === 'string' && typeof value === 'string' && obj[key] && value) {
|
if (typeof obj[key] === 'string' && typeof value === 'string' && obj[key] && value) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
obj[key] = customTwMerge(obj[key], value)
|
obj[key] = twMerge(obj[key], value)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -30,21 +30,26 @@ export default function installTailwind(
|
|||||||
|
|
||||||
// 2. add config template
|
// 2. add config template
|
||||||
const configTemplate = addTemplate({
|
const configTemplate = addTemplate({
|
||||||
filename: 'nuxtui-tailwind.config.cjs',
|
filename: 'nuxtui-tailwind.config.mjs',
|
||||||
write: true,
|
write: true,
|
||||||
getContents: ({ nuxt }) => `
|
getContents: ({ nuxt }) => `
|
||||||
const { defaultExtractor: createDefaultExtractor } = require('tailwindcss/lib/lib/defaultExtractor.js')
|
import { defaultExtractor as createDefaultExtractor } from "tailwindcss/lib/lib/defaultExtractor.js";
|
||||||
const { customSafelistExtractor, generateSafelist } = require(${JSON.stringify(resolve(runtimeDir, 'utils', 'colors'))})
|
import { customSafelistExtractor, generateSafelist } from ${JSON.stringify(resolve(runtimeDir, 'utils', 'colors'))};
|
||||||
|
import formsPlugin from "@tailwindcss/forms";
|
||||||
|
import aspectRatio from "@tailwindcss/aspect-ratio";
|
||||||
|
import typography from "@tailwindcss/typography";
|
||||||
|
import containerQueries from "@tailwindcss/container-queries";
|
||||||
|
import headlessUi from "@headlessui/tailwindcss";
|
||||||
|
|
||||||
const defaultExtractor = createDefaultExtractor({ tailwindConfig: { separator: ':' } })
|
const defaultExtractor = createDefaultExtractor({ tailwindConfig: { separator: ':' } });
|
||||||
|
|
||||||
module.exports = {
|
export default {
|
||||||
plugins: [
|
plugins: [
|
||||||
require('@tailwindcss/forms')({ strategy: 'class' }),
|
formsPlugin({ strategy: 'class' }),
|
||||||
require('@tailwindcss/aspect-ratio'),
|
aspectRatio,
|
||||||
require('@tailwindcss/typography'),
|
typography,
|
||||||
require('@tailwindcss/container-queries'),
|
containerQueries,
|
||||||
require('@headlessui/tailwindcss')
|
headlessUi
|
||||||
],
|
],
|
||||||
content: {
|
content: {
|
||||||
files: [
|
files: [
|
||||||
|
|||||||
Reference in New Issue
Block a user