mirror of
https://github.com/ArthurDanjou/ui.git
synced 2026-01-28 19:00:35 +01:00
feat(module)!: migrate to reka-ui (#2448)
This commit is contained in:
@@ -7,7 +7,7 @@
|
|||||||
[![License][license-src]][license-href]
|
[![License][license-src]][license-href]
|
||||||
[![Nuxt][nuxt-src]][nuxt-href]
|
[![Nuxt][nuxt-src]][nuxt-href]
|
||||||
|
|
||||||
We're thrilled to introduce Nuxt UI v3, a significant upgrade to our UI library that delivers extensive improvements and robust new capabilities. This major update harnesses the combined strengths of [Radix Vue](https://www.radix-vue.com/), [Tailwind CSS v4](https://tailwindcss.com/docs/v4-beta), and [Tailwind Variants](https://www.tailwind-variants.org/) to offer developers an unparalleled set of tools for creating sophisticated, accessible, and highly performant user interfaces.
|
We're thrilled to introduce Nuxt UI v3, a significant upgrade to our UI library that delivers extensive improvements and robust new capabilities. This major update harnesses the combined strengths of [Reka UI](https://reka-ui.com/), [Tailwind CSS v4](https://tailwindcss.com/docs/v4-beta), and [Tailwind Variants](https://www.tailwind-variants.org/) to offer developers an unparalleled set of tools for creating sophisticated, accessible, and highly performant user interfaces.
|
||||||
|
|
||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> You are on the `v3` development branch, check out the [dev branch](https://github.com/nuxt/ui) for Nuxt UI v2.
|
> You are on the `v3` development branch, check out the [dev branch](https://github.com/nuxt/ui) for Nuxt UI v2.
|
||||||
@@ -99,7 +99,7 @@ Learn more in the [installation guide](https://ui3.nuxt.dev/getting-started/inst
|
|||||||
- [nuxt/icon](https://github.com/nuxt/icon)
|
- [nuxt/icon](https://github.com/nuxt/icon)
|
||||||
- [nuxt/fonts](https://github.com/nuxt/fonts)
|
- [nuxt/fonts](https://github.com/nuxt/fonts)
|
||||||
- [nuxt-modules/color-mode](https://github.com/nuxt-modules/color-mode)
|
- [nuxt-modules/color-mode](https://github.com/nuxt-modules/color-mode)
|
||||||
- [radix-vue/radix-vue](https://github.com/radix-vue/radix-vue)
|
- [unovue/reka-ui](https://github.com/unovue/reka-ui)
|
||||||
- [tailwindlabs/tailwindcss](https://github.com/tailwindlabs/tailwindcss)
|
- [tailwindlabs/tailwindcss](https://github.com/tailwindlabs/tailwindcss)
|
||||||
- [vueuse/vueuse](https://github.com/vueuse/vueuse)
|
- [vueuse/vueuse](https://github.com/vueuse/vueuse)
|
||||||
|
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ export interface ${upperName}Slots {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Primitive } from 'radix-vue'
|
import { Primitive } from 'reka-ui'
|
||||||
|
|
||||||
const props = withDefaults(defineProps<${upperName}Props>(), { as: 'div' })
|
const props = withDefaults(defineProps<${upperName}Props>(), { as: 'div' })
|
||||||
defineSlots<${upperName}Slots>()
|
defineSlots<${upperName}Slots>()
|
||||||
@@ -72,7 +72,7 @@ const ui = ${camelName}()
|
|||||||
: `
|
: `
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { tv, type VariantProps } from 'tailwind-variants'
|
import { tv, type VariantProps } from 'tailwind-variants'
|
||||||
import type { ${upperName}RootProps, ${upperName}RootEmits } from 'radix-vue'
|
import type { ${upperName}RootProps, ${upperName}RootEmits } from 'reka-ui'
|
||||||
import type { AppConfig } from '@nuxt/schema'
|
import type { AppConfig } from '@nuxt/schema'
|
||||||
import _appConfig from '#build/app.config'
|
import _appConfig from '#build/app.config'
|
||||||
import theme from '#build/${path}/${prose ? 'prose/' : ''}${content ? 'content/' : ''}${kebabName}'
|
import theme from '#build/${path}/${prose ? 'prose/' : ''}${content ? 'content/' : ''}${kebabName}'
|
||||||
@@ -94,7 +94,7 @@ export interface ${upperName}Slots {}
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ${upperName}Root, useForwardPropsEmits } from 'radix-vue'
|
import { ${upperName}Root, useForwardPropsEmits } from 'reka-ui'
|
||||||
import { reactivePick } from '@vueuse/core'
|
import { reactivePick } from '@vueuse/core'
|
||||||
|
|
||||||
const props = defineProps<${upperName}Props>()
|
const props = defineProps<${upperName}Props>()
|
||||||
@@ -149,7 +149,7 @@ import ComponentRender from '../${content ? '../' : ''}component-render'
|
|||||||
describe('${upperName}', () => {
|
describe('${upperName}', () => {
|
||||||
it.each([
|
it.each([
|
||||||
// Props
|
// Props
|
||||||
['with as', { props: { as: 'div' } }],
|
['with as', { props: { as: 'section' } }],
|
||||||
['with class', { props: { class: '' } }],
|
['with class', { props: { class: '' } }],
|
||||||
['with ui', { props: { ui: {} } }],
|
['with ui', { props: { ui: {} } }],
|
||||||
// Slots
|
// Slots
|
||||||
@@ -175,8 +175,8 @@ links: ${pro
|
|||||||
? ''
|
? ''
|
||||||
: `
|
: `
|
||||||
- label: ${upperName}
|
- label: ${upperName}
|
||||||
icon: i-custom-radix-vue
|
icon: i-custom-reka-ui
|
||||||
to: https://www.radix-vue.com/components/${kebabName}.html`}
|
to: https://www.reka-ui.com/components/${kebabName}.html`}
|
||||||
- label: GitHub
|
- label: GitHub
|
||||||
icon: i-simple-icons-github
|
icon: i-simple-icons-github
|
||||||
to: https://github.com/nuxt/${pro ? 'ui-pro' : 'ui'}/tree/v3/src/runtime/components/${upperName}.vue
|
to: https://github.com/nuxt/${pro ? 'ui-pro' : 'ui'}/tree/v3/src/runtime/components/${upperName}.vue
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ provide('navigation', mappedNavigation)
|
|||||||
<NuxtLoadingIndicator color="#FFF" />
|
<NuxtLoadingIndicator color="#FFF" />
|
||||||
|
|
||||||
<template v-if="!route.path.startsWith('/examples')">
|
<template v-if="!route.path.startsWith('/examples')">
|
||||||
<Banner />
|
<!-- <Banner /> -->
|
||||||
|
|
||||||
<Header :links="links" />
|
<Header :links="links" />
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |
@@ -7,7 +7,7 @@ const { frameworks } = useSharedData()
|
|||||||
v-slot="{ open }"
|
v-slot="{ open }"
|
||||||
:modal="false"
|
:modal="false"
|
||||||
:items="frameworks"
|
:items="frameworks"
|
||||||
:ui="{ content: 'w-(--radix-dropdown-menu-trigger-width)' }"
|
:ui="{ content: 'w-(--reka-dropdown-menu-trigger-width)' }"
|
||||||
>
|
>
|
||||||
<UButton
|
<UButton
|
||||||
color="neutral"
|
color="neutral"
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ defineShortcuts({
|
|||||||
v-slot="{ open }"
|
v-slot="{ open }"
|
||||||
:modal="false"
|
:modal="false"
|
||||||
:items="[{ label: `v${config.version}`, active: true, color: 'primary', checked: true, type: 'checkbox' }, { label: 'v2.19', to: 'https://ui.nuxt.com' }]"
|
:items="[{ label: `v${config.version}`, active: true, color: 'primary', checked: true, type: 'checkbox' }, { label: 'v2.19', to: 'https://ui.nuxt.com' }]"
|
||||||
:ui="{ content: 'w-(--radix-dropdown-menu-trigger-width) min-w-0' }"
|
:ui="{ content: 'w-(--reka-dropdown-menu-trigger-width) min-w-0' }"
|
||||||
size="xs"
|
size="xs"
|
||||||
>
|
>
|
||||||
<UButton
|
<UButton
|
||||||
|
|||||||
@@ -280,7 +280,7 @@ const { data: ast } = await useAsyncData(`component-code-${name}-${hash({ props:
|
|||||||
container: 'mt-0'
|
container: 'mt-0'
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<USelectMenu
|
<USelect
|
||||||
v-if="option.items?.length"
|
v-if="option.items?.length"
|
||||||
:model-value="getComponentProp(option.name)"
|
:model-value="getComponentProp(option.name)"
|
||||||
:items="option.items"
|
:items="option.items"
|
||||||
@@ -288,7 +288,6 @@ const { data: ast } = await useAsyncData(`component-code-${name}-${hash({ props:
|
|||||||
color="neutral"
|
color="neutral"
|
||||||
variant="soft"
|
variant="soft"
|
||||||
class="rounded-[var(--ui-radius)] rounded-l-none min-w-12"
|
class="rounded-[var(--ui-radius)] rounded-l-none min-w-12"
|
||||||
:search-input="false"
|
|
||||||
:class="[option.name.toLowerCase().endsWith('color') && 'pl-6']"
|
:class="[option.name.toLowerCase().endsWith('color') && 'pl-6']"
|
||||||
:ui="{ itemLeadingChip: 'size-2' }"
|
:ui="{ itemLeadingChip: 'size-2' }"
|
||||||
@update:model-value="setComponentProp(option.name, $event)"
|
@update:model-value="setComponentProp(option.name, $event)"
|
||||||
@@ -303,7 +302,7 @@ const { data: ast } = await useAsyncData(`component-code-${name}-${hash({ props:
|
|||||||
class="size-2"
|
class="size-2"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</USelectMenu>
|
</USelect>
|
||||||
<UInput
|
<UInput
|
||||||
v-else
|
v-else
|
||||||
:type="option.type?.includes('number') ? 'number' : 'text'"
|
:type="option.type?.includes('number') ? 'number' : 'text'"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Slot } from 'radix-vue'
|
import { Slot } from 'reka-ui'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { CalendarDate } from '@internationalized/date'
|
import { CalendarDate } from '@internationalized/date'
|
||||||
import type { Matcher } from 'radix-vue/date'
|
import type { Matcher } from 'reka-ui/date'
|
||||||
|
|
||||||
const modelValue = shallowRef({
|
const modelValue = shallowRef({
|
||||||
start: new CalendarDate(2022, 1, 1),
|
start: new CalendarDate(2022, 1, 1),
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { CalendarDate } from '@internationalized/date'
|
import { CalendarDate } from '@internationalized/date'
|
||||||
import type { Matcher } from 'radix-vue/date'
|
import type { Matcher } from 'reka-ui/date'
|
||||||
|
|
||||||
const modelValue = shallowRef({
|
const modelValue = shallowRef({
|
||||||
start: new CalendarDate(2022, 1, 1),
|
start: new CalendarDate(2022, 1, 1),
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ const groups = computed(() => [{
|
|||||||
id: 'users',
|
id: 'users',
|
||||||
label: searchTerm.value ? `Users matching “${searchTerm.value}”...` : 'Users',
|
label: searchTerm.value ? `Users matching “${searchTerm.value}”...` : 'Users',
|
||||||
items: users.value || [],
|
items: users.value || [],
|
||||||
filter: false
|
ignoreFilter: true
|
||||||
}])
|
}])
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -11,8 +11,8 @@ const items = [
|
|||||||
level: 2
|
level: 2
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '/getting-started#radix-vue-3',
|
id: '/getting-started#reka-ui-radix-vue',
|
||||||
label: 'Radix Vue',
|
label: 'Reka UI',
|
||||||
level: 3
|
level: 3
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ const users = [
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
const searchTerm = ref('')
|
const searchTerm = ref('B')
|
||||||
|
|
||||||
function onSelect() {
|
function onSelect() {
|
||||||
searchTerm.value = ''
|
searchTerm.value = ''
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ const groups = computed(() => [{
|
|||||||
id: 'users',
|
id: 'users',
|
||||||
label: searchTerm.value ? `Users matching “${searchTerm.value}”...` : 'Users',
|
label: searchTerm.value ? `Users matching “${searchTerm.value}”...` : 'Users',
|
||||||
items: users.value || [],
|
items: users.value || [],
|
||||||
filter: false
|
ignoreFilter: true
|
||||||
}])
|
}])
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
|
||||||
|
const value = ref('Backlog')
|
||||||
|
|
||||||
|
function onCreate(item: string) {
|
||||||
|
items.value.push(item)
|
||||||
|
|
||||||
|
value.value = item
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UInputMenu
|
||||||
|
v-model="value"
|
||||||
|
create-item
|
||||||
|
:items="items"
|
||||||
|
class="w-48"
|
||||||
|
@create="onCreate"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
@@ -16,7 +16,7 @@ const { data: users, status } = await useFetch('https://jsonplaceholder.typicode
|
|||||||
<UInputMenu
|
<UInputMenu
|
||||||
:items="users || []"
|
:items="users || []"
|
||||||
:loading="status === 'pending'"
|
:loading="status === 'pending'"
|
||||||
:filter="['label', 'email']"
|
:filter-fields="['label', 'email']"
|
||||||
icon="i-lucide-user"
|
icon="i-lucide-user"
|
||||||
placeholder="Select user"
|
placeholder="Select user"
|
||||||
class="w-80"
|
class="w-80"
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ const { data: users, status } = await useFetch('https://jsonplaceholder.typicode
|
|||||||
v-model:search-term="searchTerm"
|
v-model:search-term="searchTerm"
|
||||||
:items="users || []"
|
:items="users || []"
|
||||||
:loading="status === 'pending'"
|
:loading="status === 'pending'"
|
||||||
:filter="false"
|
ignore-filter
|
||||||
icon="i-lucide-user"
|
icon="i-lucide-user"
|
||||||
placeholder="Select user"
|
placeholder="Select user"
|
||||||
>
|
>
|
||||||
@@ -13,7 +13,7 @@ const groups = computed(() => [{
|
|||||||
id: 'users',
|
id: 'users',
|
||||||
label: searchTerm.value ? `Users matching “${searchTerm.value}”...` : 'Users',
|
label: searchTerm.value ? `Users matching “${searchTerm.value}”...` : 'Users',
|
||||||
items: users.value || [],
|
items: users.value || [],
|
||||||
filter: false
|
ignoreFilter: true
|
||||||
}])
|
}])
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ const items = [
|
|||||||
:items="items"
|
:items="items"
|
||||||
class="justify-center"
|
class="justify-center"
|
||||||
:ui="{
|
:ui="{
|
||||||
viewport: 'sm:w-[var(--radix-navigation-menu-viewport-width)]',
|
viewport: 'sm:w-[var(--reka-navigation-menu-viewport-width)]',
|
||||||
childList: 'sm:w-96',
|
childList: 'sm:w-96',
|
||||||
childLinkDescription: 'text-balance line-clamp-2'
|
childLinkDescription: 'text-balance line-clamp-2'
|
||||||
}"
|
}"
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
|
||||||
|
const value = ref('Backlog')
|
||||||
|
|
||||||
|
function onCreate(item: string) {
|
||||||
|
items.value.push(item)
|
||||||
|
|
||||||
|
value.value = item
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<USelectMenu
|
||||||
|
v-model="value"
|
||||||
|
create-item
|
||||||
|
:items="items"
|
||||||
|
class="w-48"
|
||||||
|
@create="onCreate"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
@@ -16,7 +16,7 @@ const { data: users, status } = await useFetch('https://jsonplaceholder.typicode
|
|||||||
<USelectMenu
|
<USelectMenu
|
||||||
:items="users || []"
|
:items="users || []"
|
||||||
:loading="status === 'pending'"
|
:loading="status === 'pending'"
|
||||||
:filter="['label', 'email']"
|
:filter-fields="['label', 'email']"
|
||||||
icon="i-lucide-user"
|
icon="i-lucide-user"
|
||||||
placeholder="Select user"
|
placeholder="Select user"
|
||||||
class="w-80"
|
class="w-80"
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ const { data: users, status } = await useFetch('https://jsonplaceholder.typicode
|
|||||||
v-model:search-term="searchTerm"
|
v-model:search-term="searchTerm"
|
||||||
:items="users || []"
|
:items="users || []"
|
||||||
:loading="status === 'pending'"
|
:loading="status === 'pending'"
|
||||||
:filter="false"
|
ignore-filter
|
||||||
icon="i-lucide-user"
|
icon="i-lucide-user"
|
||||||
placeholder="Select user"
|
placeholder="Select user"
|
||||||
class="w-48"
|
class="w-48"
|
||||||
@@ -26,7 +26,7 @@ function getUserAvatar(value: string) {
|
|||||||
<template #leading="{ modelValue, ui }">
|
<template #leading="{ modelValue, ui }">
|
||||||
<UAvatar
|
<UAvatar
|
||||||
v-if="modelValue"
|
v-if="modelValue"
|
||||||
v-bind="getUserAvatar(modelValue)"
|
v-bind="getUserAvatar(modelValue as string)"
|
||||||
:size="ui.leadingAvatarSize()"
|
:size="ui.leadingAvatarSize()"
|
||||||
:class="ui.leadingAvatar()"
|
:class="ui.leadingAvatar()"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ function getChip(value: string) {
|
|||||||
<template #leading="{ modelValue, ui }">
|
<template #leading="{ modelValue, ui }">
|
||||||
<UChip
|
<UChip
|
||||||
v-if="modelValue"
|
v-if="modelValue"
|
||||||
v-bind="getChip(modelValue)"
|
v-bind="getChip(modelValue as string)"
|
||||||
inset
|
inset
|
||||||
standalone
|
standalone
|
||||||
:size="ui.itemLeadingChipSize()"
|
:size="ui.itemLeadingChipSize()"
|
||||||
|
|||||||
@@ -143,14 +143,13 @@ const data = ref<Payment[]>([{
|
|||||||
const columns: TableColumn<Payment>[] = [{
|
const columns: TableColumn<Payment>[] = [{
|
||||||
id: 'select',
|
id: 'select',
|
||||||
header: ({ table }) => h(UCheckbox, {
|
header: ({ table }) => h(UCheckbox, {
|
||||||
'modelValue': table.getIsAllPageRowsSelected(),
|
'modelValue': table.getIsSomePageRowsSelected() ? 'indeterminate' : table.getIsAllPageRowsSelected(),
|
||||||
'indeterminate': table.getIsSomePageRowsSelected(),
|
'onUpdate:modelValue': (value: boolean | 'indeterminate') => table.toggleAllPageRowsSelected(!!value),
|
||||||
'onUpdate:modelValue': (value: boolean) => table.toggleAllPageRowsSelected(!!value),
|
|
||||||
'ariaLabel': 'Select all'
|
'ariaLabel': 'Select all'
|
||||||
}),
|
}),
|
||||||
cell: ({ row }) => h(UCheckbox, {
|
cell: ({ row }) => h(UCheckbox, {
|
||||||
'modelValue': row.getIsSelected(),
|
'modelValue': row.getIsSelected(),
|
||||||
'onUpdate:modelValue': (value: boolean) => row.toggleSelected(!!value),
|
'onUpdate:modelValue': (value: boolean | 'indeterminate') => row.toggleSelected(!!value),
|
||||||
'ariaLabel': 'Select row'
|
'ariaLabel': 'Select row'
|
||||||
}),
|
}),
|
||||||
enableSorting: false,
|
enableSorting: false,
|
||||||
|
|||||||
@@ -48,14 +48,13 @@ const data = ref<Payment[]>([{
|
|||||||
const columns: TableColumn<Payment>[] = [{
|
const columns: TableColumn<Payment>[] = [{
|
||||||
id: 'select',
|
id: 'select',
|
||||||
header: ({ table }) => h(UCheckbox, {
|
header: ({ table }) => h(UCheckbox, {
|
||||||
'modelValue': table.getIsAllPageRowsSelected(),
|
'modelValue': table.getIsSomePageRowsSelected() ? 'indeterminate' : table.getIsAllPageRowsSelected(),
|
||||||
'indeterminate': table.getIsSomePageRowsSelected(),
|
'onUpdate:modelValue': (value: boolean | 'indeterminate') => table.toggleAllPageRowsSelected(!!value),
|
||||||
'onUpdate:modelValue': (value: boolean) => table.toggleAllPageRowsSelected(!!value),
|
|
||||||
'ariaLabel': 'Select all'
|
'ariaLabel': 'Select all'
|
||||||
}),
|
}),
|
||||||
cell: ({ row }) => h(UCheckbox, {
|
cell: ({ row }) => h(UCheckbox, {
|
||||||
'modelValue': row.getIsSelected(),
|
'modelValue': row.getIsSelected(),
|
||||||
'onUpdate:modelValue': (value: boolean) => row.toggleSelected(!!value),
|
'onUpdate:modelValue': (value: boolean | 'indeterminate') => row.toggleSelected(!!value),
|
||||||
'ariaLabel': 'Select row'
|
'ariaLabel': 'Select row'
|
||||||
})
|
})
|
||||||
}, {
|
}, {
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ provide('navigation', mappedNavigation)
|
|||||||
<UApp>
|
<UApp>
|
||||||
<NuxtLoadingIndicator color="#FFF" />
|
<NuxtLoadingIndicator color="#FFF" />
|
||||||
|
|
||||||
<Banner />
|
<!-- <Banner /> -->
|
||||||
|
|
||||||
<Header :links="links" />
|
<Header :links="links" />
|
||||||
|
|
||||||
|
|||||||
@@ -8,12 +8,12 @@ We're thrilled to introduce this major update to our UI library, bringing signif
|
|||||||
|
|
||||||
## What's New in v3?
|
## What's New in v3?
|
||||||
|
|
||||||
### Radix Vue
|
### Reka UI (Radix Vue)
|
||||||
|
|
||||||
We've transitioned from [Headless UI](https://headlessui.com/) to [Radix Vue](https://www.radix-vue.com/) as our core component foundation. This shift brings several key advantages:
|
We've transitioned from [Headless UI](https://headlessui.com/) to [Reka UI](https://reka-ui.com/) as our core component foundation. This shift brings several key advantages:
|
||||||
|
|
||||||
- **Extensive Component Library**: With 55+ primitives, Radix Vue significantly expands our component offerings.
|
- **Extensive Component Library**: With 55+ primitives, Reka UI significantly expands our component offerings.
|
||||||
- **Active Development**: Radix Vue's growing popularity ensures ongoing improvements and updates.
|
- **Active Development**: Reka UI's growing popularity ensures ongoing improvements and updates.
|
||||||
- **Enhanced Accessibility**: Built-in accessibility features align with our commitment to inclusive design.
|
- **Enhanced Accessibility**: Built-in accessibility features align with our commitment to inclusive design.
|
||||||
- **Vue 3 Optimization**: Seamless integration with Vue 3 and the Composition API.
|
- **Vue 3 Optimization**: Seamless integration with Vue 3 and the Composition API.
|
||||||
|
|
||||||
@@ -103,7 +103,7 @@ Key points to consider:
|
|||||||
::
|
::
|
||||||
|
|
||||||
::accordion-item{label="How does Nuxt UI v3 handle accessibility?"}
|
::accordion-item{label="How does Nuxt UI v3 handle accessibility?"}
|
||||||
Nuxt UI v3 enhances accessibility through Radix Vue integration. This provides automatic ARIA attributes, keyboard navigation support, intelligent focus management, and screen reader announcements. While offering a strong foundation, proper implementation and testing in your specific use case remains crucial for full accessibility compliance. For more detailed information, refer to [Radix Vue's accessibility documentation](https://www.radix-vue.com/overview/accessibility.html).
|
Nuxt UI v3 enhances accessibility through Reka UI integration. This provides automatic ARIA attributes, keyboard navigation support, intelligent focus management, and screen reader announcements. While offering a strong foundation, proper implementation and testing in your specific use case remains crucial for full accessibility compliance. For more detailed information, refer to [Reka UI's accessibility documentation](https://reka-ui.com/docs/overview/accessibility).
|
||||||
::
|
::
|
||||||
|
|
||||||
::accordion-item{label="What is the testing approach for Nuxt UI v3?"}
|
::accordion-item{label="What is the testing approach for Nuxt UI v3?"}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ const toast = useToast()
|
|||||||
- When removing a toast, there's a 200ms delay before it's actually removed from the state, allowing for exit animations.
|
- When removing a toast, there's a 200ms delay before it's actually removed from the state, allowing for exit animations.
|
||||||
|
|
||||||
::warning
|
::warning
|
||||||
Make sure to wrap your app with the [`App`](/components/app) component which uses our [`Toaster`](https://github.com/nuxt/ui/blob/v3/src/runtime/components/Toaster.vue) component which uses the [`ToastProvider`](https://www.radix-vue.com/components/toast.html#provider) component from Radix Vue.
|
Make sure to wrap your app with the [`App`](/components/app) component which uses our [`Toaster`](https://github.com/nuxt/ui/blob/v3/src/runtime/components/Toaster.vue) component which uses the [`ToastProvider`](https://reka-ui.com/docs/components/toast#provider) component from Reka UI.
|
||||||
::
|
::
|
||||||
|
|
||||||
::tip{to="/components/toast"}
|
::tip{to="/components/toast"}
|
||||||
|
|||||||
@@ -9,13 +9,13 @@ links:
|
|||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
This component implements Radix Vue [ConfigProvider](https://www.radix-vue.com/utilities/config-provider.html) to provide global configuration to all components:
|
This component implements Reka UI [ConfigProvider](https://reka-ui.com/docs/utilities/config-provider) to provide global configuration to all components:
|
||||||
|
|
||||||
- Enables all primitives to inherit global reading direction.
|
- Enables all primitives to inherit global reading direction.
|
||||||
- Enables changing the behavior of scroll body when setting body lock.
|
- Enables changing the behavior of scroll body when setting body lock.
|
||||||
- Much more controls to prevent layout shifts.
|
- Much more controls to prevent layout shifts.
|
||||||
|
|
||||||
It's also using [ToastProvider](https://www.radix-vue.com/components/toast.html#provider) and [TooltipProvider](https://www.radix-vue.com/components/tooltip.html#provider) to provide global toasts and tooltips, as well as programmatic modals and slideovers.
|
It's also using [ToastProvider](https://reka-ui.com/docs/components/toast#provider) and [TooltipProvider](https://reka-ui.com/docs/components/tooltip#provider) to provide global toasts and tooltips, as well as programmatic modals and slideovers.
|
||||||
|
|
||||||
Use it as at the root of your app:
|
Use it as at the root of your app:
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
description: A stacked set of collapsible panels.
|
description: A stacked set of collapsible panels.
|
||||||
links:
|
links:
|
||||||
- label: Accordion
|
- label: Accordion
|
||||||
icon: i-custom-radix-vue
|
icon: i-custom-reka-ui
|
||||||
to: https://www.radix-vue.com/components/accordion.html
|
to: https://reka-ui.com/docs/components/accordion
|
||||||
- label: GitHub
|
- label: GitHub
|
||||||
icon: i-simple-icons-github
|
icon: i-simple-icons-github
|
||||||
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/Accordion.vue
|
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/Accordion.vue
|
||||||
@@ -104,6 +104,38 @@ props:
|
|||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
|
### Unmount
|
||||||
|
|
||||||
|
Use the `unmount-on-hide` prop to prevent the content from being unmounted when the accordion is collapsed. Defaults to `true`.
|
||||||
|
|
||||||
|
::component-code
|
||||||
|
---
|
||||||
|
ignore:
|
||||||
|
- items
|
||||||
|
external:
|
||||||
|
- items
|
||||||
|
hide:
|
||||||
|
- class
|
||||||
|
props:
|
||||||
|
class: 'px-4'
|
||||||
|
unmountOnHide: false
|
||||||
|
items:
|
||||||
|
- label: 'Icons'
|
||||||
|
icon: 'i-lucide-smile'
|
||||||
|
content: 'You have nothing to do, @nuxt/icon will handle it automatically.'
|
||||||
|
- label: 'Colors'
|
||||||
|
icon: 'i-lucide-swatch-book'
|
||||||
|
content: 'Choose a primary and a neutral color from your Tailwind CSS theme.'
|
||||||
|
- label: 'Components'
|
||||||
|
icon: 'i-lucide-box'
|
||||||
|
content: 'You can customize components by using the `class` / `ui` props or in your app.config.ts.'
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
::note
|
||||||
|
You can inspect the DOM to see each item's content being rendered.
|
||||||
|
::
|
||||||
|
|
||||||
### Disabled
|
### Disabled
|
||||||
|
|
||||||
Use the `disabled` property to disable the Accordion.
|
Use the `disabled` property to disable the Accordion.
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
description: An img element with fallback and Nuxt Image support.
|
description: An img element with fallback and Nuxt Image support.
|
||||||
links:
|
links:
|
||||||
- label: Avatar
|
- label: Avatar
|
||||||
icon: i-custom-radix-vue
|
icon: i-custom-reka-ui
|
||||||
to: https://www.radix-vue.com/components/avatar.html
|
to: https://reka-ui.com/docs/components/avatar
|
||||||
- label: GitHub
|
- label: GitHub
|
||||||
icon: i-simple-icons-github
|
icon: i-simple-icons-github
|
||||||
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/Avatar.vue
|
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/Avatar.vue
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ title: Calendar
|
|||||||
description: A calendar component for selecting single dates, multiple dates or date ranges.
|
description: A calendar component for selecting single dates, multiple dates or date ranges.
|
||||||
links:
|
links:
|
||||||
- label: Calendar
|
- label: Calendar
|
||||||
icon: i-custom-radix-vue
|
icon: i-custom-reka-ui
|
||||||
to: https://www.radix-vue.com/components/calendar.html
|
to: https://www.reka-ui.com/components/calendar.html
|
||||||
- label: GitHub
|
- label: GitHub
|
||||||
icon: i-simple-icons-github
|
icon: i-simple-icons-github
|
||||||
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/Calendar.vue
|
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/Calendar.vue
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
description: An input element to toggle between checked and unchecked states.
|
description: An input element to toggle between checked and unchecked states.
|
||||||
links:
|
links:
|
||||||
- label: Checkbox
|
- label: Checkbox
|
||||||
icon: i-custom-radix-vue
|
icon: i-custom-reka-ui
|
||||||
to: https://www.radix-vue.com/components/checkbox.html
|
to: https://reka-ui.com/docs/components/checkbox
|
||||||
- label: GitHub
|
- label: GitHub
|
||||||
icon: i-simple-icons-github
|
icon: i-simple-icons-github
|
||||||
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/Checkbox.vue
|
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/Checkbox.vue
|
||||||
@@ -37,12 +37,14 @@ props:
|
|||||||
|
|
||||||
### Indeterminate
|
### Indeterminate
|
||||||
|
|
||||||
Use the `indeterminate` prop to set the Checkbox to an [indeterminate state](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/checkbox#indeterminate_state_checkboxes).
|
Use the `indeterminate` value in the `v-model` directive or `default-value` prop to set the Checkbox to an [indeterminate state](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/checkbox#indeterminate_state_checkboxes).
|
||||||
|
|
||||||
::component-code
|
::component-code
|
||||||
---
|
---
|
||||||
|
ignore:
|
||||||
|
- defaultValue
|
||||||
props:
|
props:
|
||||||
indeterminate: true
|
defaultValue: 'indeterminate'
|
||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
@@ -52,8 +54,10 @@ Use the `indeterminate-icon` prop to customize the indeterminate icon. Defaults
|
|||||||
|
|
||||||
::component-code
|
::component-code
|
||||||
---
|
---
|
||||||
|
ignore:
|
||||||
|
- defaultValue
|
||||||
props:
|
props:
|
||||||
indeterminate: true
|
defaultValue: 'indeterminate'
|
||||||
indeterminateIcon: 'i-lucide-plus'
|
indeterminateIcon: 'i-lucide-plus'
|
||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
description: A collapsible element to toggle visibility of its content.
|
description: A collapsible element to toggle visibility of its content.
|
||||||
links:
|
links:
|
||||||
- label: Collapsible
|
- label: Collapsible
|
||||||
icon: i-custom-radix-vue
|
icon: i-custom-reka-ui
|
||||||
to: https://www.radix-vue.com/components/collapsible.html
|
to: https://reka-ui.com/docs/components/collapsible
|
||||||
- label: GitHub
|
- label: GitHub
|
||||||
icon: i-simple-icons-github
|
icon: i-simple-icons-github
|
||||||
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/Collapsible.vue
|
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/Collapsible.vue
|
||||||
@@ -38,6 +38,38 @@ slots:
|
|||||||
:placeholder{class="h-48"}
|
:placeholder{class="h-48"}
|
||||||
::
|
::
|
||||||
|
|
||||||
|
### Unmount
|
||||||
|
|
||||||
|
Use the `unmount-on-hide` prop to prevent the content from being unmounted when the Collapsible is collapsed. Defaults to `true`.
|
||||||
|
|
||||||
|
::component-code
|
||||||
|
---
|
||||||
|
prettier: true
|
||||||
|
ignore:
|
||||||
|
- class
|
||||||
|
props:
|
||||||
|
unmountOnHide: false
|
||||||
|
class: 'flex flex-col gap-2 w-48'
|
||||||
|
slots:
|
||||||
|
default: |
|
||||||
|
|
||||||
|
<UButton label="Open" color="neutral" variant="subtle" trailing-icon="i-lucide-chevron-down" block />
|
||||||
|
|
||||||
|
content: |
|
||||||
|
|
||||||
|
<Placeholder class="h-48" />
|
||||||
|
---
|
||||||
|
|
||||||
|
:u-button{label="Open" color="neutral" variant="subtle" trailing-icon="i-lucide-chevron-down" block}
|
||||||
|
|
||||||
|
#content
|
||||||
|
:placeholder{class="h-48"}
|
||||||
|
::
|
||||||
|
|
||||||
|
::note
|
||||||
|
You can inspect the DOM to see the content being rendered.
|
||||||
|
::
|
||||||
|
|
||||||
### Disabled
|
### Disabled
|
||||||
|
|
||||||
Use the `disabled` prop to disable the Collapsible.
|
Use the `disabled` prop to disable the Collapsible.
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ links:
|
|||||||
to: https://fusejs.io/
|
to: https://fusejs.io/
|
||||||
target: _blank
|
target: _blank
|
||||||
- label: Combobox
|
- label: Combobox
|
||||||
icon: i-custom-radix-vue
|
icon: i-custom-reka-ui
|
||||||
to: https://www.radix-vue.com/components/combobox.html
|
to: https://reka-ui.com/docs/components/combobox
|
||||||
- label: GitHub
|
- label: GitHub
|
||||||
icon: i-simple-icons-github
|
icon: i-simple-icons-github
|
||||||
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/CommandPalette.vue
|
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/CommandPalette.vue
|
||||||
@@ -29,7 +29,7 @@ The CommandPalette component filters groups and ranks matching commands by relev
|
|||||||
- `label?: string`{lang="ts-type"}
|
- `label?: string`{lang="ts-type"}
|
||||||
- `slot?: string`{lang="ts-type"}
|
- `slot?: string`{lang="ts-type"}
|
||||||
- `items?: CommandPaletteItem[]`{lang="ts-type"}
|
- `items?: CommandPaletteItem[]`{lang="ts-type"}
|
||||||
- [`filter?: boolean`{lang="ts-type"}](#without-internal-search)
|
- [`ignoreFilter?: boolean`{lang="ts-type"}](#with-ignore-filter)
|
||||||
- [`postFilter?: (searchTerm: string, items: T[]) => T[]`{lang="ts-type"}](#with-post-filtered-items)
|
- [`postFilter?: (searchTerm: string, items: T[]) => T[]`{lang="ts-type"}](#with-post-filtered-items)
|
||||||
- `highlightedIcon?: string`{lang="ts-type"}
|
- `highlightedIcon?: string`{lang="ts-type"}
|
||||||
|
|
||||||
@@ -478,14 +478,14 @@ class: '!p-0'
|
|||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
### Without internal search
|
### With ignore filter
|
||||||
|
|
||||||
You can set the `filter` field to `false` on a group to disable the internal search and use your own search logic.
|
You can set the `ignoreFilter` field to `true` on a group to disable the internal search and use your own search logic.
|
||||||
|
|
||||||
::component-example
|
::component-example
|
||||||
---
|
---
|
||||||
collapse: true
|
collapse: true
|
||||||
name: 'command-palette-filter-example'
|
name: 'command-palette-ignore-filter-example'
|
||||||
class: '!p-0'
|
class: '!p-0'
|
||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ title: ContextMenu
|
|||||||
description: A menu to display actions when right-clicking on an element.
|
description: A menu to display actions when right-clicking on an element.
|
||||||
links:
|
links:
|
||||||
- label: ContextMenu
|
- label: ContextMenu
|
||||||
icon: i-custom-radix-vue
|
icon: i-custom-reka-ui
|
||||||
to: https://www.radix-vue.com/components/context-menu.html
|
to: https://reka-ui.com/docs/components/context-menu
|
||||||
- label: GitHub
|
- label: GitHub
|
||||||
icon: i-simple-icons-github
|
icon: i-simple-icons-github
|
||||||
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/ContextMenu.vue
|
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/ContextMenu.vue
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
description: A drawer that smoothly slides in & out of the screen.
|
description: A drawer that smoothly slides in & out of the screen.
|
||||||
links:
|
links:
|
||||||
- label: Drawer
|
- label: Drawer
|
||||||
icon: i-custom-radix-vue
|
icon: i-custom-reka-ui
|
||||||
to: https://github.com/radix-vue/vaul-vue
|
to: https://github.com/unovue/vaul-vue
|
||||||
- label: GitHub
|
- label: GitHub
|
||||||
icon: i-simple-icons-github
|
icon: i-simple-icons-github
|
||||||
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/Drawer.vue
|
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/Drawer.vue
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ title: DropdownMenu
|
|||||||
description: A menu to display actions when clicking on an element.
|
description: A menu to display actions when clicking on an element.
|
||||||
links:
|
links:
|
||||||
- label: DropdownMenu
|
- label: DropdownMenu
|
||||||
icon: i-custom-radix-vue
|
icon: i-custom-reka-ui
|
||||||
to: https://www.radix-vue.com/components/dropdown-menu.html
|
to: https://reka-ui.com/docs/components/dropdown-menu
|
||||||
- label: GitHub
|
- label: GitHub
|
||||||
icon: i-simple-icons-github
|
icon: i-simple-icons-github
|
||||||
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/DropdownMenu.vue
|
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/DropdownMenu.vue
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ title: InputMenu
|
|||||||
description: An autocomplete input with real-time suggestions.
|
description: An autocomplete input with real-time suggestions.
|
||||||
links:
|
links:
|
||||||
- label: Combobox
|
- label: Combobox
|
||||||
icon: i-custom-radix-vue
|
icon: i-custom-reka-ui
|
||||||
to: https://www.radix-vue.com/components/combobox.html
|
to: https://reka-ui.com/docs/components/combobox
|
||||||
- label: GitHub
|
- label: GitHub
|
||||||
icon: i-simple-icons-github
|
icon: i-simple-icons-github
|
||||||
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/InputMenu.vue
|
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/InputMenu.vue
|
||||||
@@ -15,7 +15,7 @@ links:
|
|||||||
Use the `v-model` directive to control the value of the InputMenu or the `default-value` prop to set the initial value when you do not need to control its state.
|
Use the `v-model` directive to control the value of the InputMenu or the `default-value` prop to set the initial value when you do not need to control its state.
|
||||||
|
|
||||||
::tip
|
::tip
|
||||||
Use this over an [`Input`](/components/input) to take advantage of Radix Vue's [`Combobox`](https://www.radix-vue.com/components/combobox.html) component that offers autocomplete capabilities.
|
Use this over an [`Input`](/components/input) to take advantage of Reka UI's [`Combobox`](https://reka-ui.com/docs/components/combobox) component that offers autocomplete capabilities.
|
||||||
::
|
::
|
||||||
|
|
||||||
::note
|
::note
|
||||||
@@ -222,42 +222,6 @@ props:
|
|||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
### Create Item
|
|
||||||
|
|
||||||
Use the `create-item` prop to allow user input.
|
|
||||||
|
|
||||||
::component-code
|
|
||||||
---
|
|
||||||
prettier: true
|
|
||||||
ignore:
|
|
||||||
- modelValue
|
|
||||||
- items
|
|
||||||
external:
|
|
||||||
- items
|
|
||||||
- modelValue
|
|
||||||
items:
|
|
||||||
createItem:
|
|
||||||
- true
|
|
||||||
- 'always'
|
|
||||||
props:
|
|
||||||
modelValue: 'Backlog'
|
|
||||||
items:
|
|
||||||
- Backlog
|
|
||||||
- Todo
|
|
||||||
- In Progress
|
|
||||||
- Done
|
|
||||||
createItem: true
|
|
||||||
---
|
|
||||||
::
|
|
||||||
|
|
||||||
::note
|
|
||||||
The create option shows when no match is found by default. Set it to `always` to show it even when similar values exist.
|
|
||||||
::
|
|
||||||
|
|
||||||
::tip{to="#emits"}
|
|
||||||
Use the `@create` event to handle the creation of the item. You will receive the event and the item as arguments.
|
|
||||||
::
|
|
||||||
|
|
||||||
### Content
|
### Content
|
||||||
|
|
||||||
Use the `content` prop to control how the InputMenu content is rendered, like its `align` or `side` for example.
|
Use the `content` prop to control how the InputMenu content is rendered, like its `align` or `side` for example.
|
||||||
@@ -282,7 +246,7 @@ items:
|
|||||||
- top
|
- top
|
||||||
- bottom
|
- bottom
|
||||||
props:
|
props:
|
||||||
modelValue: Backlog
|
modelValue: 'Backlog'
|
||||||
content:
|
content:
|
||||||
align: center
|
align: center
|
||||||
side: bottom
|
side: bottom
|
||||||
@@ -310,7 +274,7 @@ external:
|
|||||||
- items
|
- items
|
||||||
- modelValue
|
- modelValue
|
||||||
props:
|
props:
|
||||||
modelValue: Backlog
|
modelValue: 'Backlog'
|
||||||
arrow: true
|
arrow: true
|
||||||
items:
|
items:
|
||||||
- Backlog
|
- Backlog
|
||||||
@@ -734,6 +698,25 @@ name: 'input-menu-icon-example'
|
|||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
|
### With create item
|
||||||
|
|
||||||
|
Use the `create-item` prop to enable users to add custom values that aren't in the predefined options.
|
||||||
|
|
||||||
|
::component-example
|
||||||
|
---
|
||||||
|
collapse: true
|
||||||
|
name: 'input-menu-create-item-example'
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
::note
|
||||||
|
The create option shows when no match is found by default. Set it to `always` to show it even when similar values exist.
|
||||||
|
::
|
||||||
|
|
||||||
|
::tip{to="#emits"}
|
||||||
|
Use the `@create` event to handle the creation of the item. You will receive the event and the item as arguments.
|
||||||
|
::
|
||||||
|
|
||||||
### With fetched items
|
### With fetched items
|
||||||
|
|
||||||
You can fetch items from an API and use them in the InputMenu.
|
You can fetch items from an API and use them in the InputMenu.
|
||||||
@@ -745,14 +728,14 @@ name: 'input-menu-fetch-example'
|
|||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
### Without internal search
|
### With ignore filter
|
||||||
|
|
||||||
Set the `filter` prop to `false` to disable the internal search and use your own search logic.
|
Set the `ignore-filter` prop to `true` to disable the internal search and use your own search logic.
|
||||||
|
|
||||||
::component-example
|
::component-example
|
||||||
---
|
---
|
||||||
collapse: true
|
collapse: true
|
||||||
name: 'input-menu-filter-example'
|
name: 'input-menu-ignore-filter-example'
|
||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
@@ -760,9 +743,9 @@ name: 'input-menu-filter-example'
|
|||||||
This example uses [`refDebounced`](https://vueuse.org/shared/refDebounced/#refdebounced) to debounce the API calls.
|
This example uses [`refDebounced`](https://vueuse.org/shared/refDebounced/#refdebounced) to debounce the API calls.
|
||||||
::
|
::
|
||||||
|
|
||||||
### With custom search
|
### With filter fields
|
||||||
|
|
||||||
Use the `filter` prop with an array of fields to filter on. Defaults to `[labelKey]`.
|
Use the `filter-fields` prop with an array of fields to filter on. Defaults to `[labelKey]`.
|
||||||
|
|
||||||
::component-example
|
::component-example
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ title: InputNumber
|
|||||||
description: Input numerical values with a customizable range.
|
description: Input numerical values with a customizable range.
|
||||||
links:
|
links:
|
||||||
- label: Number Field
|
- label: Number Field
|
||||||
icon: i-custom-radix-vue
|
icon: i-custom-reka-ui
|
||||||
to: https://www.radix-vue.com/components/number-field
|
to: https://www.reka-ui.com/components/input-number
|
||||||
- label: GitHub
|
- label: GitHub
|
||||||
icon: i-simple-icons-github
|
icon: i-simple-icons-github
|
||||||
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/InputNumber.vue
|
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/InputNumber.vue
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
description: A dialog window that can be used to display a message or request user input.
|
description: A dialog window that can be used to display a message or request user input.
|
||||||
links:
|
links:
|
||||||
- label: Dialog
|
- label: Dialog
|
||||||
icon: i-custom-radix-vue
|
icon: i-custom-reka-ui
|
||||||
to: https://www.radix-vue.com/components/dialog.html
|
to: https://reka-ui.com/docs/components/dialog
|
||||||
- label: GitHub
|
- label: GitHub
|
||||||
icon: i-simple-icons-github
|
icon: i-simple-icons-github
|
||||||
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/Modal.vue
|
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/Modal.vue
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ title: NavigationMenu
|
|||||||
description: A list of links that can be displayed horizontally or vertically.
|
description: A list of links that can be displayed horizontally or vertically.
|
||||||
links:
|
links:
|
||||||
- label: NavigationMenu
|
- label: NavigationMenu
|
||||||
icon: i-custom-radix-vue
|
icon: i-custom-reka-ui
|
||||||
to: https://www.radix-vue.com/components/navigation-menu.html
|
to: https://reka-ui.com/docs/components/navigation-menu
|
||||||
- label: GitHub
|
- label: GitHub
|
||||||
icon: i-simple-icons-github
|
icon: i-simple-icons-github
|
||||||
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/NavigationMenu.vue
|
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/NavigationMenu.vue
|
||||||
@@ -249,7 +249,7 @@ external:
|
|||||||
- items
|
- items
|
||||||
props:
|
props:
|
||||||
highlight: true
|
highlight: true
|
||||||
highlightColor: ''
|
highlightColor: 'primary'
|
||||||
orientation: 'horizontal'
|
orientation: 'horizontal'
|
||||||
items:
|
items:
|
||||||
- - label: Guide
|
- - label: Guide
|
||||||
@@ -538,6 +538,98 @@ props:
|
|||||||
The arrow is animated to follow the active item.
|
The arrow is animated to follow the active item.
|
||||||
::
|
::
|
||||||
|
|
||||||
|
### Unmount
|
||||||
|
|
||||||
|
Use the `unmount-on-hide` prop to control the content unmounting behavior. Defaults to `true`.
|
||||||
|
|
||||||
|
::component-code
|
||||||
|
---
|
||||||
|
collapse: true
|
||||||
|
ignore:
|
||||||
|
- items
|
||||||
|
- arrow
|
||||||
|
- class
|
||||||
|
external:
|
||||||
|
- items
|
||||||
|
props:
|
||||||
|
unmountOnHide: false
|
||||||
|
items:
|
||||||
|
- label: Guide
|
||||||
|
icon: i-lucide-book-open
|
||||||
|
to: /getting-started
|
||||||
|
children:
|
||||||
|
- label: Introduction
|
||||||
|
description: Fully styled and customizable components for Nuxt.
|
||||||
|
icon: i-lucide-house
|
||||||
|
- label: Installation
|
||||||
|
description: Learn how to install and configure Nuxt UI in your application.
|
||||||
|
icon: i-lucide-cloud-download
|
||||||
|
- label: 'Icons'
|
||||||
|
icon: 'i-lucide-smile'
|
||||||
|
description: 'You have nothing to do, @nuxt/icon will handle it automatically.'
|
||||||
|
- label: 'Colors'
|
||||||
|
icon: 'i-lucide-swatch-book'
|
||||||
|
description: 'Choose a primary and a neutral color from your Tailwind CSS theme.'
|
||||||
|
- label: 'Theme'
|
||||||
|
icon: 'i-lucide-cog'
|
||||||
|
description: 'You can customize components by using the `class` / `ui` props or in your app.config.ts.'
|
||||||
|
- label: Composables
|
||||||
|
icon: i-lucide-database
|
||||||
|
to: /composables
|
||||||
|
children:
|
||||||
|
- label: defineShortcuts
|
||||||
|
icon: i-lucide-file-text
|
||||||
|
description: Define shortcuts for your application.
|
||||||
|
to: /composables/define-shortcuts
|
||||||
|
- label: useModal
|
||||||
|
icon: i-lucide-file-text
|
||||||
|
description: Display a modal within your application.
|
||||||
|
to: /composables/use-modal
|
||||||
|
- label: useSlideover
|
||||||
|
icon: i-lucide-file-text
|
||||||
|
description: Display a slideover within your application.
|
||||||
|
to: /composables/use-slideover
|
||||||
|
- label: useToast
|
||||||
|
icon: i-lucide-file-text
|
||||||
|
description: Display a toast within your application.
|
||||||
|
to: /composables/use-toast
|
||||||
|
- label: Components
|
||||||
|
icon: i-lucide-box
|
||||||
|
to: /components
|
||||||
|
active: true
|
||||||
|
children:
|
||||||
|
- label: Link
|
||||||
|
icon: i-lucide-file-text
|
||||||
|
description: Use NuxtLink with superpowers.
|
||||||
|
to: /components/link
|
||||||
|
- label: Modal
|
||||||
|
icon: i-lucide-file-text
|
||||||
|
description: Display a modal within your application.
|
||||||
|
to: /components/modal
|
||||||
|
- label: NavigationMenu
|
||||||
|
icon: i-lucide-file-text
|
||||||
|
description: Display a list of links.
|
||||||
|
to: /components/navigation-menu
|
||||||
|
- label: Pagination
|
||||||
|
icon: i-lucide-file-text
|
||||||
|
description: Display a list of pages.
|
||||||
|
to: /components/pagination
|
||||||
|
- label: Popover
|
||||||
|
icon: i-lucide-file-text
|
||||||
|
description: Display a non-modal dialog that floats around a trigger element.
|
||||||
|
to: /components/popover
|
||||||
|
- label: Progress
|
||||||
|
icon: i-lucide-file-text
|
||||||
|
description: Show a horizontal bar to indicate task progression.
|
||||||
|
to: /components/progress
|
||||||
|
class: 'justify-center'
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
::note
|
||||||
|
You can inspect the DOM to see each item's content being rendered.
|
||||||
|
::
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
### Control active item
|
### Control active item
|
||||||
@@ -592,7 +684,7 @@ name: 'navigation-menu-content-slot-example'
|
|||||||
::
|
::
|
||||||
|
|
||||||
::note
|
::note
|
||||||
In this example, we add the `sm:w-[var(--radix-navigation-menu-viewport-width)]` class on the `viewport` to have a dynamic width. This requires to set a width on the content's first child.
|
In this example, we add the `sm:w-[var(--reka-navigation-menu-viewport-width)]` class on the `viewport` to have a dynamic width. This requires to set a width on the content's first child.
|
||||||
::
|
::
|
||||||
|
|
||||||
## API
|
## API
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
description: A list of buttons or links to navigate through pages.
|
description: A list of buttons or links to navigate through pages.
|
||||||
links:
|
links:
|
||||||
- label: Pagination
|
- label: Pagination
|
||||||
icon: i-custom-radix-vue
|
icon: i-custom-reka-ui
|
||||||
to: https://www.radix-vue.com/components/pagination.html
|
to: https://reka-ui.com/docs/components/pagination
|
||||||
- label: GitHub
|
- label: GitHub
|
||||||
icon: i-simple-icons-github
|
icon: i-simple-icons-github
|
||||||
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/Pagination.vue
|
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/Pagination.vue
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
---
|
---
|
||||||
description: A non-modal dialog that floats around a trigger element.
|
description: A non-modal dialog that floats around a trigger element.
|
||||||
links:
|
links:
|
||||||
|
- label: HoverCard
|
||||||
|
icon: i-custom-reka-ui
|
||||||
|
to: https://reka-ui.com/docs/components/hover-card
|
||||||
- label: Popover
|
- label: Popover
|
||||||
icon: i-custom-radix-vue
|
icon: i-custom-reka-ui
|
||||||
to: https://www.radix-vue.com/components/popover.html
|
to: https://reka-ui.com/docs/components/popover
|
||||||
- label: GitHub
|
- label: GitHub
|
||||||
icon: i-simple-icons-github
|
icon: i-simple-icons-github
|
||||||
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/Popover.vue
|
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/Popover.vue
|
||||||
@@ -64,7 +67,7 @@ slots:
|
|||||||
::
|
::
|
||||||
|
|
||||||
::note
|
::note
|
||||||
When using the `hover` mode, the Radix Vue [`HoverCard`](https://www.radix-vue.com/components/hover-card.html) component is used instead of the [`Popover`](https://www.radix-vue.com/components/popover.html).
|
When using the `hover` mode, the Reka UI [`HoverCard`](https://reka-ui.com/docs/components/hover-card) component is used instead of the [`Popover`](https://reka-ui.com/docs/components/popover).
|
||||||
::
|
::
|
||||||
|
|
||||||
### Delay
|
### Delay
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
description: An indicator showing the progress of a task.
|
description: An indicator showing the progress of a task.
|
||||||
links:
|
links:
|
||||||
- label: Progress
|
- label: Progress
|
||||||
icon: i-custom-radix-vue
|
icon: i-custom-reka-ui
|
||||||
to: https://www.radix-vue.com/components/progress.html
|
to: https://reka-ui.com/docs/components/progress
|
||||||
- label: GitHub
|
- label: GitHub
|
||||||
icon: i-simple-icons-github
|
icon: i-simple-icons-github
|
||||||
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/Progress.vue
|
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/Progress.vue
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ title: RadioGroup
|
|||||||
description: A set of radio buttons to select a single option from a list.
|
description: A set of radio buttons to select a single option from a list.
|
||||||
links:
|
links:
|
||||||
- label: RadioGroup
|
- label: RadioGroup
|
||||||
icon: i-custom-radix-vue
|
icon: i-custom-reka-ui
|
||||||
to: https://www.radix-vue.com/components/radio-group.html
|
to: https://reka-ui.com/docs/components/radio-group
|
||||||
- label: GitHub
|
- label: GitHub
|
||||||
icon: i-simple-icons-github
|
icon: i-simple-icons-github
|
||||||
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/RadioGroup.vue
|
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/RadioGroup.vue
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ title: SelectMenu
|
|||||||
description: An advanced searchable select element.
|
description: An advanced searchable select element.
|
||||||
links:
|
links:
|
||||||
- label: Combobox
|
- label: Combobox
|
||||||
icon: i-custom-radix-vue
|
icon: i-custom-reka-ui
|
||||||
to: https://www.radix-vue.com/components/combobox.html
|
to: https://reka-ui.com/docs/components/combobox
|
||||||
- label: GitHub
|
- label: GitHub
|
||||||
icon: i-simple-icons-github
|
icon: i-simple-icons-github
|
||||||
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/SelectMenu.vue
|
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/SelectMenu.vue
|
||||||
@@ -15,7 +15,7 @@ links:
|
|||||||
Use the `v-model` directive to control the value of the SelectMenu or the `default-value` prop to set the initial value when you do not need to control its state.
|
Use the `v-model` directive to control the value of the SelectMenu or the `default-value` prop to set the initial value when you do not need to control its state.
|
||||||
|
|
||||||
::tip
|
::tip
|
||||||
Use this over a [`Select`](/components/select) to take advantage of Radix Vue's [`Combobox`](https://www.radix-vue.com/components/combobox.html) component that offers search capabilities and multiple selection.
|
Use this over a [`Select`](/components/select) to take advantage of Reka UI's [`Combobox`](https://reka-ui.com/docs/components/combobox) component that offers search capabilities and multiple selection.
|
||||||
::
|
::
|
||||||
|
|
||||||
::note
|
::note
|
||||||
@@ -239,44 +239,6 @@ props:
|
|||||||
You can set the `search-input` prop to `false` to hide the search input.
|
You can set the `search-input` prop to `false` to hide the search input.
|
||||||
::
|
::
|
||||||
|
|
||||||
### Create Item
|
|
||||||
|
|
||||||
Use the `create-item` prop to allow user input.
|
|
||||||
|
|
||||||
::component-code
|
|
||||||
---
|
|
||||||
prettier: true
|
|
||||||
ignore:
|
|
||||||
- modelValue
|
|
||||||
- items
|
|
||||||
- class
|
|
||||||
external:
|
|
||||||
- items
|
|
||||||
- modelValue
|
|
||||||
items:
|
|
||||||
createItem:
|
|
||||||
- true
|
|
||||||
- 'always'
|
|
||||||
props:
|
|
||||||
modelValue: 'Backlog'
|
|
||||||
createItem: true
|
|
||||||
items:
|
|
||||||
- Backlog
|
|
||||||
- Todo
|
|
||||||
- In Progress
|
|
||||||
- Done
|
|
||||||
class: 'w-48'
|
|
||||||
---
|
|
||||||
::
|
|
||||||
|
|
||||||
::note
|
|
||||||
The create option shows when no match is found by default. Set it to `always` to show it even when similar values exist.
|
|
||||||
::
|
|
||||||
|
|
||||||
::tip{to="#emits"}
|
|
||||||
Use the `@create` event to handle the creation of the item. You will receive the event and the item as arguments.
|
|
||||||
::
|
|
||||||
|
|
||||||
### Content
|
### Content
|
||||||
|
|
||||||
Use the `content` prop to control how the SelectMenu content is rendered, like its `align` or `side` for example.
|
Use the `content` prop to control how the SelectMenu content is rendered, like its `align` or `side` for example.
|
||||||
@@ -302,7 +264,7 @@ items:
|
|||||||
- top
|
- top
|
||||||
- bottom
|
- bottom
|
||||||
props:
|
props:
|
||||||
modelValue: Backlog
|
modelValue: 'Backlog'
|
||||||
content:
|
content:
|
||||||
align: center
|
align: center
|
||||||
side: bottom
|
side: bottom
|
||||||
@@ -332,7 +294,7 @@ external:
|
|||||||
- items
|
- items
|
||||||
- modelValue
|
- modelValue
|
||||||
props:
|
props:
|
||||||
modelValue: Backlog
|
modelValue: 'Backlog'
|
||||||
arrow: true
|
arrow: true
|
||||||
items:
|
items:
|
||||||
- Backlog
|
- Backlog
|
||||||
@@ -769,6 +731,25 @@ name: 'select-menu-icon-example'
|
|||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
|
### With create item
|
||||||
|
|
||||||
|
Use the `create-item` prop to enable users to add custom values that aren't in the predefined options.
|
||||||
|
|
||||||
|
::component-example
|
||||||
|
---
|
||||||
|
collapse: true
|
||||||
|
name: 'select-menu-create-item-example'
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
::note
|
||||||
|
The create option shows when no match is found by default. Set it to `always` to show it even when similar values exist.
|
||||||
|
::
|
||||||
|
|
||||||
|
::tip{to="#emits"}
|
||||||
|
Use the `@create` event to handle the creation of the item. You will receive the event and the item as arguments.
|
||||||
|
::
|
||||||
|
|
||||||
### With fetched items
|
### With fetched items
|
||||||
|
|
||||||
You can fetch items from an API and use them in the SelectMenu.
|
You can fetch items from an API and use them in the SelectMenu.
|
||||||
@@ -780,14 +761,14 @@ name: 'select-menu-fetch-example'
|
|||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
### Without internal search
|
### With ignore filter
|
||||||
|
|
||||||
Set the `filter` prop to `false` to disable the internal search and use your own search logic.
|
Set the `ignore-filter` prop to `true` to disable the internal search and use your own search logic.
|
||||||
|
|
||||||
::component-example
|
::component-example
|
||||||
---
|
---
|
||||||
collapse: true
|
collapse: true
|
||||||
name: 'select-menu-filter-example'
|
name: 'select-menu-ignore-filter-example'
|
||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
@@ -795,9 +776,9 @@ name: 'select-menu-filter-example'
|
|||||||
This example uses [`refDebounced`](https://vueuse.org/shared/refDebounced/#refdebounced) to debounce the API calls.
|
This example uses [`refDebounced`](https://vueuse.org/shared/refDebounced/#refdebounced) to debounce the API calls.
|
||||||
::
|
::
|
||||||
|
|
||||||
### With custom search
|
### With filter fields
|
||||||
|
|
||||||
Use the `filter` prop with an array of fields to filter on. Defaults to `[labelKey]`.
|
Use the `filter-fields` prop with an array of fields to filter on. Defaults to `[labelKey]`.
|
||||||
|
|
||||||
::component-example
|
::component-example
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
description: A select element to choose from a list of options.
|
description: A select element to choose from a list of options.
|
||||||
links:
|
links:
|
||||||
- label: Select
|
- label: Select
|
||||||
icon: i-custom-radix-vue
|
icon: i-custom-reka-ui
|
||||||
to: https://www.radix-vue.com/components/select.html
|
to: https://reka-ui.com/docs/components/select
|
||||||
- label: GitHub
|
- label: GitHub
|
||||||
icon: i-simple-icons-github
|
icon: i-simple-icons-github
|
||||||
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/Select.vue
|
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/Select.vue
|
||||||
@@ -135,6 +135,39 @@ props:
|
|||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
|
### Multiple
|
||||||
|
|
||||||
|
Use the `multiple` prop to allow multiple selections, the selected items will be separated by a comma in the trigger.
|
||||||
|
|
||||||
|
::component-code
|
||||||
|
---
|
||||||
|
prettier: true
|
||||||
|
ignore:
|
||||||
|
- modelValue
|
||||||
|
- items
|
||||||
|
- multiple
|
||||||
|
- class
|
||||||
|
external:
|
||||||
|
- items
|
||||||
|
- modelValue
|
||||||
|
props:
|
||||||
|
modelValue:
|
||||||
|
- Backlog
|
||||||
|
- Todo
|
||||||
|
multiple: true
|
||||||
|
items:
|
||||||
|
- Backlog
|
||||||
|
- Todo
|
||||||
|
- In Progress
|
||||||
|
- Done
|
||||||
|
class: 'w-48'
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
::caution
|
||||||
|
Ensure to pass an array to the `default-value` prop or the `v-model` directive.
|
||||||
|
::
|
||||||
|
|
||||||
### Placeholder
|
### Placeholder
|
||||||
|
|
||||||
Use the `placeholder` prop to set a placeholder text.
|
Use the `placeholder` prop to set a placeholder text.
|
||||||
@@ -160,11 +193,7 @@ props:
|
|||||||
|
|
||||||
### Content
|
### Content
|
||||||
|
|
||||||
Use the `content` prop to control how the Select content is rendered, like its `align`, `side` or `position` for example.
|
Use the `content` prop to control how the Select content is rendered, like its `align` or `side` for example.
|
||||||
|
|
||||||
::warning
|
|
||||||
The `content.align`, `content.side`, etc. properties only apply when `content.position` is set to `popper`.
|
|
||||||
::
|
|
||||||
|
|
||||||
::component-code
|
::component-code
|
||||||
---
|
---
|
||||||
@@ -177,9 +206,6 @@ external:
|
|||||||
- items
|
- items
|
||||||
- modelValue
|
- modelValue
|
||||||
items:
|
items:
|
||||||
content.position:
|
|
||||||
- 'item-aligned'
|
|
||||||
- 'popper'
|
|
||||||
content.align:
|
content.align:
|
||||||
- start
|
- start
|
||||||
- center
|
- center
|
||||||
@@ -190,9 +216,8 @@ items:
|
|||||||
- top
|
- top
|
||||||
- bottom
|
- bottom
|
||||||
props:
|
props:
|
||||||
modelValue: 'Todo'
|
modelValue: 'Backlog'
|
||||||
content:
|
content:
|
||||||
position: 'item-aligned'
|
|
||||||
align: center
|
align: center
|
||||||
side: bottom
|
side: bottom
|
||||||
sideOffset: 8
|
sideOffset: 8
|
||||||
@@ -205,11 +230,6 @@ props:
|
|||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
::note{to="https://www.radix-vue.com/components/select.html#change-the-positioning-mode"}
|
|
||||||
Read more about the `content.position` prop in the **Radix Vue** documentation.
|
|
||||||
::
|
|
||||||
|
|
||||||
<!--
|
|
||||||
### Arrow
|
### Arrow
|
||||||
|
|
||||||
Use the `arrow` prop to display an arrow on the Select.
|
Use the `arrow` prop to display an arrow on the Select.
|
||||||
@@ -226,7 +246,7 @@ external:
|
|||||||
- items
|
- items
|
||||||
- modelValue
|
- modelValue
|
||||||
props:
|
props:
|
||||||
modelValue: 'Todo'
|
modelValue: 'Backlog'
|
||||||
arrow: true
|
arrow: true
|
||||||
items:
|
items:
|
||||||
- Backlog
|
- Backlog
|
||||||
@@ -237,8 +257,6 @@ props:
|
|||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
-->
|
|
||||||
|
|
||||||
### Color
|
### Color
|
||||||
|
|
||||||
Use the `color` prop to change the ring color when the Select is focused.
|
Use the `color` prop to change the ring color when the Select is focused.
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
description: Separates content horizontally or vertically.
|
description: Separates content horizontally or vertically.
|
||||||
links:
|
links:
|
||||||
- label: Separator
|
- label: Separator
|
||||||
icon: i-custom-radix-vue
|
icon: i-custom-reka-ui
|
||||||
to: https://www.radix-vue.com/components/separator.html
|
to: https://reka-ui.com/docs/components/separator
|
||||||
- label: GitHub
|
- label: GitHub
|
||||||
icon: i-simple-icons-github
|
icon: i-simple-icons-github
|
||||||
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/Separator.vue
|
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/Separator.vue
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
description: A dialog that slides in from any side of the screen.
|
description: A dialog that slides in from any side of the screen.
|
||||||
links:
|
links:
|
||||||
- label: Dialog
|
- label: Dialog
|
||||||
icon: i-custom-radix-vue
|
icon: i-custom-reka-ui
|
||||||
to: https://www.radix-vue.com/components/dialog.html
|
to: https://reka-ui.com/docs/components/dialog
|
||||||
- label: GitHub
|
- label: GitHub
|
||||||
icon: i-simple-icons-github
|
icon: i-simple-icons-github
|
||||||
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/Slideover.vue
|
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/Slideover.vue
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
description: An input to select a numeric value within a range.
|
description: An input to select a numeric value within a range.
|
||||||
links:
|
links:
|
||||||
- label: Slider
|
- label: Slider
|
||||||
icon: i-custom-radix-vue
|
icon: i-custom-reka-ui
|
||||||
to: https://www.radix-vue.com/components/slider.html
|
to: https://reka-ui.com/docs/components/slider
|
||||||
- label: GitHub
|
- label: GitHub
|
||||||
icon: i-simple-icons-github
|
icon: i-simple-icons-github
|
||||||
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/Slider.vue
|
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/Slider.vue
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
description: A control that toggles between two states.
|
description: A control that toggles between two states.
|
||||||
links:
|
links:
|
||||||
- label: Switch
|
- label: Switch
|
||||||
icon: i-custom-radix-vue
|
icon: i-custom-reka-ui
|
||||||
to: https://www.radix-vue.com/components/switch.html
|
to: https://reka-ui.com/docs/components/switch
|
||||||
- label: GitHub
|
- label: GitHub
|
||||||
icon: i-simple-icons-github
|
icon: i-simple-icons-github
|
||||||
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/Switch.vue
|
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/Switch.vue
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
description: A set of tab panels that are displayed one at a time.
|
description: A set of tab panels that are displayed one at a time.
|
||||||
links:
|
links:
|
||||||
- label: Tabs
|
- label: Tabs
|
||||||
icon: i-custom-radix-vue
|
icon: i-custom-reka-ui
|
||||||
to: https://www.radix-vue.com/components/tabs.html
|
to: https://reka-ui.com/docs/components/tabs
|
||||||
- label: GitHub
|
- label: GitHub
|
||||||
icon: i-simple-icons-github
|
icon: i-simple-icons-github
|
||||||
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/Tabs.vue
|
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/Tabs.vue
|
||||||
@@ -44,9 +44,7 @@ props:
|
|||||||
|
|
||||||
### Content
|
### Content
|
||||||
|
|
||||||
Use the `content` prop to control how the Tabs are rendered.
|
Set the `content` prop to `false` to turn the Tabs into a toggle-only control without displaying any content. Defaults to `true`.
|
||||||
|
|
||||||
You can set it to `false` to prevent the Tabs from rendering any content and act as a toggle.
|
|
||||||
|
|
||||||
::component-code
|
::component-code
|
||||||
---
|
---
|
||||||
@@ -69,20 +67,20 @@ props:
|
|||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
You can also choose to only render the content of the active tab by setting `content.forceMount` to `false`.
|
### Unmount
|
||||||
|
|
||||||
|
Use the `unmount-on-hide` prop to prevent the content from being unmounted when the Tabs is collapsed. Defaults to `true`.
|
||||||
|
|
||||||
::component-code
|
::component-code
|
||||||
---
|
---
|
||||||
prettier: true
|
|
||||||
ignore:
|
ignore:
|
||||||
- content.forceMount
|
- content
|
||||||
- items
|
- items
|
||||||
- class
|
- class
|
||||||
external:
|
external:
|
||||||
- items
|
- items
|
||||||
props:
|
props:
|
||||||
content:
|
unmountOnHide: false
|
||||||
forceMount: false
|
|
||||||
items:
|
items:
|
||||||
- label: Account
|
- label: Account
|
||||||
icon: 'i-lucide-user'
|
icon: 'i-lucide-user'
|
||||||
@@ -95,7 +93,7 @@ props:
|
|||||||
::
|
::
|
||||||
|
|
||||||
::note
|
::note
|
||||||
You can inspect the DOM to see that the content of the inactive tab is not rendered.
|
You can inspect the DOM to see each item's content being rendered.
|
||||||
::
|
::
|
||||||
|
|
||||||
### Color
|
### Color
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
description: A succinct message to provide information or feedback to the user.
|
description: A succinct message to provide information or feedback to the user.
|
||||||
links:
|
links:
|
||||||
- label: Toast
|
- label: Toast
|
||||||
icon: i-custom-radix-vue
|
icon: i-custom-reka-ui
|
||||||
to: https://www.radix-vue.com/components/toast.html
|
to: https://reka-ui.com/docs/components/toast
|
||||||
- label: GitHub
|
- label: GitHub
|
||||||
icon: i-simple-icons-github
|
icon: i-simple-icons-github
|
||||||
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/Toast.vue
|
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/Toast.vue
|
||||||
@@ -14,7 +14,7 @@ links:
|
|||||||
Use the [useToast](/composables/use-toast) composable to display a toast in your application.
|
Use the [useToast](/composables/use-toast) composable to display a toast in your application.
|
||||||
|
|
||||||
::warning
|
::warning
|
||||||
Make sure to wrap your app with the [`App`](/components/app) component which uses our [`Toaster`](https://github.com/nuxt/ui/blob/v3/src/runtime/components/Toaster.vue) component which uses the [`ToastProvider`](https://www.radix-vue.com/components/toast.html#provider) component from Radix Vue.
|
Make sure to wrap your app with the [`App`](/components/app) component which uses our [`Toaster`](https://github.com/nuxt/ui/blob/v3/src/runtime/components/Toaster.vue) component which uses the [`ToastProvider`](https://reka-ui.com/docs/components/toast#provider) component from Reka UI.
|
||||||
::
|
::
|
||||||
|
|
||||||
::tip{to="/components/app#props"}
|
::tip{to="/components/app#props"}
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
description: A popup that reveals information when hovering over an element.
|
description: A popup that reveals information when hovering over an element.
|
||||||
links:
|
links:
|
||||||
- label: Tooltip
|
- label: Tooltip
|
||||||
icon: i-custom-radix-vue
|
icon: i-custom-reka-ui
|
||||||
to: https://www.radix-vue.com/components/tooltip.html
|
to: https://reka-ui.com/docs/components/tooltip
|
||||||
- label: GitHub
|
- label: GitHub
|
||||||
icon: i-simple-icons-github
|
icon: i-simple-icons-github
|
||||||
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/Tooltip.vue
|
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/Tooltip.vue
|
||||||
@@ -14,7 +14,7 @@ links:
|
|||||||
Use a [Button](/components/button) or any other component in the default slot of the Tooltip.
|
Use a [Button](/components/button) or any other component in the default slot of the Tooltip.
|
||||||
|
|
||||||
::warning
|
::warning
|
||||||
Make sure to wrap your app with the [`App`](/components/app) component which uses the [`TooltipProvider`](https://www.radix-vue.com/components/tooltip.html#provider) component from Radix Vue.
|
Make sure to wrap your app with the [`App`](/components/app) component which uses the [`TooltipProvider`](https://reka-ui.com/docs/components/tooltip#provider) component from Reka UI.
|
||||||
::
|
::
|
||||||
|
|
||||||
::tip{to="/components/app#props"}
|
::tip{to="/components/app#props"}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
"@nuxt/content": "https://pkg.pr.new/@nuxt/content@c5b1a4f",
|
"@nuxt/content": "https://pkg.pr.new/@nuxt/content@c5b1a4f",
|
||||||
"@nuxt/image": "^1.8.1",
|
"@nuxt/image": "^1.8.1",
|
||||||
"@nuxt/ui": "latest",
|
"@nuxt/ui": "latest",
|
||||||
"@nuxt/ui-pro": "https://pkg.pr.new/@nuxt/ui-pro@3cbc7f0",
|
"@nuxt/ui-pro": "https://pkg.pr.new/@nuxt/ui-pro@4fc0904",
|
||||||
"@nuxthub/core": "^0.8.7",
|
"@nuxthub/core": "^0.8.7",
|
||||||
"@nuxtjs/plausible": "^1.2.0",
|
"@nuxtjs/plausible": "^1.2.0",
|
||||||
"@octokit/rest": "^21.0.2",
|
"@octokit/rest": "^21.0.2",
|
||||||
|
|||||||
@@ -103,8 +103,8 @@
|
|||||||
"magic-string": "^0.30.14",
|
"magic-string": "^0.30.14",
|
||||||
"mlly": "^1.7.3",
|
"mlly": "^1.7.3",
|
||||||
"ohash": "^1.1.4",
|
"ohash": "^1.1.4",
|
||||||
|
"reka-ui": "https://pkg.pr.new/reka-ui@8238615",
|
||||||
"pathe": "^1.1.2",
|
"pathe": "^1.1.2",
|
||||||
"radix-vue": "^1.9.10",
|
|
||||||
"scule": "^1.3.0",
|
"scule": "^1.3.0",
|
||||||
"sirv": "^3.0.0",
|
"sirv": "^3.0.0",
|
||||||
"tailwind-variants": "^0.3.0",
|
"tailwind-variants": "^0.3.0",
|
||||||
|
|||||||
@@ -30,6 +30,6 @@ export default defineConfig({
|
|||||||
],
|
],
|
||||||
optimizeDeps: {
|
optimizeDeps: {
|
||||||
// prevents reloading page when navigating between components
|
// prevents reloading page when navigating between components
|
||||||
include: ['radix-vue/namespaced', 'vaul-vue', 'embla-carousel-vue', 'embla-carousel-autoplay', 'embla-carousel-auto-scroll', 'embla-carousel-auto-height', 'embla-carousel-class-names', 'embla-carousel-fade', 'embla-carousel-wheel-gestures']
|
include: ['reka-ui/namespaced', 'vaul-vue', 'embla-carousel-vue', 'embla-carousel-autoplay', 'embla-carousel-auto-scroll', 'embla-carousel-auto-height', 'embla-carousel-class-names', 'embla-carousel-fade', 'embla-carousel-wheel-gestures']
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ const checked = ref(true)
|
|||||||
<UCheckbox label="Error" color="error" :model-value="true" />
|
<UCheckbox label="Error" color="error" :model-value="true" />
|
||||||
<UCheckbox label="Icon" icon="i-lucide-heart" :model-value="true" />
|
<UCheckbox label="Icon" icon="i-lucide-heart" :model-value="true" />
|
||||||
<UCheckbox label="Default value" :default-value="true" />
|
<UCheckbox label="Default value" :default-value="true" />
|
||||||
<UCheckbox label="Indeterminate" indeterminate />
|
<UCheckbox label="Indeterminate" default-value="indeterminate" />
|
||||||
<UCheckbox label="Required" required />
|
<UCheckbox label="Required" required />
|
||||||
<UCheckbox label="Disabled" disabled />
|
<UCheckbox label="Disabled" disabled />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ defineShortcuts({
|
|||||||
<UButton label="Open modal" color="neutral" variant="outline" />
|
<UButton label="Open modal" color="neutral" variant="outline" />
|
||||||
|
|
||||||
<template #content>
|
<template #content>
|
||||||
<ReuseTemplate :close="true" @close="open = false" />
|
<ReuseTemplate :close="true" @update:open="open = $event" />
|
||||||
</template>
|
</template>
|
||||||
</UModal>
|
</UModal>
|
||||||
|
|
||||||
@@ -138,7 +138,7 @@ defineShortcuts({
|
|||||||
<UButton label="Open drawer" color="neutral" variant="outline" />
|
<UButton label="Open drawer" color="neutral" variant="outline" />
|
||||||
|
|
||||||
<template #content>
|
<template #content>
|
||||||
<ReuseTemplate class="border-t border-[var(--ui-border)]" />
|
<ReuseTemplate class="border-t border-[var(--ui-border)] mt-4" />
|
||||||
</template>
|
</template>
|
||||||
</UDrawer>
|
</UDrawer>
|
||||||
|
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ const { data: users, status } = await useFetch('https://jsonplaceholder.typicode
|
|||||||
<div class="flex flex-col gap-4 w-48">
|
<div class="flex flex-col gap-4 w-48">
|
||||||
<UInputMenu :items="items" placeholder="Disabled" disabled />
|
<UInputMenu :items="items" placeholder="Disabled" disabled />
|
||||||
<UInputMenu :items="items" placeholder="Required" required />
|
<UInputMenu :items="items" placeholder="Required" required />
|
||||||
<UInputMenu v-model="selectedItems" :items="items" placeholder="Multiple" multiple color="neutral" />
|
<UInputMenu v-model="selectedItems" :items="items" placeholder="Multiple" multiple />
|
||||||
<UInputMenu :items="items" loading placeholder="Search..." />
|
<UInputMenu :items="items" loading placeholder="Search..." />
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-4">
|
<div class="flex items-center gap-4">
|
||||||
@@ -119,7 +119,7 @@ const { data: users, status } = await useFetch('https://jsonplaceholder.typicode
|
|||||||
v-model:search-term="searchTerm"
|
v-model:search-term="searchTerm"
|
||||||
:items="users || []"
|
:items="users || []"
|
||||||
:loading="status === 'pending'"
|
:loading="status === 'pending'"
|
||||||
:filter="false"
|
ignore-filter
|
||||||
icon="i-lucide-user"
|
icon="i-lucide-user"
|
||||||
placeholder="Search users..."
|
placeholder="Search users..."
|
||||||
:size="size"
|
:size="size"
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ const { data: users, status } = await useFetch('https://jsonplaceholder.typicode
|
|||||||
v-model:search-term="searchTerm"
|
v-model:search-term="searchTerm"
|
||||||
:items="users || []"
|
:items="users || []"
|
||||||
:loading="status === 'pending'"
|
:loading="status === 'pending'"
|
||||||
:filter="false"
|
ignore-filter
|
||||||
icon="i-lucide-user"
|
icon="i-lucide-user"
|
||||||
placeholder="Search users..."
|
placeholder="Search users..."
|
||||||
:size="size"
|
:size="size"
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ const fruits = ['Apple', 'Banana', 'Blueberry', 'Grapes', 'Pineapple']
|
|||||||
const vegetables = ['Aubergine', 'Broccoli', 'Carrot', 'Courgette', 'Leek']
|
const vegetables = ['Aubergine', 'Broccoli', 'Carrot', 'Courgette', 'Leek']
|
||||||
|
|
||||||
const items = [[{ label: 'Fruits', type: 'label' }, ...fruits], [{ label: 'Vegetables', type: 'label' }, ...vegetables]]
|
const items = [[{ label: 'Fruits', type: 'label' }, ...fruits], [{ label: 'Vegetables', type: 'label' }, ...vegetables]]
|
||||||
|
const selectedItems = ref([fruits[0]!, vegetables[0]!])
|
||||||
|
|
||||||
const statuses = [{
|
const statuses = [{
|
||||||
label: 'Backlog',
|
label: 'Backlog',
|
||||||
@@ -40,7 +41,7 @@ const { data: users, status } = await useFetch('https://jsonplaceholder.typicode
|
|||||||
lazy: true
|
lazy: true
|
||||||
})
|
})
|
||||||
|
|
||||||
function getStatusIcon(value: string): string {
|
function getStatusIcon(value: string) {
|
||||||
return statuses.find(status => status.value === value)?.icon || 'i-lucide-user'
|
return statuses.find(status => status.value === value)?.icon || 'i-lucide-user'
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,7 +53,7 @@ function getUserAvatar(value: string) {
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col items-center gap-4">
|
<div class="flex flex-col items-center gap-4">
|
||||||
<div class="flex flex-col gap-4 w-48">
|
<div class="flex flex-col gap-4 w-48">
|
||||||
<USelect :items="items" placeholder="Search..." />
|
<USelect :items="items" placeholder="Search..." default-value="Apple" />
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<USelect
|
<USelect
|
||||||
@@ -90,6 +91,7 @@ function getUserAvatar(value: string) {
|
|||||||
<div class="flex flex-col gap-4 w-48">
|
<div class="flex flex-col gap-4 w-48">
|
||||||
<USelect :items="items" placeholder="Disabled" disabled />
|
<USelect :items="items" placeholder="Disabled" disabled />
|
||||||
<USelect :items="items" placeholder="Required" required />
|
<USelect :items="items" placeholder="Required" required />
|
||||||
|
<USelect v-model="selectedItems" :items="items" placeholder="Multiple" multiple />
|
||||||
<USelect :items="items" loading placeholder="Search..." />
|
<USelect :items="items" loading placeholder="Search..." />
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-4">
|
<div class="flex items-center gap-4">
|
||||||
@@ -114,7 +116,7 @@ function getUserAvatar(value: string) {
|
|||||||
class="w-48"
|
class="w-48"
|
||||||
>
|
>
|
||||||
<template #leading="{ modelValue, ui }">
|
<template #leading="{ modelValue, ui }">
|
||||||
<UIcon v-if="modelValue" :name="getStatusIcon(modelValue)" :class="ui.leadingIcon()" />
|
<UIcon v-if="modelValue" :name="getStatusIcon(modelValue as string)" :class="ui.leadingIcon()" />
|
||||||
</template>
|
</template>
|
||||||
</USelect>
|
</USelect>
|
||||||
</div>
|
</div>
|
||||||
@@ -130,7 +132,7 @@ function getUserAvatar(value: string) {
|
|||||||
class="w-48"
|
class="w-48"
|
||||||
>
|
>
|
||||||
<template #leading="{ modelValue, ui }">
|
<template #leading="{ modelValue, ui }">
|
||||||
<UAvatar v-if="modelValue" :size="ui.itemLeadingAvatarSize()" v-bind="getUserAvatar(modelValue)" />
|
<UAvatar v-if="modelValue" :size="ui.itemLeadingAvatarSize()" v-bind="getUserAvatar(modelValue as string)" />
|
||||||
</template>
|
</template>
|
||||||
</USelect>
|
</USelect>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -145,14 +145,13 @@ const data = ref<Payment[]>([{
|
|||||||
const columns: TableColumn<Payment>[] = [{
|
const columns: TableColumn<Payment>[] = [{
|
||||||
id: 'select',
|
id: 'select',
|
||||||
header: ({ table }) => h(UCheckbox, {
|
header: ({ table }) => h(UCheckbox, {
|
||||||
'modelValue': table.getIsAllPageRowsSelected(),
|
'modelValue': table.getIsSomePageRowsSelected() ? 'indeterminate' : table.getIsAllPageRowsSelected(),
|
||||||
'indeterminate': table.getIsSomePageRowsSelected(),
|
'onUpdate:modelValue': (value: boolean | 'indeterminate') => table.toggleAllPageRowsSelected(!!value),
|
||||||
'onUpdate:modelValue': (value: boolean) => table.toggleAllPageRowsSelected(!!value),
|
|
||||||
'ariaLabel': 'Select all'
|
'ariaLabel': 'Select all'
|
||||||
}),
|
}),
|
||||||
cell: ({ row }) => h(UCheckbox, {
|
cell: ({ row }) => h(UCheckbox, {
|
||||||
'modelValue': row.getIsSelected(),
|
'modelValue': row.getIsSelected(),
|
||||||
'onUpdate:modelValue': (value: boolean) => row.toggleSelected(!!value),
|
'onUpdate:modelValue': (value: boolean | 'indeterminate') => row.toggleSelected(!!value),
|
||||||
'ariaLabel': 'Select row'
|
'ariaLabel': 'Select row'
|
||||||
}),
|
}),
|
||||||
enableSorting: false,
|
enableSorting: false,
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import { createResolver } from '@nuxt/kit'
|
|||||||
const { resolve } = createResolver(import.meta.url)
|
const { resolve } = createResolver(import.meta.url)
|
||||||
|
|
||||||
export default defineNuxtConfig({
|
export default defineNuxtConfig({
|
||||||
|
|
||||||
modules: ['../src/module'],
|
modules: ['../src/module'],
|
||||||
|
|
||||||
devtools: { enabled: true },
|
devtools: { enabled: true },
|
||||||
|
|
||||||
ui: {
|
ui: {
|
||||||
|
|||||||
2047
pnpm-lock.yaml
generated
2047
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { tv } from 'tailwind-variants'
|
import { tv } from 'tailwind-variants'
|
||||||
import type { AccordionRootProps, AccordionRootEmits } from 'radix-vue'
|
import type { AccordionRootProps, AccordionRootEmits } from 'reka-ui'
|
||||||
import type { AppConfig } from '@nuxt/schema'
|
import type { AppConfig } from '@nuxt/schema'
|
||||||
import _appConfig from '#build/app.config'
|
import _appConfig from '#build/app.config'
|
||||||
import theme from '#build/ui/accordion'
|
import theme from '#build/ui/accordion'
|
||||||
@@ -22,7 +22,7 @@ export interface AccordionItem {
|
|||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AccordionProps<T> extends Pick<AccordionRootProps, 'collapsible' | 'defaultValue' | 'modelValue' | 'type' | 'disabled'> {
|
export interface AccordionProps<T> extends Pick<AccordionRootProps, 'collapsible' | 'defaultValue' | 'modelValue' | 'type' | 'disabled' | 'unmountOnHide'> {
|
||||||
/**
|
/**
|
||||||
* The element or component this component should render as.
|
* The element or component this component should render as.
|
||||||
* @defaultValue 'div'
|
* @defaultValue 'div'
|
||||||
@@ -89,7 +89,7 @@ extendDevtoolsMeta({
|
|||||||
|
|
||||||
<script setup lang="ts" generic="T extends AccordionItem">
|
<script setup lang="ts" generic="T extends AccordionItem">
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { AccordionRoot, AccordionItem, AccordionHeader, AccordionTrigger, AccordionContent, useForwardPropsEmits } from 'radix-vue'
|
import { AccordionRoot, AccordionItem, AccordionHeader, AccordionTrigger, AccordionContent, useForwardPropsEmits } from 'reka-ui'
|
||||||
import { reactivePick } from '@vueuse/core'
|
import { reactivePick } from '@vueuse/core'
|
||||||
import { useAppConfig } from '#imports'
|
import { useAppConfig } from '#imports'
|
||||||
import { get } from '../utils'
|
import { get } from '../utils'
|
||||||
@@ -104,7 +104,7 @@ const emits = defineEmits<AccordionEmits>()
|
|||||||
const slots = defineSlots<AccordionSlots<T>>()
|
const slots = defineSlots<AccordionSlots<T>>()
|
||||||
|
|
||||||
const appConfig = useAppConfig()
|
const appConfig = useAppConfig()
|
||||||
const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'collapsible', 'defaultValue', 'disabled', 'modelValue', 'type'), emits)
|
const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'collapsible', 'defaultValue', 'disabled', 'modelValue', 'type', 'unmountOnHide'), emits)
|
||||||
|
|
||||||
const ui = computed(() => accordion({
|
const ui = computed(() => accordion({
|
||||||
disabled: props.disabled
|
disabled: props.disabled
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ extendDevtoolsMeta<AlertProps>({ defaultProps: { title: 'Heads up!' } })
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { Primitive } from 'radix-vue'
|
import { Primitive } from 'reka-ui'
|
||||||
import { useAppConfig } from '#imports'
|
import { useAppConfig } from '#imports'
|
||||||
import { useLocale } from '../composables/useLocale'
|
import { useLocale } from '../composables/useLocale'
|
||||||
import UIcon from './Icon.vue'
|
import UIcon from './Icon.vue'
|
||||||
@@ -75,8 +75,8 @@ const props = defineProps<AlertProps>()
|
|||||||
const emits = defineEmits<AlertEmits>()
|
const emits = defineEmits<AlertEmits>()
|
||||||
const slots = defineSlots<AlertSlots>()
|
const slots = defineSlots<AlertSlots>()
|
||||||
|
|
||||||
const appConfig = useAppConfig()
|
|
||||||
const { t } = useLocale()
|
const { t } = useLocale()
|
||||||
|
const appConfig = useAppConfig()
|
||||||
|
|
||||||
const multiline = computed(() => !!props.title && !!props.description)
|
const multiline = computed(() => !!props.title && !!props.description)
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { ConfigProviderProps, TooltipProviderProps } from 'radix-vue'
|
import type { ConfigProviderProps, TooltipProviderProps } from 'reka-ui'
|
||||||
import { localeContextInjectionKey } from '../composables/useLocale'
|
import { localeContextInjectionKey } from '../composables/useLocale'
|
||||||
import { extendDevtoolsMeta } from '../composables/extendDevtoolsMeta'
|
import { extendDevtoolsMeta } from '../composables/extendDevtoolsMeta'
|
||||||
import type { ToasterProps, Locale } from '../types'
|
import type { ToasterProps, Locale } from '../types'
|
||||||
|
|
||||||
export interface AppProps extends Omit<ConfigProviderProps, 'useId' | 'dir'> {
|
export interface AppProps extends Omit<ConfigProviderProps, 'useId' | 'dir' | 'locale'> {
|
||||||
tooltip?: TooltipProviderProps
|
tooltip?: TooltipProviderProps
|
||||||
toaster?: ToasterProps | null
|
toaster?: ToasterProps | null
|
||||||
locale?: Locale
|
locale?: Locale
|
||||||
@@ -23,7 +23,7 @@ extendDevtoolsMeta({ ignore: true })
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { toRef, useId, provide } from 'vue'
|
import { toRef, useId, provide } from 'vue'
|
||||||
import { ConfigProvider, TooltipProvider, useForwardProps } from 'radix-vue'
|
import { ConfigProvider, TooltipProvider, useForwardProps } from 'reka-ui'
|
||||||
import { reactivePick } from '@vueuse/core'
|
import { reactivePick } from '@vueuse/core'
|
||||||
import UToaster from './Toaster.vue'
|
import UToaster from './Toaster.vue'
|
||||||
import UModalProvider from './ModalProvider.vue'
|
import UModalProvider from './ModalProvider.vue'
|
||||||
@@ -41,7 +41,7 @@ provide(localeContextInjectionKey, locale)
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<ConfigProvider :use-id="() => (useId() as string)" :dir="locale?.dir" v-bind="configProviderProps">
|
<ConfigProvider :use-id="() => (useId() as string)" :dir="locale?.dir" :locale="locale?.code" v-bind="configProviderProps">
|
||||||
<TooltipProvider v-bind="tooltipProps">
|
<TooltipProvider v-bind="tooltipProps">
|
||||||
<UToaster v-if="toaster !== null" v-bind="toasterProps">
|
<UToaster v-if="toaster !== null" v-bind="toasterProps">
|
||||||
<slot />
|
<slot />
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { tv, type VariantProps } from 'tailwind-variants'
|
import { tv, type VariantProps } from 'tailwind-variants'
|
||||||
import type { AvatarFallbackProps } from 'radix-vue'
|
import type { AvatarFallbackProps } from 'reka-ui'
|
||||||
import type { AppConfig } from '@nuxt/schema'
|
import type { AppConfig } from '@nuxt/schema'
|
||||||
import { extendDevtoolsMeta } from '../composables/extendDevtoolsMeta'
|
import { extendDevtoolsMeta } from '../composables/extendDevtoolsMeta'
|
||||||
import _appConfig from '#build/app.config'
|
import _appConfig from '#build/app.config'
|
||||||
@@ -32,7 +32,7 @@ extendDevtoolsMeta<AvatarProps>({ defaultProps: { src: 'https://avatars.githubus
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { AvatarRoot, AvatarImage, AvatarFallback, useForwardProps } from 'radix-vue'
|
import { AvatarRoot, AvatarImage, AvatarFallback, useForwardProps } from 'reka-ui'
|
||||||
import { reactivePick } from '@vueuse/core'
|
import { reactivePick } from '@vueuse/core'
|
||||||
import { useAvatarGroup } from '../composables/useAvatarGroup'
|
import { useAvatarGroup } from '../composables/useAvatarGroup'
|
||||||
import UIcon from './Icon.vue'
|
import UIcon from './Icon.vue'
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ extendDevtoolsMeta({ example: 'AvatarGroupExample' })
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, provide } from 'vue'
|
import { computed, provide } from 'vue'
|
||||||
import { Primitive } from 'radix-vue'
|
import { Primitive } from 'reka-ui'
|
||||||
import { avatarGroupInjectionKey } from '../composables/useAvatarGroup'
|
import { avatarGroupInjectionKey } from '../composables/useAvatarGroup'
|
||||||
import UAvatar from './Avatar.vue'
|
import UAvatar from './Avatar.vue'
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export interface BadgeSlots {
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { Primitive } from 'radix-vue'
|
import { Primitive } from 'reka-ui'
|
||||||
import { useComponentIcons } from '../composables/useComponentIcons'
|
import { useComponentIcons } from '../composables/useComponentIcons'
|
||||||
import UIcon from './Icon.vue'
|
import UIcon from './Icon.vue'
|
||||||
|
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ extendDevtoolsMeta({
|
|||||||
|
|
||||||
<script setup lang="ts" generic="T extends BreadcrumbItem">
|
<script setup lang="ts" generic="T extends BreadcrumbItem">
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { Primitive } from 'radix-vue'
|
import { Primitive } from 'reka-ui'
|
||||||
import { useAppConfig } from '#imports'
|
import { useAppConfig } from '#imports'
|
||||||
import { useLocale } from '../composables/useLocale'
|
import { useLocale } from '../composables/useLocale'
|
||||||
import { get } from '../utils'
|
import { get } from '../utils'
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export interface ButtonSlots {
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { type Ref, computed, ref, inject } from 'vue'
|
import { type Ref, computed, ref, inject } from 'vue'
|
||||||
import { useForwardProps } from 'radix-vue'
|
import { useForwardProps } from 'reka-ui'
|
||||||
import { useComponentIcons } from '../composables/useComponentIcons'
|
import { useComponentIcons } from '../composables/useComponentIcons'
|
||||||
import { useButtonGroup } from '../composables/useButtonGroup'
|
import { useButtonGroup } from '../composables/useButtonGroup'
|
||||||
import { formLoadingInjectionKey } from '../composables/useFormField'
|
import { formLoadingInjectionKey } from '../composables/useFormField'
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ extendDevtoolsMeta({ example: 'ButtonGroupExample' })
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { provide, computed } from 'vue'
|
import { provide, computed } from 'vue'
|
||||||
import { Primitive } from 'radix-vue'
|
import { Primitive } from 'reka-ui'
|
||||||
import { buttonGroupInjectionKey } from '../composables/useButtonGroup'
|
import { buttonGroupInjectionKey } from '../composables/useButtonGroup'
|
||||||
|
|
||||||
const props = withDefaults(defineProps<ButtonGroupProps>(), {
|
const props = withDefaults(defineProps<ButtonGroupProps>(), {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { tv, type VariantProps } from 'tailwind-variants'
|
import { tv, type VariantProps } from 'tailwind-variants'
|
||||||
import type { CalendarRootProps, CalendarRootEmits, RangeCalendarRootEmits, DateRange, CalendarCellTriggerProps } from 'radix-vue'
|
import type { CalendarRootProps, CalendarRootEmits, RangeCalendarRootEmits, DateRange, CalendarCellTriggerProps } from 'reka-ui'
|
||||||
import type { DateValue } from '@internationalized/date'
|
import type { DateValue } from '@internationalized/date'
|
||||||
import type { AppConfig } from '@nuxt/schema'
|
import type { AppConfig } from '@nuxt/schema'
|
||||||
import _appConfig from '#build/app.config'
|
import _appConfig from '#build/app.config'
|
||||||
@@ -73,8 +73,8 @@ export interface CalendarSlots {
|
|||||||
|
|
||||||
<script setup lang="ts" generic="R extends boolean = false, M extends boolean = false">
|
<script setup lang="ts" generic="R extends boolean = false, M extends boolean = false">
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { useForwardPropsEmits } from 'radix-vue'
|
import { useForwardPropsEmits } from 'reka-ui'
|
||||||
import { Calendar as SingleCalendar, RangeCalendar } from 'radix-vue/namespaced'
|
import { Calendar as SingleCalendar, RangeCalendar } from 'reka-ui/namespaced'
|
||||||
import { reactiveOmit } from '@vueuse/core'
|
import { reactiveOmit } from '@vueuse/core'
|
||||||
import { useLocale } from '../composables/useLocale'
|
import { useLocale } from '../composables/useLocale'
|
||||||
import UButton from './Button.vue'
|
import UButton from './Button.vue'
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ extendDevtoolsMeta({ example: 'CardExample' })
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Primitive } from 'radix-vue'
|
import { Primitive } from 'reka-ui'
|
||||||
|
|
||||||
const props = defineProps<CardProps>()
|
const props = defineProps<CardProps>()
|
||||||
const slots = defineSlots<CardSlots>()
|
const slots = defineSlots<CardSlots>()
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { tv, type VariantProps } from 'tailwind-variants'
|
import { tv, type VariantProps } from 'tailwind-variants'
|
||||||
import type { AppConfig } from '@nuxt/schema'
|
import type { AppConfig } from '@nuxt/schema'
|
||||||
|
import type { AcceptableValue } from 'reka-ui'
|
||||||
import type { EmblaCarouselType, EmblaOptionsType, EmblaPluginType } from 'embla-carousel'
|
import type { EmblaCarouselType, EmblaOptionsType, EmblaPluginType } from 'embla-carousel'
|
||||||
import type { AutoplayOptionsType } from 'embla-carousel-autoplay'
|
import type { AutoplayOptionsType } from 'embla-carousel-autoplay'
|
||||||
import type { AutoScrollOptionsType } from 'embla-carousel-auto-scroll'
|
import type { AutoScrollOptionsType } from 'embla-carousel-auto-scroll'
|
||||||
@@ -12,7 +13,7 @@ import _appConfig from '#build/app.config'
|
|||||||
import theme from '#build/ui/carousel'
|
import theme from '#build/ui/carousel'
|
||||||
import { extendDevtoolsMeta } from '../composables/extendDevtoolsMeta'
|
import { extendDevtoolsMeta } from '../composables/extendDevtoolsMeta'
|
||||||
import type { ButtonProps } from '../types'
|
import type { ButtonProps } from '../types'
|
||||||
import type { AcceptableValue, PartialString } from '../types/utils'
|
import type { PartialString } from '../types/utils'
|
||||||
|
|
||||||
const appConfig = _appConfig as AppConfig & { ui: { carousel: Partial<typeof theme> } }
|
const appConfig = _appConfig as AppConfig & { ui: { carousel: Partial<typeof theme> } }
|
||||||
|
|
||||||
@@ -21,6 +22,11 @@ const carousel = tv({ extend: tv(theme), ...(appConfig.ui?.carousel || {}) })
|
|||||||
type CarouselVariants = VariantProps<typeof carousel>
|
type CarouselVariants = VariantProps<typeof carousel>
|
||||||
|
|
||||||
export interface CarouselProps<T> extends Omit<EmblaOptionsType, 'axis' | 'container' | 'slides' | 'direction'> {
|
export interface CarouselProps<T> extends Omit<EmblaOptionsType, 'axis' | 'container' | 'slides' | 'direction'> {
|
||||||
|
/**
|
||||||
|
* The element or component this component should render as.
|
||||||
|
* @defaultValue 'div'
|
||||||
|
*/
|
||||||
|
as?: any
|
||||||
/**
|
/**
|
||||||
* Configure the prev button when arrows are enabled.
|
* Configure the prev button when arrows are enabled.
|
||||||
* @defaultValue { size: 'md', color: 'neutral', variant: 'link' }
|
* @defaultValue { size: 'md', color: 'neutral', variant: 'link' }
|
||||||
@@ -97,7 +103,7 @@ extendDevtoolsMeta({ example: 'CarouselExample' })
|
|||||||
<script setup lang="ts" generic="T extends AcceptableValue">
|
<script setup lang="ts" generic="T extends AcceptableValue">
|
||||||
import { computed, ref, watch, onMounted } from 'vue'
|
import { computed, ref, watch, onMounted } from 'vue'
|
||||||
import useEmblaCarousel from 'embla-carousel-vue'
|
import useEmblaCarousel from 'embla-carousel-vue'
|
||||||
import { useForwardProps } from 'radix-vue'
|
import { Primitive, useForwardProps } from 'reka-ui'
|
||||||
import { reactivePick, computedAsync } from '@vueuse/core'
|
import { reactivePick, computedAsync } from '@vueuse/core'
|
||||||
import { useAppConfig } from '#imports'
|
import { useAppConfig } from '#imports'
|
||||||
import { useLocale } from '../composables/useLocale'
|
import { useLocale } from '../composables/useLocale'
|
||||||
@@ -254,7 +260,8 @@ defineExpose({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div
|
<Primitive
|
||||||
|
:as="as"
|
||||||
role="region"
|
role="region"
|
||||||
aria-roledescription="carousel"
|
aria-roledescription="carousel"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
@@ -311,5 +318,5 @@ defineExpose({
|
|||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Primitive>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { tv, type VariantProps } from 'tailwind-variants'
|
import { tv, type VariantProps } from 'tailwind-variants'
|
||||||
import type { CheckboxRootProps } from 'radix-vue'
|
import type { CheckboxRootProps } from 'reka-ui'
|
||||||
import type { AppConfig } from '@nuxt/schema'
|
import type { AppConfig } from '@nuxt/schema'
|
||||||
import _appConfig from '#build/app.config'
|
import _appConfig from '#build/app.config'
|
||||||
import theme from '#build/ui/checkbox'
|
import theme from '#build/ui/checkbox'
|
||||||
@@ -12,7 +12,12 @@ const checkbox = tv({ extend: tv(theme), ...(appConfig.ui?.checkbox || {}) })
|
|||||||
|
|
||||||
type CheckboxVariants = VariantProps<typeof checkbox>
|
type CheckboxVariants = VariantProps<typeof checkbox>
|
||||||
|
|
||||||
export interface CheckboxProps extends Pick<CheckboxRootProps, 'disabled' | 'required' | 'name' | 'value' | 'id'> {
|
export interface CheckboxProps extends Pick<CheckboxRootProps, 'disabled' | 'required' | 'name' | 'value' | 'id' | 'defaultValue'> {
|
||||||
|
/**
|
||||||
|
* The element or component this component should render as.
|
||||||
|
* @defaultValue 'div'
|
||||||
|
*/
|
||||||
|
as?: any
|
||||||
label?: string
|
label?: string
|
||||||
description?: string
|
description?: string
|
||||||
color?: CheckboxVariants['color']
|
color?: CheckboxVariants['color']
|
||||||
@@ -22,21 +27,17 @@ export interface CheckboxProps extends Pick<CheckboxRootProps, 'disabled' | 'req
|
|||||||
* @defaultValue appConfig.ui.icons.check
|
* @defaultValue appConfig.ui.icons.check
|
||||||
*/
|
*/
|
||||||
icon?: string
|
icon?: string
|
||||||
indeterminate?: boolean
|
|
||||||
/**
|
/**
|
||||||
* The icon displayed when the checkbox is indeterminate.
|
* The icon displayed when the checkbox is indeterminate.
|
||||||
* @defaultValue appConfig.ui.icons.minus
|
* @defaultValue appConfig.ui.icons.minus
|
||||||
*/
|
*/
|
||||||
indeterminateIcon?: string
|
indeterminateIcon?: string
|
||||||
/** The checked state of the checkbox when it is initially rendered. Use when you do not need to control its checked state. */
|
|
||||||
defaultValue?: boolean
|
|
||||||
class?: any
|
class?: any
|
||||||
ui?: Partial<typeof checkbox.slots>
|
ui?: Partial<typeof checkbox.slots>
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CheckboxEmits {
|
export type CheckboxEmits = {
|
||||||
(e: 'update:modelValue', payload: boolean): void
|
change: [payload: Event]
|
||||||
(e: 'change', payload: Event): void
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CheckboxSlots {
|
export interface CheckboxSlots {
|
||||||
@@ -49,7 +50,7 @@ extendDevtoolsMeta({ defaultProps: { label: 'Check me!' } })
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, useId } from 'vue'
|
import { computed, useId } from 'vue'
|
||||||
import { CheckboxRoot, CheckboxIndicator, Label, useForwardProps } from 'radix-vue'
|
import { Primitive, CheckboxRoot, CheckboxIndicator, Label, useForwardProps } from 'reka-ui'
|
||||||
import { reactivePick } from '@vueuse/core'
|
import { reactivePick } from '@vueuse/core'
|
||||||
import { useAppConfig } from '#imports'
|
import { useAppConfig } from '#imports'
|
||||||
import { useFormField } from '../composables/useFormField'
|
import { useFormField } from '../composables/useFormField'
|
||||||
@@ -59,29 +60,20 @@ const props = defineProps<CheckboxProps>()
|
|||||||
const slots = defineSlots<CheckboxSlots>()
|
const slots = defineSlots<CheckboxSlots>()
|
||||||
const emits = defineEmits<CheckboxEmits>()
|
const emits = defineEmits<CheckboxEmits>()
|
||||||
|
|
||||||
const modelValue = defineModel<boolean | undefined>({ default: undefined })
|
const modelValue = defineModel<boolean | 'indeterminate'>({ default: undefined })
|
||||||
|
|
||||||
const rootProps = useForwardProps(reactivePick(props, 'required', 'value'))
|
const rootProps = useForwardProps(reactivePick(props, 'required', 'value', 'defaultValue'))
|
||||||
|
|
||||||
const appConfig = useAppConfig()
|
const appConfig = useAppConfig()
|
||||||
const { id: _id, emitFormChange, emitFormInput, size, color, name, disabled } = useFormField<CheckboxProps>(props)
|
const { id: _id, emitFormChange, emitFormInput, size, color, name, disabled } = useFormField<CheckboxProps>(props)
|
||||||
const id = _id.value ?? useId()
|
const id = _id.value ?? useId()
|
||||||
|
|
||||||
const checked = computed({
|
|
||||||
get() {
|
|
||||||
return props.indeterminate ? 'indeterminate' : modelValue.value
|
|
||||||
},
|
|
||||||
set(value) {
|
|
||||||
modelValue.value = value === 'indeterminate' ? undefined : value
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const ui = computed(() => checkbox({
|
const ui = computed(() => checkbox({
|
||||||
size: size.value,
|
size: size.value,
|
||||||
color: color.value,
|
color: color.value,
|
||||||
required: props.required,
|
required: props.required,
|
||||||
disabled: disabled.value,
|
disabled: disabled.value,
|
||||||
checked: (modelValue.value ?? props.defaultValue) || props.indeterminate
|
checked: Boolean(modelValue.value ?? props.defaultValue)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
function onUpdate(value: any) {
|
function onUpdate(value: any) {
|
||||||
@@ -93,23 +85,25 @@ function onUpdate(value: any) {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- eslint-disable vue/no-template-shadow -->
|
||||||
<template>
|
<template>
|
||||||
<div :class="ui.root({ class: [props.class, props.ui?.root] })">
|
<Primitive :as="as" :class="ui.root({ class: [props.class, props.ui?.root] })">
|
||||||
<div :class="ui.container({ class: props.ui?.container })">
|
<div :class="ui.container({ class: props.ui?.container })">
|
||||||
<CheckboxRoot
|
<CheckboxRoot
|
||||||
:id="id"
|
:id="id"
|
||||||
v-model:checked="checked"
|
|
||||||
:default-checked="defaultValue"
|
|
||||||
v-bind="rootProps"
|
v-bind="rootProps"
|
||||||
|
v-model="modelValue"
|
||||||
:name="name"
|
:name="name"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
:class="ui.base({ class: props.ui?.base })"
|
:class="ui.base({ class: props.ui?.base })"
|
||||||
@update:checked="onUpdate"
|
@update:model-value="onUpdate"
|
||||||
>
|
>
|
||||||
<CheckboxIndicator as-child>
|
<template #default="{ modelValue }">
|
||||||
<UIcon v-if="indeterminate" :name="indeterminateIcon || appConfig.ui.icons.minus" :class="ui.icon({ class: props.ui?.icon })" />
|
<CheckboxIndicator as-child>
|
||||||
<UIcon v-else :name="icon || appConfig.ui.icons.check" :class="ui.icon({ class: props.ui?.icon })" />
|
<UIcon v-if="modelValue === 'indeterminate'" :name="indeterminateIcon || appConfig.ui.icons.minus" :class="ui.icon({ class: props.ui?.icon })" />
|
||||||
</CheckboxIndicator>
|
<UIcon v-else :name="icon || appConfig.ui.icons.check" :class="ui.icon({ class: props.ui?.icon })" />
|
||||||
|
</CheckboxIndicator>
|
||||||
|
</template>
|
||||||
</CheckboxRoot>
|
</CheckboxRoot>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -125,5 +119,5 @@ function onUpdate(value: any) {
|
|||||||
</slot>
|
</slot>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Primitive>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ extendDevtoolsMeta({ example: 'ChipExample' })
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { Primitive, Slot } from 'radix-vue'
|
import { Primitive, Slot } from 'reka-ui'
|
||||||
import { useAvatarGroup } from '../composables/useAvatarGroup'
|
import { useAvatarGroup } from '../composables/useAvatarGroup'
|
||||||
|
|
||||||
defineOptions({ inheritAttrs: false })
|
defineOptions({ inheritAttrs: false })
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { tv } from 'tailwind-variants'
|
import { tv } from 'tailwind-variants'
|
||||||
import type { CollapsibleRootProps, CollapsibleRootEmits } from 'radix-vue'
|
import type { CollapsibleRootProps, CollapsibleRootEmits } from 'reka-ui'
|
||||||
import type { AppConfig } from '@nuxt/schema'
|
import type { AppConfig } from '@nuxt/schema'
|
||||||
import _appConfig from '#build/app.config'
|
import _appConfig from '#build/app.config'
|
||||||
import theme from '#build/ui/collapsible'
|
import theme from '#build/ui/collapsible'
|
||||||
@@ -10,7 +10,7 @@ const appConfig = _appConfig as AppConfig & { ui: { collapsible: Partial<typeof
|
|||||||
|
|
||||||
const collapsible = tv({ extend: tv(theme), ...(appConfig.ui?.collapsible || {}) })
|
const collapsible = tv({ extend: tv(theme), ...(appConfig.ui?.collapsible || {}) })
|
||||||
|
|
||||||
export interface CollapsibleProps extends Pick<CollapsibleRootProps, 'defaultOpen' | 'open' | 'disabled'> {
|
export interface CollapsibleProps extends Pick<CollapsibleRootProps, 'defaultOpen' | 'open' | 'disabled' | 'unmountOnHide'> {
|
||||||
/**
|
/**
|
||||||
* The element or component this component should render as.
|
* The element or component this component should render as.
|
||||||
* @defaultValue 'div'
|
* @defaultValue 'div'
|
||||||
@@ -31,14 +31,14 @@ extendDevtoolsMeta({ example: 'CollapsibleExample' })
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { CollapsibleRoot, CollapsibleTrigger, CollapsibleContent, useForwardPropsEmits } from 'radix-vue'
|
import { CollapsibleRoot, CollapsibleTrigger, CollapsibleContent, useForwardPropsEmits } from 'reka-ui'
|
||||||
import { reactivePick } from '@vueuse/core'
|
import { reactivePick } from '@vueuse/core'
|
||||||
|
|
||||||
const props = defineProps<CollapsibleProps>()
|
const props = defineProps<CollapsibleProps>()
|
||||||
const emits = defineEmits<CollapsibleEmits>()
|
const emits = defineEmits<CollapsibleEmits>()
|
||||||
const slots = defineSlots<CollapsibleSlots>()
|
const slots = defineSlots<CollapsibleSlots>()
|
||||||
|
|
||||||
const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'defaultOpen', 'open', 'disabled'), emits)
|
const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'defaultOpen', 'open', 'disabled', 'unmountOnHide'), emits)
|
||||||
|
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
const ui = collapsible()
|
const ui = collapsible()
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { tv } from 'tailwind-variants'
|
import { tv } from 'tailwind-variants'
|
||||||
import type { ComboboxRootProps, ComboboxRootEmits } from 'radix-vue'
|
import type { ListboxRootProps, ListboxRootEmits } from 'reka-ui'
|
||||||
import type { FuseResult } from 'fuse.js'
|
import type { FuseResult } from 'fuse.js'
|
||||||
import type { AppConfig } from '@nuxt/schema'
|
import type { AppConfig } from '@nuxt/schema'
|
||||||
import type { UseFuseOptions } from '@vueuse/integrations/useFuse'
|
import type { UseFuseOptions } from '@vueuse/integrations/useFuse'
|
||||||
@@ -37,17 +37,17 @@ export interface CommandPaletteGroup<T> {
|
|||||||
items?: T[]
|
items?: T[]
|
||||||
/**
|
/**
|
||||||
* Whether to filter group items with [useFuse](https://vueuse.org/integrations/useFuse).
|
* Whether to filter group items with [useFuse](https://vueuse.org/integrations/useFuse).
|
||||||
* When `false`, items will not be filtered which is useful for custom filtering (useAsyncData, useFetch, etc.).
|
* When `true`, items will not be filtered which is useful for custom filtering (useAsyncData, useFetch, etc.).
|
||||||
* @defaultValue true
|
* @defaultValue false
|
||||||
*/
|
*/
|
||||||
filter?: boolean
|
ignoreFilter?: boolean
|
||||||
/** Filter group items after the search happened. */
|
/** Filter group items after the search happened. */
|
||||||
postFilter?: (searchTerm: string, items: T[]) => T[]
|
postFilter?: (searchTerm: string, items: T[]) => T[]
|
||||||
/** The icon displayed when an item is highlighted. */
|
/** The icon displayed when an item is highlighted. */
|
||||||
highlightedIcon?: string
|
highlightedIcon?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CommandPaletteProps<G, T> extends Pick<ComboboxRootProps, 'multiple' | 'disabled' | 'modelValue' | 'defaultValue' | 'selectedValue' | 'resetSearchTermOnBlur'>, Pick<UseComponentIconsProps, 'loading' | 'loadingIcon'> {
|
export interface CommandPaletteProps<G, T> extends Pick<ListboxRootProps, 'multiple' | 'disabled' | 'modelValue' | 'defaultValue' | 'highlightOnHover'>, Pick<UseComponentIconsProps, 'loading' | 'loadingIcon'> {
|
||||||
/**
|
/**
|
||||||
* The element or component this component should render as.
|
* The element or component this component should render as.
|
||||||
* @defaultValue 'div'
|
* @defaultValue 'div'
|
||||||
@@ -102,7 +102,9 @@ export interface CommandPaletteProps<G, T> extends Pick<ComboboxRootProps, 'mult
|
|||||||
ui?: PartialString<typeof commandPalette.slots>
|
ui?: PartialString<typeof commandPalette.slots>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CommandPaletteEmits<T> = ComboboxRootEmits<T>
|
export type CommandPaletteEmits<T> = ListboxRootEmits<T> & {
|
||||||
|
'update:open': [value: boolean]
|
||||||
|
}
|
||||||
|
|
||||||
type SlotProps<T> = (props: { item: T, index: number }) => any
|
type SlotProps<T> = (props: { item: T, index: number }) => any
|
||||||
|
|
||||||
@@ -120,7 +122,7 @@ extendDevtoolsMeta({ example: 'CommandPaletteExample', ignoreProps: ['groups'] }
|
|||||||
|
|
||||||
<script setup lang="ts" generic="G extends CommandPaletteGroup<T>, T extends CommandPaletteItem">
|
<script setup lang="ts" generic="G extends CommandPaletteGroup<T>, T extends CommandPaletteItem">
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { ComboboxRoot, ComboboxInput, ComboboxPortal, ComboboxContent, ComboboxEmpty, ComboboxViewport, ComboboxGroup, ComboboxLabel, ComboboxItem, ComboboxItemIndicator, useForwardProps, useForwardPropsEmits } from 'radix-vue'
|
import { ListboxRoot, ListboxFilter, ListboxContent, ListboxGroup, ListboxGroupLabel, ListboxItem, ListboxItemIndicator, useForwardProps, useForwardPropsEmits } from 'reka-ui'
|
||||||
import { defu } from 'defu'
|
import { defu } from 'defu'
|
||||||
import { reactivePick } from '@vueuse/core'
|
import { reactivePick } from '@vueuse/core'
|
||||||
import { useFuse } from '@vueuse/integrations/useFuse'
|
import { useFuse } from '@vueuse/integrations/useFuse'
|
||||||
@@ -145,9 +147,10 @@ const slots = defineSlots<CommandPaletteSlots<G, T>>()
|
|||||||
|
|
||||||
const searchTerm = defineModel<string>('searchTerm', { default: '' })
|
const searchTerm = defineModel<string>('searchTerm', { default: '' })
|
||||||
|
|
||||||
const appConfig = useAppConfig()
|
|
||||||
const { t } = useLocale()
|
const { t } = useLocale()
|
||||||
const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'disabled', 'multiple', 'modelValue', 'defaultValue', 'selectedValue', 'resetSearchTermOnBlur'), emits)
|
const appConfig = useAppConfig()
|
||||||
|
|
||||||
|
const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'disabled', 'multiple', 'modelValue', 'defaultValue', 'highlightOnHover'), emits)
|
||||||
const inputProps = useForwardProps(reactivePick(props, 'loading', 'loadingIcon', 'placeholder'))
|
const inputProps = useForwardProps(reactivePick(props, 'loading', 'loadingIcon', 'placeholder'))
|
||||||
|
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
@@ -169,7 +172,7 @@ const items = computed(() => props.groups?.filter((group) => {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if (group.filter === false) {
|
if (group.ignoreFilter) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,7 +220,7 @@ const groups = computed(() => {
|
|||||||
return getGroupWithItems(group, items)
|
return getGroupWithItems(group, items)
|
||||||
}).filter(group => !!group)
|
}).filter(group => !!group)
|
||||||
|
|
||||||
const nonFuseGroups = props.groups?.filter(group => group.filter === false && group.items?.length).map((group) => {
|
const nonFuseGroups = props.groups?.filter(group => group.ignoreFilter && group.items?.length).map((group) => {
|
||||||
return getGroupWithItems(group, group.items || [])
|
return getGroupWithItems(group, group.items || [])
|
||||||
}) || []
|
}) || []
|
||||||
|
|
||||||
@@ -230,8 +233,8 @@ const groups = computed(() => {
|
|||||||
|
|
||||||
<!-- eslint-disable vue/no-v-html -->
|
<!-- eslint-disable vue/no-v-html -->
|
||||||
<template>
|
<template>
|
||||||
<ComboboxRoot v-bind="rootProps" v-model:search-term="searchTerm" open :class="ui.root({ class: [props.class, props.ui?.root] })">
|
<ListboxRoot v-bind="rootProps" :class="ui.root({ class: [props.class, props.ui?.root] })">
|
||||||
<ComboboxInput as-child>
|
<ListboxFilter v-model="searchTerm" as-child>
|
||||||
<UInput
|
<UInput
|
||||||
variant="none"
|
variant="none"
|
||||||
autofocus
|
autofocus
|
||||||
@@ -256,72 +259,70 @@ const groups = computed(() => {
|
|||||||
</slot>
|
</slot>
|
||||||
</template>
|
</template>
|
||||||
</UInput>
|
</UInput>
|
||||||
</ComboboxInput>
|
</ListboxFilter>
|
||||||
|
|
||||||
<ComboboxPortal disabled>
|
<ListboxContent :class="ui.content({ class: props.ui?.content })">
|
||||||
<ComboboxContent :class="ui.content({ class: props.ui?.content })" :dismissable="false">
|
<div v-if="groups?.length" :class="ui.viewport({ class: props.ui?.viewport })">
|
||||||
<ComboboxEmpty :class="ui.empty({ class: props.ui?.empty })">
|
<ListboxGroup v-for="(group, groupIndex) in groups" :key="`group-${groupIndex}`" :class="ui.group({ class: props.ui?.group })">
|
||||||
<slot name="empty" :search-term="searchTerm">
|
<ListboxGroupLabel v-if="get(group, props.labelKey as string)" :class="ui.label({ class: props.ui?.label })">
|
||||||
{{ searchTerm ? t('commandPalette.noMatch', { searchTerm }) : t('commandPalette.noData') }}
|
{{ get(group, props.labelKey as string) }}
|
||||||
</slot>
|
</ListboxGroupLabel>
|
||||||
</ComboboxEmpty>
|
|
||||||
|
|
||||||
<ComboboxViewport :class="ui.viewport({ class: props.ui?.viewport })">
|
<ListboxItem
|
||||||
<ComboboxGroup v-for="(group, groupIndex) in groups" :key="`group-${groupIndex}`" :class="ui.group({ class: props.ui?.group })">
|
v-for="(item, index) in group.items"
|
||||||
<ComboboxLabel v-if="get(group, props.labelKey as string)" :class="ui.label({ class: props.ui?.label })">
|
:key="`group-${groupIndex}-${index}`"
|
||||||
{{ get(group, props.labelKey as string) }}
|
:value="omit(item, ['matches' as any, 'group' as any, 'onSelect', 'labelHtml', 'suffixHtml'])"
|
||||||
</ComboboxLabel>
|
:disabled="item.disabled"
|
||||||
|
:class="ui.item({ class: props.ui?.item, active: item.active })"
|
||||||
|
@select="item.onSelect"
|
||||||
|
>
|
||||||
|
<slot :name="item.slot || group.slot || 'item'" :item="item" :index="index">
|
||||||
|
<slot :name="item.slot ? `${item.slot}-leading` : group.slot ? `${group.slot}-leading` : `item-leading`" :item="item" :index="index">
|
||||||
|
<UIcon v-if="item.loading" :name="loadingIcon || appConfig.ui.icons.loading" :class="ui.itemLeadingIcon({ class: props.ui?.itemLeadingIcon, loading: true })" />
|
||||||
|
<UIcon v-else-if="item.icon" :name="item.icon" :class="ui.itemLeadingIcon({ class: props.ui?.itemLeadingIcon, active: item.active })" />
|
||||||
|
<UAvatar v-else-if="item.avatar" :size="((props.ui?.itemLeadingAvatarSize || ui.itemLeadingAvatarSize()) as AvatarProps['size'])" v-bind="item.avatar" :class="ui.itemLeadingAvatar({ class: props.ui?.itemLeadingAvatar, active: item.active })" />
|
||||||
|
<UChip
|
||||||
|
v-else-if="item.chip"
|
||||||
|
:size="((props.ui?.itemLeadingChipSize || ui.itemLeadingChipSize()) as ChipProps['size'])"
|
||||||
|
inset
|
||||||
|
standalone
|
||||||
|
v-bind="item.chip"
|
||||||
|
:class="ui.itemLeadingChip({ class: props.ui?.itemLeadingChip, active: item.active })"
|
||||||
|
/>
|
||||||
|
</slot>
|
||||||
|
|
||||||
<ComboboxItem
|
<span v-if="item.labelHtml || get(item, props.labelKey as string) || !!slots[item.slot ? `${item.slot}-label` : group.slot ? `${group.slot}-label` : `item-label`]" :class="ui.itemLabel({ class: props.ui?.itemLabel, active: item.active })">
|
||||||
v-for="(item, index) in group.items"
|
<slot :name="item.slot ? `${item.slot}-label` : group.slot ? `${group.slot}-label` : `item-label`" :item="item" :index="index">
|
||||||
:key="`group-${groupIndex}-${index}`"
|
<span v-if="item.prefix" :class="ui.itemLabelPrefix({ class: props.ui?.itemLabelPrefix })">{{ item.prefix }}</span>
|
||||||
:value="omit(item, ['matches' as any, 'group' as any, 'onSelect', 'labelHtml', 'suffixHtml'])"
|
|
||||||
:disabled="item.disabled"
|
<span :class="ui.itemLabelBase({ class: props.ui?.itemLabelBase, active: item.active })" v-html="item.labelHtml || get(item, props.labelKey as string)" />
|
||||||
:class="ui.item({ class: props.ui?.item, active: item.active })"
|
|
||||||
@select="item.onSelect"
|
<span :class="ui.itemLabelSuffix({ class: props.ui?.itemLabelSuffix, active: item.active })" v-html="item.suffixHtml || item.suffix" />
|
||||||
>
|
</slot>
|
||||||
<slot :name="item.slot || group.slot || 'item'" :item="item" :index="index">
|
</span>
|
||||||
<slot :name="item.slot ? `${item.slot}-leading` : group.slot ? `${group.slot}-leading` : `item-leading`" :item="item" :index="index">
|
|
||||||
<UIcon v-if="item.loading" :name="loadingIcon || appConfig.ui.icons.loading" :class="ui.itemLeadingIcon({ class: props.ui?.itemLeadingIcon, loading: true })" />
|
<span :class="ui.itemTrailing({ class: props.ui?.itemTrailing })">
|
||||||
<UIcon v-else-if="item.icon" :name="item.icon" :class="ui.itemLeadingIcon({ class: props.ui?.itemLeadingIcon, active: item.active })" />
|
<slot :name="item.slot ? `${item.slot}-trailing` : group.slot ? `${group.slot}-trailing` : `item-trailing`" :item="item" :index="index">
|
||||||
<UAvatar v-else-if="item.avatar" :size="((props.ui?.itemLeadingAvatarSize || ui.itemLeadingAvatarSize()) as AvatarProps['size'])" v-bind="item.avatar" :class="ui.itemLeadingAvatar({ class: props.ui?.itemLeadingAvatar, active: item.active })" />
|
<span v-if="item.kbds?.length" :class="ui.itemTrailingKbds({ class: props.ui?.itemTrailingKbds })">
|
||||||
<UChip
|
<UKbd v-for="(kbd, kbdIndex) in item.kbds" :key="kbdIndex" :size="((props.ui?.itemTrailingKbdsSize || ui.itemTrailingKbdsSize()) as KbdProps['size'])" v-bind="typeof kbd === 'string' ? { value: kbd } : kbd" />
|
||||||
v-else-if="item.chip"
|
</span>
|
||||||
:size="((props.ui?.itemLeadingChipSize || ui.itemLeadingChipSize()) as ChipProps['size'])"
|
<UIcon v-else-if="group.highlightedIcon" :name="group.highlightedIcon" :class="ui.itemTrailingHighlightedIcon({ class: props.ui?.itemTrailingHighlightedIcon })" />
|
||||||
inset
|
|
||||||
standalone
|
|
||||||
v-bind="item.chip"
|
|
||||||
:class="ui.itemLeadingChip({ class: props.ui?.itemLeadingChip, active: item.active })"
|
|
||||||
/>
|
|
||||||
</slot>
|
</slot>
|
||||||
|
|
||||||
<span v-if="item.labelHtml || get(item, props.labelKey as string) || !!slots[item.slot ? `${item.slot}-label` : group.slot ? `${group.slot}-label` : `item-label`]" :class="ui.itemLabel({ class: props.ui?.itemLabel, active: item.active })">
|
<ListboxItemIndicator as-child>
|
||||||
<slot :name="item.slot ? `${item.slot}-label` : group.slot ? `${group.slot}-label` : `item-label`" :item="item" :index="index">
|
<UIcon :name="selectedIcon || appConfig.ui.icons.check" :class="ui.itemTrailingIcon({ class: props.ui?.itemTrailingIcon })" />
|
||||||
<span v-if="item.prefix" :class="ui.itemLabelPrefix({ class: props.ui?.itemLabelPrefix })">{{ item.prefix }}</span>
|
</ListboxItemIndicator>
|
||||||
|
</span>
|
||||||
|
</slot>
|
||||||
|
</ListboxItem>
|
||||||
|
</ListboxGroup>
|
||||||
|
</div>
|
||||||
|
|
||||||
<span :class="ui.itemLabelBase({ class: props.ui?.itemLabelBase, active: item.active })" v-html="item.labelHtml || get(item, props.labelKey as string)" />
|
<div v-else :class="ui.empty({ class: props.ui?.empty })">
|
||||||
|
<slot name="empty" :search-term="searchTerm">
|
||||||
<span :class="ui.itemLabelSuffix({ class: props.ui?.itemLabelSuffix, active: item.active })" v-html="item.suffixHtml || item.suffix" />
|
{{ searchTerm ? t('commandPalette.noMatch', { searchTerm }) : t('commandPalette.noData') }}
|
||||||
</slot>
|
</slot>
|
||||||
</span>
|
</div>
|
||||||
|
</ListboxContent>
|
||||||
<span :class="ui.itemTrailing({ class: props.ui?.itemTrailing })">
|
</ListboxRoot>
|
||||||
<slot :name="item.slot ? `${item.slot}-trailing` : group.slot ? `${group.slot}-trailing` : `item-trailing`" :item="item" :index="index">
|
|
||||||
<span v-if="item.kbds?.length" :class="ui.itemTrailingKbds({ class: props.ui?.itemTrailingKbds })">
|
|
||||||
<UKbd v-for="(kbd, kbdIndex) in item.kbds" :key="kbdIndex" :size="((props.ui?.itemTrailingKbdsSize || ui.itemTrailingKbdsSize()) as KbdProps['size'])" v-bind="typeof kbd === 'string' ? { value: kbd } : kbd" />
|
|
||||||
</span>
|
|
||||||
<UIcon v-else-if="group.highlightedIcon" :name="group.highlightedIcon" :class="ui.itemTrailingHighlightedIcon({ class: props.ui?.itemTrailingHighlightedIcon })" />
|
|
||||||
</slot>
|
|
||||||
|
|
||||||
<ComboboxItemIndicator as-child>
|
|
||||||
<UIcon :name="selectedIcon || appConfig.ui.icons.check" :class="ui.itemTrailingIcon({ class: props.ui?.itemTrailingIcon })" />
|
|
||||||
</ComboboxItemIndicator>
|
|
||||||
</span>
|
|
||||||
</slot>
|
|
||||||
</ComboboxItem>
|
|
||||||
</ComboboxGroup>
|
|
||||||
</ComboboxViewport>
|
|
||||||
</ComboboxContent>
|
|
||||||
</ComboboxPortal>
|
|
||||||
</ComboboxRoot>
|
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ extendDevtoolsMeta({ example: 'ContainerExample' })
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Primitive } from 'radix-vue'
|
import { Primitive } from 'reka-ui'
|
||||||
|
|
||||||
const props = defineProps<ContainerProps>()
|
const props = defineProps<ContainerProps>()
|
||||||
defineSlots<ContainerSlots>()
|
defineSlots<ContainerSlots>()
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { tv, type VariantProps } from 'tailwind-variants'
|
import { tv, type VariantProps } from 'tailwind-variants'
|
||||||
import type { ContextMenuRootProps, ContextMenuRootEmits, ContextMenuContentProps } from 'radix-vue'
|
import type { ContextMenuRootProps, ContextMenuRootEmits, ContextMenuContentProps } from 'reka-ui'
|
||||||
import type { AppConfig } from '@nuxt/schema'
|
import type { AppConfig } from '@nuxt/schema'
|
||||||
import _appConfig from '#build/app.config'
|
import _appConfig from '#build/app.config'
|
||||||
import theme from '#build/ui/context-menu'
|
import theme from '#build/ui/context-menu'
|
||||||
@@ -143,7 +143,7 @@ extendDevtoolsMeta({
|
|||||||
|
|
||||||
<script setup lang="ts" generic="T extends ContextMenuItem">
|
<script setup lang="ts" generic="T extends ContextMenuItem">
|
||||||
import { computed, toRef } from 'vue'
|
import { computed, toRef } from 'vue'
|
||||||
import { ContextMenuRoot, ContextMenuTrigger, useForwardPropsEmits } from 'radix-vue'
|
import { ContextMenuRoot, ContextMenuTrigger, useForwardPropsEmits } from 'reka-ui'
|
||||||
import { reactivePick } from '@vueuse/core'
|
import { reactivePick } from '@vueuse/core'
|
||||||
import { omit } from '../utils'
|
import { omit } from '../utils'
|
||||||
import UContextMenuContent from './ContextMenuContent.vue'
|
import UContextMenuContent from './ContextMenuContent.vue'
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { tv } from 'tailwind-variants'
|
import { tv } from 'tailwind-variants'
|
||||||
import type { ContextMenuContentProps as RadixContextMenuContentProps, ContextMenuContentEmits as RadixContextMenuContentEmits } from 'radix-vue'
|
import type { ContextMenuContentProps as RekaContextMenuContentProps, ContextMenuContentEmits as RekaContextMenuContentEmits } from 'reka-ui'
|
||||||
import theme from '#build/ui/context-menu'
|
import theme from '#build/ui/context-menu'
|
||||||
import type { KbdProps, AvatarProps, ContextMenuItem, ContextMenuSlots } from '../types'
|
import type { KbdProps, AvatarProps, ContextMenuItem, ContextMenuSlots } from '../types'
|
||||||
|
|
||||||
const _contextMenu = tv(theme)()
|
const _contextMenu = tv(theme)()
|
||||||
|
|
||||||
interface ContextMenuContentProps<T> extends Omit<RadixContextMenuContentProps, 'as' | 'asChild' | 'forceMount'> {
|
interface ContextMenuContentProps<T> extends Omit<RekaContextMenuContentProps, 'as' | 'asChild' | 'forceMount'> {
|
||||||
items?: T[] | T[][]
|
items?: T[] | T[][]
|
||||||
portal?: boolean
|
portal?: boolean
|
||||||
sub?: boolean
|
sub?: boolean
|
||||||
@@ -18,13 +18,13 @@ interface ContextMenuContentProps<T> extends Omit<RadixContextMenuContentProps,
|
|||||||
uiOverride?: any
|
uiOverride?: any
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ContextMenuContentEmits extends RadixContextMenuContentEmits {}
|
interface ContextMenuContentEmits extends RekaContextMenuContentEmits {}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script setup lang="ts" generic="T extends ContextMenuItem">
|
<script setup lang="ts" generic="T extends ContextMenuItem">
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { ContextMenu } from 'radix-vue/namespaced'
|
import { ContextMenu } from 'reka-ui/namespaced'
|
||||||
import { useForwardPropsEmits } from 'radix-vue'
|
import { useForwardPropsEmits } from 'reka-ui'
|
||||||
import { reactiveOmit, createReusableTemplate } from '@vueuse/core'
|
import { reactiveOmit, createReusableTemplate } from '@vueuse/core'
|
||||||
import { useAppConfig } from '#imports'
|
import { useAppConfig } from '#imports'
|
||||||
import { omit, get } from '../utils'
|
import { omit, get } from '../utils'
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { tv } from 'tailwind-variants'
|
import { tv } from 'tailwind-variants'
|
||||||
import type { DrawerRootProps, DrawerRootEmits } from 'vaul-vue'
|
import type { DrawerRootProps, DrawerRootEmits } from 'vaul-vue'
|
||||||
import type { DialogContentProps } from 'radix-vue'
|
import type { DialogContentProps } from 'reka-ui'
|
||||||
import type { AppConfig } from '@nuxt/schema'
|
import type { AppConfig } from '@nuxt/schema'
|
||||||
import _appConfig from '#build/app.config'
|
import _appConfig from '#build/app.config'
|
||||||
import theme from '#build/ui/drawer'
|
import theme from '#build/ui/drawer'
|
||||||
@@ -58,7 +58,7 @@ extendDevtoolsMeta({ example: 'DrawerExample' })
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, toRef } from 'vue'
|
import { computed, toRef } from 'vue'
|
||||||
import { useForwardPropsEmits } from 'radix-vue'
|
import { useForwardPropsEmits } from 'reka-ui'
|
||||||
import { DrawerRoot, DrawerTrigger, DrawerPortal, DrawerOverlay, DrawerContent, DrawerTitle, DrawerDescription } from 'vaul-vue'
|
import { DrawerRoot, DrawerTrigger, DrawerPortal, DrawerOverlay, DrawerContent, DrawerTitle, DrawerDescription } from 'vaul-vue'
|
||||||
import { reactivePick } from '@vueuse/core'
|
import { reactivePick } from '@vueuse/core'
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { tv, type VariantProps } from 'tailwind-variants'
|
import { tv, type VariantProps } from 'tailwind-variants'
|
||||||
import type { DropdownMenuRootProps, DropdownMenuRootEmits, DropdownMenuContentProps, DropdownMenuArrowProps } from 'radix-vue'
|
import type { DropdownMenuRootProps, DropdownMenuRootEmits, DropdownMenuContentProps, DropdownMenuArrowProps } from 'reka-ui'
|
||||||
import type { AppConfig } from '@nuxt/schema'
|
import type { AppConfig } from '@nuxt/schema'
|
||||||
import _appConfig from '#build/app.config'
|
import _appConfig from '#build/app.config'
|
||||||
import theme from '#build/ui/dropdown-menu'
|
import theme from '#build/ui/dropdown-menu'
|
||||||
@@ -140,7 +140,7 @@ extendDevtoolsMeta({
|
|||||||
<script setup lang="ts" generic="T extends DropdownMenuItem">
|
<script setup lang="ts" generic="T extends DropdownMenuItem">
|
||||||
import { computed, toRef } from 'vue'
|
import { computed, toRef } from 'vue'
|
||||||
import { defu } from 'defu'
|
import { defu } from 'defu'
|
||||||
import { DropdownMenuRoot, DropdownMenuTrigger, DropdownMenuArrow, useForwardPropsEmits } from 'radix-vue'
|
import { DropdownMenuRoot, DropdownMenuTrigger, DropdownMenuArrow, useForwardPropsEmits } from 'reka-ui'
|
||||||
import { reactivePick } from '@vueuse/core'
|
import { reactivePick } from '@vueuse/core'
|
||||||
import { omit } from '../utils'
|
import { omit } from '../utils'
|
||||||
import UDropdownMenuContent from './DropdownMenuContent.vue'
|
import UDropdownMenuContent from './DropdownMenuContent.vue'
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
<!-- eslint-disable vue/block-tag-newline -->
|
<!-- eslint-disable vue/block-tag-newline -->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { tv } from 'tailwind-variants'
|
import { tv } from 'tailwind-variants'
|
||||||
import type { DropdownMenuContentProps as RadixDropdownMenuContentProps, DropdownMenuContentEmits as RadixDropdownMenuContentEmits } from 'radix-vue'
|
import type { DropdownMenuContentProps as RekaDropdownMenuContentProps, DropdownMenuContentEmits as RekaDropdownMenuContentEmits } from 'reka-ui'
|
||||||
import theme from '#build/ui/dropdown-menu'
|
import theme from '#build/ui/dropdown-menu'
|
||||||
import type { KbdProps, AvatarProps, DropdownMenuItem, DropdownMenuSlots } from '../types'
|
import type { KbdProps, AvatarProps, DropdownMenuItem, DropdownMenuSlots } from '../types'
|
||||||
|
|
||||||
const _dropdownMenu = tv(theme)()
|
const _dropdownMenu = tv(theme)()
|
||||||
|
|
||||||
interface DropdownMenuContentProps<T> extends Omit<RadixDropdownMenuContentProps, 'as' | 'asChild' | 'forceMount'> {
|
interface DropdownMenuContentProps<T> extends Omit<RekaDropdownMenuContentProps, 'as' | 'asChild' | 'forceMount'> {
|
||||||
items?: T[] | T[][]
|
items?: T[] | T[][]
|
||||||
portal?: boolean
|
portal?: boolean
|
||||||
sub?: boolean
|
sub?: boolean
|
||||||
@@ -19,7 +19,7 @@ interface DropdownMenuContentProps<T> extends Omit<RadixDropdownMenuContentProps
|
|||||||
uiOverride?: any
|
uiOverride?: any
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DropdownMenuContentEmits extends RadixDropdownMenuContentEmits {}
|
interface DropdownMenuContentEmits extends RekaDropdownMenuContentEmits {}
|
||||||
|
|
||||||
type DropdownMenuContentSlots<T extends { slot?: string }> = Omit<DropdownMenuSlots<T>, 'default'> & {
|
type DropdownMenuContentSlots<T extends { slot?: string }> = Omit<DropdownMenuSlots<T>, 'default'> & {
|
||||||
default(props?: {}): any
|
default(props?: {}): any
|
||||||
@@ -29,8 +29,8 @@ type DropdownMenuContentSlots<T extends { slot?: string }> = Omit<DropdownMenuSl
|
|||||||
|
|
||||||
<script setup lang="ts" generic="T extends DropdownMenuItem">
|
<script setup lang="ts" generic="T extends DropdownMenuItem">
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { DropdownMenu } from 'radix-vue/namespaced'
|
import { DropdownMenu } from 'reka-ui/namespaced'
|
||||||
import { useForwardPropsEmits } from 'radix-vue'
|
import { useForwardPropsEmits } from 'reka-ui'
|
||||||
import { reactiveOmit, createReusableTemplate } from '@vueuse/core'
|
import { reactiveOmit, createReusableTemplate } from '@vueuse/core'
|
||||||
import { useAppConfig } from '#imports'
|
import { useAppConfig } from '#imports'
|
||||||
import { omit, get } from '../utils'
|
import { omit, get } from '../utils'
|
||||||
|
|||||||
@@ -12,6 +12,11 @@ const formField = tv({ extend: tv(theme), ...(appConfig.ui?.formField || {}) })
|
|||||||
type FormFieldVariants = VariantProps<typeof formField>
|
type FormFieldVariants = VariantProps<typeof formField>
|
||||||
|
|
||||||
export interface FormFieldProps {
|
export interface FormFieldProps {
|
||||||
|
/**
|
||||||
|
* The element or component this component should render as.
|
||||||
|
* @defaultValue 'div'
|
||||||
|
*/
|
||||||
|
as?: any
|
||||||
/** The name of the FormField. Also used to match form errors. */
|
/** The name of the FormField. Also used to match form errors. */
|
||||||
name?: string
|
name?: string
|
||||||
/** A regular expression to match form error names. */
|
/** A regular expression to match form error names. */
|
||||||
@@ -43,7 +48,7 @@ extendDevtoolsMeta({ example: 'FormFieldExample', defaultProps: { label: 'Label'
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref, inject, provide, type Ref, useId } from 'vue'
|
import { computed, ref, inject, provide, type Ref, useId } from 'vue'
|
||||||
import { Label } from 'radix-vue'
|
import { Primitive, Label } from 'reka-ui'
|
||||||
import { formFieldInjectionKey, inputIdInjectionKey } from '../composables/useFormField'
|
import { formFieldInjectionKey, inputIdInjectionKey } from '../composables/useFormField'
|
||||||
import type { FormError, FormFieldInjectedOptions } from '../types/form'
|
import type { FormError, FormFieldInjectedOptions } from '../types/form'
|
||||||
|
|
||||||
@@ -74,7 +79,7 @@ provide(formFieldInjectionKey, computed(() => ({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div :class="ui.root({ class: [props.class, props.ui?.root] })">
|
<Primitive :as="as" :class="ui.root({ class: [props.class, props.ui?.root] })">
|
||||||
<div :class="ui.wrapper({ class: props.ui?.wrapper })">
|
<div :class="ui.wrapper({ class: props.ui?.wrapper })">
|
||||||
<div v-if="label || !!slots.label" :class="ui.labelWrapper({ class: props.ui?.labelWrapper })">
|
<div v-if="label || !!slots.label" :class="ui.labelWrapper({ class: props.ui?.labelWrapper })">
|
||||||
<Label :for="id" :class="ui.label({ class: props.ui?.label })">
|
<Label :for="id" :class="ui.label({ class: props.ui?.label })">
|
||||||
@@ -110,5 +115,5 @@ provide(formFieldInjectionKey, computed(() => ({
|
|||||||
</slot>
|
</slot>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Primitive>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export interface IconProps {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useForwardProps } from 'radix-vue'
|
import { useForwardProps } from 'reka-ui'
|
||||||
import { reactivePick } from '@vueuse/core'
|
import { reactivePick } from '@vueuse/core'
|
||||||
|
|
||||||
const props = defineProps<IconProps>()
|
const props = defineProps<IconProps>()
|
||||||
|
|||||||
@@ -15,6 +15,11 @@ const input = tv({ extend: tv(theme), ...(appConfig.ui?.input || {}) })
|
|||||||
type InputVariants = VariantProps<typeof input>
|
type InputVariants = VariantProps<typeof input>
|
||||||
|
|
||||||
export interface InputProps extends UseComponentIconsProps {
|
export interface InputProps extends UseComponentIconsProps {
|
||||||
|
/**
|
||||||
|
* The element or component this component should render as.
|
||||||
|
* @defaultValue 'div'
|
||||||
|
*/
|
||||||
|
as?: any
|
||||||
id?: string
|
id?: string
|
||||||
name?: string
|
name?: string
|
||||||
type?: InputHTMLAttributes['type']
|
type?: InputHTMLAttributes['type']
|
||||||
@@ -49,6 +54,7 @@ export interface InputSlots {
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, onMounted } from 'vue'
|
import { ref, computed, onMounted } from 'vue'
|
||||||
|
import { Primitive } from 'reka-ui'
|
||||||
import { useButtonGroup } from '../composables/useButtonGroup'
|
import { useButtonGroup } from '../composables/useButtonGroup'
|
||||||
import { useComponentIcons } from '../composables/useComponentIcons'
|
import { useComponentIcons } from '../composables/useComponentIcons'
|
||||||
import { useFormField } from '../composables/useFormField'
|
import { useFormField } from '../composables/useFormField'
|
||||||
@@ -147,7 +153,7 @@ onMounted(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div :class="ui.root({ class: [props.class, props.ui?.root] })">
|
<Primitive :as="as" :class="ui.root({ class: [props.class, props.ui?.root] })">
|
||||||
<input
|
<input
|
||||||
:id="id"
|
:id="id"
|
||||||
ref="inputRef"
|
ref="inputRef"
|
||||||
@@ -179,5 +185,5 @@ onMounted(() => {
|
|||||||
<UIcon v-if="trailingIconName" :name="trailingIconName" :class="ui.trailingIcon({ class: props.ui?.trailingIcon })" />
|
<UIcon v-if="trailingIconName" :name="trailingIconName" :class="ui.trailingIcon({ class: props.ui?.trailingIcon })" />
|
||||||
</slot>
|
</slot>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</Primitive>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { InputHTMLAttributes } from 'vue'
|
import type { InputHTMLAttributes } from 'vue'
|
||||||
import { tv, type VariantProps } from 'tailwind-variants'
|
import { tv, type VariantProps } from 'tailwind-variants'
|
||||||
import type { ComboboxRootProps, ComboboxRootEmits, ComboboxContentProps, ComboboxArrowProps } from 'radix-vue'
|
import type { ComboboxRootProps, ComboboxRootEmits, ComboboxContentProps, ComboboxArrowProps, AcceptableValue } from 'reka-ui'
|
||||||
import type { AppConfig } from '@nuxt/schema'
|
import type { AppConfig } from '@nuxt/schema'
|
||||||
import _appConfig from '#build/app.config'
|
import _appConfig from '#build/app.config'
|
||||||
import theme from '#build/ui/input-menu'
|
import theme from '#build/ui/input-menu'
|
||||||
import type { UseComponentIconsProps } from '../composables/useComponentIcons'
|
import type { UseComponentIconsProps } from '../composables/useComponentIcons'
|
||||||
import { extendDevtoolsMeta } from '../composables/extendDevtoolsMeta'
|
import { extendDevtoolsMeta } from '../composables/extendDevtoolsMeta'
|
||||||
import type { AvatarProps, ChipProps, InputProps } from '../types'
|
import type { AvatarProps, ChipProps, InputProps } from '../types'
|
||||||
import type { AcceptableValue, ArrayOrWrapped, PartialString, MaybeArrayOfArray, MaybeArrayOfArrayItem, SelectModelValue, SelectModelValueEmits, SelectItemKey } from '../types/utils'
|
import type { PartialString, MaybeArrayOfArray, MaybeArrayOfArrayItem, SelectModelValue, SelectModelValueEmits, SelectItemKey } from '../types/utils'
|
||||||
|
|
||||||
const appConfig = _appConfig as AppConfig & { ui: { inputMenu: Partial<typeof theme> } }
|
const appConfig = _appConfig as AppConfig & { ui: { inputMenu: Partial<typeof theme> } }
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ export interface InputMenuItem {
|
|||||||
|
|
||||||
type InputMenuVariants = VariantProps<typeof inputMenu>
|
type InputMenuVariants = VariantProps<typeof inputMenu>
|
||||||
|
|
||||||
export interface InputMenuProps<T extends MaybeArrayOfArrayItem<I>, I extends MaybeArrayOfArray<InputMenuItem | AcceptableValue> = MaybeArrayOfArray<InputMenuItem | AcceptableValue>, V extends SelectItemKey<T> | undefined = undefined, M extends boolean = false> extends Pick<ComboboxRootProps<T>, 'defaultValue' | 'selectedValue' | 'open' | 'defaultOpen' | 'searchTerm' | 'disabled' | 'name' | 'resetSearchTermOnBlur'>, UseComponentIconsProps {
|
export interface InputMenuProps<T extends MaybeArrayOfArrayItem<I>, I extends MaybeArrayOfArray<InputMenuItem | AcceptableValue | boolean> = MaybeArrayOfArray<InputMenuItem | AcceptableValue | boolean>, V extends SelectItemKey<T> | undefined = undefined, M extends boolean = false> extends Pick<ComboboxRootProps<T>, 'open' | 'defaultOpen' | 'disabled' | 'name' | 'resetSearchTermOnBlur' | 'highlightOnHover'>, UseComponentIconsProps {
|
||||||
/**
|
/**
|
||||||
* The element or component this component should render as.
|
* The element or component this component should render as.
|
||||||
* @defaultValue 'div'
|
* @defaultValue 'div'
|
||||||
@@ -77,13 +77,6 @@ export interface InputMenuProps<T extends MaybeArrayOfArrayItem<I>, I extends Ma
|
|||||||
* @defaultValue true
|
* @defaultValue true
|
||||||
*/
|
*/
|
||||||
portal?: boolean
|
portal?: boolean
|
||||||
/**
|
|
||||||
* Whether to filter items or not, can be an array of fields to filter. Defaults to `[labelKey]`.
|
|
||||||
* When `false`, items will not be filtered which is useful for custom filtering (useAsyncData, useFetch, etc.).
|
|
||||||
* `['label']`{lang="ts-type"}
|
|
||||||
* @defaultValue true
|
|
||||||
*/
|
|
||||||
filter?: boolean | string[]
|
|
||||||
/**
|
/**
|
||||||
* When `items` is an array of objects, select the field to use as the value instead of the object itself.
|
* When `items` is an array of objects, select the field to use as the value instead of the object itself.
|
||||||
* @defaultValue undefined
|
* @defaultValue undefined
|
||||||
@@ -95,33 +88,45 @@ export interface InputMenuProps<T extends MaybeArrayOfArrayItem<I>, I extends Ma
|
|||||||
*/
|
*/
|
||||||
labelKey?: V
|
labelKey?: V
|
||||||
items?: I
|
items?: I
|
||||||
|
/** The value of the InputMenu when initially rendered. Use when you do not need to control the state of the InputMenu. */
|
||||||
|
defaultValue?: SelectModelValue<T, V, M>
|
||||||
|
/** The controlled value of the InputMenu. Can be binded-with with `v-model`. */
|
||||||
|
modelValue?: SelectModelValue<T, V, M>
|
||||||
|
/** Whether multiple options can be selected or not. */
|
||||||
|
multiple?: M & boolean
|
||||||
/** Highlight the ring color like a focus state. */
|
/** Highlight the ring color like a focus state. */
|
||||||
highlight?: boolean
|
highlight?: boolean
|
||||||
/**
|
/**
|
||||||
* Determines if custom user input that does not exist in options can be added.
|
* Determines if custom user input that does not exist in options can be added.
|
||||||
* @defaultValue false
|
* @defaultValue false
|
||||||
*/
|
*/
|
||||||
createItem?: boolean | 'always' | { placement?: 'top' | 'bottom', when?: 'empty' | 'always' }
|
createItem?: boolean | 'always' | { position?: 'top' | 'bottom', when?: 'empty' | 'always' }
|
||||||
|
/**
|
||||||
|
* Fields to filter items by.
|
||||||
|
* @defaultValue [labelKey]
|
||||||
|
*/
|
||||||
|
filterFields?: string[]
|
||||||
|
/**
|
||||||
|
* When `true`, disable the default filters, useful for custom filtering (useAsyncData, useFetch, etc.).
|
||||||
|
* @defaultValue false
|
||||||
|
*/
|
||||||
|
ignoreFilter?: boolean
|
||||||
class?: any
|
class?: any
|
||||||
ui?: PartialString<typeof inputMenu.slots>
|
ui?: PartialString<typeof inputMenu.slots>
|
||||||
/** The controlled value of the Combobox. Can be binded-with with `v-model`. */
|
|
||||||
modelValue?: SelectModelValue<T, V, M>
|
|
||||||
/** Whether multiple options can be selected or not. */
|
|
||||||
multiple?: M & boolean
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type InputMenuEmits<T, V, M extends boolean> = Omit<ComboboxRootEmits<T>, 'update:modelValue'> & {
|
export type InputMenuEmits<T, V, M extends boolean> = Omit<ComboboxRootEmits<T>, 'update:modelValue'> & {
|
||||||
change: [payload: Event]
|
change: [payload: Event]
|
||||||
blur: [payload: FocusEvent]
|
blur: [payload: FocusEvent]
|
||||||
focus: [payload: FocusEvent]
|
focus: [payload: FocusEvent]
|
||||||
create: [payload: Event, item: T]
|
create: [item: string]
|
||||||
} & SelectModelValueEmits<T, V, M>
|
} & SelectModelValueEmits<T, V, M>
|
||||||
|
|
||||||
type SlotProps<T> = (props: { item: T, index: number }) => any
|
type SlotProps<T> = (props: { item: T, index: number }) => any
|
||||||
|
|
||||||
export interface InputMenuSlots<T> {
|
export interface InputMenuSlots<T, M extends boolean> {
|
||||||
'leading'(props: { modelValue: T, open: boolean, ui: any }): any
|
'leading'(props: { modelValue?: M extends true ? T[] : T, open: boolean, ui: any }): any
|
||||||
'trailing'(props: { modelValue: T, open: boolean, ui: any }): any
|
'trailing'(props: { modelValue?: M extends true ? T[] : T, open: boolean, ui: any }): any
|
||||||
'empty'(props: { searchTerm?: string }): any
|
'empty'(props: { searchTerm?: string }): any
|
||||||
'item': SlotProps<T>
|
'item': SlotProps<T>
|
||||||
'item-leading': SlotProps<T>
|
'item-leading': SlotProps<T>
|
||||||
@@ -129,24 +134,23 @@ export interface InputMenuSlots<T> {
|
|||||||
'item-trailing': SlotProps<T>
|
'item-trailing': SlotProps<T>
|
||||||
'tags-item-text': SlotProps<T>
|
'tags-item-text': SlotProps<T>
|
||||||
'tags-item-delete': SlotProps<T>
|
'tags-item-delete': SlotProps<T>
|
||||||
'create-item-label'(props: { item: T }): any
|
'create-item-label'(props: { item: string }): any
|
||||||
}
|
}
|
||||||
|
|
||||||
extendDevtoolsMeta({ defaultProps: { items: ['Option 1', 'Option 2', 'Option 3'] } })
|
extendDevtoolsMeta({ defaultProps: { items: ['Option 1', 'Option 2', 'Option 3'] } })
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script setup lang="ts" generic="T extends MaybeArrayOfArrayItem<I>, I extends MaybeArrayOfArray<InputMenuItem | AcceptableValue> = MaybeArrayOfArray<InputMenuItem | AcceptableValue>, V extends SelectItemKey<T> | undefined = undefined, M extends boolean = false">
|
<script setup lang="ts" generic="T extends MaybeArrayOfArrayItem<I>, I extends MaybeArrayOfArray<InputMenuItem | AcceptableValue | boolean> = MaybeArrayOfArray<InputMenuItem | AcceptableValue | boolean>, V extends SelectItemKey<T> | undefined = undefined, M extends boolean = false">
|
||||||
import { computed, ref, toRef, onMounted, toRaw } from 'vue'
|
import { computed, ref, toRef, onMounted, toRaw } from 'vue'
|
||||||
import { ComboboxRoot, ComboboxArrow, ComboboxAnchor, ComboboxInput, ComboboxTrigger, ComboboxPortal, ComboboxContent, ComboboxViewport, ComboboxEmpty, ComboboxGroup, ComboboxLabel, ComboboxSeparator, ComboboxItem, ComboboxItemIndicator, TagsInputRoot, TagsInputItem, TagsInputItemText, TagsInputItemDelete, TagsInputInput, useForwardPropsEmits } from 'radix-vue'
|
import { ComboboxRoot, ComboboxArrow, ComboboxAnchor, ComboboxInput, ComboboxTrigger, ComboboxPortal, ComboboxContent, ComboboxViewport, 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'
|
|
||||||
import { reactivePick, createReusableTemplate } from '@vueuse/core'
|
import { reactivePick, createReusableTemplate } from '@vueuse/core'
|
||||||
import { useAppConfig } from '#imports'
|
import { useAppConfig } from '#imports'
|
||||||
import { useButtonGroup } from '../composables/useButtonGroup'
|
import { useButtonGroup } from '../composables/useButtonGroup'
|
||||||
import { useComponentIcons } from '../composables/useComponentIcons'
|
import { useComponentIcons } from '../composables/useComponentIcons'
|
||||||
import { useFormField } from '../composables/useFormField'
|
import { useFormField } from '../composables/useFormField'
|
||||||
import { useLocale } from '../composables/useLocale'
|
import { useLocale } from '../composables/useLocale'
|
||||||
import { get, escapeRegExp } from '../utils'
|
import { get, compare } from '../utils'
|
||||||
import UIcon from './Icon.vue'
|
import UIcon from './Icon.vue'
|
||||||
import UAvatar from './Avatar.vue'
|
import UAvatar from './Avatar.vue'
|
||||||
import UChip from './Chip.vue'
|
import UChip from './Chip.vue'
|
||||||
@@ -157,17 +161,18 @@ const props = withDefaults(defineProps<InputMenuProps<T, I, V, M>>(), {
|
|||||||
type: 'text',
|
type: 'text',
|
||||||
autofocusDelay: 0,
|
autofocusDelay: 0,
|
||||||
portal: true,
|
portal: true,
|
||||||
filter: true,
|
|
||||||
labelKey: 'label' as never
|
labelKey: 'label' as never
|
||||||
})
|
})
|
||||||
const emits = defineEmits<InputMenuEmits<T, V, M>>()
|
const emits = defineEmits<InputMenuEmits<T, V, M>>()
|
||||||
const slots = defineSlots<InputMenuSlots<T>>()
|
const slots = defineSlots<InputMenuSlots<T, M>>()
|
||||||
|
|
||||||
const searchTerm = defineModel<string>('searchTerm', { default: '' })
|
const searchTerm = defineModel<string>('searchTerm', { default: '' })
|
||||||
|
|
||||||
const appConfig = useAppConfig()
|
|
||||||
const { t } = useLocale()
|
const { t } = useLocale()
|
||||||
const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'modelValue', 'defaultValue', 'selectedValue', 'open', 'defaultOpen', 'multiple', 'resetSearchTermOnBlur'), emits)
|
const appConfig = useAppConfig()
|
||||||
|
const { contains } = useFilter({ sensitivity: 'base' })
|
||||||
|
|
||||||
|
const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'modelValue', 'defaultValue', 'open', 'defaultOpen', 'multiple', 'resetSearchTermOnBlur', 'highlightOnHover', 'ignoreFilter'), emits)
|
||||||
const contentProps = toRef(() => defu(props.content, { side: 'bottom', sideOffset: 8, collisionPadding: 8, position: 'popper' }) as ComboboxContentProps)
|
const contentProps = toRef(() => defu(props.content, { side: 'bottom', sideOffset: 8, collisionPadding: 8, position: 'popper' }) as ComboboxContentProps)
|
||||||
const arrowProps = toRef(() => props.arrow as ComboboxArrowProps)
|
const arrowProps = toRef(() => props.arrow as ComboboxArrowProps)
|
||||||
|
|
||||||
@@ -175,10 +180,10 @@ const { emitFormBlur, emitFormChange, emitFormInput, size: formGroupSize, color,
|
|||||||
const { orientation, size: buttonGroupSize } = useButtonGroup<InputProps>(props)
|
const { orientation, size: buttonGroupSize } = useButtonGroup<InputProps>(props)
|
||||||
const { isLeading, isTrailing, leadingIconName, trailingIconName } = useComponentIcons(toRef(() => defu(props, { trailingIcon: appConfig.ui.icons.chevronDown })))
|
const { isLeading, isTrailing, leadingIconName, trailingIconName } = useComponentIcons(toRef(() => defu(props, { trailingIcon: appConfig.ui.icons.chevronDown })))
|
||||||
|
|
||||||
const [DefineCreateItemTemplate, ReuseCreateItemTemplate] = createReusableTemplate()
|
|
||||||
|
|
||||||
const inputSize = computed(() => buttonGroupSize.value || formGroupSize.value)
|
const inputSize = computed(() => buttonGroupSize.value || formGroupSize.value)
|
||||||
|
|
||||||
|
const [DefineCreateItemTemplate, ReuseCreateItemTemplate] = createReusableTemplate()
|
||||||
|
|
||||||
const ui = computed(() => inputMenu({
|
const ui = computed(() => inputMenu({
|
||||||
color: color.value,
|
color: color.value,
|
||||||
variant: props.variant,
|
variant: props.variant,
|
||||||
@@ -196,69 +201,49 @@ function displayValue(value: T): string {
|
|||||||
return value && (typeof value === 'object' ? get(value, props.labelKey as string) : value)
|
return value && (typeof value === 'object' ? get(value, props.labelKey as string) : value)
|
||||||
}
|
}
|
||||||
|
|
||||||
const item = items.value.find(item => isEqual(get(item as Record<string, any>, props.valueKey as string), value))
|
const item = items.value.find(item => compare(typeof item === 'object' ? get(item, 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
function filterFunction(
|
|
||||||
inputItems: ArrayOrWrapped<T> = items.value as ArrayOrWrapped<T>,
|
|
||||||
filterSearchTerm: string = searchTerm.value,
|
|
||||||
comparator = (item: any, term: string) => String(item).search(new RegExp(term, 'i')) !== -1
|
|
||||||
): ArrayOrWrapped<T> {
|
|
||||||
if (props.filter === false) {
|
|
||||||
return inputItems
|
|
||||||
}
|
|
||||||
|
|
||||||
const fields = Array.isArray(props.filter) ? props.filter : [props.labelKey]
|
|
||||||
const escapedSearchTerm = escapeRegExp(filterSearchTerm ?? '')
|
|
||||||
|
|
||||||
return inputItems.filter((item) => {
|
|
||||||
if (typeof item !== 'object') {
|
|
||||||
return comparator(item, escapedSearchTerm)
|
|
||||||
}
|
|
||||||
|
|
||||||
return fields.some((field) => {
|
|
||||||
const child = get(item, field as string)
|
|
||||||
|
|
||||||
return child !== null && child !== undefined && comparator(child, escapedSearchTerm)
|
|
||||||
})
|
|
||||||
}) as ArrayOrWrapped<T>
|
|
||||||
}
|
|
||||||
|
|
||||||
const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0]) ? props.items : [props.items]) as InputMenuItem[][] : [])
|
const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0]) ? props.items : [props.items]) as InputMenuItem[][] : [])
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
const items = computed(() => groups.value.flatMap(group => group) as T[])
|
const items = computed(() => groups.value.flatMap(group => group) as T[])
|
||||||
|
|
||||||
const creatable = computed(() => {
|
const filteredGroups = computed(() => {
|
||||||
if (!props.createItem) {
|
if (props.ignoreFilter || !searchTerm.value) {
|
||||||
|
return groups.value
|
||||||
|
}
|
||||||
|
|
||||||
|
const fields = Array.isArray(props.filterFields) ? props.filterFields : [props.labelKey] as string[]
|
||||||
|
|
||||||
|
return groups.value.map(items => items.filter((item) => {
|
||||||
|
if (typeof item !== 'object') {
|
||||||
|
return contains(item, searchTerm.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.type && ['label', 'separator'].includes(item.type)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return fields.some(field => contains(get(item, field), searchTerm.value))
|
||||||
|
})).filter(group => group.filter(item => !item.type || !['label', 'separator'].includes(item.type)).length > 0)
|
||||||
|
})
|
||||||
|
const filteredItems = computed(() => filteredGroups.value.flatMap(group => group) as T[])
|
||||||
|
|
||||||
|
const createItem = computed(() => {
|
||||||
|
if (!props.createItem || !searchTerm.value) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
const isModelValueCustom = props.modelValue && filterFunction((props.multiple && Array.isArray(props.modelValue) ? props.modelValue : [props.modelValue]) as ArrayOrWrapped<T>, searchTerm.value, (item, term) => String(item) === term).length === 1
|
const newItem = props.valueKey ? { [props.valueKey]: searchTerm.value } as T : searchTerm.value
|
||||||
|
|
||||||
if (isModelValueCustom) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
const filteredItems = filterFunction()
|
|
||||||
const newItem = searchTerm.value && {
|
|
||||||
item: props.valueKey ? { [props.valueKey]: searchTerm.value, [props.labelKey ?? 'label']: searchTerm.value } : searchTerm.value,
|
|
||||||
position: ((typeof props.createItem === 'object' && props.createItem.placement) || 'bottom') as 'top' | 'bottom'
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((typeof props.createItem === 'object' && props.createItem.when === 'always') || props.createItem === 'always') {
|
if ((typeof props.createItem === 'object' && props.createItem.when === 'always') || props.createItem === 'always') {
|
||||||
return (filteredItems.length === 1 && filterFunction(filteredItems, searchTerm.value, (item, term) => String(item) === term).length === 1) ? false : newItem
|
return !filteredItems.value.find(item => compare(item, newItem, props.valueKey))
|
||||||
}
|
}
|
||||||
|
|
||||||
return filteredItems.length > 0 ? false : newItem
|
return !filteredItems.value.length
|
||||||
})
|
})
|
||||||
|
const createItemPosition = computed(() => typeof props.createItem === 'object' ? props.createItem.position : 'bottom')
|
||||||
const rootItems = computed(() => [
|
|
||||||
...(creatable.value && creatable.value.position === 'top' ? [creatable.value.item] : []),
|
|
||||||
...filterFunction(),
|
|
||||||
...(creatable.value && creatable.value.position === 'bottom' ? [creatable.value.item] : [])
|
|
||||||
] as ArrayOrWrapped<T>)
|
|
||||||
|
|
||||||
const inputRef = ref<InstanceType<typeof ComboboxInput> | null>(null)
|
const inputRef = ref<InstanceType<typeof ComboboxInput> | null>(null)
|
||||||
|
|
||||||
@@ -310,17 +295,18 @@ defineExpose({
|
|||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- eslint-disable vue/no-template-shadow -->
|
||||||
<template>
|
<template>
|
||||||
<DefineCreateItemTemplate>
|
<DefineCreateItemTemplate>
|
||||||
<ComboboxGroup v-if="creatable" :class="ui.group({ class: props.ui?.group })">
|
<ComboboxGroup :class="ui.group({ class: props.ui?.group })">
|
||||||
<ComboboxItem
|
<ComboboxItem
|
||||||
:class="ui.item({ class: props.ui?.item })"
|
:class="ui.item({ class: props.ui?.item })"
|
||||||
:value="valueKey && typeof creatable.item === 'object' ? get(creatable.item, props.valueKey as string) : creatable.item"
|
:value="searchTerm"
|
||||||
@select="e => emits('create', e, (creatable as any).item as T)"
|
@select.prevent="emits('create', searchTerm)"
|
||||||
>
|
>
|
||||||
<span :class="ui.itemLabel({ class: props.ui?.itemLabel })">
|
<span :class="ui.itemLabel({ class: props.ui?.itemLabel })">
|
||||||
<slot name="create-item-label" :item="(creatable.item as T)">
|
<slot name="create-item-label" :item="searchTerm">
|
||||||
{{ t('inputMenu.create', { label: typeof creatable.item === 'object' ? get(creatable.item, props.labelKey as string) : creatable.item }) }}
|
{{ t('inputMenu.create', { label: searchTerm }) }}
|
||||||
</slot>
|
</slot>
|
||||||
</span>
|
</span>
|
||||||
</ComboboxItem>
|
</ComboboxItem>
|
||||||
@@ -331,13 +317,11 @@ defineExpose({
|
|||||||
:id="id"
|
:id="id"
|
||||||
v-slot="{ modelValue, open }"
|
v-slot="{ modelValue, open }"
|
||||||
v-bind="rootProps"
|
v-bind="rootProps"
|
||||||
v-model:search-term="searchTerm"
|
|
||||||
:name="name"
|
:name="name"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
:display-value="displayValue"
|
|
||||||
:filter-function="() => rootItems"
|
|
||||||
:class="ui.root({ class: [props.class, props.ui?.root] })"
|
:class="ui.root({ class: [props.class, props.ui?.root] })"
|
||||||
:as-child="!!multiple"
|
:as-child="!!multiple"
|
||||||
|
ignore-filter
|
||||||
@update:model-value="onUpdate"
|
@update:model-value="onUpdate"
|
||||||
@update:open="onUpdateOpen"
|
@update:open="onUpdateOpen"
|
||||||
@keydown.enter="$event.preventDefault()"
|
@keydown.enter="$event.preventDefault()"
|
||||||
@@ -345,7 +329,7 @@ defineExpose({
|
|||||||
<ComboboxAnchor :as-child="!multiple" :class="ui.base({ class: props.ui?.base })">
|
<ComboboxAnchor :as-child="!multiple" :class="ui.base({ class: props.ui?.base })">
|
||||||
<TagsInputRoot
|
<TagsInputRoot
|
||||||
v-if="multiple"
|
v-if="multiple"
|
||||||
v-slot="{ modelValue: tags }: { modelValue: AcceptableValue[] }"
|
v-slot="{ modelValue: tags }"
|
||||||
:model-value="(modelValue as string[])"
|
:model-value="(modelValue as string[])"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
delimiter=""
|
delimiter=""
|
||||||
@@ -367,7 +351,7 @@ defineExpose({
|
|||||||
</TagsInputItemDelete>
|
</TagsInputItemDelete>
|
||||||
</TagsInputItem>
|
</TagsInputItem>
|
||||||
|
|
||||||
<ComboboxInput as-child>
|
<ComboboxInput v-model="searchTerm" :display-value="displayValue" as-child>
|
||||||
<TagsInputInput
|
<TagsInputInput
|
||||||
ref="inputRef"
|
ref="inputRef"
|
||||||
v-bind="$attrs"
|
v-bind="$attrs"
|
||||||
@@ -382,24 +366,25 @@ defineExpose({
|
|||||||
<ComboboxInput
|
<ComboboxInput
|
||||||
v-else
|
v-else
|
||||||
ref="inputRef"
|
ref="inputRef"
|
||||||
|
v-model="searchTerm"
|
||||||
|
:display-value="displayValue"
|
||||||
v-bind="$attrs"
|
v-bind="$attrs"
|
||||||
:type="type"
|
:type="type"
|
||||||
:placeholder="placeholder"
|
:placeholder="placeholder"
|
||||||
:required="required"
|
:required="required"
|
||||||
:class="ui.base({ class: props.ui?.base })"
|
|
||||||
@blur="onBlur"
|
@blur="onBlur"
|
||||||
@focus="onFocus"
|
@focus="onFocus"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<span v-if="isLeading || !!avatar || !!slots.leading" :class="ui.leading({ class: props.ui?.leading })">
|
<span v-if="isLeading || !!avatar || !!slots.leading" :class="ui.leading({ class: props.ui?.leading })">
|
||||||
<slot name="leading" :model-value="(modelValue as T)" :open="open" :ui="ui">
|
<slot name="leading" :model-value="(modelValue as M extends true ? T[] : T)" :open="open" :ui="ui">
|
||||||
<UIcon v-if="isLeading && leadingIconName" :name="leadingIconName" :class="ui.leadingIcon({ class: props.ui?.leadingIcon })" />
|
<UIcon v-if="isLeading && leadingIconName" :name="leadingIconName" :class="ui.leadingIcon({ class: props.ui?.leadingIcon })" />
|
||||||
<UAvatar v-else-if="!!avatar" :size="((props.ui?.itemLeadingAvatarSize || ui.itemLeadingAvatarSize()) as AvatarProps['size'])" v-bind="avatar" :class="ui.itemLeadingAvatar({ class: props.ui?.itemLeadingAvatar })" />
|
<UAvatar v-else-if="!!avatar" :size="((props.ui?.itemLeadingAvatarSize || ui.itemLeadingAvatarSize()) as AvatarProps['size'])" v-bind="avatar" :class="ui.itemLeadingAvatar({ class: props.ui?.itemLeadingAvatar })" />
|
||||||
</slot>
|
</slot>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<ComboboxTrigger v-if="isTrailing || !!slots.trailing" :class="ui.trailing({ class: props.ui?.trailing })">
|
<ComboboxTrigger v-if="isTrailing || !!slots.trailing" :class="ui.trailing({ class: props.ui?.trailing })">
|
||||||
<slot name="trailing" :model-value="(modelValue as T)" :open="open" :ui="ui">
|
<slot name="trailing" :model-value="(modelValue as M extends true ? T[] : T)" :open="open" :ui="ui">
|
||||||
<UIcon v-if="trailingIconName" :name="trailingIconName" :class="ui.trailingIcon({ class: props.ui?.trailingIcon })" />
|
<UIcon v-if="trailingIconName" :name="trailingIconName" :class="ui.trailingIcon({ class: props.ui?.trailingIcon })" />
|
||||||
</slot>
|
</slot>
|
||||||
</ComboboxTrigger>
|
</ComboboxTrigger>
|
||||||
@@ -414,9 +399,9 @@ defineExpose({
|
|||||||
</ComboboxEmpty>
|
</ComboboxEmpty>
|
||||||
|
|
||||||
<ComboboxViewport :class="ui.viewport({ class: props.ui?.viewport })">
|
<ComboboxViewport :class="ui.viewport({ class: props.ui?.viewport })">
|
||||||
<ReuseCreateItemTemplate v-if="creatable && creatable.position === 'top'" />
|
<ReuseCreateItemTemplate v-if="createItem && createItemPosition === 'top'" />
|
||||||
|
|
||||||
<ComboboxGroup v-for="(group, groupIndex) in groups" :key="`group-${groupIndex}`" :class="ui.group({ class: props.ui?.group })">
|
<ComboboxGroup v-for="(group, groupIndex) in filteredGroups" :key="`group-${groupIndex}`" :class="ui.group({ class: props.ui?.group })">
|
||||||
<template v-for="(item, index) in group" :key="`group-${groupIndex}-${index}`">
|
<template v-for="(item, index) in group" :key="`group-${groupIndex}-${index}`">
|
||||||
<ComboboxLabel v-if="item?.type === 'label'" :class="ui.label({ class: props.ui?.label })">
|
<ComboboxLabel v-if="item?.type === 'label'" :class="ui.label({ class: props.ui?.label })">
|
||||||
{{ get(item, props.labelKey as string) }}
|
{{ get(item, props.labelKey as string) }}
|
||||||
@@ -463,7 +448,7 @@ defineExpose({
|
|||||||
</template>
|
</template>
|
||||||
</ComboboxGroup>
|
</ComboboxGroup>
|
||||||
|
|
||||||
<ReuseCreateItemTemplate v-if="creatable && creatable.position === 'bottom'" />
|
<ReuseCreateItemTemplate v-if="createItem && createItemPosition === 'bottom'" />
|
||||||
</ComboboxViewport>
|
</ComboboxViewport>
|
||||||
|
|
||||||
<ComboboxArrow v-if="!!arrow" v-bind="arrowProps" :class="ui.arrow({ class: props.ui?.arrow })" />
|
<ComboboxArrow v-if="!!arrow" v-bind="arrowProps" :class="ui.arrow({ class: props.ui?.arrow })" />
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { tv, type VariantProps } from 'tailwind-variants'
|
import { tv, type VariantProps } from 'tailwind-variants'
|
||||||
import type { NumberFieldRootProps } from 'radix-vue'
|
import type { NumberFieldRootProps } from 'reka-ui'
|
||||||
import type { AppConfig } from '@nuxt/schema'
|
import type { AppConfig } from '@nuxt/schema'
|
||||||
import _appConfig from '#build/app.config'
|
import _appConfig from '#build/app.config'
|
||||||
import theme from '#build/ui/input-number'
|
import theme from '#build/ui/input-number'
|
||||||
@@ -75,7 +75,7 @@ export interface InputNumberSlots {
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted, ref, computed } from 'vue'
|
import { onMounted, ref, computed } from 'vue'
|
||||||
import { NumberFieldRoot, NumberFieldInput, NumberFieldDecrement, NumberFieldIncrement, useForwardPropsEmits } from 'radix-vue'
|
import { NumberFieldRoot, NumberFieldInput, NumberFieldDecrement, NumberFieldIncrement, useForwardPropsEmits } from 'reka-ui'
|
||||||
import { reactivePick } from '@vueuse/core'
|
import { reactivePick } from '@vueuse/core'
|
||||||
import { useFormField } from '../composables/useFormField'
|
import { useFormField } from '../composables/useFormField'
|
||||||
import { useLocale } from '../composables/useLocale'
|
import { useLocale } from '../composables/useLocale'
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ extendDevtoolsMeta({ defaultProps: { value: 'K' } })
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Primitive } from 'radix-vue'
|
import { Primitive } from 'reka-ui'
|
||||||
import { useKbd } from '../composables/useKbd'
|
import { useKbd } from '../composables/useKbd'
|
||||||
|
|
||||||
const props = withDefaults(defineProps<KbdProps>(), {
|
const props = withDefaults(defineProps<KbdProps>(), {
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ extendDevtoolsMeta({ example: 'LinkExample' })
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { isEqual, diff } from 'ohash'
|
import { isEqual, diff } from 'ohash'
|
||||||
import { useForwardProps } from 'radix-vue'
|
import { useForwardProps } from 'reka-ui'
|
||||||
import { reactiveOmit } from '@vueuse/core'
|
import { reactiveOmit } from '@vueuse/core'
|
||||||
import { useRoute } from '#imports'
|
import { useRoute } from '#imports'
|
||||||
import ULinkBase from './LinkBase.vue'
|
import ULinkBase from './LinkBase.vue'
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export interface LinkBaseProps {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Primitive } from 'radix-vue'
|
import { Primitive } from 'reka-ui'
|
||||||
|
|
||||||
const props = withDefaults(defineProps<LinkBaseProps>(), {
|
const props = withDefaults(defineProps<LinkBaseProps>(), {
|
||||||
as: 'button',
|
as: 'button',
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { tv } from 'tailwind-variants'
|
import { tv } from 'tailwind-variants'
|
||||||
import type { DialogRootProps, DialogRootEmits, DialogContentProps } from 'radix-vue'
|
import type { DialogRootProps, DialogRootEmits, DialogContentProps } from 'reka-ui'
|
||||||
import type { AppConfig } from '@nuxt/schema'
|
import type { AppConfig } from '@nuxt/schema'
|
||||||
import _appConfig from '#build/app.config'
|
import _appConfig from '#build/app.config'
|
||||||
import theme from '#build/ui/modal'
|
import theme from '#build/ui/modal'
|
||||||
@@ -74,7 +74,7 @@ extendDevtoolsMeta({ example: 'ModalExample' })
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, toRef } from 'vue'
|
import { computed, toRef } from 'vue'
|
||||||
import { DialogRoot, DialogTrigger, DialogPortal, DialogOverlay, DialogContent, DialogTitle, DialogDescription, DialogClose, useForwardPropsEmits } from 'radix-vue'
|
import { DialogRoot, DialogTrigger, DialogPortal, DialogOverlay, DialogContent, DialogTitle, DialogDescription, DialogClose, useForwardPropsEmits } from 'reka-ui'
|
||||||
import { reactivePick } from '@vueuse/core'
|
import { reactivePick } from '@vueuse/core'
|
||||||
import { useAppConfig } from '#imports'
|
import { useAppConfig } from '#imports'
|
||||||
import { useLocale } from '../composables/useLocale'
|
import { useLocale } from '../composables/useLocale'
|
||||||
@@ -90,6 +90,9 @@ const props = withDefaults(defineProps<ModalProps>(), {
|
|||||||
const emits = defineEmits<ModalEmits>()
|
const emits = defineEmits<ModalEmits>()
|
||||||
const slots = defineSlots<ModalSlots>()
|
const slots = defineSlots<ModalSlots>()
|
||||||
|
|
||||||
|
const { t } = useLocale()
|
||||||
|
const appConfig = useAppConfig()
|
||||||
|
|
||||||
const rootProps = useForwardPropsEmits(reactivePick(props, 'open', 'defaultOpen', 'modal'), emits)
|
const rootProps = useForwardPropsEmits(reactivePick(props, 'open', 'defaultOpen', 'modal'), emits)
|
||||||
const contentProps = toRef(() => props.content)
|
const contentProps = toRef(() => props.content)
|
||||||
const contentEvents = computed(() => {
|
const contentEvents = computed(() => {
|
||||||
@@ -110,9 +113,6 @@ const contentEvents = computed(() => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const appConfig = useAppConfig()
|
|
||||||
const { t } = useLocale()
|
|
||||||
|
|
||||||
const ui = computed(() => modal({
|
const ui = computed(() => modal({
|
||||||
transition: props.transition,
|
transition: props.transition,
|
||||||
fullscreen: props.fullscreen
|
fullscreen: props.fullscreen
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { tv, type VariantProps } from 'tailwind-variants'
|
import { tv, type VariantProps } from 'tailwind-variants'
|
||||||
import type { NavigationMenuRootProps, NavigationMenuRootEmits, NavigationMenuContentProps, CollapsibleRootProps } from 'radix-vue'
|
import type { NavigationMenuRootProps, NavigationMenuRootEmits, NavigationMenuContentProps, CollapsibleRootProps } from 'reka-ui'
|
||||||
import type { AppConfig } from '@nuxt/schema'
|
import type { AppConfig } from '@nuxt/schema'
|
||||||
import _appConfig from '#build/app.config'
|
import _appConfig from '#build/app.config'
|
||||||
import theme from '#build/ui/navigation-menu'
|
import theme from '#build/ui/navigation-menu'
|
||||||
@@ -31,7 +31,7 @@ export interface NavigationMenuItem extends Omit<LinkProps, 'raw' | 'custom'>, P
|
|||||||
|
|
||||||
type NavigationMenuVariants = VariantProps<typeof navigationMenu>
|
type NavigationMenuVariants = VariantProps<typeof navigationMenu>
|
||||||
|
|
||||||
export interface NavigationMenuProps<T> extends Pick<NavigationMenuRootProps, 'defaultValue' | 'delayDuration' | 'disableClickTrigger' | 'disableHoverTrigger' | 'modelValue' | 'skipDelayDuration'> {
|
export interface NavigationMenuProps<T> extends Pick<NavigationMenuRootProps, 'modelValue' | 'defaultValue' | 'delayDuration' | 'disableClickTrigger' | 'disableHoverTrigger' | 'skipDelayDuration' | 'disablePointerLeaveClose' | 'unmountOnHide'> {
|
||||||
/**
|
/**
|
||||||
* The element or component this component should render as.
|
* The element or component this component should render as.
|
||||||
* @defaultValue 'div'
|
* @defaultValue 'div'
|
||||||
@@ -122,8 +122,8 @@ extendDevtoolsMeta({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script setup lang="ts" generic="T extends MaybeArrayOfArrayItem<I>, I extends MaybeArrayOfArray<NavigationMenuItem>">
|
<script setup lang="ts" generic="T extends MaybeArrayOfArrayItem<I>, I extends MaybeArrayOfArray<NavigationMenuItem>">
|
||||||
import { computed, reactive, toRef } from 'vue'
|
import { computed, toRef } from 'vue'
|
||||||
import { NavigationMenuRoot, NavigationMenuList, NavigationMenuItem, NavigationMenuTrigger, NavigationMenuContent, NavigationMenuLink, NavigationMenuIndicator, NavigationMenuViewport, useForwardPropsEmits } from 'radix-vue'
|
import { NavigationMenuRoot, NavigationMenuList, NavigationMenuItem, NavigationMenuTrigger, NavigationMenuContent, NavigationMenuLink, NavigationMenuIndicator, NavigationMenuViewport, useForwardPropsEmits } from 'reka-ui'
|
||||||
import { createReusableTemplate } from '@vueuse/core'
|
import { createReusableTemplate } from '@vueuse/core'
|
||||||
import { get } from '../utils'
|
import { get } from '../utils'
|
||||||
import { pickLinkProps } from '../utils/link'
|
import { pickLinkProps } from '../utils/link'
|
||||||
@@ -137,19 +137,24 @@ import UCollapsible from './Collapsible.vue'
|
|||||||
const props = withDefaults(defineProps<NavigationMenuProps<I>>(), {
|
const props = withDefaults(defineProps<NavigationMenuProps<I>>(), {
|
||||||
orientation: 'horizontal',
|
orientation: 'horizontal',
|
||||||
delayDuration: 0,
|
delayDuration: 0,
|
||||||
labelKey: 'label'
|
labelKey: 'label',
|
||||||
|
unmountOnHide: true
|
||||||
})
|
})
|
||||||
const emits = defineEmits<NavigationMenuEmits>()
|
const emits = defineEmits<NavigationMenuEmits>()
|
||||||
const slots = defineSlots<NavigationMenuSlots<T>>()
|
const slots = defineSlots<NavigationMenuSlots<T>>()
|
||||||
|
|
||||||
const rootProps = useForwardPropsEmits(reactive({
|
const rootProps = useForwardPropsEmits(computed(() => ({
|
||||||
as: props.as,
|
as: props.as,
|
||||||
modelValue: props.modelValue,
|
modelValue: props.modelValue,
|
||||||
defaultValue: props.defaultValue,
|
defaultValue: props.defaultValue,
|
||||||
delayDuration: props.delayDuration,
|
delayDuration: props.delayDuration,
|
||||||
skipDelayDuration: props.skipDelayDuration,
|
skipDelayDuration: props.skipDelayDuration,
|
||||||
orientation: props.orientation
|
orientation: props.orientation,
|
||||||
}), emits)
|
disableClickTrigger: props.disableClickTrigger,
|
||||||
|
disableHoverTrigger: props.disableHoverTrigger,
|
||||||
|
disablePointerLeaveClose: props.disablePointerLeaveClose,
|
||||||
|
unmountOnHide: props.unmountOnHide
|
||||||
|
})), emits)
|
||||||
|
|
||||||
const contentProps = toRef(() => props.content)
|
const contentProps = toRef(() => props.content)
|
||||||
|
|
||||||
@@ -199,7 +204,7 @@ const lists = computed(() => props.items?.length ? (Array.isArray(props.items[0]
|
|||||||
</slot>
|
</slot>
|
||||||
</DefineItemTemplate>
|
</DefineItemTemplate>
|
||||||
|
|
||||||
<NavigationMenuRoot v-bind="rootProps" :data-orientation="orientation" :class="ui.root({ class: [props.class, props.ui?.root] })">
|
<NavigationMenuRoot v-bind="rootProps" :class="ui.root({ class: [props.class, props.ui?.root] })">
|
||||||
<template v-for="(list, listIndex) in lists" :key="`list-${listIndex}`">
|
<template v-for="(list, listIndex) in lists" :key="`list-${listIndex}`">
|
||||||
<NavigationMenuList :class="ui.list({ class: props.ui?.list })">
|
<NavigationMenuList :class="ui.list({ class: props.ui?.list })">
|
||||||
<component
|
<component
|
||||||
@@ -209,6 +214,7 @@ const lists = computed(() => props.items?.length ? (Array.isArray(props.items[0]
|
|||||||
as="li"
|
as="li"
|
||||||
:value="item.value || String(index)"
|
:value="item.value || String(index)"
|
||||||
:default-open="item.defaultOpen"
|
:default-open="item.defaultOpen"
|
||||||
|
:unmount-on-hide="(item.children?.length && orientation === 'vertical') ? unmountOnHide : undefined"
|
||||||
:open="item.open"
|
:open="item.open"
|
||||||
:class="ui.item({ class: props.ui?.item })"
|
:class="ui.item({ class: props.ui?.item })"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { tv } from 'tailwind-variants'
|
import { tv } from 'tailwind-variants'
|
||||||
import type { PaginationRootProps, PaginationRootEmits } from 'radix-vue'
|
import type { PaginationRootProps, PaginationRootEmits } from 'reka-ui'
|
||||||
import type { AppConfig } from '@nuxt/schema'
|
import type { AppConfig } from '@nuxt/schema'
|
||||||
import type { RouteLocationRaw } from '#vue-router'
|
import type { RouteLocationRaw } from '#vue-router'
|
||||||
import _appConfig from '#build/app.config'
|
import _appConfig from '#build/app.config'
|
||||||
@@ -12,7 +12,7 @@ const appConfig = _appConfig as AppConfig & { ui: { pagination: Partial<typeof t
|
|||||||
|
|
||||||
const pagination = tv({ extend: tv(theme), ...(appConfig.ui?.pagination || {}) })
|
const pagination = tv({ extend: tv(theme), ...(appConfig.ui?.pagination || {}) })
|
||||||
|
|
||||||
export interface PaginationProps extends Pick<PaginationRootProps, 'defaultPage' | 'disabled' | 'itemsPerPage' | 'page' | 'showEdges' | 'siblingCount' | 'total'> {
|
export interface PaginationProps extends Partial<Pick<PaginationRootProps, 'defaultPage' | 'disabled' | 'itemsPerPage' | 'page' | 'showEdges' | 'siblingCount' | 'total'>> {
|
||||||
/**
|
/**
|
||||||
* The element or component this component should render as.
|
* The element or component this component should render as.
|
||||||
* @defaultValue 'div'
|
* @defaultValue 'div'
|
||||||
@@ -104,7 +104,7 @@ extendDevtoolsMeta({ defaultProps: { total: 50 } })
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { PaginationRoot, PaginationList, PaginationListItem, PaginationFirst, PaginationPrev, PaginationEllipsis, PaginationNext, PaginationLast, useForwardPropsEmits } from 'radix-vue'
|
import { PaginationRoot, PaginationList, PaginationListItem, PaginationFirst, PaginationPrev, PaginationEllipsis, PaginationNext, PaginationLast, useForwardPropsEmits } from 'reka-ui'
|
||||||
import { reactivePick } from '@vueuse/core'
|
import { reactivePick } from '@vueuse/core'
|
||||||
import { useAppConfig } from '#imports'
|
import { useAppConfig } from '#imports'
|
||||||
import { useLocale } from '../composables/useLocale'
|
import { useLocale } from '../composables/useLocale'
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user