fix(NavigationMenu): proxy `modelValue` / `defaultValue` in vertical … · nuxt/ui@cffaaaa

11

<!-- eslint-disable vue/block-tag-newline -->

22

<script lang="ts">

3-

import type { NavigationMenuRootProps, NavigationMenuRootEmits, NavigationMenuContentProps, NavigationMenuContentEmits, AccordionRootProps } from 'reka-ui'

3+

import type { NavigationMenuRootProps, NavigationMenuContentProps, NavigationMenuContentEmits, AccordionRootProps } from 'reka-ui'

44

import type { AppConfig } from '@nuxt/schema'

55

import theme from '#build/ui/navigation-menu'

66

import type { AvatarProps, BadgeProps, IconProps, LinkProps, PopoverProps, TooltipProps } from '../types'

@@ -63,12 +63,49 @@ export interface NavigationMenuItem extends Omit<LinkProps, 'type' | 'raw' | 'cu

6363

[key: string]: any

6464

}

656566-

export interface NavigationMenuProps<T extends ArrayOrNested<NavigationMenuItem> = ArrayOrNested<NavigationMenuItem>> extends Pick<NavigationMenuRootProps, 'modelValue' | 'defaultValue' | 'delayDuration' | 'disableClickTrigger' | 'disableHoverTrigger' | 'skipDelayDuration' | 'disablePointerLeaveClose' | 'unmountOnHide'>, Pick<AccordionRootProps, 'disabled' | 'type' | 'collapsible'> {

66+

type SingleOrMultipleType = 'single' | 'multiple'

67+

type Orientation = NavigationMenuRootProps['orientation']

68+69+

type NavigationMenuModelValue<

70+

K extends SingleOrMultipleType = SingleOrMultipleType,

71+

O extends Orientation = Orientation

72+

> = O extends 'horizontal' ? string : K extends 'single' ? string : K extends 'multiple' ? string[] : string | string[]

73+74+

export interface NavigationMenuProps<

75+

T extends ArrayOrNested<NavigationMenuItem> = ArrayOrNested<NavigationMenuItem>,

76+

K extends SingleOrMultipleType = SingleOrMultipleType,

77+

O extends Orientation = Orientation

78+

> extends Pick<NavigationMenuRootProps, 'delayDuration' | 'disableClickTrigger' | 'disableHoverTrigger' | 'skipDelayDuration' | 'disablePointerLeaveClose' | 'unmountOnHide'>, Pick<AccordionRootProps, 'disabled' | 'collapsible'> {

6779

/**

6880

* The element or component this component should render as.

6981

* @defaultValue 'div'

7082

*/

7183

as?: any

84+

/**

85+

* Determines whether a "single" or "multiple" items can be selected at a time.

86+

*

87+

* Only works when `orientation` is `vertical`.

88+

* @defaultValue 'multiple'

89+

*/

90+

type?: K

91+

/**

92+

* The controlled value of the active item(s).

93+

* - In horizontal orientation: always `string`

94+

* - In vertical orientation with `type="single"`: `string`

95+

* - In vertical orientation with `type="multiple"`: `string[]`

96+

*

97+

* Use this when you need to control the state of the items. Can be binded with `v-model`

98+

*/

99+

modelValue?: NavigationMenuModelValue<K, O>

100+

/**

101+

* The default active value of the item(s).

102+

* - In horizontal orientation: always `string`

103+

* - In vertical orientation with `type="single"`: `string`

104+

* - In vertical orientation with `type="multiple"`: `string[]`

105+

*

106+

* Use when you do not need to control the state of the item(s).

107+

*/

108+

defaultValue?: NavigationMenuModelValue<K, O>

72109

/**

73110

* The icon displayed to open the menu.

74111

* @defaultValue appConfig.ui.icons.chevronDown

@@ -95,7 +132,7 @@ export interface NavigationMenuProps<T extends ArrayOrNested<NavigationMenuItem>

95132

* The orientation of the menu.

96133

* @defaultValue 'horizontal'

97134

*/

98-

orientation?: NavigationMenuRootProps['orientation']

135+

orientation?: O

99136

/**

100137

* Collapse the navigation menu to only show icons.

101138

* Only works when `orientation` is `vertical`.

@@ -142,7 +179,18 @@ export interface NavigationMenuProps<T extends ArrayOrNested<NavigationMenuItem>

142179

ui?: NavigationMenu['slots']

143180

}

144181145-

export interface NavigationMenuEmits extends NavigationMenuRootEmits {}

182+

export type NavigationMenuEmits<

183+

K extends SingleOrMultipleType = SingleOrMultipleType,

184+

O extends Orientation = Orientation

185+

> = {

186+

/**

187+

* Event handler called when the value changes.

188+

* - In horizontal orientation: emits `string`

189+

* - In vertical orientation with `type="single"`: emits `string | undefined`

190+

* - In vertical orientation with `type="multiple"`: emits `string[] | undefined`

191+

*/

192+

'update:modelValue': [value: NavigationMenuModelValue<K, O> | undefined]

193+

}

146194147195

type SlotProps<T extends NavigationMenuItem> = (props: { item: T, index: number, active?: boolean, ui: NavigationMenu['ui'] }) => any

148196

@@ -163,7 +211,7 @@ export type NavigationMenuSlots<

163211164212

</script>

165213166-

<script setup lang="ts" generic="T extends ArrayOrNested<NavigationMenuItem>">

214+

<script setup lang="ts" generic="T extends ArrayOrNested<NavigationMenuItem>, K extends SingleOrMultipleType = SingleOrMultipleType, O extends Orientation = Orientation">

167215

import { computed, toRef } from 'vue'

168216

import { NavigationMenuRoot, NavigationMenuList, NavigationMenuItem, NavigationMenuTrigger, NavigationMenuContent, NavigationMenuLink, NavigationMenuIndicator, NavigationMenuViewport, AccordionRoot, AccordionItem, AccordionTrigger, AccordionContent, useForwardPropsEmits } from 'reka-ui'

169217

import { defu } from 'defu'

@@ -182,25 +230,23 @@ import UTooltip from './Tooltip.vue'

182230183231

defineOptions({ inheritAttrs: false })

184232185-

const props = withDefaults(defineProps<NavigationMenuProps<T>>(), {

186-

orientation: 'horizontal',

233+

const props = withDefaults(defineProps<NavigationMenuProps<T, K, O>>(), {

234+

orientation: 'horizontal' as never,

187235

contentOrientation: 'horizontal',

188236

externalIcon: true,

189237

delayDuration: 0,

190-

type: 'multiple',

238+

type: 'multiple' as never,

191239

collapsible: true,

192240

unmountOnHide: true,

193241

labelKey: 'label'

194242

})

195-

const emits = defineEmits<NavigationMenuEmits>()

243+

const emits = defineEmits<NavigationMenuEmits<K, O>>()

196244

const slots = defineSlots<NavigationMenuSlots<T>>()

197245198246

const appConfig = useAppConfig() as NavigationMenu['AppConfig']

199247200248

const rootProps = useForwardPropsEmits(computed(() => ({

201249

as: props.as,

202-

modelValue: props.modelValue,

203-

defaultValue: props.defaultValue,

204250

delayDuration: props.delayDuration,

205251

skipDelayDuration: props.skipDelayDuration,

206252

orientation: props.orientation,

@@ -415,14 +461,27 @@ function getAccordionDefaultValue(list: NavigationMenuItem[], level = 0) {

415461

</component>

416462

</DefineItemTemplate>

417463418-

<NavigationMenuRoot v-bind="{ ...rootProps, ...$attrs }" :data-collapsed="collapsed" data-slot="root" :class="ui.root({ class: [props.ui?.root, props.class] })">

464+

<NavigationMenuRoot

465+

v-bind="{

466+

...rootProps,

467+

...(orientation === 'horizontal' ? {

468+

modelValue: modelValue as string,

469+

defaultValue: defaultValue as string

470+

} : {}),

471+

...$attrs

472+

}"

473+

:data-collapsed="collapsed"

474+

data-slot="root"

475+

:class="ui.root({ class: [props.ui?.root, props.class] })"

476+

>

419477

<slot name="list-leading" />

420478421479

<template v-for="(list, listIndex) in lists" :key="`list-${listIndex}`">

422480

<component

423481

v-bind="orientation === 'vertical' && !collapsed ? {

424482

...accordionProps,

425-

defaultValue: getAccordionDefaultValue(list)

483+

modelValue,

484+

defaultValue: defaultValue ?? getAccordionDefaultValue(list)

426485

} : {}"

427486

:is="orientation === 'vertical' && !collapsed ? AccordionRoot : NavigationMenuList"

428487

as="ul"