feat(NavigationMenu): add trigger type in items

This commit is contained in:
Benjamin Canac
2025-05-22 12:50:29 +02:00
parent 02363994d6
commit 9cf9f25f44
2 changed files with 14 additions and 12 deletions

View File

@@ -47,6 +47,7 @@ const items = [
label: 'Components', label: 'Components',
icon: 'i-lucide-box', icon: 'i-lucide-box',
to: '/components/navigation-menu', to: '/components/navigation-menu',
type: 'trigger',
active: true, active: true,
defaultOpen: true, defaultOpen: true,
children: [{ children: [{

View File

@@ -42,10 +42,11 @@ export interface NavigationMenuItem extends Omit<LinkProps, 'type' | 'raw' | 'cu
trailingIcon?: string trailingIcon?: string
/** /**
* The type of the item. * The type of the item.
* The `label` type only works on `vertical` orientation. * The `label` type is only displayed in `vertical` orientation.
* The `trigger` type is used to force the item to be collapsible when its a link in `vertical` orientation.
* @defaultValue 'link' * @defaultValue 'link'
*/ */
type?: 'label' | 'link' type?: 'label' | 'trigger' | 'link'
slot?: string slot?: string
/** /**
* The value of the item. Avoid using `index` as the value to prevent conflicts in horizontal orientation with Reka UI. * The value of the item. Avoid using `index` as the value to prevent conflicts in horizontal orientation with Reka UI.
@@ -300,21 +301,21 @@ function getAccordionDefaultValue(list: NavigationMenuItem[]) {
<div v-if="orientation === 'vertical' && item.type === 'label' && !collapsed" :class="ui.label({ class: [props.ui?.label, item.ui?.label, item.class] })"> <div v-if="orientation === 'vertical' && item.type === 'label' && !collapsed" :class="ui.label({ class: [props.ui?.label, item.ui?.label, item.class] })">
<ReuseLinkTemplate :item="item" :index="index" /> <ReuseLinkTemplate :item="item" :index="index" />
</div> </div>
<ULink v-else-if="item.type !== 'label'" v-slot="{ active, ...slotProps }" v-bind="pickLinkProps(item as Omit<NavigationMenuItem, 'type'>)" custom> <ULink v-else-if="item.type !== 'label'" v-slot="{ active, ...slotProps }" v-bind="(orientation === 'vertical' && item.children?.length && !collapsed && item.type === 'trigger') ? {} : pickLinkProps(item as Omit<NavigationMenuItem, 'type'>)" custom>
<component <component
:is="(orientation === 'horizontal' && (item.children?.length || !!slots[(item.slot ? `${item.slot}-content` : 'item-content') as keyof NavigationMenuSlots<T>])) ? NavigationMenuTrigger : ((orientation === 'vertical' && item.children?.length && !collapsed && !(slotProps as any).href) ? AccordionTrigger : NavigationMenuLink)" :is="(orientation === 'horizontal' && (item.children?.length || !!slots[(item.slot ? `${item.slot}-content` : 'item-content') as keyof NavigationMenuSlots<T>])) ? NavigationMenuTrigger : ((orientation === 'vertical' && item.children?.length && !collapsed && !(slotProps as any).href) ? AccordionTrigger : NavigationMenuLink)"
as-child as-child
:active="active" :active="active || item.active"
:disabled="item.disabled" :disabled="item.disabled"
@select="item.onSelect" @select="item.onSelect"
> >
<UPopover v-if="orientation === 'vertical' && collapsed && item.children?.length && (!!props.popover || !!item.popover)" v-bind="{ ...popoverProps, ...(typeof item.popover === 'boolean' ? {} : item.popover || {}) }" :ui="{ content: ui.content({ class: [props.ui?.content, item.ui?.content] }) }"> <UPopover v-if="orientation === 'vertical' && collapsed && item.children?.length && (!!props.popover || !!item.popover)" v-bind="{ ...popoverProps, ...(typeof item.popover === 'boolean' ? {} : item.popover || {}) }" :ui="{ content: ui.content({ class: [props.ui?.content, item.ui?.content] }) }">
<ULinkBase v-bind="slotProps" :class="ui.link({ class: [props.ui?.link, item.ui?.link, item.class], active, disabled: !!item.disabled, level: level > 0 })"> <ULinkBase v-bind="slotProps" :class="ui.link({ class: [props.ui?.link, item.ui?.link, item.class], active: active || item.active, disabled: !!item.disabled, level: level > 0 })">
<ReuseLinkTemplate :item="item" :active="active" :index="index" /> <ReuseLinkTemplate :item="item" :active="active || item.active" :index="index" />
</ULinkBase> </ULinkBase>
<template #content> <template #content>
<slot :name="((item.slot ? `${item.slot}-content` : 'item-content') as keyof NavigationMenuSlots<T>)" :item="item" :active="active" :index="index"> <slot :name="((item.slot ? `${item.slot}-content` : 'item-content') as keyof NavigationMenuSlots<T>)" :item="item" :active="active || item.active" :index="index">
<ul :class="ui.childList({ class: [props.ui?.childList, item.ui?.childList] })"> <ul :class="ui.childList({ class: [props.ui?.childList, item.ui?.childList] })">
<li :class="ui.childLabel({ class: [props.ui?.childLabel, item.ui?.childLabel] })"> <li :class="ui.childLabel({ class: [props.ui?.childLabel, item.ui?.childLabel] })">
{{ get(item, props.labelKey as string) }} {{ get(item, props.labelKey as string) }}
@@ -339,17 +340,17 @@ function getAccordionDefaultValue(list: NavigationMenuItem[]) {
</template> </template>
</UPopover> </UPopover>
<UTooltip v-else-if="orientation === 'vertical' && collapsed && (!!props.tooltip || !!item.tooltip)" :text="get(item, props.labelKey as string)" v-bind="{ ...tooltipProps, ...(typeof item.tooltip === 'boolean' ? {} : item.tooltip || {}) }"> <UTooltip v-else-if="orientation === 'vertical' && collapsed && (!!props.tooltip || !!item.tooltip)" :text="get(item, props.labelKey as string)" v-bind="{ ...tooltipProps, ...(typeof item.tooltip === 'boolean' ? {} : item.tooltip || {}) }">
<ULinkBase v-bind="slotProps" :class="ui.link({ class: [props.ui?.link, item.ui?.link, item.class], active, disabled: !!item.disabled, level: level > 0 })"> <ULinkBase v-bind="slotProps" :class="ui.link({ class: [props.ui?.link, item.ui?.link, item.class], active: active || item.active, disabled: !!item.disabled, level: level > 0 })">
<ReuseLinkTemplate :item="item" :active="active" :index="index" /> <ReuseLinkTemplate :item="item" :active="active || item.active" :index="index" />
</ULinkBase> </ULinkBase>
</UTooltip> </UTooltip>
<ULinkBase v-else v-bind="slotProps" :class="ui.link({ class: [props.ui?.link, item.ui?.link, item.class], active, disabled: !!item.disabled, level: orientation === 'horizontal' || level > 0 })"> <ULinkBase v-else v-bind="slotProps" :class="ui.link({ class: [props.ui?.link, item.ui?.link, item.class], active: active || item.active, disabled: !!item.disabled, level: orientation === 'horizontal' || level > 0 })">
<ReuseLinkTemplate :item="item" :active="active" :index="index" /> <ReuseLinkTemplate :item="item" :active="active || item.active" :index="index" />
</ULinkBase> </ULinkBase>
</component> </component>
<NavigationMenuContent v-if="orientation === 'horizontal' && (item.children?.length || !!slots[(item.slot ? `${item.slot}-content` : 'item-content') as keyof NavigationMenuSlots<T>])" v-bind="contentProps" :class="ui.content({ class: [props.ui?.content, item.ui?.content] })"> <NavigationMenuContent v-if="orientation === 'horizontal' && (item.children?.length || !!slots[(item.slot ? `${item.slot}-content` : 'item-content') as keyof NavigationMenuSlots<T>])" v-bind="contentProps" :class="ui.content({ class: [props.ui?.content, item.ui?.content] })">
<slot :name="((item.slot ? `${item.slot}-content` : 'item-content') as keyof NavigationMenuSlots<T>)" :item="item" :active="active" :index="index"> <slot :name="((item.slot ? `${item.slot}-content` : 'item-content') as keyof NavigationMenuSlots<T>)" :item="item" :active="active || item.active" :index="index">
<ul :class="ui.childList({ class: [props.ui?.childList, item.ui?.childList] })"> <ul :class="ui.childList({ class: [props.ui?.childList, item.ui?.childList] })">
<li v-for="(childItem, childIndex) in item.children" :key="childIndex" :class="ui.childItem({ class: [props.ui?.childItem, item.ui?.childItem] })"> <li v-for="(childItem, childIndex) in item.children" :key="childIndex" :class="ui.childItem({ class: [props.ui?.childItem, item.ui?.childItem] })">
<ULink v-slot="{ active: childActive, ...childSlotProps }" v-bind="pickLinkProps(childItem)" custom> <ULink v-slot="{ active: childActive, ...childSlotProps }" v-bind="pickLinkProps(childItem)" custom>