Skip to content

Commit

Permalink
feat(pro:layout): add siderHover and compress props (#759)
Browse files Browse the repository at this point in the history
  • Loading branch information
typistZxd committed Feb 21, 2022
1 parent c947a1f commit faf0913
Show file tree
Hide file tree
Showing 17 changed files with 224 additions and 39 deletions.
4 changes: 4 additions & 0 deletions packages/components/menu/demo/StateSwitching.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,12 @@ order: 5
- 支持 2 种主题:亮色和暗黑,默认为亮色
- 支持 2 种状态:展开和收起,默认为展开

> 提示:为了用户更灵活控制,折叠后状态由用户自行控制。若采用inline模式,为了视图更好地展示,建议折叠后将模式设置为vertical。
## en

- Support three modes: vertical, horizontal and inline, the default is vertical
- Support two themes: light and dark, the default is light
- Supports 2 states: expanded and collapsed, the default is expanded

> Tip: For more flexible control by the user, the collapsed state is controlled by the user. If inline mode is used, in order to display the view better, it is recommended to set the mode to vertical after folding.
5 changes: 1 addition & 4 deletions packages/components/menu/src/Menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,7 @@ export default defineComponent({
const config = useGlobalConfig('menu')

const indent = computed(() => props.indent ?? config.indent)
const mode = computed(() => {
const { collapsed, mode } = props
return collapsed && mode !== 'horizontal' ? 'vertical' : mode
})
const mode = computed(() => props.mode)

const theme = computed(() => props.theme ?? config.theme)

Expand Down
5 changes: 3 additions & 2 deletions packages/components/menu/src/composables/usePaddingLeft.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,17 @@

import { type ComputedRef, computed } from 'vue'

import { type MenuMode } from '../types'
import { type MenuMode, type MenuProps } from '../types'

export const usePaddingLeft = (
menuProps: MenuProps,
mode: ComputedRef<MenuMode>,
indent: ComputedRef<number>,
level: number,
grouped: boolean,
): ComputedRef<string | undefined> => {
return computed(() => {
if (mode.value !== 'inline') {
if (mode.value !== 'inline' || menuProps.collapsed) {
return undefined
}
const groupedLeft = grouped ? 8 : 0
Expand Down
3 changes: 2 additions & 1 deletion packages/components/menu/src/contents/MenuItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export default defineComponent({

// menuContext must exist
const {
props: menuProps,
slots: menuSlots,
mergedPrefixCls,
indent,
Expand All @@ -48,7 +49,7 @@ export default defineComponent({
})

const level = menuSubContext ? menuSubContext.level + 1 : 1
const paddingLeft = usePaddingLeft(mode, indent, level, menuItemGroupContext)
const paddingLeft = usePaddingLeft(menuProps, mode, indent, level, menuItemGroupContext)
const style = computed(() => {
return { paddingLeft: paddingLeft.value }
})
Expand Down
4 changes: 2 additions & 2 deletions packages/components/menu/src/contents/MenuItemGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@ export default defineComponent({
provide(menuItemGroupToken, true)

// menuContext must exist
const { slots: menuSlots, mergedPrefixCls, mode, indent, handleClick } = inject(menuToken)!
const { props: menuProps, slots: menuSlots, mergedPrefixCls, mode, indent, handleClick } = inject(menuToken)!
const menuSubContext = inject(menuSubToken, null)
const menuItemGroupContext = inject(menuItemGroupToken, null)

const level = menuSubContext ? menuSubContext.level + 1 : 1
const paddingLeft = usePaddingLeft(mode, indent, level, !!menuItemGroupContext)
const paddingLeft = usePaddingLeft(menuProps, mode, indent, level, !!menuItemGroupContext)
const labelStyle = computed(() => ({ paddingLeft: paddingLeft.value }))

const onClick = (evt: Event) => {
Expand Down
2 changes: 1 addition & 1 deletion packages/components/menu/src/contents/menu-sub/MenuSub.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export default defineComponent({
const target = computed(() => menuProps.target ?? config.target ?? `${mergedPrefixCls.value}-overlay-container`)
const mode = useMode(menuMode, menuSubContext)
const level = menuSubContext ? menuSubContext.level + 1 : 1
const paddingLeft = usePaddingLeft(mode, indent, level, menuItemGroupContext)
const paddingLeft = usePaddingLeft(menuProps, mode, indent, level, menuItemGroupContext)
const isSelected = computed(() => getState(props.data!.children, selectedKeys.value))
const { isExpanded, changeExpanded, handleMouseEvent } = useExpanded(props, key, expandedKeys, handleExpand, mode)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ exports[`ProLayout render work 1`] = `
<!---->
<div class=\\"ix-pro-layout-sider-content\\">
<ul class=\\"ix-menu ix-menu-light ix-menu-inline\\">
<li class=\\"ix-menu-sub ix-menu-sub-expanded ix-menu-sub-selected ix-menu-sub-inline\\">
<div class=\\"ix-menu-sub-label\\" style=\\"padding-left: 24px;\\"><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(-90deg);\\" role=\\"img\\" aria-label=\\"right\\"></i></span></div>
<li class=\\"ix-menu-sub ix-menu-sub-selected ix-menu-sub-inline\\">
<div class=\\"ix-menu-sub-label\\" style=\\"padding-left: 24px;\\"><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(90deg);\\" role=\\"img\\" aria-label=\\"right\\"></i></span></div>
<transition-stub>
<ul class=\\"ix-menu-inline\\">
<ul class=\\"ix-menu-inline\\" style=\\"display: none;\\">
<li class=\\"ix-menu-item ix-menu-item-disabled\\" style=\\"padding-left: 48px;\\"><i class=\\"ix-icon ix-icon-setting\\" role=\\"img\\" aria-label=\\"setting\\"></i><span>Item 4</span></li>
<li class=\\"ix-menu-item ix-menu-item-selected\\" style=\\"padding-left: 48px;\\"><i class=\\"ix-icon ix-icon-setting\\" role=\\"img\\" aria-label=\\"setting\\"></i><span>Item 5</span></li>
<li class=\\"ix-menu-divider\\"></li>
Expand Down
40 changes: 39 additions & 1 deletion packages/pro/layout/__tests__/layoutPro.spec.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { MountingOptions, flushPromises, mount } from '@vue/test-utils'
import { h } from 'vue'

import { renderWork } from '@tests'
import { renderWork, wait } from '@tests'

import ProLayout from '../src/Layout'
import LayoutSiderTrigger from '../src/LayoutSiderTrigger'
import LayoutSider from '../src/contents/Sider'
import { ProLayoutMenuData, ProLayoutProps } from '../src/types'

const dataSource: ProLayoutMenuData[] = [
Expand Down Expand Up @@ -115,6 +116,43 @@ describe('ProLayout', () => {
expect(onUpdateCollapsed).toBeCalledWith(true)
})

test('compress work', async () => {
const wrapper = ProLayoutMount({})
await flushPromises()

expect(wrapper.classes()).not.toContain('ix-pro-layout-float')

await wrapper.setProps({ compress: false })

expect(wrapper.classes()).toContain('ix-pro-layout-float')

await wrapper.setProps({ compress: true })

expect(wrapper.classes()).not.toContain('ix-pro-layout-float')
})

test('collapsedDelay work', async () => {
const onUpdateCollapsed = jest.fn()
const wrapper = ProLayoutMount({
props: {
siderHover: { delay: 10 },
collapsed: true,
'onUpdate:collapsed': onUpdateCollapsed,
},
})
await flushPromises()

expect(wrapper.find('.ix-pro-layout-sider-collapsed').exists()).toBe(true)

await wrapper.findComponent(LayoutSider).trigger('mouseenter')

expect(onUpdateCollapsed).not.toBeCalled()

await wait(10)

expect(onUpdateCollapsed).toBeCalledWith(false)
})

test('fixed work', async () => {
const wrapper = ProLayoutMount({ props: { fixed: true } })
await flushPromises()
Expand Down
10 changes: 8 additions & 2 deletions packages/pro/layout/demo/Collapsed.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,14 @@ order: 4

## zh

可以通过设置 `collapsed` 或者使用 `IxProLayoutSiderTrigger` 来控制侧边栏的折叠状态。
- 可以通过设置 `collapsed` 或者使用 `IxProLayoutSiderTrigger` 来控制侧边栏的折叠状态。
- 可以通过配置 `siderHover` 是否通过悬浮侧边栏展开,同时可以设置悬浮展开的延迟时间,单位为`ms`

> 注意:启用 `siderHover` 则不允许设置 `siderMenu.mode`,因为 `mode` 已经内置
## en

The collapse of the sider can be controlled by setting `collapsed` or using `IxProLayoutSiderTrigger`.
- The collapse of the sider can be controlled by setting `collapsed` or using `IxProLayoutSiderTrigger`.
- You can configure whether `siderHover` is expanded through the floating sidebar, and you can set the delay time of the hovering expansion, the unit is `ms`.

> Note: enabling `siderHover` does not allow setting `siderMenu.mode`, because `mode` is already built-in
14 changes: 12 additions & 2 deletions packages/pro/layout/demo/Collapsed.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
<template>
<IxProLayout v-model:activeKey="activeKey" v-model:collapsed="collapsed" :menus="dataSource">
<IxProLayout
v-model:activeKey="activeKey"
v-model:collapsed="collapsed"
:siderHover="siderHover"
:menus="dataSource"
:compress="false"
:style="{ height: '300px' }"
>
<template #itemLabel="item">
<router-link :to="item.key">{{ item.label }}</router-link>
</template>
Expand All @@ -21,7 +28,10 @@ import { ref } from 'vue'
import { type MenuData } from '@idux/components/menu'
const activeKey = ref()
const collapsed = ref(false)
const collapsed = ref(true)
const siderHover = ref({
delay: 500,
})
const dataSource: MenuData[] = [
{
type: 'sub',
Expand Down
10 changes: 9 additions & 1 deletion packages/pro/layout/docs/Index.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ order: 0
| `v-model:collapsed` | 左侧菜单折叠收起 | `boolean` | `false` | - | 仅对于侧边菜单生效 |
| `fixed` | 固定 | `boolean \| {sider: boolean, header: boolean}` | `false` | - | - |
| `menus` | 菜单数据 | `MenuData[]` | `[]` | - | - |
| `compress` | 展开侧边栏是否压缩右侧内容区域 | `boolean` | `true` | - | 设置为 `false`时必须设置 `IxProLayout` 高度 |
| `sider` | 侧边栏的更多配置 | `LayoutSiderProps` | - | - | 例如:可以配置响应式断点:`breakpoint` |
| `siderHover` | 鼠标悬浮侧边栏时展开 | `boolean \| SiderHover` | `-` | - | `delay`单位为 `ms` |
| `siderMenu` | 侧边栏菜单组件的更多配置 | `MenuProps` | - | - | 例如:可以配置缩进宽度:`indent` 和菜单模式: `mode` |
| `theme` | 主题 | `light \| dark \| {sider: light \| dark, header: light \| dark}` | `light` | - | - |
| `type` | 布局类型 | `'header' \| 'sider' \| 'both' \| 'mixin'` | `mixin` | - | 参见示例:[布局类型](#pro-layout-demo-Type) |
Expand All @@ -39,6 +41,12 @@ order: 0
> 为了便于自定义导航菜单,除了上面表格中列举出来的插槽外,还支持 `IxMenu` 的全部插槽,参见 [MenuSlots](/components/menu/zh#MenuSlots).
> 默认会将 `IxProLayout` 的所有插槽传递给内部的 `IxMenu` 组件,参考示例中的 `itemLabel` 插槽。
```ts
interface SiderHover {
delay: number
}
```

### IxProLayoutSiderTrigger

侧边栏折叠状态触发器,可以控制侧边栏的折叠状态,可以放在 `IxProLayout` 中的任意位置。
Expand Down Expand Up @@ -83,4 +91,4 @@ order: 0
| `@pro-layout-sider-trigger-background` | `inherit` | - | - |
| `@pro-layout-sider-trigger-color` | `@text-color` | - | - |
| `@pro-layout-sider-trigger-active-color` | `@color-primary` | - | - |
<!--- insert less variable end --->
<!--- insert less variable end --->
54 changes: 51 additions & 3 deletions packages/pro/layout/src/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
* found in the LICENSE file at https://github.com/IDuxFE/idux/blob/main/LICENSE
*/

import { computed, defineComponent, normalizeClass, provide } from 'vue'
import { type ComputedRef, type Ref, computed, defineComponent, normalizeClass, provide, ref } from 'vue'

import { isBoolean, isUndefined } from 'lodash-es'

import { useControlledProp } from '@idux/cdk/utils'
import { IxLayout, IxLayoutContent, IxLayoutFooter } from '@idux/components/layout'
Expand All @@ -16,7 +18,7 @@ import { useHeaderMenus, useSiderMenus } from './composables/useMenu'
import Header from './contents/Header'
import Sider from './contents/Sider'
import { proLayoutToken } from './token'
import { proLayoutProps } from './types'
import { type ProLayoutProps, type SiderHoverCtrl, proLayoutProps } from './types'
import { getTargetPaths } from './utils/menu'

export default defineComponent({
Expand All @@ -32,6 +34,9 @@ export default defineComponent({
const activeHeaderKey = useActiveHeaderKey(props, activePaths, headerMenus)
const siderMenus = useSiderMenus(props, activeHeaderKey)
const [collapsed, setCollapsed] = useControlledProp(props, 'collapsed', false)
const siderHover = useHoverTrigger(props)

const { handleCollapsedDelay } = useHandleCollapsedDelay(siderHover, setCollapsed)

provide(proLayoutToken, {
props,
Expand All @@ -44,16 +49,19 @@ export default defineComponent({
activeHeaderKey,
siderMenus,
collapsed,
siderHover,
handleCollapsedDelay, // 延迟折叠
setCollapsed,
})

const layoutClasses = computed(() => {
const { type, fixed } = props
const { type, fixed, compress } = props
const prefixCls = mergedPrefixCls.value
return normalizeClass({
[prefixCls]: true,
[`${prefixCls}-is-${type}`]: true,
[`${prefixCls}-fixed`]: fixed,
[`${prefixCls}-float`]: !compress,
})
})

Expand All @@ -75,3 +83,43 @@ export default defineComponent({
}
},
})

function useHoverTrigger(props: ProLayoutProps): ComputedRef<SiderHoverCtrl> {
return computed(() => {
if (isUndefined(props.siderHover) || isBoolean(props.siderHover)) {
return {
enable: !!props.siderHover,
delay: 0,
}
}
return {
enable: true,
...props.siderHover,
}
})
}

function useHandleCollapsedDelay(siderHover: ComputedRef<SiderHoverCtrl>, setCollapsed: (collapsed: boolean) => void) {
const timer: Ref<number | null> = ref(null)

const handleCollapsedDelay = (collapsed: boolean) => {
if (siderHover.value.delay) {
timer.value && clearTimeout(timer.value)
if (!collapsed) {
timer.value = setTimeout(() => {
setCollapsed(collapsed)
timer.value = null
}, siderHover.value.delay)
} else {
setCollapsed(collapsed)
}
} else {
setCollapsed(collapsed)
}
}

return {
timer,
handleCollapsedDelay,
}
}
5 changes: 4 additions & 1 deletion packages/pro/layout/src/LayoutSiderTrigger.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@ export default defineComponent({
}
})

const handleClick = () => setCollapsed(!collapsed.value)
const handleClick = (evt: Event) => {
evt.stopPropagation()
setCollapsed(!collapsed.value)
}

return () => {
let children: VNodeTypes
Expand Down

0 comments on commit faf0913

Please sign in to comment.