diff --git a/packages/components/config/src/defaultConfig.ts b/packages/components/config/src/defaultConfig.ts index bb03c25e8..196356a8d 100644 --- a/packages/components/config/src/defaultConfig.ts +++ b/packages/components/config/src/defaultConfig.ts @@ -125,6 +125,7 @@ export const defaultConfig: GlobalConfig = { closable: true, closeOnEsc: true, closeIcon: 'close-filled', + distance: 160, height: 256, mask: true, maskClosable: true, diff --git a/packages/components/config/src/types.ts b/packages/components/config/src/types.ts index 8a0db8d60..b5131a79a 100644 --- a/packages/components/config/src/types.ts +++ b/packages/components/config/src/types.ts @@ -238,6 +238,7 @@ export interface DrawerConfig { closeIcon: string closeOnEsc: boolean container?: PortalTargetType + distance: number height: string | number mask: boolean maskClosable: boolean diff --git a/packages/components/drawer/demo/MultiLevel.vue b/packages/components/drawer/demo/MultiLevel.vue index db2d6abb3..347ab20e0 100644 --- a/packages/components/drawer/demo/MultiLevel.vue +++ b/packages/components/drawer/demo/MultiLevel.vue @@ -3,7 +3,7 @@

This is parent drawer

Open Child Drawer - +

This is child drawer

Open Grandchild Drawer diff --git a/packages/components/drawer/docs/Api.zh.md b/packages/components/drawer/docs/Api.zh.md index 07b11df91..78cd9f296 100644 --- a/packages/components/drawer/docs/Api.zh.md +++ b/packages/components/drawer/docs/Api.zh.md @@ -19,6 +19,7 @@ | `maskClosable` | 点击蒙层是否允许关闭 | `boolean` | `true` | ✅ | - | | `offset` | 抽屉偏移量 | `number \| string` | `0` | - | `placement` 为`start/end` 时, 为顶部偏移量,`top/bottom` 时, 为左边偏移量 | | `placement` | 抽屉打开方向 | `'top' \| 'bottom' \| 'start' \| 'end'` | `'end'` | - | - | +| `distance` | 多层抽屉场景与子抽屉的距离 | `number` | `160` | ✅ | 如果大于抽屉的宽度,则以抽屉的宽度为距离 | | `width` | 抽屉宽度 | `string \| number` | `'480'` | ✅ | 默认值仅在 `placement为` 为 `start/end` 时生效,其他情况默认为 `100%` | | `zIndex` | 设置抽屉的 `z-index` | `number` | - | - | - | | `onAfterOpen` | 打开后的回调 | `() => void` | - | - | - | diff --git a/packages/components/drawer/src/Drawer.tsx b/packages/components/drawer/src/Drawer.tsx index 012d9398d..80c7fc649 100644 --- a/packages/components/drawer/src/Drawer.tsx +++ b/packages/components/drawer/src/Drawer.tsx @@ -22,6 +22,7 @@ import { } from 'vue' import { CdkPortal } from '@idux/cdk/portal' +import { useResizeObserver } from '@idux/cdk/resize' import { BlockScrollStrategy, type ScrollStrategy } from '@idux/cdk/scroll' import { callEmit, useControlledProp } from '@idux/cdk/utils' import { ɵMask } from '@idux/components/_private/mask' @@ -48,12 +49,15 @@ export default defineComponent({ const mergedPortalTarget = usePortalTarget(props, config, common, mergedPrefixCls) const mask = computed(() => props.mask ?? config.mask) + const mergedDistance = computed(() => props.distance ?? config.distance) const { loaded, delayedLoaded, visible, setVisible, animatedVisible, mergedVisible } = useVisible(props) const currentZIndex = useZIndex(toRef(props, 'zIndex'), toRef(common, 'overlayZIndex'), visible) + const drawerElRef = ref() + const { open, close } = useTrigger(props, setVisible) - const { level, levelAction, push, pull } = useLevel(visible) + const { levelAction, distance, push, pull } = useLevel(props, visible, mergedDistance, drawerElRef) provide(drawerToken, { props, @@ -61,13 +65,14 @@ export default defineComponent({ common, config, mergedPrefixCls, + drawerElRef, visible, delayedLoaded, animatedVisible, mergedVisible, currentZIndex, - level, levelAction, + distance, push, pull, }) @@ -181,27 +186,78 @@ function useTrigger(props: DrawerProps, setVisible: (visible: boolean) => void) return { open, close } } -function useLevel(visible: Ref) { +function useLevel( + props: DrawerProps, + visible: Ref, + mergedDistance: ComputedRef, + drawerElRef: Ref, +) { const parentContext = inject(drawerToken, null) const level = ref(0) + const distance = ref(0) const levelAction = ref<'push' | 'pull'>() + const drawerSize = ref(0) + const sizeAttrName = computed(() => (['top', 'bottom'].includes(props.placement) ? 'height' : 'width')) + + let sizeUpdateCb: (() => void) | undefined + + useResizeObserver(drawerElRef, ({ contentRect }) => { + drawerSize.value = contentRect[sizeAttrName.value] ?? 0 + + sizeUpdateCb?.() + }) + + const onSizeUpdate = (cb: () => void) => { + sizeUpdateCb = () => { + cb() + sizeUpdateCb = undefined + } + } + + const updateDistance = (childSize: number) => { + const minDisatance = Math.min(drawerSize.value, mergedDistance.value) + if (!drawerSize.value || !childSize || drawerSize.value - childSize >= minDisatance) { + distance.value = 0 + } else { + distance.value = minDisatance - (drawerSize.value - childSize) + } + } - const push = () => { + const push = (childSize: number) => { level.value++ - parentContext?.push() + + updateDistance(childSize) + pushParent() } - const pull = () => { + const pull = (childSize: number) => { level.value-- - parentContext?.pull() + + updateDistance(childSize) + pullParent() + } + + const pushParent = () => { + if (parentContext?.props.placement !== props.placement) { + return + } + + parentContext?.push(drawerSize.value + distance.value) + } + const pullParent = () => { + if (parentContext?.props.placement !== props.placement) { + return + } + + parentContext?.pull(visible.value ? drawerSize.value + distance.value : 0) } watch(visible, value => { if (value) { - parentContext?.push() + onSizeUpdate(pushParent) } else { - parentContext?.pull() + pullParent() levelAction.value = undefined } }) @@ -212,15 +268,15 @@ function useLevel(visible: Ref) { onMounted(() => { if (visible.value) { - parentContext?.push() + pushParent() } }) onBeforeUnmount(() => { if (visible.value) { - parentContext?.pull() + pullParent() } }) - return { level, levelAction, push, pull } + return { levelAction, distance, push, pull } } diff --git a/packages/components/drawer/src/DrawerWrapper.tsx b/packages/components/drawer/src/DrawerWrapper.tsx index 5b2beba4e..d0ed4f759 100644 --- a/packages/components/drawer/src/DrawerWrapper.tsx +++ b/packages/components/drawer/src/DrawerWrapper.tsx @@ -37,7 +37,6 @@ const drawerTransitionMap = { end: 'move-end', } const horizontalPlacement = ['start', 'end'] -const defaultDistance = 160 export default defineComponent({ inheritAttrs: false, @@ -49,13 +48,14 @@ export default defineComponent({ common, config, mergedPrefixCls, + drawerElRef, visible, delayedLoaded, animatedVisible, mergedVisible, currentZIndex, - level, levelAction, + distance, } = inject(drawerToken)! const { close } = inject(DRAWER_TOKEN)! const { closable, closeIcon, closeOnEsc, mask, maskClosable } = useConfig(props, config) @@ -83,12 +83,13 @@ export default defineComponent({ const transformStyle = computed(() => { const { placement } = props const horizontal = isHorizontal.value - const distance = level.value * defaultDistance let transform if (horizontal) { - transform = distance > 0 ? `translateX(${placement === 'start' ? distance : -distance}px)` : undefined + transform = + distance.value > 0 ? `translateX(${placement === 'start' ? distance.value : -distance.value}px)` : undefined } else { - transform = distance > 0 ? `translateY(${placement === 'top' ? distance : -distance}px)` : undefined + transform = + distance.value > 0 ? `translateY(${placement === 'top' ? distance.value : -distance.value}px)` : undefined } return transform }) @@ -158,6 +159,7 @@ export default defineComponent({ {delayedLoaded.value && (
+ drawerElRef: Ref visible: Ref delayedLoaded: Ref animatedVisible: Ref mergedVisible: ComputedRef currentZIndex: ComputedRef - level: Ref levelAction: Ref<'push' | 'pull' | undefined> - push: () => void - pull: () => void + distance: Ref + push: (childSize: number) => void + pull: (childSize: number) => void } export const drawerToken: InjectionKey = Symbol('drawerToken') diff --git a/packages/components/drawer/src/types.ts b/packages/components/drawer/src/types.ts index dad50c842..dfbbfdb23 100644 --- a/packages/components/drawer/src/types.ts +++ b/packages/components/drawer/src/types.ts @@ -77,6 +77,10 @@ export const drawerProps = { type: String as PropType, default: 'end', }, + distance: { + type: Number, + default: undefined, + }, scrollStrategy: Object as PropType, width: [String, Number] as PropType, zIndex: Number,