Skip to content

Commit

Permalink
feat(comp:dropdown): support hideOnClick (#715)
Browse files Browse the repository at this point in the history
refactor(comp:menu): better type declarations
  • Loading branch information
danranVm committed Jan 12, 2022
1 parent 5d1490a commit fbbe478
Show file tree
Hide file tree
Showing 27 changed files with 295 additions and 303 deletions.
1 change: 1 addition & 0 deletions packages/components/dropdown/docs/Index.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ order: 0
| `autoAdjust` | 悬浮层被遮挡时自动调整位置 | `boolean` | `true` || - |
| `destroyOnHide` | 隐藏时是否销毁浮层 | `boolean` | `false` || - |
| `disabled` | 菜单是否禁用 | `boolean` | `false` | - | - |
| `hideOnClick` | 点击后是否隐藏菜单 | `boolean` | `true` | - | - |
| `offset` | 悬浮层位置偏移量 | `[number, number]` | `[0,8]` || - |
| `placement` | 悬浮层的对齐方式 | `PopperPlacement` | `bottomStart` || - |
| `showArrow` | 是否显示箭头 | `boolean` | `false` || - |
Expand Down
12 changes: 4 additions & 8 deletions packages/components/dropdown/src/Dropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,14 @@
* found in the LICENSE file at https://github.com/IDuxFE/idux/blob/main/LICENSE
*/

import type { DropdownProps } from './types'
import type { DropdownConfig } from '@idux/components/config'
import type { ComputedRef } from 'vue'

import { computed, defineComponent, provide } from 'vue'
import { type ComputedRef, computed, defineComponent, provide, toRef } from 'vue'

import { useControlledProp } from '@idux/cdk/utils'
import { ɵOverlay } from '@idux/components/_private/overlay'
import { useGlobalConfig } from '@idux/components/config'
import { type DropdownConfig, useGlobalConfig } from '@idux/components/config'

import { dropdownToken } from './token'
import { dropdownProps } from './types'
import { type DropdownProps, dropdownProps } from './types'

const defaultDelay: [number, number] = [0, 100]

Expand All @@ -29,7 +25,7 @@ export default defineComponent({
const config = useGlobalConfig('dropdown')
const [visibility, setVisibility] = useControlledProp(props, 'visible', false)
const configProps = useConfigProps(props, config, mergedPrefixCls, setVisibility)
provide(dropdownToken, { setVisibility })
provide(dropdownToken, { hideOnClick: toRef(props, 'hideOnClick'), setVisibility })

return () => {
return (
Expand Down
3 changes: 2 additions & 1 deletion packages/components/dropdown/src/token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@
* found in the LICENSE file at https://github.com/IDuxFE/idux/blob/main/LICENSE
*/

import { InjectionKey } from 'vue'
import { type InjectionKey, type Ref } from 'vue'

export interface DropdownContext {
hideOnClick: Ref<boolean>
setVisibility: (visible: boolean) => void
}

Expand Down
1 change: 1 addition & 0 deletions packages/components/dropdown/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export const dropdownProps = {
autoAdjust: IxPropTypes.bool,
destroyOnHide: IxPropTypes.bool,
disabled: IxPropTypes.bool.def(false),
hideOnClick: IxPropTypes.bool.def(true),
offset: IxPropTypes.array() as unknown as VueTypeDef<[number, number]>,
placement: ɵOverlayPlacementDef,
showArrow: IxPropTypes.bool,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@

exports[`Menu render work 1`] = `
"<ul class=\\"ix-menu ix-menu-light ix-menu-vertical\\">
<li class=\\"ix-menu-item\\"><span class=\\"ix-menu-item-icon\\"><i class=\\"ix-icon ix-icon-home\\" role=\\"img\\" aria-label=\\"home\\"></i></span><span><a>Item 1</a></span></li>
<li class=\\"ix-menu-item\\"><span class=\\"ix-menu-item-icon\\"><i class=\\"ix-icon ix-icon-mail\\" role=\\"img\\" aria-label=\\"mail\\"></i></span><span>Item 2</span></li>
<li class=\\"ix-menu-item ix-menu-item-disabled\\"><span class=\\"ix-menu-item-icon\\"><i class=\\"ix-icon ix-icon-appstore\\" role=\\"img\\" aria-label=\\"appstore\\"></i></span><span>Item 3</span></li>
<li class=\\"ix-menu-item\\"><span class=\\"ix-menu-item-icon\\"><i class=\\"ix-icon ix-icon-home\\" role=\\"img\\" aria-label=\\"home\\"></i></span><span class=\\"ix-menu-item-content\\"><a>Item 1</a></span></li>
<li class=\\"ix-menu-item\\"><span class=\\"ix-menu-item-icon\\"><i class=\\"ix-icon ix-icon-mail\\" role=\\"img\\" aria-label=\\"mail\\"></i></span><span class=\\"ix-menu-item-content\\">Item 2</span></li>
<li class=\\"ix-menu-item ix-menu-item-disabled\\"><span class=\\"ix-menu-item-icon\\"><i class=\\"ix-icon ix-icon-appstore\\" role=\\"img\\" aria-label=\\"appstore\\"></i></span><span class=\\"ix-menu-item-content\\">Item 3</span></li>
<li class=\\"ix-menu-divider\\"></li>
<li class=\\"ix-menu-sub ix-menu-sub-vertical\\">
<div class=\\"ix-menu-sub-title\\"><span class=\\"ix-menu-sub-title-icon\\"><i class=\\"ix-icon ix-icon-setting\\" role=\\"img\\" aria-label=\\"setting\\"></i></span><span>Sub Menu 1</span><span class=\\"ix-menu-sub-title-suffix\\"><i class=\\"ix-icon ix-icon-right\\" role=\\"img\\" aria-label=\\"right\\"></i></span></div>
<div class=\\"ix-menu-sub-title\\"><span class=\\"ix-menu-sub-title-icon\\"><i class=\\"ix-icon ix-icon-setting\\" role=\\"img\\" aria-label=\\"setting\\"></i></span><span class=\\"ix-menu-sub-title-content\\">Sub Menu 1</span><span class=\\"ix-menu-sub-title-suffix\\"><i class=\\"ix-icon ix-icon-right\\" role=\\"img\\" aria-label=\\"right\\"></i></span></div>
<!---->
</li>
<li class=\\"ix-menu-sub ix-menu-sub-disabled ix-menu-sub-vertical\\">
<div class=\\"ix-menu-sub-title\\"><span class=\\"ix-menu-sub-title-icon\\"><i class=\\"ix-icon ix-icon-github\\" role=\\"img\\" aria-label=\\"github\\"></i></span><span>Menu Sub 4</span><span class=\\"ix-menu-sub-title-suffix\\"><i class=\\"ix-icon ix-icon-right\\" role=\\"img\\" aria-label=\\"right\\"></i></span></div>
<div class=\\"ix-menu-sub-title\\"><span class=\\"ix-menu-sub-title-icon\\"><i class=\\"ix-icon ix-icon-github\\" role=\\"img\\" aria-label=\\"github\\"></i></span><span class=\\"ix-menu-sub-title-content\\">Menu Sub 4</span><span class=\\"ix-menu-sub-title-suffix\\"><i class=\\"ix-icon ix-icon-right\\" role=\\"img\\" aria-label=\\"right\\"></i></span></div>
<!---->
</li>
</ul>"
Expand Down
8 changes: 2 additions & 6 deletions packages/components/menu/__tests__/menu.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,6 @@ import { h } from 'vue'
import { renderWork, wait } from '@tests'

import Menu from '../src/Menu'
// import MenuDivider from '../src/MenuDivider'
import MenuItem from '../src/contents/MenuItem'
// import MenuItemGroup from '../src/MenuItemGroup'
import MenuSub from '../src/contents/menu-sub/MenuSub'
import { MenuData, MenuProps } from '../src/types'

const dataSource: MenuData[] = [
Expand Down Expand Up @@ -88,7 +84,7 @@ describe('Menu', () => {
props: { expandedKeys: ['sub1'], 'onUpdate:expandedKeys': onUpdateExpandedKeys, mode: 'inline' },
})

const subs = wrapper.findAllComponents(MenuSub)
const subs = wrapper.findAll('.ix-menu-sub')
expect(subs.length).toBe(4)
expect(subs[0].classes()).toContain('ix-menu-sub-expanded')
expect(subs[1].classes()).not.toContain('ix-menu-sub-expanded')
Expand All @@ -113,7 +109,7 @@ describe('Menu', () => {
const onUpdateSelectedKeys = jest.fn()
const wrapper = MenuMount({ props: { selectedKeys: ['item1'], 'onUpdate:selectedKeys': onUpdateSelectedKeys } })

const items = wrapper.findAllComponents(MenuItem)
const items = wrapper.findAll('.ix-menu-item')
expect(items.length).toBe(3)
expect(items[0].classes()).toContain('ix-menu-item-selected')
expect(items[1].classes()).not.toContain('ix-menu-item-selected')
Expand Down
142 changes: 77 additions & 65 deletions packages/components/menu/docs/Index.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,99 +19,99 @@ order: 0
| `collapsed` | 菜单收起状态 | `boolean` | `false` | - | - |
| `dataSource` | 菜单数据数组 | `MenuData[]` | - | - | 优先级高于 `default` 插槽 |
| `indent` | `inline` 模式时的菜单缩进宽度 | `string \| number` | `24` || 仅支持 `inline` 模式 |
| `overlayClassName` | 悬浮层的自定义 `class` | `string` | - | - | - |
| `overlayClassName` | 悬浮层的自定义 `class` | `string` | - | - | `inline` 模式时无效 |
| `mode` | 菜单模式,现在支持垂直、水平和内嵌 | `'vertical' \| 'horizontal' \| 'inline'` | `'vertical'` | - | - |
| `multiple` | 是否支持多选 | `boolean` | `false` | - | - |
| `selectable` | 是否允许选中 | `boolean` | - | - |`IxDropdown` 中默认为 `false`, 其他情况默认为 `true` |
| `target` | 自定义菜单容器节点 | `string \| HTMLElement \| () => string \| HTMLElement` | - || `inline` 模式时无效 |
| `theme` | 主题颜色 | `'light' \| 'dark'` | `'light'` || - |
| `onClick` | 点击 `IxMenuItem``IxMenuSub` 后的回调 | `(options: MenuClickOptions) => void>` | - | - |
| `onClick` | 点击菜单后的回调 | `(options: MenuClickOptions) => void>` | - | - |

```ts
export type MenuData = MenuItem | MenuItemGroup | MenuSub | MenuDivider

export interface MenuCommon {
type?: 'item' | 'itemGroup' | 'sub' | 'divider'
additional?: {
class?: any
style?: any
[key: string]: unknown
}
export type MenuData = MenuItemProps | MenuItemGroupProps | MenuSubProps | MenuDividerProps

export interface MenuClickOptions {
event: Event
key: VKey
slots?: unknown
type: 'item' | 'itemGroup' | 'sub'
}
```

export interface MenuItemSlots {
icon?: string | ((data: MenuItem & { selected: boolean }) => VNodeChild)
label?: string | ((data: MenuItem & { selected: boolean }) => VNodeChild)
}
export interface MenuItem extends MenuItemPublicProps, MenuCommon {
type?: 'item'
slots?: MenuItemSlots
}
#### MenuCommonProps

export interface MenuItemGroupSlots {
icon?: string | ((data: MenuItemGroup) => VNodeChild)
label?: string | ((data: MenuItemGroup) => VNodeChild)
}
export interface MenuItemGroup extends MenuItemGroupPublicProps, MenuCommon {
type: 'itemGroup'
slots?: MenuItemGroupSlots
}
`MenuData` 的通用属性

export interface MenuSubSlots {
icon?: string | ((data: MenuSub & { expanded: boolean; selected: boolean }) => VNodeChild)
label?: string | ((data: MenuSub & { expanded: boolean; selected: boolean }) => VNodeChild)
suffix?: string | ((data: MenuSub & { expanded: boolean; selected: boolean }) => VNodeChild)
}
export interface MenuSub extends MenuSubPublicProps, MenuCommon {
type: 'sub'
additional?: {
class?: any
style?: any
[key: string]: unknown
}
slots?: MenuSubSlots
}
| 名称 | 说明 | 类型 | 默认值 | 全局配置 | 备注 |
| --- | --- | --- | --- | --- | --- |
| `type` | 菜单类型 | `'item' \| 'itemGroup' \| 'sub' \| 'divider'` | `'item'` | - | - |
| `key` | 唯一标识 | `VKey` | - | - | 必传 |
| `additional` | 菜单的额外配置 | `object` | - | - | 可以传入 `class`, `style` 等原生 DOM 属性 |

#### MenuItemProps

菜单项的配置,继承自 `MenuCommonProps`

| 名称 | 说明 | 类型 | 默认值 | 全局配置 | 备注 |
| --- | --- | --- | --- | --- | --- |
| `type` | 菜单类型 | `'item'` | `'item'` | - | - |
| `disabled` | 是否禁用 | `boolean` | - | - | - |
| `icon` | 菜单图标| `string \| VNode` | - | - |
| `label` | 菜单文本 | `string` | - | - |
| `slots` | 自定义菜单内容 | `MenuItemSlots` | - | - |

export interface MenuDivider {
type: 'divider'
additional?: {
class?: any
style?: any
[key: string]: unknown
}
key?: VKey
```ts
export interface MenuItemSlots {
/**
* 如果类型为 `string`, 会根据该字符串去 `IxMenu` 的插槽中获取
*/
icon?: string | ((data: MenuItem & { selected: boolean }) => VNodeChild)
label?: string | ((data: MenuItem & { selected: boolean }) => VNodeChild)
}
```

### IxMenuItem
#### MenuItemGroupProps

#### MenuItemProps
菜单组的配置,继承自 `MenuCommonProps`

| 名称 | 说明 | 类型 | 默认值 | 全局配置 | 备注 |
| --- | --- | --- | --- | --- | --- |
| `key` | 唯一标识 | `VKey` | - | - | 必传 |
| `disabled` | 是否禁用 | `boolean` | `false` | - | - |
| `icon` | 菜单图标| `string \| VNode \| #icon` | - | - |
| `type` | 菜单类型 | `'itemGroup'` | - | - | 必传 |
| `children` | 子菜单数据 | `MenuData[]` | - | - | - |
| `icon` | 菜单图标| `string \| VNode \| #icon` | - | - |
| `label` | 菜单文本 | `string \| #label` | - | - |
| `slots` | 自定义菜单内容 | `MenuItemGroupSlots` | - | - |

### IxMenuSub
```ts
export interface MenuItemGroupSlots {
icon?: string | ((data: MenuItemGroupProps) => VNodeChild)
label?: string | ((data: MenuItemGroupProps) => VNodeChild)
}
```

#### MenuSubProps

子菜单的配置,继承自 `MenuCommonProps`

| 名称 | 说明 | 类型 | 默认值 | 全局配置 | 备注 |
| --- | --- | --- | --- | --- | --- |
| `key` | 唯一标识 | `VKey` | - | - | 必传 |
| `children` | 子菜单数据 | `MenuData[] \| #default` | - | - | - |
| `disabled` | 是否禁用 | `boolean` | `false` | - | - |
| `icon` | 菜单图标| `string \| VNode \| #icon` | - | - | - |
| `label` | 菜单文本 | `string \| #label` | - | - |
| `suffix` | 后缀图标 | `string \| #suffix` | `right` || - |
| `type` | 菜单类型 | `'sub'` | - | - | 必传 |
| `children` | 子菜单数据 | `MenuData[]` | - | - | - |
| `disabled` | 是否禁用 | `boolean` | - | - | - |
| `icon` | 菜单图标| `string \| VNode` | - | - |
| `label` | 菜单文本 | `string` | - | - |
| `offset` | 浮层偏移量 | `[number, number]` | `[0, 8]` || `inline` 模式时无效 |
| `suffix` | 后缀图标 | `string` | `right` || - |
| `slots` | 自定义菜单内容 | `MenuSubSlots` | - | - |

### IxMenuItemGroup
```ts
export interface MenuSubSlots {
icon?: string | ((data: MenuSubProps & { expanded: boolean; selected: boolean }) => VNodeChild)
label?: string | ((data: MenuSubProps & { expanded: boolean; selected: boolean }) => VNodeChild)
suffix?: string | ((data: MenuSubProps & { expanded: boolean; selected: boolean }) => VNodeChild)
}
```

#### MenuItemGroupProps
#### MenuDividerProps

| 名称 | 说明 | 类型 | 默认值 | 全局配置 | 备注 |
| --- | --- | --- | --- | --- | --- |
Expand All @@ -120,6 +120,18 @@ export interface MenuDivider {
| `icon` | 菜单图标| `string \| VNode \| #icon` | - | - |
| `label` | 菜单文本 | `string \| #label` | - | - |

### IxMenuItem

`template` 中设置 `MenuItemProps`

### IxMenuSub

`template` 中设置 `MenuSubProps`

### IxMenuItemGroup

`template` 中设置 `MenuItemGroupProps`

### IxMenuDivider

菜单分割线
`template` 中设置 `MenuDividerProps`
7 changes: 0 additions & 7 deletions packages/components/menu/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,15 @@ export type {
MenuInstance,
MenuComponent,
MenuPublicProps as MenuProps,
MenuItemInstance,
MenuItemComponent,
MenuItemPublicProps as MenuItemProps,
MenuItemGroupInstance,
MenuItemGroupComponent,
MenuItemGroupPublicProps as MenuItemGroupProps,
MenuSubInstance,
MenuSubComponent,
MenuSubPublicProps as MenuSubProps,
MenuDividerComponent,
MenuMode,
MenuTheme,
MenuClickOptions,
MenuData,
MenuItem,
MenuItemGroup,
MenuSub,
MenuDivider,
} from './src/types'
6 changes: 2 additions & 4 deletions packages/components/menu/src/Menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,16 @@
* found in the LICENSE file at https://github.com/IDuxFE/idux/blob/main/LICENSE
*/

import type { VKey } from '@idux/cdk/utils'

import { computed, defineComponent, inject, normalizeClass, provide } from 'vue'

import { callEmit } from '@idux/cdk/utils'
import { type VKey, callEmit } from '@idux/cdk/utils'
import { useGlobalConfig } from '@idux/components/config'
import { ɵDropdownToken } from '@idux/components/dropdown'

import { coverChildren } from './children/Utils'
import { useDataSource } from './composables/useDataSource'
import { useExpanded } from './composables/useExpanded'
import { useSelected } from './composables/useSelected'
import { coverChildren } from './contents/Utils'
import { menuToken } from './token'
import { menuProps } from './types'

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@
* found in the LICENSE file at https://github.com/IDuxFE/idux/blob/main/LICENSE
*/

import type { FunctionalComponent, HTMLAttributes } from 'vue'

import { inject } from 'vue'
import { type FunctionalComponent, type HTMLAttributes, inject } from 'vue'

import { menuToken } from '../token'

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { useKey } from '@idux/components/utils'

import { usePaddingLeft } from '../composables/usePaddingLeft'
import { menuItemGroupToken, menuSubToken, menuToken } from '../token'
import { MenuItem, menuItemProps } from '../types'
import { menuItemProps } from '../types'
import { coverIcon } from './Utils'

export default defineComponent({
Expand All @@ -40,7 +40,7 @@ export default defineComponent({
const prefixCls = `${mergedPrefixCls.value}-item`
return normalizeClass({
[prefixCls]: true,
[`${prefixCls}-disabled`]: props.disabled,
[`${prefixCls}-disabled`]: props.data.disabled,
[`${prefixCls}-selected`]: isSelected.value,
})
})
Expand All @@ -59,27 +59,23 @@ export default defineComponent({
}

return () => {
const { disabled, icon, label } = props
const slots = props.slots || {}
const { additional, disabled, icon, label, slots = {} } = props.data
const iconSlot = isString(slots.icon) ? menuSlots[slots.icon] : slots.icon
// <IxMenuItem key="key">label</IxMenuItem>
let labelSlot = slots.label ?? slots.default
if (isString(labelSlot)) {
labelSlot = menuSlots[labelSlot]
}

const slotProps =
iconSlot || labelSlot
? ({ ...props, key, selected: isSelected.value } as MenuItem & { selected: boolean })
: undefined
const slotProps = iconSlot || labelSlot ? { ...props.data, selected: isSelected.value } : undefined
const iconNode = coverIcon(iconSlot, slotProps!, icon)
const labelNode = labelSlot ? labelSlot(slotProps) : label

const prefixCls = `${mergedPrefixCls.value}-item`
return (
<li class={classes.value} style={style.value} onClick={disabled ? undefined : onClick}>
<li class={classes.value} style={style.value} {...additional} onClick={disabled ? undefined : onClick}>
{iconNode && <span class={`${prefixCls}-icon`}>{iconNode}</span>}
<span>{labelNode}</span>
<span class={`${prefixCls}-content`}>{labelNode}</span>
</li>
)
}
Expand Down

0 comments on commit fbbe478

Please sign in to comment.