mirror of
https://github.com/ArthurDanjou/ui.git
synced 2026-01-20 15:01:46 +01:00
Compare commits
61 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fd95a3f646 | ||
|
|
23770d8cf0 | ||
|
|
84e75ad237 | ||
|
|
00dd8c27bd | ||
|
|
5f81a79edf | ||
|
|
1a02b3abe7 | ||
|
|
83631ccbca | ||
|
|
0f9b5d47a6 | ||
|
|
f623ec1130 | ||
|
|
a79f7c0a34 | ||
|
|
1c9835d7f1 | ||
|
|
6d8d82a265 | ||
|
|
66a80c7486 | ||
|
|
0546c7922c | ||
|
|
eafe707c7d | ||
|
|
5d1919a538 | ||
|
|
781365a5ed | ||
|
|
0129e2db40 | ||
|
|
45b1a4bd32 | ||
|
|
f32f578125 | ||
|
|
4b044866a5 | ||
|
|
9b768ec12b | ||
|
|
7584d72f42 | ||
|
|
6b5ddc18bd | ||
|
|
4dd92f7f36 | ||
|
|
cbc27422a4 | ||
|
|
db508b218f | ||
|
|
ad33b26729 | ||
|
|
f07968afef | ||
|
|
a8dc9b216a | ||
|
|
32474e21f7 | ||
|
|
c023fb400c | ||
|
|
4548809ee5 | ||
|
|
6b52963339 | ||
|
|
2c2ff0f473 | ||
|
|
0b762d61e7 | ||
|
|
9cbb68871c | ||
|
|
7c5b47ea72 | ||
|
|
7196d81b4c | ||
|
|
1cb8df869f | ||
|
|
67cc349c6c | ||
|
|
1f0f6181db | ||
|
|
18b6133b11 | ||
|
|
51bfb9a4e1 | ||
|
|
76e1cc84db | ||
|
|
d539f2540b | ||
|
|
e53cdeaf0b | ||
|
|
4d72a758fa | ||
|
|
a2e9b7da07 | ||
|
|
e408eabd8b | ||
|
|
5718dfd69a | ||
|
|
4a9b66aeb3 | ||
|
|
464ff0b703 | ||
|
|
6984989a2c | ||
|
|
2dcc11ff89 | ||
|
|
29efa99fb7 | ||
|
|
6c432028ae | ||
|
|
0270ce9251 | ||
|
|
182e3b6e8f | ||
|
|
26afa45fbf | ||
|
|
edd92d01a9 |
1
.nuxtrc
1
.nuxtrc
@@ -1,3 +1,2 @@
|
|||||||
imports.autoImport=false
|
imports.autoImport=false
|
||||||
typescript.includeWorkspace=true
|
typescript.includeWorkspace=true
|
||||||
typescript.strict=false
|
|
||||||
|
|||||||
21
CHANGELOG.md
21
CHANGELOG.md
@@ -1,5 +1,26 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [2.11.1](https://github.com/nuxt/ui/compare/v2.11.0...v2.11.1) (2023-12-11)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **Breadcrumb:** handle divider in rtl ([#1049](https://github.com/nuxt/ui/issues/1049)) ([e53cdea](https://github.com/nuxt/ui/commit/e53cdeaf0b3746da76cb6a658a5f71064d97fc9a))
|
||||||
|
* **CommandPalette:** improve performances and avoid multiple recompute ([db508b2](https://github.com/nuxt/ui/commit/db508b218f5277b2522566f790bd268eae2ee1e5))
|
||||||
|
* **CommandPalette:** missing right padding on input with close button ([ad33b26](https://github.com/nuxt/ui/commit/ad33b26729b1bf3d21f8d480e04c197f4fbb6119))
|
||||||
|
* **components:** move remaining classes to config ([#1039](https://github.com/nuxt/ui/issues/1039)) ([e408eab](https://github.com/nuxt/ui/commit/e408eabd8b841cdf8c71ce27c35c9675f2db8625))
|
||||||
|
* **module:** prevent class merging on `default` children ([f07968a](https://github.com/nuxt/ui/commit/f07968afef263d38183ce6c9cd9185ef7eee0494)), closes [#1076](https://github.com/nuxt/ui/issues/1076)
|
||||||
|
* **Notification:** handle dynamic backgrounds ([#1063](https://github.com/nuxt/ui/issues/1063)) ([1f0f618](https://github.com/nuxt/ui/commit/1f0f6181db7fa1ab45b8f7fec8df1cedccaec688))
|
||||||
|
* **RadioGroup:** props reactivity issues ([#1065](https://github.com/nuxt/ui/issues/1065)) ([7196d81](https://github.com/nuxt/ui/commit/7196d81b4cecf1711a01bed5fed1236ab3b2398b))
|
||||||
|
* **types:** favor `Record<string, any>>` instead of `object` ([4d72a75](https://github.com/nuxt/ui/commit/4d72a758fad5cffa09f3aaf6b3df9baf7edc2a9f))
|
||||||
|
* **types:** improve with strict mode ([#1041](https://github.com/nuxt/ui/issues/1041)) ([4a9b66a](https://github.com/nuxt/ui/commit/4a9b66aeb32a332e2d5be7e236e5d4567044b3e2))
|
||||||
|
* **types:** workaround for `popper` weak type ([5718dfd](https://github.com/nuxt/ui/commit/5718dfd69a7040987354485b30f7da7aee342abb)), closes [#644](https://github.com/nuxt/ui/issues/644)
|
||||||
|
|
||||||
|
|
||||||
|
### Reverts
|
||||||
|
|
||||||
|
* Revert "chore(deps): pin `vitest`" ([6984989](https://github.com/nuxt/ui/commit/6984989a2c20fbde177d1e64ea1a7cae07f03c4d))
|
||||||
|
|
||||||
## [2.11.0](https://github.com/nuxt/ui/compare/v2.10.0...v2.11.0) (2023-11-23)
|
## [2.11.0](https://github.com/nuxt/ui/compare/v2.10.0...v2.11.0) (2023-11-23)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ defineProps({
|
|||||||
<h1 class="m-0 text-[75px] font-semibold mb-2 text-white flex items-center">
|
<h1 class="m-0 text-[75px] font-semibold mb-2 text-white flex items-center">
|
||||||
<span>{{ title }}</span>
|
<span>{{ title }}</span>
|
||||||
</h1>
|
</h1>
|
||||||
<p class="text-[32px] text-[#94a3b8] leading-tight">
|
<p v-if="description" class="text-[32px] text-[#94a3b8] leading-tight">
|
||||||
{{ description.slice(0, 200) }}
|
{{ description.slice(0, 200) }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
45
docs/components/ads/AdsCarbon.vue
Normal file
45
docs/components/ads/AdsCarbon.vue
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
<template>
|
||||||
|
<div ref="carbonads" class="carbon" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
const carbonads = ref(null)
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (carbonads.value) {
|
||||||
|
const script = document.createElement('script')
|
||||||
|
script.setAttribute('type', 'text/javascript')
|
||||||
|
script.setAttribute('src', 'https://cdn.carbonads.com/carbon.js?serve=CWYIVK3E&placement=uinuxtcom')
|
||||||
|
script.setAttribute('id', '_carbonads_js')
|
||||||
|
carbonads.value.appendChild(script)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="postcss">
|
||||||
|
.carbon > #carbonads {
|
||||||
|
@apply relative border border-gray-200 dark:border-gray-800 rounded-lg bg-white dark:bg-gray-800/50 hover:border-gray-300 dark:hover:border-gray-700 w-full transition-colors min-h-[220px];
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
.carbon-text {
|
||||||
|
@apply text-gray-700 dark:text-gray-200;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.carbon-img {
|
||||||
|
@apply flex justify-center p-2 w-full;
|
||||||
|
|
||||||
|
& > img {
|
||||||
|
@apply !max-w-full w-full rounded;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.carbon-text {
|
||||||
|
@apply flex px-2 text-sm text-gray-500 dark:text-gray-400 transition-colors text-center w-full;
|
||||||
|
}
|
||||||
|
|
||||||
|
.carbon-poweredby {
|
||||||
|
@apply block text-xs text-center text-gray-400 dark:text-gray-500 hover:text-gray-500 dark:hover:text-gray-400 pt-1 pb-2 px-2 transition-colors;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
27
docs/components/ads/AdsPro.vue
Normal file
27
docs/components/ads/AdsPro.vue
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<template>
|
||||||
|
<div class="relative group/ad border border-gray-200 dark:border-gray-800 rounded-lg bg-white dark:bg-gray-800/50 hover:border-gray-300 dark:hover:border-gray-700 p-2 w-full transition-colors">
|
||||||
|
<NuxtLink to="/pro" class="focus:outline-none" tabindex="-1">
|
||||||
|
<span class="absolute inset-0" aria-hidden="true" />
|
||||||
|
</NuxtLink>
|
||||||
|
|
||||||
|
<UColorModeImage
|
||||||
|
light="/pro/illustrations/docs-light.svg"
|
||||||
|
dark="/pro/illustrations/docs-dark.svg"
|
||||||
|
alt="Nuxt UI Pro"
|
||||||
|
loading="lazy"
|
||||||
|
class="w-full"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="flex flex-col items-center mt-2 text-center">
|
||||||
|
<div class="inline-flex gap-1.5">
|
||||||
|
<Logo class="h-4 w-auto" />
|
||||||
|
|
||||||
|
<UBadge variant="subtle" size="xs" label="Pro" class="font-semibold" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p class="text-sm text-gray-500 dark:text-gray-400 group-hover/ad:text-gray-700 dark:group-hover/ad:text-gray-200 mt-1 transition-colors">
|
||||||
|
The Building Blocks for Modern Web Apps.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -110,6 +110,10 @@ const props = defineProps({
|
|||||||
componentClass: {
|
componentClass: {
|
||||||
type: String,
|
type: String,
|
||||||
default: ''
|
default: ''
|
||||||
|
},
|
||||||
|
ignoreVModel: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -218,6 +222,9 @@ const code = computed(() => {
|
|||||||
if (value === 'undefined' || value === null) {
|
if (value === 'undefined' || value === null) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if (key === 'modelValue' && props.ignoreVModel) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
code += ` ${(typeof value === 'boolean' && (value !== true || key === 'modelValue')) || typeof value === 'object' || typeof value === 'number' ? ':' : ''}${key === 'modelValue' ? 'model-value' : kebabCase(key)}${typeof value === 'boolean' && !!value && key !== 'modelValue' ? '' : `="${typeof value === 'object' ? renderObject(value) : value}"`}`
|
code += ` ${(typeof value === 'boolean' && (value !== true || key === 'modelValue')) || typeof value === 'object' || typeof value === 'number' ? ':' : ''}${key === 'modelValue' ? 'model-value' : kebabCase(key)}${typeof value === 'boolean' && !!value && key !== 'modelValue' ? '' : `="${typeof value === 'object' ? renderObject(value) : value}"`}`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<Field v-bind="prop">
|
<Field v-bind="prop">
|
||||||
<code v-if="prop.default">{{ prop.default }}</code>
|
<code v-if="prop.default" class="leading-6">{{ prop.default }}</code>
|
||||||
<p v-if="prop.description">
|
<p v-if="prop.description">
|
||||||
{{ prop.description }}
|
{{ prop.description }}
|
||||||
</p>
|
</p>
|
||||||
@@ -22,8 +22,8 @@
|
|||||||
<ComponentPropsField v-for="subProp in Object.values(prop.schema.schema)" :key="(subProp as any).name" :prop="subProp" />
|
<ComponentPropsField v-for="subProp in Object.values(prop.schema.schema)" :key="(subProp as any).name" :prop="subProp" />
|
||||||
</FieldGroup>
|
</FieldGroup>
|
||||||
</Collapsible>
|
</Collapsible>
|
||||||
<div v-else-if="prop.schema?.kind === 'enum' && prop.schema.type !== 'boolean' && startsWithCapital(prop.schema.type) && !prop.schema.type.startsWith(prop.schema.schema[0])" class="space-x-1 leading-7 -my-1">
|
<div v-else-if="prop.schema?.kind === 'enum' && prop.schema.type !== 'boolean' && startsWithCapital(prop.schema.type) && !prop.schema.type.startsWith(prop.schema.schema[0])" class="flex items-center flex-wrap gap-1 -my-1">
|
||||||
<code v-for="value in prop.schema.schema.filter(value => typeof value === 'string')" :key="value" class="whitespace-pre-wrap break-words">{{ value }}</code>
|
<code v-for="value in prop.schema.schema.filter(value => typeof value === 'string')" :key="value" class="whitespace-pre-wrap break-words leading-6">{{ value }}</code>
|
||||||
</div>
|
</div>
|
||||||
</Field>
|
</Field>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ const options = [
|
|||||||
|
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
input: undefined,
|
input: undefined,
|
||||||
|
inputMenu: undefined,
|
||||||
textarea: undefined,
|
textarea: undefined,
|
||||||
select: undefined,
|
select: undefined,
|
||||||
selectMenu: undefined,
|
selectMenu: undefined,
|
||||||
@@ -23,6 +24,9 @@ const state = reactive({
|
|||||||
|
|
||||||
const schema = z.object({
|
const schema = z.object({
|
||||||
input: z.string().min(10),
|
input: z.string().min(10),
|
||||||
|
inputMenu: z.any().refine(option => option?.value === 'option-2', {
|
||||||
|
message: 'Select Option 2'
|
||||||
|
}),
|
||||||
textarea: z.string().min(10),
|
textarea: z.string().min(10),
|
||||||
select: z.string().refine(value => value === 'option-2', {
|
select: z.string().refine(value => value === 'option-2', {
|
||||||
message: 'Select Option 2'
|
message: 'Select Option 2'
|
||||||
@@ -61,6 +65,10 @@ async function onSubmit (event: FormSubmitEvent<Schema>) {
|
|||||||
<UInput v-model="state.input" />
|
<UInput v-model="state.input" />
|
||||||
</UFormGroup>
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UFormGroup name="inputMenu" label="Input Menu">
|
||||||
|
<UInputMenu v-model="state.inputMenu" :options="options" />
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
<UFormGroup name="textarea" label="Textarea">
|
<UFormGroup name="textarea" label="Textarea">
|
||||||
<UTextarea v-model="state.textarea" />
|
<UTextarea v-model="state.textarea" />
|
||||||
</UFormGroup>
|
</UFormGroup>
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<script setup>
|
||||||
|
const people = ['Wade Cooper', 'Arlene Mccoy', 'Devon Webb', 'Tom Cook', 'Tanya Fox', 'Hellen Schmidt', 'Caroline Schultz', 'Mason Heaney', 'Claudie Smitham', 'Emil Schaefer']
|
||||||
|
|
||||||
|
const selected = ref(people[0])
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UInputMenu v-model="selected" :options="people" />
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<script setup>
|
||||||
|
const people = []
|
||||||
|
|
||||||
|
const selected = ref()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UInputMenu v-model="selected" :options="people">
|
||||||
|
<template #empty>
|
||||||
|
No people
|
||||||
|
</template>
|
||||||
|
</UInputMenu>
|
||||||
|
</template>
|
||||||
36
docs/components/content/examples/InputMenuExampleObjects.vue
Normal file
36
docs/components/content/examples/InputMenuExampleObjects.vue
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
<script setup>
|
||||||
|
const people = [{
|
||||||
|
id: 'benjamincanac',
|
||||||
|
label: 'benjamincanac',
|
||||||
|
href: 'https://github.com/benjamincanac',
|
||||||
|
target: '_blank',
|
||||||
|
avatar: { src: 'https://avatars.githubusercontent.com/u/739984?v=4' }
|
||||||
|
}, {
|
||||||
|
id: 'Atinux',
|
||||||
|
label: 'Atinux',
|
||||||
|
href: 'https://github.com/Atinux',
|
||||||
|
target: '_blank',
|
||||||
|
avatar: { src: 'https://avatars.githubusercontent.com/u/904724?v=4' }
|
||||||
|
}, {
|
||||||
|
id: 'smarroufin',
|
||||||
|
label: 'smarroufin',
|
||||||
|
href: 'https://github.com/smarroufin',
|
||||||
|
target: '_blank',
|
||||||
|
avatar: { src: 'https://avatars.githubusercontent.com/u/7547335?v=4' }
|
||||||
|
}, {
|
||||||
|
id: 'nobody',
|
||||||
|
label: 'Nobody',
|
||||||
|
icon: 'i-heroicons-user-circle'
|
||||||
|
}]
|
||||||
|
|
||||||
|
const selected = ref(people[0])
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UInputMenu v-model="selected" :options="people">
|
||||||
|
<template #leading>
|
||||||
|
<UIcon v-if="selected.icon" :name="selected.icon" class="w-4 h-4 mx-0.5" />
|
||||||
|
<UAvatar v-else-if="selected.avatar" v-bind="selected.avatar" size="3xs" class="mx-0.5" />
|
||||||
|
</template>
|
||||||
|
</UInputMenu>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
<script setup>
|
||||||
|
const people = [{
|
||||||
|
id: 1,
|
||||||
|
name: 'Wade Cooper'
|
||||||
|
}, {
|
||||||
|
id: 2,
|
||||||
|
name: 'Arlene Mccoy'
|
||||||
|
}, {
|
||||||
|
id: 3,
|
||||||
|
name: 'Devon Webb'
|
||||||
|
}, {
|
||||||
|
id: 4,
|
||||||
|
name: 'Tom Cook'
|
||||||
|
}]
|
||||||
|
|
||||||
|
const selected = ref(people[0].name)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UInputMenu
|
||||||
|
v-model="selected"
|
||||||
|
:options="people"
|
||||||
|
value-attribute="name"
|
||||||
|
option-attribute="name"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<script setup>
|
||||||
|
const people = ['Wade Cooper', 'Arlene Mccoy', 'Devon Webb', 'Tom Cook', 'Tanya Fox', 'Hellen Schmidt', 'Caroline Schultz', 'Mason Heaney', 'Claudie Smitham', 'Emil Schaefer']
|
||||||
|
|
||||||
|
const selected = ref(people[0])
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UInputMenu v-model="selected" :options="people" searchable>
|
||||||
|
<template #option-empty="{ query }">
|
||||||
|
<q>{{ query }}</q> not found
|
||||||
|
</template>
|
||||||
|
</UInputMenu>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<script setup>
|
||||||
|
const people = [
|
||||||
|
{ name: 'Wade Cooper', online: true },
|
||||||
|
{ name: 'Arlene Mccoy', online: false },
|
||||||
|
{ name: 'Devon Webb', online: false },
|
||||||
|
{ name: 'Tom Cook', online: true },
|
||||||
|
{ name: 'Tanya Fox', online: false },
|
||||||
|
{ name: 'Hellen Schmidt', online: true },
|
||||||
|
{ name: 'Caroline Schultz', online: true },
|
||||||
|
{ name: 'Mason Heaney', online: false },
|
||||||
|
{ name: 'Claudie Smitham', online: true },
|
||||||
|
{ name: 'Emil Schaefer', online: false }
|
||||||
|
]
|
||||||
|
|
||||||
|
const selected = ref(people[3])
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UInputMenu v-model="selected" :options="people" option-attribute="name">
|
||||||
|
<template #option="{ option: person }">
|
||||||
|
<span :class="[person.online ? 'bg-green-400' : 'bg-gray-200', 'inline-block h-2 w-2 flex-shrink-0 rounded-full']" aria-hidden="true" />
|
||||||
|
<span class="truncate">{{ person.name }}</span>
|
||||||
|
</template>
|
||||||
|
</UInputMenu>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<script setup>
|
||||||
|
const people = ['Wade Cooper', 'Arlene Mccoy', 'Devon Webb', 'Tom Cook', 'Tanya Fox', 'Hellen Schmidt', 'Caroline Schultz', 'Mason Heaney', 'Claudie Smitham', 'Emil Schaefer']
|
||||||
|
|
||||||
|
const selected = ref(people[0])
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UInputMenu v-model="selected" :options="people" :popper="{ arrow: true }" />
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<script setup>
|
||||||
|
const people = ['Wade Cooper', 'Arlene Mccoy', 'Devon Webb', 'Tom Cook', 'Tanya Fox', 'Hellen Schmidt', 'Caroline Schultz', 'Mason Heaney', 'Claudie Smitham', 'Emil Schaefer']
|
||||||
|
|
||||||
|
const selected = ref(people[0])
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UInputMenu v-model="selected" :options="people" :popper="{ offsetDistance: 0 }" />
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<script setup>
|
||||||
|
const people = ['Wade Cooper', 'Arlene Mccoy', 'Devon Webb', 'Tom Cook', 'Tanya Fox', 'Hellen Schmidt', 'Caroline Schultz', 'Mason Heaney', 'Claudie Smitham', 'Emil Schaefer']
|
||||||
|
|
||||||
|
const selected = ref(people[0])
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UInputMenu v-model="selected" :options="people" :popper="{ placement: 'right-start' }" />
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<script setup>
|
||||||
|
const options = [
|
||||||
|
{ id: 1, name: 'Wade Cooper', colors: ['red', 'yellow'] },
|
||||||
|
{ id: 2, name: 'Arlene Mccoy', colors: ['blue', 'yellow'] },
|
||||||
|
{ id: 3, name: 'Devon Webb', colors: ['green', 'blue'] },
|
||||||
|
{ id: 4, name: 'Tom Cook', colors: ['blue', 'red'] },
|
||||||
|
{ id: 5, name: 'Tanya Fox', colors: ['green', 'red'] },
|
||||||
|
{ id: 5, name: 'Hellen Schmidt', colors: ['green', 'yellow'] }
|
||||||
|
]
|
||||||
|
|
||||||
|
const selected = ref(options[1])
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UInputMenu
|
||||||
|
v-model="selected"
|
||||||
|
:options="options"
|
||||||
|
placeholder="Select a person"
|
||||||
|
by="id"
|
||||||
|
option-attribute="name"
|
||||||
|
:search-attributes="['name', 'colors']"
|
||||||
|
>
|
||||||
|
<template #option="{ option: person }">
|
||||||
|
<span v-for="color in person.colors" :key="color.id" class="h-2 w-2 rounded-full" :class="`bg-${color}-500 dark:bg-${color}-400`" />
|
||||||
|
<span class="truncate">{{ person.name }}</span>
|
||||||
|
</template>
|
||||||
|
</UInputMenu>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<script setup>
|
||||||
|
const people = []
|
||||||
|
|
||||||
|
const selected = ref()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<USelectMenu v-model="selected" :options="people">
|
||||||
|
<template #empty>
|
||||||
|
No people
|
||||||
|
</template>
|
||||||
|
</USelectMenu>
|
||||||
|
</template>
|
||||||
@@ -28,11 +28,9 @@ const selected = ref(people[0])
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<USelectMenu v-model="selected" :options="people">
|
<USelectMenu v-model="selected" :options="people">
|
||||||
<template #label>
|
<template #leading>
|
||||||
<UIcon v-if="selected.icon" :name="selected.icon" class="w-4 h-4" />
|
<UIcon v-if="selected.icon" :name="selected.icon" class="w-4 h-4 mx-0.5" />
|
||||||
<UAvatar v-else-if="selected.avatar" v-bind="selected.avatar" size="3xs" />
|
<UAvatar v-else-if="selected.avatar" v-bind="selected.avatar" size="3xs" class="mx-0.5" />
|
||||||
|
|
||||||
{{ selected.label }}
|
|
||||||
</template>
|
</template>
|
||||||
</USelectMenu>
|
</USelectMenu>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -13,9 +13,7 @@ const people = [{
|
|||||||
name: 'Tom Cook'
|
name: 'Tom Cook'
|
||||||
}]
|
}]
|
||||||
|
|
||||||
const selected = ref(people[0].id)
|
const selected = ref(people[0].name)
|
||||||
|
|
||||||
const current = computed(() => people.find(person => person.id === selected.value))
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -23,11 +21,7 @@ const current = computed(() => people.find(person => person.id === selected.valu
|
|||||||
v-model="selected"
|
v-model="selected"
|
||||||
:options="people"
|
:options="people"
|
||||||
placeholder="Select people"
|
placeholder="Select people"
|
||||||
value-attribute="id"
|
value-attribute="name"
|
||||||
option-attribute="name"
|
option-attribute="name"
|
||||||
>
|
/>
|
||||||
<template #label>
|
|
||||||
{{ current.name }}
|
|
||||||
</template>
|
|
||||||
</USelectMenu>
|
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
const options = [
|
const options = [
|
||||||
{ id: 1, name: 'Wade Cooper', favoriteColors: ['red', 'yellow'] },
|
{ id: 1, name: 'Wade Cooper', colors: ['red', 'yellow'] },
|
||||||
{ id: 2, name: 'Arlene Mccoy', favoriteColors: ['blue', 'yellow'] },
|
{ id: 2, name: 'Arlene Mccoy', colors: ['blue', 'yellow'] },
|
||||||
{ id: 3, name: 'Devon Webb', favoriteColors: ['green', 'blue'] },
|
{ id: 3, name: 'Devon Webb', colors: ['green', 'blue'] },
|
||||||
{ id: 4, name: 'Tom Cook', favoriteColors: ['blue', 'red'] },
|
{ id: 4, name: 'Tom Cook', colors: ['blue', 'red'] },
|
||||||
{ id: 5, name: 'Tanya Fox', favoriteColors: ['green', 'red'] },
|
{ id: 5, name: 'Tanya Fox', colors: ['green', 'red'] },
|
||||||
{ id: 5, name: 'Hellen Schmidt', favoriteColors: ['green', 'yellow'] }
|
{ id: 5, name: 'Hellen Schmidt', colors: ['green', 'yellow'] }
|
||||||
]
|
]
|
||||||
|
|
||||||
const selected = ref(options[1])
|
const selected = ref(options[1])
|
||||||
@@ -15,16 +15,15 @@ const selected = ref(options[1])
|
|||||||
<USelectMenu
|
<USelectMenu
|
||||||
v-model="selected"
|
v-model="selected"
|
||||||
:options="options"
|
:options="options"
|
||||||
class="w-full lg:w-96"
|
placeholder="Select a person"
|
||||||
placeholder="Select an user"
|
|
||||||
searchable
|
searchable
|
||||||
searchable-placeholder="Search by name or favorite colors"
|
searchable-placeholder="Search by name or color"
|
||||||
option-attribute="name"
|
option-attribute="name"
|
||||||
by="id"
|
by="id"
|
||||||
:search-attributes="['name', 'favoriteColors']"
|
:search-attributes="['name', 'colors']"
|
||||||
>
|
>
|
||||||
<template #option="{ option: person }">
|
<template #option="{ option: person }">
|
||||||
<span v-for="color in person.favoriteColors" :key="color.id" class="h-2 w-2 rounded-full" :class="`bg-${color}-500 dark:bg-${color}-400`" />
|
<span v-for="color in person.colors" :key="color.id" class="h-2 w-2 rounded-full" :class="`bg-${color}-500 dark:bg-${color}-400`" />
|
||||||
<span class="truncate">{{ person.name }}</span>
|
<span class="truncate">{{ person.name }}</span>
|
||||||
</template>
|
</template>
|
||||||
</USelectMenu>
|
</USelectMenu>
|
||||||
|
|||||||
@@ -14,9 +14,6 @@ const links = [{
|
|||||||
}, {
|
}, {
|
||||||
label: 'Examples',
|
label: 'Examples',
|
||||||
to: '/getting-started/examples'
|
to: '/getting-started/examples'
|
||||||
}, {
|
|
||||||
label: 'Roadmap',
|
|
||||||
to: '/getting-started/roadmap'
|
|
||||||
}]
|
}]
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<NuxtLink v-if="date.release" :to="`https://github.com/nuxt/ui/releases/tag/${date.release.name}`" target="_blank" class="text-gray-900 dark:text-white font-bold text-3xl mt-2 group hover:text-primary-500 dark:hover:text-primary-400 transition-[color]">
|
<NuxtLink v-if="date.release" :to="`https://github.com/nuxt/ui/releases/tag/${date.release.name}`" target="_blank" class="text-gray-900 dark:text-white font-bold text-3xl mt-2 group hover:text-primary-500 dark:hover:text-primary-400 transition-[color]">
|
||||||
{{ date.release.name }}
|
{{ date.release.name }}
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
<ul v-else-if="date.pulls?.length" class="mt-2 space-y-1 text-gray-600 dark:text-gray-300">
|
<ul v-if="date.pulls?.length" class="mt-2 space-y-1 text-gray-600 dark:text-gray-300">
|
||||||
<li v-for="pull in date.pulls" :key="pull.id" class="text-sm/6 break-all">
|
<li v-for="pull in date.pulls" :key="pull.id" class="text-sm/6 break-all">
|
||||||
<NuxtLink :to="`https://github.com/${pull.user.login}`" target="_blank" class="text-gray-900 dark:text-white transition-colors inline-flex items-center gap-1 rounded-full bg-gray-100/50 dark:bg-gray-800/50 dark:hover:bg-gray-800 p-0.5 pr-1 ring-1 ring-gray-300 dark:ring-gray-700 text-xs font-medium flex-shrink-0 align-middle">
|
<NuxtLink :to="`https://github.com/${pull.user.login}`" target="_blank" class="text-gray-900 dark:text-white transition-colors inline-flex items-center gap-1 rounded-full bg-gray-100/50 dark:bg-gray-800/50 dark:hover:bg-gray-800 p-0.5 pr-1 ring-1 ring-gray-300 dark:ring-gray-700 text-xs font-medium flex-shrink-0 align-middle">
|
||||||
<UAvatar :src="`https://github.com/${pull.user.login}.png`" size="3xs" />
|
<UAvatar :src="`https://github.com/${pull.user.login}.png`" size="3xs" />
|
||||||
|
|||||||
@@ -142,18 +142,10 @@ To use the latest updates pushed on the [`dev`](https://github.com/nuxt/ui/tree/
|
|||||||
|
|
||||||
Update your `package.json` to the following:
|
Update your `package.json` to the following:
|
||||||
|
|
||||||
```json [package.json]
|
|
||||||
{
|
|
||||||
"devDependencies": {
|
|
||||||
"@nuxt/ui": "npm:@nuxt/ui-edge@latest"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
```diff [package.json]
|
```diff [package.json]
|
||||||
{
|
{
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
- "@nuxt/ui": "^2.9.0"
|
- "@nuxt/ui": "^2.11.0"
|
||||||
+ "@nuxt/ui": "npm:@nuxt/ui-edge@latest"
|
+ "@nuxt/ui": "npm:@nuxt/ui-edge@latest"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -311,7 +311,7 @@ export default defineNuxtConfig({
|
|||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
### Custom config :u-badge{label="New" class="align-middle ml-2 !rounded-full" variant="subtle"}
|
### Custom config
|
||||||
|
|
||||||
If you have specific needs, like using a custom icon collection, you can use the `icons` option in your `nuxt.config.ts` as an object to override the config of the [egoist/tailwindcss-icons](https://github.com/egoist/tailwindcss-icons#plugin-options) plugin.
|
If you have specific needs, like using a custom icon collection, you can use the `icons` option in your `nuxt.config.ts` as an object to override the config of the [egoist/tailwindcss-icons](https://github.com/egoist/tailwindcss-icons#plugin-options) plugin.
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
---
|
---
|
||||||
description: Display a chip indicator on any component.
|
description: Display a chip indicator on any component.
|
||||||
navigation:
|
|
||||||
badge: New
|
|
||||||
links:
|
links:
|
||||||
- label: GitHub
|
- label: GitHub
|
||||||
icon: i-simple-icons-github
|
icon: i-simple-icons-github
|
||||||
|
|||||||
@@ -14,8 +14,9 @@ links:
|
|||||||
Pass an array of arrays to the `items` prop of the Dropdown component. Each array represents a group of items. Each item can have the following properties:
|
Pass an array of arrays to the `items` prop of the Dropdown component. Each array represents a group of items. Each item can have the following properties:
|
||||||
|
|
||||||
- `label` - The label of the item.
|
- `label` - The label of the item.
|
||||||
|
- `labelClass` - The class of the item label. :u-badge{label="New" class="!rounded-full" variant="subtle"}
|
||||||
- `icon` - The icon of the item.
|
- `icon` - The icon of the item.
|
||||||
- `iconClass` - The class of the icon of the item.
|
- `iconClass` - The class of the item icon.
|
||||||
- `avatar` - The avatar of the item. You can pass all the props of the [Avatar](/elements/avatar) component.
|
- `avatar` - The avatar of the item. You can pass all the props of the [Avatar](/elements/avatar) component.
|
||||||
- `shortcuts` - The shortcuts of the item.
|
- `shortcuts` - The shortcuts of the item.
|
||||||
- `slot` - The slot of the item.
|
- `slot` - The slot of the item.
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ props:
|
|||||||
You won't be able to use any icon in the `name` prop here as icons are bundled using [egoist/tailwindcss-icons](https://github.com/egoist/tailwindcss-icons), read more about this in [Theming](/getting-started/theming#icons).
|
You won't be able to use any icon in the `name` prop here as icons are bundled using [egoist/tailwindcss-icons](https://github.com/egoist/tailwindcss-icons), read more about this in [Theming](/getting-started/theming#icons).
|
||||||
::
|
::
|
||||||
|
|
||||||
### Dynamic :u-badge{label="New" class="align-middle ml-2 !rounded-full" variant="subtle"}
|
### Dynamic
|
||||||
|
|
||||||
You can use the `dynamic` prop to enable dynamic icon loading. This will use [`nuxt-icon`](https://github.com/nuxt-modules/icon) instead and allow you to use any icon from [Iconify](https://iconify.design/) as well as the `{collection_name}:{icon_name}` pattern.
|
You can use the `dynamic` prop to enable dynamic icon loading. This will use [`nuxt-icon`](https://github.com/nuxt-modules/icon) instead and allow you to use any icon from [Iconify](https://iconify.design/) as well as the `{collection_name}:{icon_name}` pattern.
|
||||||
|
|
||||||
|
|||||||
@@ -148,6 +148,21 @@ excludedProps:
|
|||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
|
### Padded
|
||||||
|
|
||||||
|
Use the `padded` prop to remove the padding of the Input.
|
||||||
|
|
||||||
|
::component-card
|
||||||
|
---
|
||||||
|
props:
|
||||||
|
padded: false
|
||||||
|
baseProps:
|
||||||
|
placeholder: 'Search...'
|
||||||
|
variant: 'none'
|
||||||
|
class: 'w-full'
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
## Slots
|
## Slots
|
||||||
|
|
||||||
### `leading`
|
### `leading`
|
||||||
@@ -157,13 +172,13 @@ Use the `#leading` slot to set the content of the leading icon.
|
|||||||
::component-card
|
::component-card
|
||||||
---
|
---
|
||||||
slots:
|
slots:
|
||||||
leading: <UAvatar src="https://avatars.githubusercontent.com/u/739984?v=4" size="3xs" />
|
leading: <UAvatar src="https://avatars.githubusercontent.com/u/739984?v=4" size="3xs" class="mx-0.5" />
|
||||||
baseProps:
|
baseProps:
|
||||||
placeholder: 'Search...'
|
placeholder: 'Search...'
|
||||||
---
|
---
|
||||||
|
|
||||||
#leading
|
#leading
|
||||||
:u-avatar{src="https://avatars.githubusercontent.com/u/739984?v=4" size="3xs"}
|
:u-avatar{src="https://avatars.githubusercontent.com/u/739984?v=4" size="3xs" class="mx-0.5"}
|
||||||
::
|
::
|
||||||
|
|
||||||
### `trailing`
|
### `trailing`
|
||||||
|
|||||||
170
docs/content/3.forms/2.input-menu.md
Normal file
170
docs/content/3.forms/2.input-menu.md
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
---
|
||||||
|
title: InputMenu
|
||||||
|
description: Display an autocomplete input with real-time suggestions.
|
||||||
|
links:
|
||||||
|
- label: GitHub
|
||||||
|
icon: i-simple-icons-github
|
||||||
|
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/forms/InputMenu.vue
|
||||||
|
- label: 'Combobox'
|
||||||
|
icon: i-simple-icons-headlessui
|
||||||
|
to: 'https://headlessui.com/vue/combobox'
|
||||||
|
navigation:
|
||||||
|
badge: New
|
||||||
|
---
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
The `InputMenu` component renders by default an [Input](/forms/input) component and is based on the `ui.input` preset. You can use most of the `Input` props to configure the display such as [color](/forms/input#style), [variant](/forms/input#style), [size](/forms/input#size), [placeholder](/forms/input#placeholder), [icon](/forms/input#icon), [disabled](/forms/input#disabled), etc.
|
||||||
|
|
||||||
|
You can use the `ui` prop like the `Input` component to override the default config. The `uiMenu` prop can be used to override the default menu config.
|
||||||
|
|
||||||
|
Pass an array of strings or objects to the `options` prop to display in the menu.
|
||||||
|
|
||||||
|
::component-example
|
||||||
|
---
|
||||||
|
component: 'input-menu-example-basic'
|
||||||
|
componentProps:
|
||||||
|
class: 'w-full lg:w-48'
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
::callout{icon="i-heroicons-exclamation-triangle"}
|
||||||
|
This component does not support multiple values. Use the [SelectMenu](/forms/select-menu#multiple) component instead.
|
||||||
|
::
|
||||||
|
|
||||||
|
### Objects
|
||||||
|
|
||||||
|
You can pass an array of objects to `options` and either compare on the whole object or use the `by` prop to compare on a specific key. You can configure which field will be used to display the label through the `option-attribute` prop that defaults to `label`.
|
||||||
|
|
||||||
|
::component-example
|
||||||
|
---
|
||||||
|
component: 'input-menu-example-objects'
|
||||||
|
componentProps:
|
||||||
|
class: 'w-full lg:w-48'
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
Use the `search-attributes` prop with an array of property names to search on each option object. Nested attributes can be accessed using `dot.notation`. When the property value is an array or object, these are cast to string so these can be searched within.
|
||||||
|
|
||||||
|
::component-example
|
||||||
|
---
|
||||||
|
component: 'input-menu-example-search-attributes'
|
||||||
|
componentProps:
|
||||||
|
class: 'w-full lg:w-48'
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
If you only want to select a single object property rather than the whole object as value, you can set the `value-attribute` property. This prop defaults to `null`.
|
||||||
|
|
||||||
|
::component-example
|
||||||
|
---
|
||||||
|
component: 'input-menu-example-objects-value-attribute'
|
||||||
|
componentProps:
|
||||||
|
class: 'w-full lg:w-48'
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
### Icon
|
||||||
|
|
||||||
|
The `InputMenu` has a button on the right to toggle the menu. Use the `trailing-icon` prop to set a different icon or change it globally in `ui.inputMenu.default.trailingIcon`. Defaults to `i-heroicons-chevron-down-20-solid`.
|
||||||
|
|
||||||
|
::component-card
|
||||||
|
---
|
||||||
|
baseProps:
|
||||||
|
class: 'w-full lg:w-48'
|
||||||
|
placeholder: 'Select a person'
|
||||||
|
options: ['Wade Cooper', 'Arlene Mccoy', 'Devon Webb', 'Tom Cook', 'Tanya Fox', 'Hellen Schmidt', 'Caroline Schultz', 'Mason Heaney', 'Claudie Smitham', 'Emil Schaefer']
|
||||||
|
props:
|
||||||
|
trailingIcon: 'i-heroicons-chevron-up-down-20-solid'
|
||||||
|
excludedProps:
|
||||||
|
- trailingIcon
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
Use the `selected-icon` prop to set a different icon or change it globally in `ui.inputMenu.default.selectedIcon`. Defaults to `i-heroicons-check-20-solid`.
|
||||||
|
|
||||||
|
::component-card
|
||||||
|
---
|
||||||
|
baseProps:
|
||||||
|
class: 'w-full lg:w-48'
|
||||||
|
placeholder: 'Select a person'
|
||||||
|
options: ['Wade Cooper', 'Arlene Mccoy', 'Devon Webb', 'Tom Cook', 'Tanya Fox', 'Hellen Schmidt', 'Caroline Schultz', 'Mason Heaney', 'Claudie Smitham', 'Emil Schaefer']
|
||||||
|
props:
|
||||||
|
selectedIcon: 'i-heroicons-hand-thumb-up-solid'
|
||||||
|
excludedProps:
|
||||||
|
- selectedIcon
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
::callout{icon="i-heroicons-light-bulb"}
|
||||||
|
Learn how to customize icons from the [Input](/forms/input#icon) component.
|
||||||
|
::
|
||||||
|
|
||||||
|
## Popper
|
||||||
|
|
||||||
|
Use the `popper` prop to customize the popper instance.
|
||||||
|
|
||||||
|
### Arrow
|
||||||
|
|
||||||
|
:component-example{component="input-menu-example-popper-arrow"}
|
||||||
|
|
||||||
|
### Placement
|
||||||
|
|
||||||
|
:component-example{component="input-menu-example-popper-placement"}
|
||||||
|
|
||||||
|
### Offset
|
||||||
|
|
||||||
|
:component-example{component="input-menu-example-popper-offset"}
|
||||||
|
|
||||||
|
## Slots
|
||||||
|
|
||||||
|
### `option`
|
||||||
|
|
||||||
|
Use the `#option` slot to customize the option content. You will have access to the `option`, `active` and `selected` properties in the slot scope.
|
||||||
|
|
||||||
|
::component-example
|
||||||
|
---
|
||||||
|
component: 'input-menu-example-option-slot'
|
||||||
|
componentProps:
|
||||||
|
class: 'w-full lg:w-48'
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
### `option-empty`
|
||||||
|
|
||||||
|
Use the `#option-empty` slot to customize the content displayed when the `searchable` prop is `true` and there is no options. You will have access to the `query` property in the slot scope.
|
||||||
|
|
||||||
|
::component-example
|
||||||
|
---
|
||||||
|
component: 'input-menu-example-option-empty-slot'
|
||||||
|
componentProps:
|
||||||
|
class: 'w-full lg:w-48'
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
### `empty`
|
||||||
|
|
||||||
|
Use the `#empty` slot to customize the content displayed when there is no options. Defaults to `No options.`.
|
||||||
|
|
||||||
|
::component-example
|
||||||
|
---
|
||||||
|
component: 'input-menu-example-empty-slot'
|
||||||
|
componentProps:
|
||||||
|
class: 'w-full lg:w-48'
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
## Props
|
||||||
|
|
||||||
|
:component-props
|
||||||
|
|
||||||
|
## Config
|
||||||
|
|
||||||
|
::callout{icon="i-heroicons-light-bulb"}
|
||||||
|
Use the `ui` prop to override the input config and the `uiMenu` prop to override the menu config.
|
||||||
|
::
|
||||||
|
|
||||||
|
::tabs{:selectedIndex="1"}
|
||||||
|
:component-preset{label="Input (ui)" slug="Input"}
|
||||||
|
:component-preset{label="InputMenu (uiMenu)"}
|
||||||
|
::
|
||||||
@@ -132,6 +132,21 @@ props:
|
|||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
|
### Padded
|
||||||
|
|
||||||
|
Use the `padded` prop to remove the padding of the Textarea.
|
||||||
|
|
||||||
|
::component-card
|
||||||
|
---
|
||||||
|
props:
|
||||||
|
padded: false
|
||||||
|
baseProps:
|
||||||
|
placeholder: 'Search...'
|
||||||
|
variant: 'none'
|
||||||
|
class: 'w-full'
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
## Props
|
## Props
|
||||||
|
|
||||||
:component-props
|
:component-props
|
||||||
|
|||||||
@@ -175,6 +175,25 @@ excludedProps:
|
|||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
|
### Padded
|
||||||
|
|
||||||
|
Use the `padded` prop to remove the padding of the Select.
|
||||||
|
|
||||||
|
::component-card
|
||||||
|
---
|
||||||
|
props:
|
||||||
|
padded: false
|
||||||
|
baseProps:
|
||||||
|
placeholder: 'Search...'
|
||||||
|
options:
|
||||||
|
- 'United States'
|
||||||
|
- 'Canada'
|
||||||
|
- 'Mexico'
|
||||||
|
variant: 'none'
|
||||||
|
class: 'w-full'
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
## Slots
|
## Slots
|
||||||
|
|
||||||
### `leading`
|
### `leading`
|
||||||
@@ -184,7 +203,7 @@ Use the `#leading` slot to set the content of the leading icon.
|
|||||||
::component-card
|
::component-card
|
||||||
---
|
---
|
||||||
slots:
|
slots:
|
||||||
leading: <UAvatar src="https://avatars.githubusercontent.com/u/739984?v=4" size="3xs" />
|
leading: <UAvatar src="https://avatars.githubusercontent.com/u/739984?v=4" size="3xs" class="mx-0.5" />
|
||||||
baseProps:
|
baseProps:
|
||||||
options:
|
options:
|
||||||
- 'United States'
|
- 'United States'
|
||||||
@@ -194,7 +213,7 @@ baseProps:
|
|||||||
---
|
---
|
||||||
|
|
||||||
#leading
|
#leading
|
||||||
:u-avatar{src="https://avatars.githubusercontent.com/u/739984?v=4" size="3xs"}
|
:u-avatar{src="https://avatars.githubusercontent.com/u/739984?v=4" size="3xs" class="mx-0.5"}
|
||||||
::
|
::
|
||||||
|
|
||||||
### `trailing`
|
### `trailing`
|
||||||
|
|||||||
@@ -14,13 +14,15 @@ links:
|
|||||||
|
|
||||||
The `SelectMenu` component renders by default a [Select](/forms/select) component and is based on the `ui.select` preset. You can use most of the `Select` props to configure the display if you don't want to override the default slot such as [color](/forms/select#style), [variant](/forms/select#style), [size](/forms/select#size), [placeholder](/forms/select#placeholder), [icon](/forms/select#icon), [disabled](/forms/select#disabled), etc.
|
The `SelectMenu` component renders by default a [Select](/forms/select) component and is based on the `ui.select` preset. You can use most of the `Select` props to configure the display if you don't want to override the default slot such as [color](/forms/select#style), [variant](/forms/select#style), [size](/forms/select#size), [placeholder](/forms/select#placeholder), [icon](/forms/select#icon), [disabled](/forms/select#disabled), etc.
|
||||||
|
|
||||||
|
You can use the `ui` prop like the `Select` component to override the default config. The `uiMenu` prop can be used to override the default menu config.
|
||||||
|
|
||||||
Like the `Select` component, you can use the `options` prop to pass an array of strings or objects.
|
Like the `Select` component, you can use the `options` prop to pass an array of strings or objects.
|
||||||
|
|
||||||
::component-example
|
::component-example
|
||||||
---
|
---
|
||||||
component: 'select-menu-example-basic'
|
component: 'select-menu-example-basic'
|
||||||
componentProps:
|
componentProps:
|
||||||
class: 'w-full lg:w-40'
|
class: 'w-full lg:w-48'
|
||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
@@ -32,7 +34,7 @@ You can use the `multiple` prop to select multiple values.
|
|||||||
---
|
---
|
||||||
component: 'select-menu-example-multiple'
|
component: 'select-menu-example-multiple'
|
||||||
componentProps:
|
componentProps:
|
||||||
class: 'w-full lg:w-40'
|
class: 'w-full lg:w-48'
|
||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
@@ -44,7 +46,7 @@ You can pass an array of objects to `options` and either compare on the whole ob
|
|||||||
---
|
---
|
||||||
component: 'select-menu-example-objects'
|
component: 'select-menu-example-objects'
|
||||||
componentProps:
|
componentProps:
|
||||||
class: 'w-full lg:w-40'
|
class: 'w-full lg:w-48'
|
||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
@@ -54,7 +56,7 @@ If you only want to select a single object property rather than the whole object
|
|||||||
---
|
---
|
||||||
component: 'select-menu-example-objects-value-attribute'
|
component: 'select-menu-example-objects-value-attribute'
|
||||||
componentProps:
|
componentProps:
|
||||||
class: 'w-full lg:w-40'
|
class: 'w-full lg:w-48'
|
||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
@@ -65,7 +67,7 @@ Use the `selected-icon` prop to set a different icon or change it globally in `u
|
|||||||
::component-card
|
::component-card
|
||||||
---
|
---
|
||||||
baseProps:
|
baseProps:
|
||||||
class: 'w-full lg:w-40'
|
class: 'w-full lg:w-48'
|
||||||
placeholder: 'Select a person'
|
placeholder: 'Select a person'
|
||||||
options: ['Wade Cooper', 'Arlene Mccoy', 'Devon Webb', 'Tom Cook', 'Tanya Fox', 'Hellen Schmidt', 'Caroline Schultz', 'Mason Heaney', 'Claudie Smitham', 'Emil Schaefer']
|
options: ['Wade Cooper', 'Arlene Mccoy', 'Devon Webb', 'Tom Cook', 'Tanya Fox', 'Hellen Schmidt', 'Caroline Schultz', 'Mason Heaney', 'Claudie Smitham', 'Emil Schaefer']
|
||||||
props:
|
props:
|
||||||
@@ -79,7 +81,7 @@ excludedProps:
|
|||||||
Learn how to customize icons from the [Select](/forms/select#icon) component.
|
Learn how to customize icons from the [Select](/forms/select#icon) component.
|
||||||
::
|
::
|
||||||
|
|
||||||
### Search
|
## Searchable
|
||||||
|
|
||||||
Use the `searchable` prop to enable search.
|
Use the `searchable` prop to enable search.
|
||||||
|
|
||||||
@@ -90,7 +92,7 @@ This will use Headless UI [Combobox](https://headlessui.com/vue/combobox) compon
|
|||||||
::component-card
|
::component-card
|
||||||
---
|
---
|
||||||
baseProps:
|
baseProps:
|
||||||
class: 'w-full lg:w-40'
|
class: 'w-full lg:w-48'
|
||||||
placeholder: 'Select a person'
|
placeholder: 'Select a person'
|
||||||
options: ['Wade Cooper', 'Arlene Mccoy', 'Devon Webb', 'Tom Cook', 'Tanya Fox', 'Hellen Schmidt', 'Caroline Schultz', 'Mason Heaney', 'Claudie Smitham', 'Emil Schaefer']
|
options: ['Wade Cooper', 'Arlene Mccoy', 'Devon Webb', 'Tom Cook', 'Tanya Fox', 'Hellen Schmidt', 'Caroline Schultz', 'Mason Heaney', 'Claudie Smitham', 'Emil Schaefer']
|
||||||
props:
|
props:
|
||||||
@@ -99,28 +101,26 @@ props:
|
|||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
#### Search Attributes
|
### Attributes
|
||||||
|
|
||||||
Use the `search-attributes` with an array of property names to search on each option object.
|
Use the `search-attributes` prop with an array of property names to search on each option object. Nested attributes can be accessed using `dot.notation`. When the property value is an array or object, these are cast to string so these can be searched within.
|
||||||
|
|
||||||
Nested attributes can be accessed using `dot.notation`. When the property value is an array or object, these are cast to string so these can be searched within.
|
|
||||||
|
|
||||||
::component-example
|
::component-example
|
||||||
---
|
---
|
||||||
component: 'select-menu-example-search-attributes'
|
component: 'select-menu-example-search-attributes'
|
||||||
componentProps:
|
componentProps:
|
||||||
class: 'w-full lg:w-96'
|
class: 'w-full lg:w-48'
|
||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
#### Clear on close :u-badge{label="New" class="align-middle ml-2 !rounded-full" variant="subtle"}
|
### Clear on close
|
||||||
|
|
||||||
By default, the search query will be kept after the menu is closed. To clear it on close, set the `clear-search-on-close` prop.
|
By default, the search query will be kept after the menu is closed. To clear it on close, set the `clear-search-on-close` prop.
|
||||||
|
|
||||||
::component-card
|
::component-card
|
||||||
---
|
---
|
||||||
baseProps:
|
baseProps:
|
||||||
class: 'w-full lg:w-40'
|
class: 'w-full lg:w-48'
|
||||||
placeholder: 'Select a person'
|
placeholder: 'Select a person'
|
||||||
searchable: true
|
searchable: true
|
||||||
searchablePlaceholder: 'Search a person...'
|
searchablePlaceholder: 'Search a person...'
|
||||||
@@ -140,11 +140,11 @@ Use the `debounce` prop to adjust the delay of the function.
|
|||||||
---
|
---
|
||||||
component: 'select-menu-example-async-search'
|
component: 'select-menu-example-async-search'
|
||||||
componentProps:
|
componentProps:
|
||||||
class: 'w-full lg:w-40'
|
class: 'w-full lg:w-48'
|
||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
### Create option
|
## Creatable
|
||||||
|
|
||||||
Use the `creatable` prop to enable the creation of new options when the search doesn't return any results (only works with `searchable`).
|
Use the `creatable` prop to enable the creation of new options when the search doesn't return any results (only works with `searchable`).
|
||||||
|
|
||||||
@@ -154,7 +154,7 @@ Try to search for something that doesn't exist in the example below.
|
|||||||
---
|
---
|
||||||
component: 'select-menu-example-creatable'
|
component: 'select-menu-example-creatable'
|
||||||
componentProps:
|
componentProps:
|
||||||
class: 'w-full lg:w-40'
|
class: 'w-full lg:w-48'
|
||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
@@ -184,7 +184,7 @@ You can override the `#label` slot and handle the display yourself.
|
|||||||
---
|
---
|
||||||
component: 'select-menu-example-multiple-slot'
|
component: 'select-menu-example-multiple-slot'
|
||||||
componentProps:
|
componentProps:
|
||||||
class: 'w-full lg:w-40'
|
class: 'w-full lg:w-48'
|
||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
@@ -196,7 +196,7 @@ You can also override the `#default` slot entirely.
|
|||||||
---
|
---
|
||||||
component: 'select-menu-example-button'
|
component: 'select-menu-example-button'
|
||||||
componentProps:
|
componentProps:
|
||||||
class: 'w-full lg:w-40'
|
class: 'w-full lg:w-48'
|
||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
@@ -208,7 +208,7 @@ Use the `#option` slot to customize the option content. You will have access to
|
|||||||
---
|
---
|
||||||
component: 'select-menu-example-option-slot'
|
component: 'select-menu-example-option-slot'
|
||||||
componentProps:
|
componentProps:
|
||||||
class: 'w-full lg:w-40'
|
class: 'w-full lg:w-48'
|
||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
@@ -220,7 +220,7 @@ Use the `#option-empty` slot to customize the content displayed when the `search
|
|||||||
---
|
---
|
||||||
component: 'select-menu-example-option-empty-slot'
|
component: 'select-menu-example-option-empty-slot'
|
||||||
componentProps:
|
componentProps:
|
||||||
class: 'w-full lg:w-40'
|
class: 'w-full lg:w-48'
|
||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
@@ -232,10 +232,29 @@ Use the `#option-create` slot to customize the content displayed when the `creat
|
|||||||
An example is available in the [Create option](#create-option) section.
|
An example is available in the [Create option](#create-option) section.
|
||||||
::
|
::
|
||||||
|
|
||||||
|
### `empty` :u-badge{label="New" class="align-middle ml-2 !rounded-full" variant="subtle"}
|
||||||
|
|
||||||
|
Use the `#empty` slot to customize the content displayed when there is no options. Defaults to `No options.`.
|
||||||
|
|
||||||
|
::component-example
|
||||||
|
---
|
||||||
|
component: 'select-menu-example-empty-slot'
|
||||||
|
componentProps:
|
||||||
|
class: 'w-full lg:w-48'
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
## Props
|
## Props
|
||||||
|
|
||||||
:component-props
|
:component-props
|
||||||
|
|
||||||
## Config
|
## Config
|
||||||
|
|
||||||
:component-preset
|
::callout{icon="i-heroicons-light-bulb"}
|
||||||
|
Use the `ui` prop to override the select config and the `uiMenu` prop to override the menu config.
|
||||||
|
::
|
||||||
|
|
||||||
|
::tabs{:selectedIndex="1"}
|
||||||
|
:component-preset{label="Select (ui)" slug="Select"}
|
||||||
|
:component-preset{label="SelectMenu (uiMenu)"}
|
||||||
|
::
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ props:
|
|||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
### Size :u-badge{label="New" class="align-middle ml-2 !rounded-full" variant="subtle"}
|
### Size
|
||||||
|
|
||||||
Use the `size` prop to change the size of the Toggle.
|
Use the `size` prop to change the size of the Toggle.
|
||||||
|
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ code: >-
|
|||||||
This will only work with form elements that support the `size` prop.
|
This will only work with form elements that support the `size` prop.
|
||||||
::
|
::
|
||||||
|
|
||||||
### Eager Validation :u-badge{label="New" class="align-middle ml-2 !rounded-full" variant="subtle"}
|
### Eager Validation
|
||||||
|
|
||||||
By default, validation is only triggered after the initial `blur` event. This is to prevent the form from being validated as the user is typing. You can override this behavior by setting the `eager-validation` prop to `true`
|
By default, validation is only triggered after the initial `blur` event. This is to prevent the form from being validated as the user is typing. You can override this behavior by setting the `eager-validation` prop to `true`
|
||||||
|
|
||||||
|
|||||||
@@ -12,8 +12,9 @@ links:
|
|||||||
Pass an array to the `links` prop of the VerticalNavigation component. Each link can have the following properties:
|
Pass an array to the `links` prop of the VerticalNavigation component. Each link can have the following properties:
|
||||||
|
|
||||||
- `label` - The label of the link.
|
- `label` - The label of the link.
|
||||||
|
- `labelClass` - The class of the link label. :u-badge{label="New" class="!rounded-full" variant="subtle"}
|
||||||
- `icon` - The icon of the link.
|
- `icon` - The icon of the link.
|
||||||
- `iconClass` - The class of the icon link.
|
- `iconClass` - The class of the link icon.
|
||||||
- `avatar` - The avatar of the link. You can pass all the props of the [Avatar](/elements/avatar) component.
|
- `avatar` - The avatar of the link. You can pass all the props of the [Avatar](/elements/avatar) component.
|
||||||
- `badge` - A badge to display next to the label.
|
- `badge` - A badge to display next to the label.
|
||||||
- `click` - The click handler of the link.
|
- `click` - The click handler of the link.
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: Breadcrumb
|
title: Breadcrumb
|
||||||
description: A list of links that indicate the current page's location within a navigational hierarchy.
|
description: A list of links that indicate the current page's location within a navigational hierarchy.
|
||||||
navigation:
|
|
||||||
badge: New
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
@@ -10,8 +8,9 @@ navigation:
|
|||||||
Pass an array to the `links` prop of the Breadcrumb component. Each link can have the following properties:
|
Pass an array to the `links` prop of the Breadcrumb component. Each link can have the following properties:
|
||||||
|
|
||||||
- `label` - The label of the link.
|
- `label` - The label of the link.
|
||||||
|
- `labelClass` - The class of the link label. :u-badge{label="New" class="!rounded-full" variant="subtle"}
|
||||||
- `icon` - The icon of the link.
|
- `icon` - The icon of the link.
|
||||||
- `iconClass` - The class of the icon link.
|
- `iconClass` - The class of the link icon.
|
||||||
|
|
||||||
You can also pass any property from the [NuxtLink](https://nuxt.com/docs/api/components/nuxt-link#props) component such as `to`, `exact`, etc.
|
You can also pass any property from the [NuxtLink](https://nuxt.com/docs/api/components/nuxt-link#props) component such as `to`, `exact`, etc.
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ Use the `open` prop to manually control showing the panel.
|
|||||||
|
|
||||||
:component-example{component="popover-example-open"}
|
:component-example{component="popover-example-open"}
|
||||||
|
|
||||||
### Overlay :u-badge{label="New" class="align-middle ml-2 !rounded-full" variant="subtle"}
|
### Overlay
|
||||||
|
|
||||||
:component-example{component="popover-example-overlay"}
|
:component-example{component="popover-example-overlay"}
|
||||||
|
|
||||||
|
|||||||
@@ -67,8 +67,8 @@ sections:
|
|||||||
color: white
|
color: white
|
||||||
size: lg
|
size: lg
|
||||||
trailingIcon: i-heroicons-arrow-right-20-solid
|
trailingIcon: i-heroicons-arrow-right-20-solid
|
||||||
- title: 'A collection of <span class="text-primary">30+</span> components'
|
- title: 'A collection of <span class="text-primary">40+</span> components'
|
||||||
description: 'Get access to 30+ beautifully designed and fully customizable components built for Nuxt. These components<br class="hidden lg:block"> are updated regularly to ensure that you always have the latest features and functionalities.'
|
description: 'Get access to 40+ beautifully designed and fully customizable components built for Nuxt. These components<br class="hidden lg:block"> are updated regularly to ensure that you always have the latest features and functionalities.'
|
||||||
class: 'dark:bg-gradient-to-b from-gray-950/50 to-gray-900'
|
class: 'dark:bg-gradient-to-b from-gray-950/50 to-gray-900'
|
||||||
slot: categories
|
slot: categories
|
||||||
links:
|
links:
|
||||||
@@ -82,12 +82,12 @@ sections:
|
|||||||
to: /elements/dropdown
|
to: /elements/dropdown
|
||||||
image:
|
image:
|
||||||
path: /illustrations/elements
|
path: /illustrations/elements
|
||||||
badge: 9
|
badge: 15
|
||||||
- label: Forms
|
- label: Forms
|
||||||
to: /forms/form
|
to: /forms/form
|
||||||
image:
|
image:
|
||||||
path: /illustrations/forms
|
path: /illustrations/forms
|
||||||
badge: 10
|
badge: 12
|
||||||
- label: Data
|
- label: Data
|
||||||
to: /data/table
|
to: /data/table
|
||||||
image:
|
image:
|
||||||
@@ -97,17 +97,17 @@ sections:
|
|||||||
to: /navigation/command-palette
|
to: /navigation/command-palette
|
||||||
image:
|
image:
|
||||||
path: /illustrations/navigation
|
path: /illustrations/navigation
|
||||||
badge: 4
|
badge: 5
|
||||||
- label: Overlays
|
- label: Overlays
|
||||||
to: /overlays/modal
|
to: /overlays/modal
|
||||||
image:
|
image:
|
||||||
path: /illustrations/overlays
|
path: /illustrations/overlays
|
||||||
badge: 6
|
badge: 7
|
||||||
- label: Layout
|
- label: Layout
|
||||||
to: /layout/card
|
to: /layout/card
|
||||||
image:
|
image:
|
||||||
path: /illustrations/layout
|
path: /illustrations/layout
|
||||||
badge: 3
|
badge: 4
|
||||||
cta:
|
cta:
|
||||||
title: Trusted and supported by our<br class="hidden lg:block"> amazing community
|
title: Trusted and supported by our<br class="hidden lg:block"> amazing community
|
||||||
pro:
|
pro:
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ const navigation = computed(() => {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
return nav.value.filter(item => item._path !== '/dev')
|
return nav.value?.filter(item => item._path !== '/dev') || []
|
||||||
})
|
})
|
||||||
|
|
||||||
const links = computed(() => {
|
const links = computed(() => {
|
||||||
|
|||||||
@@ -102,6 +102,7 @@ export default defineNuxtConfig({
|
|||||||
process.env.NUXT_UI_PRO_PATH ? resolve(process.env.NUXT_UI_PRO_PATH, '.docs', 'components') : '.c12'
|
process.env.NUXT_UI_PRO_PATH ? resolve(process.env.NUXT_UI_PRO_PATH, '.docs', 'components') : '.c12'
|
||||||
],
|
],
|
||||||
metaFields: {
|
metaFields: {
|
||||||
|
type: false,
|
||||||
props: true,
|
props: true,
|
||||||
slots: true,
|
slots: true,
|
||||||
events: false,
|
events: false,
|
||||||
@@ -123,5 +124,13 @@ export default defineNuxtConfig({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
typescript: {
|
||||||
|
strict: false
|
||||||
|
},
|
||||||
|
vite: {
|
||||||
|
optimizeDeps: {
|
||||||
|
include: ['date-fns']
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -5,28 +5,28 @@
|
|||||||
"@nuxt/ui": "workspace:latest"
|
"@nuxt/ui": "workspace:latest"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@iconify-json/heroicons": "^1.1.13",
|
"@iconify-json/heroicons": "^1.1.15",
|
||||||
"@iconify-json/simple-icons": "^1.1.79",
|
"@iconify-json/simple-icons": "^1.1.82",
|
||||||
"@nuxt/content": "^2.9.0",
|
"@nuxt/content": "^2.9.0",
|
||||||
"@nuxt/devtools": "^1.0.3",
|
"@nuxt/devtools": "^1.0.4",
|
||||||
"@nuxt/eslint-config": "^0.2.0",
|
"@nuxt/eslint-config": "^0.2.0",
|
||||||
"@nuxt/ui-pro": "npm:@nuxt/ui-pro-edge@0.4.2-28344234.90e73a4",
|
"@nuxt/ui-pro": "npm:@nuxt/ui-pro-edge@0.6.0-28371586.2e2b852",
|
||||||
"@nuxthq/studio": "^1.0.5",
|
"@nuxthq/studio": "^1.0.5",
|
||||||
"@nuxtjs/fontaine": "^0.4.1",
|
"@nuxtjs/fontaine": "^0.4.1",
|
||||||
"@nuxtjs/google-fonts": "^3.0.2",
|
"@nuxtjs/google-fonts": "^3.1.0",
|
||||||
"@nuxtjs/mdc": "^0.2.8",
|
"@nuxtjs/mdc": "^0.2.8",
|
||||||
"@nuxtjs/plausible": "^0.2.3",
|
"@nuxtjs/plausible": "^0.2.4",
|
||||||
"@octokit/rest": "^20.0.2",
|
"@octokit/rest": "^20.0.2",
|
||||||
"@vueuse/nuxt": "^10.6.1",
|
"@vueuse/nuxt": "^10.7.0",
|
||||||
"date-fns": "^2.30.0",
|
"date-fns": "^2.30.0",
|
||||||
"eslint": "^8.54.0",
|
"eslint": "^8.55.0",
|
||||||
"joi": "^17.11.0",
|
"joi": "^17.11.0",
|
||||||
"nuxt": "^3.8.2",
|
"nuxt": "^3.8.2",
|
||||||
"nuxt-cloudflare-analytics": "^1.0.8",
|
"nuxt-cloudflare-analytics": "^1.0.8",
|
||||||
"nuxt-component-meta": "npm:nuxt-component-meta-edge@0.5.5-28315603.0a285c7",
|
"nuxt-component-meta": "^0.6.0",
|
||||||
"nuxt-og-image": "^2.2.4",
|
"nuxt-og-image": "^2.2.4",
|
||||||
"prettier": "^3.1.0",
|
"prettier": "^3.1.0",
|
||||||
"typescript": "^5.3.2",
|
"typescript": "^5.3.3",
|
||||||
"ufo": "^1.3.2",
|
"ufo": "^1.3.2",
|
||||||
"v-calendar": "^3.1.2",
|
"v-calendar": "^3.1.2",
|
||||||
"valibot": "^0.21.0",
|
"valibot": "^0.21.0",
|
||||||
|
|||||||
@@ -17,6 +17,13 @@
|
|||||||
<UDivider v-if="page.body?.toc?.links?.length" type="dashed" />
|
<UDivider v-if="page.body?.toc?.links?.length" type="dashed" />
|
||||||
|
|
||||||
<UPageLinks title="Community" :links="links" />
|
<UPageLinks title="Community" :links="links" />
|
||||||
|
|
||||||
|
<UDivider type="dashed" />
|
||||||
|
|
||||||
|
<div class="space-y-3">
|
||||||
|
<AdsPro />
|
||||||
|
<AdsCarbon />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</UDocsToc>
|
</UDocsToc>
|
||||||
|
|||||||
28
package.json
28
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@nuxt/ui",
|
"name": "@nuxt/ui",
|
||||||
"version": "2.11.0",
|
"version": "2.11.1",
|
||||||
"repository": "nuxt/ui",
|
"repository": "nuxt/ui",
|
||||||
"homepage": "https://ui.nuxt.com",
|
"homepage": "https://ui.nuxt.com",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -35,44 +35,42 @@
|
|||||||
"@egoist/tailwindcss-icons": "^1.4.0",
|
"@egoist/tailwindcss-icons": "^1.4.0",
|
||||||
"@headlessui/tailwindcss": "^0.2.0",
|
"@headlessui/tailwindcss": "^0.2.0",
|
||||||
"@headlessui/vue": "^1.7.16",
|
"@headlessui/vue": "^1.7.16",
|
||||||
"@iconify-json/heroicons": "^1.1.13",
|
"@iconify-json/heroicons": "^1.1.15",
|
||||||
"@nuxt/kit": "^3.8.2",
|
"@nuxt/kit": "^3.8.2",
|
||||||
"@nuxtjs/color-mode": "^3.3.2",
|
"@nuxtjs/color-mode": "^3.3.2",
|
||||||
"@nuxtjs/tailwindcss": "^6.10.0",
|
"@nuxtjs/tailwindcss": "^6.10.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.7",
|
"@tailwindcss/forms": "^0.5.7",
|
||||||
"@tailwindcss/typography": "^0.5.10",
|
"@tailwindcss/typography": "^0.5.10",
|
||||||
"@vueuse/core": "^10.6.1",
|
"@vueuse/core": "^10.7.0",
|
||||||
"@vueuse/integrations": "^10.6.1",
|
"@vueuse/integrations": "^10.7.0",
|
||||||
"@vueuse/math": "^10.6.1",
|
"@vueuse/math": "^10.7.0",
|
||||||
"defu": "^6.1.3",
|
"defu": "^6.1.3",
|
||||||
"fuse.js": "^6.6.2",
|
"fuse.js": "^6.6.2",
|
||||||
"nuxt-icon": "^0.6.6",
|
"nuxt-icon": "^0.6.7",
|
||||||
"ohash": "^1.1.3",
|
"ohash": "^1.1.3",
|
||||||
"pathe": "^1.1.1",
|
"pathe": "^1.1.1",
|
||||||
"scule": "^1.1.0",
|
"scule": "^1.1.1",
|
||||||
"tailwind-merge": "^1.14.0",
|
"tailwind-merge": "^2.1.0",
|
||||||
"tailwindcss": "^3.3.5"
|
"tailwindcss": "^3.3.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@nuxt/eslint-config": "^0.2.0",
|
"@nuxt/eslint-config": "^0.2.0",
|
||||||
"@nuxt/module-builder": "^0.5.4",
|
"@nuxt/module-builder": "^0.5.4",
|
||||||
"@release-it/conventional-changelog": "^8.0.1",
|
"@release-it/conventional-changelog": "^8.0.1",
|
||||||
"@testing-library/vue": "^8.0.1",
|
"eslint": "^8.55.0",
|
||||||
"@vitejs/plugin-vue": "^4.5.0",
|
|
||||||
"eslint": "^8.54.0",
|
|
||||||
"happy-dom": "^12.10.3",
|
"happy-dom": "^12.10.3",
|
||||||
"joi": "^17.11.0",
|
"joi": "^17.11.0",
|
||||||
"nuxt": "^3.8.2",
|
"nuxt": "^3.8.2",
|
||||||
"nuxt-vitest": "^0.11.5",
|
"nuxt-vitest": "^0.11.5",
|
||||||
"release-it": "^17.0.0",
|
"release-it": "^17.0.0",
|
||||||
"typescript": "^5.3.2",
|
"typescript": "^5.3.3",
|
||||||
"unbuild": "^2.0.0",
|
"unbuild": "^2.0.0",
|
||||||
"valibot": "^0.21.0",
|
"valibot": "^0.21.0",
|
||||||
"vitest": "^0.33.0",
|
"vitest": "^0.33.0",
|
||||||
"vue-tsc": "^1.8.22",
|
"vue-tsc": "^1.8.25",
|
||||||
"yup": "^1.3.2",
|
"yup": "^1.3.2",
|
||||||
"zod": "^3.22.4"
|
"zod": "^3.22.4"
|
||||||
},
|
},
|
||||||
|
|||||||
6
playground/app.config.ts
Normal file
6
playground/app.config.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
export default defineAppConfig({
|
||||||
|
ui: {
|
||||||
|
primary: 'green',
|
||||||
|
gray: 'cool'
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
import module from '../src/module'
|
|
||||||
|
|
||||||
export default defineNuxtConfig({
|
export default defineNuxtConfig({
|
||||||
modules: [
|
modules: [
|
||||||
module
|
'../src/module'
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|||||||
6
playground/tailwind.config.ts
Normal file
6
playground/tailwind.config.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import type { Config } from 'tailwindcss'
|
||||||
|
|
||||||
|
export default <Partial<Config>>{
|
||||||
|
theme: {
|
||||||
|
}
|
||||||
|
}
|
||||||
4164
pnpm-lock.yaml
generated
4164
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -243,7 +243,7 @@ export const generateSafelist = (colors: string[], globalColors) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const customSafelistExtractor = (prefix, content: string, colors: string[], safelistColors: string[]) => {
|
export const customSafelistExtractor = (prefix, content: string, colors: string[], safelistColors: string[]) => {
|
||||||
const classes = []
|
const classes: string[] = []
|
||||||
const regex = /<([A-Za-z][A-Za-z0-9]*(?:-[A-Za-z][A-Za-z0-9]*)*)\s+(?![^>]*:color\b)[^>]*\bcolor=["']([^"']+)["'][^>]*>/gs
|
const regex = /<([A-Za-z][A-Za-z0-9]*(?:-[A-Za-z][A-Za-z0-9]*)*)\s+(?![^>]*:color\b)[^>]*\bcolor=["']([^"']+)["'][^>]*>/gs
|
||||||
|
|
||||||
const matches = content.matchAll(regex)
|
const matches = content.matchAll(regex)
|
||||||
|
|||||||
@@ -10,10 +10,15 @@ import type { DeepPartial, Strategy } from './runtime/types/utils'
|
|||||||
|
|
||||||
const defaultExtractor = createDefaultExtractor({ tailwindConfig: { separator: ':' } })
|
const defaultExtractor = createDefaultExtractor({ tailwindConfig: { separator: ':' } })
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
delete defaultColors.lightBlue
|
delete defaultColors.lightBlue
|
||||||
|
// @ts-ignore
|
||||||
delete defaultColors.warmGray
|
delete defaultColors.warmGray
|
||||||
|
// @ts-ignore
|
||||||
delete defaultColors.trueGray
|
delete defaultColors.trueGray
|
||||||
|
// @ts-ignore
|
||||||
delete defaultColors.coolGray
|
delete defaultColors.coolGray
|
||||||
|
// @ts-ignore
|
||||||
delete defaultColors.blueGray
|
delete defaultColors.blueGray
|
||||||
|
|
||||||
type UI = {
|
type UI = {
|
||||||
@@ -79,12 +84,15 @@ export default defineNuxtModule<ModuleOptions>({
|
|||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
nuxt.hook('tailwindcss:config', function (tailwindConfig) {
|
nuxt.hook('tailwindcss:config', function (tailwindConfig) {
|
||||||
|
tailwindConfig.theme = tailwindConfig.theme || {}
|
||||||
|
tailwindConfig.theme.extend = tailwindConfig.theme.extend || {}
|
||||||
|
tailwindConfig.theme.extend.colors = tailwindConfig.theme.extend.colors || {}
|
||||||
|
|
||||||
const globalColors: any = {
|
const globalColors: any = {
|
||||||
...(tailwindConfig.theme.colors || defaultColors),
|
...(tailwindConfig.theme.colors || defaultColors),
|
||||||
...tailwindConfig.theme.extend?.colors
|
...tailwindConfig.theme.extend?.colors
|
||||||
}
|
}
|
||||||
|
|
||||||
tailwindConfig.theme.extend.colors = tailwindConfig.theme.extend.colors || {}
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
globalColors.primary = tailwindConfig.theme.extend.colors.primary = {
|
globalColors.primary = tailwindConfig.theme.extend.colors.primary = {
|
||||||
50: 'rgb(var(--color-primary-50) / <alpha-value>)',
|
50: 'rgb(var(--color-primary-50) / <alpha-value>)',
|
||||||
@@ -132,7 +140,7 @@ export default defineNuxtModule<ModuleOptions>({
|
|||||||
}
|
}
|
||||||
|
|
||||||
tailwindConfig.safelist = tailwindConfig.safelist || []
|
tailwindConfig.safelist = tailwindConfig.safelist || []
|
||||||
tailwindConfig.safelist.push(...generateSafelist(options.safelistColors, colors))
|
tailwindConfig.safelist.push(...generateSafelist(options.safelistColors || [], colors))
|
||||||
|
|
||||||
tailwindConfig.plugins = tailwindConfig.plugins || []
|
tailwindConfig.plugins = tailwindConfig.plugins || []
|
||||||
tailwindConfig.plugins.push(iconsPlugin(Array.isArray(options.icons) || options.icons === 'all' ? { collections: getIconCollections(options.icons) } : typeof options.icons === 'object' ? options.icons as IconsPluginOptions : {}))
|
tailwindConfig.plugins.push(iconsPlugin(Array.isArray(options.icons) || options.icons === 'all' ? { collections: getIconCollections(options.icons) } : typeof options.icons === 'object' ? options.icons as IconsPluginOptions : {}))
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<slot :name="`${column.key}-header`" :column="column" :sort="sort" :on-sort="onSort">
|
<slot :name="`${column.key}-header`" :column="column" :sort="sort" :on-sort="onSort">
|
||||||
<UButton
|
<UButton
|
||||||
v-if="column.sortable"
|
v-if="column.sortable"
|
||||||
v-bind="{ ...ui.default.sortButton, ...sortButton }"
|
v-bind="{ ...(ui.default.sortButton || {}), ...sortButton }"
|
||||||
:icon="(!sort.column || sort.column !== column.key) ? (sortButton.icon || ui.default.sortButton.icon) : sort.direction === 'asc' ? sortAscIcon : sortDescIcon"
|
:icon="(!sort.column || sort.column !== column.key) ? (sortButton.icon || ui.default.sortButton.icon) : sort.direction === 'asc' ? sortAscIcon : sortDescIcon"
|
||||||
:label="column[columnAttribute]"
|
:label="column[columnAttribute]"
|
||||||
@click="onSort(column)"
|
@click="onSort(column)"
|
||||||
@@ -75,7 +75,7 @@ import UButton from '../elements/Button.vue'
|
|||||||
import UIcon from '../elements/Icon.vue'
|
import UIcon from '../elements/Icon.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, omit, get } from '../../utils'
|
import { mergeConfig, get } from '../../utils'
|
||||||
import type { Strategy, Button } from '../../types'
|
import type { Strategy, Button } from '../../types'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
@@ -104,7 +104,7 @@ export default defineComponent({
|
|||||||
default: () => defaultComparator
|
default: () => defaultComparator
|
||||||
},
|
},
|
||||||
rows: {
|
rows: {
|
||||||
type: Array as PropType<{ [key: string]: any, click?: Function }[]>,
|
type: Array as PropType<{ [key: string]: any }[]>,
|
||||||
default: () => []
|
default: () => []
|
||||||
},
|
},
|
||||||
columns: {
|
columns: {
|
||||||
@@ -145,18 +145,18 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
class: {
|
class: {
|
||||||
type: [String, Object, Array] as PropType<any>,
|
type: [String, Object, Array] as PropType<any>,
|
||||||
default: undefined
|
default: () => ''
|
||||||
},
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>,
|
type: Object as PropType<Partial<typeof config> & { strategy?: Strategy }>,
|
||||||
default: undefined
|
default: () => ({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: ['update:modelValue', 'update:sort'],
|
emits: ['update:modelValue', 'update:sort'],
|
||||||
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(omit(props.rows[0] ?? {}, ['click'])).map((key) => ({ key, label: upperFirst(key), sortable: false })))
|
const columns = computed(() => props.columns ?? Object.keys(props.rows[0] ?? {}).map((key) => ({ key, label: upperFirst(key), sortable: false })))
|
||||||
|
|
||||||
const sort = ref(defu({}, props.sort, { column: null, direction: 'asc' }))
|
const sort = ref(defu({}, props.sort, { column: null, direction: 'asc' }))
|
||||||
|
|
||||||
|
|||||||
@@ -93,17 +93,17 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
class: {
|
class: {
|
||||||
type: [String, Object, Array] as PropType<any>,
|
type: [String, Object, Array] as PropType<any>,
|
||||||
default: undefined
|
default: () => ''
|
||||||
},
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>,
|
type: Object as PropType<Partial<typeof config> & { strategy?: Strategy }>,
|
||||||
default: undefined
|
default: () => ({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup (props) {
|
setup (props) {
|
||||||
const { ui, attrs } = useUI('accordion', toRef(props, 'ui'), config, toRef(props, 'class'))
|
const { ui, attrs } = useUI('accordion', toRef(props, 'ui'), config, toRef(props, 'class'))
|
||||||
|
|
||||||
const uiButton = computed<Partial<typeof configButton>>(() => configButton)
|
const uiButton = computed<typeof configButton>(() => configButton)
|
||||||
|
|
||||||
const buttonRefs = ref<{ open: boolean, close: (e: EventTarget) => {} }[]>([])
|
const buttonRefs = ref<{ open: boolean, close: (e: EventTarget) => {} }[]>([])
|
||||||
|
|
||||||
@@ -114,7 +114,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
buttonRefs.value.forEach((button) => {
|
buttonRefs.value.forEach((button) => {
|
||||||
if (button.open) {
|
if (button.open) {
|
||||||
button.close(e.target)
|
button.close(e.target as EventTarget)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<UIcon v-if="icon" :name="icon" :class="ui.icon.base" />
|
<UIcon v-if="icon" :name="icon" :class="ui.icon.base" />
|
||||||
<UAvatar v-if="avatar" v-bind="{ size: ui.avatar.size, ...avatar }" :class="ui.avatar.base" />
|
<UAvatar v-if="avatar" v-bind="{ size: ui.avatar.size, ...avatar }" :class="ui.avatar.base" />
|
||||||
|
|
||||||
<div class="w-0 flex-1">
|
<div :class="ui.inner">
|
||||||
<p :class="ui.title">
|
<p :class="ui.title">
|
||||||
<slot name="title" :title="title">
|
<slot name="title" :title="title">
|
||||||
{{ title }}
|
{{ title }}
|
||||||
@@ -17,15 +17,15 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div v-if="(description || $slots.description) && actions.length" :class="ui.actions">
|
<div v-if="(description || $slots.description) && actions.length" :class="ui.actions">
|
||||||
<UButton v-for="(action, index) of actions" :key="index" v-bind="{ ...ui.default.actionButton, ...action }" @click.stop="action.click" />
|
<UButton v-for="(action, index) of actions" :key="index" v-bind="{ ...(ui.default.actionButton || {}), ...action }" @click.stop="action.click" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="closeButton || (!description && !$slots.description && actions.length)" :class="twMerge(ui.actions, 'mt-0')">
|
<div v-if="closeButton || (!description && !$slots.description && actions.length)" :class="twMerge(ui.actions, 'mt-0')">
|
||||||
<template v-if="!description && !$slots.description && actions.length">
|
<template v-if="!description && !$slots.description && actions.length">
|
||||||
<UButton v-for="(action, index) of actions" :key="index" v-bind="{ ...ui.default.actionButton, ...action }" @click.stop="action.click" />
|
<UButton v-for="(action, index) of actions" :key="index" v-bind="{ ...(ui.default.actionButton || {}), ...action }" @click.stop="action.click" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<UButton v-if="closeButton" aria-label="Close" v-bind="{ ...ui.default.closeButton, ...closeButton }" @click.stop="$emit('close')" />
|
<UButton v-if="closeButton" aria-label="Close" v-bind="{ ...(ui.default.closeButton || {}), ...closeButton }" @click.stop="$emit('close')" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -34,13 +34,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, Strategy } from '../../types'
|
import type { Avatar, Button, AlertColor, AlertVariant, Strategy } from '../../types'
|
||||||
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'
|
||||||
@@ -73,7 +73,7 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
closeButton: {
|
closeButton: {
|
||||||
type: Object as PropType<Button>,
|
type: Object as PropType<Button>,
|
||||||
default: () => config.default.closeButton as Button
|
default: () => config.default.closeButton as unknown as Button
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
type: Array as PropType<(Button & { click?: Function })[]>,
|
type: Array as PropType<(Button & { click?: Function })[]>,
|
||||||
@@ -98,11 +98,11 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
class: {
|
class: {
|
||||||
type: [String, Object, Array] as PropType<any>,
|
type: [String, Object, Array] as PropType<any>,
|
||||||
default: undefined
|
default: () => ''
|
||||||
},
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>,
|
type: Object as PropType<Partial<typeof config> & { strategy?: Strategy }>,
|
||||||
default: undefined
|
default: () => ({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: ['close'],
|
emits: ['close'],
|
||||||
|
|||||||
@@ -22,10 +22,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 } from '../../types'
|
import type { AvatarSize, AvatarChipColor, AvatarChipPosition, Strategy } from '../../types'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
@@ -86,11 +86,11 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
class: {
|
class: {
|
||||||
type: [String, Object, Array] as PropType<any>,
|
type: [String, Object, Array] as PropType<any>,
|
||||||
default: undefined
|
default: () => ''
|
||||||
},
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>,
|
type: Object as PropType<Partial<typeof config> & { strategy?: Strategy }>,
|
||||||
default: undefined
|
default: () => ({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup (props) {
|
setup (props) {
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
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 UAvatar from './Avatar.vue'
|
import UAvatar from './Avatar.vue'
|
||||||
import { useUI } from '../../composables/useUI'
|
import { useUI } from '../../composables/useUI'
|
||||||
import { mergeConfig, getSlotsChildren } from '../../utils'
|
import { mergeConfig, twMerge, getSlotsChildren } from '../../utils'
|
||||||
import type { AvatarSize, Strategy } from '../../types'
|
import type { AvatarSize, Strategy } from '../../types'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
@@ -29,11 +29,11 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
class: {
|
class: {
|
||||||
type: [String, Object, Array] as PropType<any>,
|
type: [String, Object, Array] as PropType<any>,
|
||||||
default: undefined
|
default: () => ''
|
||||||
},
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof avatarGroupConfig & { strategy?: Strategy }>>,
|
type: Object as PropType<Partial<typeof avatarGroupConfig> & { strategy?: Strategy }>,
|
||||||
default: undefined
|
default: () => ({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup (props, { slots }) {
|
setup (props, { slots }) {
|
||||||
|
|||||||
@@ -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 { useInjectButtonGroup } from '../../composables/useButtonGroup'
|
import { useInjectButtonGroup } from '../../composables/useButtonGroup'
|
||||||
import type { BadgeColor, BadgeSize, BadgeVariant, Strategy } from '../../types'
|
import type { BadgeColor, BadgeSize, BadgeVariant, Strategy } from '../../types'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
@@ -51,11 +51,11 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
class: {
|
class: {
|
||||||
type: [String, Object, Array] as PropType<any>,
|
type: [String, Object, Array] as PropType<any>,
|
||||||
default: undefined
|
default: () => ''
|
||||||
},
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>,
|
type: Object as PropType<Partial<typeof config> & { strategy?: Strategy }>,
|
||||||
default: undefined
|
default: () => ({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup (props) {
|
setup (props) {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
</slot>
|
</slot>
|
||||||
|
|
||||||
<slot>
|
<slot>
|
||||||
<span v-if="label" :class="[truncate ? 'text-left break-all line-clamp-1' : '']">
|
<span v-if="label" :class="[truncate ? ui.truncate : '']">
|
||||||
{{ label }}
|
{{ label }}
|
||||||
</span>
|
</span>
|
||||||
</slot>
|
</slot>
|
||||||
@@ -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 } from '../../utils'
|
import { mergeConfig, twMerge } from '../../utils'
|
||||||
import { useInjectButtonGroup } from '../../composables/useButtonGroup'
|
import { useInjectButtonGroup } from '../../composables/useButtonGroup'
|
||||||
import type { ButtonColor, ButtonSize, ButtonVariant, Strategy } from '../../types'
|
import type { ButtonColor, ButtonSize, ButtonVariant, Strategy } from '../../types'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
@@ -121,11 +121,11 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
class: {
|
class: {
|
||||||
type: [String, Object, Array] as PropType<any>,
|
type: [String, Object, Array] as PropType<any>,
|
||||||
default: undefined
|
default: () => ''
|
||||||
},
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>,
|
type: Object as PropType<Partial<typeof config> & { strategy?: Strategy }>,
|
||||||
default: undefined
|
default: () => ({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup (props, { slots }) {
|
setup (props, { slots }) {
|
||||||
@@ -154,7 +154,7 @@ export default defineComponent({
|
|||||||
ui.value.gap[size.value],
|
ui.value.gap[size.value],
|
||||||
props.padded && ui.value[isSquare.value ? 'square' : 'padding'][size.value],
|
props.padded && ui.value[isSquare.value ? 'square' : 'padding'][size.value],
|
||||||
variant?.replaceAll('{color}', props.color),
|
variant?.replaceAll('{color}', props.color),
|
||||||
props.block ? 'w-full flex justify-center items-center' : 'inline-flex items-center'
|
props.block ? ui.value.block : ui.value.inline
|
||||||
), props.class)
|
), props.class)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -178,7 +178,7 @@ export default defineComponent({
|
|||||||
return twJoin(
|
return twJoin(
|
||||||
ui.value.icon.base,
|
ui.value.icon.base,
|
||||||
ui.value.icon.size[size.value],
|
ui.value.icon.size[size.value],
|
||||||
props.loading && 'animate-spin'
|
props.loading && ui.value.icon.loading
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -186,11 +186,13 @@ export default defineComponent({
|
|||||||
return twJoin(
|
return twJoin(
|
||||||
ui.value.icon.base,
|
ui.value.icon.base,
|
||||||
ui.value.icon.size[size.value],
|
ui.value.icon.size[size.value],
|
||||||
props.loading && !isLeading.value && 'animate-spin'
|
props.loading && !isLeading.value && ui.value.icon.loading
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
|
ui,
|
||||||
attrs,
|
attrs,
|
||||||
isLeading,
|
isLeading,
|
||||||
isTrailing,
|
isTrailing,
|
||||||
|
|||||||
@@ -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 { mergeConfig, twMerge, getSlotsChildren } from '../../utils'
|
||||||
import { useProvideButtonGroup } from '../../composables/useButtonGroup'
|
import { useProvideButtonGroup } from '../../composables/useButtonGroup'
|
||||||
import type { ButtonSize, Strategy } from '../../types'
|
import type { ButtonSize, Strategy } from '../../types'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
@@ -32,11 +32,11 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
class: {
|
class: {
|
||||||
type: [String, Object, Array] as PropType<any>,
|
type: [String, Object, Array] as PropType<any>,
|
||||||
default: undefined
|
default: () => ''
|
||||||
},
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof buttonGroupConfig & { strategy?: Strategy }>>,
|
type: Object as PropType<Partial<typeof buttonGroupConfig> & { strategy?: Strategy }>,
|
||||||
default: undefined
|
default: () => ({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup (props, { slots }) {
|
setup (props, { slots }) {
|
||||||
@@ -52,20 +52,7 @@ export default defineComponent({
|
|||||||
), props.class)
|
), props.class)
|
||||||
})
|
})
|
||||||
|
|
||||||
const rounded = computed(() => {
|
const rounded = computed(() => ui.value.orientation[ui.value.rounded][props.orientation])
|
||||||
const roundedMap = {
|
|
||||||
'rounded-none': { horizontal: { start: 'rounded-s-none', end: 'rounded-e-none' }, vertical: { start: 'rounded-t-none', end: 'rounded-b-none' } },
|
|
||||||
'rounded-sm': { horizontal: { start: 'rounded-s-sm', end: 'rounded-e-sm' }, vertical: { start: 'rounded-t-sm', end: 'rounded-b-sm' } },
|
|
||||||
rounded: { horizontal: { start: 'rounded-s', end: 'rounded-e' }, vertical: { start: 'rounded-t', end: 'rounded-b' } },
|
|
||||||
'rounded-md': { horizontal: { start: 'rounded-s-md', end: 'rounded-e-md' }, vertical: { start: 'rounded-t-md', end: 'rounded-b-md' } },
|
|
||||||
'rounded-lg': { horizontal: { start: 'rounded-s-lg', end: 'rounded-e-lg' }, vertical: { start: 'rounded-t-lg', end: 'rounded-b-lg' } },
|
|
||||||
'rounded-xl': { horizontal: { start: 'rounded-s-xl', end: 'rounded-e-xl' }, vertical: { start: 'rounded-t-xl', end: 'rounded-b-xl' } },
|
|
||||||
'rounded-2xl': { horizontal: { start: 'rounded-s-2xl', end: 'rounded-e-2xl' }, vertical: { start: 'rounded-t-2xl', end: 'rounded-b-2xl' } },
|
|
||||||
'rounded-3xl': { horizontal: { start: 'rounded-s-3xl', end: 'rounded-e-3xl' }, vertical: { start: 'rounded-t-3xl', end: 'rounded-b-3xl' } },
|
|
||||||
'rounded-full': { horizontal: { start: 'rounded-s-full', end: 'rounded-e-full' }, vertical: { start: 'rounded-t-full', end: 'rounded-b-full' } }
|
|
||||||
}
|
|
||||||
return roundedMap[ui.value.rounded][props.orientation]
|
|
||||||
})
|
|
||||||
|
|
||||||
useProvideButtonGroup({ orientation: toRef(props, 'orientation'), size: toRef(props, 'size'), ui, rounded })
|
useProvideButtonGroup({ orientation: toRef(props, 'orientation'), size: toRef(props, 'size'), ui, rounded })
|
||||||
|
|
||||||
|
|||||||
@@ -61,11 +61,11 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
class: {
|
class: {
|
||||||
type: [String, Object, Array] as PropType<any>,
|
type: [String, Object, Array] as PropType<any>,
|
||||||
default: undefined
|
default: () => ''
|
||||||
},
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>,
|
type: Object as PropType<Partial<typeof config> & { strategy?: Strategy }>,
|
||||||
default: undefined
|
default: () => ({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup (props) {
|
setup (props) {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
ref="trigger"
|
ref="trigger"
|
||||||
as="div"
|
as="div"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
class="inline-flex w-full"
|
:class="ui.trigger"
|
||||||
role="button"
|
role="button"
|
||||||
@mouseover="onMouseOver"
|
@mouseover="onMouseOver"
|
||||||
>
|
>
|
||||||
@@ -18,10 +18,10 @@
|
|||||||
<div v-if="open && items.length" ref="container" :class="[ui.container, ui.width]" :style="containerStyle" @mouseover="onMouseOver">
|
<div v-if="open && items.length" ref="container" :class="[ui.container, ui.width]" :style="containerStyle" @mouseover="onMouseOver">
|
||||||
<Transition appear v-bind="ui.transition">
|
<Transition appear v-bind="ui.transition">
|
||||||
<div>
|
<div>
|
||||||
<div v-if="popper.arrow" data-popper-arrow :class="['invisible before:visible before:block before:rotate-45 before:z-[-1]', Object.values(ui.arrow)]" />
|
<div v-if="popper.arrow" data-popper-arrow :class="Object.values(ui.arrow)" />
|
||||||
<HMenuItems :class="[ui.base, ui.divide, ui.ring, ui.rounded, ui.shadow, ui.background, ui.height]" static>
|
<HMenuItems :class="[ui.base, ui.divide, ui.ring, ui.rounded, ui.shadow, ui.background, ui.height]" static>
|
||||||
<div v-for="(subItems, index) of items" :key="index" :class="ui.padding">
|
<div v-for="(subItems, index) of items" :key="index" :class="ui.padding">
|
||||||
<NuxtLink v-for="(item, subIndex) of subItems" :key="subIndex" v-slot="{ href, target, rel, navigate, isExternal }" v-bind="omit(item, ['label', 'slot', 'icon', 'iconClass', 'avatar', 'shortcuts', 'disabled', 'click'])" custom>
|
<NuxtLink v-for="(item, subIndex) of subItems" :key="subIndex" v-slot="{ href, target, rel, navigate, isExternal }" v-bind="omit(item, ['label', 'labelClass', 'slot', 'icon', 'iconClass', 'avatar', 'shortcuts', 'disabled', 'click'])" custom>
|
||||||
<HMenuItem v-slot="{ active, disabled: itemDisabled, close }" :disabled="item.disabled">
|
<HMenuItem v-slot="{ active, disabled: itemDisabled, close }" :disabled="item.disabled">
|
||||||
<component
|
<component
|
||||||
:is="!!href ? 'a' : 'button'"
|
:is="!!href ? 'a' : 'button'"
|
||||||
@@ -32,10 +32,10 @@
|
|||||||
@click="onClick($event, item, { href, navigate, close, isExternal })"
|
@click="onClick($event, item, { href, navigate, close, isExternal })"
|
||||||
>
|
>
|
||||||
<slot :name="item.slot || 'item'" :item="item">
|
<slot :name="item.slot || 'item'" :item="item">
|
||||||
<UIcon v-if="item.icon" :name="item.icon" :class="[ui.item.icon.base, active ? ui.item.icon.active : ui.item.icon.inactive, item.iconClass]" />
|
<UIcon v-if="item.icon" :name="item.icon" :class="twMerge(twJoin(ui.item.icon.base, active ? ui.item.icon.active : ui.item.icon.inactive), item.iconClass)" />
|
||||||
<UAvatar v-else-if="item.avatar" v-bind="{ size: ui.item.avatar.size, ...item.avatar }" :class="ui.item.avatar.base" />
|
<UAvatar v-else-if="item.avatar" v-bind="{ size: ui.item.avatar.size, ...item.avatar }" :class="ui.item.avatar.base" />
|
||||||
|
|
||||||
<span class="truncate">{{ item.label }}</span>
|
<span :class="twMerge(ui.item.label, item.labelClass)">{{ item.label }}</span>
|
||||||
|
|
||||||
<span v-if="item.shortcuts?.length" :class="ui.item.shortcuts">
|
<span v-if="item.shortcuts?.length" :class="ui.item.shortcuts">
|
||||||
<UKbd v-for="shortcut of item.shortcuts" :key="shortcut">{{ shortcut }}</UKbd>
|
<UKbd v-for="shortcut of item.shortcuts" :key="shortcut">{{ shortcut }}</UKbd>
|
||||||
@@ -57,12 +57,13 @@ import { defineComponent, ref, computed, toRef, onMounted, resolveComponent } fr
|
|||||||
import type { PropType } from 'vue'
|
import type { PropType } from 'vue'
|
||||||
import { Menu as HMenu, MenuButton as HMenuButton, MenuItems as HMenuItems, MenuItem as HMenuItem } from '@headlessui/vue'
|
import { Menu as HMenu, MenuButton as HMenuButton, MenuItems as HMenuItems, MenuItem as HMenuItem } from '@headlessui/vue'
|
||||||
import { defu } from 'defu'
|
import { defu } from 'defu'
|
||||||
|
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, omit } from '../../utils'
|
import { mergeConfig, twMerge, omit } from '../../utils'
|
||||||
import type { DropdownItem, PopperOptions, Strategy } from '../../types'
|
import type { DropdownItem, PopperOptions, Strategy } from '../../types'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
@@ -109,11 +110,11 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
class: {
|
class: {
|
||||||
type: [String, Object, Array] as PropType<any>,
|
type: [String, Object, Array] as PropType<any>,
|
||||||
default: undefined
|
default: () => ''
|
||||||
},
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>,
|
type: Object as PropType<Partial<typeof config> & { strategy?: Strategy }>,
|
||||||
default: undefined
|
default: () => ({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup (props) {
|
setup (props) {
|
||||||
@@ -214,6 +215,8 @@ export default defineComponent({
|
|||||||
onMouseLeave,
|
onMouseLeave,
|
||||||
onClick,
|
onClick,
|
||||||
omit,
|
omit,
|
||||||
|
twMerge,
|
||||||
|
twJoin,
|
||||||
NuxtLink
|
NuxtLink
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 { KbdSize, Strategy } from '../../types'
|
import type { KbdSize, Strategy } from '../../types'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
@@ -33,11 +33,11 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
class: {
|
class: {
|
||||||
type: [String, Object, Array] as PropType<any>,
|
type: [String, Object, Array] as PropType<any>,
|
||||||
default: undefined
|
default: () => ''
|
||||||
},
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>,
|
type: Object as PropType<Partial<typeof config> & { strategy?: Strategy }>,
|
||||||
default: undefined
|
default: () => ({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup (props) {
|
setup (props) {
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
:role="disabled ? 'link' : undefined"
|
:role="disabled ? 'link' : undefined"
|
||||||
:rel="rel"
|
:rel="rel"
|
||||||
:target="target"
|
:target="target"
|
||||||
:class="active ? activeClass : resolveLinkClass(route, $route, { isActive, isExactActive })"
|
:class="active !== undefined ? (active ? activeClass : inactiveClass) : resolveLinkClass(route, $route, { isActive, isExactActive })"
|
||||||
@click="(e) => !isExternal && navigate(e)"
|
@click="(e) => !isExternal && navigate(e)"
|
||||||
>
|
>
|
||||||
<slot v-bind="{ isActive: exact ? isExactActive : isActive }" />
|
<slot v-bind="{ isActive: exact ? isExactActive : isActive }" />
|
||||||
@@ -48,7 +48,7 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
active: {
|
active: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: undefined
|
||||||
},
|
},
|
||||||
exact: {
|
exact: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
|
|||||||
@@ -87,11 +87,11 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
class: {
|
class: {
|
||||||
type: [String, Object, Array] as PropType<any>,
|
type: [String, Object, Array] as PropType<any>,
|
||||||
default: undefined
|
default: () => ''
|
||||||
},
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>,
|
type: Object as PropType<Partial<typeof config> & { strategy?: Strategy }>,
|
||||||
default: undefined
|
default: () => ({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup (props) {
|
setup (props) {
|
||||||
|
|||||||
@@ -44,15 +44,15 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
icon: {
|
icon: {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'i-heroicons-minus'
|
default: () => meterGroupConfig.default.icon
|
||||||
},
|
},
|
||||||
class: {
|
class: {
|
||||||
type: [String, Object, Array] as PropType<any>,
|
type: [String, Object, Array] as PropType<any>,
|
||||||
default: undefined
|
default: () => ''
|
||||||
},
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof meterGroupConfig & { strategy?: Strategy }>>,
|
type: Object as PropType<Partial<typeof meterGroupConfig> & { strategy?: Strategy }>,
|
||||||
default: undefined
|
default: () => ({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup (props, { slots }) {
|
setup (props, { slots }) {
|
||||||
@@ -70,21 +70,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
const children = computed(() => getSlotsChildren(slots))
|
const children = computed(() => getSlotsChildren(slots))
|
||||||
|
|
||||||
const rounded = computed(() => {
|
const rounded = computed(() => ui.value.orientation[ui.value.rounded])
|
||||||
const roundedMap = {
|
|
||||||
'rounded-none': { left: 'rounded-s-none', right: 'rounded-e-none' },
|
|
||||||
'rounded-sm': { left: 'rounded-s-sm', right: 'rounded-e-sm' },
|
|
||||||
rounded: { left: 'rounded-s', right: 'rounded-e' },
|
|
||||||
'rounded-md': { left: 'rounded-s-md', right: 'rounded-e-md' },
|
|
||||||
'rounded-lg': { left: 'rounded-s-lg', right: 'rounded-e-lg' },
|
|
||||||
'rounded-xl': { left: 'rounded-s-xl', right: 'rounded-e-xl' },
|
|
||||||
'rounded-2xl': { left: 'rounded-s-2xl', right: 'rounded-e-2xl' },
|
|
||||||
'rounded-3xl': { left: 'rounded-s-3xl', right: 'rounded-e-3xl' },
|
|
||||||
'rounded-full': { left: 'rounded-s-full', right: 'rounded-e-full' }
|
|
||||||
}
|
|
||||||
|
|
||||||
return roundedMap[ui.value.rounded]
|
|
||||||
})
|
|
||||||
|
|
||||||
function clampPercent (value: number, min: number, max: number): number {
|
function clampPercent (value: number, min: number, max: number): number {
|
||||||
if (min == max) {
|
if (min == max) {
|
||||||
@@ -128,7 +114,7 @@ export default defineComponent({
|
|||||||
vProps.ui.wrapper = node.props?.ui?.wrapper || ''
|
vProps.ui.wrapper = node.props?.ui?.wrapper || ''
|
||||||
vProps.ui.wrapper += [
|
vProps.ui.wrapper += [
|
||||||
node.props?.ui?.wrapper,
|
node.props?.ui?.wrapper,
|
||||||
props.ui?.meter?.background || ui.value.background,
|
ui.value.background,
|
||||||
ui.value.transition
|
ui.value.transition
|
||||||
].filter(Boolean).join(' ')
|
].filter(Boolean).join(' ')
|
||||||
|
|
||||||
@@ -153,8 +139,8 @@ export default defineComponent({
|
|||||||
|
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
delete(clone.children?.label)
|
delete(clone.children?.label)
|
||||||
delete(clone.props.indicator)
|
delete(clone.props?.indicator)
|
||||||
delete(clone.props.label)
|
delete(clone.props?.label)
|
||||||
|
|
||||||
return clone
|
return clone
|
||||||
}))
|
}))
|
||||||
@@ -198,7 +184,7 @@ export default defineComponent({
|
|||||||
vNodeSlots[0] = slots.indicator({ percent: percent.value })
|
vNodeSlots[0] = slots.indicator({ percent: percent.value })
|
||||||
}
|
}
|
||||||
|
|
||||||
vNodeSlots[2] = h('ol', { class: 'list-disc list-inside' }, labels.value.map((label, key) => {
|
vNodeSlots[2] = h('ol', { class: ui.value.list }, labels.value.map((label, key) => {
|
||||||
const labelClass = computed(() => {
|
const labelClass = computed(() => {
|
||||||
return twJoin(
|
return twJoin(
|
||||||
uiMeter.value.label.base,
|
uiMeter.value.label.base,
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ export default defineComponent({
|
|||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
props: {
|
props: {
|
||||||
value: {
|
value: {
|
||||||
type: [Number, null, undefined],
|
type: Number,
|
||||||
default: null
|
default: null
|
||||||
},
|
},
|
||||||
max: {
|
max: {
|
||||||
@@ -73,11 +73,11 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
class: {
|
class: {
|
||||||
type: [String, Object, Array] as PropType<any>,
|
type: [String, Object, Array] as PropType<any>,
|
||||||
default: undefined
|
default: () => ''
|
||||||
},
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>,
|
type: Object as PropType<Partial<typeof config> & { strategy?: Strategy }>,
|
||||||
default: undefined
|
default: () => ({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup (props) {
|
setup (props) {
|
||||||
@@ -173,7 +173,7 @@ export default defineComponent({
|
|||||||
return classes.join(' ')
|
return classes.join(' ')
|
||||||
}
|
}
|
||||||
|
|
||||||
const isIndeterminate = computed(() => [undefined, null].includes(props.value))
|
const isIndeterminate = computed(() => props.value === undefined || props.value === null)
|
||||||
const isSteps = computed(() => Array.isArray(props.max))
|
const isSteps = computed(() => Array.isArray(props.max))
|
||||||
|
|
||||||
const realMax = computed(() => {
|
const realMax = computed(() => {
|
||||||
@@ -191,8 +191,8 @@ export default defineComponent({
|
|||||||
const percent = computed(() => {
|
const percent = computed(() => {
|
||||||
switch (true) {
|
switch (true) {
|
||||||
case props.value < 0: return 0
|
case props.value < 0: return 0
|
||||||
case props.value > realMax.value: return 100
|
case props.value > (realMax.value as number): return 100
|
||||||
default: return (props.value / realMax.value) * 100
|
default: return (props.value / (realMax.value as number)) * 100
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :class="ui.wrapper">
|
<div :class="ui.wrapper">
|
||||||
<div class="flex items-center h-5">
|
<div :class="ui.container">
|
||||||
<input
|
<input
|
||||||
:id="inputId"
|
:id="inputId"
|
||||||
v-model="toggle"
|
v-model="toggle"
|
||||||
@@ -11,13 +11,12 @@
|
|||||||
:checked="checked"
|
:checked="checked"
|
||||||
:indeterminate="indeterminate"
|
:indeterminate="indeterminate"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
class="form-checkbox"
|
|
||||||
:class="inputClass"
|
:class="inputClass"
|
||||||
v-bind="attrs"
|
v-bind="attrs"
|
||||||
@change="onChange"
|
@change="onChange"
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="label || $slots.label" class="ms-3 flex flex-col">
|
<div v-if="label || $slots.label" :class="ui.inner">
|
||||||
<label :for="inputId" :class="ui.label">
|
<label :for="inputId" :class="ui.label">
|
||||||
<slot name="label">{{ label }}</slot>
|
<slot name="label">{{ label }}</slot>
|
||||||
<span v-if="required" :class="ui.required">*</span>
|
<span v-if="required" :class="ui.required">*</span>
|
||||||
@@ -32,10 +31,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 { Strategy } from '../../types'
|
import type { Strategy } from '../../types'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
@@ -100,11 +99,11 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
class: {
|
class: {
|
||||||
type: [String, Object, Array] as PropType<any>,
|
type: [String, Object, Array] as PropType<any>,
|
||||||
default: undefined
|
default: () => ''
|
||||||
},
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>,
|
type: Object as PropType<Partial<typeof config> & { strategy?: Strategy }>,
|
||||||
default: undefined
|
default: () => ({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: ['update:modelValue', 'change'],
|
emits: ['update:modelValue', 'change'],
|
||||||
@@ -130,11 +129,12 @@ export default defineComponent({
|
|||||||
const inputClass = computed(() => {
|
const inputClass = computed(() => {
|
||||||
return twMerge(twJoin(
|
return twMerge(twJoin(
|
||||||
ui.value.base,
|
ui.value.base,
|
||||||
|
ui.value.form,
|
||||||
ui.value.rounded,
|
ui.value.rounded,
|
||||||
ui.value.background,
|
ui.value.background,
|
||||||
ui.value.border,
|
ui.value.border,
|
||||||
ui.value.ring.replaceAll('{color}', color.value),
|
color.value && ui.value.ring.replaceAll('{color}', color.value),
|
||||||
ui.value.color.replaceAll('{color}', color.value)
|
color.value && ui.value.color.replaceAll('{color}', color.value)
|
||||||
), props.inputClass)
|
), props.inputClass)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -247,7 +247,7 @@ async function getValibotError (
|
|||||||
const result = await schema._parse(state)
|
const result = await schema._parse(state)
|
||||||
if (result.issues) {
|
if (result.issues) {
|
||||||
return result.issues.map((issue) => ({
|
return result.issues.map((issue) => ({
|
||||||
path: issue.path.map(p => p.key).join('.'),
|
path: issue.path?.map(p => p.key).join('.') || '',
|
||||||
message: issue.message
|
message: issue.message
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,11 +89,11 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
class: {
|
class: {
|
||||||
type: [String, Object, Array] as PropType<any>,
|
type: [String, Object, Array] as PropType<any>,
|
||||||
default: undefined
|
default: () => ''
|
||||||
},
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>,
|
type: Object as PropType<Partial<typeof config> & { strategy?: Strategy }>,
|
||||||
default: undefined
|
default: () => ({})
|
||||||
},
|
},
|
||||||
eagerValidation: {
|
eagerValidation: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
:required="required"
|
:required="required"
|
||||||
:placeholder="placeholder"
|
:placeholder="placeholder"
|
||||||
:disabled="disabled || loading"
|
:disabled="disabled || loading"
|
||||||
class="form-input"
|
|
||||||
:class="inputClass"
|
:class="inputClass"
|
||||||
v-bind="attrs"
|
v-bind="attrs"
|
||||||
@input="onInput"
|
@input="onInput"
|
||||||
@@ -35,12 +34,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 UIcon from '../elements/Icon.vue'
|
import UIcon from '../elements/Icon.vue'
|
||||||
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 { mergeConfig, twMerge, looseToNumber } from '../../utils'
|
||||||
import { useInjectButtonGroup } from '../../composables/useButtonGroup'
|
import { useInjectButtonGroup } from '../../composables/useButtonGroup'
|
||||||
import type { InputSize, InputColor, InputVariant, Strategy } from '../../types'
|
import type { InputSize, InputColor, InputVariant, Strategy } from '../../types'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
@@ -153,11 +152,11 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
class: {
|
class: {
|
||||||
type: [String, Object, Array] as PropType<any>,
|
type: [String, Object, Array] as PropType<any>,
|
||||||
default: undefined
|
default: () => ''
|
||||||
},
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>,
|
type: Object as PropType<Partial<typeof config> & { strategy?: Strategy }>,
|
||||||
default: undefined
|
default: () => ({})
|
||||||
},
|
},
|
||||||
modelModifiers: {
|
modelModifiers: {
|
||||||
type: Object as PropType<{ trim?: boolean, lazy?: boolean, number?: boolean }>,
|
type: Object as PropType<{ trim?: boolean, lazy?: boolean, number?: boolean }>,
|
||||||
@@ -199,13 +198,13 @@ export default defineComponent({
|
|||||||
emitFormInput()
|
emitFormInput()
|
||||||
}
|
}
|
||||||
|
|
||||||
const onInput = (event: InputEvent) => {
|
const onInput = (event: Event) => {
|
||||||
if (!modelModifiers.value.lazy) {
|
if (!modelModifiers.value.lazy) {
|
||||||
updateInput((event.target as HTMLInputElement).value)
|
updateInput((event.target as HTMLInputElement).value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const onChange = (event: InputEvent) => {
|
const onChange = (event: Event) => {
|
||||||
const value = (event.target as HTMLInputElement).value
|
const value = (event.target as HTMLInputElement).value
|
||||||
|
|
||||||
if (modelModifiers.value.lazy) {
|
if (modelModifiers.value.lazy) {
|
||||||
@@ -234,6 +233,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
return twMerge(twJoin(
|
return twMerge(twJoin(
|
||||||
ui.value.base,
|
ui.value.base,
|
||||||
|
ui.value.form,
|
||||||
rounded.value,
|
rounded.value,
|
||||||
ui.value.placeholder,
|
ui.value.placeholder,
|
||||||
ui.value.size[size.value],
|
ui.value.size[size.value],
|
||||||
@@ -279,9 +279,9 @@ export default defineComponent({
|
|||||||
const leadingIconClass = computed(() => {
|
const leadingIconClass = computed(() => {
|
||||||
return twJoin(
|
return twJoin(
|
||||||
ui.value.icon.base,
|
ui.value.icon.base,
|
||||||
appConfig.ui.colors.includes(color.value) && ui.value.icon.color.replaceAll('{color}', color.value),
|
color.value && appConfig.ui.colors.includes(color.value) && ui.value.icon.color.replaceAll('{color}', color.value),
|
||||||
ui.value.icon.size[size.value],
|
ui.value.icon.size[size.value],
|
||||||
props.loading && 'animate-spin'
|
props.loading && ui.value.icon.loading
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -296,9 +296,9 @@ export default defineComponent({
|
|||||||
const trailingIconClass = computed(() => {
|
const trailingIconClass = computed(() => {
|
||||||
return twJoin(
|
return twJoin(
|
||||||
ui.value.icon.base,
|
ui.value.icon.base,
|
||||||
appConfig.ui.colors.includes(color.value) && ui.value.icon.color.replaceAll('{color}', color.value),
|
color.value && appConfig.ui.colors.includes(color.value) && ui.value.icon.color.replaceAll('{color}', color.value),
|
||||||
ui.value.icon.size[size.value],
|
ui.value.icon.size[size.value],
|
||||||
props.loading && !isLeading.value && 'animate-spin'
|
props.loading && !isLeading.value && ui.value.icon.loading
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
411
src/runtime/components/forms/InputMenu.vue
Normal file
411
src/runtime/components/forms/InputMenu.vue
Normal file
@@ -0,0 +1,411 @@
|
|||||||
|
<template>
|
||||||
|
<HCombobox
|
||||||
|
v-slot="{ open }"
|
||||||
|
:by="by"
|
||||||
|
:name="name"
|
||||||
|
:model-value="modelValue"
|
||||||
|
:disabled="disabled || loading"
|
||||||
|
as="div"
|
||||||
|
:class="ui.wrapper"
|
||||||
|
@update:model-value="onUpdate"
|
||||||
|
>
|
||||||
|
<div :class="uiMenu.trigger">
|
||||||
|
<HComboboxInput
|
||||||
|
:id="inputId"
|
||||||
|
ref="input"
|
||||||
|
:name="name"
|
||||||
|
:required="required"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
:disabled="disabled || loading"
|
||||||
|
:class="inputClass"
|
||||||
|
autocomplete="off"
|
||||||
|
v-bind="attrs"
|
||||||
|
:display-value="() => ['string', 'number'].includes(typeof modelValue) ? modelValue : modelValue[optionAttribute]"
|
||||||
|
@change="query = $event.target.value"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<span v-if="(isLeading && leadingIconName) || $slots.leading" :class="leadingWrapperIconClass">
|
||||||
|
<slot name="leading" :disabled="disabled" :loading="loading">
|
||||||
|
<UIcon :name="leadingIconName" :class="leadingIconClass" />
|
||||||
|
</slot>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<HComboboxButton v-if="(isTrailing && trailingIconName) || $slots.trailing" ref="trigger" :class="trailingWrapperIconClass">
|
||||||
|
<slot name="trailing" :disabled="disabled" :loading="loading">
|
||||||
|
<UIcon :name="trailingIconName" :class="trailingIconClass" />
|
||||||
|
</slot>
|
||||||
|
</HComboboxButton>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="open" ref="container" :class="[uiMenu.container, uiMenu.width]">
|
||||||
|
<Transition appear v-bind="uiMenu.transition">
|
||||||
|
<div>
|
||||||
|
<div v-if="popper.arrow" data-popper-arrow :class="Object.values(uiMenu.arrow)" />
|
||||||
|
|
||||||
|
<HComboboxOptions static :class="[uiMenu.base, uiMenu.ring, uiMenu.rounded, uiMenu.shadow, uiMenu.background, uiMenu.padding, uiMenu.height]">
|
||||||
|
<HComboboxOption
|
||||||
|
v-for="(option, index) in filteredOptions"
|
||||||
|
v-slot="{ active, selected, disabled: optionDisabled }"
|
||||||
|
:key="index"
|
||||||
|
as="template"
|
||||||
|
:value="valueAttribute ? option[valueAttribute] : option"
|
||||||
|
:disabled="option.disabled"
|
||||||
|
>
|
||||||
|
<li :class="[uiMenu.option.base, uiMenu.option.rounded, uiMenu.option.padding, uiMenu.option.size, uiMenu.option.color, active ? uiMenu.option.active : uiMenu.option.inactive, selected && uiMenu.option.selected, optionDisabled && uiMenu.option.disabled]">
|
||||||
|
<div :class="uiMenu.option.container">
|
||||||
|
<slot name="option" :option="option" :active="active" :selected="selected">
|
||||||
|
<UIcon v-if="option.icon" :name="option.icon" :class="[uiMenu.option.icon.base, active ? uiMenu.option.icon.active : uiMenu.option.icon.inactive, option.iconClass]" aria-hidden="true" />
|
||||||
|
<UAvatar
|
||||||
|
v-else-if="option.avatar"
|
||||||
|
v-bind="{ size: uiMenu.option.avatar.size, ...option.avatar }"
|
||||||
|
:class="uiMenu.option.avatar.base"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
<span v-else-if="option.chip" :class="uiMenu.option.chip.base" :style="{ background: `#${option.chip}` }" />
|
||||||
|
|
||||||
|
<span class="truncate">{{ ['string', 'number'].includes(typeof option) ? option : option[optionAttribute] }}</span>
|
||||||
|
</slot>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<span v-if="selected" :class="[uiMenu.option.selectedIcon.wrapper, uiMenu.option.selectedIcon.padding]">
|
||||||
|
<UIcon :name="selectedIcon" :class="uiMenu.option.selectedIcon.base" aria-hidden="true" />
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
</HComboboxOption>
|
||||||
|
|
||||||
|
<p v-if="query && !filteredOptions.length" :class="uiMenu.option.empty">
|
||||||
|
<slot name="option-empty" :query="query">
|
||||||
|
No results for "{{ query }}".
|
||||||
|
</slot>
|
||||||
|
</p>
|
||||||
|
<p v-else-if="!filteredOptions.length" :class="uiMenu.empty">
|
||||||
|
<slot name="empty" :query="query">
|
||||||
|
No options.
|
||||||
|
</slot>
|
||||||
|
</p>
|
||||||
|
</HComboboxOptions>
|
||||||
|
</div>
|
||||||
|
</Transition>
|
||||||
|
</div>
|
||||||
|
</HCombobox>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { ref, computed, toRef, watch, defineComponent } from 'vue'
|
||||||
|
import type { PropType } from 'vue'
|
||||||
|
import {
|
||||||
|
Combobox as HCombobox,
|
||||||
|
ComboboxButton as HComboboxButton,
|
||||||
|
ComboboxOptions as HComboboxOptions,
|
||||||
|
ComboboxOption as HComboboxOption,
|
||||||
|
ComboboxInput as HComboboxInput
|
||||||
|
} from '@headlessui/vue'
|
||||||
|
import { computedAsync } from '@vueuse/core'
|
||||||
|
import { defu } from 'defu'
|
||||||
|
import { twJoin } from 'tailwind-merge'
|
||||||
|
import UIcon from '../elements/Icon.vue'
|
||||||
|
import UAvatar from '../elements/Avatar.vue'
|
||||||
|
import { useUI } from '../../composables/useUI'
|
||||||
|
import { usePopper } from '../../composables/usePopper'
|
||||||
|
import { useFormGroup } from '../../composables/useFormGroup'
|
||||||
|
import { get, mergeConfig, twMerge } from '../../utils'
|
||||||
|
import { useInjectButtonGroup } from '../../composables/useButtonGroup'
|
||||||
|
import type { InputSize, InputColor, InputVariant, PopperOptions, Strategy } from '../../types'
|
||||||
|
// @ts-expect-error
|
||||||
|
import appConfig from '#build/app.config'
|
||||||
|
import { input, inputMenu } from '#ui/ui.config'
|
||||||
|
|
||||||
|
const config = mergeConfig<typeof input>(appConfig.ui.strategy, appConfig.ui.input, input)
|
||||||
|
|
||||||
|
const configMenu = mergeConfig<typeof inputMenu>(appConfig.ui.strategy, appConfig.ui.inputMenu, inputMenu)
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
components: {
|
||||||
|
HCombobox,
|
||||||
|
HComboboxButton,
|
||||||
|
HComboboxOptions,
|
||||||
|
HComboboxOption,
|
||||||
|
HComboboxInput,
|
||||||
|
UIcon,
|
||||||
|
UAvatar
|
||||||
|
},
|
||||||
|
inheritAttrs: false,
|
||||||
|
props: {
|
||||||
|
modelValue: {
|
||||||
|
type: [String, Number, Object, Array],
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
by: {
|
||||||
|
type: String,
|
||||||
|
default: undefined
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
type: Array as PropType<{ [key: string]: any, disabled?: boolean }[] | string[]>,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
id: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
required: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
icon: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
loadingIcon: {
|
||||||
|
type: String,
|
||||||
|
default: () => config.default.loadingIcon
|
||||||
|
},
|
||||||
|
leadingIcon: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
trailingIcon: {
|
||||||
|
type: String,
|
||||||
|
default: () => configMenu.default.trailingIcon
|
||||||
|
},
|
||||||
|
trailing: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
leading: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
loading: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
selectedIcon: {
|
||||||
|
type: String,
|
||||||
|
default: () => configMenu.default.selectedIcon
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
placeholder: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
padded: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
type: String as PropType<InputSize>,
|
||||||
|
default: null,
|
||||||
|
validator (value: string) {
|
||||||
|
return Object.keys(config.size).includes(value)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
color: {
|
||||||
|
type: String as PropType<InputColor>,
|
||||||
|
default: () => config.default.color,
|
||||||
|
validator (value: string) {
|
||||||
|
return [...appConfig.ui.colors, ...Object.keys(config.color)].includes(value)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
variant: {
|
||||||
|
type: String as PropType<InputVariant>,
|
||||||
|
default: () => config.default.variant,
|
||||||
|
validator (value: string) {
|
||||||
|
return [
|
||||||
|
...Object.keys(config.variant),
|
||||||
|
...Object.values(config.color).flatMap(value => Object.keys(value))
|
||||||
|
].includes(value)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
optionAttribute: {
|
||||||
|
type: String,
|
||||||
|
default: 'label'
|
||||||
|
},
|
||||||
|
valueAttribute: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
searchAttributes: {
|
||||||
|
type: Array,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
popper: {
|
||||||
|
type: Object as PropType<PopperOptions>,
|
||||||
|
default: () => ({})
|
||||||
|
},
|
||||||
|
inputClass: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
class: {
|
||||||
|
type: [String, Object, Array] as PropType<any>,
|
||||||
|
default: () => ''
|
||||||
|
},
|
||||||
|
ui: {
|
||||||
|
type: Object as PropType<Partial<typeof config> & { strategy?: Strategy }>,
|
||||||
|
default: () => ({})
|
||||||
|
},
|
||||||
|
uiMenu: {
|
||||||
|
type: Object as PropType<Partial<typeof configMenu> & { strategy?: Strategy }>,
|
||||||
|
default: () => ({})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
emits: ['update:modelValue', 'open', 'close', 'change'],
|
||||||
|
setup (props, { emit, slots }) {
|
||||||
|
const { ui, attrs } = useUI('input', toRef(props, 'ui'), config, toRef(props, 'class'))
|
||||||
|
|
||||||
|
const { ui: uiMenu } = useUI('inputMenu', toRef(props, 'uiMenu'), configMenu)
|
||||||
|
|
||||||
|
const popper = computed<PopperOptions>(() => defu({}, props.popper, uiMenu.value.popper as PopperOptions))
|
||||||
|
|
||||||
|
const [trigger, container] = usePopper(popper.value)
|
||||||
|
|
||||||
|
const { size: sizeButtonGroup, rounded } = useInjectButtonGroup({ ui, props })
|
||||||
|
const { emitFormBlur, emitFormChange, inputId, color, size: sizeFormGroup, name } = useFormGroup(props, config)
|
||||||
|
|
||||||
|
const size = computed(() => sizeButtonGroup.value || sizeFormGroup.value)
|
||||||
|
|
||||||
|
const query = ref('')
|
||||||
|
|
||||||
|
const inputClass = computed(() => {
|
||||||
|
const variant = ui.value.color?.[color.value as string]?.[props.variant as string] || ui.value.variant[props.variant]
|
||||||
|
|
||||||
|
return twMerge(twJoin(
|
||||||
|
ui.value.base,
|
||||||
|
ui.value.form,
|
||||||
|
rounded.value,
|
||||||
|
ui.value.placeholder,
|
||||||
|
ui.value.size[size.value],
|
||||||
|
props.padded ? ui.value.padding[size.value] : 'p-0',
|
||||||
|
variant?.replaceAll('{color}', color.value),
|
||||||
|
(isLeading.value || slots.leading) && ui.value.leading.padding[size.value],
|
||||||
|
(isTrailing.value || slots.trailing) && ui.value.trailing.padding[size.value]
|
||||||
|
), props.inputClass)
|
||||||
|
})
|
||||||
|
|
||||||
|
const isLeading = computed(() => {
|
||||||
|
return (props.icon && props.leading) || (props.icon && !props.trailing) || (props.loading && !props.trailing) || props.leadingIcon
|
||||||
|
})
|
||||||
|
|
||||||
|
const isTrailing = computed(() => {
|
||||||
|
return (props.icon && props.trailing) || (props.loading && props.trailing) || props.trailingIcon
|
||||||
|
})
|
||||||
|
|
||||||
|
const leadingIconName = computed(() => {
|
||||||
|
if (props.loading) {
|
||||||
|
return props.loadingIcon
|
||||||
|
}
|
||||||
|
|
||||||
|
return props.leadingIcon || props.icon
|
||||||
|
})
|
||||||
|
|
||||||
|
const trailingIconName = computed(() => {
|
||||||
|
if (props.loading && !isLeading.value) {
|
||||||
|
return props.loadingIcon
|
||||||
|
}
|
||||||
|
|
||||||
|
return props.trailingIcon || props.icon
|
||||||
|
})
|
||||||
|
|
||||||
|
const leadingWrapperIconClass = computed(() => {
|
||||||
|
return twJoin(
|
||||||
|
ui.value.icon.leading.wrapper,
|
||||||
|
ui.value.icon.leading.pointer,
|
||||||
|
ui.value.icon.leading.padding[size.value]
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
const leadingIconClass = computed(() => {
|
||||||
|
return twJoin(
|
||||||
|
ui.value.icon.base,
|
||||||
|
color.value && appConfig.ui.colors.includes(color.value) && ui.value.icon.color.replaceAll('{color}', color.value),
|
||||||
|
ui.value.icon.size[size.value],
|
||||||
|
props.loading && ui.value.icon.loading
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
const trailingWrapperIconClass = computed(() => {
|
||||||
|
return twJoin(
|
||||||
|
ui.value.icon.trailing.wrapper,
|
||||||
|
ui.value.icon.trailing.padding[size.value]
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
const trailingIconClass = computed(() => {
|
||||||
|
return twJoin(
|
||||||
|
ui.value.icon.base,
|
||||||
|
color.value && appConfig.ui.colors.includes(color.value) && ui.value.icon.color.replaceAll('{color}', color.value),
|
||||||
|
ui.value.icon.size[size.value],
|
||||||
|
props.loading && !isLeading.value && ui.value.icon.loading
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
const filteredOptions = computedAsync(async () => {
|
||||||
|
if (query.value === '') {
|
||||||
|
return props.options
|
||||||
|
}
|
||||||
|
|
||||||
|
return (props.options as any[]).filter((option: any) => {
|
||||||
|
return (props.searchAttributes?.length ? props.searchAttributes : [props.optionAttribute]).some((searchAttribute: any) => {
|
||||||
|
if (['string', 'number'].includes(typeof option)) {
|
||||||
|
return String(option).search(new RegExp(query.value, 'i')) !== -1
|
||||||
|
}
|
||||||
|
|
||||||
|
const child = get(option, searchAttribute)
|
||||||
|
|
||||||
|
return child !== null && child !== undefined && String(child).search(new RegExp(query.value, 'i')) !== -1
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(container, (value) => {
|
||||||
|
if (value) {
|
||||||
|
emit('open')
|
||||||
|
} else {
|
||||||
|
emit('close')
|
||||||
|
emitFormBlur()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
function onUpdate (event: any) {
|
||||||
|
emit('update:modelValue', event)
|
||||||
|
emit('change', event)
|
||||||
|
emitFormChange()
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
|
ui,
|
||||||
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
|
uiMenu,
|
||||||
|
attrs,
|
||||||
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
|
name,
|
||||||
|
inputId,
|
||||||
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
|
popper,
|
||||||
|
trigger,
|
||||||
|
container,
|
||||||
|
isLeading,
|
||||||
|
isTrailing,
|
||||||
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
|
inputClass,
|
||||||
|
leadingIconName,
|
||||||
|
leadingIconClass,
|
||||||
|
leadingWrapperIconClass,
|
||||||
|
trailingIconName,
|
||||||
|
trailingIconClass,
|
||||||
|
trailingWrapperIconClass,
|
||||||
|
filteredOptions,
|
||||||
|
query,
|
||||||
|
onUpdate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :class="ui.wrapper">
|
<div :class="ui.wrapper">
|
||||||
<div class="flex items-center h-5">
|
<div :class="ui.container">
|
||||||
<input
|
<input
|
||||||
:id="inputId"
|
:id="inputId"
|
||||||
v-model="pick"
|
v-model="pick"
|
||||||
@@ -9,12 +9,11 @@
|
|||||||
:value="value"
|
:value="value"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
type="radio"
|
type="radio"
|
||||||
class="form-radio"
|
|
||||||
:class="inputClass"
|
:class="inputClass"
|
||||||
v-bind="attrs"
|
v-bind="attrs"
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="label || $slots.label" class="ms-3 flex flex-col">
|
<div v-if="label || $slots.label" :class="ui.inner">
|
||||||
<label :for="inputId" :class="ui.label">
|
<label :for="inputId" :class="ui.label">
|
||||||
<slot name="label">{{ label }}</slot>
|
<slot name="label">{{ label }}</slot>
|
||||||
<span v-if="required" :class="ui.required">*</span>
|
<span v-if="required" :class="ui.required">*</span>
|
||||||
@@ -29,9 +28,9 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, defineComponent, inject, toRef, onMounted, ref } from 'vue'
|
import { computed, defineComponent, inject, toRef, onMounted, ref } 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 { Strategy } from '../../types'
|
import type { Strategy } from '../../types'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
@@ -90,11 +89,11 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
class: {
|
class: {
|
||||||
type: [String, Object, Array] as PropType<any>,
|
type: [String, Object, Array] as PropType<any>,
|
||||||
default: undefined
|
default: () => ''
|
||||||
},
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>,
|
type: Object as PropType<Partial<typeof config> & { strategy?: Strategy }>,
|
||||||
default: undefined
|
default: () => ({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: ['update:modelValue', 'change'],
|
emits: ['update:modelValue', 'change'],
|
||||||
@@ -128,10 +127,11 @@ export default defineComponent({
|
|||||||
const inputClass = computed(() => {
|
const inputClass = computed(() => {
|
||||||
return twMerge(twJoin(
|
return twMerge(twJoin(
|
||||||
ui.value.base,
|
ui.value.base,
|
||||||
|
ui.value.form,
|
||||||
ui.value.background,
|
ui.value.background,
|
||||||
ui.value.border,
|
ui.value.border,
|
||||||
ui.value.ring.replaceAll('{color}', color.value),
|
color.value && ui.value.ring.replaceAll('{color}', color.value),
|
||||||
ui.value.color.replaceAll('{color}', color.value)
|
color.value && ui.value.color.replaceAll('{color}', color.value)
|
||||||
), props.inputClass)
|
), props.inputClass)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -83,15 +83,15 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
class: {
|
class: {
|
||||||
type: [String, Object, Array] as PropType<any>,
|
type: [String, Object, Array] as PropType<any>,
|
||||||
default: undefined
|
default: () => ''
|
||||||
},
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>,
|
type: Object as PropType<Partial<typeof config> & { strategy?: Strategy }>,
|
||||||
default: undefined
|
default: () => ({})
|
||||||
},
|
},
|
||||||
uiRadio: {
|
uiRadio: {
|
||||||
type: Object as PropType<Partial<typeof configRadio & { strategy?: Strategy }>>,
|
type: Object as PropType<Partial<typeof configRadio> & { strategy?: Strategy }>,
|
||||||
default: undefined
|
default: () => ({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: ['update:modelValue', 'change'],
|
emits: ['update:modelValue', 'change'],
|
||||||
@@ -99,7 +99,7 @@ export default defineComponent({
|
|||||||
const { ui, attrs } = useUI('radioGroup', toRef(props, 'ui'), config, toRef(props, 'class'))
|
const { ui, attrs } = useUI('radioGroup', toRef(props, 'ui'), config, toRef(props, 'class'))
|
||||||
const { ui: uiRadio } = useUI('radio', toRef(props, 'uiRadio'), configRadio)
|
const { ui: uiRadio } = useUI('radio', toRef(props, 'uiRadio'), configRadio)
|
||||||
|
|
||||||
const { emitFormChange, color, name } = useFormGroup({ ...props, isFieldset: true }, config)
|
const { emitFormChange, color, name } = useFormGroup(props, config)
|
||||||
provide('radio-group', { color, name })
|
provide('radio-group', { color, name })
|
||||||
|
|
||||||
const onUpdate = (value: any) => {
|
const onUpdate = (value: any) => {
|
||||||
|
|||||||
@@ -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, Strategy } from '../../types'
|
import type { RangeSize, Strategy } from '../../types'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
@@ -85,11 +85,11 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
class: {
|
class: {
|
||||||
type: [String, Object, Array] as PropType<any>,
|
type: [String, Object, Array] as PropType<any>,
|
||||||
default: undefined
|
default: () => ''
|
||||||
},
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>,
|
type: Object as PropType<Partial<typeof config> & { strategy?: Strategy }>,
|
||||||
default: undefined
|
default: () => ({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: ['update:modelValue', 'change'],
|
emits: ['update:modelValue', 'change'],
|
||||||
@@ -124,7 +124,7 @@ export default defineComponent({
|
|||||||
ui.value.base,
|
ui.value.base,
|
||||||
ui.value.background,
|
ui.value.background,
|
||||||
ui.value.rounded,
|
ui.value.rounded,
|
||||||
ui.value.ring.replaceAll('{color}', color.value),
|
color.value && ui.value.ring.replaceAll('{color}', color.value),
|
||||||
ui.value.size[size.value]
|
ui.value.size[size.value]
|
||||||
), props.inputClass)
|
), props.inputClass)
|
||||||
})
|
})
|
||||||
@@ -133,7 +133,7 @@ export default defineComponent({
|
|||||||
return twJoin(
|
return twJoin(
|
||||||
ui.value.thumb.base,
|
ui.value.thumb.base,
|
||||||
// Intermediate class to allow thumb ring or background color (set to `current`) as it's impossible to safelist with arbitrary values
|
// Intermediate class to allow thumb ring or background color (set to `current`) as it's impossible to safelist with arbitrary values
|
||||||
ui.value.thumb.color.replaceAll('{color}', color.value),
|
color.value && ui.value.thumb.color.replaceAll('{color}', color.value),
|
||||||
ui.value.thumb.ring,
|
ui.value.thumb.ring,
|
||||||
ui.value.thumb.background,
|
ui.value.thumb.background,
|
||||||
ui.value.thumb.size[size.value]
|
ui.value.thumb.size[size.value]
|
||||||
@@ -153,7 +153,7 @@ export default defineComponent({
|
|||||||
return twJoin(
|
return twJoin(
|
||||||
ui.value.progress.base,
|
ui.value.progress.base,
|
||||||
ui.value.progress.rounded,
|
ui.value.progress.rounded,
|
||||||
ui.value.progress.background.replaceAll('{color}', color.value),
|
color.value && ui.value.progress.background.replaceAll('{color}', color.value),
|
||||||
ui.value.progress.size[size.value]
|
ui.value.progress.size[size.value]
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
:value="modelValue"
|
:value="modelValue"
|
||||||
:required="required"
|
:required="required"
|
||||||
:disabled="disabled || loading"
|
:disabled="disabled || loading"
|
||||||
class="form-select"
|
|
||||||
:class="selectClass"
|
:class="selectClass"
|
||||||
v-bind="attrs"
|
v-bind="attrs"
|
||||||
@input="onInput"
|
@input="onInput"
|
||||||
@@ -56,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 { mergeConfig, twMerge, get } from '../../utils'
|
||||||
import { useInjectButtonGroup } from '../../composables/useButtonGroup'
|
import { useInjectButtonGroup } from '../../composables/useButtonGroup'
|
||||||
import type { SelectSize, SelectColor, SelectVariant, Strategy } from '../../types'
|
import type { SelectSize, SelectColor, SelectVariant, Strategy } from '../../types'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
@@ -173,11 +172,11 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
class: {
|
class: {
|
||||||
type: [String, Object, Array] as PropType<any>,
|
type: [String, Object, Array] as PropType<any>,
|
||||||
default: undefined
|
default: () => ''
|
||||||
},
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>,
|
type: Object as PropType<Partial<typeof config> & { strategy?: Strategy }>,
|
||||||
default: undefined
|
default: () => ({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: ['update:modelValue', 'change'],
|
emits: ['update:modelValue', 'change'],
|
||||||
@@ -190,7 +189,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
const size = computed(() => sizeButtonGroup.value || sizeFormGroup.value)
|
const size = computed(() => sizeButtonGroup.value || sizeFormGroup.value)
|
||||||
|
|
||||||
const onInput = (event: InputEvent) => {
|
const onInput = (event: Event) => {
|
||||||
emit('update:modelValue', (event.target as HTMLInputElement).value)
|
emit('update:modelValue', (event.target as HTMLInputElement).value)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,6 +255,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
return twMerge(twJoin(
|
return twMerge(twJoin(
|
||||||
ui.value.base,
|
ui.value.base,
|
||||||
|
ui.value.form,
|
||||||
rounded.value,
|
rounded.value,
|
||||||
ui.value.size[size.value],
|
ui.value.size[size.value],
|
||||||
props.padded ? ui.value.padding[size.value] : 'p-0',
|
props.padded ? ui.value.padding[size.value] : 'p-0',
|
||||||
@@ -300,9 +300,9 @@ export default defineComponent({
|
|||||||
const leadingIconClass = computed(() => {
|
const leadingIconClass = computed(() => {
|
||||||
return twJoin(
|
return twJoin(
|
||||||
ui.value.icon.base,
|
ui.value.icon.base,
|
||||||
appConfig.ui.colors.includes(color.value) && ui.value.icon.color.replaceAll('{color}', color.value),
|
color.value && appConfig.ui.colors.includes(color.value) && ui.value.icon.color.replaceAll('{color}', color.value),
|
||||||
ui.value.icon.size[size.value],
|
ui.value.icon.size[size.value],
|
||||||
props.loading && 'animate-spin'
|
props.loading && ui.value.icon.loading
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -317,9 +317,9 @@ export default defineComponent({
|
|||||||
const trailingIconClass = computed(() => {
|
const trailingIconClass = computed(() => {
|
||||||
return twJoin(
|
return twJoin(
|
||||||
ui.value.icon.base,
|
ui.value.icon.base,
|
||||||
appConfig.ui.colors.includes(color.value) && ui.value.icon.color.replaceAll('{color}', color.value),
|
color.value && appConfig.ui.colors.includes(color.value) && ui.value.icon.color.replaceAll('{color}', color.value),
|
||||||
ui.value.icon.size[size.value],
|
ui.value.icon.size[size.value],
|
||||||
props.loading && !isLeading.value && 'animate-spin'
|
props.loading && !isLeading.value && ui.value.icon.loading
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
v-if="required"
|
v-if="required"
|
||||||
:value="modelValue"
|
:value="modelValue"
|
||||||
:required="required"
|
:required="required"
|
||||||
class="absolute inset-0 w-px opacity-0 cursor-default"
|
:class="uiMenu.required"
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
>
|
>
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
ref="trigger"
|
ref="trigger"
|
||||||
as="div"
|
as="div"
|
||||||
role="button"
|
role="button"
|
||||||
class="inline-flex w-full"
|
:class="uiMenu.trigger"
|
||||||
>
|
>
|
||||||
<slot :open="open" :disabled="disabled" :loading="loading">
|
<slot :open="open" :disabled="disabled" :loading="loading">
|
||||||
<button :id="inputId" :class="selectClass" :disabled="disabled || loading" type="button" v-bind="attrs">
|
<button :id="inputId" :class="selectClass" :disabled="disabled || loading" type="button" v-bind="attrs">
|
||||||
@@ -36,9 +36,9 @@
|
|||||||
</span>
|
</span>
|
||||||
|
|
||||||
<slot name="label">
|
<slot name="label">
|
||||||
<span v-if="multiple && Array.isArray(modelValue) && modelValue.length" class="block truncate">{{ modelValue.length }} selected</span>
|
<span v-if="multiple && Array.isArray(modelValue) && modelValue.length" :class="uiMenu.label">{{ modelValue.length }} selected</span>
|
||||||
<span v-else-if="!multiple && modelValue" class="block truncate">{{ ['string', 'number'].includes(typeof modelValue) ? modelValue : modelValue[optionAttribute] }}</span>
|
<span v-else-if="!multiple && modelValue" :class="uiMenu.label">{{ ['string', 'number'].includes(typeof modelValue) ? modelValue : modelValue[optionAttribute] }}</span>
|
||||||
<span v-else class="block truncate" :class="uiMenu.placeholder">{{ placeholder || ' ' }}</span>
|
<span v-else :class="uiMenu.label">{{ placeholder || ' ' }}</span>
|
||||||
</slot>
|
</slot>
|
||||||
|
|
||||||
<span v-if="(isTrailing && trailingIconName) || $slots.trailing" :class="trailingWrapperIconClass">
|
<span v-if="(isTrailing && trailingIconName) || $slots.trailing" :class="trailingWrapperIconClass">
|
||||||
@@ -53,8 +53,9 @@
|
|||||||
<div v-if="open" ref="container" :class="[uiMenu.container, uiMenu.width]">
|
<div v-if="open" ref="container" :class="[uiMenu.container, uiMenu.width]">
|
||||||
<Transition appear v-bind="uiMenu.transition">
|
<Transition appear v-bind="uiMenu.transition">
|
||||||
<div>
|
<div>
|
||||||
<div v-if="popper.arrow" data-popper-arrow :class="['invisible before:visible before:block before:rotate-45 before:z-[-1]', Object.values(uiMenu.arrow)]" />
|
<div v-if="popper.arrow" data-popper-arrow :class="Object.values(uiMenu.arrow)" />
|
||||||
<component :is="searchable ? 'HComboboxOptions' : 'HListboxOptions'" static :class="[uiMenu.base, uiMenu.divide, uiMenu.ring, uiMenu.rounded, uiMenu.shadow, uiMenu.background, uiMenu.padding, uiMenu.height]">
|
|
||||||
|
<component :is="searchable ? 'HComboboxOptions' : 'HListboxOptions'" static :class="[uiMenu.base, uiMenu.ring, uiMenu.rounded, uiMenu.shadow, uiMenu.background, uiMenu.padding, uiMenu.height]">
|
||||||
<HComboboxInput
|
<HComboboxInput
|
||||||
v-if="searchable"
|
v-if="searchable"
|
||||||
ref="searchInput"
|
ref="searchInput"
|
||||||
@@ -101,7 +102,7 @@
|
|||||||
<li :class="[uiMenu.option.base, uiMenu.option.rounded, uiMenu.option.padding, uiMenu.option.size, uiMenu.option.color, active ? uiMenu.option.active : uiMenu.option.inactive]">
|
<li :class="[uiMenu.option.base, uiMenu.option.rounded, uiMenu.option.padding, uiMenu.option.size, uiMenu.option.color, active ? uiMenu.option.active : uiMenu.option.inactive]">
|
||||||
<div :class="uiMenu.option.container">
|
<div :class="uiMenu.option.container">
|
||||||
<slot name="option-create" :option="queryOption" :active="active" :selected="selected">
|
<slot name="option-create" :option="queryOption" :active="active" :selected="selected">
|
||||||
<span class="block truncate">Create "{{ queryOption[optionAttribute] }}"</span>
|
<span :class="uiMenu.option.create">Create "{{ queryOption[optionAttribute] }}"</span>
|
||||||
</slot>
|
</slot>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
@@ -111,6 +112,11 @@
|
|||||||
No results for "{{ query }}".
|
No results for "{{ query }}".
|
||||||
</slot>
|
</slot>
|
||||||
</p>
|
</p>
|
||||||
|
<p v-else-if="!filteredOptions.length" :class="uiMenu.empty">
|
||||||
|
<slot name="empty" :query="query">
|
||||||
|
No options.
|
||||||
|
</slot>
|
||||||
|
</p>
|
||||||
</component>
|
</component>
|
||||||
</div>
|
</div>
|
||||||
</Transition>
|
</Transition>
|
||||||
@@ -134,13 +140,13 @@ 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 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 } from '../../types'
|
import type { SelectSize, SelectColor, SelectVariant, PopperOptions, Strategy } from '../../types'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
@@ -305,15 +311,15 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
class: {
|
class: {
|
||||||
type: [String, Object, Array] as PropType<any>,
|
type: [String, Object, Array] as PropType<any>,
|
||||||
default: undefined
|
default: () => ''
|
||||||
},
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>,
|
type: Object as PropType<Partial<typeof config> & { strategy?: Strategy }>,
|
||||||
default: undefined
|
default: () => ({})
|
||||||
},
|
},
|
||||||
uiMenu: {
|
uiMenu: {
|
||||||
type: Object as PropType<Partial<typeof configMenu & { strategy?: Strategy }>>,
|
type: Object as PropType<Partial<typeof configMenu> & { strategy?: Strategy }>,
|
||||||
default: undefined
|
default: () => ({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: ['update:modelValue', 'open', 'close', 'change'],
|
emits: ['update:modelValue', 'open', 'close', 'change'],
|
||||||
@@ -339,15 +345,14 @@ export default defineComponent({
|
|||||||
|
|
||||||
return twMerge(twJoin(
|
return twMerge(twJoin(
|
||||||
ui.value.base,
|
ui.value.base,
|
||||||
|
uiMenu.value.select,
|
||||||
rounded.value,
|
rounded.value,
|
||||||
'text-left cursor-default',
|
|
||||||
ui.value.size[size.value],
|
ui.value.size[size.value],
|
||||||
ui.value.gap[size.value],
|
ui.value.gap[size.value],
|
||||||
props.padded ? ui.value.padding[size.value] : 'p-0',
|
props.padded ? ui.value.padding[size.value] : 'p-0',
|
||||||
variant?.replaceAll('{color}', color.value),
|
variant?.replaceAll('{color}', color.value),
|
||||||
(isLeading.value || slots.leading) && ui.value.leading.padding[size.value],
|
(isLeading.value || slots.leading) && ui.value.leading.padding[size.value],
|
||||||
(isTrailing.value || slots.trailing) && ui.value.trailing.padding[size.value],
|
(isTrailing.value || slots.trailing) && ui.value.trailing.padding[size.value]
|
||||||
'inline-flex items-center'
|
|
||||||
), props.selectClass)
|
), props.selectClass)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -386,9 +391,9 @@ export default defineComponent({
|
|||||||
const leadingIconClass = computed(() => {
|
const leadingIconClass = computed(() => {
|
||||||
return twJoin(
|
return twJoin(
|
||||||
ui.value.icon.base,
|
ui.value.icon.base,
|
||||||
appConfig.ui.colors.includes(color.value) && ui.value.icon.color.replaceAll('{color}', color.value),
|
color.value && appConfig.ui.colors.includes(color.value) && ui.value.icon.color.replaceAll('{color}', color.value),
|
||||||
ui.value.icon.size[size.value],
|
ui.value.icon.size[size.value],
|
||||||
props.loading && 'animate-spin'
|
props.loading && ui.value.icon.loading
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -403,9 +408,9 @@ export default defineComponent({
|
|||||||
const trailingIconClass = computed(() => {
|
const trailingIconClass = computed(() => {
|
||||||
return twJoin(
|
return twJoin(
|
||||||
ui.value.icon.base,
|
ui.value.icon.base,
|
||||||
appConfig.ui.colors.includes(color.value) && ui.value.icon.color.replaceAll('{color}', color.value),
|
color.value && appConfig.ui.colors.includes(color.value) && ui.value.icon.color.replaceAll('{color}', color.value),
|
||||||
ui.value.icon.size[size.value],
|
ui.value.icon.size[size.value],
|
||||||
props.loading && !isLeading.value && 'animate-spin'
|
props.loading && !isLeading.value && ui.value.icon.loading
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
:required="required"
|
:required="required"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
:placeholder="placeholder"
|
:placeholder="placeholder"
|
||||||
class="form-textarea"
|
|
||||||
:class="textareaClass"
|
:class="textareaClass"
|
||||||
v-bind="attrs"
|
v-bind="attrs"
|
||||||
@input="onInput"
|
@input="onInput"
|
||||||
@@ -24,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 { mergeConfig, twMerge, looseToNumber } from '../../utils'
|
||||||
import type { TextareaSize, TextareaColor, TextareaVariant, Strategy } from '../../types'
|
import type { TextareaSize, TextareaColor, TextareaVariant, Strategy } from '../../types'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
@@ -117,11 +116,11 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
class: {
|
class: {
|
||||||
type: [String, Object, Array] as PropType<any>,
|
type: [String, Object, Array] as PropType<any>,
|
||||||
default: undefined
|
default: () => ''
|
||||||
},
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>,
|
type: Object as PropType<Partial<typeof config> & { strategy?: Strategy }>,
|
||||||
default: undefined
|
default: () => ({})
|
||||||
},
|
},
|
||||||
modelModifiers: {
|
modelModifiers: {
|
||||||
type: Object as PropType<{ trim?: boolean, lazy?: boolean, number?: boolean }>,
|
type: Object as PropType<{ trim?: boolean, lazy?: boolean, number?: boolean }>,
|
||||||
@@ -180,14 +179,14 @@ export default defineComponent({
|
|||||||
emitFormInput()
|
emitFormInput()
|
||||||
}
|
}
|
||||||
|
|
||||||
const onInput = (event: InputEvent) => {
|
const onInput = (event: Event) => {
|
||||||
autoResize()
|
autoResize()
|
||||||
if (!modelModifiers.value.lazy) {
|
if (!modelModifiers.value.lazy) {
|
||||||
updateInput((event.target as HTMLInputElement).value)
|
updateInput((event.target as HTMLInputElement).value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const onChange = (event: InputEvent) => {
|
const onChange = (event: Event) => {
|
||||||
const value = (event.target as HTMLInputElement).value
|
const value = (event.target as HTMLInputElement).value
|
||||||
|
|
||||||
if (modelModifiers.value.lazy) {
|
if (modelModifiers.value.lazy) {
|
||||||
@@ -227,6 +226,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
return twMerge(twJoin(
|
return twMerge(twJoin(
|
||||||
ui.value.base,
|
ui.value.base,
|
||||||
|
ui.value.form,
|
||||||
ui.value.rounded,
|
ui.value.rounded,
|
||||||
ui.value.placeholder,
|
ui.value.placeholder,
|
||||||
ui.value.size[size.value],
|
ui.value.size[size.value],
|
||||||
|
|||||||
@@ -22,11 +22,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 } from '@headlessui/vue'
|
import { Switch as HSwitch } 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, Strategy } from '../../types'
|
import type { ToggleSize, Strategy } from '../../types'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
@@ -73,10 +73,6 @@ export default defineComponent({
|
|||||||
return appConfig.ui.colors.includes(value)
|
return appConfig.ui.colors.includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
class: {
|
|
||||||
type: [String, Object, Array] as PropType<any>,
|
|
||||||
default: undefined
|
|
||||||
},
|
|
||||||
size: {
|
size: {
|
||||||
type: String as PropType<ToggleSize>,
|
type: String as PropType<ToggleSize>,
|
||||||
default: config.default.size,
|
default: config.default.size,
|
||||||
@@ -84,9 +80,13 @@ export default defineComponent({
|
|||||||
return Object.keys(config.size).includes(value)
|
return Object.keys(config.size).includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
class: {
|
||||||
|
type: [String, Object, Array] as PropType<any>,
|
||||||
|
default: () => ''
|
||||||
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>,
|
type: Object as PropType<Partial<typeof config> & { strategy?: Strategy }>,
|
||||||
default: undefined
|
default: () => ({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: ['update:modelValue'],
|
emits: ['update:modelValue'],
|
||||||
@@ -110,8 +110,8 @@ export default defineComponent({
|
|||||||
ui.value.base,
|
ui.value.base,
|
||||||
ui.value.size[props.size],
|
ui.value.size[props.size],
|
||||||
ui.value.rounded,
|
ui.value.rounded,
|
||||||
ui.value.ring.replaceAll('{color}', color.value),
|
color.value && ui.value.ring.replaceAll('{color}', color.value),
|
||||||
(active.value ? ui.value.active : ui.value.inactive).replaceAll('{color}', color.value)
|
color.value && (active.value ? ui.value.active : ui.value.inactive).replaceAll('{color}', color.value)
|
||||||
), props.class)
|
), props.class)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -126,14 +126,14 @@ export default defineComponent({
|
|||||||
const onIconClass = computed(() => {
|
const onIconClass = computed(() => {
|
||||||
return twJoin(
|
return twJoin(
|
||||||
ui.value.icon.size[props.size],
|
ui.value.icon.size[props.size],
|
||||||
ui.value.icon.on.replaceAll('{color}', color.value)
|
color.value && ui.value.icon.on.replaceAll('{color}', color.value)
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
const offIconClass = computed(() => {
|
const offIconClass = computed(() => {
|
||||||
return twJoin(
|
return twJoin(
|
||||||
ui.value.icon.size[props.size],
|
ui.value.icon.size[props.size],
|
||||||
ui.value.icon.off.replaceAll('{color}', color.value)
|
color.value && ui.value.icon.off.replaceAll('{color}', color.value)
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -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 { Strategy } from '../../types'
|
import type { Strategy } from '../../types'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
@@ -38,11 +38,11 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
class: {
|
class: {
|
||||||
type: [String, Object, Array] as PropType<any>,
|
type: [String, Object, Array] as PropType<any>,
|
||||||
default: undefined
|
default: () => ''
|
||||||
},
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>,
|
type: Object as PropType<Partial<typeof config> & { strategy?: Strategy }>,
|
||||||
default: undefined
|
default: () => ({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup (props) {
|
setup (props) {
|
||||||
|
|||||||
@@ -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 { Strategy } from '../../types'
|
import type { Strategy } from '../../types'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
@@ -26,11 +26,11 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
class: {
|
class: {
|
||||||
type: [String, Object, Array] as PropType<any>,
|
type: [String, Object, Array] as PropType<any>,
|
||||||
default: undefined
|
default: () => ''
|
||||||
},
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>,
|
type: Object as PropType<Partial<typeof config> & { strategy?: Strategy }>,
|
||||||
default: undefined
|
default: () => ({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup (props) {
|
setup (props) {
|
||||||
|
|||||||
@@ -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, Strategy } from '../../types'
|
import type { Avatar, Strategy } from '../../types'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
@@ -64,11 +64,11 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
class: {
|
class: {
|
||||||
type: [String, Object, Array] as PropType<any>,
|
type: [String, Object, Array] as PropType<any>,
|
||||||
default: undefined
|
default: () => ''
|
||||||
},
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>,
|
type: Object as PropType<Partial<typeof config> & { strategy?: Strategy }>,
|
||||||
default: undefined
|
default: () => ({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup (props) {
|
setup (props) {
|
||||||
@@ -91,17 +91,11 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const borderClass = computed(() => {
|
const borderClass = computed(() => {
|
||||||
const typeClass = ({
|
|
||||||
solid: 'border-solid',
|
|
||||||
dotted: 'border-dotted',
|
|
||||||
dashed: 'border-dashed'
|
|
||||||
})[props.type]
|
|
||||||
|
|
||||||
return twJoin(
|
return twJoin(
|
||||||
ui.value.border.base,
|
ui.value.border.base,
|
||||||
isHorizontal.value ? ui.value.border.horizontal : ui.value.border.vertical,
|
isHorizontal.value ? ui.value.border.horizontal : ui.value.border.vertical,
|
||||||
isHorizontal.value ? ui.value.border.size.horizontal : ui.value.border.size.vertical,
|
isHorizontal.value ? ui.value.border.size.horizontal : ui.value.border.size.vertical,
|
||||||
typeClass
|
ui.value.border.type[props.type]
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -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 { Strategy } from '../../types'
|
import type { Strategy } from '../../types'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
@@ -20,11 +20,11 @@ export default defineComponent({
|
|||||||
props: {
|
props: {
|
||||||
class: {
|
class: {
|
||||||
type: [String, Object, Array] as PropType<any>,
|
type: [String, Object, Array] as PropType<any>,
|
||||||
default: undefined
|
default: () => ''
|
||||||
},
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>,
|
type: Object as PropType<Partial<typeof config> & { strategy?: Strategy }>,
|
||||||
default: undefined
|
default: () => ({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup (props) {
|
setup (props) {
|
||||||
|
|||||||
@@ -5,19 +5,19 @@
|
|||||||
<ULink
|
<ULink
|
||||||
as="span"
|
as="span"
|
||||||
:class="[ui.base, index === links.length - 1 ? ui.active : !!link.to ? ui.inactive : '']"
|
:class="[ui.base, index === links.length - 1 ? ui.active : !!link.to ? ui.inactive : '']"
|
||||||
v-bind="omit(link, ['label', 'icon', 'iconClass'])"
|
v-bind="omit(link, ['label', 'labelClass', 'icon', 'iconClass'])"
|
||||||
:aria-current="index === links.length - 1 ? 'page' : undefined"
|
:aria-current="index === links.length - 1 ? 'page' : undefined"
|
||||||
>
|
>
|
||||||
<slot name="icon" :link="link" :index="index" :is-active="index === links.length - 1">
|
<slot name="icon" :link="link" :index="index" :is-active="index === links.length - 1">
|
||||||
<UIcon
|
<UIcon
|
||||||
v-if="link.icon"
|
v-if="link.icon"
|
||||||
:name="link.icon"
|
:name="link.icon"
|
||||||
:class="[ui.icon.base, index === links.length - 1 ? ui.icon.active : !!link.to ? ui.icon.inactive : '', link.iconClass]"
|
:class="twMerge(twJoin(ui.icon.base, index === links.length - 1 ? ui.icon.active : !!link.to ? ui.icon.inactive : ''), link.iconClass)"
|
||||||
/>
|
/>
|
||||||
</slot>
|
</slot>
|
||||||
|
|
||||||
<slot :link="link" :index="index" :is-active="index === links.length - 1">
|
<slot :link="link" :index="index" :is-active="index === links.length - 1">
|
||||||
{{ link.label }}
|
<span v-if="link.label" :class="twMerge(ui.label, link.labelClass)">{{ link.label }}</span>
|
||||||
</slot>
|
</slot>
|
||||||
</ULink>
|
</ULink>
|
||||||
|
|
||||||
@@ -35,10 +35,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 { 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, omit } from '../../utils'
|
import { mergeConfig, twMerge, omit } from '../../utils'
|
||||||
import type { BreadcrumbLink, Strategy } from '../../types'
|
import type { BreadcrumbLink, Strategy } from '../../types'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
@@ -63,11 +64,11 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
class: {
|
class: {
|
||||||
type: [String, Object, Array] as PropType<any>,
|
type: [String, Object, Array] as PropType<any>,
|
||||||
default: undefined
|
default: () => ''
|
||||||
},
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>,
|
type: Object as PropType<Partial<typeof config> & { strategy?: Strategy }>,
|
||||||
default: undefined
|
default: () => ({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup (props) {
|
setup (props) {
|
||||||
@@ -77,7 +78,9 @@ export default defineComponent({
|
|||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
ui,
|
ui,
|
||||||
attrs,
|
attrs,
|
||||||
omit
|
omit,
|
||||||
|
twMerge,
|
||||||
|
twJoin
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -14,14 +14,14 @@
|
|||||||
<HComboboxInput
|
<HComboboxInput
|
||||||
ref="comboboxInput"
|
ref="comboboxInput"
|
||||||
:value="query"
|
:value="query"
|
||||||
:class="[ui.input.base, ui.input.size, ui.input.height, ui.input.padding, icon && ui.input.icon.padding]"
|
:class="[ui.input.base, ui.input.size, ui.input.height, ui.input.padding, icon && ui.input.icon.padding, closeButton && ui.input.closeButton.padding]"
|
||||||
:placeholder="placeholder"
|
:placeholder="placeholder"
|
||||||
:aria-label="placeholder"
|
:aria-label="placeholder"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
@change="query = $event.target.value"
|
@change="query = $event.target.value"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<UButton v-if="closeButton" aria-label="Close" v-bind="{ ...ui.default.closeButton, ...closeButton }" :class="ui.input.closeButton" @click="onClear" />
|
<UButton v-if="closeButton" aria-label="Close" v-bind="{ ...(ui.default.closeButton || {}), ...closeButton }" :class="ui.input.closeButton.base" @click="onClear" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<HComboboxOptions
|
<HComboboxOptions
|
||||||
@@ -135,7 +135,7 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
closeButton: {
|
closeButton: {
|
||||||
type: Object as PropType<Button>,
|
type: Object as PropType<Button>,
|
||||||
default: () => config.default.closeButton as Button
|
default: () => config.default.closeButton as unknown as Button
|
||||||
},
|
},
|
||||||
emptyState: {
|
emptyState: {
|
||||||
type: Object as PropType<{ icon: string, label: string, queryLabel: string }>,
|
type: Object as PropType<{ icon: string, label: string, queryLabel: string }>,
|
||||||
@@ -171,11 +171,11 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
class: {
|
class: {
|
||||||
type: [String, Object, Array] as PropType<any>,
|
type: [String, Object, Array] as PropType<any>,
|
||||||
default: undefined
|
default: () => ''
|
||||||
},
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>,
|
type: Object as PropType<Partial<typeof config> & { strategy?: Strategy }>,
|
||||||
default: undefined
|
default: () => ({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: ['update:modelValue', 'close'],
|
emits: ['update:modelValue', 'close'],
|
||||||
@@ -217,7 +217,7 @@ export default defineComponent({
|
|||||||
const commands: Command[] = []
|
const commands: Command[] = []
|
||||||
for (const group of props.groups) {
|
for (const group of props.groups) {
|
||||||
if (!group.search) {
|
if (!group.search) {
|
||||||
commands.push(...group.commands.map(command => ({ ...command, group: group.key })))
|
commands.push(...(group.commands?.map(command => ({ ...command, group: group.key })) || []))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return commands
|
return commands
|
||||||
@@ -227,46 +227,57 @@ export default defineComponent({
|
|||||||
|
|
||||||
const { results } = useFuse(query, commands, options)
|
const { results } = useFuse(query, commands, options)
|
||||||
|
|
||||||
|
function getGroupWithCommands (group: Group, commands: Command[]) {
|
||||||
|
if (!group) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (group.filter && typeof group.filter === 'function') {
|
||||||
|
commands = group.filter(query.value, commands)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...group,
|
||||||
|
commands: commands.slice(0, options.value.resultLimit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const groups = computed(() => {
|
const groups = computed(() => {
|
||||||
const groups: Group[] = []
|
if (!results.value) {
|
||||||
|
return []
|
||||||
const groupedCommands: Record<string, typeof results['value']> = {}
|
|
||||||
for (const command of results.value) {
|
|
||||||
groupedCommands[command.item.group] ||= []
|
|
||||||
groupedCommands[command.item.group].push(command)
|
|
||||||
}
|
}
|
||||||
for (const key in groupedCommands) {
|
|
||||||
|
const groupedCommands: Record<string, Command[]> = results.value.reduce((acc, command) => {
|
||||||
|
const { item, ...data } = command
|
||||||
|
if (!item.group) {
|
||||||
|
return acc
|
||||||
|
}
|
||||||
|
|
||||||
|
acc[item.group] ||= []
|
||||||
|
acc[item.group].push({ ...item, ...data })
|
||||||
|
|
||||||
|
return acc
|
||||||
|
}, {})
|
||||||
|
|
||||||
|
const groups: Group[] = Object.entries(groupedCommands).map(([key, commands]) => {
|
||||||
const group = props.groups.find(group => group.key === key)
|
const group = props.groups.find(group => group.key === key)
|
||||||
|
if (!group) {
|
||||||
let commands = groupedCommands[key].map((result) => {
|
return null
|
||||||
const { item, ...data } = result
|
|
||||||
|
|
||||||
return {
|
|
||||||
...item,
|
|
||||||
...data
|
|
||||||
} as Command
|
|
||||||
})
|
|
||||||
|
|
||||||
if (group.filter && typeof group.filter === 'function') {
|
|
||||||
commands = group.filter(query.value, commands)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
groups.push({ ...group, commands: commands.slice(0, options.value.resultLimit) })
|
return getGroupWithCommands(group, commands)
|
||||||
}
|
}).filter(Boolean)
|
||||||
|
|
||||||
for (const group of props.groups) {
|
const searchGroups = props.groups.filter(group => !!group.search && searchResults.value[group.key]?.length).map(group => {
|
||||||
if (group.search && searchResults.value[group.key]?.length) {
|
const commands = (searchResults.value[group.key] || [])
|
||||||
let commands = (searchResults.value[group.key] || [])
|
|
||||||
|
|
||||||
if (group.filter && typeof group.filter === 'function') {
|
return getGroupWithCommands(group, [...commands])
|
||||||
commands = group.filter(query.value, commands)
|
})
|
||||||
}
|
|
||||||
|
|
||||||
groups.push({ ...group, commands: commands.slice(0, options.value.resultLimit) })
|
return [
|
||||||
}
|
...groups,
|
||||||
}
|
...searchGroups
|
||||||
|
]
|
||||||
return groups
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const debouncedSearch = useDebounceFn(async () => {
|
const debouncedSearch = useDebounceFn(async () => {
|
||||||
@@ -278,6 +289,7 @@ export default defineComponent({
|
|||||||
isLoading.value = true
|
isLoading.value = true
|
||||||
|
|
||||||
await Promise.all(searchableGroups.map(async (group) => {
|
await Promise.all(searchableGroups.map(async (group) => {
|
||||||
|
// @ts-ignore
|
||||||
searchResults.value[group.key] = await group.search(query.value)
|
searchResults.value[group.key] = await group.search(query.value)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
@@ -304,7 +316,7 @@ export default defineComponent({
|
|||||||
return twJoin(
|
return twJoin(
|
||||||
ui.value.input.icon.base,
|
ui.value.input.icon.base,
|
||||||
ui.value.input.icon.size,
|
ui.value.input.icon.size,
|
||||||
((props.loading || isLoading.value) && props.loadingIcon) && 'animate-spin'
|
((props.loading || isLoading.value) && props.loadingIcon) && ui.value.input.icon.loading
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
:size="size"
|
:size="size"
|
||||||
:disabled="!canGoFirstOrPrev"
|
:disabled="!canGoFirstOrPrev"
|
||||||
:class="[ui.base, ui.rounded]"
|
:class="[ui.base, ui.rounded]"
|
||||||
v-bind="{ ...ui.default.firstButton, ...firstButton }"
|
v-bind="{ ...(ui.default.firstButton || {}), ...firstButton }"
|
||||||
:ui="{ rounded: '' }"
|
:ui="{ rounded: '' }"
|
||||||
aria-label="First"
|
aria-label="First"
|
||||||
@click="onClickFirst"
|
@click="onClickFirst"
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
:size="size"
|
:size="size"
|
||||||
:disabled="!canGoFirstOrPrev"
|
:disabled="!canGoFirstOrPrev"
|
||||||
:class="[ui.base, ui.rounded]"
|
:class="[ui.base, ui.rounded]"
|
||||||
v-bind="{ ...ui.default.prevButton, ...prevButton }"
|
v-bind="{ ...(ui.default.prevButton || {}), ...prevButton }"
|
||||||
:ui="{ rounded: '' }"
|
:ui="{ rounded: '' }"
|
||||||
aria-label="Prev"
|
aria-label="Prev"
|
||||||
@click="onClickPrev"
|
@click="onClickPrev"
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
:key="`${page}-${index}`"
|
:key="`${page}-${index}`"
|
||||||
:size="size"
|
:size="size"
|
||||||
:label="`${page}`"
|
:label="`${page}`"
|
||||||
v-bind="page === currentPage ? { ...ui.default.activeButton, ...activeButton } : { ...ui.default.inactiveButton, ...inactiveButton }"
|
v-bind="page === currentPage ? { ...(ui.default.activeButton || {}), ...activeButton } : { ...(ui.default.inactiveButton || {}), ...inactiveButton }"
|
||||||
:class="[{ 'pointer-events-none': typeof page === 'string', 'z-[1]': page === currentPage }, ui.base, ui.rounded]"
|
:class="[{ 'pointer-events-none': typeof page === 'string', 'z-[1]': page === currentPage }, ui.base, ui.rounded]"
|
||||||
:ui="{ rounded: '' }"
|
:ui="{ rounded: '' }"
|
||||||
@click="() => onClickPage(page)"
|
@click="() => onClickPage(page)"
|
||||||
@@ -43,7 +43,7 @@
|
|||||||
:size="size"
|
:size="size"
|
||||||
:disabled="!canGoLastOrNext"
|
:disabled="!canGoLastOrNext"
|
||||||
:class="[ui.base, ui.rounded]"
|
:class="[ui.base, ui.rounded]"
|
||||||
v-bind="{ ...ui.default.nextButton, ...nextButton }"
|
v-bind="{ ...(ui.default.nextButton || {}), ...nextButton }"
|
||||||
:ui="{ rounded: '' }"
|
:ui="{ rounded: '' }"
|
||||||
aria-label="Next"
|
aria-label="Next"
|
||||||
@click="onClickNext"
|
@click="onClickNext"
|
||||||
@@ -56,7 +56,7 @@
|
|||||||
:size="size"
|
:size="size"
|
||||||
:disabled="!canGoLastOrNext"
|
:disabled="!canGoLastOrNext"
|
||||||
:class="[ui.base, ui.rounded]"
|
:class="[ui.base, ui.rounded]"
|
||||||
v-bind="{ ...ui.default.lastButton, ...lastButton }"
|
v-bind="{ ...(ui.default.lastButton || {}), ...lastButton }"
|
||||||
:ui="{ rounded: '' }"
|
:ui="{ rounded: '' }"
|
||||||
aria-label="Last"
|
aria-label="Last"
|
||||||
@click="onClickLast"
|
@click="onClickLast"
|
||||||
@@ -150,11 +150,11 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
class: {
|
class: {
|
||||||
type: [String, Object, Array] as PropType<any>,
|
type: [String, Object, Array] as PropType<any>,
|
||||||
default: undefined
|
default: () => ''
|
||||||
},
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>,
|
type: Object as PropType<Partial<typeof config> & { strategy?: Strategy }>,
|
||||||
default: undefined
|
default: () => ({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: ['update:modelValue'],
|
emits: ['update:modelValue'],
|
||||||
|
|||||||
@@ -91,11 +91,11 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
class: {
|
class: {
|
||||||
type: [String, Object, Array] as PropType<any>,
|
type: [String, Object, Array] as PropType<any>,
|
||||||
default: undefined
|
default: () => ''
|
||||||
},
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>,
|
type: Object as PropType<Partial<typeof config> & { strategy?: Strategy }>,
|
||||||
default: undefined
|
default: () => ({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: ['update:modelValue', 'change'],
|
emits: ['update:modelValue', 'change'],
|
||||||
@@ -106,33 +106,37 @@ export default defineComponent({
|
|||||||
const itemRefs = ref<HTMLElement[]>([])
|
const itemRefs = ref<HTMLElement[]>([])
|
||||||
const markerRef = ref<HTMLElement>()
|
const markerRef = ref<HTMLElement>()
|
||||||
|
|
||||||
const selectedIndex = ref(props.modelValue || props.defaultIndex)
|
const selectedIndex = ref<number | undefined>(props.modelValue || props.defaultIndex)
|
||||||
|
|
||||||
// Methods
|
// Methods
|
||||||
|
|
||||||
function calcMarkerSize (index: number) {
|
function calcMarkerSize (index: number | undefined) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const tab = itemRefs.value[index]?.$el
|
const tab = itemRefs.value[index]?.$el
|
||||||
if (!tab) {
|
if (!tab) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!markerRef.value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
markerRef.value.style.top = `${tab.offsetTop}px`
|
markerRef.value.style.top = `${tab.offsetTop}px`
|
||||||
markerRef.value.style.left = `${tab.offsetLeft}px`
|
markerRef.value.style.left = `${tab.offsetLeft}px`
|
||||||
markerRef.value.style.width = `${tab.offsetWidth}px`
|
markerRef.value.style.width = `${tab.offsetWidth}px`
|
||||||
markerRef.value.style.height = `${tab.offsetHeight}px`
|
markerRef.value.style.height = `${tab.offsetHeight}px`
|
||||||
}
|
}
|
||||||
|
|
||||||
function onChange (index) {
|
function onChange (index: number) {
|
||||||
selectedIndex.value = index
|
selectedIndex.value = index
|
||||||
|
|
||||||
emit('change', index)
|
emit('change', index)
|
||||||
|
|
||||||
if (props.modelValue !== undefined) {
|
if (props.modelValue !== undefined) {
|
||||||
emit('update:modelValue', index)
|
emit('update:modelValue', selectedIndex.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
calcMarkerSize(index)
|
calcMarkerSize(selectedIndex.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
useResizeObserver(listRef, () => {
|
useResizeObserver(listRef, () => {
|
||||||
@@ -141,7 +145,8 @@ export default defineComponent({
|
|||||||
|
|
||||||
watch(() => props.modelValue, (value) => {
|
watch(() => props.modelValue, (value) => {
|
||||||
selectedIndex.value = value
|
selectedIndex.value = value
|
||||||
calcMarkerSize(value)
|
|
||||||
|
calcMarkerSize(selectedIndex.value)
|
||||||
})
|
})
|
||||||
|
|
||||||
onMounted(() => calcMarkerSize(selectedIndex.value))
|
onMounted(() => calcMarkerSize(selectedIndex.value))
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
v-for="(link, index) of links"
|
v-for="(link, index) of links"
|
||||||
v-slot="{ isActive }"
|
v-slot="{ isActive }"
|
||||||
:key="index"
|
:key="index"
|
||||||
v-bind="omit(link, ['label', 'icon', 'iconClass', 'avatar', 'badge', 'click'])"
|
v-bind="omit(link, ['label', 'labelClass', 'icon', 'iconClass', 'avatar', 'badge', 'click'])"
|
||||||
:class="[ui.base, ui.padding, ui.width, ui.ring, ui.rounded, ui.font, ui.size]"
|
:class="[ui.base, ui.padding, ui.width, ui.ring, ui.rounded, ui.font, ui.size]"
|
||||||
:active-class="ui.active"
|
:active-class="ui.active"
|
||||||
:inactive-class="ui.inactive"
|
:inactive-class="ui.inactive"
|
||||||
@@ -22,11 +22,11 @@
|
|||||||
<UIcon
|
<UIcon
|
||||||
v-if="link.icon"
|
v-if="link.icon"
|
||||||
:name="link.icon"
|
:name="link.icon"
|
||||||
:class="[ui.icon.base, isActive ? ui.icon.active : ui.icon.inactive, link.iconClass]"
|
:class="twMerge(twJoin(ui.icon.base, isActive ? ui.icon.active : ui.icon.inactive), link.iconClass)"
|
||||||
/>
|
/>
|
||||||
</slot>
|
</slot>
|
||||||
<slot :link="link" :is-active="isActive">
|
<slot :link="link" :is-active="isActive">
|
||||||
<span v-if="link.label" :class="ui.label">{{ link.label }}</span>
|
<span v-if="link.label" :class="twMerge(ui.label, link.labelClass)">{{ link.label }}</span>
|
||||||
</slot>
|
</slot>
|
||||||
<slot name="badge" :link="link" :is-active="isActive">
|
<slot name="badge" :link="link" :is-active="isActive">
|
||||||
<span v-if="link.badge" :class="[ui.badge.base, isActive ? ui.badge.active : ui.badge.inactive]">
|
<span v-if="link.badge" :class="[ui.badge.base, isActive ? ui.badge.active : ui.badge.inactive]">
|
||||||
@@ -40,11 +40,12 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { toRef, defineComponent } from 'vue'
|
import { toRef, defineComponent } from 'vue'
|
||||||
import type { PropType } from 'vue'
|
import type { PropType } from 'vue'
|
||||||
|
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 ULink from '../elements/Link.vue'
|
import ULink from '../elements/Link.vue'
|
||||||
import { useUI } from '../../composables/useUI'
|
import { useUI } from '../../composables/useUI'
|
||||||
import { mergeConfig, omit } from '../../utils'
|
import { mergeConfig, twMerge, omit } from '../../utils'
|
||||||
import type { VerticalNavigationLink, Strategy } from '../../types'
|
import type { VerticalNavigationLink, Strategy } from '../../types'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
@@ -66,11 +67,11 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
class: {
|
class: {
|
||||||
type: [String, Object, Array] as PropType<any>,
|
type: [String, Object, Array] as PropType<any>,
|
||||||
default: undefined
|
default: () => ''
|
||||||
},
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>,
|
type: Object as PropType<Partial<typeof config> & { strategy?: Strategy }>,
|
||||||
default: undefined
|
default: () => ({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup (props) {
|
setup (props) {
|
||||||
@@ -80,7 +81,9 @@ export default defineComponent({
|
|||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
ui,
|
ui,
|
||||||
attrs,
|
attrs,
|
||||||
omit
|
omit,
|
||||||
|
twMerge,
|
||||||
|
twJoin
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
<div v-if="isOpen" ref="container" :class="wrapperClass" v-bind="attrs">
|
<div v-if="isOpen" ref="container" :class="wrapperClass" v-bind="attrs">
|
||||||
<Transition appear v-bind="ui.transition">
|
<Transition appear v-bind="ui.transition">
|
||||||
<div>
|
<div>
|
||||||
<div v-if="popper.arrow" data-popper-arrow :class="['invisible before:visible before:block before:rotate-45 before:z-[-1]', Object.values(ui.arrow)]" />
|
<div v-if="popper.arrow" data-popper-arrow :class="Object.values(ui.arrow)" />
|
||||||
|
|
||||||
<div :class="[ui.base, ui.ring, ui.rounded, ui.shadow, ui.background]">
|
<div :class="[ui.base, ui.ring, ui.rounded, ui.shadow, ui.background]">
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
@@ -17,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 { PopperOptions, Strategy } from '../../types'
|
import type { PopperOptions, Strategy } from '../../types'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
@@ -45,11 +46,11 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
class: {
|
class: {
|
||||||
type: [String, Object, Array] as PropType<any>,
|
type: [String, Object, Array] as PropType<any>,
|
||||||
default: undefined
|
default: () => ''
|
||||||
},
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>,
|
type: Object as PropType<Partial<typeof config> & { strategy?: Strategy }>,
|
||||||
default: undefined
|
default: () => ({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: ['update:modelValue', 'close'],
|
emits: ['update:modelValue', 'close'],
|
||||||
|
|||||||
@@ -14,10 +14,7 @@
|
|||||||
ui.background,
|
ui.background,
|
||||||
ui.ring,
|
ui.ring,
|
||||||
ui.shadow,
|
ui.shadow,
|
||||||
fullscreen ? 'w-screen' : ui.width,
|
fullscreen ? ui.fullscreen : [ui.width, ui.height, ui.rounded, ui.margin],
|
||||||
fullscreen ? 'h-screen' : ui.height,
|
|
||||||
fullscreen ? 'rounded-none' : ui.rounded,
|
|
||||||
fullscreen ? 'm-0' : ui.margin
|
|
||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
<slot />
|
<slot />
|
||||||
@@ -77,11 +74,11 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
class: {
|
class: {
|
||||||
type: [String, Object, Array] as PropType<any>,
|
type: [String, Object, Array] as PropType<any>,
|
||||||
default: undefined
|
default: () => ''
|
||||||
},
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>,
|
type: Object as PropType<Partial<typeof config> & { strategy?: Strategy }>,
|
||||||
default: undefined
|
default: () => ({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: ['update:modelValue', 'close'],
|
emits: ['update:modelValue', 'close'],
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
<UIcon v-if="icon" :name="icon" :class="iconClass" />
|
<UIcon v-if="icon" :name="icon" :class="iconClass" />
|
||||||
<UAvatar v-if="avatar" v-bind="{ size: ui.avatar.size, ...avatar }" :class="ui.avatar.base" />
|
<UAvatar v-if="avatar" v-bind="{ size: ui.avatar.size, ...avatar }" :class="ui.avatar.base" />
|
||||||
|
|
||||||
<div class="w-0 flex-1">
|
<div :class="ui.inner">
|
||||||
<p :class="ui.title">
|
<p :class="ui.title">
|
||||||
<slot name="title" :title="title">
|
<slot name="title" :title="title">
|
||||||
{{ title }}
|
{{ title }}
|
||||||
@@ -25,15 +25,15 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div v-if="(description || $slots.description) && actions.length" :class="ui.actions">
|
<div v-if="(description || $slots.description) && actions.length" :class="ui.actions">
|
||||||
<UButton v-for="(action, index) of actions" :key="index" v-bind="{ ...ui.default.actionButton, ...action }" @click.stop="onAction(action)" />
|
<UButton v-for="(action, index) of actions" :key="index" v-bind="{ ...(ui.default.actionButton || {}), ...action }" @click.stop="onAction(action)" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="closeButton || (!description && !$slots.description && actions.length)" :class="twMerge(ui.actions, 'mt-0')">
|
<div v-if="closeButton || (!description && !$slots.description && actions.length)" :class="twMerge(ui.actions, 'mt-0')">
|
||||||
<template v-if="!description && !$slots.description && actions.length">
|
<template v-if="!description && !$slots.description && actions.length">
|
||||||
<UButton v-for="(action, index) of actions" :key="index" v-bind="{ ...ui.default.actionButton, ...action }" @click.stop="onAction(action)" />
|
<UButton v-for="(action, index) of actions" :key="index" v-bind="{ ...(ui.default.actionButton || {}), ...action }" @click.stop="onAction(action)" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<UButton v-if="closeButton" aria-label="Close" v-bind="{ ...ui.default.closeButton, ...closeButton }" @click.stop="onClose" />
|
<UButton v-if="closeButton" aria-label="Close" v-bind="{ ...(ui.default.closeButton || {}), ...closeButton }" @click.stop="onClose" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="timeout" :class="progressClass" :style="progressStyle" />
|
<div v-if="timeout" :class="progressClass" :style="progressStyle" />
|
||||||
@@ -45,13 +45,13 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { ref, computed, toRef, onMounted, onUnmounted, watchEffect, defineComponent } from 'vue'
|
import { ref, computed, toRef, onMounted, onUnmounted, 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 } from '../../types'
|
import type { Avatar, Button, NotificationColor, NotificationAction, Strategy } from '../../types'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
@@ -112,11 +112,11 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
class: {
|
class: {
|
||||||
type: [String, Object, Array] as PropType<any>,
|
type: [String, Object, Array] as PropType<any>,
|
||||||
default: undefined
|
default: () => ''
|
||||||
},
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>,
|
type: Object as PropType<Partial<typeof config> & { strategy?: Strategy }>,
|
||||||
default: undefined
|
default: () => ({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: ['close'],
|
emits: ['close'],
|
||||||
@@ -129,7 +129,7 @@ export default defineComponent({
|
|||||||
const wrapperClass = computed(() => {
|
const wrapperClass = computed(() => {
|
||||||
return twMerge(twJoin(
|
return twMerge(twJoin(
|
||||||
ui.value.wrapper,
|
ui.value.wrapper,
|
||||||
ui.value.background,
|
ui.value.background?.replaceAll('{color}', props.color),
|
||||||
ui.value.rounded,
|
ui.value.rounded,
|
||||||
ui.value.shadow
|
ui.value.shadow
|
||||||
), props.class)
|
), props.class)
|
||||||
|
|||||||
@@ -22,11 +22,11 @@
|
|||||||
<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 UNotification from './Notification.vue'
|
import UNotification from './Notification.vue'
|
||||||
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 { Notification, Strategy } from '../../types'
|
import type { Notification, Strategy } from '../../types'
|
||||||
import { useState } from '#imports'
|
import { useState } from '#imports'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
@@ -43,11 +43,11 @@ export default defineComponent({
|
|||||||
props: {
|
props: {
|
||||||
class: {
|
class: {
|
||||||
type: [String, Object, Array] as PropType<any>,
|
type: [String, Object, Array] as PropType<any>,
|
||||||
default: undefined
|
default: () => ''
|
||||||
},
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>,
|
type: Object as PropType<Partial<typeof config> & { strategy?: Strategy }>,
|
||||||
default: undefined
|
default: () => ({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup (props) {
|
setup (props) {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
ref="trigger"
|
ref="trigger"
|
||||||
as="div"
|
as="div"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
class="inline-flex w-full"
|
:class="ui.trigger"
|
||||||
role="button"
|
role="button"
|
||||||
@mouseover="onMouseOver"
|
@mouseover="onMouseOver"
|
||||||
>
|
>
|
||||||
@@ -22,7 +22,8 @@
|
|||||||
<div v-if="(open !== undefined) ? open : headlessOpen" ref="container" :class="[ui.container, ui.width]" :style="containerStyle" @mouseover="onMouseOver">
|
<div v-if="(open !== undefined) ? open : headlessOpen" ref="container" :class="[ui.container, ui.width]" :style="containerStyle" @mouseover="onMouseOver">
|
||||||
<Transition appear v-bind="ui.transition">
|
<Transition appear v-bind="ui.transition">
|
||||||
<div>
|
<div>
|
||||||
<div v-if="popper.arrow" data-popper-arrow :class="['invisible before:visible before:block before:rotate-45 before:z-[-1]', Object.values(ui.arrow)]" />
|
<div v-if="popper.arrow" data-popper-arrow :class="Object.values(ui.arrow)" />
|
||||||
|
|
||||||
<HPopoverPanel :class="[ui.base, ui.background, ui.ring, ui.rounded, ui.shadow]" static>
|
<HPopoverPanel :class="[ui.base, ui.background, ui.ring, ui.rounded, ui.shadow]" static>
|
||||||
<slot name="panel" :open="(open !== undefined) ? open : headlessOpen" :close="close" />
|
<slot name="panel" :open="(open !== undefined) ? open : headlessOpen" :close="close" />
|
||||||
</HPopoverPanel>
|
</HPopoverPanel>
|
||||||
@@ -33,7 +34,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, ref, toRef, onMounted, defineComponent } from 'vue'
|
import { computed, ref, toRef, onMounted, defineComponent, watch } from 'vue'
|
||||||
import type { PropType } from 'vue'
|
import type { PropType } from 'vue'
|
||||||
import { defu } from 'defu'
|
import { defu } from 'defu'
|
||||||
import { Popover as HPopover, PopoverButton as HPopoverButton, PopoverPanel as HPopoverPanel } from '@headlessui/vue'
|
import { Popover as HPopover, PopoverButton as HPopoverButton, PopoverPanel as HPopoverPanel } from '@headlessui/vue'
|
||||||
@@ -86,15 +87,15 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
class: {
|
class: {
|
||||||
type: [String, Object, Array] as PropType<any>,
|
type: [String, Object, Array] as PropType<any>,
|
||||||
default: undefined
|
default: () => ''
|
||||||
},
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>,
|
type: Object as PropType<Partial<typeof config> & { strategy?: Strategy }>,
|
||||||
default: undefined
|
default: () => ({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: ['update:open'],
|
emits: ['update:open', 'open', 'close'],
|
||||||
setup (props) {
|
setup (props, { emit }) {
|
||||||
const { ui, attrs } = useUI('popover', toRef(props, 'ui'), config, toRef(props, 'class'))
|
const { ui, attrs } = useUI('popover', toRef(props, 'ui'), config, toRef(props, 'class'))
|
||||||
|
|
||||||
const popper = computed<PopperOptions>(() => defu(props.mode === 'hover' ? { offsetDistance: 0 } : {}, props.popper, ui.value.popper as PopperOptions))
|
const popper = computed<PopperOptions>(() => defu(props.mode === 'hover' ? { offsetDistance: 0 } : {}, props.popper, ui.value.popper as PopperOptions))
|
||||||
@@ -169,6 +170,11 @@ export default defineComponent({
|
|||||||
}, props.closeDelay)
|
}, props.closeDelay)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
watch(() => popoverApi.value?.popoverState, (newValue: number, oldValue: number) => {
|
||||||
|
if (oldValue === undefined) return
|
||||||
|
emit(newValue === 0 ? 'open' : 'close')
|
||||||
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
ui,
|
ui,
|
||||||
|
|||||||
@@ -63,11 +63,11 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
class: {
|
class: {
|
||||||
type: [String, Object, Array] as PropType<any>,
|
type: [String, Object, Array] as PropType<any>,
|
||||||
default: undefined
|
default: () => ''
|
||||||
},
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>,
|
type: Object as PropType<Partial<typeof config> & { strategy?: Strategy }>,
|
||||||
default: undefined
|
default: () => ({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: ['update:modelValue', 'close'],
|
emits: ['update:modelValue', 'close'],
|
||||||
@@ -90,10 +90,10 @@ export default defineComponent({
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
...ui.value.transition,
|
...ui.value.transition,
|
||||||
enterFrom: props.side === 'left' ? '-translate-x-full' : 'translate-x-full',
|
enterFrom: props.side === 'left' ? ui.value.translate.left : ui.value.translate.right,
|
||||||
enterTo: 'translate-x-0',
|
enterTo: ui.value.translate.base,
|
||||||
leaveFrom: 'translate-x-0',
|
leaveFrom: ui.value.translate.base,
|
||||||
leaveTo: props.side === 'left' ? '-translate-x-full' : 'translate-x-full'
|
leaveTo: props.side === 'left' ? ui.value.translate.left : ui.value.translate.right
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<div v-if="open && !prevent" ref="container" :class="[ui.container, ui.width]">
|
<div v-if="open && !prevent" ref="container" :class="[ui.container, ui.width]">
|
||||||
<Transition appear v-bind="ui.transition">
|
<Transition appear v-bind="ui.transition">
|
||||||
<div>
|
<div>
|
||||||
<div v-if="popper.arrow" data-popper-arrow :class="['invisible before:visible before:block before:rotate-45 before:z-[-1]', Object.values(ui.arrow)]" />
|
<div v-if="popper.arrow" data-popper-arrow :class="Object.values(ui.arrow)" />
|
||||||
|
|
||||||
<div :class="[ui.base, ui.background, ui.color, ui.rounded, ui.shadow, ui.ring]">
|
<div :class="[ui.base, ui.background, ui.color, ui.rounded, ui.shadow, ui.ring]">
|
||||||
<slot name="text">
|
<slot name="text">
|
||||||
@@ -15,7 +15,8 @@
|
|||||||
</slot>
|
</slot>
|
||||||
|
|
||||||
<span v-if="shortcuts?.length" :class="ui.shortcuts">
|
<span v-if="shortcuts?.length" :class="ui.shortcuts">
|
||||||
<span class="mx-1 text-gray-700 dark:text-gray-200">·</span>
|
<span :class="ui.middot">·</span>
|
||||||
|
|
||||||
<UKbd v-for="shortcut of shortcuts" :key="shortcut" size="xs">
|
<UKbd v-for="shortcut of shortcuts" :key="shortcut" size="xs">
|
||||||
{{ shortcut }}
|
{{ shortcut }}
|
||||||
</Ukbd>
|
</Ukbd>
|
||||||
@@ -74,11 +75,11 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
class: {
|
class: {
|
||||||
type: [String, Object, Array] as PropType<any>,
|
type: [String, Object, Array] as PropType<any>,
|
||||||
default: undefined
|
default: () => ''
|
||||||
},
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>,
|
type: Object as PropType<Partial<typeof config> & { strategy?: Strategy }>,
|
||||||
default: undefined
|
default: () => ({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup (props) {
|
setup (props) {
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ export const defineShortcuts = (config: ShortcutsConfig, options: ShortcutsOptio
|
|||||||
|
|
||||||
let shortcuts: Shortcut[] = []
|
let shortcuts: Shortcut[] = []
|
||||||
|
|
||||||
const chainedInputs = ref([])
|
const chainedInputs = ref<string[]>([])
|
||||||
const clearChainedInput = () => {
|
const clearChainedInput = () => {
|
||||||
chainedInputs.value.splice(0, chainedInputs.value.length)
|
chainedInputs.value.splice(0, chainedInputs.value.length)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ import type { FormEvent, FormEventType, InjectedFormGroupValue } from '../types/
|
|||||||
import { uid } from '../utils/uid'
|
import { uid } from '../utils/uid'
|
||||||
|
|
||||||
type InputProps = {
|
type InputProps = {
|
||||||
id?: string | null
|
id?: string
|
||||||
size?: string | number | symbol
|
size?: string | number | symbol
|
||||||
color?: string
|
color?: string
|
||||||
name?: string
|
name?: string
|
||||||
isFieldset?: boolean
|
|
||||||
eagerValidation?: boolean
|
eagerValidation?: boolean
|
||||||
|
legend?: string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useFormGroup = (inputProps?: InputProps, config?: any) => {
|
export const useFormGroup = (inputProps?: InputProps, config?: any) => {
|
||||||
@@ -20,7 +20,8 @@ export const useFormGroup = (inputProps?: InputProps, config?: any) => {
|
|||||||
const inputId = ref(inputProps?.id)
|
const inputId = ref(inputProps?.id)
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
inputId.value = inputProps?.isFieldset ? null : inputProps?.id ?? uid()
|
// Remove FormGroup label bindings for RadioGroup elements to avoid label conflicts
|
||||||
|
inputId.value = inputProps?.legend === null || inputProps.legend ? undefined : inputProps?.id ?? uid()
|
||||||
|
|
||||||
if (formGroup) {
|
if (formGroup) {
|
||||||
// Updates for="..." attribute on label if inputProps.id is provided
|
// Updates for="..." attribute on label if inputProps.id is provided
|
||||||
@@ -41,17 +42,17 @@ export const useFormGroup = (inputProps?: InputProps, config?: any) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function emitFormBlur () {
|
function emitFormBlur () {
|
||||||
emitFormEvent('blur', formGroup?.name.value)
|
emitFormEvent('blur', formGroup?.name.value as string)
|
||||||
blurred.value = true
|
blurred.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
function emitFormChange () {
|
function emitFormChange () {
|
||||||
emitFormEvent('change', formGroup?.name.value)
|
emitFormEvent('change', formGroup?.name.value as string)
|
||||||
}
|
}
|
||||||
|
|
||||||
const emitFormInput = useDebounceFn(() => {
|
const emitFormInput = useDebounceFn(() => {
|
||||||
if (blurred.value || formGroup?.eagerValidation.value) {
|
if (blurred.value || formGroup?.eagerValidation.value) {
|
||||||
emitFormEvent('input', formGroup?.name.value)
|
emitFormEvent('input', formGroup?.name.value as string)
|
||||||
}
|
}
|
||||||
}, 300)
|
}, 300)
|
||||||
|
|
||||||
@@ -59,7 +60,7 @@ export const useFormGroup = (inputProps?: InputProps, config?: any) => {
|
|||||||
inputId,
|
inputId,
|
||||||
name: computed(() => inputProps?.name ?? formGroup?.name.value),
|
name: computed(() => inputProps?.name ?? formGroup?.name.value),
|
||||||
size: computed(() => {
|
size: computed(() => {
|
||||||
const formGroupSize = config.size[formGroup?.size.value] ? formGroup?.size.value : null
|
const formGroupSize = config.size[formGroup?.size.value as string] ? formGroup?.size.value : null
|
||||||
return inputProps?.size ?? formGroupSize ?? config?.default?.size
|
return inputProps?.size ?? formGroupSize ?? config?.default?.size
|
||||||
}),
|
}),
|
||||||
color: computed(() => formGroup?.error?.value ? 'red' : inputProps?.color),
|
color: computed(() => formGroup?.error?.value ? 'red' : inputProps?.color),
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { useAppConfig } from '#imports'
|
|||||||
import { mergeConfig, omit, get } from '../utils'
|
import { mergeConfig, omit, get } from '../utils'
|
||||||
import type { Strategy } from '../types'
|
import type { Strategy } from '../types'
|
||||||
|
|
||||||
export const useUI = <T>(key, $ui: Ref<Partial<T & { strategy: Strategy }> | undefined>, $config?: Ref<T> | T, $wrapperClass?: Ref<string>, withAppConfig: boolean = false) => {
|
export const useUI = <T>(key, $ui?: Ref<Partial<T> & { strategy?: Strategy } | undefined>, $config?: Ref<T> | T, $wrapperClass?: Ref<string>, withAppConfig: boolean = false) => {
|
||||||
const $attrs = useAttrs()
|
const $attrs = useAttrs()
|
||||||
const appConfig = useAppConfig()
|
const appConfig = useAppConfig()
|
||||||
|
|
||||||
|
|||||||
3
src/runtime/types/breadcrumb.d.ts
vendored
3
src/runtime/types/breadcrumb.d.ts
vendored
@@ -2,6 +2,9 @@ import type { Link } from './link'
|
|||||||
|
|
||||||
export interface BreadcrumbLink extends Link {
|
export interface BreadcrumbLink extends Link {
|
||||||
label: string
|
label: string
|
||||||
|
labelClass?: string
|
||||||
icon?: string
|
icon?: string
|
||||||
iconClass?: string
|
iconClass?: string
|
||||||
|
// FIXME: This is a workaround for `link.to` not being resolved although it extends `NuxtLinkProps`
|
||||||
|
[key: string]: any
|
||||||
}
|
}
|
||||||
|
|||||||
1
src/runtime/types/dropdown.d.ts
vendored
1
src/runtime/types/dropdown.d.ts
vendored
@@ -3,6 +3,7 @@ import type { Avatar } from './avatar'
|
|||||||
|
|
||||||
export interface DropdownItem extends NuxtLinkProps {
|
export interface DropdownItem extends NuxtLinkProps {
|
||||||
label: string
|
label: string
|
||||||
|
labelClass?: string
|
||||||
slot?: string
|
slot?: string
|
||||||
icon?: string
|
icon?: string
|
||||||
iconClass?: string
|
iconClass?: string
|
||||||
|
|||||||
2
src/runtime/types/form.d.ts
vendored
2
src/runtime/types/form.d.ts
vendored
@@ -31,6 +31,6 @@ export interface InjectedFormGroupValue {
|
|||||||
inputId: Ref<string | undefined>
|
inputId: Ref<string | undefined>
|
||||||
name: Ref<string>
|
name: Ref<string>
|
||||||
size: Ref<string | number | symbol>
|
size: Ref<string | number | symbol>
|
||||||
error: Ref<string | boolean>
|
error: Ref<string | boolean | undefined>
|
||||||
eagerValidation: Ref<boolean>
|
eagerValidation: Ref<boolean>
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user