Skip to content

Commit

Permalink
feat(comp:menu): add overlayDelay prop and menu item support custom s…
Browse files Browse the repository at this point in the history
…uffix (#1300)

fix #1292
  • Loading branch information
danranVm committed Nov 24, 2022
1 parent af018fb commit f1dde91
Show file tree
Hide file tree
Showing 26 changed files with 406 additions and 413 deletions.
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
.@{collapse-transition-prefix} {

&-enter-active,
&-leave-active {
overflow: hidden;
transition: height @transition-duration-base @ease-in-out, width @transition-duration-base @ease-in-out,
opacity @transition-duration-base @ease-in-out;
transition: height var(--ix-transition-duration-medium) var(--ix-ease-in-out),
width var(--ix-transition-duration-medium) var(--ix-ease-in-out),
opacity var(--ix-transition-duration-medium) var(--ix-ease-in-out);
}
}
4 changes: 2 additions & 2 deletions packages/components/config/src/defaultConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,8 @@ export const defaultConfig: GlobalConfig = {
},
menu: {
getKey: 'key',
indent: 24,
offset: [0, 4],
indent: 16,
offset: [0, 8],
suffix: 'right',
theme: 'light',
},
Expand Down
2 changes: 1 addition & 1 deletion packages/components/config/src/seerConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export const seerConfig: DeepPartialGlobalConfig = {
labelAlign: 'start',
},
menu: {
indent: 16,
offset: [0, 4],
},
modal: {
centered: true,
Expand Down
2 changes: 0 additions & 2 deletions packages/components/dropdown/style/index.less
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@
}

.@{menu-prefix} {
border-radius: @dropdown-overlay-border-radius;

&&-dropdown {
&.@{menu-prefix}-vertical,
&.@{menu-prefix}-inline {
Expand Down
16 changes: 11 additions & 5 deletions packages/components/menu/__tests__/__snapshots__/menu.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,21 @@

exports[`Menu > render work 1`] = `
"<ul class=\\"ix-menu ix-menu-light ix-menu-vertical\\">
<li class=\\"ix-menu-item\\" aria-selected=\\"false\\" role=\\"menuitem\\"><i class=\\"ix-icon ix-icon-home\\" role=\\"img\\" aria-label=\\"home\\"></i><span><a>Item 1</a></span></li>
<li class=\\"ix-menu-item\\" aria-label=\\"Item 2\\" aria-selected=\\"false\\" role=\\"menuitem\\"><i class=\\"ix-icon ix-icon-mail\\" role=\\"img\\" aria-label=\\"mail\\"></i><span>Item 2</span></li>
<li class=\\"ix-menu-item ix-menu-item-disabled\\" aria-label=\\"Item 3\\" aria-selected=\\"false\\" role=\\"menuitem\\"><i class=\\"ix-icon ix-icon-appstore\\" role=\\"img\\" aria-label=\\"appstore\\"></i><span>Item 3</span></li>
<li class=\\"ix-menu-item ix-menu-level-1\\" aria-selected=\\"false\\" role=\\"menuitem\\"><i class=\\"ix-icon ix-icon-home\\" role=\\"img\\" aria-label=\\"home\\"></i><span><a>Item 1</a></span>
<!---->
</li>
<li class=\\"ix-menu-item ix-menu-level-1\\" aria-label=\\"Item 2\\" aria-selected=\\"false\\" role=\\"menuitem\\"><i class=\\"ix-icon ix-icon-mail\\" role=\\"img\\" aria-label=\\"mail\\"></i><span>Item 2</span>
<!---->
</li>
<li class=\\"ix-menu-item ix-menu-level-1 ix-menu-item-disabled\\" aria-label=\\"Item 3\\" aria-selected=\\"false\\" role=\\"menuitem\\"><i class=\\"ix-icon ix-icon-appstore\\" role=\\"img\\" aria-label=\\"appstore\\"></i><span>Item 3</span>
<!---->
</li>
<li class=\\"ix-menu-divider\\"></li>
<li class=\\"ix-menu-sub ix-menu-sub-vertical\\" aria-expanded=\\"false\\" aria-haspopup=\\"true\\" role=\\"menuitem\\">
<li class=\\"ix-menu-sub ix-menu-level-1\\" aria-expanded=\\"false\\" aria-haspopup=\\"true\\" role=\\"menuitem\\">
<div class=\\"ix-menu-sub-label\\"><i class=\\"ix-icon ix-icon-setting\\" role=\\"img\\" aria-label=\\"setting\\"></i><span>Sub Menu 1</span><span class=\\"ix-menu-sub-label-suffix\\"><i class=\\"ix-icon ix-icon-right\\" style=\\"transform: rotate(0deg);\\" role=\\"img\\" aria-label=\\"right\\"></i></span></div>
<!---->
</li>
<li class=\\"ix-menu-sub ix-menu-sub-disabled ix-menu-sub-vertical\\" aria-expanded=\\"false\\" aria-haspopup=\\"true\\" role=\\"menuitem\\">
<li class=\\"ix-menu-sub ix-menu-level-1 ix-menu-sub-disabled\\" aria-expanded=\\"false\\" aria-haspopup=\\"true\\" role=\\"menuitem\\">
<div class=\\"ix-menu-sub-label\\"><i class=\\"ix-icon ix-icon-github\\" role=\\"img\\" aria-label=\\"github\\"></i><span>Menu Sub 4</span><span class=\\"ix-menu-sub-label-suffix\\"><i class=\\"ix-icon ix-icon-right\\" style=\\"transform: rotate(0deg);\\" role=\\"img\\" aria-label=\\"right\\"></i></span></div>
<!---->
</li>
Expand Down
12 changes: 11 additions & 1 deletion packages/components/menu/demo/StateSwitching.vue
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,21 @@ const dataSource: MenuData[] = [
key: 'sub4',
icon: 'github',
label: 'Menu Sub 4',
disabled: true,
children: [
{ key: 'item10', label: 'Item 10' },
{ key: 'item11', label: 'Item 11' },
],
},
{
type: 'sub',
key: 'sub5',
icon: 'gitlab',
label: 'Menu Sub 5',
disabled: true,
children: [
{ key: 'item12', label: 'Item 12' },
{ key: 'item13', label: 'Item 13' },
],
},
]
</script>
7 changes: 5 additions & 2 deletions packages/components/menu/docs/Api.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@
| `customAdditional` | 自定义下拉选项的额外属性 | `MenuCustomAdditional` | - | - | 例如 `class`, 或者原生事件 |
| `dataSource` | 菜单数据数组 | `MenuData[]` | - | - | 优先级高于 `default` 插槽 |
| `getKey` | 获取数据的唯一标识 | `string \| (data: MenuData) => VKey` | `key` || - |
| `indent` | `inline` 模式时的菜单缩进宽度 | `string \| number` | `24` || `seer` 主题下默认为 `16` |
| `indent` | `inline` 模式时的菜单缩进宽度 | `string \| number` | `16` || - |
| `mode` | 菜单模式,现在支持垂直、水平和内嵌 | `'vertical' \| 'horizontal' \| 'inline'` | `'vertical'` | - | - |
| `multiple` | 是否支持多选 | `boolean` | `false` | - | - |
| `overlayClassName` | 悬浮层的自定义 `class` | `string` | - | - | `inline` 模式时无效 |
| `overlayContainer` | 自定义菜单容器节点 | `string \| HTMLElement \| () => string \| HTMLElement` | - || `inline` 模式时无效 |
| `overlayDelay` | 浮层的打开和关闭延迟 | `number \| [number | null, number | null]` | `[0, 100]` | - | 为数组时,第一个元素是延迟显示的时间,第二个元素是延迟隐藏的时间 |
| `selectable` | 是否允许选中 | `boolean` | `true` | - | - |
| `theme` | 主题颜色 | `'light' \| 'dark'` | `'light'` || - |
| `onClick` | 点击菜单后的回调 | `(options: MenuClickOptions) => void` | - | - |
Expand All @@ -41,8 +42,10 @@ export type MenuCustomAdditional = (options: { data: MenuData; index: number })
| `disabled` | 是否禁用 | `boolean` | - | - | - |
| `icon` | 菜单图标| `string \| VNode` | - | - |
| `label` | 菜单文本 | `string` | - | - |
| `suffix` | 后缀图标 | `string` | - | - | - |
| `customIcon` | 自定义图标 | `string \| ((data: MenuItemProps & { selected: boolean }) => VNodeChild)` | `'itemIcon'` | - | 类型为 `string` 时,对应插槽名 |
| `customLabel` | 自定义文本 | `string \| ((data: MenuItemProps & { selected: boolean }) => VNodeChild)` | `'itemLabel'` | - | 类型为 `string` 时,对应插槽名 |
| `customSuffix` | 自定义文本 | `string \| ((data: MenuItemProps & { selected: boolean }) => VNodeChild)` | `'itemLabel'` | - | 类型为 `string` 时,对应插槽名 |
#### MenuItemGroupProps
Expand All @@ -67,7 +70,7 @@ export type MenuCustomAdditional = (options: { data: MenuData; index: number })
| `icon` | 菜单图标| `string \| VNode` | - | - |
| `label` | 菜单文本 | `string` | - | - |
| `offset` | 浮层偏移量 | `[number, number]` | `[0, 4]` | ✅ | `inline` 模式时无效 |
| `suffix` | 后缀图标 | `string` | `right` | ✅ | - |
| `suffix` | 后缀图标 | `string` | `right` | ✅ | `horizontal` 模式下全局配置不生效 |
| `customIcon` | 自定义图标 | `string \| ((data: MenuSubProps & { expanded: boolean; selected: boolean }) => VNodeChild)` | `'subIcon'` | - | 类型为 `string` 时,对应插槽名 |
| `customLabel` | 自定义文本 | `string \| ((data: MenuSubProps & { expanded: boolean; selected: boolean }) => VNodeChild)` | `'subLabel'` | - | 类型为 `string` 时,对应插槽名 |
| `customSuffix` | 自定义后缀图标 | `string \| ((data: MenuSubProps & { expanded: boolean; selected: boolean }) => VNodeChild)` | `'subSuffix'` | - | 类型为 `string` 时,对应插槽名 |
Expand Down
2 changes: 1 addition & 1 deletion packages/components/menu/docs/Index.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ category: components
type: 导航
title: Menu
subtitle: 导航菜单
order: 0
theme: true
---

60 changes: 26 additions & 34 deletions packages/components/menu/docs/Theme.zh.md
Original file line number Diff line number Diff line change
@@ -1,39 +1,31 @@
| 名称 | default | seer | 备注 |
| --- | --- | --- | --- |
| `@menu-text-color` | `@text-color` | - | - |
| `@menu-background-color` | `@background-color-component` | - | - |
| `@menu-highlight-color` | `@color-primary` | - | - |
| `@menu-highlight-background-color` | `~colorPalette('@{color-primary}', -50) ` | - | - |
| `@menu-disabled-color` | `@disabled-color` | - | - |
| `@menu-disabled-background-color` | `transparent` | - | - |
| `@menu-border-width` | `@border-width-sm` | - | - |
| `@menu-border-style` | `@border-style` | - | - |
| `@menu-border-color` | `@border-color-split` | - | - |
| `@menu-font-size` | `@font-size-md` | - | - |
| `@menu-vertical-padding` | `4px 8px` | - | - |
| `@menu-vertical-font-size` | `@font-size-md` | - | - |
| `@menu-item-height` | `@height-lg` | `44px` | - |
| `@menu-item-padding-left` | `@spacing-md` | - | - |
| `@menu-item-padding-right` | `@spacing-xl` | - | - |
| `@menu-item-margin` | `@spacing-xs 0` | - | - |
| `@menu-item-icon-margin-right` | `@spacing-sm` | - | - |
| `@menu-item-border-right-width` | `@border-width-lg` | - | - |
| `@menu-item-border-bottom-width` | `@border-width-md` | - | - |
| `@menu-item-group-text-color` | `@text-color-secondary` | - | - |
| `@menu-item-group-content-padding-left` | `@spacing-xl` | - | - |
| `@menu-item-horizontal-padding` | `0 @spacing-xs` | - | - |
| `@menu-item-divider-margin` | `@spacing-xs` | - | - |
| `@menu-sub-suffix-icon-right` | `8px` | - | - |
| `@menu-text-color` | `var(--ix-text-color)` | - | - |
| `@menu-text-color-hover` | `var(--ix-text-color)` | - | - |
| `@menu-text-color-active` | `var(--ix-color-primary)` | - | - |
| `@menu-text-color-disabled` | `var(--ix-text-color-disabled)` | - | - |
| `@menu-background-color` | `var(--ix-background-color)` | - | - |
| `@menu-background-color-hover` | `var(--ix-background-color-middle)` | - | - |
| `@menu-background-color-active` | `var(--ix-color-primary-l50)` | - | - |
| `@menu-background-color-disabled` | `var(--ix-background-color)` | - | - |
| `@menu-horizontal-background-color-active` | `var(--ix-background-color-light)` | - | - |
| `@menu-border-color` | `var(--ix-border-color)` | - | - |
| `@menu-height` | `var(--ix-height-md)` | `36px` | - |
| `@menu-border-radius` | `var(--ix-border-radius-md)` | `0` | - |
| `@menu-font-size` | `var(--ix-font-size-md)` | `var(--ix-font-size-sm)` | - |
| `@menu-icon-font-size` | `var(--ix-font-size-lg)` | - | - |
| `@menu-item-group-text-color` | `var(--ix-text-color-info)` | - | - |
| `@menu-overlay-min-width` | `128px` | - | - |
| `@menu-overlay-border-radius` | `@border-radius-md` | `@border-radius-sm` | - |
| `@menu-overlay-box-shadow` | `@shadow-bottom-md` | - | - |
| `@menu-dark-disabled-color` | `@disabled-color-dark` | - | - |
| `@menu-dark-color` | `@text-color-dark` | `@color-graphite-l30` | - |
| `@menu-dark-background-color` | `@background-color-component-dark` | `@color-graphite-d40` | - |
| `@menu-dark-highlight-background-color` | `fade(@color-white, 6%)` | `@color-graphite-d30` | - |
| `@menu-dark-highlight-color` | `@color-primary` | `@color-graphite-l30` | - |
| `@menu-dark-group-label-color` | `@color-graphite-d10` | - | - |
| `@menu-collapsed-font-size` | `@font-size-lg` | - | - |
| `@menu-collapsed-width` | `64px` | `44px` | - |
| `@menu-sub-inline-font-size` | `@font-size-md` | - | - |
| `@menu-icon-font-size` | `@font-size-lg` | - | - |
| `@menu-collapsed-width` | `48px` | `44px` | - |
| `@menu-dark-text-color` | `var(--ix-text-color-inverse)` | - | - |
| `@menu-dark-text-color-hover` | `var(--ix-color-primary)` | `var(--ix-text-color-inverse)` | - |
| `@menu-dark-text-color-active` | `var(--ix-color-primary)` | `var(--ix-text-color-inverse)` | - |
| `@menu-dark-text-color-disabled` | `var(--ix-text-color-inverse-disabled)` | - | - |
| `@menu-dark-background-color` | `var(--ix-background-color-inverse)` | - | - |
| `@menu-dark-background-color-hover` | `transparent` | `@color-graphite-d50` | - |
| `@menu-dark-background-color-active` | `@color-black` | `var(--ix-color-primary)` | - |
| `@menu-dark-background-color-disabled` | `transparent` | - | - |
| `@menu-dark-horizontal-background-color-active` | `@color-grey-d30` | `@color-graphite-d30` | - |
| `@menu-dark-item-group-text-color` | `@color-grey-d10` | `@color-graphite-d10` | - |
17 changes: 11 additions & 6 deletions packages/components/menu/src/contents/MenuItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,20 @@ export default defineComponent({
} = inject(menuToken)!
const menuSubContext = inject(menuSubToken, null)
const menuItemGroupContext = inject(menuItemGroupToken, false)
const level = menuSubContext ? menuSubContext.level + 1 : 1

const isSelected = computed(() => selectedKeys.value.includes(key))

const classes = computed(() => {
const prefixCls = `${mergedPrefixCls.value}-item`
const prefixCls = mergedPrefixCls.value
return normalizeClass({
[prefixCls]: true,
[`${prefixCls}-disabled`]: props.data.disabled,
[`${prefixCls}-selected`]: isSelected.value,
[`${prefixCls}-item`]: true,
[`${prefixCls}-level-${level}`]: true,
[`${prefixCls}-item-disabled`]: props.data.disabled,
[`${prefixCls}-item-selected`]: isSelected.value,
})
})

const level = menuSubContext ? menuSubContext.level + 1 : 1
const mode = computed(() => menuProps.mode)
const paddingLeft = usePaddingLeft(menuProps, mode, indent, level, menuItemGroupContext)
const style = computed(() => {
Expand All @@ -60,19 +61,22 @@ export default defineComponent({
}

return () => {
const { disabled, icon, label, customIcon, customLabel } = props.data
const { disabled, icon, label, customIcon, customLabel, customSuffix } = props.data

const iconRender = customIcon ?? 'itemIcon'
const iconSlot = isString(iconRender) ? menuSlots[iconRender] : iconRender
const labelRender = customLabel ?? 'itemLabel'
const labelSlot = isString(labelRender) ? menuSlots[labelRender] : labelRender
const suffixRender = customSuffix ?? 'itemSuffix'
const suffixSlot = isString(suffixRender) ? menuSlots[suffixRender] : suffixRender

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
const suffixNode = coverIcon(suffixSlot, slotProps!, props.data.suffix)
return (
<li
class={classes.value}
Expand All @@ -85,6 +89,7 @@ export default defineComponent({
>
{iconNode}
<span>{labelNode}</span>
{suffixNode && <span class={`${mergedPrefixCls.value}-item-suffix`}>{suffixNode}</span>}
</li>
)
}
Expand Down
12 changes: 10 additions & 2 deletions packages/components/menu/src/contents/MenuItemGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* found in the LICENSE file at https://github.com/IDuxFE/idux/blob/main/LICENSE
*/

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

import { isString } from 'lodash-es'

Expand Down Expand Up @@ -41,6 +41,14 @@ export default defineComponent({
const paddingLeft = usePaddingLeft(menuProps, mode, indent, level, !!menuItemGroupContext)
const labelStyle = computed(() => ({ paddingLeft: paddingLeft.value }))

const classes = computed(() => {
const prefixCls = mergedPrefixCls.value
return normalizeClass({
[`${prefixCls}-item-group`]: true,
[`${prefixCls}-level-${level}`]: true,
})
})

const onClick = (evt: Event) => {
handleClick(key, 'itemGroup', evt)
}
Expand All @@ -62,7 +70,7 @@ export default defineComponent({
? menuProps.customAdditional({ data: props.data, index: props.index })
: undefined
return (
<li class={prefixCls} aria-label={label} {...customAdditional}>
<li class={classes.value} aria-label={label} {...customAdditional}>
<div class={`${prefixCls}-label`} style={labelStyle.value} onClick={onClick}>
{iconNode}
<span>{labelNode}</span>
Expand Down
11 changes: 9 additions & 2 deletions packages/components/menu/src/contents/menu-sub/Label.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,14 @@ export default defineComponent({
const { props, key, isExpanded, isSelected, changeExpanded, handleMouseEvent, mode, paddingLeft } =
inject(menuSubToken)!

const suffix = computed(() => props.data.suffix ?? config.suffix)
const mergedSuffix = computed(() => {
const { suffix } = props.data
if (suffix) {
return suffix
}
return mode.value !== 'horizontal' ? config.suffix : undefined
})

const rotate = computed(() => {
if (mode.value === 'inline') {
return isExpanded.value ? -90 : 90
Expand Down Expand Up @@ -66,7 +73,7 @@ export default defineComponent({
: undefined
const iconNode = coverIcon(iconSlot, slotProps!, icon)
const labelNode = labelSlot ? labelSlot(slotProps!) : label
const suffixNode = coverIcon(suffixSlot, slotProps!, suffix.value, rotate.value)
const suffixNode = coverIcon(suffixSlot, slotProps!, mergedSuffix.value, rotate.value)

const prefixCls = `${mergedPrefixCls.value}-sub-label`
return (
Expand Down
16 changes: 7 additions & 9 deletions packages/components/menu/src/contents/menu-sub/MenuSub.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@ import InlineContent from './InlineContent'
import Label from './Label'
import OverlayContent from './OverlayContent'

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

export default defineComponent({
name: 'MenuSub',
props: menuSubProps,
Expand Down Expand Up @@ -85,13 +83,13 @@ export default defineComponent({
const placement = computed(() => (mode.value === 'vertical' ? 'rightStart' : 'bottomStart'))

const classes = computed(() => {
const prefixCls = `${mergedPrefixCls.value}-sub`
const prefixCls = mergedPrefixCls.value
return normalizeClass({
[prefixCls]: true,
[`${prefixCls}-disabled`]: props.data.disabled,
[`${prefixCls}-expanded`]: isExpanded.value,
[`${prefixCls}-selected`]: isSelected.value,
[`${prefixCls}-${mode.value}`]: true,
[`${prefixCls}-sub`]: true,
[`${prefixCls}-level-${level}`]: true,
[`${prefixCls}-sub-disabled`]: props.data.disabled,
[`${prefixCls}-sub-expanded`]: isExpanded.value,
[`${prefixCls}-sub-selected`]: isSelected.value,
})
})

Expand Down Expand Up @@ -121,7 +119,7 @@ export default defineComponent({
autoAdjust
container={mergedOverlayContainer.value}
destroyOnHide={false}
delay={defaultDelay}
delay={menuProps.overlayDelay}
disabled={disabled}
offset={offset.value}
placement={placement.value}
Expand Down
6 changes: 6 additions & 0 deletions packages/components/menu/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ export const menuProps = {
type: [String, HTMLElement, Function] as PropType<PortalTargetType>,
default: undefined,
},
overlayDelay: {
type: [Number, Array] as PropType<number | [number | null, number | null]>,
default: [0, 100],
},
selectable: {
type: Boolean,
default: true,
Expand All @@ -72,8 +76,10 @@ export interface MenuItemProps<K = VKey> {
disabled?: boolean
icon?: string | VNode
label?: string
suffix?: string
customIcon?: string | ((data: MenuItemProps<K> & { selected: boolean }) => VNodeChild)
customLabel?: string | ((data: MenuItemProps<K> & { selected: boolean }) => VNodeChild)
customSuffix?: string | ((data: MenuItemProps<K> & { selected: boolean }) => VNodeChild)
}
export type MenuItemPublicProps = Omit<MenuItemProps, 'type'>
export type MenuItemComponent = FunctionalComponent<
Expand Down
Loading

0 comments on commit f1dde91

Please sign in to comment.