mirror of
https://github.com/ArthurDanjou/ui.git
synced 2026-01-14 12:14:41 +01:00
feat(DropdownMenu): pass index to slots
This commit is contained in:
@@ -41,7 +41,7 @@ export interface DropdownMenuProps<T> extends Omit<DropdownMenuRootProps, 'dir'>
|
||||
|
||||
export interface DropdownMenuEmits extends DropdownMenuRootEmits {}
|
||||
|
||||
type SlotProps<T> = (props: { item: T, active?: boolean }) => any
|
||||
type SlotProps<T> = (props: { item: T, active?: boolean, index: number }) => any
|
||||
|
||||
export interface DropdownMenuSlots<T> {
|
||||
default(): any
|
||||
@@ -59,7 +59,10 @@ import { reactivePick } from '@vueuse/core'
|
||||
import { UDropdownMenuContent } from '#components'
|
||||
import { omit } from '#ui/utils'
|
||||
|
||||
const props = withDefaults(defineProps<DropdownMenuProps<T>>(), { portal: true })
|
||||
const props = withDefaults(defineProps<DropdownMenuProps<T>>(), {
|
||||
portal: true,
|
||||
modal: false
|
||||
})
|
||||
const emits = defineEmits<DropdownMenuEmits>()
|
||||
const slots = defineSlots<DropdownMenuSlots<T>>()
|
||||
|
||||
|
||||
@@ -38,20 +38,20 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DefineItemTemplate v-slot="{ item, active }">
|
||||
<slot name="leading" :item="item" :active="active">
|
||||
<DefineItemTemplate v-slot="{ item, active, index }">
|
||||
<slot name="leading" :item="item" :active="active" :index="index">
|
||||
<UAvatar v-if="item.avatar" size="2xs" v-bind="item.avatar" :class="ui.itemLeadingAvatar({ active })" />
|
||||
<UIcon v-else-if="item.icon" :name="item.icon" :class="ui.itemLeadingIcon({ active })" />
|
||||
</slot>
|
||||
|
||||
<span v-if="item.label || $slots.default" :class="ui.itemLabel()">
|
||||
<slot name="label" :item="item" :active="active">
|
||||
<slot name="label" :item="item" :active="active" :index="index">
|
||||
{{ item.label }}
|
||||
</slot>
|
||||
</span>
|
||||
|
||||
<span v-if="$slots.trailing || item.children?.length || item.shortcuts?.length" :class="ui.itemTrailing()">
|
||||
<slot name="trailing" :item="item" :active="active">
|
||||
<slot name="trailing" :item="item" :active="active" :index="index">
|
||||
<UIcon v-if="item.children?.length" :name="appConfig.ui.icons.chevronRight" :class="ui.itemTrailingIcon()" />
|
||||
<span v-else-if="item.shortcuts?.length" :class="ui.itemTrailingShortcuts()">
|
||||
<UKbd v-for="(shortcut, shortcutIndex) in item.shortcuts" :key="shortcutIndex" size="md" v-bind="typeof shortcut === 'string' ? { value: shortcut } : shortcut" />
|
||||
@@ -62,10 +62,10 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
|
||||
|
||||
<DropdownMenu.Portal :disabled="!portal">
|
||||
<component :is="sub ? DropdownMenu.SubContent : DropdownMenu.Content" :class="props.class" v-bind="contentProps">
|
||||
<DropdownMenu.Group v-for="(group, index) in groups" :key="`group-${index}`" :class="ui.group()">
|
||||
<template v-for="(item, itemIndex) in group" :key="`group-${index}-${itemIndex}`">
|
||||
<DropdownMenu.Group v-for="(group, groupIndex) in groups" :key="`group-${groupIndex}`" :class="ui.group()">
|
||||
<template v-for="(item, index) in group" :key="`group-${groupIndex}-${index}`">
|
||||
<DropdownMenu.Label v-if="item.type === 'label'" :class="ui.label()">
|
||||
<ReuseItemTemplate :item="item" />
|
||||
<ReuseItemTemplate :item="item" :index="index" />
|
||||
</DropdownMenu.Label>
|
||||
<DropdownMenu.Sub v-else-if="item?.children?.length">
|
||||
<DropdownMenu.SubTrigger
|
||||
@@ -77,7 +77,7 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
|
||||
:text-value="item.label"
|
||||
:class="ui.item()"
|
||||
>
|
||||
<ReuseItemTemplate :item="item" />
|
||||
<ReuseItemTemplate :item="item" :index="index" />
|
||||
</DropdownMenu.SubTrigger>
|
||||
|
||||
<UDropdownMenuContent
|
||||
@@ -100,7 +100,7 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
|
||||
<DropdownMenu.Item v-else as-child :disabled="item.disabled" :text-value="item.label" @select="item.select">
|
||||
<ULink v-slot="{ active, ...slotProps }" v-bind="omit((item as DropdownMenuItem), ['label', 'icon', 'avatar', 'shortcuts', 'slot', 'open', 'defaultOpen', 'select', 'children', 'type'])" custom>
|
||||
<ULinkBase v-bind="slotProps" :class="ui.item({ active })">
|
||||
<ReuseItemTemplate :item="item" :active="active" />
|
||||
<ReuseItemTemplate :item="item" :active="active" :index="index" />
|
||||
</ULinkBase>
|
||||
</ULink>
|
||||
</DropdownMenu.Item>
|
||||
|
||||
@@ -7,7 +7,7 @@ exports[`DropdownMenu > renders with class correctly 1`] = `
|
||||
|
||||
|
||||
<div data-radix-popper-content-wrapper="" style="position: fixed; left: 0px; top: 0px; transform: translate(0, -200%); min-width: max-content;">
|
||||
<div role="menu" aria-orientation="vertical" data-radix-menu-content="" data-state="open" dir="ltr" tabindex="-1" data-orientation="vertical" style="outline-color: none; outline-style: none; outline-width: initial; pointer-events: auto; --radix-dropdown-menu-content-transform-origin: var(--radix-popper-transform-origin); --radix-dropdown-menu-content-available-width: var(--radix-popper-available-width); --radix-dropdown-menu-content-available-height: var(--radix-popper-available-height); --radix-dropdown-menu-trigger-width: var(--radix-popper-anchor-width); --radix-dropdown-menu-trigger-height: var(--radix-popper-anchor-height); animation: none;" data-dismissable-layer="" id="radix-vue-dropdown-menu-content-2" aria-labelledby="" class="bg-white dark:bg-gray-900 shadow-lg rounded-md ring ring-gray-200 dark:ring-gray-800 divide-y divide-gray-200 dark:divide-gray-800 overflow-y-auto scroll-py-1 data-[state=open]:animate-[scale-in_100ms_ease-out] data-[state=closed]:animate-[scale-out_100ms_ease-in] min-w-96" data-side="bottom" data-align="center">
|
||||
<div role="menu" aria-orientation="vertical" data-radix-menu-content="" data-state="open" dir="ltr" tabindex="-1" data-orientation="vertical" style="outline-color: none; outline-style: none; outline-width: initial; --radix-dropdown-menu-content-transform-origin: var(--radix-popper-transform-origin); --radix-dropdown-menu-content-available-width: var(--radix-popper-available-width); --radix-dropdown-menu-content-available-height: var(--radix-popper-available-height); --radix-dropdown-menu-trigger-width: var(--radix-popper-anchor-width); --radix-dropdown-menu-trigger-height: var(--radix-popper-anchor-height); animation: none;" data-dismissable-layer="" id="radix-vue-dropdown-menu-content-2" aria-labelledby="" class="bg-white dark:bg-gray-900 shadow-lg rounded-md ring ring-gray-200 dark:ring-gray-800 divide-y divide-gray-200 dark:divide-gray-800 overflow-y-auto scroll-py-1 data-[state=open]:animate-[scale-in_100ms_ease-out] data-[state=closed]:animate-[scale-out_100ms_ease-in] min-w-96" data-side="bottom" data-align="center">
|
||||
<div role="group" class="p-1 isolate"><button type="button" role="menuitem" tabindex="-1" data-radix-vue-collection-item="" class="group relative w-full flex items-center gap-1.5 p-1.5 text-sm select-none outline-none before:absolute before:z-[-1] before:inset-px before:rounded-md data-disabled:cursor-not-allowed data-disabled:opacity-75 text-gray-700 dark:text-gray-200 data-highlighted:text-gray-900 dark:data-highlighted:text-white data-[state=open]:text-gray-900 dark:data-[state=open]:text-white data-highlighted:before:bg-gray-50 dark:data-highlighted:before:bg-gray-800/50 data-[state=open]:before:bg-gray-50 dark:data-[state=open]:before:bg-gray-800/50"><span class="inline-flex items-center justify-center select-none overflow-hidden rounded-full align-middle bg-gray-100 dark:bg-gray-800 size-5 text-[10px] shrink-0"><img role="img" src="https://avatars.githubusercontent.com/u/739984?v=4" class="h-full w-full rounded-[inherit] object-cover" style="display: none;"><span class="font-medium leading-none text-gray-500 dark:text-gray-400 truncate"></span></span><span class="truncate">Profile</span>
|
||||
<!--v-if-->
|
||||
</button><button type="button" role="menuitem" tabindex="-1" data-radix-vue-collection-item="" class="group relative w-full flex items-center gap-1.5 p-1.5 text-sm select-none outline-none before:absolute before:z-[-1] before:inset-px before:rounded-md data-disabled:cursor-not-allowed data-disabled:opacity-75 text-gray-700 dark:text-gray-200 data-highlighted:text-gray-900 dark:data-highlighted:text-white data-[state=open]:text-gray-900 dark:data-[state=open]:text-white data-highlighted:before:bg-gray-50 dark:data-highlighted:before:bg-gray-800/50 data-[state=open]:before:bg-gray-50 dark:data-[state=open]:before:bg-gray-800/50">
|
||||
@@ -30,7 +30,7 @@ exports[`DropdownMenu > renders with items correctly 1`] = `
|
||||
|
||||
|
||||
<div data-radix-popper-content-wrapper="" style="position: fixed; left: 0px; top: 0px; transform: translate(0, -200%); min-width: max-content;">
|
||||
<div role="menu" aria-orientation="vertical" data-radix-menu-content="" data-state="open" dir="ltr" tabindex="-1" data-orientation="vertical" style="outline-color: none; outline-style: none; outline-width: initial; --radix-dropdown-menu-content-transform-origin: var(--radix-popper-transform-origin); --radix-dropdown-menu-content-available-width: var(--radix-popper-available-width); --radix-dropdown-menu-content-available-height: var(--radix-popper-available-height); --radix-dropdown-menu-trigger-width: var(--radix-popper-anchor-width); --radix-dropdown-menu-trigger-height: var(--radix-popper-anchor-height); animation: none; pointer-events: auto;" data-dismissable-layer="" id="radix-vue-dropdown-menu-content-1" aria-labelledby="" class="min-w-48 bg-white dark:bg-gray-900 shadow-lg rounded-md ring ring-gray-200 dark:ring-gray-800 divide-y divide-gray-200 dark:divide-gray-800 overflow-y-auto scroll-py-1 data-[state=open]:animate-[scale-in_100ms_ease-out] data-[state=closed]:animate-[scale-out_100ms_ease-in]" data-side="bottom" data-align="center">
|
||||
<div role="menu" aria-orientation="vertical" data-radix-menu-content="" data-state="open" dir="ltr" tabindex="-1" data-orientation="vertical" style="outline-color: none; outline-style: none; outline-width: initial; --radix-dropdown-menu-content-transform-origin: var(--radix-popper-transform-origin); --radix-dropdown-menu-content-available-width: var(--radix-popper-available-width); --radix-dropdown-menu-content-available-height: var(--radix-popper-available-height); --radix-dropdown-menu-trigger-width: var(--radix-popper-anchor-width); --radix-dropdown-menu-trigger-height: var(--radix-popper-anchor-height); animation: none;" data-dismissable-layer="" id="radix-vue-dropdown-menu-content-1" aria-labelledby="" class="min-w-48 bg-white dark:bg-gray-900 shadow-lg rounded-md ring ring-gray-200 dark:ring-gray-800 divide-y divide-gray-200 dark:divide-gray-800 overflow-y-auto scroll-py-1 data-[state=open]:animate-[scale-in_100ms_ease-out] data-[state=closed]:animate-[scale-out_100ms_ease-in]" data-side="bottom" data-align="center">
|
||||
<div role="group" class="p-1 isolate"><button type="button" role="menuitem" tabindex="-1" data-radix-vue-collection-item="" class="group relative w-full flex items-center gap-1.5 p-1.5 text-sm select-none outline-none before:absolute before:z-[-1] before:inset-px before:rounded-md data-disabled:cursor-not-allowed data-disabled:opacity-75 text-gray-700 dark:text-gray-200 data-highlighted:text-gray-900 dark:data-highlighted:text-white data-[state=open]:text-gray-900 dark:data-[state=open]:text-white data-highlighted:before:bg-gray-50 dark:data-highlighted:before:bg-gray-800/50 data-[state=open]:before:bg-gray-50 dark:data-[state=open]:before:bg-gray-800/50"><span class="inline-flex items-center justify-center select-none overflow-hidden rounded-full align-middle bg-gray-100 dark:bg-gray-800 size-5 text-[10px] shrink-0"><img role="img" src="https://avatars.githubusercontent.com/u/739984?v=4" class="h-full w-full rounded-[inherit] object-cover" style="display: none;"><span class="font-medium leading-none text-gray-500 dark:text-gray-400 truncate"></span></span><span class="truncate">Profile</span>
|
||||
<!--v-if-->
|
||||
</button><button type="button" role="menuitem" tabindex="-1" data-radix-vue-collection-item="" class="group relative w-full flex items-center gap-1.5 p-1.5 text-sm select-none outline-none before:absolute before:z-[-1] before:inset-px before:rounded-md data-disabled:cursor-not-allowed data-disabled:opacity-75 text-gray-700 dark:text-gray-200 data-highlighted:text-gray-900 dark:data-highlighted:text-white data-[state=open]:text-gray-900 dark:data-[state=open]:text-white data-highlighted:before:bg-gray-50 dark:data-highlighted:before:bg-gray-800/50 data-[state=open]:before:bg-gray-50 dark:data-[state=open]:before:bg-gray-800/50">
|
||||
@@ -53,7 +53,7 @@ exports[`DropdownMenu > renders with ui correctly 1`] = `
|
||||
|
||||
|
||||
<div data-radix-popper-content-wrapper="" style="position: fixed; left: 0px; top: 0px; transform: translate(0, -200%); min-width: max-content;">
|
||||
<div role="menu" aria-orientation="vertical" data-radix-menu-content="" data-state="open" dir="ltr" tabindex="-1" data-orientation="vertical" style="outline-color: none; outline-style: none; outline-width: initial; pointer-events: auto; --radix-dropdown-menu-content-transform-origin: var(--radix-popper-transform-origin); --radix-dropdown-menu-content-available-width: var(--radix-popper-available-width); --radix-dropdown-menu-content-available-height: var(--radix-popper-available-height); --radix-dropdown-menu-trigger-width: var(--radix-popper-anchor-width); --radix-dropdown-menu-trigger-height: var(--radix-popper-anchor-height); animation: none;" data-dismissable-layer="" id="radix-vue-dropdown-menu-content-3" aria-labelledby="" class="min-w-48 bg-white dark:bg-gray-900 shadow-lg rounded-md ring ring-gray-200 dark:ring-gray-800 divide-y divide-gray-200 dark:divide-gray-800 overflow-y-auto scroll-py-1 data-[state=open]:animate-[scale-in_100ms_ease-out] data-[state=closed]:animate-[scale-out_100ms_ease-in]" data-side="bottom" data-align="center">
|
||||
<div role="menu" aria-orientation="vertical" data-radix-menu-content="" data-state="open" dir="ltr" tabindex="-1" data-orientation="vertical" style="outline-color: none; outline-style: none; outline-width: initial; --radix-dropdown-menu-content-transform-origin: var(--radix-popper-transform-origin); --radix-dropdown-menu-content-available-width: var(--radix-popper-available-width); --radix-dropdown-menu-content-available-height: var(--radix-popper-available-height); --radix-dropdown-menu-trigger-width: var(--radix-popper-anchor-width); --radix-dropdown-menu-trigger-height: var(--radix-popper-anchor-height); animation: none;" data-dismissable-layer="" id="radix-vue-dropdown-menu-content-3" aria-labelledby="" class="min-w-48 bg-white dark:bg-gray-900 shadow-lg rounded-md ring ring-gray-200 dark:ring-gray-800 divide-y divide-gray-200 dark:divide-gray-800 overflow-y-auto scroll-py-1 data-[state=open]:animate-[scale-in_100ms_ease-out] data-[state=closed]:animate-[scale-out_100ms_ease-in]" data-side="bottom" data-align="center">
|
||||
<div role="group" class="p-1 isolate"><button type="button" role="menuitem" tabindex="-1" data-radix-vue-collection-item="" class="group relative w-full flex items-center gap-1.5 p-1.5 text-sm select-none outline-none before:absolute before:z-[-1] before:inset-px before:rounded-md data-disabled:cursor-not-allowed data-disabled:opacity-75 text-gray-700 dark:text-gray-200 data-highlighted:text-gray-900 dark:data-highlighted:text-white data-[state=open]:text-gray-900 dark:data-[state=open]:text-white data-highlighted:before:bg-gray-50 dark:data-highlighted:before:bg-gray-800/50 data-[state=open]:before:bg-gray-50 dark:data-[state=open]:before:bg-gray-800/50"><span class="inline-flex items-center justify-center select-none overflow-hidden rounded-full align-middle bg-gray-100 dark:bg-gray-800 size-5 text-[10px] shrink-0"><img role="img" src="https://avatars.githubusercontent.com/u/739984?v=4" class="h-full w-full rounded-[inherit] object-cover" style="display: none;"><span class="font-medium leading-none text-gray-500 dark:text-gray-400 truncate"></span></span><span class="truncate">Profile</span>
|
||||
<!--v-if-->
|
||||
</button><button type="button" role="menuitem" tabindex="-1" data-radix-vue-collection-item="" class="group relative w-full flex items-center gap-1.5 p-1.5 text-sm select-none outline-none before:absolute before:z-[-1] before:inset-px before:rounded-md data-disabled:cursor-not-allowed data-disabled:opacity-75 text-gray-700 dark:text-gray-200 data-highlighted:text-gray-900 dark:data-highlighted:text-white data-[state=open]:text-gray-900 dark:data-[state=open]:text-white data-highlighted:before:bg-gray-50 dark:data-highlighted:before:bg-gray-800/50 data-[state=open]:before:bg-gray-50 dark:data-[state=open]:before:bg-gray-800/50">
|
||||
|
||||
Reference in New Issue
Block a user