mirror of
https://github.com/ArthurDanjou/ui.git
synced 2026-01-14 12:14:41 +01:00
feat(module)!: migrate to reka-ui (#2448)
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
[![License][license-src]][license-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]
|
||||
> 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/fonts](https://github.com/nuxt/fonts)
|
||||
- [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)
|
||||
- [vueuse/vueuse](https://github.com/vueuse/vueuse)
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ export interface ${upperName}Slots {
|
||||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Primitive } from 'radix-vue'
|
||||
import { Primitive } from 'reka-ui'
|
||||
|
||||
const props = withDefaults(defineProps<${upperName}Props>(), { as: 'div' })
|
||||
defineSlots<${upperName}Slots>()
|
||||
@@ -72,7 +72,7 @@ const ui = ${camelName}()
|
||||
: `
|
||||
<script lang="ts">
|
||||
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 _appConfig from '#build/app.config'
|
||||
import theme from '#build/${path}/${prose ? 'prose/' : ''}${content ? 'content/' : ''}${kebabName}'
|
||||
@@ -94,7 +94,7 @@ export interface ${upperName}Slots {}
|
||||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ${upperName}Root, useForwardPropsEmits } from 'radix-vue'
|
||||
import { ${upperName}Root, useForwardPropsEmits } from 'reka-ui'
|
||||
import { reactivePick } from '@vueuse/core'
|
||||
|
||||
const props = defineProps<${upperName}Props>()
|
||||
@@ -149,7 +149,7 @@ import ComponentRender from '../${content ? '../' : ''}component-render'
|
||||
describe('${upperName}', () => {
|
||||
it.each([
|
||||
// Props
|
||||
['with as', { props: { as: 'div' } }],
|
||||
['with as', { props: { as: 'section' } }],
|
||||
['with class', { props: { class: '' } }],
|
||||
['with ui', { props: { ui: {} } }],
|
||||
// Slots
|
||||
@@ -175,8 +175,8 @@ links: ${pro
|
||||
? ''
|
||||
: `
|
||||
- label: ${upperName}
|
||||
icon: i-custom-radix-vue
|
||||
to: https://www.radix-vue.com/components/${kebabName}.html`}
|
||||
icon: i-custom-reka-ui
|
||||
to: https://www.reka-ui.com/components/${kebabName}.html`}
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
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" />
|
||||
|
||||
<template v-if="!route.path.startsWith('/examples')">
|
||||
<Banner />
|
||||
<!-- <Banner /> -->
|
||||
|
||||
<Header :links="links" />
|
||||
</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 }"
|
||||
:modal="false"
|
||||
:items="frameworks"
|
||||
:ui="{ content: 'w-(--radix-dropdown-menu-trigger-width)' }"
|
||||
:ui="{ content: 'w-(--reka-dropdown-menu-trigger-width)' }"
|
||||
>
|
||||
<UButton
|
||||
color="neutral"
|
||||
|
||||
@@ -30,7 +30,7 @@ defineShortcuts({
|
||||
v-slot="{ open }"
|
||||
:modal="false"
|
||||
: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"
|
||||
>
|
||||
<UButton
|
||||
|
||||
@@ -280,7 +280,7 @@ const { data: ast } = await useAsyncData(`component-code-${name}-${hash({ props:
|
||||
container: 'mt-0'
|
||||
}"
|
||||
>
|
||||
<USelectMenu
|
||||
<USelect
|
||||
v-if="option.items?.length"
|
||||
:model-value="getComponentProp(option.name)"
|
||||
:items="option.items"
|
||||
@@ -288,7 +288,6 @@ const { data: ast } = await useAsyncData(`component-code-${name}-${hash({ props:
|
||||
color="neutral"
|
||||
variant="soft"
|
||||
class="rounded-[var(--ui-radius)] rounded-l-none min-w-12"
|
||||
:search-input="false"
|
||||
:class="[option.name.toLowerCase().endsWith('color') && 'pl-6']"
|
||||
:ui="{ itemLeadingChip: 'size-2' }"
|
||||
@update:model-value="setComponentProp(option.name, $event)"
|
||||
@@ -303,7 +302,7 @@ const { data: ast } = await useAsyncData(`component-code-${name}-${hash({ props:
|
||||
class="size-2"
|
||||
/>
|
||||
</template>
|
||||
</USelectMenu>
|
||||
</USelect>
|
||||
<UInput
|
||||
v-else
|
||||
:type="option.type?.includes('number') ? 'number' : 'text'"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { Slot } from 'radix-vue'
|
||||
import { Slot } from 'reka-ui'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { CalendarDate } from '@internationalized/date'
|
||||
import type { Matcher } from 'radix-vue/date'
|
||||
import type { Matcher } from 'reka-ui/date'
|
||||
|
||||
const modelValue = shallowRef({
|
||||
start: new CalendarDate(2022, 1, 1),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { CalendarDate } from '@internationalized/date'
|
||||
import type { Matcher } from 'radix-vue/date'
|
||||
import type { Matcher } from 'reka-ui/date'
|
||||
|
||||
const modelValue = shallowRef({
|
||||
start: new CalendarDate(2022, 1, 1),
|
||||
|
||||
@@ -14,7 +14,7 @@ const groups = computed(() => [{
|
||||
id: 'users',
|
||||
label: searchTerm.value ? `Users matching “${searchTerm.value}”...` : 'Users',
|
||||
items: users.value || [],
|
||||
filter: false
|
||||
ignoreFilter: true
|
||||
}])
|
||||
</script>
|
||||
|
||||
@@ -11,8 +11,8 @@ const items = [
|
||||
level: 2
|
||||
},
|
||||
{
|
||||
id: '/getting-started#radix-vue-3',
|
||||
label: 'Radix Vue',
|
||||
id: '/getting-started#reka-ui-radix-vue',
|
||||
label: 'Reka UI',
|
||||
level: 3
|
||||
},
|
||||
{
|
||||
|
||||
@@ -72,7 +72,7 @@ const users = [
|
||||
}
|
||||
]
|
||||
|
||||
const searchTerm = ref('')
|
||||
const searchTerm = ref('B')
|
||||
|
||||
function onSelect() {
|
||||
searchTerm.value = ''
|
||||
|
||||
@@ -13,7 +13,7 @@ const groups = computed(() => [{
|
||||
id: 'users',
|
||||
label: searchTerm.value ? `Users matching “${searchTerm.value}”...` : 'Users',
|
||||
items: users.value || [],
|
||||
filter: false
|
||||
ignoreFilter: true
|
||||
}])
|
||||
</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
|
||||
:items="users || []"
|
||||
:loading="status === 'pending'"
|
||||
:filter="['label', 'email']"
|
||||
:filter-fields="['label', 'email']"
|
||||
icon="i-lucide-user"
|
||||
placeholder="Select user"
|
||||
class="w-80"
|
||||
|
||||
@@ -20,7 +20,7 @@ const { data: users, status } = await useFetch('https://jsonplaceholder.typicode
|
||||
v-model:search-term="searchTerm"
|
||||
:items="users || []"
|
||||
:loading="status === 'pending'"
|
||||
:filter="false"
|
||||
ignore-filter
|
||||
icon="i-lucide-user"
|
||||
placeholder="Select user"
|
||||
>
|
||||
@@ -13,7 +13,7 @@ const groups = computed(() => [{
|
||||
id: 'users',
|
||||
label: searchTerm.value ? `Users matching “${searchTerm.value}”...` : 'Users',
|
||||
items: users.value || [],
|
||||
filter: false
|
||||
ignoreFilter: true
|
||||
}])
|
||||
</script>
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ const items = [
|
||||
:items="items"
|
||||
class="justify-center"
|
||||
:ui="{
|
||||
viewport: 'sm:w-[var(--radix-navigation-menu-viewport-width)]',
|
||||
viewport: 'sm:w-[var(--reka-navigation-menu-viewport-width)]',
|
||||
childList: 'sm:w-96',
|
||||
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
|
||||
:items="users || []"
|
||||
:loading="status === 'pending'"
|
||||
:filter="['label', 'email']"
|
||||
:filter-fields="['label', 'email']"
|
||||
icon="i-lucide-user"
|
||||
placeholder="Select user"
|
||||
class="w-80"
|
||||
|
||||
@@ -20,7 +20,7 @@ const { data: users, status } = await useFetch('https://jsonplaceholder.typicode
|
||||
v-model:search-term="searchTerm"
|
||||
:items="users || []"
|
||||
:loading="status === 'pending'"
|
||||
:filter="false"
|
||||
ignore-filter
|
||||
icon="i-lucide-user"
|
||||
placeholder="Select user"
|
||||
class="w-48"
|
||||
@@ -26,7 +26,7 @@ function getUserAvatar(value: string) {
|
||||
<template #leading="{ modelValue, ui }">
|
||||
<UAvatar
|
||||
v-if="modelValue"
|
||||
v-bind="getUserAvatar(modelValue)"
|
||||
v-bind="getUserAvatar(modelValue as string)"
|
||||
:size="ui.leadingAvatarSize()"
|
||||
:class="ui.leadingAvatar()"
|
||||
/>
|
||||
|
||||
@@ -34,7 +34,7 @@ function getChip(value: string) {
|
||||
<template #leading="{ modelValue, ui }">
|
||||
<UChip
|
||||
v-if="modelValue"
|
||||
v-bind="getChip(modelValue)"
|
||||
v-bind="getChip(modelValue as string)"
|
||||
inset
|
||||
standalone
|
||||
:size="ui.itemLeadingChipSize()"
|
||||
|
||||
@@ -143,14 +143,13 @@ const data = ref<Payment[]>([{
|
||||
const columns: TableColumn<Payment>[] = [{
|
||||
id: 'select',
|
||||
header: ({ table }) => h(UCheckbox, {
|
||||
'modelValue': table.getIsAllPageRowsSelected(),
|
||||
'indeterminate': table.getIsSomePageRowsSelected(),
|
||||
'onUpdate:modelValue': (value: boolean) => table.toggleAllPageRowsSelected(!!value),
|
||||
'modelValue': table.getIsSomePageRowsSelected() ? 'indeterminate' : table.getIsAllPageRowsSelected(),
|
||||
'onUpdate:modelValue': (value: boolean | 'indeterminate') => table.toggleAllPageRowsSelected(!!value),
|
||||
'ariaLabel': 'Select all'
|
||||
}),
|
||||
cell: ({ row }) => h(UCheckbox, {
|
||||
'modelValue': row.getIsSelected(),
|
||||
'onUpdate:modelValue': (value: boolean) => row.toggleSelected(!!value),
|
||||
'onUpdate:modelValue': (value: boolean | 'indeterminate') => row.toggleSelected(!!value),
|
||||
'ariaLabel': 'Select row'
|
||||
}),
|
||||
enableSorting: false,
|
||||
|
||||
@@ -48,14 +48,13 @@ const data = ref<Payment[]>([{
|
||||
const columns: TableColumn<Payment>[] = [{
|
||||
id: 'select',
|
||||
header: ({ table }) => h(UCheckbox, {
|
||||
'modelValue': table.getIsAllPageRowsSelected(),
|
||||
'indeterminate': table.getIsSomePageRowsSelected(),
|
||||
'onUpdate:modelValue': (value: boolean) => table.toggleAllPageRowsSelected(!!value),
|
||||
'modelValue': table.getIsSomePageRowsSelected() ? 'indeterminate' : table.getIsAllPageRowsSelected(),
|
||||
'onUpdate:modelValue': (value: boolean | 'indeterminate') => table.toggleAllPageRowsSelected(!!value),
|
||||
'ariaLabel': 'Select all'
|
||||
}),
|
||||
cell: ({ row }) => h(UCheckbox, {
|
||||
'modelValue': row.getIsSelected(),
|
||||
'onUpdate:modelValue': (value: boolean) => row.toggleSelected(!!value),
|
||||
'onUpdate:modelValue': (value: boolean | 'indeterminate') => row.toggleSelected(!!value),
|
||||
'ariaLabel': 'Select row'
|
||||
})
|
||||
}, {
|
||||
|
||||
@@ -91,7 +91,7 @@ provide('navigation', mappedNavigation)
|
||||
<UApp>
|
||||
<NuxtLoadingIndicator color="#FFF" />
|
||||
|
||||
<Banner />
|
||||
<!-- <Banner /> -->
|
||||
|
||||
<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?
|
||||
|
||||
### 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.
|
||||
- **Active Development**: Radix Vue's growing popularity ensures ongoing improvements and updates.
|
||||
- **Extensive Component Library**: With 55+ primitives, Reka UI significantly expands our component offerings.
|
||||
- **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.
|
||||
- **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?"}
|
||||
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?"}
|
||||
|
||||
@@ -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.
|
||||
|
||||
::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"}
|
||||
|
||||
@@ -9,13 +9,13 @@ links:
|
||||
|
||||
## 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 changing the behavior of scroll body when setting body lock.
|
||||
- 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:
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
description: A stacked set of collapsible panels.
|
||||
links:
|
||||
- label: Accordion
|
||||
icon: i-custom-radix-vue
|
||||
to: https://www.radix-vue.com/components/accordion.html
|
||||
icon: i-custom-reka-ui
|
||||
to: https://reka-ui.com/docs/components/accordion
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
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
|
||||
|
||||
Use the `disabled` property to disable the Accordion.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
description: An img element with fallback and Nuxt Image support.
|
||||
links:
|
||||
- label: Avatar
|
||||
icon: i-custom-radix-vue
|
||||
to: https://www.radix-vue.com/components/avatar.html
|
||||
icon: i-custom-reka-ui
|
||||
to: https://reka-ui.com/docs/components/avatar
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
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.
|
||||
links:
|
||||
- label: Calendar
|
||||
icon: i-custom-radix-vue
|
||||
to: https://www.radix-vue.com/components/calendar.html
|
||||
icon: i-custom-reka-ui
|
||||
to: https://www.reka-ui.com/components/calendar.html
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
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.
|
||||
links:
|
||||
- label: Checkbox
|
||||
icon: i-custom-radix-vue
|
||||
to: https://www.radix-vue.com/components/checkbox.html
|
||||
icon: i-custom-reka-ui
|
||||
to: https://reka-ui.com/docs/components/checkbox
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/Checkbox.vue
|
||||
@@ -37,12 +37,14 @@ props:
|
||||
|
||||
### 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
|
||||
---
|
||||
ignore:
|
||||
- defaultValue
|
||||
props:
|
||||
indeterminate: true
|
||||
defaultValue: 'indeterminate'
|
||||
---
|
||||
::
|
||||
|
||||
@@ -52,8 +54,10 @@ Use the `indeterminate-icon` prop to customize the indeterminate icon. Defaults
|
||||
|
||||
::component-code
|
||||
---
|
||||
ignore:
|
||||
- defaultValue
|
||||
props:
|
||||
indeterminate: true
|
||||
defaultValue: 'indeterminate'
|
||||
indeterminateIcon: 'i-lucide-plus'
|
||||
---
|
||||
::
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
description: A collapsible element to toggle visibility of its content.
|
||||
links:
|
||||
- label: Collapsible
|
||||
icon: i-custom-radix-vue
|
||||
to: https://www.radix-vue.com/components/collapsible.html
|
||||
icon: i-custom-reka-ui
|
||||
to: https://reka-ui.com/docs/components/collapsible
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/Collapsible.vue
|
||||
@@ -38,6 +38,38 @@ slots:
|
||||
: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
|
||||
|
||||
Use the `disabled` prop to disable the Collapsible.
|
||||
|
||||
@@ -6,8 +6,8 @@ links:
|
||||
to: https://fusejs.io/
|
||||
target: _blank
|
||||
- label: Combobox
|
||||
icon: i-custom-radix-vue
|
||||
to: https://www.radix-vue.com/components/combobox.html
|
||||
icon: i-custom-reka-ui
|
||||
to: https://reka-ui.com/docs/components/combobox
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
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"}
|
||||
- `slot?: string`{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)
|
||||
- `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
|
||||
---
|
||||
collapse: true
|
||||
name: 'command-palette-filter-example'
|
||||
name: 'command-palette-ignore-filter-example'
|
||||
class: '!p-0'
|
||||
---
|
||||
::
|
||||
|
||||
@@ -3,8 +3,8 @@ title: ContextMenu
|
||||
description: A menu to display actions when right-clicking on an element.
|
||||
links:
|
||||
- label: ContextMenu
|
||||
icon: i-custom-radix-vue
|
||||
to: https://www.radix-vue.com/components/context-menu.html
|
||||
icon: i-custom-reka-ui
|
||||
to: https://reka-ui.com/docs/components/context-menu
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
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.
|
||||
links:
|
||||
- label: Drawer
|
||||
icon: i-custom-radix-vue
|
||||
to: https://github.com/radix-vue/vaul-vue
|
||||
icon: i-custom-reka-ui
|
||||
to: https://github.com/unovue/vaul-vue
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
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.
|
||||
links:
|
||||
- label: DropdownMenu
|
||||
icon: i-custom-radix-vue
|
||||
to: https://www.radix-vue.com/components/dropdown-menu.html
|
||||
icon: i-custom-reka-ui
|
||||
to: https://reka-ui.com/docs/components/dropdown-menu
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
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.
|
||||
links:
|
||||
- label: Combobox
|
||||
icon: i-custom-radix-vue
|
||||
to: https://www.radix-vue.com/components/combobox.html
|
||||
icon: i-custom-reka-ui
|
||||
to: https://reka-ui.com/docs/components/combobox
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
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.
|
||||
|
||||
::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
|
||||
@@ -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
|
||||
|
||||
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
|
||||
- bottom
|
||||
props:
|
||||
modelValue: Backlog
|
||||
modelValue: 'Backlog'
|
||||
content:
|
||||
align: center
|
||||
side: bottom
|
||||
@@ -310,7 +274,7 @@ external:
|
||||
- items
|
||||
- modelValue
|
||||
props:
|
||||
modelValue: Backlog
|
||||
modelValue: 'Backlog'
|
||||
arrow: true
|
||||
items:
|
||||
- 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
|
||||
|
||||
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
|
||||
---
|
||||
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.
|
||||
::
|
||||
|
||||
### 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
|
||||
---
|
||||
|
||||
@@ -3,8 +3,8 @@ title: InputNumber
|
||||
description: Input numerical values with a customizable range.
|
||||
links:
|
||||
- label: Number Field
|
||||
icon: i-custom-radix-vue
|
||||
to: https://www.radix-vue.com/components/number-field
|
||||
icon: i-custom-reka-ui
|
||||
to: https://www.reka-ui.com/components/input-number
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
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.
|
||||
links:
|
||||
- label: Dialog
|
||||
icon: i-custom-radix-vue
|
||||
to: https://www.radix-vue.com/components/dialog.html
|
||||
icon: i-custom-reka-ui
|
||||
to: https://reka-ui.com/docs/components/dialog
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
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.
|
||||
links:
|
||||
- label: NavigationMenu
|
||||
icon: i-custom-radix-vue
|
||||
to: https://www.radix-vue.com/components/navigation-menu.html
|
||||
icon: i-custom-reka-ui
|
||||
to: https://reka-ui.com/docs/components/navigation-menu
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/NavigationMenu.vue
|
||||
@@ -249,7 +249,7 @@ external:
|
||||
- items
|
||||
props:
|
||||
highlight: true
|
||||
highlightColor: ''
|
||||
highlightColor: 'primary'
|
||||
orientation: 'horizontal'
|
||||
items:
|
||||
- - label: Guide
|
||||
@@ -538,6 +538,98 @@ props:
|
||||
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
|
||||
|
||||
### Control active item
|
||||
@@ -592,7 +684,7 @@ name: 'navigation-menu-content-slot-example'
|
||||
::
|
||||
|
||||
::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
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
description: A list of buttons or links to navigate through pages.
|
||||
links:
|
||||
- label: Pagination
|
||||
icon: i-custom-radix-vue
|
||||
to: https://www.radix-vue.com/components/pagination.html
|
||||
icon: i-custom-reka-ui
|
||||
to: https://reka-ui.com/docs/components/pagination
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
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.
|
||||
links:
|
||||
- label: HoverCard
|
||||
icon: i-custom-reka-ui
|
||||
to: https://reka-ui.com/docs/components/hover-card
|
||||
- label: Popover
|
||||
icon: i-custom-radix-vue
|
||||
to: https://www.radix-vue.com/components/popover.html
|
||||
icon: i-custom-reka-ui
|
||||
to: https://reka-ui.com/docs/components/popover
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/Popover.vue
|
||||
@@ -64,7 +67,7 @@ slots:
|
||||
::
|
||||
|
||||
::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
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
description: An indicator showing the progress of a task.
|
||||
links:
|
||||
- label: Progress
|
||||
icon: i-custom-radix-vue
|
||||
to: https://www.radix-vue.com/components/progress.html
|
||||
icon: i-custom-reka-ui
|
||||
to: https://reka-ui.com/docs/components/progress
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
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.
|
||||
links:
|
||||
- label: RadioGroup
|
||||
icon: i-custom-radix-vue
|
||||
to: https://www.radix-vue.com/components/radio-group.html
|
||||
icon: i-custom-reka-ui
|
||||
to: https://reka-ui.com/docs/components/radio-group
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
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.
|
||||
links:
|
||||
- label: Combobox
|
||||
icon: i-custom-radix-vue
|
||||
to: https://www.radix-vue.com/components/combobox.html
|
||||
icon: i-custom-reka-ui
|
||||
to: https://reka-ui.com/docs/components/combobox
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
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.
|
||||
|
||||
::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
|
||||
@@ -239,44 +239,6 @@ props:
|
||||
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
|
||||
|
||||
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
|
||||
- bottom
|
||||
props:
|
||||
modelValue: Backlog
|
||||
modelValue: 'Backlog'
|
||||
content:
|
||||
align: center
|
||||
side: bottom
|
||||
@@ -332,7 +294,7 @@ external:
|
||||
- items
|
||||
- modelValue
|
||||
props:
|
||||
modelValue: Backlog
|
||||
modelValue: 'Backlog'
|
||||
arrow: true
|
||||
items:
|
||||
- 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
|
||||
|
||||
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
|
||||
---
|
||||
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.
|
||||
::
|
||||
|
||||
### 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
|
||||
---
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
description: A select element to choose from a list of options.
|
||||
links:
|
||||
- label: Select
|
||||
icon: i-custom-radix-vue
|
||||
to: https://www.radix-vue.com/components/select.html
|
||||
icon: i-custom-reka-ui
|
||||
to: https://reka-ui.com/docs/components/select
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
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
|
||||
|
||||
Use the `placeholder` prop to set a placeholder text.
|
||||
@@ -160,11 +193,7 @@ props:
|
||||
|
||||
### Content
|
||||
|
||||
Use the `content` prop to control how the Select content is rendered, like its `align`, `side` or `position` for example.
|
||||
|
||||
::warning
|
||||
The `content.align`, `content.side`, etc. properties only apply when `content.position` is set to `popper`.
|
||||
::
|
||||
Use the `content` prop to control how the Select content is rendered, like its `align` or `side` for example.
|
||||
|
||||
::component-code
|
||||
---
|
||||
@@ -177,9 +206,6 @@ external:
|
||||
- items
|
||||
- modelValue
|
||||
items:
|
||||
content.position:
|
||||
- 'item-aligned'
|
||||
- 'popper'
|
||||
content.align:
|
||||
- start
|
||||
- center
|
||||
@@ -190,9 +216,8 @@ items:
|
||||
- top
|
||||
- bottom
|
||||
props:
|
||||
modelValue: 'Todo'
|
||||
modelValue: 'Backlog'
|
||||
content:
|
||||
position: 'item-aligned'
|
||||
align: center
|
||||
side: bottom
|
||||
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
|
||||
|
||||
Use the `arrow` prop to display an arrow on the Select.
|
||||
@@ -226,7 +246,7 @@ external:
|
||||
- items
|
||||
- modelValue
|
||||
props:
|
||||
modelValue: 'Todo'
|
||||
modelValue: 'Backlog'
|
||||
arrow: true
|
||||
items:
|
||||
- Backlog
|
||||
@@ -237,8 +257,6 @@ props:
|
||||
---
|
||||
::
|
||||
|
||||
-->
|
||||
|
||||
### Color
|
||||
|
||||
Use the `color` prop to change the ring color when the Select is focused.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
description: Separates content horizontally or vertically.
|
||||
links:
|
||||
- label: Separator
|
||||
icon: i-custom-radix-vue
|
||||
to: https://www.radix-vue.com/components/separator.html
|
||||
icon: i-custom-reka-ui
|
||||
to: https://reka-ui.com/docs/components/separator
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
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.
|
||||
links:
|
||||
- label: Dialog
|
||||
icon: i-custom-radix-vue
|
||||
to: https://www.radix-vue.com/components/dialog.html
|
||||
icon: i-custom-reka-ui
|
||||
to: https://reka-ui.com/docs/components/dialog
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
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.
|
||||
links:
|
||||
- label: Slider
|
||||
icon: i-custom-radix-vue
|
||||
to: https://www.radix-vue.com/components/slider.html
|
||||
icon: i-custom-reka-ui
|
||||
to: https://reka-ui.com/docs/components/slider
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
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.
|
||||
links:
|
||||
- label: Switch
|
||||
icon: i-custom-radix-vue
|
||||
to: https://www.radix-vue.com/components/switch.html
|
||||
icon: i-custom-reka-ui
|
||||
to: https://reka-ui.com/docs/components/switch
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
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.
|
||||
links:
|
||||
- label: Tabs
|
||||
icon: i-custom-radix-vue
|
||||
to: https://www.radix-vue.com/components/tabs.html
|
||||
icon: i-custom-reka-ui
|
||||
to: https://reka-ui.com/docs/components/tabs
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/Tabs.vue
|
||||
@@ -44,9 +44,7 @@ props:
|
||||
|
||||
### Content
|
||||
|
||||
Use the `content` prop to control how the Tabs are rendered.
|
||||
|
||||
You can set it to `false` to prevent the Tabs from rendering any content and act as a toggle.
|
||||
Set the `content` prop to `false` to turn the Tabs into a toggle-only control without displaying any content. Defaults to `true`.
|
||||
|
||||
::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
|
||||
---
|
||||
prettier: true
|
||||
ignore:
|
||||
- content.forceMount
|
||||
- content
|
||||
- items
|
||||
- class
|
||||
external:
|
||||
- items
|
||||
props:
|
||||
content:
|
||||
forceMount: false
|
||||
unmountOnHide: false
|
||||
items:
|
||||
- label: Account
|
||||
icon: 'i-lucide-user'
|
||||
@@ -95,7 +93,7 @@ props:
|
||||
::
|
||||
|
||||
::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
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
description: A succinct message to provide information or feedback to the user.
|
||||
links:
|
||||
- label: Toast
|
||||
icon: i-custom-radix-vue
|
||||
to: https://www.radix-vue.com/components/toast.html
|
||||
icon: i-custom-reka-ui
|
||||
to: https://reka-ui.com/docs/components/toast
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
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.
|
||||
|
||||
::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"}
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
description: A popup that reveals information when hovering over an element.
|
||||
links:
|
||||
- label: Tooltip
|
||||
icon: i-custom-radix-vue
|
||||
to: https://www.radix-vue.com/components/tooltip.html
|
||||
icon: i-custom-reka-ui
|
||||
to: https://reka-ui.com/docs/components/tooltip
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
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.
|
||||
|
||||
::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"}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"@nuxt/content": "https://pkg.pr.new/@nuxt/content@c5b1a4f",
|
||||
"@nuxt/image": "^1.8.1",
|
||||
"@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",
|
||||
"@nuxtjs/plausible": "^1.2.0",
|
||||
"@octokit/rest": "^21.0.2",
|
||||
|
||||
@@ -103,8 +103,8 @@
|
||||
"magic-string": "^0.30.14",
|
||||
"mlly": "^1.7.3",
|
||||
"ohash": "^1.1.4",
|
||||
"reka-ui": "https://pkg.pr.new/reka-ui@8238615",
|
||||
"pathe": "^1.1.2",
|
||||
"radix-vue": "^1.9.10",
|
||||
"scule": "^1.3.0",
|
||||
"sirv": "^3.0.0",
|
||||
"tailwind-variants": "^0.3.0",
|
||||
|
||||
@@ -30,6 +30,6 @@ export default defineConfig({
|
||||
],
|
||||
optimizeDeps: {
|
||||
// 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="Icon" icon="i-lucide-heart" :model-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="Disabled" disabled />
|
||||
</div>
|
||||
|
||||
@@ -130,7 +130,7 @@ defineShortcuts({
|
||||
<UButton label="Open modal" color="neutral" variant="outline" />
|
||||
|
||||
<template #content>
|
||||
<ReuseTemplate :close="true" @close="open = false" />
|
||||
<ReuseTemplate :close="true" @update:open="open = $event" />
|
||||
</template>
|
||||
</UModal>
|
||||
|
||||
@@ -138,7 +138,7 @@ defineShortcuts({
|
||||
<UButton label="Open drawer" color="neutral" variant="outline" />
|
||||
|
||||
<template #content>
|
||||
<ReuseTemplate class="border-t border-[var(--ui-border)]" />
|
||||
<ReuseTemplate class="border-t border-[var(--ui-border)] mt-4" />
|
||||
</template>
|
||||
</UDrawer>
|
||||
|
||||
|
||||
@@ -83,7 +83,7 @@ const { data: users, status } = await useFetch('https://jsonplaceholder.typicode
|
||||
<div class="flex flex-col gap-4 w-48">
|
||||
<UInputMenu :items="items" placeholder="Disabled" disabled />
|
||||
<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..." />
|
||||
</div>
|
||||
<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"
|
||||
:items="users || []"
|
||||
:loading="status === 'pending'"
|
||||
:filter="false"
|
||||
ignore-filter
|
||||
icon="i-lucide-user"
|
||||
placeholder="Search users..."
|
||||
:size="size"
|
||||
|
||||
@@ -124,7 +124,7 @@ const { data: users, status } = await useFetch('https://jsonplaceholder.typicode
|
||||
v-model:search-term="searchTerm"
|
||||
:items="users || []"
|
||||
:loading="status === 'pending'"
|
||||
:filter="false"
|
||||
ignore-filter
|
||||
icon="i-lucide-user"
|
||||
placeholder="Search users..."
|
||||
:size="size"
|
||||
|
||||
@@ -10,6 +10,7 @@ const fruits = ['Apple', 'Banana', 'Blueberry', 'Grapes', 'Pineapple']
|
||||
const vegetables = ['Aubergine', 'Broccoli', 'Carrot', 'Courgette', 'Leek']
|
||||
|
||||
const items = [[{ label: 'Fruits', type: 'label' }, ...fruits], [{ label: 'Vegetables', type: 'label' }, ...vegetables]]
|
||||
const selectedItems = ref([fruits[0]!, vegetables[0]!])
|
||||
|
||||
const statuses = [{
|
||||
label: 'Backlog',
|
||||
@@ -40,7 +41,7 @@ const { data: users, status } = await useFetch('https://jsonplaceholder.typicode
|
||||
lazy: true
|
||||
})
|
||||
|
||||
function getStatusIcon(value: string): string {
|
||||
function getStatusIcon(value: string) {
|
||||
return statuses.find(status => status.value === value)?.icon || 'i-lucide-user'
|
||||
}
|
||||
|
||||
@@ -52,7 +53,7 @@ function getUserAvatar(value: string) {
|
||||
<template>
|
||||
<div class="flex flex-col items-center gap-4">
|
||||
<div class="flex flex-col gap-4 w-48">
|
||||
<USelect :items="items" placeholder="Search..." />
|
||||
<USelect :items="items" placeholder="Search..." default-value="Apple" />
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<USelect
|
||||
@@ -90,6 +91,7 @@ function getUserAvatar(value: string) {
|
||||
<div class="flex flex-col gap-4 w-48">
|
||||
<USelect :items="items" placeholder="Disabled" disabled />
|
||||
<USelect :items="items" placeholder="Required" required />
|
||||
<USelect v-model="selectedItems" :items="items" placeholder="Multiple" multiple />
|
||||
<USelect :items="items" loading placeholder="Search..." />
|
||||
</div>
|
||||
<div class="flex items-center gap-4">
|
||||
@@ -114,7 +116,7 @@ function getUserAvatar(value: string) {
|
||||
class="w-48"
|
||||
>
|
||||
<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>
|
||||
</USelect>
|
||||
</div>
|
||||
@@ -130,7 +132,7 @@ function getUserAvatar(value: string) {
|
||||
class="w-48"
|
||||
>
|
||||
<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>
|
||||
</USelect>
|
||||
</div>
|
||||
|
||||
@@ -145,14 +145,13 @@ const data = ref<Payment[]>([{
|
||||
const columns: TableColumn<Payment>[] = [{
|
||||
id: 'select',
|
||||
header: ({ table }) => h(UCheckbox, {
|
||||
'modelValue': table.getIsAllPageRowsSelected(),
|
||||
'indeterminate': table.getIsSomePageRowsSelected(),
|
||||
'onUpdate:modelValue': (value: boolean) => table.toggleAllPageRowsSelected(!!value),
|
||||
'modelValue': table.getIsSomePageRowsSelected() ? 'indeterminate' : table.getIsAllPageRowsSelected(),
|
||||
'onUpdate:modelValue': (value: boolean | 'indeterminate') => table.toggleAllPageRowsSelected(!!value),
|
||||
'ariaLabel': 'Select all'
|
||||
}),
|
||||
cell: ({ row }) => h(UCheckbox, {
|
||||
'modelValue': row.getIsSelected(),
|
||||
'onUpdate:modelValue': (value: boolean) => row.toggleSelected(!!value),
|
||||
'onUpdate:modelValue': (value: boolean | 'indeterminate') => row.toggleSelected(!!value),
|
||||
'ariaLabel': 'Select row'
|
||||
}),
|
||||
enableSorting: false,
|
||||
|
||||
@@ -3,8 +3,8 @@ import { createResolver } from '@nuxt/kit'
|
||||
const { resolve } = createResolver(import.meta.url)
|
||||
|
||||
export default defineNuxtConfig({
|
||||
|
||||
modules: ['../src/module'],
|
||||
|
||||
devtools: { enabled: true },
|
||||
|
||||
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">
|
||||
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 _appConfig from '#build/app.config'
|
||||
import theme from '#build/ui/accordion'
|
||||
@@ -22,7 +22,7 @@ export interface AccordionItem {
|
||||
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.
|
||||
* @defaultValue 'div'
|
||||
@@ -89,7 +89,7 @@ extendDevtoolsMeta({
|
||||
|
||||
<script setup lang="ts" generic="T extends AccordionItem">
|
||||
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 { useAppConfig } from '#imports'
|
||||
import { get } from '../utils'
|
||||
@@ -104,7 +104,7 @@ const emits = defineEmits<AccordionEmits>()
|
||||
const slots = defineSlots<AccordionSlots<T>>()
|
||||
|
||||
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({
|
||||
disabled: props.disabled
|
||||
|
||||
@@ -64,7 +64,7 @@ extendDevtoolsMeta<AlertProps>({ defaultProps: { title: 'Heads up!' } })
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { Primitive } from 'radix-vue'
|
||||
import { Primitive } from 'reka-ui'
|
||||
import { useAppConfig } from '#imports'
|
||||
import { useLocale } from '../composables/useLocale'
|
||||
import UIcon from './Icon.vue'
|
||||
@@ -75,8 +75,8 @@ const props = defineProps<AlertProps>()
|
||||
const emits = defineEmits<AlertEmits>()
|
||||
const slots = defineSlots<AlertSlots>()
|
||||
|
||||
const appConfig = useAppConfig()
|
||||
const { t } = useLocale()
|
||||
const appConfig = useAppConfig()
|
||||
|
||||
const multiline = computed(() => !!props.title && !!props.description)
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<script lang="ts">
|
||||
import type { ConfigProviderProps, TooltipProviderProps } from 'radix-vue'
|
||||
import type { ConfigProviderProps, TooltipProviderProps } from 'reka-ui'
|
||||
import { localeContextInjectionKey } from '../composables/useLocale'
|
||||
import { extendDevtoolsMeta } from '../composables/extendDevtoolsMeta'
|
||||
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
|
||||
toaster?: ToasterProps | null
|
||||
locale?: Locale
|
||||
@@ -23,7 +23,7 @@ extendDevtoolsMeta({ ignore: true })
|
||||
|
||||
<script setup lang="ts">
|
||||
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 UToaster from './Toaster.vue'
|
||||
import UModalProvider from './ModalProvider.vue'
|
||||
@@ -41,7 +41,7 @@ provide(localeContextInjectionKey, locale)
|
||||
</script>
|
||||
|
||||
<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">
|
||||
<UToaster v-if="toaster !== null" v-bind="toasterProps">
|
||||
<slot />
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
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 { extendDevtoolsMeta } from '../composables/extendDevtoolsMeta'
|
||||
import _appConfig from '#build/app.config'
|
||||
@@ -32,7 +32,7 @@ extendDevtoolsMeta<AvatarProps>({ defaultProps: { src: 'https://avatars.githubus
|
||||
|
||||
<script setup lang="ts">
|
||||
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 { useAvatarGroup } from '../composables/useAvatarGroup'
|
||||
import UIcon from './Icon.vue'
|
||||
|
||||
@@ -35,7 +35,7 @@ extendDevtoolsMeta({ example: 'AvatarGroupExample' })
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, provide } from 'vue'
|
||||
import { Primitive } from 'radix-vue'
|
||||
import { Primitive } from 'reka-ui'
|
||||
import { avatarGroupInjectionKey } from '../composables/useAvatarGroup'
|
||||
import UAvatar from './Avatar.vue'
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ export interface BadgeSlots {
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { Primitive } from 'radix-vue'
|
||||
import { Primitive } from 'reka-ui'
|
||||
import { useComponentIcons } from '../composables/useComponentIcons'
|
||||
import UIcon from './Icon.vue'
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ extendDevtoolsMeta({
|
||||
|
||||
<script setup lang="ts" generic="T extends BreadcrumbItem">
|
||||
import { computed } from 'vue'
|
||||
import { Primitive } from 'radix-vue'
|
||||
import { Primitive } from 'reka-ui'
|
||||
import { useAppConfig } from '#imports'
|
||||
import { useLocale } from '../composables/useLocale'
|
||||
import { get } from '../utils'
|
||||
|
||||
@@ -43,7 +43,7 @@ export interface ButtonSlots {
|
||||
|
||||
<script setup lang="ts">
|
||||
import { type Ref, computed, ref, inject } from 'vue'
|
||||
import { useForwardProps } from 'radix-vue'
|
||||
import { useForwardProps } from 'reka-ui'
|
||||
import { useComponentIcons } from '../composables/useComponentIcons'
|
||||
import { useButtonGroup } from '../composables/useButtonGroup'
|
||||
import { formLoadingInjectionKey } from '../composables/useFormField'
|
||||
|
||||
@@ -35,7 +35,7 @@ extendDevtoolsMeta({ example: 'ButtonGroupExample' })
|
||||
|
||||
<script setup lang="ts">
|
||||
import { provide, computed } from 'vue'
|
||||
import { Primitive } from 'radix-vue'
|
||||
import { Primitive } from 'reka-ui'
|
||||
import { buttonGroupInjectionKey } from '../composables/useButtonGroup'
|
||||
|
||||
const props = withDefaults(defineProps<ButtonGroupProps>(), {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
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 { AppConfig } from '@nuxt/schema'
|
||||
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">
|
||||
import { computed } from 'vue'
|
||||
import { useForwardPropsEmits } from 'radix-vue'
|
||||
import { Calendar as SingleCalendar, RangeCalendar } from 'radix-vue/namespaced'
|
||||
import { useForwardPropsEmits } from 'reka-ui'
|
||||
import { Calendar as SingleCalendar, RangeCalendar } from 'reka-ui/namespaced'
|
||||
import { reactiveOmit } from '@vueuse/core'
|
||||
import { useLocale } from '../composables/useLocale'
|
||||
import UButton from './Button.vue'
|
||||
|
||||
@@ -29,7 +29,7 @@ extendDevtoolsMeta({ example: 'CardExample' })
|
||||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Primitive } from 'radix-vue'
|
||||
import { Primitive } from 'reka-ui'
|
||||
|
||||
const props = defineProps<CardProps>()
|
||||
const slots = defineSlots<CardSlots>()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { tv, type VariantProps } from 'tailwind-variants'
|
||||
import type { AppConfig } from '@nuxt/schema'
|
||||
import type { AcceptableValue } from 'reka-ui'
|
||||
import type { EmblaCarouselType, EmblaOptionsType, EmblaPluginType } from 'embla-carousel'
|
||||
import type { AutoplayOptionsType } from 'embla-carousel-autoplay'
|
||||
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 { extendDevtoolsMeta } from '../composables/extendDevtoolsMeta'
|
||||
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> } }
|
||||
|
||||
@@ -21,6 +22,11 @@ const carousel = tv({ extend: tv(theme), ...(appConfig.ui?.carousel || {}) })
|
||||
type CarouselVariants = VariantProps<typeof carousel>
|
||||
|
||||
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.
|
||||
* @defaultValue { size: 'md', color: 'neutral', variant: 'link' }
|
||||
@@ -97,7 +103,7 @@ extendDevtoolsMeta({ example: 'CarouselExample' })
|
||||
<script setup lang="ts" generic="T extends AcceptableValue">
|
||||
import { computed, ref, watch, onMounted } from '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 { useAppConfig } from '#imports'
|
||||
import { useLocale } from '../composables/useLocale'
|
||||
@@ -254,7 +260,8 @@ defineExpose({
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
<Primitive
|
||||
:as="as"
|
||||
role="region"
|
||||
aria-roledescription="carousel"
|
||||
tabindex="0"
|
||||
@@ -311,5 +318,5 @@ defineExpose({
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Primitive>
|
||||
</template>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
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 _appConfig from '#build/app.config'
|
||||
import theme from '#build/ui/checkbox'
|
||||
@@ -12,7 +12,12 @@ const checkbox = tv({ extend: tv(theme), ...(appConfig.ui?.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
|
||||
description?: string
|
||||
color?: CheckboxVariants['color']
|
||||
@@ -22,21 +27,17 @@ export interface CheckboxProps extends Pick<CheckboxRootProps, 'disabled' | 'req
|
||||
* @defaultValue appConfig.ui.icons.check
|
||||
*/
|
||||
icon?: string
|
||||
indeterminate?: boolean
|
||||
/**
|
||||
* The icon displayed when the checkbox is indeterminate.
|
||||
* @defaultValue appConfig.ui.icons.minus
|
||||
*/
|
||||
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
|
||||
ui?: Partial<typeof checkbox.slots>
|
||||
}
|
||||
|
||||
export interface CheckboxEmits {
|
||||
(e: 'update:modelValue', payload: boolean): void
|
||||
(e: 'change', payload: Event): void
|
||||
export type CheckboxEmits = {
|
||||
change: [payload: Event]
|
||||
}
|
||||
|
||||
export interface CheckboxSlots {
|
||||
@@ -49,7 +50,7 @@ extendDevtoolsMeta({ defaultProps: { label: 'Check me!' } })
|
||||
|
||||
<script setup lang="ts">
|
||||
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 { useAppConfig } from '#imports'
|
||||
import { useFormField } from '../composables/useFormField'
|
||||
@@ -59,29 +60,20 @@ const props = defineProps<CheckboxProps>()
|
||||
const slots = defineSlots<CheckboxSlots>()
|
||||
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 { id: _id, emitFormChange, emitFormInput, size, color, name, disabled } = useFormField<CheckboxProps>(props)
|
||||
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({
|
||||
size: size.value,
|
||||
color: color.value,
|
||||
required: props.required,
|
||||
disabled: disabled.value,
|
||||
checked: (modelValue.value ?? props.defaultValue) || props.indeterminate
|
||||
checked: Boolean(modelValue.value ?? props.defaultValue)
|
||||
}))
|
||||
|
||||
function onUpdate(value: any) {
|
||||
@@ -93,23 +85,25 @@ function onUpdate(value: any) {
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- eslint-disable vue/no-template-shadow -->
|
||||
<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 })">
|
||||
<CheckboxRoot
|
||||
:id="id"
|
||||
v-model:checked="checked"
|
||||
:default-checked="defaultValue"
|
||||
v-bind="rootProps"
|
||||
v-model="modelValue"
|
||||
:name="name"
|
||||
:disabled="disabled"
|
||||
:class="ui.base({ class: props.ui?.base })"
|
||||
@update:checked="onUpdate"
|
||||
@update:model-value="onUpdate"
|
||||
>
|
||||
<CheckboxIndicator as-child>
|
||||
<UIcon v-if="indeterminate" :name="indeterminateIcon || appConfig.ui.icons.minus" :class="ui.icon({ class: props.ui?.icon })" />
|
||||
<UIcon v-else :name="icon || appConfig.ui.icons.check" :class="ui.icon({ class: props.ui?.icon })" />
|
||||
</CheckboxIndicator>
|
||||
<template #default="{ modelValue }">
|
||||
<CheckboxIndicator as-child>
|
||||
<UIcon v-if="modelValue === 'indeterminate'" :name="indeterminateIcon || appConfig.ui.icons.minus" :class="ui.icon({ class: props.ui?.icon })" />
|
||||
<UIcon v-else :name="icon || appConfig.ui.icons.check" :class="ui.icon({ class: props.ui?.icon })" />
|
||||
</CheckboxIndicator>
|
||||
</template>
|
||||
</CheckboxRoot>
|
||||
</div>
|
||||
|
||||
@@ -125,5 +119,5 @@ function onUpdate(value: any) {
|
||||
</slot>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Primitive>
|
||||
</template>
|
||||
|
||||
@@ -44,7 +44,7 @@ extendDevtoolsMeta({ example: 'ChipExample' })
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { Primitive, Slot } from 'radix-vue'
|
||||
import { Primitive, Slot } from 'reka-ui'
|
||||
import { useAvatarGroup } from '../composables/useAvatarGroup'
|
||||
|
||||
defineOptions({ inheritAttrs: false })
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
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 _appConfig from '#build/app.config'
|
||||
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 || {}) })
|
||||
|
||||
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.
|
||||
* @defaultValue 'div'
|
||||
@@ -31,14 +31,14 @@ extendDevtoolsMeta({ example: 'CollapsibleExample' })
|
||||
</script>
|
||||
|
||||
<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'
|
||||
|
||||
const props = defineProps<CollapsibleProps>()
|
||||
const emits = defineEmits<CollapsibleEmits>()
|
||||
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
|
||||
const ui = collapsible()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
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 { AppConfig } from '@nuxt/schema'
|
||||
import type { UseFuseOptions } from '@vueuse/integrations/useFuse'
|
||||
@@ -37,17 +37,17 @@ export interface CommandPaletteGroup<T> {
|
||||
items?: T[]
|
||||
/**
|
||||
* 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.).
|
||||
* @defaultValue true
|
||||
* When `true`, items will not be filtered which is useful for custom filtering (useAsyncData, useFetch, etc.).
|
||||
* @defaultValue false
|
||||
*/
|
||||
filter?: boolean
|
||||
ignoreFilter?: boolean
|
||||
/** Filter group items after the search happened. */
|
||||
postFilter?: (searchTerm: string, items: T[]) => T[]
|
||||
/** The icon displayed when an item is highlighted. */
|
||||
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.
|
||||
* @defaultValue 'div'
|
||||
@@ -102,7 +102,9 @@ export interface CommandPaletteProps<G, T> extends Pick<ComboboxRootProps, 'mult
|
||||
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
|
||||
|
||||
@@ -120,7 +122,7 @@ extendDevtoolsMeta({ example: 'CommandPaletteExample', ignoreProps: ['groups'] }
|
||||
|
||||
<script setup lang="ts" generic="G extends CommandPaletteGroup<T>, T extends CommandPaletteItem">
|
||||
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 { reactivePick } from '@vueuse/core'
|
||||
import { useFuse } from '@vueuse/integrations/useFuse'
|
||||
@@ -145,9 +147,10 @@ const slots = defineSlots<CommandPaletteSlots<G, T>>()
|
||||
|
||||
const searchTerm = defineModel<string>('searchTerm', { default: '' })
|
||||
|
||||
const appConfig = useAppConfig()
|
||||
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'))
|
||||
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
@@ -169,7 +172,7 @@ const items = computed(() => props.groups?.filter((group) => {
|
||||
return false
|
||||
}
|
||||
|
||||
if (group.filter === false) {
|
||||
if (group.ignoreFilter) {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -217,7 +220,7 @@ const groups = computed(() => {
|
||||
return getGroupWithItems(group, items)
|
||||
}).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 || [])
|
||||
}) || []
|
||||
|
||||
@@ -230,8 +233,8 @@ const groups = computed(() => {
|
||||
|
||||
<!-- eslint-disable vue/no-v-html -->
|
||||
<template>
|
||||
<ComboboxRoot v-bind="rootProps" v-model:search-term="searchTerm" open :class="ui.root({ class: [props.class, props.ui?.root] })">
|
||||
<ComboboxInput as-child>
|
||||
<ListboxRoot v-bind="rootProps" :class="ui.root({ class: [props.class, props.ui?.root] })">
|
||||
<ListboxFilter v-model="searchTerm" as-child>
|
||||
<UInput
|
||||
variant="none"
|
||||
autofocus
|
||||
@@ -256,72 +259,70 @@ const groups = computed(() => {
|
||||
</slot>
|
||||
</template>
|
||||
</UInput>
|
||||
</ComboboxInput>
|
||||
</ListboxFilter>
|
||||
|
||||
<ComboboxPortal disabled>
|
||||
<ComboboxContent :class="ui.content({ class: props.ui?.content })" :dismissable="false">
|
||||
<ComboboxEmpty :class="ui.empty({ class: props.ui?.empty })">
|
||||
<slot name="empty" :search-term="searchTerm">
|
||||
{{ searchTerm ? t('commandPalette.noMatch', { searchTerm }) : t('commandPalette.noData') }}
|
||||
</slot>
|
||||
</ComboboxEmpty>
|
||||
<ListboxContent :class="ui.content({ class: props.ui?.content })">
|
||||
<div v-if="groups?.length" :class="ui.viewport({ class: props.ui?.viewport })">
|
||||
<ListboxGroup v-for="(group, groupIndex) in groups" :key="`group-${groupIndex}`" :class="ui.group({ class: props.ui?.group })">
|
||||
<ListboxGroupLabel v-if="get(group, props.labelKey as string)" :class="ui.label({ class: props.ui?.label })">
|
||||
{{ get(group, props.labelKey as string) }}
|
||||
</ListboxGroupLabel>
|
||||
|
||||
<ComboboxViewport :class="ui.viewport({ class: props.ui?.viewport })">
|
||||
<ComboboxGroup v-for="(group, groupIndex) in groups" :key="`group-${groupIndex}`" :class="ui.group({ class: props.ui?.group })">
|
||||
<ComboboxLabel v-if="get(group, props.labelKey as string)" :class="ui.label({ class: props.ui?.label })">
|
||||
{{ get(group, props.labelKey as string) }}
|
||||
</ComboboxLabel>
|
||||
<ListboxItem
|
||||
v-for="(item, index) in group.items"
|
||||
:key="`group-${groupIndex}-${index}`"
|
||||
:value="omit(item, ['matches' as any, 'group' as any, 'onSelect', 'labelHtml', 'suffixHtml'])"
|
||||
: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
|
||||
v-for="(item, index) in group.items"
|
||||
:key="`group-${groupIndex}-${index}`"
|
||||
:value="omit(item, ['matches' as any, 'group' as any, 'onSelect', 'labelHtml', 'suffixHtml'])"
|
||||
: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 })"
|
||||
/>
|
||||
<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 })">
|
||||
<slot :name="item.slot ? `${item.slot}-label` : group.slot ? `${group.slot}-label` : `item-label`" :item="item" :index="index">
|
||||
<span v-if="item.prefix" :class="ui.itemLabelPrefix({ class: props.ui?.itemLabelPrefix })">{{ item.prefix }}</span>
|
||||
|
||||
<span :class="ui.itemLabelBase({ class: props.ui?.itemLabelBase, active: item.active })" v-html="item.labelHtml || get(item, props.labelKey as string)" />
|
||||
|
||||
<span :class="ui.itemLabelSuffix({ class: props.ui?.itemLabelSuffix, active: item.active })" v-html="item.suffixHtml || item.suffix" />
|
||||
</slot>
|
||||
</span>
|
||||
|
||||
<span :class="ui.itemTrailing({ class: props.ui?.itemTrailing })">
|
||||
<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>
|
||||
|
||||
<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 })">
|
||||
<slot :name="item.slot ? `${item.slot}-label` : group.slot ? `${group.slot}-label` : `item-label`" :item="item" :index="index">
|
||||
<span v-if="item.prefix" :class="ui.itemLabelPrefix({ class: props.ui?.itemLabelPrefix })">{{ item.prefix }}</span>
|
||||
<ListboxItemIndicator as-child>
|
||||
<UIcon :name="selectedIcon || appConfig.ui.icons.check" :class="ui.itemTrailingIcon({ class: props.ui?.itemTrailingIcon })" />
|
||||
</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)" />
|
||||
|
||||
<span :class="ui.itemLabelSuffix({ class: props.ui?.itemLabelSuffix, active: item.active })" v-html="item.suffixHtml || item.suffix" />
|
||||
</slot>
|
||||
</span>
|
||||
|
||||
<span :class="ui.itemTrailing({ class: props.ui?.itemTrailing })">
|
||||
<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>
|
||||
<div v-else :class="ui.empty({ class: props.ui?.empty })">
|
||||
<slot name="empty" :search-term="searchTerm">
|
||||
{{ searchTerm ? t('commandPalette.noMatch', { searchTerm }) : t('commandPalette.noData') }}
|
||||
</slot>
|
||||
</div>
|
||||
</ListboxContent>
|
||||
</ListboxRoot>
|
||||
</template>
|
||||
|
||||
@@ -26,7 +26,7 @@ extendDevtoolsMeta({ example: 'ContainerExample' })
|
||||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Primitive } from 'radix-vue'
|
||||
import { Primitive } from 'reka-ui'
|
||||
|
||||
const props = defineProps<ContainerProps>()
|
||||
defineSlots<ContainerSlots>()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
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 _appConfig from '#build/app.config'
|
||||
import theme from '#build/ui/context-menu'
|
||||
@@ -143,7 +143,7 @@ extendDevtoolsMeta({
|
||||
|
||||
<script setup lang="ts" generic="T extends ContextMenuItem">
|
||||
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 { omit } from '../utils'
|
||||
import UContextMenuContent from './ContextMenuContent.vue'
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<script lang="ts">
|
||||
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 type { KbdProps, AvatarProps, ContextMenuItem, ContextMenuSlots } from '../types'
|
||||
|
||||
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[][]
|
||||
portal?: boolean
|
||||
sub?: boolean
|
||||
@@ -18,13 +18,13 @@ interface ContextMenuContentProps<T> extends Omit<RadixContextMenuContentProps,
|
||||
uiOverride?: any
|
||||
}
|
||||
|
||||
interface ContextMenuContentEmits extends RadixContextMenuContentEmits {}
|
||||
interface ContextMenuContentEmits extends RekaContextMenuContentEmits {}
|
||||
</script>
|
||||
|
||||
<script setup lang="ts" generic="T extends ContextMenuItem">
|
||||
import { computed } from 'vue'
|
||||
import { ContextMenu } from 'radix-vue/namespaced'
|
||||
import { useForwardPropsEmits } from 'radix-vue'
|
||||
import { ContextMenu } from 'reka-ui/namespaced'
|
||||
import { useForwardPropsEmits } from 'reka-ui'
|
||||
import { reactiveOmit, createReusableTemplate } from '@vueuse/core'
|
||||
import { useAppConfig } from '#imports'
|
||||
import { omit, get } from '../utils'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { tv } from 'tailwind-variants'
|
||||
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 _appConfig from '#build/app.config'
|
||||
import theme from '#build/ui/drawer'
|
||||
@@ -58,7 +58,7 @@ extendDevtoolsMeta({ example: 'DrawerExample' })
|
||||
|
||||
<script setup lang="ts">
|
||||
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 { reactivePick } from '@vueuse/core'
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
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 _appConfig from '#build/app.config'
|
||||
import theme from '#build/ui/dropdown-menu'
|
||||
@@ -140,7 +140,7 @@ extendDevtoolsMeta({
|
||||
<script setup lang="ts" generic="T extends DropdownMenuItem">
|
||||
import { computed, toRef } from 'vue'
|
||||
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 { omit } from '../utils'
|
||||
import UDropdownMenuContent from './DropdownMenuContent.vue'
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<!-- eslint-disable vue/block-tag-newline -->
|
||||
<script lang="ts">
|
||||
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 type { KbdProps, AvatarProps, DropdownMenuItem, DropdownMenuSlots } from '../types'
|
||||
|
||||
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[][]
|
||||
portal?: boolean
|
||||
sub?: boolean
|
||||
@@ -19,7 +19,7 @@ interface DropdownMenuContentProps<T> extends Omit<RadixDropdownMenuContentProps
|
||||
uiOverride?: any
|
||||
}
|
||||
|
||||
interface DropdownMenuContentEmits extends RadixDropdownMenuContentEmits {}
|
||||
interface DropdownMenuContentEmits extends RekaDropdownMenuContentEmits {}
|
||||
|
||||
type DropdownMenuContentSlots<T extends { slot?: string }> = Omit<DropdownMenuSlots<T>, 'default'> & {
|
||||
default(props?: {}): any
|
||||
@@ -29,8 +29,8 @@ type DropdownMenuContentSlots<T extends { slot?: string }> = Omit<DropdownMenuSl
|
||||
|
||||
<script setup lang="ts" generic="T extends DropdownMenuItem">
|
||||
import { computed } from 'vue'
|
||||
import { DropdownMenu } from 'radix-vue/namespaced'
|
||||
import { useForwardPropsEmits } from 'radix-vue'
|
||||
import { DropdownMenu } from 'reka-ui/namespaced'
|
||||
import { useForwardPropsEmits } from 'reka-ui'
|
||||
import { reactiveOmit, createReusableTemplate } from '@vueuse/core'
|
||||
import { useAppConfig } from '#imports'
|
||||
import { omit, get } from '../utils'
|
||||
|
||||
@@ -12,6 +12,11 @@ const formField = tv({ extend: tv(theme), ...(appConfig.ui?.formField || {}) })
|
||||
type FormFieldVariants = VariantProps<typeof formField>
|
||||
|
||||
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. */
|
||||
name?: string
|
||||
/** A regular expression to match form error names. */
|
||||
@@ -43,7 +48,7 @@ extendDevtoolsMeta({ example: 'FormFieldExample', defaultProps: { label: 'Label'
|
||||
|
||||
<script setup lang="ts">
|
||||
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 type { FormError, FormFieldInjectedOptions } from '../types/form'
|
||||
|
||||
@@ -74,7 +79,7 @@ provide(formFieldInjectionKey, computed(() => ({
|
||||
</script>
|
||||
|
||||
<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 v-if="label || !!slots.label" :class="ui.labelWrapper({ class: props.ui?.labelWrapper })">
|
||||
<Label :for="id" :class="ui.label({ class: props.ui?.label })">
|
||||
@@ -110,5 +115,5 @@ provide(formFieldInjectionKey, computed(() => ({
|
||||
</slot>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Primitive>
|
||||
</template>
|
||||
|
||||
@@ -13,7 +13,7 @@ export interface IconProps {
|
||||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useForwardProps } from 'radix-vue'
|
||||
import { useForwardProps } from 'reka-ui'
|
||||
import { reactivePick } from '@vueuse/core'
|
||||
|
||||
const props = defineProps<IconProps>()
|
||||
|
||||
@@ -15,6 +15,11 @@ const input = tv({ extend: tv(theme), ...(appConfig.ui?.input || {}) })
|
||||
type InputVariants = VariantProps<typeof input>
|
||||
|
||||
export interface InputProps extends UseComponentIconsProps {
|
||||
/**
|
||||
* The element or component this component should render as.
|
||||
* @defaultValue 'div'
|
||||
*/
|
||||
as?: any
|
||||
id?: string
|
||||
name?: string
|
||||
type?: InputHTMLAttributes['type']
|
||||
@@ -49,6 +54,7 @@ export interface InputSlots {
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { Primitive } from 'reka-ui'
|
||||
import { useButtonGroup } from '../composables/useButtonGroup'
|
||||
import { useComponentIcons } from '../composables/useComponentIcons'
|
||||
import { useFormField } from '../composables/useFormField'
|
||||
@@ -147,7 +153,7 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="ui.root({ class: [props.class, props.ui?.root] })">
|
||||
<Primitive :as="as" :class="ui.root({ class: [props.class, props.ui?.root] })">
|
||||
<input
|
||||
:id="id"
|
||||
ref="inputRef"
|
||||
@@ -179,5 +185,5 @@ onMounted(() => {
|
||||
<UIcon v-if="trailingIconName" :name="trailingIconName" :class="ui.trailingIcon({ class: props.ui?.trailingIcon })" />
|
||||
</slot>
|
||||
</span>
|
||||
</div>
|
||||
</Primitive>
|
||||
</template>
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
<script lang="ts">
|
||||
import type { InputHTMLAttributes } from 'vue'
|
||||
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 _appConfig from '#build/app.config'
|
||||
import theme from '#build/ui/input-menu'
|
||||
import type { UseComponentIconsProps } from '../composables/useComponentIcons'
|
||||
import { extendDevtoolsMeta } from '../composables/extendDevtoolsMeta'
|
||||
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> } }
|
||||
|
||||
@@ -30,7 +30,7 @@ export interface InputMenuItem {
|
||||
|
||||
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.
|
||||
* @defaultValue 'div'
|
||||
@@ -77,13 +77,6 @@ export interface InputMenuProps<T extends MaybeArrayOfArrayItem<I>, I extends Ma
|
||||
* @defaultValue true
|
||||
*/
|
||||
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.
|
||||
* @defaultValue undefined
|
||||
@@ -95,33 +88,45 @@ export interface InputMenuProps<T extends MaybeArrayOfArrayItem<I>, I extends Ma
|
||||
*/
|
||||
labelKey?: V
|
||||
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?: boolean
|
||||
/**
|
||||
* Determines if custom user input that does not exist in options can be added.
|
||||
* @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
|
||||
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'> & {
|
||||
change: [payload: Event]
|
||||
blur: [payload: FocusEvent]
|
||||
focus: [payload: FocusEvent]
|
||||
create: [payload: Event, item: T]
|
||||
create: [item: string]
|
||||
} & SelectModelValueEmits<T, V, M>
|
||||
|
||||
type SlotProps<T> = (props: { item: T, index: number }) => any
|
||||
|
||||
export interface InputMenuSlots<T> {
|
||||
'leading'(props: { modelValue: T, open: boolean, ui: any }): any
|
||||
'trailing'(props: { modelValue: T, open: boolean, ui: any }): any
|
||||
export interface InputMenuSlots<T, M extends boolean> {
|
||||
'leading'(props: { modelValue?: M extends true ? T[] : T, open: boolean, ui: any }): any
|
||||
'trailing'(props: { modelValue?: M extends true ? T[] : T, open: boolean, ui: any }): any
|
||||
'empty'(props: { searchTerm?: string }): any
|
||||
'item': SlotProps<T>
|
||||
'item-leading': SlotProps<T>
|
||||
@@ -129,24 +134,23 @@ export interface InputMenuSlots<T> {
|
||||
'item-trailing': SlotProps<T>
|
||||
'tags-item-text': 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'] } })
|
||||
</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 { 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 { isEqual } from 'ohash'
|
||||
import { reactivePick, createReusableTemplate } from '@vueuse/core'
|
||||
import { useAppConfig } from '#imports'
|
||||
import { useButtonGroup } from '../composables/useButtonGroup'
|
||||
import { useComponentIcons } from '../composables/useComponentIcons'
|
||||
import { useFormField } from '../composables/useFormField'
|
||||
import { useLocale } from '../composables/useLocale'
|
||||
import { get, escapeRegExp } from '../utils'
|
||||
import { get, compare } from '../utils'
|
||||
import UIcon from './Icon.vue'
|
||||
import UAvatar from './Avatar.vue'
|
||||
import UChip from './Chip.vue'
|
||||
@@ -157,17 +161,18 @@ const props = withDefaults(defineProps<InputMenuProps<T, I, V, M>>(), {
|
||||
type: 'text',
|
||||
autofocusDelay: 0,
|
||||
portal: true,
|
||||
filter: true,
|
||||
labelKey: 'label' as never
|
||||
})
|
||||
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 appConfig = useAppConfig()
|
||||
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 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 { 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 [DefineCreateItemTemplate, ReuseCreateItemTemplate] = createReusableTemplate()
|
||||
|
||||
const ui = computed(() => inputMenu({
|
||||
color: color.value,
|
||||
variant: props.variant,
|
||||
@@ -196,69 +201,49 @@ function displayValue(value: T): string {
|
||||
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)
|
||||
}
|
||||
|
||||
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[][] : [])
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
const items = computed(() => groups.value.flatMap(group => group) as T[])
|
||||
|
||||
const creatable = computed(() => {
|
||||
if (!props.createItem) {
|
||||
const filteredGroups = computed(() => {
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
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'
|
||||
}
|
||||
const newItem = props.valueKey ? { [props.valueKey]: searchTerm.value } as T : searchTerm.value
|
||||
|
||||
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 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 createItemPosition = computed(() => typeof props.createItem === 'object' ? props.createItem.position : 'bottom')
|
||||
|
||||
const inputRef = ref<InstanceType<typeof ComboboxInput> | null>(null)
|
||||
|
||||
@@ -310,17 +295,18 @@ defineExpose({
|
||||
})
|
||||
</script>
|
||||
|
||||
<!-- eslint-disable vue/no-template-shadow -->
|
||||
<template>
|
||||
<DefineCreateItemTemplate>
|
||||
<ComboboxGroup v-if="creatable" :class="ui.group({ class: props.ui?.group })">
|
||||
<ComboboxGroup :class="ui.group({ class: props.ui?.group })">
|
||||
<ComboboxItem
|
||||
:class="ui.item({ class: props.ui?.item })"
|
||||
:value="valueKey && typeof creatable.item === 'object' ? get(creatable.item, props.valueKey as string) : creatable.item"
|
||||
@select="e => emits('create', e, (creatable as any).item as T)"
|
||||
:value="searchTerm"
|
||||
@select.prevent="emits('create', searchTerm)"
|
||||
>
|
||||
<span :class="ui.itemLabel({ class: props.ui?.itemLabel })">
|
||||
<slot name="create-item-label" :item="(creatable.item as T)">
|
||||
{{ t('inputMenu.create', { label: typeof creatable.item === 'object' ? get(creatable.item, props.labelKey as string) : creatable.item }) }}
|
||||
<slot name="create-item-label" :item="searchTerm">
|
||||
{{ t('inputMenu.create', { label: searchTerm }) }}
|
||||
</slot>
|
||||
</span>
|
||||
</ComboboxItem>
|
||||
@@ -331,13 +317,11 @@ defineExpose({
|
||||
:id="id"
|
||||
v-slot="{ modelValue, open }"
|
||||
v-bind="rootProps"
|
||||
v-model:search-term="searchTerm"
|
||||
:name="name"
|
||||
:disabled="disabled"
|
||||
:display-value="displayValue"
|
||||
:filter-function="() => rootItems"
|
||||
:class="ui.root({ class: [props.class, props.ui?.root] })"
|
||||
:as-child="!!multiple"
|
||||
ignore-filter
|
||||
@update:model-value="onUpdate"
|
||||
@update:open="onUpdateOpen"
|
||||
@keydown.enter="$event.preventDefault()"
|
||||
@@ -345,7 +329,7 @@ defineExpose({
|
||||
<ComboboxAnchor :as-child="!multiple" :class="ui.base({ class: props.ui?.base })">
|
||||
<TagsInputRoot
|
||||
v-if="multiple"
|
||||
v-slot="{ modelValue: tags }: { modelValue: AcceptableValue[] }"
|
||||
v-slot="{ modelValue: tags }"
|
||||
:model-value="(modelValue as string[])"
|
||||
:disabled="disabled"
|
||||
delimiter=""
|
||||
@@ -367,7 +351,7 @@ defineExpose({
|
||||
</TagsInputItemDelete>
|
||||
</TagsInputItem>
|
||||
|
||||
<ComboboxInput as-child>
|
||||
<ComboboxInput v-model="searchTerm" :display-value="displayValue" as-child>
|
||||
<TagsInputInput
|
||||
ref="inputRef"
|
||||
v-bind="$attrs"
|
||||
@@ -382,24 +366,25 @@ defineExpose({
|
||||
<ComboboxInput
|
||||
v-else
|
||||
ref="inputRef"
|
||||
v-model="searchTerm"
|
||||
:display-value="displayValue"
|
||||
v-bind="$attrs"
|
||||
:type="type"
|
||||
:placeholder="placeholder"
|
||||
:required="required"
|
||||
:class="ui.base({ class: props.ui?.base })"
|
||||
@blur="onBlur"
|
||||
@focus="onFocus"
|
||||
/>
|
||||
|
||||
<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 })" />
|
||||
<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>
|
||||
</span>
|
||||
|
||||
<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 })" />
|
||||
</slot>
|
||||
</ComboboxTrigger>
|
||||
@@ -414,9 +399,9 @@ defineExpose({
|
||||
</ComboboxEmpty>
|
||||
|
||||
<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}`">
|
||||
<ComboboxLabel v-if="item?.type === 'label'" :class="ui.label({ class: props.ui?.label })">
|
||||
{{ get(item, props.labelKey as string) }}
|
||||
@@ -463,7 +448,7 @@ defineExpose({
|
||||
</template>
|
||||
</ComboboxGroup>
|
||||
|
||||
<ReuseCreateItemTemplate v-if="creatable && creatable.position === 'bottom'" />
|
||||
<ReuseCreateItemTemplate v-if="createItem && createItemPosition === 'bottom'" />
|
||||
</ComboboxViewport>
|
||||
|
||||
<ComboboxArrow v-if="!!arrow" v-bind="arrowProps" :class="ui.arrow({ class: props.ui?.arrow })" />
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
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 _appConfig from '#build/app.config'
|
||||
import theme from '#build/ui/input-number'
|
||||
@@ -75,7 +75,7 @@ export interface InputNumberSlots {
|
||||
|
||||
<script setup lang="ts">
|
||||
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 { useFormField } from '../composables/useFormField'
|
||||
import { useLocale } from '../composables/useLocale'
|
||||
|
||||
@@ -31,7 +31,7 @@ extendDevtoolsMeta({ defaultProps: { value: 'K' } })
|
||||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Primitive } from 'radix-vue'
|
||||
import { Primitive } from 'reka-ui'
|
||||
import { useKbd } from '../composables/useKbd'
|
||||
|
||||
const props = withDefaults(defineProps<KbdProps>(), {
|
||||
|
||||
@@ -95,7 +95,7 @@ extendDevtoolsMeta({ example: 'LinkExample' })
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { isEqual, diff } from 'ohash'
|
||||
import { useForwardProps } from 'radix-vue'
|
||||
import { useForwardProps } from 'reka-ui'
|
||||
import { reactiveOmit } from '@vueuse/core'
|
||||
import { useRoute } from '#imports'
|
||||
import ULinkBase from './LinkBase.vue'
|
||||
|
||||
@@ -13,7 +13,7 @@ export interface LinkBaseProps {
|
||||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Primitive } from 'radix-vue'
|
||||
import { Primitive } from 'reka-ui'
|
||||
|
||||
const props = withDefaults(defineProps<LinkBaseProps>(), {
|
||||
as: 'button',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
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 _appConfig from '#build/app.config'
|
||||
import theme from '#build/ui/modal'
|
||||
@@ -74,7 +74,7 @@ extendDevtoolsMeta({ example: 'ModalExample' })
|
||||
|
||||
<script setup lang="ts">
|
||||
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 { useAppConfig } from '#imports'
|
||||
import { useLocale } from '../composables/useLocale'
|
||||
@@ -90,6 +90,9 @@ const props = withDefaults(defineProps<ModalProps>(), {
|
||||
const emits = defineEmits<ModalEmits>()
|
||||
const slots = defineSlots<ModalSlots>()
|
||||
|
||||
const { t } = useLocale()
|
||||
const appConfig = useAppConfig()
|
||||
|
||||
const rootProps = useForwardPropsEmits(reactivePick(props, 'open', 'defaultOpen', 'modal'), emits)
|
||||
const contentProps = toRef(() => props.content)
|
||||
const contentEvents = computed(() => {
|
||||
@@ -110,9 +113,6 @@ const contentEvents = computed(() => {
|
||||
}
|
||||
})
|
||||
|
||||
const appConfig = useAppConfig()
|
||||
const { t } = useLocale()
|
||||
|
||||
const ui = computed(() => modal({
|
||||
transition: props.transition,
|
||||
fullscreen: props.fullscreen
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
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 _appConfig from '#build/app.config'
|
||||
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>
|
||||
|
||||
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.
|
||||
* @defaultValue 'div'
|
||||
@@ -122,8 +122,8 @@ extendDevtoolsMeta({
|
||||
</script>
|
||||
|
||||
<script setup lang="ts" generic="T extends MaybeArrayOfArrayItem<I>, I extends MaybeArrayOfArray<NavigationMenuItem>">
|
||||
import { computed, reactive, toRef } from 'vue'
|
||||
import { NavigationMenuRoot, NavigationMenuList, NavigationMenuItem, NavigationMenuTrigger, NavigationMenuContent, NavigationMenuLink, NavigationMenuIndicator, NavigationMenuViewport, useForwardPropsEmits } from 'radix-vue'
|
||||
import { computed, toRef } from 'vue'
|
||||
import { NavigationMenuRoot, NavigationMenuList, NavigationMenuItem, NavigationMenuTrigger, NavigationMenuContent, NavigationMenuLink, NavigationMenuIndicator, NavigationMenuViewport, useForwardPropsEmits } from 'reka-ui'
|
||||
import { createReusableTemplate } from '@vueuse/core'
|
||||
import { get } from '../utils'
|
||||
import { pickLinkProps } from '../utils/link'
|
||||
@@ -137,19 +137,24 @@ import UCollapsible from './Collapsible.vue'
|
||||
const props = withDefaults(defineProps<NavigationMenuProps<I>>(), {
|
||||
orientation: 'horizontal',
|
||||
delayDuration: 0,
|
||||
labelKey: 'label'
|
||||
labelKey: 'label',
|
||||
unmountOnHide: true
|
||||
})
|
||||
const emits = defineEmits<NavigationMenuEmits>()
|
||||
const slots = defineSlots<NavigationMenuSlots<T>>()
|
||||
|
||||
const rootProps = useForwardPropsEmits(reactive({
|
||||
const rootProps = useForwardPropsEmits(computed(() => ({
|
||||
as: props.as,
|
||||
modelValue: props.modelValue,
|
||||
defaultValue: props.defaultValue,
|
||||
delayDuration: props.delayDuration,
|
||||
skipDelayDuration: props.skipDelayDuration,
|
||||
orientation: props.orientation
|
||||
}), emits)
|
||||
orientation: props.orientation,
|
||||
disableClickTrigger: props.disableClickTrigger,
|
||||
disableHoverTrigger: props.disableHoverTrigger,
|
||||
disablePointerLeaveClose: props.disablePointerLeaveClose,
|
||||
unmountOnHide: props.unmountOnHide
|
||||
})), emits)
|
||||
|
||||
const contentProps = toRef(() => props.content)
|
||||
|
||||
@@ -199,7 +204,7 @@ const lists = computed(() => props.items?.length ? (Array.isArray(props.items[0]
|
||||
</slot>
|
||||
</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}`">
|
||||
<NavigationMenuList :class="ui.list({ class: props.ui?.list })">
|
||||
<component
|
||||
@@ -209,6 +214,7 @@ const lists = computed(() => props.items?.length ? (Array.isArray(props.items[0]
|
||||
as="li"
|
||||
:value="item.value || String(index)"
|
||||
:default-open="item.defaultOpen"
|
||||
:unmount-on-hide="(item.children?.length && orientation === 'vertical') ? unmountOnHide : undefined"
|
||||
:open="item.open"
|
||||
:class="ui.item({ class: props.ui?.item })"
|
||||
>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
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 { RouteLocationRaw } from '#vue-router'
|
||||
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 || {}) })
|
||||
|
||||
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.
|
||||
* @defaultValue 'div'
|
||||
@@ -104,7 +104,7 @@ extendDevtoolsMeta({ defaultProps: { total: 50 } })
|
||||
|
||||
<script setup lang="ts">
|
||||
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 { useAppConfig } from '#imports'
|
||||
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