diff --git a/packages/components/config/src/defaultConfig.ts b/packages/components/config/src/defaultConfig.ts index 945d6b41e..814750155 100644 --- a/packages/components/config/src/defaultConfig.ts +++ b/packages/components/config/src/defaultConfig.ts @@ -145,6 +145,7 @@ export const defaultConfig: GlobalConfig = { zoom: [0.5, 2], }, menu: { + getKey: 'key', indent: 24, offset: [0, 8], suffix: 'right', diff --git a/packages/components/config/src/types.ts b/packages/components/config/src/types.ts index c1954afe3..99469b971 100644 --- a/packages/components/config/src/types.ts +++ b/packages/components/config/src/types.ts @@ -19,7 +19,7 @@ import type { DatePickerType } from '@idux/components/date-picker/src/types' import type { FormLabelAlign, FormLayout, FormSize } from '@idux/components/form' import type { ListSize } from '@idux/components/list' import type { Locale } from '@idux/components/locales' -import type { MenuTheme } from '@idux/components/menu' +import type { MenuData, MenuTheme } from '@idux/components/menu' import type { MessageType } from '@idux/components/message' import type { ModalType } from '@idux/components/modal' import type { NotificationPlacement, NotificationType } from '@idux/components/notification' @@ -206,8 +206,12 @@ export interface DropdownConfig { autoAdjust: boolean destroyOnHide: boolean offset: [number, number] + overlayContainer?: PortalTargetType placement: PopperPlacement showArrow: boolean + /** + * @deprecated please use `overlayContainer` instead' + */ target?: PortalTargetType trigger: PopperTrigger } @@ -259,9 +263,14 @@ export interface ImageViewerConfig { } export interface MenuConfig { + getKey: string | ((data: MenuData) => VKey) indent: number offset: [number, number] + overlayContainer?: PortalTargetType suffix: string + /** + * @deprecated please use `overlayContainer` instead' + */ target?: PortalTargetType theme: MenuTheme } diff --git a/packages/components/dropdown/docs/Index.zh.md b/packages/components/dropdown/docs/Index.zh.md index 9c6c198b2..7ead452c2 100644 --- a/packages/components/dropdown/docs/Index.zh.md +++ b/packages/components/dropdown/docs/Index.zh.md @@ -20,9 +20,9 @@ order: 0 | `disabled` | 菜单是否禁用 | `boolean` | `false` | - | - | | `hideOnClick` | 点击后是否隐藏菜单 | `boolean` | `true` | - | - | | `offset` | 悬浮层位置偏移量 | `[number, number]` | `[0,8]` | ✅ | - | +| `overlayContainer` | 自定义下拉框容器节点 | `string \| HTMLElement \| () => string \| HTMLElement` | - | ✅ | - | | `placement` | 悬浮层的对齐方式 | `PopperPlacement` | `'bottomStart'` | ✅ | - | | `showArrow` | 是否显示箭头 | `boolean` | `false` | ✅ | - | -| `target` | 自定义下拉框容器节点 | `string \| HTMLElement \| () => string \| HTMLElement` | - | ✅ | - | | `trigger` | 悬浮层触发方式 | `PopperTrigger` | `hover` | ✅ | - | #### DropdownSlots diff --git a/packages/components/dropdown/src/Dropdown.tsx b/packages/components/dropdown/src/Dropdown.tsx index 1896180a2..4bfbc3da7 100644 --- a/packages/components/dropdown/src/Dropdown.tsx +++ b/packages/components/dropdown/src/Dropdown.tsx @@ -58,7 +58,12 @@ function useConfigProps( offset: props.offset ?? config.offset, placement: props.placement ?? config.placement, showArrow: props.showArrow ?? config.showArrow, - target: props.target ?? config.target ?? `${mergedPrefixCls.value}-container`, + target: + props.target ?? + props.overlayContainer ?? + config.target ?? + config.overlayContainer ?? + `${mergedPrefixCls.value}-overlay-container`, trigger: trigger, ['onUpdate:visible']: setVisibility, } diff --git a/packages/components/dropdown/src/types.ts b/packages/components/dropdown/src/types.ts index 58d9c1982..157873667 100644 --- a/packages/components/dropdown/src/types.ts +++ b/packages/components/dropdown/src/types.ts @@ -20,8 +20,12 @@ export const dropdownProps = { disabled: IxPropTypes.bool.def(false), hideOnClick: IxPropTypes.bool.def(true), offset: IxPropTypes.array() as unknown as VueTypeDef<[number, number]>, + overlayContainer: ɵPortalTargetDef, placement: ɵOverlayPlacementDef, showArrow: IxPropTypes.bool, + /** + * @deprecated please use `overlayContainer` instead' + */ target: ɵPortalTargetDef, trigger: ɵOverlayTriggerDef, @@ -30,6 +34,6 @@ export const dropdownProps = { } export type DropdownProps = ExtractInnerPropTypes -export type DropdownPublicProps = ExtractPublicPropTypes +export type DropdownPublicProps = Omit, 'target'> export type DropdownComponent = DefineComponent & DropdownPublicProps> export type DropdownInstance = InstanceType> diff --git a/packages/components/menu/__tests__/__snapshots__/menu.spec.ts.snap b/packages/components/menu/__tests__/__snapshots__/menu.spec.ts.snap index a76a33539..aa1270888 100644 --- a/packages/components/menu/__tests__/__snapshots__/menu.spec.ts.snap +++ b/packages/components/menu/__tests__/__snapshots__/menu.spec.ts.snap @@ -2,15 +2,15 @@ exports[`Menu > render work 1`] = ` "
    -
  • Item 1
  • -
  • Item 2
  • -
  • Item 3
  • +
  • Item 1
  • +
  • Item 2
  • +
  • Item 3
  • -
  • +
  • Sub Menu 1
  • -
  • +
  • Menu Sub 4
  • diff --git a/packages/components/menu/docs/Index.zh.md b/packages/components/menu/docs/Index.zh.md index 393214001..036a8fbb1 100644 --- a/packages/components/menu/docs/Index.zh.md +++ b/packages/components/menu/docs/Index.zh.md @@ -17,13 +17,15 @@ order: 0 | `v-model:expandedKeys` | 当前展开的 `IxMenuSub` 的 `key` 数组 | `VKey[]` | - | - | - | | `v-model:selectedKeys` | 当前选中的 `IxMenuItem` 的 `key` 数组 | `VKey[]` | - | - | - | | `collapsed` | 菜单收起状态 | `boolean` | `false` | - | - | +| `customAdditional` | 自定义下拉选项的额外属性 | `MenuCustomAdditional` | - | - | 例如 `class`, 或者原生事件 | | `dataSource` | 菜单数据数组 | `MenuData[]` | - | - | 优先级高于 `default` 插槽 | +| `getKey` | 获取数据的唯一标识 | `string \| (data: CascaderData) => VKey` | `key` | ✅ | - | | `indent` | `inline` 模式时的菜单缩进宽度 | `string \| number` | `24` | ✅ | 仅支持 `inline` 模式 | -| `overlayClassName` | 悬浮层的自定义 `class` | `string` | - | - | `inline` 模式时无效 | | `mode` | 菜单模式,现在支持垂直、水平和内嵌 | `'vertical' \| 'horizontal' \| 'inline'` | `'vertical'` | - | - | | `multiple` | 是否支持多选 | `boolean` | `false` | - | - | +| `overlayClassName` | 悬浮层的自定义 `class` | `string` | - | - | `inline` 模式时无效 | +| `overlayContainer` | 自定义菜单容器节点 | `string \| HTMLElement \| () => string \| HTMLElement` | - | ✅ | `inline` 模式时无效 | | `selectable` | 是否允许选中 | `boolean` | `true` | - | - | -| `target` | 自定义菜单容器节点 | `string \| HTMLElement \| () => string \| HTMLElement` | - | ✅ | `inline` 模式时无效 | | `theme` | 主题颜色 | `'light' \| 'dark'` | `'light'` | ✅ | - | | `onClick` | 点击菜单后的回调 | `(options: MenuClickOptions) => void` | - | - | @@ -35,24 +37,15 @@ export interface MenuClickOptions { key: VKey type: 'item' | 'itemGroup' | 'sub' } -``` -#### MenuCommonProps - -`MenuData` 的通用属性 - -| 名称 | 说明 | 类型 | 默认值 | 全局配置 | 备注 | -| --- | --- | --- | --- | --- | --- | -| `type` | 菜单类型 | `'item' \| 'itemGroup' \| 'sub' \| 'divider'` | - | - | - | -| `key` | 唯一标识 | `VKey` | - | - | 必传 | -| `additional` | 菜单的额外配置 | `object` | - | - | 可以传入 `class`, `style` 等原生 DOM 属性 | +export type MenuCustomAdditional = (options: { data: MenuData; index: number }) => Record | undefined +``` #### MenuItemProps -菜单项的配置,继承自 `MenuCommonProps` - | 名称 | 说明 | 类型 | 默认值 | 全局配置 | 备注 | | --- | --- | --- | --- | --- | --- | +| `key` | 唯一标识 | `VKey` | - | - | 必传, 可以通过 `getKey` 指定其他字段 | | `type` | 菜单类型 | `'item'` | `'item'` | - | - | | `disabled` | 是否禁用 | `boolean` | - | - | - | | `icon` | 菜单图标| `string \| VNode` | - | - | @@ -62,10 +55,9 @@ export interface MenuClickOptions { #### MenuItemGroupProps -菜单组的配置,继承自 `MenuCommonProps` - | 名称 | 说明 | 类型 | 默认值 | 全局配置 | 备注 | | --- | --- | --- | --- | --- | --- | +| `key` | 唯一标识 | `VKey` | - | - | 必传, 可以通过 `getKey` 指定其他字段 | | `type` | 菜单类型 | `'itemGroup'` | - | - | 必传 | | `children` | 子菜单数据 | `MenuData[]` | - | - | - | | `icon` | 菜单图标| `string \| VNode` | - | - | @@ -75,10 +67,9 @@ export interface MenuClickOptions { #### MenuSubProps -子菜单的配置,继承自 `MenuCommonProps` - | 名称 | 说明 | 类型 | 默认值 | 全局配置 | 备注 | | --- | --- | --- | --- | --- | --- | +| `key` | 唯一标识 | `VKey` | - | - | 必传, 可以通过 `getKey` 指定其他字段 | | `type` | 菜单类型 | `'sub'` | - | - | 必传 | | `children` | 子菜单数据 | `MenuData[]` | - | - | - | | `disabled` | 是否禁用 | `boolean` | - | - | - | @@ -94,9 +85,8 @@ export interface MenuClickOptions { | 名称 | 说明 | 类型 | 默认值 | 全局配置 | 备注 | | --- | --- | --- | --- | --- | --- | +| `key` | 唯一标识 | `VKey` | - | - | 必传, 可以通过 `getKey` 指定其他字段 | | `type` | 菜单类型 | `'divider'` | - | - | 必传 | -| `key` | 唯一标识 | `VKey` | - | - | 必传 | -| `additional` | 菜单的额外配置 | `object` | - | - | 可以传入 `class`, `style` 等原生 DOM 属性 | ### IxMenuItem diff --git a/packages/components/menu/index.ts b/packages/components/menu/index.ts index 295adcfda..294914c5c 100644 --- a/packages/components/menu/index.ts +++ b/packages/components/menu/index.ts @@ -32,5 +32,6 @@ export type { MenuMode, MenuTheme, MenuClickOptions, + MenuCustomAdditional, MenuData, } from './src/types' diff --git a/packages/components/menu/src/Menu.tsx b/packages/components/menu/src/Menu.tsx index 43ab66eda..0b7c0e26b 100644 --- a/packages/components/menu/src/Menu.tsx +++ b/packages/components/menu/src/Menu.tsx @@ -10,6 +10,7 @@ import { computed, defineComponent, inject, normalizeClass, provide } from 'vue' import { type VKey, callEmit } from '@idux/cdk/utils' import { useGlobalConfig } from '@idux/components/config' import { ɵDropdownToken } from '@idux/components/dropdown' +import { useGetKey } from '@idux/components/utils' import { useDataSource } from './composables/useDataSource' import { useExpanded } from './composables/useExpanded' @@ -25,7 +26,7 @@ export default defineComponent({ const common = useGlobalConfig('common') const mergedPrefixCls = computed(() => `${common.prefixCls}-menu`) const config = useGlobalConfig('menu') - + const mergedGetKey = useGetKey(props, config, 'components/menu') const indent = computed(() => props.indent ?? config.indent) const mode = computed(() => props.mode) @@ -44,6 +45,7 @@ export default defineComponent({ slots, config, mergedPrefixCls, + mergedGetKey, indent, theme, expandedKeys, @@ -66,7 +68,7 @@ export default defineComponent({ const dateSource = useDataSource(props, slots) return () => { - return
      {coverChildren(dateSource.value)}
    + return
      {coverChildren(dateSource.value, mergedGetKey.value)}
    } }, }) diff --git a/packages/components/menu/src/contents/MenuDivider.tsx b/packages/components/menu/src/contents/MenuDivider.tsx index a6d77f7c9..73fc76cc9 100644 --- a/packages/components/menu/src/contents/MenuDivider.tsx +++ b/packages/components/menu/src/contents/MenuDivider.tsx @@ -8,10 +8,19 @@ import { type FunctionalComponent, type HTMLAttributes, inject } from 'vue' import { menuToken } from '../token' +import { MenuDividerProps } from '../types' -const MenuDivider: FunctionalComponent = () => { - const { mergedPrefixCls } = inject(menuToken)! - return
  • +const MenuDivider: FunctionalComponent< + HTMLAttributes & { + data: MenuDividerProps + index: number + } +> = props => { + const { props: menuProps, mergedPrefixCls } = inject(menuToken)! + const customAdditional = menuProps.customAdditional + ? menuProps.customAdditional({ data: props.data, index: props.index }) + : undefined + return
  • } MenuDivider.displayName = 'MenuDivider' diff --git a/packages/components/menu/src/contents/MenuItem.tsx b/packages/components/menu/src/contents/MenuItem.tsx index e8723f246..ac4f9324c 100644 --- a/packages/components/menu/src/contents/MenuItem.tsx +++ b/packages/components/menu/src/contents/MenuItem.tsx @@ -76,9 +76,20 @@ export default defineComponent({ const slotProps = iconSlot || labelSlot ? { ...props.data, selected: isSelected.value } : undefined const iconNode = coverIcon(iconSlot, slotProps!, icon) const labelNode = labelSlot ? labelSlot(slotProps!) : label - + const customAdditional = menuProps.customAdditional + ? menuProps.customAdditional({ data: props.data, index: props.index }) + : undefined return ( -
  • +
  • diff --git a/packages/components/menu/src/contents/MenuItemGroup.tsx b/packages/components/menu/src/contents/MenuItemGroup.tsx index 8f6d68f4b..02ae9ac49 100644 --- a/packages/components/menu/src/contents/MenuItemGroup.tsx +++ b/packages/components/menu/src/contents/MenuItemGroup.tsx @@ -26,7 +26,14 @@ export default defineComponent({ provide(menuItemGroupToken, true) // menuContext must exist - const { props: menuProps, slots: menuSlots, mergedPrefixCls, indent, handleClick } = inject(menuToken)! + const { + props: menuProps, + slots: menuSlots, + mergedPrefixCls, + mergedGetKey, + indent, + handleClick, + } = inject(menuToken)! const menuSubContext = inject(menuSubToken, null) const menuItemGroupContext = inject(menuItemGroupToken, null) @@ -57,13 +64,16 @@ export default defineComponent({ const labelNode = labelSlot ? labelSlot(slotProps) : label const prefixCls = `${mergedPrefixCls.value}-item-group` + const customAdditional = menuProps.customAdditional + ? menuProps.customAdditional({ data: props.data, index: props.index }) + : undefined return ( -
  • +
  • {iconNode} {labelNode}
    -
      {coverChildren(children)}
    +
      {coverChildren(children, mergedGetKey.value)}
  • ) } diff --git a/packages/components/menu/src/contents/Utils.tsx b/packages/components/menu/src/contents/Utils.tsx index 1f8905bb2..cf8ba8086 100644 --- a/packages/components/menu/src/contents/Utils.tsx +++ b/packages/components/menu/src/contents/Utils.tsx @@ -11,6 +11,7 @@ import { isString } from 'lodash-es' import { Logger } from '@idux/cdk/utils' import { IxIcon } from '@idux/components/icon' +import { type GetKeyFn } from '@idux/components/utils' import { type MenuData } from '../types' import MenuDivider from './MenuDivider' @@ -18,22 +19,23 @@ import MenuItem from './MenuItem' import MenuItemGroup from './MenuItemGroup' import MenuSub from './menu-sub/MenuSub' -export function coverChildren(data?: MenuData[]): VNode[] { +export function coverChildren(data: MenuData[] | undefined, getKetFn: GetKeyFn): VNode[] { if (!data || data.length === 0) { return [] } const nodes: VNode[] = [] - data.forEach(item => { + data.forEach((item, index) => { const { type } = item + const key = getKetFn(item) if (!type || type === 'item') { - nodes.push() + nodes.push() } else if (type === 'sub') { - nodes.push() + nodes.push() } else if (type === 'itemGroup') { - nodes.push() + nodes.push() } else if (type === 'divider') { - nodes.push() + nodes.push() } else { __DEV__ && Logger.warn('components/menu', `type [${type}] not supported`) } diff --git a/packages/components/menu/src/contents/menu-sub/InlineContent.tsx b/packages/components/menu/src/contents/menu-sub/InlineContent.tsx index 40f85fa85..e64aed0a0 100644 --- a/packages/components/menu/src/contents/menu-sub/InlineContent.tsx +++ b/packages/components/menu/src/contents/menu-sub/InlineContent.tsx @@ -15,7 +15,7 @@ import { coverChildren } from '../Utils' export default defineComponent({ name: 'MenuSubInlineContent', setup() { - const { mergedPrefixCls } = inject(menuToken)! + const { mergedPrefixCls, mergedGetKey } = inject(menuToken)! const { props, isExpanded } = inject(menuSubToken)! const classes = computed(() => { @@ -29,7 +29,7 @@ export default defineComponent({ return ( <ɵCollapseTransition appear>
      - {coverChildren(props.data.children)} + {coverChildren(props.data.children, mergedGetKey.value)}
    ) diff --git a/packages/components/menu/src/contents/menu-sub/MenuSub.tsx b/packages/components/menu/src/contents/menu-sub/MenuSub.tsx index 7fe89be3f..e6abb4ae9 100644 --- a/packages/components/menu/src/contents/menu-sub/MenuSub.tsx +++ b/packages/components/menu/src/contents/menu-sub/MenuSub.tsx @@ -55,7 +55,14 @@ export default defineComponent({ const menuItemGroupContext = inject(menuItemGroupToken, false) const key = useKey() - const target = computed(() => menuProps.target ?? config.target ?? `${mergedPrefixCls.value}-overlay-container`) + const target = computed( + () => + menuProps.target ?? + menuProps.overlayContainer ?? + config.target ?? + config.overlayContainer ?? + `${mergedPrefixCls.value}-overlay-container`, + ) const mode = useMode(menuProps, menuSubContext) const level = menuSubContext ? menuSubContext.level + 1 : 1 const paddingLeft = usePaddingLeft(menuProps, mode, indent, level, menuItemGroupContext) @@ -106,8 +113,9 @@ export default defineComponent({ return () => { const { additional, disabled } = props.data + const isInline = mode.value === 'inline' let children: VNodeTypes - if (mode.value === 'inline') { + if (isInline) { children = [, ] } else { const trigger = () => @@ -129,8 +137,18 @@ export default defineComponent({ /> ) } + const customAdditional = menuProps.customAdditional + ? menuProps.customAdditional({ data: props.data, index: props.index }) + : undefined return ( -
  • +
  • ) diff --git a/packages/components/menu/src/token.ts b/packages/components/menu/src/token.ts index 20b3998d5..8f88202c1 100644 --- a/packages/components/menu/src/token.ts +++ b/packages/components/menu/src/token.ts @@ -8,6 +8,7 @@ import type { MenuMode, MenuProps, MenuSubProps, MenuTheme } from './types' import type { VKey } from '@idux/cdk/utils' import type { MenuConfig } from '@idux/components/config' +import type { GetKeyFn } from '@idux/components/utils' import type { ComputedRef, InjectionKey, Slots } from 'vue' export interface MenuContext { @@ -15,6 +16,7 @@ export interface MenuContext { slots: Slots config: MenuConfig mergedPrefixCls: ComputedRef + mergedGetKey: ComputedRef indent: ComputedRef theme: ComputedRef expandedKeys: ComputedRef @@ -27,7 +29,7 @@ export interface MenuContext { export const menuToken: InjectionKey = Symbol('menuToken') export interface MenuSubContext { - props: { data: MenuSubProps } + props: { data: MenuSubProps; index: number } key: VKey isExpanded: ComputedRef isSelected: ComputedRef diff --git a/packages/components/menu/src/types.ts b/packages/components/menu/src/types.ts index 2dd494483..92668456d 100644 --- a/packages/components/menu/src/types.ts +++ b/packages/components/menu/src/types.ts @@ -7,7 +7,14 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { type DefineComponent, type FunctionalComponent, type HTMLAttributes, type VNode, type VNodeChild } from 'vue' +import { + type DefineComponent, + type FunctionalComponent, + type HTMLAttributes, + type PropType, + type VNode, + type VNodeChild, +} from 'vue' import { ɵPortalTargetDef } from '@idux/cdk/portal' import { type ExtractInnerPropTypes, type ExtractPublicPropTypes, IxPropTypes, type VKey } from '@idux/cdk/utils' @@ -24,15 +31,22 @@ export interface MenuClickOptions { export const menuProps = { expandedKeys: IxPropTypes.arrayOf(IxPropTypes.oneOfType([String, Number, Symbol])), selectedKeys: IxPropTypes.arrayOf(IxPropTypes.oneOfType([String, Number, Symbol])), + collapsed: IxPropTypes.bool.def(false), + customAdditional: { type: Object as PropType, default: undefined }, dataSource: IxPropTypes.array(), + getKey: { type: [String, Function] as PropType VKey)>, default: undefined }, indent: IxPropTypes.number, mode: IxPropTypes.oneOf(['vertical', 'horizontal', 'inline']).def('vertical'), multiple: IxPropTypes.bool.def(false), + overlayClassName: IxPropTypes.string, + overlayContainer: ɵPortalTargetDef, selectable: IxPropTypes.bool.def(true), + /** + * @deprecated please use `overlayContainer` instead' + */ target: ɵPortalTargetDef, theme: IxPropTypes.oneOf(['light', 'dark']), - overlayClassName: IxPropTypes.string, // events 'onUpdate:expandedKeys': IxPropTypes.emit<(expandedKeys: VKey[]) => void>(), @@ -41,26 +55,17 @@ export const menuProps = { } export type MenuProps = ExtractInnerPropTypes -export type MenuPublicProps = ExtractPublicPropTypes +export type MenuPublicProps = Omit, 'target'> export type MenuComponent = DefineComponent & MenuPublicProps> export type MenuInstance = InstanceType> -export interface MenuCommonProps { - type?: 'item' | 'itemGroup' | 'sub' | 'divider' - key: VKey - additional?: { - class?: any - style?: any - [key: string]: unknown - } -} - export interface MenuItemSlots { icon?: string | ((data: MenuItemProps & { selected: boolean }) => VNodeChild) label?: string | ((data: MenuItemProps & { selected: boolean }) => VNodeChild) } -export interface MenuItemProps extends MenuCommonProps { +export interface MenuItemProps { + key?: VKey type?: 'item' disabled?: boolean icon?: string | VNode @@ -71,8 +76,17 @@ export interface MenuItemProps extends MenuCommonProps { slots?: MenuItemSlots customIcon?: string | ((data: MenuItemProps & { selected: boolean }) => VNodeChild) customLabel?: string | ((data: MenuItemProps & { selected: boolean }) => VNodeChild) + + /** + * @deprecated please use `customAdditional` instead' + */ + additional?: { + class?: any + style?: any + [key: string]: unknown + } } -export type MenuItemPublicProps = Omit +export type MenuItemPublicProps = Omit export type MenuItemComponent = FunctionalComponent< Omit & MenuItemPublicProps > @@ -83,8 +97,9 @@ export interface MenuItemGroupSlots { label?: string | ((data: MenuItemGroupProps) => VNodeChild) } -export interface MenuItemGroupProps extends MenuCommonProps { +export interface MenuItemGroupProps { type: 'itemGroup' + key?: VKey children?: MenuData[] icon?: string | VNode label?: string @@ -94,8 +109,17 @@ export interface MenuItemGroupProps extends MenuCommonProps { slots?: MenuItemGroupSlots customIcon?: string | ((data: MenuItemGroupProps) => VNodeChild) customLabel?: string | ((data: MenuItemGroupProps) => VNodeChild) + + /** + * @deprecated please use `customAdditional` instead' + */ + additional?: { + class?: any + style?: any + [key: string]: unknown + } } -export type MenuItemGroupPublicProps = Omit +export type MenuItemGroupPublicProps = Omit export type MenuItemGroupComponent = FunctionalComponent< Omit & MenuItemGroupPublicProps > @@ -107,8 +131,9 @@ export interface MenuSubSlots { suffix?: string | ((data: MenuSubProps & { expanded: boolean; selected: boolean }) => VNodeChild) } -export interface MenuSubProps extends MenuCommonProps { +export interface MenuSubProps { type: 'sub' + key?: VKey children?: MenuData[] disabled?: boolean icon?: string | VNode @@ -122,14 +147,26 @@ export interface MenuSubProps extends MenuCommonProps { customIcon?: string | ((data: MenuSubProps & { expanded: boolean; selected: boolean }) => VNodeChild) customLabel?: string | ((data: MenuSubProps & { expanded: boolean; selected: boolean }) => VNodeChild) customSuffix?: string | ((data: MenuSubProps & { expanded: boolean; selected: boolean }) => VNodeChild) + + /** + * @deprecated please use `customAdditional` instead' + */ + additional?: { + class?: any + style?: any + [key: string]: unknown + } } -export type MenuSubPublicProps = Omit +export type MenuSubPublicProps = Omit export type MenuSubComponent = FunctionalComponent & MenuSubPublicProps> export interface MenuDividerProps { type: 'divider' key?: VKey + /** + * @deprecated please use `customAdditional` instead' + */ additional?: { class?: any style?: any @@ -141,15 +178,20 @@ export type MenuDividerComponent = FunctionalComponent export type MenuData = MenuItemProps | MenuItemGroupProps | MenuSubProps | MenuDividerProps +export type MenuCustomAdditional = (options: { data: MenuData; index: number }) => Record | undefined + // private export const menuItemProps = { data: IxPropTypes.object().isRequired, -} + index: { type: Number, required: true }, +} as const export const menuItemGroupProps = { data: IxPropTypes.object().isRequired, -} + index: { type: Number, required: true }, +} as const export const menuSubProps = { data: IxPropTypes.object().isRequired, -} + index: { type: Number, required: true }, +} as const diff --git a/packages/components/select/docs/Index.zh.md b/packages/components/select/docs/Index.zh.md index 9e8ced202..e603f7972 100644 --- a/packages/components/select/docs/Index.zh.md +++ b/packages/components/select/docs/Index.zh.md @@ -34,6 +34,7 @@ order: 0 | `multiple` | 多选模式 | `boolean` | `false` | - | - | | `multipleLimit` | 最多选中多少项 | `number` | - | - | - | | `overlayClassName` | 下拉菜单的 `class` | `string` | - | - | - | +| `overlayContainer` | 自定义下拉框容器节点 | `string \| HTMLElement \| () => string \| HTMLElement` | - | ✅ | - | | `overlayMatchWidth` | 下拉菜单和选择器同宽 | `boolean` | `true` | ✅ | - | | `overlayRender` | 自定义下拉菜单内容的渲染 | `(children:VNode[]) => VNodeTypes` | - | - | - | | `placeholder` | 选择框默认文本 | `string \| #placeholder` | - | - | - | @@ -42,7 +43,6 @@ order: 0 | `searchFn` | 根据搜索的文本进行筛选 | `boolean \| SelectSearchFn` | `true` | - | 为 `true` 时使用默认的搜索规则, 如果使用远程搜索,应该设置为 `false` | | `size` | 设置选择器大小 | `'sm' \| 'md' \| 'lg'` | `md` | ✅ | - | | `suffix` | 设置后缀图标 | `string \| #suffix` | `down` | ✅ | - | -| `target` | 自定义浮层容器节点 | `string \| HTMLElement \| () => string \| HTMLElement` | - | ✅ | - || | `virtual` | 是否开启虚拟滚动 | `boolean` | `false` | - | - | | `onChange` | 选中值发生改变后的回调 | `(value: any, oldValue: any) => void` | - | - | - | | `onClear` | 清除图标被点击后的回调 | `(evt: MouseEvent) => void` | - | - | - | diff --git a/packages/pro/layout/__tests__/__snapshots__/layoutPro.spec.ts.snap b/packages/pro/layout/__tests__/__snapshots__/layoutPro.spec.ts.snap index 857ec8b4d..a3dae75df 100644 --- a/packages/pro/layout/__tests__/__snapshots__/layoutPro.spec.ts.snap +++ b/packages/pro/layout/__tests__/__snapshots__/layoutPro.spec.ts.snap @@ -13,34 +13,34 @@ exports[`ProLayout > render work 1`] = `
      -
    • +
    • Sub Menu 1
        -
      • Item 4
      • -
      • Item 5
      • +
      • Item 4
      • +
      • Item 5
      • -
      • +
      • Menu Sub 2
          -
        • +
        • Item 6
        • -
        • +
        • Item 7
      • -
      • +
      • Menu Sub 3
          -
        • +
        • Item 8
        • -
        • +
        • Item 9
        @@ -49,20 +49,20 @@ exports[`ProLayout > render work 1`] = `
    • -
    • +
    • Menu Sub 4
        -
      • +
      • Item 10
      • -
      • +
      • Item 11
    • -
    • Item 12
    • +
    • Item 12
    diff --git a/packages/pro/layout/docs/Index.zh.md b/packages/pro/layout/docs/Index.zh.md index 83feaebd2..f5466785a 100644 --- a/packages/pro/layout/docs/Index.zh.md +++ b/packages/pro/layout/docs/Index.zh.md @@ -17,11 +17,12 @@ order: 0 | `v-model:activeKey` | 当前激活的菜单 | `VKey` | `menus`第一个叶子节点 | - | - | | `v-model:collapsed` | 左侧菜单折叠收起 | `boolean` | `false` | - | 仅对于侧边菜单生效 | | `fixed` | 固定 | `boolean \| {sider: boolean, header: boolean}` | `false` | - | - | +| `headerMenu` | 顶部菜单组件的更多配置 | `MenuProps` | - | - | 例如:`getKey` | | `menus` | 菜单数据 | `MenuData[]` | `[]` | - | - | | `compress` | 展开侧边栏是否压缩右侧内容区域 | `boolean` | `true` | - | 设置为 `false`时必须设置 `IxProLayout` 高度 | | `sider` | 侧边栏的更多配置 | `LayoutSiderProps` | - | - | 例如:可以配置响应式断点:`breakpoint` | | `siderHover` | 鼠标悬浮侧边栏时展开 | `boolean \| SiderHover` | `-` | - | `delay`单位为 `ms` | -| `siderMenu` | 侧边栏菜单组件的更多配置 | `MenuProps` | - | - | 例如:可以配置缩进宽度:`indent` 和菜单模式: `mode` | +| `siderMenu` | 侧边栏菜单组件的更多配置 | `MenuProps` | - | - | 例如:`getKey`, `indent` 和 `mode` | | `theme` | 主题 | `light \| dark \| {sider: light \| dark, header: light \| dark}` | `light` | - | - | | `type` | 布局类型 | `'header' \| 'sider' \| 'both' \| 'mixin'` | `mixin` | - | 参见示例:[布局类型](#pro-layout-demo-Type) | | `onMenuClick` | 点击菜单回调 | `(options: MenuClickOptions) => void`| - | - | - | diff --git a/packages/pro/layout/src/contents/Header.tsx b/packages/pro/layout/src/contents/Header.tsx index 8e65eae9c..a9c210ebe 100644 --- a/packages/pro/layout/src/contents/Header.tsx +++ b/packages/pro/layout/src/contents/Header.tsx @@ -5,7 +5,7 @@ * found in the LICENSE file at https://github.com/IDuxFE/idux/blob/main/LICENSE */ -import { computed, defineComponent, inject, normalizeClass } from 'vue' +import { computed, defineComponent, inject, mergeProps, normalizeClass } from 'vue' import { isObject } from 'lodash-es' @@ -60,14 +60,17 @@ export default defineComponent({ return () => { const prefixCls = `${mergedPrefixCls.value}-header` - const menuProps: MenuProps = { - overlayClassName: `${prefixCls}-menu-overlay`, - dataSource: headerMenus.value, - selectedKeys: menuSelectedKeys.value, - mode: 'horizontal', - theme: theme.value, - onClick: onMenuClick, - } + const menuProps = mergeProps( + { + overlayClassName: `${prefixCls}-menu-overlay`, + dataSource: headerMenus.value, + selectedKeys: menuSelectedKeys.value, + mode: 'horizontal', + theme: theme.value, + onClick: onMenuClick, + } as MenuProps, + props.headerMenu!, + ) const contentNode = slots.headerContent ? ( slots.headerContent(menuProps) diff --git a/packages/pro/layout/src/contents/Sider.tsx b/packages/pro/layout/src/contents/Sider.tsx index 86fb217a4..29fa4c03a 100644 --- a/packages/pro/layout/src/contents/Sider.tsx +++ b/packages/pro/layout/src/contents/Sider.tsx @@ -5,13 +5,13 @@ * found in the LICENSE file at https://github.com/IDuxFE/idux/blob/main/LICENSE */ -import { type ComputedRef, computed, defineComponent, inject, normalizeClass, watch } from 'vue' +import { type ComputedRef, computed, defineComponent, inject, mergeProps, normalizeClass, watch } from 'vue' import { isObject } from 'lodash-es' import { type VKey, callEmit, useState } from '@idux/cdk/utils' import { IxLayoutSider, type LayoutSiderProps } from '@idux/components/layout' -import { IxMenu, type MenuClickOptions, MenuData, MenuProps } from '@idux/components/menu' +import { IxMenu, type MenuClickOptions, type MenuData, type MenuProps } from '@idux/components/menu' import { proLayoutToken } from '../token' import { getTargetPaths } from '../utils/menu' @@ -72,26 +72,31 @@ export default defineComponent({ return () => { const prefixCls = `${mergedPrefixCls.value}-sider` - const menuProps: MenuProps = { - overlayClassName: `${prefixCls}-menu-overlay`, - collapsed: collapsed.value, - dataSource: siderMenus.value, - expandedKeys: expandedKeys.value, - 'onUpdate:expandedKeys': setExpandedKeys, - selectedKeys: menuSelectedKeys.value, - 'onUpdate:selectedKeys': keys => setActiveKey(keys[0]), - mode: 'inline', - theme: theme.value, - onClick: onMenuClick, - ...props.siderMenu, - } + const menuProps = mergeProps( + { + overlayClassName: `${prefixCls}-menu-overlay`, + collapsed: collapsed.value, + dataSource: siderMenus.value, + expandedKeys: expandedKeys.value, + 'onUpdate:expandedKeys': setExpandedKeys, + selectedKeys: menuSelectedKeys.value, + 'onUpdate:selectedKeys': keys => setActiveKey(keys[0]), + mode: 'inline', + theme: theme.value, + onClick: onMenuClick, + } as MenuProps, + props.siderMenu!, + ) + const contentNode = slots.siderContent ? slots.siderContent(menuProps) : - const siderProps: LayoutSiderProps = { - collapsed: collapsed.value, - 'onUpdate:collapsed': setCollapsed, - ...props.sider, - } + const siderProps = mergeProps( + { + collapsed: collapsed.value, + 'onUpdate:collapsed': setCollapsed, + } as LayoutSiderProps, + props.sider!, + ) return ( diff --git a/packages/pro/layout/src/types.ts b/packages/pro/layout/src/types.ts index 3a312e726..a91035fdf 100644 --- a/packages/pro/layout/src/types.ts +++ b/packages/pro/layout/src/types.ts @@ -27,6 +27,10 @@ export const proLayoutProps = { type: [Boolean, Object] as PropType, default: false, }, + headerMenu: { + type: Object as PropType, + default: undefined, + }, menus: { type: Array as PropType, default: (): MenuData[] => [], @@ -41,7 +45,7 @@ export const proLayoutProps = { }, siderMenu: { type: Object as PropType, - default: (): MenuData[] => [], + default: undefined, }, theme: { type: [String, Object] as PropType,