mirror of
https://github.com/ArthurDanjou/ui.git
synced 2026-01-17 05:28:09 +01:00
Compare commits
3 Commits
patch-1
...
feat/custo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
259a43930c | ||
|
|
b7ab65b0c9 | ||
|
|
18a1c17d98 |
4
.github/workflows/stale.yml
vendored
4
.github/workflows/stale.yml
vendored
@@ -33,5 +33,5 @@ jobs:
|
|||||||
Thank you for your understanding and support!
|
Thank you for your understanding and support!
|
||||||
|
|
||||||
— Nuxt UI Team
|
— Nuxt UI Team
|
||||||
exempt-issue-labels: 'feature,announcement,release,reka-ui,upstream'
|
exempt-issue-labels: 'feature,announcement'
|
||||||
operations-per-run: 300
|
operations-per-run: 300
|
||||||
@@ -35,7 +35,7 @@ ${pro ? `import type { ComponentConfig } from '@nuxt/ui'` : ''}
|
|||||||
import theme from '#build/${path}/${prose ? 'prose/' : ''}${content ? 'content/' : ''}${kebabName}'
|
import theme from '#build/${path}/${prose ? 'prose/' : ''}${content ? 'content/' : ''}${kebabName}'
|
||||||
${!pro ? `import type { ComponentConfig } from '../types/utils'` : ''}
|
${!pro ? `import type { ComponentConfig } from '../types/utils'` : ''}
|
||||||
|
|
||||||
type ${upperName} = ComponentConfig<typeof theme, AppConfig, '${camelName}'${pro ? `, '${key}'` : ''}>
|
type ${upperName} = ComponentConfig<typeof theme, AppConfig, ${upperName}${pro ? `, '${key}'` : ''}>
|
||||||
|
|
||||||
export interface ${upperName}Props {
|
export interface ${upperName}Props {
|
||||||
/**
|
/**
|
||||||
@@ -80,7 +80,7 @@ ${pro ? `import type { ComponentConfig } from '@nuxt/ui'` : ''}
|
|||||||
import theme from '#build/${path}/${prose ? 'prose/' : ''}${content ? 'content/' : ''}${kebabName}'
|
import theme from '#build/${path}/${prose ? 'prose/' : ''}${content ? 'content/' : ''}${kebabName}'
|
||||||
${!pro ? `import type { ComponentConfig } from '../types/utils'` : ''}
|
${!pro ? `import type { ComponentConfig } from '../types/utils'` : ''}
|
||||||
|
|
||||||
type ${upperName} = ComponentConfig<typeof theme, AppConfig, '${camelName}'${pro ? `, '${key}'` : ''}>
|
type ${upperName} = ComponentConfig<typeof theme, AppConfig, ${upperName}${pro ? `, '${key}'` : ''}>
|
||||||
|
|
||||||
export interface ${upperName}Props extends Pick<${upperName}RootProps> {
|
export interface ${upperName}Props extends Pick<${upperName}RootProps> {
|
||||||
class?: any
|
class?: any
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ provide('navigation', mappedNavigation)
|
|||||||
<NuxtLoadingIndicator color="var(--ui-primary)" :height="2" />
|
<NuxtLoadingIndicator color="var(--ui-primary)" :height="2" />
|
||||||
|
|
||||||
<template v-if="!route.path.startsWith('/examples')">
|
<template v-if="!route.path.startsWith('/examples')">
|
||||||
<Banner />
|
<!-- <Banner /> -->
|
||||||
|
|
||||||
<Header :links="links" />
|
<Header :links="links" />
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,19 +1,15 @@
|
|||||||
<template>
|
<template>
|
||||||
<UBanner
|
<UBanner
|
||||||
id="nuxtlabs-join-vercel"
|
id="ui3-launch"
|
||||||
title="NuxtLabs is joining Vercel"
|
title="Nuxt UI v3 is officially released!"
|
||||||
icon="i-simple-icons-vercel"
|
icon="i-lucide-rocket"
|
||||||
to="https://nuxtlabs.com/?utm_source=nuxt-ui&utm_medium=banner&utm_campaign=nuxtlabs-vercel"
|
:actions="[
|
||||||
target="_blank"
|
{
|
||||||
|
label: 'Discover Nuxt UI Pro',
|
||||||
|
to: '/pro/pricing',
|
||||||
|
trailingIcon: 'i-lucide-arrow-right'
|
||||||
|
}
|
||||||
|
]"
|
||||||
close
|
close
|
||||||
:actions="[{
|
|
||||||
label: 'Read the announcement',
|
|
||||||
color: 'neutral',
|
|
||||||
variant: 'outline',
|
|
||||||
trailingIcon: 'i-lucide-arrow-right',
|
|
||||||
to: 'https://nuxtlabs.com/?utm_source=nuxt-ui&utm_medium=banner&utm_campaign=nuxtlabs-vercel',
|
|
||||||
target: '_blank',
|
|
||||||
class: 'ring-0'
|
|
||||||
}]"
|
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ const meta = await fetchComponentMeta(name as any)
|
|||||||
</ProseCode>
|
</ProseCode>
|
||||||
</ProseTd>
|
</ProseTd>
|
||||||
<ProseTd>
|
<ProseTd>
|
||||||
<HighlightInlineType v-if="slot.type" :type="slot.type.replace(/ui:\s*\{[^}]*\}/g, 'ui: {}')" />
|
<HighlightInlineType v-if="slot.type" :type="slot.type" />
|
||||||
|
|
||||||
<MDC v-if="slot.description" :value="slot.description" class="text-toned mt-1" :cache-key="`${kebabCase(route.path)}-${slot.name}-description`" />
|
<MDC v-if="slot.description" :value="slot.description" class="text-toned mt-1" :cache-key="`${kebabCase(route.path)}-${slot.name}-description`" />
|
||||||
</ProseTd>
|
</ProseTd>
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ const items = [
|
|||||||
v-slot="{ item }"
|
v-slot="{ item }"
|
||||||
orientation="vertical"
|
orientation="vertical"
|
||||||
:items="items"
|
:items="items"
|
||||||
:ui="{ container: 'h-[336px]' }"
|
|
||||||
class="w-full max-w-xs mx-auto"
|
class="w-full max-w-xs mx-auto"
|
||||||
|
:ui="{ container: 'h-[336px]' }"
|
||||||
>
|
>
|
||||||
<img :src="item" width="320" height="320" class="rounded-lg">
|
<img :src="item" width="320" height="320" class="rounded-lg">
|
||||||
</UCarousel>
|
</UCarousel>
|
||||||
|
|||||||
@@ -1,78 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
const groups = [
|
|
||||||
{
|
|
||||||
id: 'actions',
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
label: 'Add new file',
|
|
||||||
suffix: 'Create a new file in the current directory',
|
|
||||||
icon: 'i-lucide-file-plus',
|
|
||||||
kbds: ['meta', 'N']
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Add new folder',
|
|
||||||
suffix: 'Create a new folder in the current directory',
|
|
||||||
icon: 'i-lucide-folder-plus',
|
|
||||||
kbds: ['meta', 'F']
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Search files',
|
|
||||||
suffix: 'Search across all files in the project',
|
|
||||||
icon: 'i-lucide-search',
|
|
||||||
kbds: ['meta', 'P']
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Settings',
|
|
||||||
suffix: 'Open application settings',
|
|
||||||
icon: 'i-lucide-settings',
|
|
||||||
kbds: ['meta', ',']
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'recent',
|
|
||||||
label: 'Recent',
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
label: 'project.vue',
|
|
||||||
suffix: 'components/',
|
|
||||||
icon: 'i-vscode-icons-file-type-vue'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'readme.md',
|
|
||||||
suffix: 'docs/',
|
|
||||||
icon: 'i-vscode-icons-file-type-markdown'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'package.json',
|
|
||||||
suffix: 'root/',
|
|
||||||
icon: 'i-vscode-icons-file-type-node'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<UCommandPalette :groups="groups" class="flex-1 h-80">
|
|
||||||
<template #footer>
|
|
||||||
<div class="flex items-center justify-between gap-2">
|
|
||||||
<UIcon name="i-simple-icons-nuxtdotjs" class="size-5 text-dimmed ml-1" />
|
|
||||||
<div class="flex items-center gap-1">
|
|
||||||
<UButton color="neutral" variant="ghost" label="Open Command" class="text-dimmed" size="xs">
|
|
||||||
<template #trailing>
|
|
||||||
<UKbd value="enter" />
|
|
||||||
</template>
|
|
||||||
</UButton>
|
|
||||||
<USeparator orientation="vertical" class="h-4" />
|
|
||||||
<UButton color="neutral" variant="ghost" label="Actions" class="text-dimmed" size="xs">
|
|
||||||
<template #trailing>
|
|
||||||
<UKbd value="meta" />
|
|
||||||
<UKbd value="k" />
|
|
||||||
</template>
|
|
||||||
</UButton>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</UCommandPalette>
|
|
||||||
</template>
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
<template>
|
|
||||||
<UDrawer :ui="{ content: 'h-full', overlay: 'bg-inverted/30' }">
|
|
||||||
<UButton label="Open" color="neutral" variant="subtle" trailing-icon="i-lucide-chevron-up" />
|
|
||||||
|
|
||||||
<template #footer>
|
|
||||||
<UDrawer nested :ui="{ content: 'h-full', overlay: 'bg-inverted/30' }">
|
|
||||||
<UButton color="neutral" variant="outline" label="Open nested" />
|
|
||||||
|
|
||||||
<template #content>
|
|
||||||
<Placeholder class="flex-1 m-4" />
|
|
||||||
</template>
|
|
||||||
</UDrawer>
|
|
||||||
</template>
|
|
||||||
</UDrawer>
|
|
||||||
</template>
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
const { data: users } = await useFetch('https://jsonplaceholder.typicode.com/users', {
|
|
||||||
key: 'typicode-users-email',
|
|
||||||
transform: (data: { id: number, name: string, email: string }[]) => {
|
|
||||||
return data?.map(user => ({
|
|
||||||
label: user.name,
|
|
||||||
value: String(user.id),
|
|
||||||
email: user.email,
|
|
||||||
avatar: { src: `https://i.pravatar.cc/120?img=${user.id}` }
|
|
||||||
}))
|
|
||||||
},
|
|
||||||
lazy: true
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<UInputMenu
|
|
||||||
:items="users"
|
|
||||||
icon="i-lucide-user"
|
|
||||||
placeholder="Select user"
|
|
||||||
:ui="{ content: 'min-w-fit' }"
|
|
||||||
>
|
|
||||||
<template #item-label="{ item }">
|
|
||||||
{{ item.label }}
|
|
||||||
|
|
||||||
<span class="text-muted">
|
|
||||||
{{ item.email }}
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
</UInputMenu>
|
|
||||||
</template>
|
|
||||||
@@ -35,7 +35,6 @@ const items = ref([
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
] satisfies InputMenuItem[])
|
] satisfies InputMenuItem[])
|
||||||
|
|
||||||
const value = ref(items.value[0])
|
const value = ref(items.value[0])
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -40,9 +40,9 @@ const text = computed(() => {
|
|||||||
placeholder="Password"
|
placeholder="Password"
|
||||||
:color="color"
|
:color="color"
|
||||||
:type="show ? 'text' : 'password'"
|
:type="show ? 'text' : 'password'"
|
||||||
|
:ui="{ trailing: 'pe-1' }"
|
||||||
:aria-invalid="score < 4"
|
:aria-invalid="score < 4"
|
||||||
aria-describedby="password-strength"
|
aria-describedby="password-strength"
|
||||||
:ui="{ trailing: 'pe-1' }"
|
|
||||||
class="w-full"
|
class="w-full"
|
||||||
>
|
>
|
||||||
<template #trailing>
|
<template #trailing>
|
||||||
|
|||||||
@@ -24,10 +24,3 @@ const password = ref('')
|
|||||||
</template>
|
</template>
|
||||||
</UInput>
|
</UInput>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style>
|
|
||||||
/* Hide the password reveal button in Edge */
|
|
||||||
::-ms-reveal {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -6,12 +6,14 @@ const count = ref(0)
|
|||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
const overlay = useOverlay()
|
const overlay = useOverlay()
|
||||||
|
|
||||||
const modal = overlay.create(LazyModalExample)
|
const modal = overlay.create(LazyModalExample, {
|
||||||
|
props: {
|
||||||
|
count: count.value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
async function open() {
|
async function open() {
|
||||||
const instance = modal.open({
|
const instance = modal.open()
|
||||||
count: count.value
|
|
||||||
})
|
|
||||||
|
|
||||||
const shouldIncrement = await instance.result
|
const shouldIncrement = await instance.result
|
||||||
|
|
||||||
|
|||||||
@@ -62,13 +62,13 @@ const items = [
|
|||||||
<template>
|
<template>
|
||||||
<UNavigationMenu
|
<UNavigationMenu
|
||||||
:items="items"
|
:items="items"
|
||||||
|
class="w-full justify-center"
|
||||||
:ui="{
|
:ui="{
|
||||||
viewport: 'sm:w-(--reka-navigation-menu-viewport-width)',
|
viewport: 'sm:w-(--reka-navigation-menu-viewport-width)',
|
||||||
content: 'sm:w-auto',
|
content: 'sm:w-auto',
|
||||||
childList: 'sm:w-96',
|
childList: 'sm:w-96',
|
||||||
childLinkDescription: 'text-balance line-clamp-2'
|
childLinkDescription: 'text-balance line-clamp-2'
|
||||||
}"
|
}"
|
||||||
class="w-full justify-center"
|
|
||||||
>
|
>
|
||||||
<template #docs-content="{ item }">
|
<template #docs-content="{ item }">
|
||||||
<ul class="grid gap-2 p-4 lg:w-[500px] lg:grid-cols-[minmax(0,.75fr)_minmax(0,1fr)]">
|
<ul class="grid gap-2 p-4 lg:w-[500px] lg:grid-cols-[minmax(0,.75fr)_minmax(0,1fr)]">
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
const { data: users } = await useFetch('https://jsonplaceholder.typicode.com/users', {
|
|
||||||
key: 'typicode-users-email',
|
|
||||||
transform: (data: { id: number, name: string, email: string }[]) => {
|
|
||||||
return data?.map(user => ({
|
|
||||||
label: user.name,
|
|
||||||
value: String(user.id),
|
|
||||||
email: user.email,
|
|
||||||
avatar: { src: `https://i.pravatar.cc/120?img=${user.id}` }
|
|
||||||
}))
|
|
||||||
},
|
|
||||||
lazy: true
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<USelectMenu
|
|
||||||
:items="users"
|
|
||||||
icon="i-lucide-user"
|
|
||||||
placeholder="Select user"
|
|
||||||
:ui="{ content: 'min-w-fit' }"
|
|
||||||
class="w-48"
|
|
||||||
>
|
|
||||||
<template #item-label="{ item }">
|
|
||||||
{{ item.label }}
|
|
||||||
|
|
||||||
<span class="text-muted">
|
|
||||||
{{ item.email }}
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
</USelectMenu>
|
|
||||||
</template>
|
|
||||||
@@ -35,7 +35,6 @@ const items = ref([
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
] satisfies SelectMenuItem[])
|
] satisfies SelectMenuItem[])
|
||||||
|
|
||||||
const value = ref(items.value[0])
|
const value = ref(items.value[0])
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ const items = ref([
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
] satisfies SelectMenuItem[])
|
] satisfies SelectMenuItem[])
|
||||||
|
|
||||||
const value = ref(items.value[0])
|
const value = ref(items.value[0])
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ const items = ref([
|
|||||||
icon: 'i-lucide-circle-check'
|
icon: 'i-lucide-circle-check'
|
||||||
}
|
}
|
||||||
] satisfies SelectMenuItem[])
|
] satisfies SelectMenuItem[])
|
||||||
|
|
||||||
const value = ref(items.value[0])
|
const value = ref(items.value[0])
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -1,35 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
const value = ref<string>()
|
|
||||||
|
|
||||||
const { data: users } = await useFetch('https://jsonplaceholder.typicode.com/users', {
|
|
||||||
key: 'typicode-users-email',
|
|
||||||
transform: (data: { id: number, name: string, email: string }[]) => {
|
|
||||||
return data?.map(user => ({
|
|
||||||
label: user.name,
|
|
||||||
email: user.email,
|
|
||||||
value: String(user.id),
|
|
||||||
avatar: { src: `https://i.pravatar.cc/120?img=${user.id}` }
|
|
||||||
}))
|
|
||||||
},
|
|
||||||
lazy: true
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<USelect
|
|
||||||
v-model="value"
|
|
||||||
:items="users"
|
|
||||||
placeholder="Select user"
|
|
||||||
value-key="value"
|
|
||||||
:ui="{ content: 'min-w-fit' }"
|
|
||||||
class="w-48"
|
|
||||||
>
|
|
||||||
<template #item-label="{ item }">
|
|
||||||
{{ item.label }}
|
|
||||||
|
|
||||||
<span class="text-muted">
|
|
||||||
{{ item.email }}
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
</USelect>
|
|
||||||
</template>
|
|
||||||
@@ -24,8 +24,8 @@ function getUserAvatar(value: string) {
|
|||||||
:loading="status === 'pending'"
|
:loading="status === 'pending'"
|
||||||
icon="i-lucide-user"
|
icon="i-lucide-user"
|
||||||
placeholder="Select user"
|
placeholder="Select user"
|
||||||
value-key="value"
|
|
||||||
class="w-48"
|
class="w-48"
|
||||||
|
value-key="value"
|
||||||
>
|
>
|
||||||
<template #leading="{ modelValue, ui }">
|
<template #leading="{ modelValue, ui }">
|
||||||
<UAvatar
|
<UAvatar
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ const items = ref([
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
] satisfies SelectItem[])
|
] satisfies SelectItem[])
|
||||||
|
|
||||||
const value = ref(items.value[0]?.value)
|
const value = ref(items.value[0]?.value)
|
||||||
|
|
||||||
const avatar = computed(() => items.value.find(item => item.value === value.value)?.avatar)
|
const avatar = computed(() => items.value.find(item => item.value === value.value)?.avatar)
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ const items = ref([
|
|||||||
icon: 'i-lucide-circle-check'
|
icon: 'i-lucide-circle-check'
|
||||||
}
|
}
|
||||||
] satisfies SelectItem[])
|
] satisfies SelectItem[])
|
||||||
|
|
||||||
const value = ref(items.value[0]?.value)
|
const value = ref(items.value[0]?.value)
|
||||||
|
|
||||||
const icon = computed(() => items.value.find(item => item.value === value.value)?.icon)
|
const icon = computed(() => items.value.find(item => item.value === value.value)?.icon)
|
||||||
|
|||||||
@@ -6,12 +6,14 @@ const count = ref(0)
|
|||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
const overlay = useOverlay()
|
const overlay = useOverlay()
|
||||||
|
|
||||||
const slideover = overlay.create(LazySlideoverExample)
|
const slideover = overlay.create(LazySlideoverExample, {
|
||||||
|
props: {
|
||||||
|
count: count.value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
async function open() {
|
async function open() {
|
||||||
const instance = slideover.open({
|
const instance = slideover.open()
|
||||||
count: count.value
|
|
||||||
})
|
|
||||||
|
|
||||||
const shouldIncrement = await instance.result
|
const shouldIncrement = await instance.result
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ const state = reactive({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<UTabs :items="items" variant="link" :ui="{ trigger: 'grow' }" class="gap-4 w-full">
|
<UTabs :items="items" variant="link" class="gap-4 w-full" :ui="{ trigger: 'grow' }">
|
||||||
<template #account="{ item }">
|
<template #account="{ item }">
|
||||||
<p class="text-muted mb-4">
|
<p class="text-muted mb-4">
|
||||||
{{ item.description }}
|
{{ item.description }}
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ const items: TimelineItem[] = [{
|
|||||||
<template>
|
<template>
|
||||||
<UTimeline
|
<UTimeline
|
||||||
:items="items"
|
:items="items"
|
||||||
:default-value="2"
|
|
||||||
:ui="{ item: 'even:flex-row-reverse even:-translate-x-[calc(100%-2rem)] even:text-right' }"
|
:ui="{ item: 'even:flex-row-reverse even:-translate-x-[calc(100%-2rem)] even:text-right' }"
|
||||||
|
:default-value="2"
|
||||||
class="translate-x-[calc(50%-1rem)]"
|
class="translate-x-[calc(50%-1rem)]"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -42,11 +42,11 @@ const items = [{
|
|||||||
<UTimeline
|
<UTimeline
|
||||||
:items="items"
|
:items="items"
|
||||||
size="xs"
|
size="xs"
|
||||||
|
class="w-96"
|
||||||
:ui="{
|
:ui="{
|
||||||
date: 'float-end ms-1',
|
date: 'float-end ms-1',
|
||||||
description: 'px-3 py-2 ring ring-default mt-2 rounded-md text-default'
|
description: 'px-3 py-2 ring ring-default mt-2 rounded-md text-default'
|
||||||
}"
|
}"
|
||||||
class="w-96"
|
|
||||||
>
|
>
|
||||||
<template #title="{ item }">
|
<template #title="{ item }">
|
||||||
<span>{{ item.username }}</span>
|
<span>{{ item.username }}</span>
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
const toast = useToast()
|
|
||||||
|
|
||||||
function showToast() {
|
|
||||||
toast.add({
|
|
||||||
title: 'Uh oh! Something went wrong.',
|
|
||||||
description: 'There was a problem with your request.',
|
|
||||||
icon: 'i-lucide-wifi',
|
|
||||||
progress: false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<UButton label="Show toast" color="neutral" variant="outline" @click="showToast" />
|
|
||||||
</template>
|
|
||||||
@@ -7,12 +7,12 @@ const appConfig = useAppConfig()
|
|||||||
<UFormField
|
<UFormField
|
||||||
label="toaster.duration"
|
label="toaster.duration"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
class="inline-flex ring ring-accented rounded-sm"
|
||||||
:ui="{
|
:ui="{
|
||||||
wrapper: 'bg-elevated/50 rounded-l-sm flex border-r border-accented',
|
wrapper: 'bg-elevated/50 rounded-l-sm flex border-r border-accented',
|
||||||
label: 'text-muted px-2 py-1.5',
|
label: 'text-muted px-2 py-1.5',
|
||||||
container: 'mt-0'
|
container: 'mt-0'
|
||||||
}"
|
}"
|
||||||
class="inline-flex ring ring-accented rounded-sm"
|
|
||||||
>
|
>
|
||||||
<UInput
|
<UInput
|
||||||
v-model="appConfig.toaster.duration"
|
v-model="appConfig.toaster.duration"
|
||||||
|
|||||||
@@ -7,12 +7,12 @@ const appConfig = useAppConfig()
|
|||||||
<UFormField
|
<UFormField
|
||||||
label="toaster.expand"
|
label="toaster.expand"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
class="inline-flex ring ring-accented rounded-sm"
|
||||||
:ui="{
|
:ui="{
|
||||||
wrapper: 'bg-elevated/50 rounded-l-sm flex border-r border-accented',
|
wrapper: 'bg-elevated/50 rounded-l-sm flex border-r border-accented',
|
||||||
label: 'text-muted px-2 py-1.5',
|
label: 'text-muted px-2 py-1.5',
|
||||||
container: 'mt-0'
|
container: 'mt-0'
|
||||||
}"
|
}"
|
||||||
class="inline-flex ring ring-accented rounded-sm"
|
|
||||||
>
|
>
|
||||||
<USelectMenu
|
<USelectMenu
|
||||||
v-model="appConfig.toaster.expand"
|
v-model="appConfig.toaster.expand"
|
||||||
|
|||||||
@@ -10,12 +10,12 @@ const appConfig = useAppConfig()
|
|||||||
<UFormField
|
<UFormField
|
||||||
label="toaster.position"
|
label="toaster.position"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
class="inline-flex ring ring-accented rounded-sm"
|
||||||
:ui="{
|
:ui="{
|
||||||
wrapper: 'bg-elevated/50 rounded-l-sm flex border-r border-accented',
|
wrapper: 'bg-elevated/50 rounded-l-sm flex border-r border-accented',
|
||||||
label: 'text-muted px-2 py-1.5',
|
label: 'text-muted px-2 py-1.5',
|
||||||
container: 'mt-0'
|
container: 'mt-0'
|
||||||
}"
|
}"
|
||||||
class="inline-flex ring ring-accented rounded-sm"
|
|
||||||
>
|
>
|
||||||
<USelectMenu
|
<USelectMenu
|
||||||
v-model="appConfig.toaster.position"
|
v-model="appConfig.toaster.position"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { onMounted, watch } from 'vue'
|
import { onMounted, watch } from 'vue'
|
||||||
import FaviconSvg from '../../public/icon.svg?raw'
|
import FaviconSvg from 'public/icon.svg?raw'
|
||||||
|
|
||||||
export function useFaviconFromTheme() {
|
export function useFaviconFromTheme() {
|
||||||
const colorMode = useColorMode()
|
const colorMode = useColorMode()
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ provide('navigation', mappedNavigation)
|
|||||||
<UApp>
|
<UApp>
|
||||||
<NuxtLoadingIndicator color="#FFF" />
|
<NuxtLoadingIndicator color="#FFF" />
|
||||||
|
|
||||||
<Banner />
|
<!-- <Banner /> -->
|
||||||
|
|
||||||
<Header :links="links" />
|
<Header :links="links" />
|
||||||
|
|
||||||
|
|||||||
@@ -5,17 +5,6 @@ pricing:
|
|||||||
title: Upgrade to Nuxt UI [Pro]{class="text-primary"}.
|
title: Upgrade to Nuxt UI [Pro]{class="text-primary"}.
|
||||||
description: On top of 40+ open source components from Nuxt UI, Pro gives you access to 50+ premium Vue components to create beautiful & responsive Nuxt applications in minutes. It includes all primitives to build landing pages, documentations, blogs, dashboards or entire SaaS products.
|
description: On top of 40+ open source components from Nuxt UI, Pro gives you access to 50+ premium Vue components to create beautiful & responsive Nuxt applications in minutes. It includes all primitives to build landing pages, documentations, blogs, dashboards or entire SaaS products.
|
||||||
freePlan:
|
freePlan:
|
||||||
description: "**NuxtLabs is joining Vercel** :tada: As part of this transition, Nuxt UI is becoming even more accessible.<br><br> **In September, we're launching Nuxt UI v4**: a free, open-source library that unifies Nuxt UI and Nuxt UI Pro, offering 100+ components and a complete free Figma Kit for everyone."
|
|
||||||
orientation: horizontal
|
|
||||||
button:
|
|
||||||
label: Read the announcement
|
|
||||||
to: 'https://nuxtlabs.com/?utm_source=nuxt-ui&utm_medium=banner&utm_campaign=nuxtlabs-vercel'
|
|
||||||
target: _blank
|
|
||||||
color: 'neutral'
|
|
||||||
trailingIcon: 'i-lucide-arrow-right'
|
|
||||||
ui:
|
|
||||||
trailingIcon: 'ms-0'
|
|
||||||
devPlan:
|
|
||||||
title: Free in development
|
title: Free in development
|
||||||
description: Try Nuxt UI Pro for free in development, no credit card required. Upgrade when ready to deploy.
|
description: Try Nuxt UI Pro for free in development, no credit card required. Upgrade when ready to deploy.
|
||||||
orientation: horizontal
|
orientation: horizontal
|
||||||
@@ -24,9 +13,6 @@ pricing:
|
|||||||
to: '/getting-started/installation/pro/nuxt'
|
to: '/getting-started/installation/pro/nuxt'
|
||||||
color: 'neutral'
|
color: 'neutral'
|
||||||
variant: 'subtle'
|
variant: 'subtle'
|
||||||
trailingIcon: 'i-lucide-arrow-right'
|
|
||||||
ui:
|
|
||||||
trailingIcon: 'ms-0'
|
|
||||||
figma:
|
figma:
|
||||||
title: Figma Kit Pro
|
title: Figma Kit Pro
|
||||||
description: Get all Nuxt UI Pro components in a Figma kit to design your next application before coding. Everything you need, from wire-framing to high-fidelity web integration.
|
description: Get all Nuxt UI Pro components in a Figma kit to design your next application before coding. Everything you need, from wire-framing to high-fidelity web integration.
|
||||||
|
|||||||
@@ -34,19 +34,10 @@ useSeoMeta({
|
|||||||
<div class="flex flex-col bg-default gap-8 lg:gap-0">
|
<div class="flex flex-col bg-default gap-8 lg:gap-0">
|
||||||
<UPricingPlan
|
<UPricingPlan
|
||||||
v-bind="page.pricing.freePlan"
|
v-bind="page.pricing.freePlan"
|
||||||
class="lg:rounded-none ring-primary/15 ring-inset -mb-px bg-primary/5 z-[1]"
|
variant="naked"
|
||||||
:ui="{ description: 'mt-0 text-primary' }"
|
class="lg:rounded-none border-x border-default border-t border-b lg:border-b-0"
|
||||||
>
|
|
||||||
<template #description>
|
|
||||||
<MDC :value="page.pricing.freePlan.description" unwrap="p" />
|
|
||||||
</template>
|
|
||||||
</UPricingPlan>
|
|
||||||
|
|
||||||
<UPricingPlan
|
|
||||||
v-bind="page.pricing.devPlan"
|
|
||||||
class="lg:rounded-none ring-inset -mb-px"
|
|
||||||
/>
|
/>
|
||||||
<UPricingPlans compact class="-space-x-px">
|
<UPricingPlans compact>
|
||||||
<UPricingPlan
|
<UPricingPlan
|
||||||
v-for="(plan, index) in page.pricing.plans"
|
v-for="(plan, index) in page.pricing.plans"
|
||||||
:key="index"
|
:key="index"
|
||||||
@@ -56,17 +47,18 @@ useSeoMeta({
|
|||||||
:discount="plan.discount"
|
:discount="plan.discount"
|
||||||
:billing-period="plan.billing_period"
|
:billing-period="plan.billing_period"
|
||||||
:billing-cycle="plan.billing_cycle"
|
:billing-cycle="plan.billing_cycle"
|
||||||
:variant="plan.highlight ? 'subtle' : 'outline'"
|
:variant="plan.highlight ? 'soft' : 'outline'"
|
||||||
class="lg:rounded-none ring-inset -mb-px"
|
:class="['lg:rounded-none', { 'border-2 lg:border lg:border-x-0 border-primary lg:border-default': plan.highlight }]"
|
||||||
:features="plan.features"
|
:features="plan.features"
|
||||||
:button="plan.button"
|
:button="plan.button"
|
||||||
/>
|
/>
|
||||||
</UPricingPlans>
|
</UPricingPlans>
|
||||||
<UPricingPlan
|
<UPricingPlan
|
||||||
v-bind="page.pricing.figma"
|
v-bind="page.pricing.figma"
|
||||||
|
variant="naked"
|
||||||
:billing-period="page.pricing.figma.billing_period"
|
:billing-period="page.pricing.figma.billing_period"
|
||||||
:billing-cycle="page.pricing.figma.billing_cycle"
|
:billing-cycle="page.pricing.figma.billing_cycle"
|
||||||
class="lg:rounded-none ring-inset -mb-px"
|
class="lg:rounded-none border lg:border-y-0 border-default"
|
||||||
>
|
>
|
||||||
<template #features>
|
<template #features>
|
||||||
<li v-for="(feature, index) in page.pricing.figma.features" :key="index" class="flex items-center gap-2 min-w-0">
|
<li v-for="(feature, index) in page.pricing.figma.features" :key="index" class="flex items-center gap-2 min-w-0">
|
||||||
|
|||||||
@@ -225,27 +225,6 @@ export default defineNuxtConfig({
|
|||||||
This option adds the `transition-colors` class on components with hover or active states.
|
This option adds the `transition-colors` class on components with hover or active states.
|
||||||
::
|
::
|
||||||
|
|
||||||
### `theme.defaultVariants` :badge{label="Soon" class="align-text-top"}
|
|
||||||
|
|
||||||
Use the `theme.defaultVariants` option to override the default `color` and `size` variants for components.
|
|
||||||
|
|
||||||
- Default: `{ color: 'primary', size: 'md' }`{lang="ts-type"}
|
|
||||||
|
|
||||||
```ts [nuxt.config.ts]
|
|
||||||
export default defineNuxtConfig({
|
|
||||||
modules: ['@nuxt/ui'],
|
|
||||||
css: ['~/assets/css/main.css'],
|
|
||||||
ui: {
|
|
||||||
theme: {
|
|
||||||
defaultVariants: {
|
|
||||||
color: 'neutral',
|
|
||||||
size: 'sm'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Continuous Releases
|
## Continuous Releases
|
||||||
|
|
||||||
Nuxt UI uses [pkg.pr.new](https://github.com/stackblitz-labs/pkg.pr.new) for continuous preview releases, providing developers with instant access to the latest features and bug fixes without waiting for official releases.
|
Nuxt UI uses [pkg.pr.new](https://github.com/stackblitz-labs/pkg.pr.new) for continuous preview releases, providing developers with instant access to the latest features and bug fixes without waiting for official releases.
|
||||||
|
|||||||
@@ -183,28 +183,7 @@ It's recommended to install the [Tailwind CSS IntelliSense](https://marketplace.
|
|||||||
```
|
```
|
||||||
|
|
||||||
::note{to="/components/app"}
|
::note{to="/components/app"}
|
||||||
The `App` component sets up global config and is required for **Toast**, **Tooltip** and **programmatic overlays**.
|
The `App` component provides global configurations and is required for **Toast**, **Tooltip** components to work as well as **Programmatic Overlays**.
|
||||||
::
|
|
||||||
|
|
||||||
#### Add the `isolate` class to your root container
|
|
||||||
|
|
||||||
```html [index.html]{9}
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<title>Nuxt UI</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="app" class="isolate"></div>
|
|
||||||
<script type="module" src="/src/main.ts"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
```
|
|
||||||
|
|
||||||
::note
|
|
||||||
This ensures styles are scoped to your app and prevents issues with overlays and stacking contexts.
|
|
||||||
::
|
::
|
||||||
|
|
||||||
::
|
::
|
||||||
@@ -354,32 +333,6 @@ export default defineConfig({
|
|||||||
This option adds the `transition-colors` class on components with hover or active states.
|
This option adds the `transition-colors` class on components with hover or active states.
|
||||||
::
|
::
|
||||||
|
|
||||||
### `theme.defaultVariants` :badge{label="Soon" class="align-text-top"}
|
|
||||||
|
|
||||||
Use the `theme.defaultVariants` option to override the default `color` and `size` variants for components.
|
|
||||||
|
|
||||||
- Default: `{ color: 'primary', size: 'md' }`{lang="ts-type"}
|
|
||||||
|
|
||||||
```ts [vite.config.ts]
|
|
||||||
import { defineConfig } from 'vite'
|
|
||||||
import vue from '@vitejs/plugin-vue'
|
|
||||||
import ui from '@nuxt/ui/vite'
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
plugins: [
|
|
||||||
vue(),
|
|
||||||
ui({
|
|
||||||
theme: {
|
|
||||||
defaultVariants: {
|
|
||||||
color: 'neutral',
|
|
||||||
size: 'sm'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
]
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### `inertia`
|
### `inertia`
|
||||||
|
|
||||||
Use the `inertia` option to enable compatibility with [Inertia.js](https://inertiajs.com/).
|
Use the `inertia` option to enable compatibility with [Inertia.js](https://inertiajs.com/).
|
||||||
|
|||||||
@@ -536,33 +536,6 @@ import { ModalExampleComponent } from '#components'
|
|||||||
</script>
|
</script>
|
||||||
```
|
```
|
||||||
|
|
||||||
### Changed form validation
|
|
||||||
|
|
||||||
- The error object property for targeting form fields has been renamed from `path` to `name`:
|
|
||||||
|
|
||||||
```diff
|
|
||||||
<script setup lang="ts">
|
|
||||||
const validate = (state: any): FormError[] => {
|
|
||||||
const errors = []
|
|
||||||
if (!state.email) {
|
|
||||||
errors.push({
|
|
||||||
- path: 'email',
|
|
||||||
+ name: 'email',
|
|
||||||
message: 'Required'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if (!state.password) {
|
|
||||||
errors.push({
|
|
||||||
- path: 'password',
|
|
||||||
+ name: 'password',
|
|
||||||
message: 'Required'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return errors
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
::warning
|
::warning
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ Read more about this in the `@nuxt/icon` documentation.
|
|||||||
|
|
||||||
You can use local SVG files to create a custom Iconify collection.
|
You can use local SVG files to create a custom Iconify collection.
|
||||||
|
|
||||||
For example, place your icons' SVG files under a folder of your choice, for example, `./app/assets/icons`:
|
For example, place your icons' SVG files under a folder of your choice, for example, `./assets/icons`:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
assets/icons
|
assets/icons
|
||||||
@@ -104,7 +104,7 @@ export default defineNuxtConfig({
|
|||||||
icon: {
|
icon: {
|
||||||
customCollections: [{
|
customCollections: [{
|
||||||
prefix: 'custom',
|
prefix: 'custom',
|
||||||
dir: './app/assets/icons'
|
dir: './assets/icons'
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -877,20 +877,6 @@ props:
|
|||||||
This can be useful when using the CommandPalette inside a [`Modal`](/components/modal) for example.
|
This can be useful when using the CommandPalette inside a [`Modal`](/components/modal) for example.
|
||||||
::
|
::
|
||||||
|
|
||||||
### With footer slot :badge{label="Soon" class="align-text-top"}
|
|
||||||
|
|
||||||
Use the `#footer` slot to add custom content at the bottom of the CommandPalette, such as keyboard shortcuts help or additional actions.
|
|
||||||
|
|
||||||
::component-example
|
|
||||||
---
|
|
||||||
collapse: true
|
|
||||||
name: 'command-palette-footer-slot-example'
|
|
||||||
class: '!p-0'
|
|
||||||
props:
|
|
||||||
autofocus: false
|
|
||||||
---
|
|
||||||
::
|
|
||||||
|
|
||||||
### With custom slot
|
### With custom slot
|
||||||
|
|
||||||
Use the `slot` property to customize a specific item or group.
|
Use the `slot` property to customize a specific item or group.
|
||||||
|
|||||||
@@ -328,17 +328,6 @@ name: 'drawer-responsive-example'
|
|||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
### Nested drawers :badge{label="Soon" class="align-text-top"}
|
|
||||||
|
|
||||||
You can nest drawers within each other by using the `nested` prop.
|
|
||||||
|
|
||||||
::component-example
|
|
||||||
---
|
|
||||||
prettier: true
|
|
||||||
name: 'drawer-nested-example'
|
|
||||||
---
|
|
||||||
::
|
|
||||||
|
|
||||||
### With footer slot
|
### With footer slot
|
||||||
|
|
||||||
Use the `#footer` slot to add content after the Drawer's body.
|
Use the `#footer` slot to add content after the Drawer's body.
|
||||||
|
|||||||
@@ -757,33 +757,6 @@ name: 'input-menu-filter-fields-example'
|
|||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
### With full content width
|
|
||||||
|
|
||||||
You can expand the content to the full width of its items by using the `ui.content` key.
|
|
||||||
|
|
||||||
::component-example
|
|
||||||
---
|
|
||||||
name: 'input-menu-content-width-example'
|
|
||||||
collapse: true
|
|
||||||
---
|
|
||||||
::
|
|
||||||
|
|
||||||
::tip
|
|
||||||
You can also change the content width globally in your `app.config.ts`:
|
|
||||||
|
|
||||||
```
|
|
||||||
export default defineAppConfig({
|
|
||||||
ui: {
|
|
||||||
inputMenu: {
|
|
||||||
slots: {
|
|
||||||
content: 'min-w-fit'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
::
|
|
||||||
|
|
||||||
### As a CountryPicker
|
### As a CountryPicker
|
||||||
|
|
||||||
This example demonstrates using the InputMenu as a country picker with lazy loading - countries are only fetched when the menu is opened.
|
This example demonstrates using the InputMenu as a country picker with lazy loading - countries are only fetched when the menu is opened.
|
||||||
|
|||||||
@@ -62,19 +62,6 @@ items:
|
|||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
### Color :badge{label="Soon" class="align-text-top"}
|
|
||||||
|
|
||||||
Use the `color` prop to change the color of the Kbd.
|
|
||||||
|
|
||||||
::component-code
|
|
||||||
---
|
|
||||||
props:
|
|
||||||
color: neutral
|
|
||||||
slots:
|
|
||||||
default: K
|
|
||||||
---
|
|
||||||
::
|
|
||||||
|
|
||||||
### Variant
|
### Variant
|
||||||
|
|
||||||
Use the `variant` prop to change the variant of the Kbd.
|
Use the `variant` prop to change the variant of the Kbd.
|
||||||
@@ -82,7 +69,6 @@ Use the `variant` prop to change the variant of the Kbd.
|
|||||||
::component-code
|
::component-code
|
||||||
---
|
---
|
||||||
props:
|
props:
|
||||||
color: neutral
|
|
||||||
variant: solid
|
variant: solid
|
||||||
slots:
|
slots:
|
||||||
default: K
|
default: K
|
||||||
|
|||||||
@@ -790,33 +790,6 @@ name: 'select-menu-filter-fields-example'
|
|||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
### With full content width
|
|
||||||
|
|
||||||
You can expand the content to the full width of its items by using the `ui.content` key.
|
|
||||||
|
|
||||||
::component-example
|
|
||||||
---
|
|
||||||
name: 'select-menu-content-width-example'
|
|
||||||
collapse: true
|
|
||||||
---
|
|
||||||
::
|
|
||||||
|
|
||||||
::tip
|
|
||||||
You can also change the content width globally in your `app.config.ts`:
|
|
||||||
|
|
||||||
```
|
|
||||||
export default defineAppConfig({
|
|
||||||
ui: {
|
|
||||||
selectMenu: {
|
|
||||||
slots: {
|
|
||||||
content: 'min-w-fit'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
::
|
|
||||||
|
|
||||||
### As a CountryPicker
|
### As a CountryPicker
|
||||||
|
|
||||||
This example demonstrates using the SelectMenu as a country picker with lazy loading - countries are only fetched when the menu is opened.
|
This example demonstrates using the SelectMenu as a country picker with lazy loading - countries are only fetched when the menu is opened.
|
||||||
@@ -828,8 +801,6 @@ name: 'select-menu-countries-example'
|
|||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## API
|
## API
|
||||||
|
|
||||||
### Props
|
### Props
|
||||||
|
|||||||
@@ -695,33 +695,6 @@ collapse: true
|
|||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
### With full content width
|
|
||||||
|
|
||||||
You can expand the content to the full width of its items by using the `ui.content` key.
|
|
||||||
|
|
||||||
::component-example
|
|
||||||
---
|
|
||||||
name: 'select-content-width-example'
|
|
||||||
collapse: true
|
|
||||||
---
|
|
||||||
::
|
|
||||||
|
|
||||||
::tip
|
|
||||||
You can also change the content width globally in your `app.config.ts`:
|
|
||||||
|
|
||||||
```
|
|
||||||
export default defineAppConfig({
|
|
||||||
ui: {
|
|
||||||
select: {
|
|
||||||
slots: {
|
|
||||||
content: 'min-w-fit'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
::
|
|
||||||
|
|
||||||
## API
|
## API
|
||||||
|
|
||||||
### Props
|
### Props
|
||||||
|
|||||||
@@ -83,9 +83,6 @@ Use the `columns` prop as an array of [ColumnDef](https://tanstack.com/table/lat
|
|||||||
- `class`:
|
- `class`:
|
||||||
- `td`: [The classes to apply to the `td` element.]{class="text-muted"}
|
- `td`: [The classes to apply to the `td` element.]{class="text-muted"}
|
||||||
- `th`: [The classes to apply to the `th` element.]{class="text-muted"}
|
- `th`: [The classes to apply to the `th` element.]{class="text-muted"}
|
||||||
- `style`:
|
|
||||||
- `td`: [The style to apply to the `td` element.]{class="text-muted"}
|
|
||||||
- `th`: [The style to apply to the `th` element.]{class="text-muted"}
|
|
||||||
|
|
||||||
In order to render components or other HTML elements, you will need to use the Vue [`h` function](https://vuejs.org/api/render-function.html#h) inside the `header` and `cell` props. This is different from other components that use slots but allows for more flexibility.
|
In order to render components or other HTML elements, you will need to use the Vue [`h` function](https://vuejs.org/api/render-function.html#h) inside the `header` and `cell` props. This is different from other components that use slots but allows for more flexibility.
|
||||||
|
|
||||||
@@ -115,8 +112,6 @@ Use the `meta` prop as an object ([TableMeta](https://tanstack.com/table/latest/
|
|||||||
|
|
||||||
- `class`:
|
- `class`:
|
||||||
- `tr`: [The classes to apply to the `tr` element.]{class="text-muted"}
|
- `tr`: [The classes to apply to the `tr` element.]{class="text-muted"}
|
||||||
- `style`:
|
|
||||||
- `tr`: [The style to apply to the `tr` element.]{class="text-muted"}
|
|
||||||
|
|
||||||
### Loading
|
### Loading
|
||||||
|
|
||||||
|
|||||||
@@ -19,13 +19,12 @@ Use the `items` prop as an array of objects with the following properties:
|
|||||||
- `label?: string`{lang="ts-type"}
|
- `label?: string`{lang="ts-type"}
|
||||||
- `icon?: string`{lang="ts-type"}
|
- `icon?: string`{lang="ts-type"}
|
||||||
- `avatar?: AvatarProps`{lang="ts-type"}
|
- `avatar?: AvatarProps`{lang="ts-type"}
|
||||||
- `badge?: string | number | BadgeProps`{lang="ts-type"}
|
|
||||||
- `content?: string`{lang="ts-type"}
|
- `content?: string`{lang="ts-type"}
|
||||||
- `value?: string | number`{lang="ts-type"}
|
- `value?: string | number`{lang="ts-type"}
|
||||||
- `disabled?: boolean`{lang="ts-type"}
|
- `disabled?: boolean`{lang="ts-type"}
|
||||||
- [`slot?: string`{lang="ts-type"}](#with-custom-slot)
|
- [`slot?: string`{lang="ts-type"}](#with-custom-slot)
|
||||||
- `class?: any`{lang="ts-type"}
|
- `class?: any`{lang="ts-type"}
|
||||||
- `ui?: { trigger?: ClassNameValue, leadingIcon?: ClassNameValue, leadingAvatar?: ClassNameValue, leadingAvatarSize?: ClassNameValue, label?: ClassNameValue, trailingBadge?: ClassNameValue, trailingBadgeSize?: ClassNameValue, content?: ClassNameValue }`{lang="ts-type"}
|
- `ui?: { trigger?: ClassNameValue, leadingIcon?: ClassNameValue, leadingAvatar?: ClassNameValue, label?: ClassNameValue, content?: ClassNameValue }`{lang="ts-type"}
|
||||||
|
|
||||||
::component-code
|
::component-code
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ name: 'toast-color-example'
|
|||||||
|
|
||||||
### Close
|
### Close
|
||||||
|
|
||||||
Pass a `close` field to customize or hide the close [Button](/components/button) (with `false` value).
|
Pass a `close` field to customize or hide the close button (with `false` value).
|
||||||
|
|
||||||
::component-example
|
::component-example
|
||||||
---
|
---
|
||||||
@@ -143,7 +143,7 @@ You can customize this icon globally in your `vite.config.ts` under `ui.icons.cl
|
|||||||
|
|
||||||
### Actions
|
### Actions
|
||||||
|
|
||||||
Pass an `actions` field to add some [Button](/components/button) actions to the Toast.
|
Pass an `actions` field to add some [Button](/components/button) actions to the Alert.
|
||||||
|
|
||||||
::component-example
|
::component-example
|
||||||
---
|
---
|
||||||
@@ -155,23 +155,9 @@ name: 'toast-actions-example'
|
|||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
### Progress :badge{label="Soon" class="align-text-top"}
|
|
||||||
|
|
||||||
Pass a `progress` field to customize or hide the [Progress](/components/progress) bar (with `false` value).
|
|
||||||
|
|
||||||
::tip
|
|
||||||
The Progress bar inherits the Toast color by default, but you can override it using the `progress.color` field.
|
|
||||||
::
|
|
||||||
|
|
||||||
::component-example
|
|
||||||
---
|
|
||||||
name: 'toast-progress-example'
|
|
||||||
---
|
|
||||||
::
|
|
||||||
|
|
||||||
### Orientation
|
### Orientation
|
||||||
|
|
||||||
Pass an `orientation` field to the `toast.add` method to change the orientation of the Toast.
|
Use the `orientation` prop to change the orientation of the Toast.
|
||||||
|
|
||||||
::component-example
|
::component-example
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -51,5 +51,3 @@ items:
|
|||||||
url: https://wiredash.com/
|
url: https://wiredash.com/
|
||||||
- name: Zielgestalt
|
- name: Zielgestalt
|
||||||
url: https://zielgestalt.de/
|
url: https://zielgestalt.de/
|
||||||
- name: Arthur Danjou's Porfolio
|
|
||||||
url: https://arthurdanjou.fr/
|
|
||||||
|
|||||||
@@ -143,6 +143,10 @@ export default defineNuxtConfig({
|
|||||||
'/releases': { redirect: 'https://github.com/nuxt/ui/releases', prerender: false }
|
'/releases': { redirect: 'https://github.com/nuxt/ui/releases', prerender: false }
|
||||||
},
|
},
|
||||||
|
|
||||||
|
future: {
|
||||||
|
compatibilityVersion: 4
|
||||||
|
},
|
||||||
|
|
||||||
compatibilityDate: '2024-07-09',
|
compatibilityDate: '2024-07-09',
|
||||||
|
|
||||||
nitro: {
|
nitro: {
|
||||||
|
|||||||
@@ -11,26 +11,26 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ai-sdk/vue": "^1.2.12",
|
"@ai-sdk/vue": "^1.2.12",
|
||||||
"@iconify-json/logos": "^1.2.4",
|
"@iconify-json/logos": "^1.2.4",
|
||||||
"@iconify-json/lucide": "^1.2.57",
|
"@iconify-json/lucide": "^1.2.56",
|
||||||
"@iconify-json/simple-icons": "^1.2.44",
|
"@iconify-json/simple-icons": "^1.2.42",
|
||||||
"@iconify-json/vscode-icons": "^1.2.23",
|
"@iconify-json/vscode-icons": "^1.2.23",
|
||||||
"@nuxt/content": "^3.6.3",
|
"@nuxt/content": "^3.6.3",
|
||||||
"@nuxt/image": "^1.10.0",
|
"@nuxt/image": "^1.10.0",
|
||||||
"@nuxt/ui": "workspace:*",
|
"@nuxt/ui": "workspace:*",
|
||||||
"@nuxt/ui-pro": "https://pkg.pr.new/@nuxt/ui-pro@17684e4",
|
"@nuxt/ui-pro": "https://pkg.pr.new/@nuxt/ui-pro@22fdc5e",
|
||||||
"@nuxthub/core": "^0.9.0",
|
"@nuxthub/core": "^0.9.0",
|
||||||
"@nuxtjs/plausible": "^1.2.0",
|
"@nuxtjs/plausible": "^1.2.0",
|
||||||
"@octokit/rest": "^22.0.0",
|
"@octokit/rest": "^22.0.0",
|
||||||
"@rollup/plugin-yaml": "^4.1.2",
|
"@rollup/plugin-yaml": "^4.1.2",
|
||||||
"@vueuse/integrations": "^13.5.0",
|
"@vueuse/integrations": "^13.5.0",
|
||||||
"@vueuse/nuxt": "^13.5.0",
|
"@vueuse/nuxt": "^13.5.0",
|
||||||
"ai": "^4.3.19",
|
"ai": "^4.3.16",
|
||||||
"better-sqlite3": "^12.2.0",
|
"better-sqlite3": "^12.2.0",
|
||||||
"capture-website": "^4.2.0",
|
"capture-website": "^4.2.0",
|
||||||
"joi": "^17.13.3",
|
"joi": "^17.13.3",
|
||||||
"maska": "^3.2.0",
|
"maska": "^3.2.0",
|
||||||
"motion-v": "^1.5.0",
|
"motion-v": "^1.5.0",
|
||||||
"nuxt": "^4.0.1",
|
"nuxt": "^3.17.6",
|
||||||
"nuxt-component-meta": "^0.12.1",
|
"nuxt-component-meta": "^0.12.1",
|
||||||
"nuxt-llms": "^0.1.3",
|
"nuxt-llms": "^0.1.3",
|
||||||
"nuxt-og-image": "^5.1.9",
|
"nuxt-og-image": "^5.1.9",
|
||||||
@@ -40,11 +40,11 @@
|
|||||||
"superstruct": "^2.0.2",
|
"superstruct": "^2.0.2",
|
||||||
"ufo": "^1.6.1",
|
"ufo": "^1.6.1",
|
||||||
"valibot": "^1.1.0",
|
"valibot": "^1.1.0",
|
||||||
"workers-ai-provider": "^0.7.2",
|
"workers-ai-provider": "^0.7.1",
|
||||||
"yup": "^1.6.1",
|
"yup": "^1.6.1",
|
||||||
"zod": "^4.0.5"
|
"zod": "^3.25.75"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"wrangler": "^4.25.0"
|
"wrangler": "^4.23.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
16
package.json
16
package.json
@@ -2,7 +2,7 @@
|
|||||||
"name": "@nuxt/ui",
|
"name": "@nuxt/ui",
|
||||||
"description": "A UI Library for Modern Web Apps, powered by Vue & Tailwind CSS.",
|
"description": "A UI Library for Modern Web Apps, powered by Vue & Tailwind CSS.",
|
||||||
"version": "3.2.0",
|
"version": "3.2.0",
|
||||||
"packageManager": "pnpm@10.13.1",
|
"packageManager": "pnpm@10.12.4",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/nuxt/ui.git"
|
"url": "git+https://github.com/nuxt/ui.git"
|
||||||
@@ -116,8 +116,8 @@
|
|||||||
"@internationalized/number": "^3.6.3",
|
"@internationalized/number": "^3.6.3",
|
||||||
"@nuxt/fonts": "^0.11.4",
|
"@nuxt/fonts": "^0.11.4",
|
||||||
"@nuxt/icon": "^1.15.0",
|
"@nuxt/icon": "^1.15.0",
|
||||||
"@nuxt/kit": "^4.0.1",
|
"@nuxt/kit": "^3.17.6",
|
||||||
"@nuxt/schema": "^4.0.1",
|
"@nuxt/schema": "^3.17.6",
|
||||||
"@nuxtjs/color-mode": "^3.5.2",
|
"@nuxtjs/color-mode": "^3.5.2",
|
||||||
"@standard-schema/spec": "^1.0.0",
|
"@standard-schema/spec": "^1.0.0",
|
||||||
"@tailwindcss/postcss": "^4.1.11",
|
"@tailwindcss/postcss": "^4.1.11",
|
||||||
@@ -155,16 +155,16 @@
|
|||||||
"vue-component-type-helpers": "^3.0.1"
|
"vue-component-type-helpers": "^3.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@nuxt/eslint-config": "^1.6.0",
|
"@nuxt/eslint-config": "^1.5.2",
|
||||||
"@nuxt/module-builder": "^1.0.1",
|
"@nuxt/module-builder": "^1.0.1",
|
||||||
"@nuxt/test-utils": "^3.19.2",
|
"@nuxt/test-utils": "^3.19.2",
|
||||||
"@release-it/conventional-changelog": "^10.0.1",
|
"@release-it/conventional-changelog": "^10.0.1",
|
||||||
"@vue/test-utils": "^2.4.6",
|
"@vue/test-utils": "^2.4.6",
|
||||||
"embla-carousel": "^8.6.0",
|
"embla-carousel": "^8.6.0",
|
||||||
"eslint": "^9.31.0",
|
"eslint": "^9.30.1",
|
||||||
"happy-dom": "^18.0.1",
|
"happy-dom": "^18.0.1",
|
||||||
"nuxt": "^4.0.1",
|
"nuxt": "^3.17.6",
|
||||||
"release-it": "^19.0.4",
|
"release-it": "^19.0.3",
|
||||||
"vitest": "^3.2.4",
|
"vitest": "^3.2.4",
|
||||||
"vitest-environment-nuxt": "^1.0.1",
|
"vitest-environment-nuxt": "^1.0.1",
|
||||||
"vue-tsc": "^3.0.1"
|
"vue-tsc": "^3.0.1"
|
||||||
@@ -177,7 +177,7 @@
|
|||||||
"valibot": "^1.0.0",
|
"valibot": "^1.0.0",
|
||||||
"vue-router": "^4.5.0",
|
"vue-router": "^4.5.0",
|
||||||
"yup": "^1.6.0",
|
"yup": "^1.6.0",
|
||||||
"zod": "^3.24.0 || ^4.0.0"
|
"zod": "^3.24.0"
|
||||||
},
|
},
|
||||||
"peerDependenciesMeta": {
|
"peerDependenciesMeta": {
|
||||||
"@inertiajs/vue3": {
|
"@inertiajs/vue3": {
|
||||||
|
|||||||
@@ -13,12 +13,12 @@
|
|||||||
"@nuxt/ui": "workspace:*",
|
"@nuxt/ui": "workspace:*",
|
||||||
"vue": "^3.5.17",
|
"vue": "^3.5.17",
|
||||||
"vue-router": "^4.5.1",
|
"vue-router": "^4.5.1",
|
||||||
"zod": "^4.0.5"
|
"zod": "^3.25.75"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vitejs/plugin-vue": "^6.0.0",
|
"@vitejs/plugin-vue": "^5.2.4",
|
||||||
"typescript": "^5.8.3",
|
"typescript": "^5.8.3",
|
||||||
"vite": "^7.0.5",
|
"vite": "^6.3.5",
|
||||||
"vue-tsc": "^3.0.1"
|
"vue-tsc": "^3.0.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,12 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const colorHex = ref('#9C27B0')
|
const colorHex = ref('#9C27B0')
|
||||||
|
|
||||||
function handleColorChange(event: Event) {
|
|
||||||
colorHex.value = (event.target as HTMLInputElement).value
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col gap-5">
|
<div class="flex flex-col gap-5">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<span :style="{ backgroundColor: colorHex }" class="inline-flex w-5 h-5 rounded" />
|
<span :style="{ backgroundColor: colorHex }" class="inline-flex w-5 h-5 rounded" />
|
||||||
<UInput :model-value="colorHex" @change="handleColorChange" />
|
<code class="font-mono">{{ colorHex }}</code>
|
||||||
</div>
|
</div>
|
||||||
<USeparator />
|
<USeparator />
|
||||||
<div class="flex justify-between gap-2">
|
<div class="flex justify-between gap-2">
|
||||||
@@ -25,6 +21,6 @@ function handleColorChange(event: Event) {
|
|||||||
</UButton>
|
</UButton>
|
||||||
</div>
|
</div>
|
||||||
<USeparator />
|
<USeparator />
|
||||||
<UColorPicker v-model="colorHex" />
|
<UColorPicker v-model="colorHex" @update:model-value="() => console.log('model update')" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -10,8 +10,9 @@ const open = ref(false)
|
|||||||
const searchTerm = ref('')
|
const searchTerm = ref('')
|
||||||
// const searchTermDebounced = refDebounced(searchTerm, 200)
|
// const searchTermDebounced = refDebounced(searchTerm, 200)
|
||||||
const selected = ref([])
|
const selected = ref([])
|
||||||
|
const commandPalette = useTemplateRef('commandPalette')
|
||||||
|
|
||||||
const { data: users, status } = await useFetch('https://jsonplaceholder.typicode.com/users', {
|
const { data: _users, status } = await useFetch('https://jsonplaceholder.typicode.com/users', {
|
||||||
// params: { q: searchTermDebounced },
|
// params: { q: searchTermDebounced },
|
||||||
transform: (data: User[]) => {
|
transform: (data: User[]) => {
|
||||||
return data?.map(user => ({ id: user.id, label: user.name, suffix: user.email, avatar: { src: `https://i.pravatar.cc/120?img=${user.id}` } })) || []
|
return data?.map(user => ({ id: user.id, label: user.name, suffix: user.email, avatar: { src: `https://i.pravatar.cc/120?img=${user.id}` } })) || []
|
||||||
@@ -22,10 +23,6 @@ const { data: users, status } = await useFetch('https://jsonplaceholder.typicode
|
|||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
|
|
||||||
const groups = computed(() => [{
|
const groups = computed(() => [{
|
||||||
id: 'users',
|
|
||||||
label: searchTerm.value ? `Users matching “${searchTerm.value}”...` : 'Users',
|
|
||||||
items: users.value || []
|
|
||||||
}, {
|
|
||||||
id: 'actions',
|
id: 'actions',
|
||||||
items: [{
|
items: [{
|
||||||
label: 'Add new file',
|
label: 'Add new file',
|
||||||
@@ -74,6 +71,12 @@ const groups = computed(() => [{
|
|||||||
toast.add({ title: 'Label added!' })
|
toast.add({ title: 'Label added!' })
|
||||||
},
|
},
|
||||||
kbds: ['meta', 'L']
|
kbds: ['meta', 'L']
|
||||||
|
}, {
|
||||||
|
label: 'Set Wallpaper',
|
||||||
|
suffix: 'Choose from beautiful wallpaper collection.',
|
||||||
|
icon: 'i-lucide-image',
|
||||||
|
view: 'wallpaper',
|
||||||
|
placeholder: 'Search wallpapers...'
|
||||||
}, {
|
}, {
|
||||||
label: 'More actions',
|
label: 'More actions',
|
||||||
placeholder: 'Search actions...',
|
placeholder: 'Search actions...',
|
||||||
@@ -140,6 +143,116 @@ const labels = [{
|
|||||||
}]
|
}]
|
||||||
const label = ref()
|
const label = ref()
|
||||||
|
|
||||||
|
const wallpapers = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: 'red_distortion_1',
|
||||||
|
gradient: 'from-red-500 via-orange-500 to-pink-500',
|
||||||
|
category: 'Abstract',
|
||||||
|
featured: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
name: 'blue_distortion_1',
|
||||||
|
gradient: 'from-blue-600 via-purple-600 to-indigo-600',
|
||||||
|
category: 'Abstract',
|
||||||
|
featured: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
name: 'mono_dark_distortion_1',
|
||||||
|
gradient: 'from-gray-900 via-gray-700 to-gray-800',
|
||||||
|
category: 'Monochrome',
|
||||||
|
featured: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
name: 'chromatic_dark_1',
|
||||||
|
gradient: 'from-emerald-600 via-teal-600 to-cyan-600',
|
||||||
|
category: 'Chromatic',
|
||||||
|
featured: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 5,
|
||||||
|
name: 'red_distortion_2',
|
||||||
|
gradient: 'from-rose-600 via-red-600 to-orange-600',
|
||||||
|
category: 'Abstract',
|
||||||
|
featured: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 6,
|
||||||
|
name: 'purple_cosmic_1',
|
||||||
|
gradient: 'from-violet-700 via-purple-700 to-fuchsia-700',
|
||||||
|
category: 'Cosmic',
|
||||||
|
featured: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 7,
|
||||||
|
name: 'golden_sunset_1',
|
||||||
|
gradient: 'from-yellow-500 via-orange-500 to-red-500',
|
||||||
|
category: 'Nature',
|
||||||
|
featured: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 8,
|
||||||
|
name: 'ocean_deep_1',
|
||||||
|
gradient: 'from-blue-800 via-blue-900 to-indigo-900',
|
||||||
|
category: 'Nature',
|
||||||
|
featured: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 9,
|
||||||
|
name: 'mono_light_distortion_1',
|
||||||
|
gradient: 'from-gray-200 via-gray-300 to-gray-400',
|
||||||
|
category: 'Monochrome',
|
||||||
|
featured: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 10,
|
||||||
|
name: 'green_matrix_1',
|
||||||
|
gradient: 'from-green-800 via-emerald-700 to-teal-700',
|
||||||
|
category: 'Chromatic',
|
||||||
|
featured: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 11,
|
||||||
|
name: 'pink_dreams_1',
|
||||||
|
gradient: 'from-pink-500 via-rose-500 to-purple-500',
|
||||||
|
category: 'Abstract',
|
||||||
|
featured: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 12,
|
||||||
|
name: 'midnight_blue_1',
|
||||||
|
gradient: 'from-slate-900 via-blue-900 to-indigo-900',
|
||||||
|
category: 'Nature',
|
||||||
|
featured: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const filteredWallpapers = computed(() => {
|
||||||
|
let filtered = wallpapers
|
||||||
|
|
||||||
|
// Filter by search term
|
||||||
|
if (searchTerm.value.trim()) {
|
||||||
|
const search = searchTerm.value.toLowerCase()
|
||||||
|
filtered = filtered.filter(w =>
|
||||||
|
w.name.toLowerCase().includes(search)
|
||||||
|
|| w.category.toLowerCase().includes(search)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return filtered
|
||||||
|
})
|
||||||
|
|
||||||
|
function setWallpaper(wallpaper: any) {
|
||||||
|
toast.add({
|
||||||
|
title: `Wallpaper set to ${wallpaper.name}!`,
|
||||||
|
description: 'Your desktop wallpaper has been updated.',
|
||||||
|
icon: 'i-lucide-image'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// function onSelect(item: typeof groups.value[number]['items'][number]) {
|
// function onSelect(item: typeof groups.value[number]['items'][number]) {
|
||||||
function onSelect(item: any) {
|
function onSelect(item: any) {
|
||||||
console.log('Selected', item)
|
console.log('Selected', item)
|
||||||
@@ -147,6 +260,12 @@ function onSelect(item: any) {
|
|||||||
|
|
||||||
defineShortcuts({
|
defineShortcuts({
|
||||||
meta_k: () => open.value = !open.value,
|
meta_k: () => open.value = !open.value,
|
||||||
|
meta_shift_a: {
|
||||||
|
usingInput: true,
|
||||||
|
handler: () => {
|
||||||
|
commandPalette.value?.openView('askAI')
|
||||||
|
}
|
||||||
|
},
|
||||||
...extractShortcuts(groups.value)
|
...extractShortcuts(groups.value)
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
@@ -154,6 +273,7 @@ defineShortcuts({
|
|||||||
<template>
|
<template>
|
||||||
<DefineTemplate>
|
<DefineTemplate>
|
||||||
<UCommandPalette
|
<UCommandPalette
|
||||||
|
ref="commandPalette"
|
||||||
v-model="selected"
|
v-model="selected"
|
||||||
v-model:search-term="searchTerm"
|
v-model:search-term="searchTerm"
|
||||||
:loading="status === 'pending'"
|
:loading="status === 'pending'"
|
||||||
@@ -167,25 +287,49 @@ defineShortcuts({
|
|||||||
class="sm:max-h-80"
|
class="sm:max-h-80"
|
||||||
@update:model-value="onSelect"
|
@update:model-value="onSelect"
|
||||||
>
|
>
|
||||||
<template #footer>
|
<template #wallpaper>
|
||||||
<div class="flex items-center justify-between gap-2">
|
<div class="flex-1 overflow-y-auto p-6">
|
||||||
<UIcon name="i-simple-icons-nuxtdotjs" class="size-5 text-dimmed ml-1" />
|
<div class="grid grid-cols-4 gap-4">
|
||||||
<div class="flex items-center gap-1">
|
<div
|
||||||
<UButton color="neutral" variant="ghost" label="Open Command" class="text-dimmed" size="xs">
|
v-for="wallpaper in filteredWallpapers"
|
||||||
<template #trailing>
|
:key="wallpaper.id"
|
||||||
<UKbd value="enter" />
|
class="group relative cursor-pointer"
|
||||||
</template>
|
@click="setWallpaper(wallpaper)"
|
||||||
</UButton>
|
>
|
||||||
<USeparator orientation="vertical" class="h-4" />
|
<div
|
||||||
<UButton color="neutral" variant="ghost" label="Actions" class="text-dimmed" size="xs">
|
class="aspect-video rounded-lg bg-gradient-to-br shadow-lg ring-1 ring-black/5"
|
||||||
<template #trailing>
|
:class="wallpaper.gradient"
|
||||||
<UKbd value="meta" />
|
/>
|
||||||
<UKbd value="k" />
|
<div class="mt-2 px-1">
|
||||||
</template>
|
<div class="flex items-center gap-2">
|
||||||
</UButton>
|
<h3 class="text-sm font-medium text-highlighted truncate">
|
||||||
|
{{ wallpaper.name }}
|
||||||
|
</h3>
|
||||||
|
<UChip
|
||||||
|
v-if="wallpaper.featured"
|
||||||
|
label="★"
|
||||||
|
size="xs"
|
||||||
|
color="primary"
|
||||||
|
class="shrink-0"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<p class="text-xs text-dimmed">
|
||||||
|
{{ wallpaper.category }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<template #askAI>
|
||||||
|
<div class="flex flex-col items-center justify-center gap-4 p-6">
|
||||||
|
<UIcon name="i-lucide-sparkles" class="size-8 text-primary" />
|
||||||
|
<span class="text-lg font-semibold text-highlighted">
|
||||||
|
Ask me anything...
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
</UCommandPalette>
|
</UCommandPalette>
|
||||||
</DefineTemplate>
|
</DefineTemplate>
|
||||||
|
|
||||||
|
|||||||
@@ -28,20 +28,6 @@ const inset = ref(false)
|
|||||||
</template>
|
</template>
|
||||||
</UDrawer>
|
</UDrawer>
|
||||||
|
|
||||||
<UDrawer title="Drawer with nested" :inset="inset" :ui="{ content: 'h-full' }" should-scale-background>
|
|
||||||
<UButton color="neutral" variant="outline" label="Open nested" />
|
|
||||||
|
|
||||||
<template #footer>
|
|
||||||
<UDrawer :inset="inset" nested :ui="{ content: 'h-full' }">
|
|
||||||
<UButton color="neutral" variant="outline" label="Open nested" />
|
|
||||||
|
|
||||||
<template #content>
|
|
||||||
<Placeholder class="flex-1 m-4" />
|
|
||||||
</template>
|
|
||||||
</UDrawer>
|
|
||||||
</template>
|
|
||||||
</UDrawer>
|
|
||||||
|
|
||||||
<UDrawer title="Drawer with bottom direction" direction="bottom" :inset="inset">
|
<UDrawer title="Drawer with bottom direction" direction="bottom" :inset="inset">
|
||||||
<UButton color="neutral" variant="outline" label="Open on bottom" />
|
<UButton color="neutral" variant="outline" label="Open on bottom" />
|
||||||
|
|
||||||
|
|||||||
@@ -3,16 +3,20 @@ import theme from '#build/ui/kbd'
|
|||||||
import { kbdKeysMap } from '@nuxt/ui/composables/useKbd.js'
|
import { kbdKeysMap } from '@nuxt/ui/composables/useKbd.js'
|
||||||
|
|
||||||
const sizes = Object.keys(theme.variants.size) as Array<keyof typeof theme.variants.size>
|
const sizes = Object.keys(theme.variants.size) as Array<keyof typeof theme.variants.size>
|
||||||
const variants = Object.keys(theme.variants.variant) as Array<keyof typeof theme.variants.variant>
|
|
||||||
const colors = Object.keys(theme.variants.color) as Array<keyof typeof theme.variants.color>
|
|
||||||
|
|
||||||
const kbdKeys = Object.keys(kbdKeysMap)
|
const kbdKeys = Object.keys(kbdKeysMap)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2">
|
||||||
<div v-for="color in colors" :key="color" class="flex items-center gap-1 ms-[-22px]">
|
<div class="flex items-center gap-1">
|
||||||
<UKbd v-for="variant in variants" :key="`${color}-${variant}`" value="meta" :variant="variant" :color="color" />
|
<UKbd value="meta" />
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-1">
|
||||||
|
<UKbd value="meta" variant="subtle" />
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-1">
|
||||||
|
<UKbd value="meta" variant="solid" />
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-1 ms-[-220px]">
|
<div class="flex items-center gap-1 ms-[-220px]">
|
||||||
<UKbd v-for="(kdbKey, index) in kbdKeys" :key="index" :value="kdbKey" />
|
<UKbd v-for="(kdbKey, index) in kbdKeys" :key="index" :value="kdbKey" />
|
||||||
|
|||||||
@@ -25,8 +25,7 @@ const items = [{
|
|||||||
label: 'Tab3',
|
label: 'Tab3',
|
||||||
icon: 'i-lucide-bell',
|
icon: 'i-lucide-bell',
|
||||||
content: 'Finally, this is the content for Tab3',
|
content: 'Finally, this is the content for Tab3',
|
||||||
slot: 'custom' as const,
|
slot: 'custom' as const
|
||||||
badge: '300'
|
|
||||||
}]
|
}]
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,10 @@ export default defineNuxtConfig({
|
|||||||
|
|
||||||
css: ['~/assets/css/main.css'],
|
css: ['~/assets/css/main.css'],
|
||||||
|
|
||||||
|
future: {
|
||||||
|
compatibilityVersion: 4
|
||||||
|
},
|
||||||
|
|
||||||
compatibilityDate: '2024-07-09',
|
compatibilityDate: '2024-07-09',
|
||||||
|
|
||||||
vite: {
|
vite: {
|
||||||
|
|||||||
@@ -9,13 +9,13 @@
|
|||||||
"typecheck": "nuxt typecheck"
|
"typecheck": "nuxt typecheck"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@iconify-json/lucide": "^1.2.57",
|
"@iconify-json/lucide": "^1.2.56",
|
||||||
"@iconify-json/simple-icons": "^1.2.44",
|
"@iconify-json/simple-icons": "^1.2.42",
|
||||||
"@internationalized/date": "^3.8.2",
|
"@internationalized/date": "^3.8.2",
|
||||||
"@nuxt/ui": "workspace:*",
|
"@nuxt/ui": "workspace:*",
|
||||||
"@nuxthub/core": "^0.9.0",
|
"@nuxthub/core": "^0.9.0",
|
||||||
"nuxt": "^4.0.1",
|
"nuxt": "^3.17.6",
|
||||||
"zod": "^4.0.5"
|
"zod": "^3.25.75"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"typescript": "^5.8.3",
|
"typescript": "^5.8.3",
|
||||||
|
|||||||
3179
pnpm-lock.yaml
generated
3179
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -6,9 +6,6 @@ import { name, version } from '../package.json'
|
|||||||
|
|
||||||
export type * from './runtime/types'
|
export type * from './runtime/types'
|
||||||
|
|
||||||
type Color = 'primary' | 'secondary' | 'success' | 'info' | 'warning' | 'error' | (string & {})
|
|
||||||
type Size = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | (string & {})
|
|
||||||
|
|
||||||
export interface ModuleOptions {
|
export interface ModuleOptions {
|
||||||
/**
|
/**
|
||||||
* Prefix for components
|
* Prefix for components
|
||||||
@@ -41,7 +38,7 @@ export interface ModuleOptions {
|
|||||||
* @defaultValue `['primary', 'secondary', 'success', 'info', 'warning', 'error']`
|
* @defaultValue `['primary', 'secondary', 'success', 'info', 'warning', 'error']`
|
||||||
* @link https://ui.nuxt.com/getting-started/installation/nuxt#themecolors
|
* @link https://ui.nuxt.com/getting-started/installation/nuxt#themecolors
|
||||||
*/
|
*/
|
||||||
colors?: Color[]
|
colors?: string[]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enable or disable transitions on components
|
* Enable or disable transitions on components
|
||||||
@@ -49,20 +46,6 @@ export interface ModuleOptions {
|
|||||||
* @link https://ui.nuxt.com/getting-started/installation/nuxt#themetransitions
|
* @link https://ui.nuxt.com/getting-started/installation/nuxt#themetransitions
|
||||||
*/
|
*/
|
||||||
transitions?: boolean
|
transitions?: boolean
|
||||||
|
|
||||||
defaultVariants?: {
|
|
||||||
/**
|
|
||||||
* The default color variant to use for components
|
|
||||||
* @defaultValue `'primary'`
|
|
||||||
*/
|
|
||||||
color?: Color
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The default size variant to use for components
|
|
||||||
* @defaultValue `'md'`
|
|
||||||
*/
|
|
||||||
size?: Size
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,7 +85,7 @@ export default defineNuxtModule<ModuleOptions>({
|
|||||||
|
|
||||||
async function registerModule(name: string, key: string, options: Record<string, any>) {
|
async function registerModule(name: string, key: string, options: Record<string, any>) {
|
||||||
if (!hasNuxtModule(name)) {
|
if (!hasNuxtModule(name)) {
|
||||||
await installModule(name, defu((nuxt.options as any)[key], options))
|
await installModule(name, options)
|
||||||
} else {
|
} else {
|
||||||
(nuxt.options as any)[key] = defu((nuxt.options as any)[key], options)
|
(nuxt.options as any)[key] = defu((nuxt.options as any)[key], options)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ export interface AlertProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface AlertEmits {
|
export interface AlertEmits {
|
||||||
'update:open': [value: boolean]
|
(e: 'update:open', value: boolean): void
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AlertSlots {
|
export interface AlertSlots {
|
||||||
|
|||||||
@@ -256,7 +256,6 @@ const scrollSnaps = ref<number[]>([])
|
|||||||
function onInit(api: EmblaCarouselType) {
|
function onInit(api: EmblaCarouselType) {
|
||||||
scrollSnaps.value = api?.scrollSnapList() || []
|
scrollSnaps.value = api?.scrollSnapList() || []
|
||||||
}
|
}
|
||||||
|
|
||||||
function onSelect(api: EmblaCarouselType) {
|
function onSelect(api: EmblaCarouselType) {
|
||||||
canScrollNext.value = api?.canScrollNext() || false
|
canScrollNext.value = api?.canScrollNext() || false
|
||||||
canScrollPrev.value = api?.canScrollPrev() || false
|
canScrollPrev.value = api?.canScrollPrev() || false
|
||||||
@@ -301,7 +300,8 @@ defineExpose({
|
|||||||
<div
|
<div
|
||||||
v-for="(item, index) in items"
|
v-for="(item, index) in items"
|
||||||
:key="index"
|
:key="index"
|
||||||
v-bind="dots ? { role: 'tabpanel' } : { 'role': 'group', 'aria-roledescription': 'slide' }"
|
role="group"
|
||||||
|
aria-roledescription="slide"
|
||||||
:class="ui.item({ class: [props.ui?.item, isCarouselItem(item) && item.ui?.item, isCarouselItem(item) && item.class] })"
|
:class="ui.item({ class: [props.ui?.item, isCarouselItem(item) && item.ui?.item, isCarouselItem(item) && item.class] })"
|
||||||
>
|
>
|
||||||
<slot :item="item" :index="index" />
|
<slot :item="item" :index="index" />
|
||||||
@@ -333,13 +333,10 @@ defineExpose({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="dots" role="tablist" :aria-label="t('carousel.dots')" :class="ui.dots({ class: props.ui?.dots })">
|
<div v-if="dots" :class="ui.dots({ class: props.ui?.dots })">
|
||||||
<template v-for="(_, index) in scrollSnaps" :key="index">
|
<template v-for="(_, index) in scrollSnaps" :key="index">
|
||||||
<button
|
<button
|
||||||
type="button"
|
|
||||||
role="tab"
|
|
||||||
:aria-label="t('carousel.goto', { slide: index + 1 })"
|
:aria-label="t('carousel.goto', { slide: index + 1 })"
|
||||||
:aria-selected="selectedIndex === index"
|
|
||||||
:class="ui.dot({ class: props.ui?.dot, active: selectedIndex === index })"
|
:class="ui.dot({ class: props.ui?.dot, active: selectedIndex === index })"
|
||||||
:data-state="selectedIndex === index ? 'active' : undefined"
|
:data-state="selectedIndex === index ? 'active' : undefined"
|
||||||
@click="scrollTo(index)"
|
@click="scrollTo(index)"
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ export interface ChipProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ChipEmits {
|
export interface ChipEmits {
|
||||||
'update:show': [payload: boolean]
|
(e: 'update:show', payload: boolean): void
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ChipSlots {
|
export interface ChipSlots {
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ function HSVtoHSL(hsv: HSVColor): HSLObject {
|
|||||||
return {
|
return {
|
||||||
H: hsv.h,
|
H: hsv.h,
|
||||||
S: x === 0 || x === 200 ? 0 : Math.round(hsv.s * hsv.v / (x <= 100 ? x : 200 - x)),
|
S: x === 0 || x === 200 ? 0 : Math.round(hsv.s * hsv.v / (x <= 100 ? x : 200 - x)),
|
||||||
L: x / 2
|
L: Math.round(x / 2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,6 +102,7 @@ const pickedColor = computed<HSVColor>({
|
|||||||
},
|
},
|
||||||
set(value) {
|
set(value) {
|
||||||
const color = new ColorTranslator(HSVtoHSL(value), {
|
const color = new ColorTranslator(HSVtoHSL(value), {
|
||||||
|
decimals: 2,
|
||||||
labUnit: 'percent',
|
labUnit: 'percent',
|
||||||
cmykUnit: 'percent',
|
cmykUnit: 'percent',
|
||||||
cmykFunction: 'cmyk'
|
cmykFunction: 'cmyk'
|
||||||
|
|||||||
@@ -31,6 +31,11 @@ export interface CommandPaletteItem extends Omit<LinkProps, 'type' | 'raw' | 'cu
|
|||||||
*/
|
*/
|
||||||
placeholder?: string
|
placeholder?: string
|
||||||
children?: CommandPaletteItem[]
|
children?: CommandPaletteItem[]
|
||||||
|
/**
|
||||||
|
* Custom view to display instead of children items.
|
||||||
|
* When defined, clicking this item will show the custom view.
|
||||||
|
*/
|
||||||
|
view?: string
|
||||||
onSelect?(e?: Event): void
|
onSelect?(e?: Event): void
|
||||||
class?: any
|
class?: any
|
||||||
ui?: Pick<CommandPalette['slots'], 'item' | 'itemLeadingIcon' | 'itemLeadingAvatarSize' | 'itemLeadingAvatar' | 'itemLeadingChipSize' | 'itemLeadingChip' | 'itemLabel' | 'itemLabelPrefix' | 'itemLabelBase' | 'itemLabelSuffix' | 'itemTrailing' | 'itemTrailingKbds' | 'itemTrailingKbdsSize' | 'itemTrailingHighlightedIcon' | 'itemTrailingIcon'>
|
ui?: Pick<CommandPalette['slots'], 'item' | 'itemLeadingIcon' | 'itemLeadingAvatarSize' | 'itemLeadingAvatar' | 'itemLeadingChipSize' | 'itemLeadingChip' | 'itemLabel' | 'itemLabelPrefix' | 'itemLabelBase' | 'itemLabelSuffix' | 'itemTrailing' | 'itemTrailingKbds' | 'itemTrailingKbdsSize' | 'itemTrailingHighlightedIcon' | 'itemTrailingIcon'>
|
||||||
@@ -147,14 +152,13 @@ type SlotProps<T> = (props: { item: T, index: number }) => any
|
|||||||
|
|
||||||
export type CommandPaletteSlots<G extends CommandPaletteGroup<T> = CommandPaletteGroup<any>, T extends CommandPaletteItem = CommandPaletteItem> = {
|
export type CommandPaletteSlots<G extends CommandPaletteGroup<T> = CommandPaletteGroup<any>, T extends CommandPaletteItem = CommandPaletteItem> = {
|
||||||
'empty'(props: { searchTerm?: string }): any
|
'empty'(props: { searchTerm?: string }): any
|
||||||
'footer'(props: { ui: { [K in keyof Required<CommandPalette['slots']>]: (props?: Record<string, any>) => string } }): any
|
|
||||||
'back'(props: { ui: { [K in keyof Required<CommandPalette['slots']>]: (props?: Record<string, any>) => string } }): any
|
'back'(props: { ui: { [K in keyof Required<CommandPalette['slots']>]: (props?: Record<string, any>) => string } }): any
|
||||||
'close'(props: { ui: { [K in keyof Required<CommandPalette['slots']>]: (props?: Record<string, any>) => string } }): any
|
'close'(props: { ui: { [K in keyof Required<CommandPalette['slots']>]: (props?: Record<string, any>) => string } }): any
|
||||||
'item': SlotProps<T>
|
'item': SlotProps<T>
|
||||||
'item-leading': SlotProps<T>
|
'item-leading': SlotProps<T>
|
||||||
'item-label': SlotProps<T>
|
'item-label': SlotProps<T>
|
||||||
'item-trailing': SlotProps<T>
|
'item-trailing': SlotProps<T>
|
||||||
} & Record<string, SlotProps<G>> & Record<string, SlotProps<T>>
|
} & Record<string, SlotProps<G>> & Record<string, SlotProps<T>> & Record<string, (props: { current: any, searchTerm: string, navigateBack: () => void, close: () => void }) => any>
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -209,12 +213,17 @@ const fuse = computed(() => defu({}, props.fuse, {
|
|||||||
matchAllWhenSearchEmpty: true
|
matchAllWhenSearchEmpty: true
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const history = ref<(CommandPaletteGroup & { placeholder?: string })[]>([])
|
const history = ref<(CommandPaletteGroup & { placeholder?: string, view?: string })[]>([])
|
||||||
|
|
||||||
const placeholder = computed(() => history.value[history.value.length - 1]?.placeholder || props.placeholder || t('commandPalette.placeholder'))
|
const placeholder = computed(() => history.value[history.value.length - 1]?.placeholder || props.placeholder || t('commandPalette.placeholder'))
|
||||||
|
|
||||||
const groups = computed(() => history.value?.length ? [history.value[history.value.length - 1] as G] : props.groups)
|
const groups = computed(() => history.value?.length ? [history.value[history.value.length - 1] as G] : props.groups)
|
||||||
|
|
||||||
|
const currentView = computed(() => {
|
||||||
|
const current = history.value[history.value.length - 1]
|
||||||
|
return current?.view ? current : null
|
||||||
|
})
|
||||||
|
|
||||||
const items = computed(() => groups.value?.filter((group) => {
|
const items = computed(() => groups.value?.filter((group) => {
|
||||||
if (!group.id) {
|
if (!group.id) {
|
||||||
console.warn(`[@nuxt/ui] CommandPalette group is missing an \`id\` property`)
|
console.warn(`[@nuxt/ui] CommandPalette group is missing an \`id\` property`)
|
||||||
@@ -280,8 +289,33 @@ const filteredGroups = computed(() => {
|
|||||||
|
|
||||||
const listboxRootRef = useTemplateRef('listboxRootRef')
|
const listboxRootRef = useTemplateRef('listboxRootRef')
|
||||||
|
|
||||||
|
// Exposed methods for programmatic control
|
||||||
|
function openView(viewName: string) {
|
||||||
|
history.value.push({
|
||||||
|
id: `view-${viewName}`,
|
||||||
|
label: viewName,
|
||||||
|
view: viewName,
|
||||||
|
items: []
|
||||||
|
} as any)
|
||||||
|
|
||||||
|
searchTerm.value = ''
|
||||||
|
listboxRootRef.value?.highlightFirstItem()
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeView() {
|
||||||
|
if (history.value.length > 0) {
|
||||||
|
navigateBack()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
openView,
|
||||||
|
closeView,
|
||||||
|
navigateBack
|
||||||
|
})
|
||||||
|
|
||||||
function navigate(item: T) {
|
function navigate(item: T) {
|
||||||
if (!item.children?.length) {
|
if (!item.children?.length && !item.view) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -290,7 +324,8 @@ function navigate(item: T) {
|
|||||||
label: item.label,
|
label: item.label,
|
||||||
slot: item.slot,
|
slot: item.slot,
|
||||||
placeholder: item.placeholder,
|
placeholder: item.placeholder,
|
||||||
items: item.children
|
view: item.view,
|
||||||
|
items: item.children || []
|
||||||
} as any)
|
} as any)
|
||||||
|
|
||||||
searchTerm.value = ''
|
searchTerm.value = ''
|
||||||
@@ -317,7 +352,7 @@ function onBackspace() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onSelect(e: Event, item: T) {
|
function onSelect(e: Event, item: T) {
|
||||||
if (item.children?.length) {
|
if (item.children?.length || item.view) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
||||||
navigate(item)
|
navigate(item)
|
||||||
@@ -372,7 +407,17 @@ function onSelect(e: Event, item: T) {
|
|||||||
</ListboxFilter>
|
</ListboxFilter>
|
||||||
|
|
||||||
<ListboxContent :class="ui.content({ class: props.ui?.content })">
|
<ListboxContent :class="ui.content({ class: props.ui?.content })">
|
||||||
<div v-if="filteredGroups?.length" role="presentation" :class="ui.viewport({ class: props.ui?.viewport })">
|
<div v-if="currentView" :class="ui.viewport({ class: props.ui?.viewport })">
|
||||||
|
<slot
|
||||||
|
:name="currentView.view"
|
||||||
|
:current="currentView"
|
||||||
|
:search-term="searchTerm"
|
||||||
|
:navigate-back="navigateBack"
|
||||||
|
:close="closeView"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else-if="filteredGroups?.length" role="presentation" :class="ui.viewport({ class: props.ui?.viewport })">
|
||||||
<ListboxGroup v-for="group in filteredGroups" :key="`group-${group.id}`" :class="ui.group({ class: props.ui?.group })">
|
<ListboxGroup v-for="group in filteredGroups" :key="`group-${group.id}`" :class="ui.group({ class: props.ui?.group })">
|
||||||
<ListboxGroupLabel v-if="get(group, props.labelKey as string)" :class="ui.label({ class: props.ui?.label })">
|
<ListboxGroupLabel v-if="get(group, props.labelKey as string)" :class="ui.label({ class: props.ui?.label })">
|
||||||
{{ get(group, props.labelKey as string) }}
|
{{ get(group, props.labelKey as string) }}
|
||||||
@@ -416,7 +461,7 @@ function onSelect(e: Event, item: T) {
|
|||||||
<span :class="ui.itemTrailing({ class: [props.ui?.itemTrailing, item.ui?.itemTrailing] })">
|
<span :class="ui.itemTrailing({ class: [props.ui?.itemTrailing, item.ui?.itemTrailing] })">
|
||||||
<slot :name="((item.slot ? `${item.slot}-trailing` : group.slot ? `${group.slot}-trailing` : `item-trailing`) as keyof CommandPaletteSlots<G, T>)" :item="(item as any)" :index="index">
|
<slot :name="((item.slot ? `${item.slot}-trailing` : group.slot ? `${group.slot}-trailing` : `item-trailing`) as keyof CommandPaletteSlots<G, T>)" :item="(item as any)" :index="index">
|
||||||
<UIcon
|
<UIcon
|
||||||
v-if="item.children && item.children.length > 0"
|
v-if="(item.children && item.children.length > 0) || item.view"
|
||||||
:name="trailingIcon || appConfig.ui.icons.chevronRight"
|
:name="trailingIcon || appConfig.ui.icons.chevronRight"
|
||||||
:class="ui.itemTrailingIcon({ class: [props.ui?.itemTrailingIcon, item.ui?.itemTrailingIcon] })"
|
:class="ui.itemTrailingIcon({ class: [props.ui?.itemTrailingIcon, item.ui?.itemTrailingIcon] })"
|
||||||
/>
|
/>
|
||||||
@@ -445,9 +490,5 @@ function onSelect(e: Event, item: T) {
|
|||||||
</slot>
|
</slot>
|
||||||
</div>
|
</div>
|
||||||
</ListboxContent>
|
</ListboxContent>
|
||||||
|
|
||||||
<div v-if="!!slots.footer" :class="ui.footer({ class: props.ui?.footer })">
|
|
||||||
<slot name="footer" :ui="ui" />
|
|
||||||
</div>
|
|
||||||
</ListboxRoot>
|
</ListboxRoot>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -37,11 +37,6 @@ export interface DrawerProps extends Pick<DrawerRootProps, 'activeSnapPoint' | '
|
|||||||
* @defaultValue true
|
* @defaultValue true
|
||||||
*/
|
*/
|
||||||
portal?: boolean | string | HTMLElement
|
portal?: boolean | string | HTMLElement
|
||||||
/**
|
|
||||||
* Whether the drawer is nested in another drawer.
|
|
||||||
* @defaultValue false
|
|
||||||
*/
|
|
||||||
nested?: boolean
|
|
||||||
class?: any
|
class?: any
|
||||||
ui?: Drawer['slots']
|
ui?: Drawer['slots']
|
||||||
}
|
}
|
||||||
@@ -62,7 +57,7 @@ export interface DrawerSlots {
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, toRef } from 'vue'
|
import { computed, toRef } from 'vue'
|
||||||
import { VisuallyHidden, useForwardPropsEmits } from 'reka-ui'
|
import { VisuallyHidden, useForwardPropsEmits } from 'reka-ui'
|
||||||
import { DrawerRoot, DrawerRootNested, DrawerTrigger, DrawerPortal, DrawerOverlay, DrawerContent, DrawerTitle, DrawerDescription, DrawerHandle } from 'vaul-vue'
|
import { DrawerRoot, DrawerTrigger, DrawerPortal, DrawerOverlay, DrawerContent, DrawerTitle, DrawerDescription, DrawerHandle } from 'vaul-vue'
|
||||||
import { reactivePick } from '@vueuse/core'
|
import { reactivePick } from '@vueuse/core'
|
||||||
import { useAppConfig } from '#imports'
|
import { useAppConfig } from '#imports'
|
||||||
import { usePortal } from '../composables/usePortal'
|
import { usePortal } from '../composables/usePortal'
|
||||||
@@ -95,7 +90,7 @@ const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.drawer || {}
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<component :is="nested ? DrawerRootNested : DrawerRoot" v-bind="rootProps">
|
<DrawerRoot v-bind="rootProps">
|
||||||
<DrawerTrigger v-if="!!slots.default" as-child :class="props.class">
|
<DrawerTrigger v-if="!!slots.default" as-child :class="props.class">
|
||||||
<slot />
|
<slot />
|
||||||
</DrawerTrigger>
|
</DrawerTrigger>
|
||||||
@@ -149,5 +144,5 @@ const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.drawer || {}
|
|||||||
</slot>
|
</slot>
|
||||||
</DrawerContent>
|
</DrawerContent>
|
||||||
</DrawerPortal>
|
</DrawerPortal>
|
||||||
</component>
|
</DrawerRoot>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -53,8 +53,8 @@ export interface FormProps<S extends FormSchema, T extends boolean = true> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface FormEmits<S extends FormSchema, T extends boolean = true> {
|
export interface FormEmits<S extends FormSchema, T extends boolean = true> {
|
||||||
submit: [payload: FormSubmitEvent<FormData<S, T>>]
|
(e: 'submit', payload: FormSubmitEvent<FormData<S, T>>): void
|
||||||
error: [payload: FormErrorEvent]
|
(e: 'error', payload: FormErrorEvent): void
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FormSlots {
|
export interface FormSlots {
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export interface FormFieldProps {
|
|||||||
label?: string
|
label?: string
|
||||||
description?: string
|
description?: string
|
||||||
help?: string
|
help?: string
|
||||||
error?: boolean | string
|
error?: string | boolean
|
||||||
hint?: string
|
hint?: string
|
||||||
/**
|
/**
|
||||||
* @defaultValue 'md'
|
* @defaultValue 'md'
|
||||||
@@ -41,8 +41,8 @@ export interface FormFieldSlots {
|
|||||||
hint(props: { hint?: string }): any
|
hint(props: { hint?: string }): any
|
||||||
description(props: { description?: string }): any
|
description(props: { description?: string }): any
|
||||||
help(props: { help?: string }): any
|
help(props: { help?: string }): any
|
||||||
error(props: { error?: boolean | string }): any
|
error(props: { error?: string | boolean }): any
|
||||||
default(props: { error?: boolean | string }): any
|
default(props: { error?: string | boolean }): any
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -121,7 +121,7 @@ provide(formFieldInjectionKey, computed(() => ({
|
|||||||
{{ error }}
|
{{ error }}
|
||||||
</slot>
|
</slot>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="help || !!slots.help" :id="`${ariaId}-help`" :class="ui.help({ class: props.ui?.help })">
|
<div v-else-if="help || !!slots.help" :class="ui.help({ class: props.ui?.help })">
|
||||||
<slot name="help" :help="help">
|
<slot name="help" :help="help">
|
||||||
{{ help }}
|
{{ help }}
|
||||||
</slot>
|
</slot>
|
||||||
|
|||||||
@@ -52,9 +52,9 @@ export interface InputProps<T extends AcceptableValue = AcceptableValue> extends
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface InputEmits<T extends AcceptableValue = AcceptableValue> {
|
export interface InputEmits<T extends AcceptableValue = AcceptableValue> {
|
||||||
'update:modelValue': [payload: T]
|
(e: 'update:modelValue', payload: T): void
|
||||||
'blur': [event: FocusEvent]
|
(e: 'blur', event: FocusEvent): void
|
||||||
'change': [event: Event]
|
(e: 'change', event: Event): void
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface InputSlots {
|
export interface InputSlots {
|
||||||
|
|||||||
@@ -128,16 +128,15 @@ export interface InputMenuProps<T extends ArrayOrNested<InputMenuItem> = ArrayOr
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type InputMenuEmits<A extends ArrayOrNested<InputMenuItem>, VK extends GetItemKeys<A> | undefined, M extends boolean> = Pick<ComboboxRootEmits, 'update:open'> & {
|
export type InputMenuEmits<A extends ArrayOrNested<InputMenuItem>, VK extends GetItemKeys<A> | undefined, M extends boolean> = Pick<ComboboxRootEmits, 'update:open'> & {
|
||||||
'change': [payload: Event]
|
change: [payload: Event]
|
||||||
'blur': [payload: FocusEvent]
|
blur: [payload: FocusEvent]
|
||||||
'focus': [payload: FocusEvent]
|
focus: [payload: FocusEvent]
|
||||||
'create': [item: string]
|
create: [item: string]
|
||||||
/** Event handler when highlighted element changes. */
|
/** Event handler when highlighted element changes. */
|
||||||
'highlight': [payload: {
|
highlight: [payload: {
|
||||||
ref: HTMLElement
|
ref: HTMLElement
|
||||||
value: GetModelValue<A, VK, M>
|
value: GetModelValue<A, VK, M>
|
||||||
} | undefined]
|
} | undefined]
|
||||||
'remove-tag': [item: GetModelValue<A, VK, M>]
|
|
||||||
} & GetModelValueEmits<A, VK, M>
|
} & GetModelValueEmits<A, VK, M>
|
||||||
|
|
||||||
type SlotProps<T extends InputMenuItem> = (props: { item: T, index: number }) => any
|
type SlotProps<T extends InputMenuItem> = (props: { item: T, index: number }) => any
|
||||||
@@ -172,7 +171,7 @@ export interface InputMenuSlots<
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script setup lang="ts" generic="T extends ArrayOrNested<InputMenuItem>, VK extends GetItemKeys<T> | undefined = undefined, M extends boolean = false">
|
<script setup lang="ts" generic="T extends ArrayOrNested<InputMenuItem>, VK extends GetItemKeys<T> | undefined = undefined, M extends boolean = false">
|
||||||
import { computed, ref, toRef, onMounted, toRaw, nextTick } from 'vue'
|
import { computed, ref, toRef, onMounted, toRaw } from 'vue'
|
||||||
import { ComboboxRoot, ComboboxArrow, ComboboxAnchor, ComboboxInput, ComboboxTrigger, ComboboxPortal, ComboboxContent, ComboboxEmpty, ComboboxGroup, ComboboxLabel, ComboboxSeparator, ComboboxItem, ComboboxItemIndicator, TagsInputRoot, TagsInputItem, TagsInputItemText, TagsInputItemDelete, TagsInputInput, useForwardPropsEmits, useFilter } from 'reka-ui'
|
import { ComboboxRoot, ComboboxArrow, ComboboxAnchor, ComboboxInput, ComboboxTrigger, ComboboxPortal, ComboboxContent, ComboboxEmpty, ComboboxGroup, ComboboxLabel, ComboboxSeparator, ComboboxItem, ComboboxItemIndicator, TagsInputRoot, TagsInputItem, TagsInputItemText, TagsInputItemDelete, TagsInputInput, useForwardPropsEmits, useFilter } from 'reka-ui'
|
||||||
import { defu } from 'defu'
|
import { defu } from 'defu'
|
||||||
import { isEqual } from 'ohash/utils'
|
import { isEqual } from 'ohash/utils'
|
||||||
@@ -234,7 +233,11 @@ const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.inputMenu ||
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
function displayValue(value: T): string {
|
function displayValue(value: T): string {
|
||||||
const item = items.value.find(item => compare(typeof item === 'object' && props.valueKey ? get(item as Record<string, any>, props.valueKey as string) : item, value))
|
if (!props.valueKey) {
|
||||||
|
return value && (typeof value === 'object' ? get(value, props.labelKey as string) : value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const item = items.value.find(item => compare(typeof item === 'object' ? get(item as Record<string, any>, props.valueKey as string) : item, value))
|
||||||
return item && (typeof item === 'object' ? get(item, props.labelKey as string) : item)
|
return item && (typeof item === 'object' ? get(item, props.labelKey as string) : item)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -255,12 +258,8 @@ const filteredGroups = computed(() => {
|
|||||||
|
|
||||||
const fields = Array.isArray(props.filterFields) ? props.filterFields : [props.labelKey] as string[]
|
const fields = Array.isArray(props.filterFields) ? props.filterFields : [props.labelKey] as string[]
|
||||||
|
|
||||||
return groups.value.map(items => items.filter((item) => {
|
return groups.value.map(group => group.filter((item) => {
|
||||||
if (item === undefined || item === null) {
|
if (typeof item !== 'object' || item === null) {
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof item !== 'object') {
|
|
||||||
return contains(String(item), searchTerm.value)
|
return contains(String(item), searchTerm.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -268,10 +267,7 @@ const filteredGroups = computed(() => {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
return fields.some((field) => {
|
return fields.some(field => contains(get(item, field), searchTerm.value))
|
||||||
const value = get(item, field)
|
|
||||||
return value !== undefined && value !== null && contains(String(value), searchTerm.value)
|
|
||||||
})
|
|
||||||
})).filter(group => group.filter(item =>
|
})).filter(group => group.filter(item =>
|
||||||
!isInputItem(item) || (!item.type || !['label', 'separator'].includes(item.type))
|
!isInputItem(item) || (!item.type || !['label', 'separator'].includes(item.type))
|
||||||
).length > 0)
|
).length > 0)
|
||||||
@@ -302,10 +298,6 @@ function autoFocus() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
nextTick(() => {
|
|
||||||
searchTerm.value = ''
|
|
||||||
})
|
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
autoFocus()
|
autoFocus()
|
||||||
}, props.autofocusDelay)
|
}, props.autofocusDelay)
|
||||||
@@ -367,7 +359,6 @@ function onRemoveTag(event: any) {
|
|||||||
const modelValue = props.modelValue as GetModelValue<T, VK, true>
|
const modelValue = props.modelValue as GetModelValue<T, VK, true>
|
||||||
const filteredValue = modelValue.filter(value => !isEqual(value, event))
|
const filteredValue = modelValue.filter(value => !isEqual(value, event))
|
||||||
emits('update:modelValue', filteredValue as GetModelValue<T, VK, M>)
|
emits('update:modelValue', filteredValue as GetModelValue<T, VK, M>)
|
||||||
emits('remove-tag', event)
|
|
||||||
onUpdate(filteredValue)
|
onUpdate(filteredValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -441,7 +432,7 @@ defineExpose({
|
|||||||
<TagsInputItem v-for="(item, index) in tags" :key="index" :value="item" :class="ui.tagsItem({ class: [props.ui?.tagsItem, isInputItem(item) && item.ui?.tagsItem] })">
|
<TagsInputItem v-for="(item, index) in tags" :key="index" :value="item" :class="ui.tagsItem({ class: [props.ui?.tagsItem, isInputItem(item) && item.ui?.tagsItem] })">
|
||||||
<TagsInputItemText :class="ui.tagsItemText({ class: [props.ui?.tagsItemText, isInputItem(item) && item.ui?.tagsItemText] })">
|
<TagsInputItemText :class="ui.tagsItemText({ class: [props.ui?.tagsItemText, isInputItem(item) && item.ui?.tagsItemText] })">
|
||||||
<slot name="tags-item-text" :item="(item as NestedItem<T>)" :index="index">
|
<slot name="tags-item-text" :item="(item as NestedItem<T>)" :index="index">
|
||||||
{{ displayValue(item as T) ?? item }}
|
{{ displayValue(item as T) }}
|
||||||
</slot>
|
</slot>
|
||||||
</TagsInputItemText>
|
</TagsInputItemText>
|
||||||
|
|
||||||
|
|||||||
@@ -63,9 +63,9 @@ export interface InputNumberProps extends Pick<NumberFieldRootProps, 'modelValue
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface InputNumberEmits {
|
export interface InputNumberEmits {
|
||||||
'update:modelValue': [payload: number]
|
(e: 'update:modelValue', payload: number): void
|
||||||
'blur': [event: FocusEvent]
|
(e: 'blur', event: FocusEvent): void
|
||||||
'change': [payload: Event]
|
(e: 'change', payload: Event): void
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface InputNumberSlots {
|
export interface InputNumberSlots {
|
||||||
|
|||||||
@@ -13,10 +13,6 @@ export interface KbdProps {
|
|||||||
*/
|
*/
|
||||||
as?: any
|
as?: any
|
||||||
value?: KbdKey | string
|
value?: KbdKey | string
|
||||||
/**
|
|
||||||
* @defaultValue 'neutral'
|
|
||||||
*/
|
|
||||||
color?: Kbd['variants']['color']
|
|
||||||
/**
|
/**
|
||||||
* @defaultValue 'outline'
|
* @defaultValue 'outline'
|
||||||
*/
|
*/
|
||||||
@@ -52,7 +48,7 @@ const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.kbd || {}) }
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Primitive :as="as" :class="ui({ class: props.class, color: props.color, variant: props.variant, size: props.size })">
|
<Primitive :as="as" :class="ui({ variant, size, class: props.class })">
|
||||||
<slot>
|
<slot>
|
||||||
{{ getKbdKey(value) }}
|
{{ getKbdKey(value) }}
|
||||||
</slot>
|
</slot>
|
||||||
|
|||||||
@@ -177,8 +177,6 @@ import UBadge from './Badge.vue'
|
|||||||
import UPopover from './Popover.vue'
|
import UPopover from './Popover.vue'
|
||||||
import UTooltip from './Tooltip.vue'
|
import UTooltip from './Tooltip.vue'
|
||||||
|
|
||||||
defineOptions({ inheritAttrs: false })
|
|
||||||
|
|
||||||
const props = withDefaults(defineProps<NavigationMenuProps<T>>(), {
|
const props = withDefaults(defineProps<NavigationMenuProps<T>>(), {
|
||||||
orientation: 'horizontal',
|
orientation: 'horizontal',
|
||||||
contentOrientation: 'horizontal',
|
contentOrientation: 'horizontal',
|
||||||
@@ -272,7 +270,7 @@ function getAccordionDefaultValue(list: NavigationMenuItem[], level = 0) {
|
|||||||
<component :is="orientation === 'vertical' && item.children?.length && !collapsed ? AccordionTrigger : 'span'" v-if="(!collapsed || orientation !== 'vertical') && (item.badge || (orientation === 'horizontal' && (item.children?.length || !!slots[(item.slot ? `${item.slot}-content` : 'item-content') as keyof NavigationMenuSlots<T>])) || (orientation === 'vertical' && item.children?.length) || item.trailingIcon || !!slots[(item.slot ? `${item.slot}-trailing` : 'item-trailing') as keyof NavigationMenuSlots<T>])" as="span" :class="ui.linkTrailing({ class: [props.ui?.linkTrailing, item.ui?.linkTrailing] })" @click.stop.prevent>
|
<component :is="orientation === 'vertical' && item.children?.length && !collapsed ? AccordionTrigger : 'span'" v-if="(!collapsed || orientation !== 'vertical') && (item.badge || (orientation === 'horizontal' && (item.children?.length || !!slots[(item.slot ? `${item.slot}-content` : 'item-content') as keyof NavigationMenuSlots<T>])) || (orientation === 'vertical' && item.children?.length) || item.trailingIcon || !!slots[(item.slot ? `${item.slot}-trailing` : 'item-trailing') as keyof NavigationMenuSlots<T>])" as="span" :class="ui.linkTrailing({ class: [props.ui?.linkTrailing, item.ui?.linkTrailing] })" @click.stop.prevent>
|
||||||
<slot :name="((item.slot ? `${item.slot}-trailing` : 'item-trailing') as keyof NavigationMenuSlots<T>)" :item="item" :active="active" :index="index">
|
<slot :name="((item.slot ? `${item.slot}-trailing` : 'item-trailing') as keyof NavigationMenuSlots<T>)" :item="item" :active="active" :index="index">
|
||||||
<UBadge
|
<UBadge
|
||||||
v-if="item.badge !== undefined"
|
v-if="item.badge"
|
||||||
color="neutral"
|
color="neutral"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
:size="((item.ui?.linkTrailingBadgeSize || props.ui?.linkTrailingBadgeSize || ui.linkTrailingBadgeSize()) as BadgeProps['size'])"
|
:size="((item.ui?.linkTrailingBadgeSize || props.ui?.linkTrailingBadgeSize || ui.linkTrailingBadgeSize()) as BadgeProps['size'])"
|
||||||
@@ -394,7 +392,7 @@ function getAccordionDefaultValue(list: NavigationMenuItem[], level = 0) {
|
|||||||
</component>
|
</component>
|
||||||
</DefineItemTemplate>
|
</DefineItemTemplate>
|
||||||
|
|
||||||
<NavigationMenuRoot v-bind="{ ...rootProps, ...$attrs }" :data-collapsed="collapsed" :class="ui.root({ class: [props.ui?.root, props.class] })">
|
<NavigationMenuRoot v-bind="rootProps" :data-collapsed="collapsed" :class="ui.root({ class: [props.ui?.root, props.class] })">
|
||||||
<slot name="list-leading" />
|
<slot name="list-leading" />
|
||||||
|
|
||||||
<template v-for="(list, listIndex) in lists" :key="`list-${listIndex}`">
|
<template v-for="(list, listIndex) in lists" :key="`list-${listIndex}`">
|
||||||
|
|||||||
@@ -70,9 +70,7 @@ export type RadioGroupEmits = RadioGroupRootEmits & {
|
|||||||
change: [payload: Event]
|
change: [payload: Event]
|
||||||
}
|
}
|
||||||
|
|
||||||
type NormalizeItem<T extends RadioGroupItem> = Exclude<T & { id: string }, RadioGroupValue>
|
type SlotProps<T extends RadioGroupItem> = (props: { item: T & { id: string }, modelValue?: RadioGroupValue }) => any
|
||||||
|
|
||||||
type SlotProps<T extends RadioGroupItem> = (props: { item: NormalizeItem<T>, modelValue?: RadioGroupValue }) => any
|
|
||||||
|
|
||||||
export interface RadioGroupSlots<T extends RadioGroupItem = RadioGroupItem> {
|
export interface RadioGroupSlots<T extends RadioGroupItem = RadioGroupItem> {
|
||||||
legend(props?: {}): any
|
legend(props?: {}): any
|
||||||
@@ -116,21 +114,21 @@ const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.radioGroup |
|
|||||||
indicator: props.indicator
|
indicator: props.indicator
|
||||||
}))
|
}))
|
||||||
|
|
||||||
function normalizeItem(item: T): NormalizeItem<T> {
|
function normalizeItem(item: any) {
|
||||||
if (item === null) {
|
if (item === null) {
|
||||||
return {
|
return {
|
||||||
id: `${id}:null`,
|
id: `${id}:null`,
|
||||||
value: undefined,
|
value: undefined,
|
||||||
label: undefined
|
label: undefined
|
||||||
} as NormalizeItem<T>
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof item === 'string' || typeof item === 'number' || typeof item === 'bigint') {
|
if (typeof item === 'string' || typeof item === 'number') {
|
||||||
return {
|
return {
|
||||||
id: `${id}:${item}`,
|
id: `${id}:${item}`,
|
||||||
value: String(item),
|
value: String(item),
|
||||||
label: String(item)
|
label: String(item)
|
||||||
} as NormalizeItem<T>
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const value = get(item, props.valueKey as string)
|
const value = get(item, props.valueKey as string)
|
||||||
@@ -138,7 +136,7 @@ function normalizeItem(item: T): NormalizeItem<T> {
|
|||||||
const description = get(item, props.descriptionKey as string)
|
const description = get(item, props.descriptionKey as string)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...(item as NormalizeItem<T>),
|
...item,
|
||||||
value,
|
value,
|
||||||
label,
|
label,
|
||||||
description,
|
description,
|
||||||
|
|||||||
@@ -234,7 +234,11 @@ function displayValue(value: GetItemValue<T, VK> | GetItemValue<T, VK>[]): strin
|
|||||||
return values?.length ? values.join(', ') : undefined
|
return values?.length ? values.join(', ') : undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
const item = items.value.find(item => compare(typeof item === 'object' && props.valueKey ? get(item as Record<string, any>, props.valueKey as string) : item, value))
|
if (!props.valueKey) {
|
||||||
|
return value && (typeof value === 'object' ? get(value, props.labelKey as string) : value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const item = items.value.find(item => compare(typeof item === 'object' ? get(item as Record<string, any>, props.valueKey as string) : item, value))
|
||||||
return item && (typeof item === 'object' ? get(item, props.labelKey as string) : item)
|
return item && (typeof item === 'object' ? get(item, props.labelKey as string) : item)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,11 +260,7 @@ const filteredGroups = computed(() => {
|
|||||||
const fields = Array.isArray(props.filterFields) ? props.filterFields : [props.labelKey] as string[]
|
const fields = Array.isArray(props.filterFields) ? props.filterFields : [props.labelKey] as string[]
|
||||||
|
|
||||||
return groups.value.map(items => items.filter((item) => {
|
return groups.value.map(items => items.filter((item) => {
|
||||||
if (item === undefined || item === null) {
|
if (typeof item !== 'object' || item === null) {
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof item !== 'object') {
|
|
||||||
return contains(String(item), searchTerm.value)
|
return contains(String(item), searchTerm.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -268,10 +268,7 @@ const filteredGroups = computed(() => {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
return fields.some((field) => {
|
return fields.some(field => contains(get(item, field), searchTerm.value))
|
||||||
const value = get(item, field)
|
|
||||||
return value !== undefined && value !== null && contains(String(value), searchTerm.value)
|
|
||||||
})
|
|
||||||
})).filter(group => group.filter(item =>
|
})).filter(group => group.filter(item =>
|
||||||
!isSelectItem(item) || (!item.type || !['label', 'separator'].includes(item.type))
|
!isSelectItem(item) || (!item.type || !['label', 'separator'].includes(item.type))
|
||||||
).length > 0)
|
).length > 0)
|
||||||
|
|||||||
@@ -39,8 +39,8 @@ export interface SliderProps extends Pick<SliderRootProps, 'name' | 'disabled' |
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface SliderEmits<T extends number | number[] = number | number[]> {
|
export interface SliderEmits<T extends number | number[] = number | number[]> {
|
||||||
'update:modelValue': [payload: T]
|
(e: 'update:modelValue', payload: T): void
|
||||||
'change': [payload: Event]
|
(e: 'change', payload: Event): void
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -45,26 +45,12 @@ declare module '@tanstack/table-core' {
|
|||||||
th?: string | ((cell: Header<TData, TValue>) => string)
|
th?: string | ((cell: Header<TData, TValue>) => string)
|
||||||
td?: string | ((cell: Cell<TData, TValue>) => string)
|
td?: string | ((cell: Cell<TData, TValue>) => string)
|
||||||
}
|
}
|
||||||
style?: {
|
|
||||||
th?: string | Record<string, string> | ((cell: Header<TData, TValue>) => string | Record<string, string>)
|
|
||||||
td?: string | Record<string, string> | ((cell: Cell<TData, TValue>) => string | Record<string, string>)
|
|
||||||
}
|
|
||||||
colspan?: {
|
|
||||||
td?: string | ((cell: Cell<TData, TValue>) => string)
|
|
||||||
}
|
|
||||||
rowspan?: {
|
|
||||||
td?: string | ((cell: Cell<TData, TValue>) => string)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TableMeta<TData> {
|
interface TableMeta<TData> {
|
||||||
class?: {
|
class?: {
|
||||||
tr?: string | ((row: Row<TData>) => string)
|
tr?: string | ((row: Row<TData>) => string)
|
||||||
}
|
}
|
||||||
style?: {
|
|
||||||
tr?: string | Record<string, string> | ((row: Row<TData>) => string | Record<string, string>)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -383,14 +369,6 @@ function onRowContextmenu(e: Event, row: TableRow<T>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveValue<T, A = undefined>(prop: T | ((arg: A) => T), arg?: A): T | undefined {
|
|
||||||
if (typeof prop === 'function') {
|
|
||||||
// @ts-expect-error: TS can't know if prop is a function here
|
|
||||||
return prop(arg)
|
|
||||||
}
|
|
||||||
return prop
|
|
||||||
}
|
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.data, () => {
|
() => props.data, () => {
|
||||||
data.value = props.data ? [...props.data] : []
|
data.value = props.data ? [...props.data] : []
|
||||||
@@ -420,11 +398,10 @@ defineExpose({
|
|||||||
:data-pinned="header.column.getIsPinned()"
|
:data-pinned="header.column.getIsPinned()"
|
||||||
:scope="header.colSpan > 1 ? 'colgroup' : 'col'"
|
:scope="header.colSpan > 1 ? 'colgroup' : 'col'"
|
||||||
:colspan="header.colSpan > 1 ? header.colSpan : undefined"
|
:colspan="header.colSpan > 1 ? header.colSpan : undefined"
|
||||||
:rowspan="header.rowSpan > 1 ? header.rowSpan : undefined"
|
|
||||||
:class="ui.th({
|
:class="ui.th({
|
||||||
class: [
|
class: [
|
||||||
props.ui?.th,
|
props.ui?.th,
|
||||||
resolveValue(header.column.columnDef.meta?.class?.th, header)
|
typeof header.column.columnDef.meta?.class?.th === 'function' ? header.column.columnDef.meta.class.th(header) : header.column.columnDef.meta?.class?.th
|
||||||
],
|
],
|
||||||
pinned: !!header.column.getIsPinned()
|
pinned: !!header.column.getIsPinned()
|
||||||
})"
|
})"
|
||||||
@@ -452,10 +429,9 @@ defineExpose({
|
|||||||
:class="ui.tr({
|
:class="ui.tr({
|
||||||
class: [
|
class: [
|
||||||
props.ui?.tr,
|
props.ui?.tr,
|
||||||
resolveValue(tableApi.options.meta?.class?.tr, row)
|
typeof tableApi.options.meta?.class?.tr === 'function' ? tableApi.options.meta.class.tr(row) : tableApi.options.meta?.class?.tr
|
||||||
]
|
]
|
||||||
})"
|
})"
|
||||||
:style="resolveValue(tableApi.options.meta?.style?.tr, row)"
|
|
||||||
@click="onRowSelect($event, row)"
|
@click="onRowSelect($event, row)"
|
||||||
@pointerenter="onRowHover($event, row)"
|
@pointerenter="onRowHover($event, row)"
|
||||||
@pointerleave="onRowHover($event, null)"
|
@pointerleave="onRowHover($event, null)"
|
||||||
@@ -465,16 +441,13 @@ defineExpose({
|
|||||||
v-for="cell in row.getVisibleCells()"
|
v-for="cell in row.getVisibleCells()"
|
||||||
:key="cell.id"
|
:key="cell.id"
|
||||||
:data-pinned="cell.column.getIsPinned()"
|
:data-pinned="cell.column.getIsPinned()"
|
||||||
:colspan="resolveValue(cell.column.columnDef.meta?.colspan?.td, cell)"
|
|
||||||
:rowspan="resolveValue(cell.column.columnDef.meta?.rowspan?.td, cell)"
|
|
||||||
:class="ui.td({
|
:class="ui.td({
|
||||||
class: [
|
class: [
|
||||||
props.ui?.td,
|
props.ui?.td,
|
||||||
resolveValue(cell.column.columnDef.meta?.class?.td, cell)
|
typeof cell.column.columnDef.meta?.class?.td === 'function' ? cell.column.columnDef.meta.class.td(cell) : cell.column.columnDef.meta?.class?.td
|
||||||
],
|
],
|
||||||
pinned: !!cell.column.getIsPinned()
|
pinned: !!cell.column.getIsPinned()
|
||||||
})"
|
})"
|
||||||
:style="resolveValue(cell.column.columnDef.meta?.style?.td, cell)"
|
|
||||||
>
|
>
|
||||||
<slot :name="`${cell.column.id}-cell`" v-bind="cell.getContext()">
|
<slot :name="`${cell.column.id}-cell`" v-bind="cell.getContext()">
|
||||||
<FlexRender :render="cell.column.columnDef.cell" :props="cell.getContext()" />
|
<FlexRender :render="cell.column.columnDef.cell" :props="cell.getContext()" />
|
||||||
@@ -515,15 +488,13 @@ defineExpose({
|
|||||||
:key="header.id"
|
:key="header.id"
|
||||||
:data-pinned="header.column.getIsPinned()"
|
:data-pinned="header.column.getIsPinned()"
|
||||||
:colspan="header.colSpan > 1 ? header.colSpan : undefined"
|
:colspan="header.colSpan > 1 ? header.colSpan : undefined"
|
||||||
:rowspan="header.rowSpan > 1 ? header.rowSpan : undefined"
|
|
||||||
:class="ui.th({
|
:class="ui.th({
|
||||||
class: [
|
class: [
|
||||||
props.ui?.th,
|
props.ui?.th,
|
||||||
resolveValue(header.column.columnDef.meta?.class?.th, header)
|
typeof header.column.columnDef.meta?.class?.th === 'function' ? header.column.columnDef.meta.class.th(header) : header.column.columnDef.meta?.class?.th
|
||||||
],
|
],
|
||||||
pinned: !!header.column.getIsPinned()
|
pinned: !!header.column.getIsPinned()
|
||||||
})"
|
})"
|
||||||
:style="resolveValue(header.column.columnDef.meta?.style?.th, header)"
|
|
||||||
>
|
>
|
||||||
<slot :name="`${header.id}-footer`" v-bind="header.getContext()">
|
<slot :name="`${header.id}-footer`" v-bind="header.getContext()">
|
||||||
<FlexRender v-if="!header.isPlaceholder" :render="header.column.columnDef.footer" :props="header.getContext()" />
|
<FlexRender v-if="!header.isPlaceholder" :render="header.column.columnDef.footer" :props="header.getContext()" />
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
import type { TabsRootProps, TabsRootEmits } from 'reka-ui'
|
import type { TabsRootProps, TabsRootEmits } from 'reka-ui'
|
||||||
import type { AppConfig } from '@nuxt/schema'
|
import type { AppConfig } from '@nuxt/schema'
|
||||||
import theme from '#build/ui/tabs'
|
import theme from '#build/ui/tabs'
|
||||||
import type { AvatarProps, BadgeProps } from '../types'
|
import type { AvatarProps } from '../types'
|
||||||
import type { DynamicSlots, ComponentConfig } from '../types/utils'
|
import type { DynamicSlots, ComponentConfig } from '../types/utils'
|
||||||
|
|
||||||
type Tabs = ComponentConfig<typeof theme, AppConfig, 'tabs'>
|
type Tabs = ComponentConfig<typeof theme, AppConfig, 'tabs'>
|
||||||
@@ -15,18 +15,13 @@ export interface TabsItem {
|
|||||||
*/
|
*/
|
||||||
icon?: string
|
icon?: string
|
||||||
avatar?: AvatarProps
|
avatar?: AvatarProps
|
||||||
/**
|
|
||||||
* Display a badge on the item.
|
|
||||||
* `{ size: 'sm', color: 'neutral', variant: 'outline' }`{lang="ts-type"}
|
|
||||||
*/
|
|
||||||
badge?: string | number | BadgeProps
|
|
||||||
slot?: string
|
slot?: string
|
||||||
content?: string
|
content?: string
|
||||||
/** A unique value for the tab item. Defaults to the index. */
|
/** A unique value for the tab item. Defaults to the index. */
|
||||||
value?: string | number
|
value?: string | number
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
class?: any
|
class?: any
|
||||||
ui?: Pick<Tabs['slots'], 'trigger' | 'leadingIcon' | 'leadingAvatar' | 'leadingAvatarSize' | 'label' | 'trailingBadge' | 'trailingBadgeSize' | 'content'>
|
ui?: Pick<Tabs['slots'], 'trigger' | 'leadingIcon' | 'leadingAvatar' | 'label' | 'content'>
|
||||||
[key: string]: any
|
[key: string]: any
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,23 +134,14 @@ defineExpose({
|
|||||||
>
|
>
|
||||||
<slot name="leading" :item="item" :index="index">
|
<slot name="leading" :item="item" :index="index">
|
||||||
<UIcon v-if="item.icon" :name="item.icon" :class="ui.leadingIcon({ class: [props.ui?.leadingIcon, item.ui?.leadingIcon] })" />
|
<UIcon v-if="item.icon" :name="item.icon" :class="ui.leadingIcon({ class: [props.ui?.leadingIcon, item.ui?.leadingIcon] })" />
|
||||||
<UAvatar v-else-if="item.avatar" :size="((item.ui?.leadingAvatarSize || props.ui?.leadingAvatarSize || ui.leadingAvatarSize()) as AvatarProps['size'])" v-bind="item.avatar" :class="ui.leadingAvatar({ class: [props.ui?.leadingAvatar, item.ui?.leadingAvatar] })" />
|
<UAvatar v-else-if="item.avatar" :size="((props.ui?.leadingAvatarSize || ui.leadingAvatarSize()) as AvatarProps['size'])" v-bind="item.avatar" :class="ui.leadingAvatar({ class: [props.ui?.leadingAvatar, item.ui?.leadingAvatar] })" />
|
||||||
</slot>
|
</slot>
|
||||||
|
|
||||||
<span v-if="get(item, props.labelKey as string) || !!slots.default" :class="ui.label({ class: [props.ui?.label, item.ui?.label] })">
|
<span v-if="get(item, props.labelKey as string) || !!slots.default" :class="ui.label({ class: [props.ui?.label, item.ui?.label] })">
|
||||||
<slot :item="item" :index="index">{{ get(item, props.labelKey as string) }}</slot>
|
<slot :item="item" :index="index">{{ get(item, props.labelKey as string) }}</slot>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<slot name="trailing" :item="item" :index="index">
|
<slot name="trailing" :item="item" :index="index" />
|
||||||
<UBadge
|
|
||||||
v-if="item.badge !== undefined"
|
|
||||||
color="neutral"
|
|
||||||
variant="outline"
|
|
||||||
:size="((item.ui?.trailingBadgeSize || props.ui?.trailingBadgeSize || ui.trailingBadgeSize()) as BadgeProps['size'])"
|
|
||||||
v-bind="(typeof item.badge === 'string' || typeof item.badge === 'number') ? { label: item.badge } : item.badge"
|
|
||||||
:class="ui.trailingBadge({ class: [props.ui?.trailingBadge, item.ui?.trailingBadge] })"
|
|
||||||
/>
|
|
||||||
</slot>
|
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
|
|
||||||
<slot name="list-trailing" />
|
<slot name="list-trailing" />
|
||||||
|
|||||||
@@ -55,9 +55,9 @@ export interface TextareaProps<T extends TextareaValue = TextareaValue> extends
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface TextareaEmits<T extends TextareaValue = TextareaValue> {
|
export interface TextareaEmits<T extends TextareaValue = TextareaValue> {
|
||||||
'update:modelValue': [payload: T]
|
(e: 'update:modelValue', payload: T): void
|
||||||
'blur': [event: FocusEvent]
|
(e: 'blur', event: FocusEvent): void
|
||||||
'change': [event: Event]
|
(e: 'change', event: Event): void
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TextareaSlots {
|
export interface TextareaSlots {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import type { ToastRootProps, ToastRootEmits } from 'reka-ui'
|
import type { ToastRootProps, ToastRootEmits } from 'reka-ui'
|
||||||
import type { AppConfig } from '@nuxt/schema'
|
import type { AppConfig } from '@nuxt/schema'
|
||||||
import theme from '#build/ui/toast'
|
import theme from '#build/ui/toast'
|
||||||
import type { AvatarProps, ButtonProps, ProgressProps } from '../types'
|
import type { AvatarProps, ButtonProps } from '../types'
|
||||||
import type { StringOrVNode, ComponentConfig } from '../types/utils'
|
import type { StringOrVNode, ComponentConfig } from '../types/utils'
|
||||||
|
|
||||||
type Toast = ComponentConfig<typeof theme, AppConfig, 'toast'>
|
type Toast = ComponentConfig<typeof theme, AppConfig, 'toast'>
|
||||||
@@ -29,6 +29,18 @@ export interface ToastProps extends Pick<ToastRootProps, 'defaultOpen' | 'open'
|
|||||||
* @defaultValue 'vertical'
|
* @defaultValue 'vertical'
|
||||||
*/
|
*/
|
||||||
orientation?: Toast['variants']['orientation']
|
orientation?: Toast['variants']['orientation']
|
||||||
|
/**
|
||||||
|
* Whether to show the progress bar.
|
||||||
|
* @defaultValue true
|
||||||
|
*/
|
||||||
|
progress?: boolean
|
||||||
|
/**
|
||||||
|
* Display a list of actions:
|
||||||
|
* - under the title and description when orientation is `vertical`
|
||||||
|
* - next to the close button when orientation is `horizontal`
|
||||||
|
* `{ size: 'xs' }`{lang="ts-type"}
|
||||||
|
*/
|
||||||
|
actions?: ButtonProps[]
|
||||||
/**
|
/**
|
||||||
* Display a close button to dismiss the toast.
|
* Display a close button to dismiss the toast.
|
||||||
* `{ size: 'md', color: 'neutral', variant: 'link' }`{lang="ts-type"}
|
* `{ size: 'md', color: 'neutral', variant: 'link' }`{lang="ts-type"}
|
||||||
@@ -41,19 +53,6 @@ export interface ToastProps extends Pick<ToastRootProps, 'defaultOpen' | 'open'
|
|||||||
* @IconifyIcon
|
* @IconifyIcon
|
||||||
*/
|
*/
|
||||||
closeIcon?: string
|
closeIcon?: string
|
||||||
/**
|
|
||||||
* Display a list of actions:
|
|
||||||
* - under the title and description when orientation is `vertical`
|
|
||||||
* - next to the close button when orientation is `horizontal`
|
|
||||||
* `{ size: 'xs' }`{lang="ts-type"}
|
|
||||||
*/
|
|
||||||
actions?: ButtonProps[]
|
|
||||||
/**
|
|
||||||
* Display a progress bar showing the toast's remaining duration.
|
|
||||||
* `{ size: 'sm' }`{lang="ts-type"}
|
|
||||||
* @defaultValue true
|
|
||||||
*/
|
|
||||||
progress?: boolean | Pick<ProgressProps, 'color'>
|
|
||||||
class?: any
|
class?: any
|
||||||
ui?: Toast['slots']
|
ui?: Toast['slots']
|
||||||
}
|
}
|
||||||
@@ -79,11 +78,10 @@ import { tv } from '../utils/tv'
|
|||||||
import UIcon from './Icon.vue'
|
import UIcon from './Icon.vue'
|
||||||
import UAvatar from './Avatar.vue'
|
import UAvatar from './Avatar.vue'
|
||||||
import UButton from './Button.vue'
|
import UButton from './Button.vue'
|
||||||
import UProgress from './Progress.vue'
|
|
||||||
|
|
||||||
const props = withDefaults(defineProps<ToastProps>(), {
|
const props = withDefaults(defineProps<ToastProps>(), {
|
||||||
orientation: 'vertical',
|
|
||||||
close: true,
|
close: true,
|
||||||
|
orientation: 'vertical',
|
||||||
progress: true
|
progress: true
|
||||||
})
|
})
|
||||||
const emits = defineEmits<ToastEmits>()
|
const emits = defineEmits<ToastEmits>()
|
||||||
@@ -121,7 +119,7 @@ defineExpose({
|
|||||||
<template>
|
<template>
|
||||||
<ToastRoot
|
<ToastRoot
|
||||||
ref="el"
|
ref="el"
|
||||||
v-slot="{ remaining, duration, open }"
|
v-slot="{ remaining, duration }"
|
||||||
v-bind="rootProps"
|
v-bind="rootProps"
|
||||||
:data-orientation="orientation"
|
:data-orientation="orientation"
|
||||||
:class="ui.root({ class: [props.ui?.root, props.class] })"
|
:class="ui.root({ class: [props.ui?.root, props.class] })"
|
||||||
@@ -186,13 +184,6 @@ defineExpose({
|
|||||||
</ToastClose>
|
</ToastClose>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<UProgress
|
<div v-if="progress && remaining > 0 && duration" :class="ui.progress({ class: props.ui?.progress })" :style="{ width: `${remaining / duration * 100}%` }" />
|
||||||
v-if="progress && open && remaining > 0 && duration"
|
|
||||||
:model-value="remaining / duration * 100"
|
|
||||||
:color="color"
|
|
||||||
v-bind="(typeof progress === 'object' ? progress as Partial<ProgressProps> : {})"
|
|
||||||
size="sm"
|
|
||||||
:class="ui.progress({ class: props.ui?.progress })"
|
|
||||||
/>
|
|
||||||
</ToastRoot>
|
</ToastRoot>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -107,8 +107,6 @@ import { get } from '../utils'
|
|||||||
import { tv } from '../utils/tv'
|
import { tv } from '../utils/tv'
|
||||||
import UIcon from './Icon.vue'
|
import UIcon from './Icon.vue'
|
||||||
|
|
||||||
defineOptions({ inheritAttrs: false })
|
|
||||||
|
|
||||||
const props = withDefaults(defineProps<TreeProps<T, VK, M>>(), {
|
const props = withDefaults(defineProps<TreeProps<T, VK, M>>(), {
|
||||||
labelKey: 'label' as never,
|
labelKey: 'label' as never,
|
||||||
valueKey: 'value' as never
|
valueKey: 'value' as never
|
||||||
@@ -163,7 +161,7 @@ const defaultExpanded = computed(() =>
|
|||||||
@toggle="item.onToggle"
|
@toggle="item.onToggle"
|
||||||
@select="item.onSelect"
|
@select="item.onSelect"
|
||||||
>
|
>
|
||||||
<button type="button" :disabled="item.disabled || disabled" :class="ui.link({ class: [props.ui?.link, item.ui?.link, item.class], selected: isSelected, disabled: item.disabled || disabled })">
|
<button :disabled="item.disabled || disabled" :class="ui.link({ class: [props.ui?.link, item.ui?.link, item.class], selected: isSelected, disabled: item.disabled || disabled })">
|
||||||
<slot :name="((item.slot || 'item') as keyof TreeSlots<T>)" v-bind="{ index, level, expanded: isExpanded, selected: isSelected }" :item="(item as Extract<NestedItem<T>, { slot: string; }>)">
|
<slot :name="((item.slot || 'item') as keyof TreeSlots<T>)" v-bind="{ index, level, expanded: isExpanded, selected: isSelected }" :item="(item as Extract<NestedItem<T>, { slot: string; }>)">
|
||||||
<slot :name="((item.slot ? `${item.slot}-leading`: 'item-leading') as keyof TreeSlots<T>)" v-bind="{ index, level, expanded: isExpanded, selected: isSelected }" :item="(item as Extract<NestedItem<T>, { slot: string; }>)">
|
<slot :name="((item.slot ? `${item.slot}-leading`: 'item-leading') as keyof TreeSlots<T>)" v-bind="{ index, level, expanded: isExpanded, selected: isSelected }" :item="(item as Extract<NestedItem<T>, { slot: string; }>)">
|
||||||
<UIcon
|
<UIcon
|
||||||
@@ -201,7 +199,7 @@ const defaultExpanded = computed(() =>
|
|||||||
</DefineTreeTemplate>
|
</DefineTreeTemplate>
|
||||||
|
|
||||||
<TreeRoot
|
<TreeRoot
|
||||||
v-bind="{ ...(rootProps as unknown as TreeRootProps<NestedItem<T>>), ...$attrs }"
|
v-bind="(rootProps as unknown as TreeRootProps<NestedItem<T>>)"
|
||||||
:class="ui.root({ class: [props.ui?.root, props.class] })"
|
:class="ui.root({ class: [props.ui?.root, props.class] })"
|
||||||
:get-key="getItemValue"
|
:get-key="getItemValue"
|
||||||
:default-expanded="defaultExpanded"
|
:default-expanded="defaultExpanded"
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ export function defineShortcuts(config: MaybeRef<ShortcutsConfig>, options: Shor
|
|||||||
|
|
||||||
if (shortcut.enabled) {
|
if (shortcut.enabled) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
shortcut.handler(e)
|
shortcut.handler()
|
||||||
}
|
}
|
||||||
clearChainedInput()
|
clearChainedInput()
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -89,15 +89,10 @@ export function useFormField<T>(props?: Props<T>, opts?: { bind?: boolean, defer
|
|||||||
.filter(type => formField?.value?.[type])
|
.filter(type => formField?.value?.[type])
|
||||||
.map(type => `${formField?.value.ariaId}-${type}`) || []
|
.map(type => `${formField?.value.ariaId}-${type}`) || []
|
||||||
|
|
||||||
const attrs: Record<string, any> = {
|
return {
|
||||||
|
'aria-describedby': descriptiveAttrs.join(' '),
|
||||||
'aria-invalid': !!formField?.value.error
|
'aria-invalid': !!formField?.value.error
|
||||||
}
|
}
|
||||||
|
|
||||||
if (descriptiveAttrs.length > 0) {
|
|
||||||
attrs['aria-describedby'] = descriptiveAttrs.join(' ')
|
|
||||||
}
|
|
||||||
|
|
||||||
return attrs
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ function _useOverlay() {
|
|||||||
isMounted: !!defaultOpen,
|
isMounted: !!defaultOpen,
|
||||||
destroyOnClose: !!destroyOnClose,
|
destroyOnClose: !!destroyOnClose,
|
||||||
originalProps: props || {},
|
originalProps: props || {},
|
||||||
props: { ...props }
|
props: { ...(props || {}) }
|
||||||
})
|
})
|
||||||
|
|
||||||
overlays.push(options)
|
overlays.push(options)
|
||||||
@@ -87,11 +87,11 @@ function _useOverlay() {
|
|||||||
const open = <T extends Component>(id: symbol, props?: ComponentProps<T>): OpenedOverlay<T> => {
|
const open = <T extends Component>(id: symbol, props?: ComponentProps<T>): OpenedOverlay<T> => {
|
||||||
const overlay = getOverlay(id)
|
const overlay = getOverlay(id)
|
||||||
|
|
||||||
// If props are provided, merge them with the original props, otherwise use the original props
|
// If props are provided, update the overlay's props
|
||||||
if (props) {
|
if (props) {
|
||||||
overlay.props = { ...overlay.originalProps, ...props }
|
patch(overlay.id, props)
|
||||||
} else {
|
} else {
|
||||||
overlay.props = { ...overlay.originalProps }
|
patch(overlay.id, overlay.originalProps)
|
||||||
}
|
}
|
||||||
|
|
||||||
overlay.isOpen = true
|
overlay.isOpen = true
|
||||||
@@ -135,7 +135,7 @@ function _useOverlay() {
|
|||||||
const patch = <T extends Component>(id: symbol, props: Partial<ComponentProps<T>>): void => {
|
const patch = <T extends Component>(id: symbol, props: Partial<ComponentProps<T>>): void => {
|
||||||
const overlay = getOverlay(id)
|
const overlay = getOverlay(id)
|
||||||
|
|
||||||
overlay.props = { ...overlay.props, ...props }
|
overlay.props = { ...props }
|
||||||
}
|
}
|
||||||
|
|
||||||
const getOverlay = (id: symbol): Overlay => {
|
const getOverlay = (id: symbol): Overlay => {
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import { ref, onScopeDispose } from 'vue'
|
import { ref, onScopeDispose } from 'vue'
|
||||||
import type { Ref, Plugin as VuePlugin } from 'vue'
|
import type { Ref, Plugin as VuePlugin } from 'vue'
|
||||||
import { createHooks } from 'hookable'
|
import { createHooks } from 'hookable'
|
||||||
import { usePage } from '@inertiajs/vue3'
|
|
||||||
import { useColorMode as useColorModeVueUse } from '@vueuse/core'
|
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
import type { NuxtApp } from '#app'
|
import type { NuxtApp } from '#app'
|
||||||
|
import { useColorMode as useColorModeVueUse } from '@vueuse/core'
|
||||||
|
import { usePage } from '@inertiajs/vue3'
|
||||||
|
|
||||||
export { useHead } from '@unhead/vue'
|
export { useHead } from '@unhead/vue'
|
||||||
|
|
||||||
@@ -15,7 +16,6 @@ export { useLocale } from '../composables/useLocale'
|
|||||||
|
|
||||||
export const useRoute = () => {
|
export const useRoute = () => {
|
||||||
const page = usePage()
|
const page = usePage()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
fullPath: page.url
|
fullPath: page.url
|
||||||
}
|
}
|
||||||
@@ -25,10 +25,6 @@ export const useRouter = () => {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const clearError = () => {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export const useColorMode = () => {
|
export const useColorMode = () => {
|
||||||
if (!appConfig.colorMode) {
|
if (!appConfig.colorMode) {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -40,8 +40,7 @@ export default defineLocale<Messages>({
|
|||||||
carousel: {
|
carousel: {
|
||||||
prev: 'السابق',
|
prev: 'السابق',
|
||||||
next: 'التالي',
|
next: 'التالي',
|
||||||
dots: 'اختر الشريحة المراد عرضها',
|
goto: 'الذهاب إلي شريحة {slide}'
|
||||||
goto: 'الذهاب إلى شريحة {slide}'
|
|
||||||
},
|
},
|
||||||
modal: {
|
modal: {
|
||||||
close: 'إغلاق'
|
close: 'إغلاق'
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ export default defineLocale<Messages>({
|
|||||||
carousel: {
|
carousel: {
|
||||||
prev: 'Əvvəlki',
|
prev: 'Əvvəlki',
|
||||||
next: 'Növbəti',
|
next: 'Növbəti',
|
||||||
dots: 'Göstərmək üçün slayd seçin',
|
|
||||||
goto: 'Slayd {slide} keç'
|
goto: 'Slayd {slide} keç'
|
||||||
},
|
},
|
||||||
modal: {
|
modal: {
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ export default defineLocale<Messages>({
|
|||||||
carousel: {
|
carousel: {
|
||||||
prev: 'Назад',
|
prev: 'Назад',
|
||||||
next: 'Напред',
|
next: 'Напред',
|
||||||
dots: 'Изберете слайд за показване',
|
|
||||||
goto: 'Отидете на слайд {slide}'
|
goto: 'Отидете на слайд {slide}'
|
||||||
},
|
},
|
||||||
modal: {
|
modal: {
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ export default defineLocale<Messages>({
|
|||||||
carousel: {
|
carousel: {
|
||||||
prev: 'পূর্ববর্তী',
|
prev: 'পূর্ববর্তী',
|
||||||
next: 'পরবর্তী',
|
next: 'পরবর্তী',
|
||||||
dots: 'প্রদর্শনের জন্য স্লাইড নির্বাচন করুন',
|
|
||||||
goto: 'স্লাইড {slide} এ যান'
|
goto: 'স্লাইড {slide} এ যান'
|
||||||
},
|
},
|
||||||
modal: {
|
modal: {
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ export default defineLocale<Messages>({
|
|||||||
carousel: {
|
carousel: {
|
||||||
prev: 'Anterior',
|
prev: 'Anterior',
|
||||||
next: 'Següent',
|
next: 'Següent',
|
||||||
dots: 'Tria la diapositiva a mostrar',
|
|
||||||
goto: 'Anar a la diapositiva {slide}'
|
goto: 'Anar a la diapositiva {slide}'
|
||||||
},
|
},
|
||||||
modal: {
|
modal: {
|
||||||
|
|||||||
@@ -38,9 +38,8 @@ export default defineLocale<Messages>({
|
|||||||
close: 'داخستن'
|
close: 'داخستن'
|
||||||
},
|
},
|
||||||
carousel: {
|
carousel: {
|
||||||
prev: 'پێشووی',
|
prev: 'پێشوو',
|
||||||
next: 'داهاتوو',
|
next: 'داهاتوو',
|
||||||
dots: 'سلایدێک هەڵبژێرە بۆ پیشاندان',
|
|
||||||
goto: 'بڕۆ بۆ سلایدی {slide}'
|
goto: 'بڕۆ بۆ سلایدی {slide}'
|
||||||
},
|
},
|
||||||
modal: {
|
modal: {
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ export default defineLocale<Messages>({
|
|||||||
carousel: {
|
carousel: {
|
||||||
prev: 'Předchozí',
|
prev: 'Předchozí',
|
||||||
next: 'Další',
|
next: 'Další',
|
||||||
dots: 'Vyberte snímek k zobrazení',
|
|
||||||
goto: 'Přejít na {slide}'
|
goto: 'Přejít na {slide}'
|
||||||
},
|
},
|
||||||
modal: {
|
modal: {
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ export default defineLocale<Messages>({
|
|||||||
carousel: {
|
carousel: {
|
||||||
prev: 'Forrige',
|
prev: 'Forrige',
|
||||||
next: 'Næste',
|
next: 'Næste',
|
||||||
dots: 'Vælg dias til visning',
|
|
||||||
goto: 'Gå til slide {slide}'
|
goto: 'Gå til slide {slide}'
|
||||||
},
|
},
|
||||||
modal: {
|
modal: {
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ export default defineLocale<Messages>({
|
|||||||
carousel: {
|
carousel: {
|
||||||
prev: 'Zurück',
|
prev: 'Zurück',
|
||||||
next: 'Weiter',
|
next: 'Weiter',
|
||||||
dots: 'Folie zur Anzeige auswählen',
|
|
||||||
goto: 'Gehe zu {slide}'
|
goto: 'Gehe zu {slide}'
|
||||||
},
|
},
|
||||||
modal: {
|
modal: {
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ export default defineLocale<Messages>({
|
|||||||
carousel: {
|
carousel: {
|
||||||
prev: 'Προηγούμενο',
|
prev: 'Προηγούμενο',
|
||||||
next: 'Επόμενο',
|
next: 'Επόμενο',
|
||||||
dots: 'Επιλέξτε διαφάνεια για εμφάνιση',
|
|
||||||
goto: 'Μετάβαση στη διαφάνεια {slide}'
|
goto: 'Μετάβαση στη διαφάνεια {slide}'
|
||||||
},
|
},
|
||||||
modal: {
|
modal: {
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ export default defineLocale<Messages>({
|
|||||||
carousel: {
|
carousel: {
|
||||||
prev: 'Prev',
|
prev: 'Prev',
|
||||||
next: 'Next',
|
next: 'Next',
|
||||||
dots: 'Choose slide to display',
|
|
||||||
goto: 'Go to slide {slide}'
|
goto: 'Go to slide {slide}'
|
||||||
},
|
},
|
||||||
modal: {
|
modal: {
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ export default defineLocale<Messages>({
|
|||||||
carousel: {
|
carousel: {
|
||||||
prev: 'Anterior',
|
prev: 'Anterior',
|
||||||
next: 'Siguiente',
|
next: 'Siguiente',
|
||||||
dots: 'Elegir diapositiva a mostrar',
|
|
||||||
goto: 'Ir a la diapositiva {slide}'
|
goto: 'Ir a la diapositiva {slide}'
|
||||||
},
|
},
|
||||||
modal: {
|
modal: {
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ export default defineLocale<Messages>({
|
|||||||
carousel: {
|
carousel: {
|
||||||
prev: 'Eel',
|
prev: 'Eel',
|
||||||
next: 'Järg',
|
next: 'Järg',
|
||||||
dots: 'Valige kuvatav slaid',
|
|
||||||
goto: 'Mine slaidile {slide}'
|
goto: 'Mine slaidile {slide}'
|
||||||
},
|
},
|
||||||
modal: {
|
modal: {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user