From 7cec4ec5d9c882d9fae54cba221efda34ba3bc93 Mon Sep 17 00:00:00 2001 From: danranVm Date: Wed, 14 Dec 2022 10:05:52 +0800 Subject: [PATCH] fix(comp:*): opening overlay once triggers two zIndex changes (#1355) --- .../_private/overlay/src/Overlay.tsx | 7 +++++-- .../components/_private/overlay/src/types.ts | 10 +++++++-- packages/components/cascader/src/Cascader.tsx | 6 +++--- .../src/composables/useOverlayProps.ts | 5 ++--- packages/components/dropdown/src/Dropdown.tsx | 5 ++--- .../menu/src/contents/menu-sub/MenuSub.tsx | 7 ++++--- .../components/popconfirm/src/Popconfirm.tsx | 1 - packages/components/popover/src/Popover.tsx | 7 +------ packages/components/select/src/Select.tsx | 5 ++--- .../src/composables/useOverlayProps.ts | 7 +++---- packages/components/tooltip/src/Tooltip.tsx | 2 +- .../tooltip/src/useTooltipOverlay.ts | 14 +++++-------- .../components/tree-select/src/TreeSelect.tsx | 6 +++--- packages/components/utils/src/portalTarget.ts | 21 ++----------------- packages/components/utils/src/zIndex.ts | 4 ++-- packages/pro/search/src/ProSearch.tsx | 2 +- .../src/composables/useCommonOverlayProps.ts | 7 ++----- 17 files changed, 46 insertions(+), 70 deletions(-) diff --git a/packages/components/_private/overlay/src/Overlay.tsx b/packages/components/_private/overlay/src/Overlay.tsx index e5fcc853d..19654b7e9 100644 --- a/packages/components/_private/overlay/src/Overlay.tsx +++ b/packages/components/_private/overlay/src/Overlay.tsx @@ -22,6 +22,8 @@ import { withDirectives, } from 'vue' +import { isFunction } from 'lodash-es' + import { vClickOutside } from '@idux/cdk/click-outside' import { type PopperElement, type PopperEvents, type PopperOptions, usePopper } from '@idux/cdk/popper' import { CdkPortal } from '@idux/cdk/portal' @@ -59,7 +61,8 @@ export default defineComponent({ const { destroy: popperDestroy } = usePopperInit(props, initialize, destroy) const currentZIndex = useZIndex(toRef(props, 'zIndex'), toRef(common, 'overlayZIndex'), visibility) const mergedContainer = computed(() => { - return () => props.container(convertElement(triggerRef)!) + const { container = common.overlayContainer } = props + return (isFunction(container) ? container(convertElement(triggerRef)!) : container) ?? props.containerFallback }) watch(visibility, value => callEmit(props['onUpdate:visible'], value)) @@ -221,7 +224,7 @@ function renderContent( } const prefixCls = mergedPrefixCls.value const { triggerId } = props - const overlayId = triggerId != null ? `overlay-${triggerId}` : undefined + const overlayId = triggerId != null ? `__IDUX_OVERLAY-${triggerId}` : undefined const style = `z-index: ${currentZIndex.value}` const overlay = (
diff --git a/packages/components/_private/overlay/src/types.ts b/packages/components/_private/overlay/src/types.ts index 3926a2bc5..8d7c3fe8a 100644 --- a/packages/components/_private/overlay/src/types.ts +++ b/packages/components/_private/overlay/src/types.ts @@ -7,6 +7,7 @@ import type { PopperOptions, PopperPlacement, PopperTrigger } from '@idux/cdk/popper' import type { ExtractInnerPropTypes, ExtractPublicPropTypes, MaybeArray } from '@idux/cdk/utils' +import type { OverlayContainerType } from '@idux/components/utils' import type { DefineComponent, HTMLAttributes, PropType } from 'vue' export const overlayPlacementDef = String as PropType @@ -18,6 +19,8 @@ export const overlayProps = { type: Boolean, default: undefined, }, + placement: overlayPlacementDef, + allowEnter: { type: Boolean, default: undefined, @@ -31,7 +34,11 @@ export const overlayProps = { default: undefined, }, container: { - type: Function as PropType<(element?: Element) => string | HTMLElement>, + type: [String, HTMLElement, Function] as PropType, + default: undefined, + }, + containerFallback: { + type: String, required: true, }, delay: overlayDelayDef, @@ -44,7 +51,6 @@ export const overlayProps = { default: undefined, }, offset: Array as unknown as PropType<[number, number]>, - placement: overlayPlacementDef, showArrow: { type: Boolean, default: undefined, diff --git a/packages/components/cascader/src/Cascader.tsx b/packages/components/cascader/src/Cascader.tsx index 7d784daeb..3ba2b4453 100644 --- a/packages/components/cascader/src/Cascader.tsx +++ b/packages/components/cascader/src/Cascader.tsx @@ -14,7 +14,7 @@ import { ɵSelector, type ɵSelectorInstance } from '@idux/components/_private/s import { useGlobalConfig } from '@idux/components/config' import { useFormItemRegister, useFormSize, useFormStatus } from '@idux/components/form' import { ɵUseOverlayState } from '@idux/components/select' -import { useGetKey, useOverlayContainer } from '@idux/components/utils' +import { useGetKey } from '@idux/components/utils' import { useActiveState } from './composables/useActiveState' import { useDataSource } from './composables/useDataSource' @@ -35,7 +35,6 @@ export default defineComponent({ const common = useGlobalConfig('common') const config = useGlobalConfig('cascader') const mergedPrefixCls = computed(() => `${common.prefixCls}-cascader`) - const mergedOverlayContainer = useOverlayContainer(props, config, common, mergedPrefixCls) const mergedChildrenKey = computed(() => props.childrenKey ?? config.childrenKey) const mergedClearIcon = computed(() => props.clearIcon ?? config.clearIcon) @@ -175,7 +174,8 @@ export default defineComponent({ const overlayProps = { class: overlayClasses.value, clickOutside: true, - container: mergedOverlayContainer.value, + container: props.overlayContainer ?? config.overlayContainer, + containerFallback: `.${mergedPrefixCls.value}-overlay-container`, disabled: accessor.disabled || props.readonly, offset: defaultOffset, placement: 'bottomStart', diff --git a/packages/components/date-picker/src/composables/useOverlayProps.ts b/packages/components/date-picker/src/composables/useOverlayProps.ts index 8f13dbd58..b7ac7f1a7 100644 --- a/packages/components/date-picker/src/composables/useOverlayProps.ts +++ b/packages/components/date-picker/src/composables/useOverlayProps.ts @@ -8,18 +8,17 @@ import { type ComputedRef, computed } from 'vue' import { type ɵOverlayProps } from '@idux/components/_private/overlay' -import { useOverlayContainer } from '@idux/components/utils' import { type DatePickerContext, type DateRangePickerContext } from '../token' const defaultOffset: [number, number] = [0, 4] export function useOverlayProps(context: DatePickerContext | DateRangePickerContext): ComputedRef<ɵOverlayProps> { const { props, common, config, accessor, mergedPrefixCls, overlayOpened, setOverlayOpened, onAfterLeave } = context - const mergedOverlayContainer = useOverlayContainer(props, config, common, mergedPrefixCls) return computed(() => { return { clickOutside: true, - container: mergedOverlayContainer.value, + container: props.overlayContainer ?? config.overlayContainer, + containerFallback: `.${mergedPrefixCls.value}-overlay-container`, disabled: accessor.disabled || props.readonly, offset: defaultOffset, placement: 'bottomStart', diff --git a/packages/components/dropdown/src/Dropdown.tsx b/packages/components/dropdown/src/Dropdown.tsx index d54076be2..29a8bfbcb 100644 --- a/packages/components/dropdown/src/Dropdown.tsx +++ b/packages/components/dropdown/src/Dropdown.tsx @@ -10,7 +10,6 @@ import { computed, defineComponent, provide, toRef } from 'vue' import { useControlledProp } from '@idux/cdk/utils' import { ɵOverlay } from '@idux/components/_private/overlay' import { type DropdownConfig, useGlobalConfig } from '@idux/components/config' -import { useOverlayContainer } from '@idux/components/utils' import { dropdownToken } from './token' import { type DropdownProps, dropdownProps } from './types' @@ -24,7 +23,6 @@ export default defineComponent({ const common = useGlobalConfig('common') const config = useGlobalConfig('dropdown') const mergedPrefixCls = computed(() => `${common.prefixCls}-dropdown`) - const mergedOverlayContainer = useOverlayContainer(props, config, common, mergedPrefixCls) const [visibility, setVisibility] = useControlledProp(props, 'visible', false) const configProps = useConfigProps(props, config, setVisibility) @@ -37,7 +35,8 @@ export default defineComponent({ visible={visibility.value} v-slots={{ default: slots.default, content: slots.overlay }} class={mergedPrefixCls.value} - container={mergedOverlayContainer.value} + container={props.overlayContainer ?? config.overlayContainer} + containerFallback={`.${mergedPrefixCls.value}-overlay-container`} delay={defaultDelay} disabled={props.disabled} transitionName={`${common.prefixCls}-fade`} diff --git a/packages/components/menu/src/contents/menu-sub/MenuSub.tsx b/packages/components/menu/src/contents/menu-sub/MenuSub.tsx index 378768937..eb991b0e0 100644 --- a/packages/components/menu/src/contents/menu-sub/MenuSub.tsx +++ b/packages/components/menu/src/contents/menu-sub/MenuSub.tsx @@ -23,7 +23,7 @@ import { debounce } from 'lodash-es' import { type VKey, useState } from '@idux/cdk/utils' import { ɵOverlay } from '@idux/components/_private/overlay' import { useGlobalConfig } from '@idux/components/config' -import { useKey, useOverlayContainer } from '@idux/components/utils' +import { useKey } from '@idux/components/utils' import { usePaddingLeft } from '../../composables/usePaddingLeft' import { type MenuSubContext, menuItemGroupToken, menuSubToken, menuToken } from '../../token' @@ -53,7 +53,7 @@ export default defineComponent({ const menuItemGroupContext = inject(menuItemGroupToken, false) const key = useKey() - const mergedOverlayContainer = useOverlayContainer(menuProps, config, common, mergedPrefixCls) + const mode = useMode(menuProps, menuSubContext) const level = menuSubContext ? menuSubContext.level + 1 : 1 const paddingLeft = usePaddingLeft(menuProps, mode, indent, level, menuItemGroupContext) @@ -117,7 +117,8 @@ export default defineComponent({ v-slots={{ default: trigger, content: content }} class={overlayClasses.value} autoAdjust - container={mergedOverlayContainer.value} + container={menuProps.overlayContainer ?? config.overlayContainer} + containerFallback={`.${mergedPrefixCls.value}-overlay-container`} destroyOnHide={false} delay={menuProps.overlayDelay} disabled={disabled} diff --git a/packages/components/popconfirm/src/Popconfirm.tsx b/packages/components/popconfirm/src/Popconfirm.tsx index 8209d8317..ac570b0d3 100644 --- a/packages/components/popconfirm/src/Popconfirm.tsx +++ b/packages/components/popconfirm/src/Popconfirm.tsx @@ -27,7 +27,6 @@ export default defineComponent({ const { overlayRef, updatePopper, visible, setVisible, overlayProps } = ɵUseTooltipOverlay( props, config, - common, mergedPrefixCls, ) diff --git a/packages/components/popover/src/Popover.tsx b/packages/components/popover/src/Popover.tsx index 43627100c..06432ed64 100644 --- a/packages/components/popover/src/Popover.tsx +++ b/packages/components/popover/src/Popover.tsx @@ -23,12 +23,7 @@ export default defineComponent({ const common = useGlobalConfig('common') const config = useGlobalConfig('popover') const mergedPrefixCls = computed(() => `${common.prefixCls}-popover`) - const { overlayRef, updatePopper, overlayProps, setVisible } = ɵUseTooltipOverlay( - props, - config, - common, - mergedPrefixCls, - ) + const { overlayRef, updatePopper, overlayProps, setVisible } = ɵUseTooltipOverlay(props, config, mergedPrefixCls) expose({ updatePopper }) diff --git a/packages/components/select/src/Select.tsx b/packages/components/select/src/Select.tsx index 814f079d2..a78d8c8dc 100644 --- a/packages/components/select/src/Select.tsx +++ b/packages/components/select/src/Select.tsx @@ -17,7 +17,6 @@ import { ɵOverlay } from '@idux/components/_private/overlay' import { ɵSelector, type ɵSelectorInstance } from '@idux/components/_private/selector' import { type SelectConfig, useGlobalConfig } from '@idux/components/config' import { useFormItemRegister, useFormSize, useFormStatus } from '@idux/components/form' -import { useOverlayContainer } from '@idux/components/utils' import { useActiveState } from './composables/useActiveState' import { GetKeyFn, useGetOptionKey } from './composables/useGetOptionKey' @@ -38,7 +37,6 @@ export default defineComponent({ const common = useGlobalConfig('common') const config = useGlobalConfig('select') const mergedPrefixCls = computed(() => `${common.prefixCls}-select`) - const mergedOverlayContainer = useOverlayContainer(props, config, common, mergedPrefixCls) const triggerRef = ref<ɵSelectorInstance>() const focus = () => triggerRef.value?.focus() @@ -196,7 +194,8 @@ export default defineComponent({ class: overlayClasses.value, style: overlayStyle.value, clickOutside: true, - container: mergedOverlayContainer.value, + container: props.overlayContainer ?? config.overlayContainer, + containerFallback: `.${mergedPrefixCls.value}-overlay-container`, disabled: accessor.disabled || props.readonly, offset: props.offset ?? config.offset, placement: 'bottomStart', diff --git a/packages/components/time-picker/src/composables/useOverlayProps.ts b/packages/components/time-picker/src/composables/useOverlayProps.ts index ce568c47f..d64c69da4 100644 --- a/packages/components/time-picker/src/composables/useOverlayProps.ts +++ b/packages/components/time-picker/src/composables/useOverlayProps.ts @@ -9,18 +9,17 @@ import type { ɵOverlayProps } from '@idux/components/_private/overlay' import { type ComputedRef, computed } from 'vue' -import { useOverlayContainer } from '@idux/components/utils' - import { TimePickerContext, TimeRangePickerContext } from '../tokens' const defaultOffset: [number, number] = [0, 4] export function useOverlayProps(context: TimePickerContext | TimeRangePickerContext): ComputedRef<ɵOverlayProps> { const { props, common, config, accessor, mergedPrefixCls, overlayOpened, setOverlayOpened } = context - const mergedOverlayContainer = useOverlayContainer(props, config, common, mergedPrefixCls) + return computed(() => { return { clickOutside: true, - container: mergedOverlayContainer.value, + container: props.overlayContainer ?? config.overlayContainer, + containerFallback: `.${mergedPrefixCls.value}-overlay-container`, disabled: accessor.disabled || props.readonly, offset: defaultOffset, placement: 'bottomStart', diff --git a/packages/components/tooltip/src/Tooltip.tsx b/packages/components/tooltip/src/Tooltip.tsx index 23eb77524..485741021 100644 --- a/packages/components/tooltip/src/Tooltip.tsx +++ b/packages/components/tooltip/src/Tooltip.tsx @@ -23,7 +23,7 @@ export default defineComponent({ const common = useGlobalConfig('common') const config = useGlobalConfig('tooltip') const mergedPrefixCls = computed(() => `${common.prefixCls}-tooltip`) - const { overlayRef, updatePopper, overlayProps } = useTooltipOverlay(props, config, common, mergedPrefixCls) + const { overlayRef, updatePopper, overlayProps } = useTooltipOverlay(props, config, mergedPrefixCls) expose({ updatePopper }) return () => { diff --git a/packages/components/tooltip/src/useTooltipOverlay.ts b/packages/components/tooltip/src/useTooltipOverlay.ts index cc3ba79b5..c1278f78f 100644 --- a/packages/components/tooltip/src/useTooltipOverlay.ts +++ b/packages/components/tooltip/src/useTooltipOverlay.ts @@ -5,12 +5,11 @@ * found in the LICENSE file at https://github.com/IDuxFE/idux/blob/main/LICENSE */ -import { type ComputedRef, type Ref, computed, onDeactivated, ref, toRef } from 'vue' +import { type ComputedRef, type Ref, computed, onDeactivated, ref } from 'vue' import { useControlledProp } from '@idux/cdk/utils' import { type ɵOverlayInstance, type ɵOverlayProps } from '@idux/components/_private/overlay' -import { type CommonConfig, type TooltipConfig } from '@idux/components/config' -import { useOverlayContainer, useZIndex } from '@idux/components/utils' +import { type TooltipConfig } from '@idux/components/config' import { type TooltipProps } from './types' @@ -25,16 +24,12 @@ export interface TooltipOverlayContext { export function useTooltipOverlay( props: TooltipProps, config: TooltipConfig, - common: CommonConfig, mergedPrefixCls: ComputedRef, ): TooltipOverlayContext { const overlayRef = ref<ɵOverlayInstance>() const updatePopper = () => overlayRef.value?.updatePopper() - const mergedOverlayContainer = useOverlayContainer(props, config, common, mergedPrefixCls) - const [visible, setVisible] = useControlledProp(props, 'visible', false) - const currentZIndex = useZIndex(toRef(props, 'zIndex'), toRef(common, 'overlayZIndex'), visible) onDeactivated(() => { if (visible.value && props.closeOnDeactivated) { @@ -49,7 +44,8 @@ export function useTooltipOverlay( ['onUpdate:visible']: setVisible, autoAdjust: props.autoAdjust ?? config.autoAdjust, clickOutside: trigger === 'click' || trigger === 'contextmenu', - container: mergedOverlayContainer.value, + container: props.overlayContainer ?? config.overlayContainer, + containerFallback: `.${mergedPrefixCls.value}-overlay-container`, delay: props.delay ?? config.delay, destroyOnHide: props.destroyOnHide ?? config.destroyOnHide, disabled: props.disabled, @@ -57,7 +53,7 @@ export function useTooltipOverlay( showArrow: true, placement: props.placement ?? config.placement, trigger: trigger, - zIndex: currentZIndex.value, + zIndex: props.zIndex, } }) return { overlayRef, updatePopper, visible, setVisible, overlayProps } diff --git a/packages/components/tree-select/src/TreeSelect.tsx b/packages/components/tree-select/src/TreeSelect.tsx index 2b23a6270..99d617fbf 100644 --- a/packages/components/tree-select/src/TreeSelect.tsx +++ b/packages/components/tree-select/src/TreeSelect.tsx @@ -16,7 +16,7 @@ import { useGlobalConfig } from '@idux/components/config' import { useFormItemRegister, useFormSize, useFormStatus } from '@idux/components/form' import { ɵUseOverlayState } from '@idux/components/select' import { type TreeInstance } from '@idux/components/tree' -import { useGetKey, useOverlayContainer } from '@idux/components/utils' +import { useGetKey } from '@idux/components/utils' import { useMergeNodes } from './composables/useDataSource' import { useSelectedState } from './composables/useSelectedState' @@ -33,7 +33,6 @@ export default defineComponent({ const locale = useGlobalConfig('locale') const config = useGlobalConfig('treeSelect') const mergedPrefixCls = computed(() => `${common.prefixCls}-tree-select`) - const mergedOverlayContainer = useOverlayContainer(props, config, common, mergedPrefixCls) const mergedChildrenKey = computed(() => props.childrenKey ?? config.childrenKey) const mergedGetKey = useGetKey(props, config, 'components/tree-select') const mergedLabelKey = computed(() => props.labelKey ?? config.labelKey) @@ -180,7 +179,8 @@ export default defineComponent({ class: overlayClasses.value, style: overlayStyle.value, clickOutside: true, - container: mergedOverlayContainer.value, + container: props.overlayContainer ?? config.overlayContainer, + containerFallback: `.${mergedPrefixCls.value}-overlay-container`, disabled: accessor.disabled || props.readonly, offset: props.offset ?? config.offset, placement: 'bottomStart', diff --git a/packages/components/utils/src/portalTarget.ts b/packages/components/utils/src/portalTarget.ts index c49bcee88..004db11ba 100644 --- a/packages/components/utils/src/portalTarget.ts +++ b/packages/components/utils/src/portalTarget.ts @@ -27,26 +27,9 @@ export function usePortalTarget( config: ContainerProps, common: { overlayContainer?: OverlayContainerType }, mergedPrefix: ComputedRef, -): ComputedRef<() => string | HTMLElement> { +): ComputedRef { return computed(() => { const container = props.container ?? config.container ?? common.overlayContainer - return () => (isFunction(container) ? container() : container) ?? `.${mergedPrefix.value}-container` - }) -} - -interface OverlayContainerProps { - overlayContainer?: OverlayContainerType -} - -export function useOverlayContainer( - props: OverlayContainerProps, - config: OverlayContainerProps, - common: { overlayContainer?: OverlayContainerType }, - mergedPrefix: ComputedRef, -): ComputedRef<(element?: Element) => string | HTMLElement> { - return computed(() => { - const container = props.overlayContainer ?? config.overlayContainer ?? common.overlayContainer - return element => - (isFunction(container) ? container(element) : container) ?? `.${mergedPrefix.value}-overlay-container` + return (isFunction(container) ? container() : container) ?? `.${mergedPrefix.value}-container` }) } diff --git a/packages/components/utils/src/zIndex.ts b/packages/components/utils/src/zIndex.ts index 94623d610..ffa8186d1 100644 --- a/packages/components/utils/src/zIndex.ts +++ b/packages/components/utils/src/zIndex.ts @@ -7,7 +7,7 @@ import { type ComputedRef, type Ref, computed, ref, watch } from 'vue' -import { isFunction } from 'lodash-es' +import { isFunction, isNil } from 'lodash-es' let zIndexCount = 0 @@ -31,7 +31,7 @@ export const useZIndex: UseZIndex = (controlZIndex, configZIndex, visible) => { watch( visible, newVisible => { - if (newVisible) { + if (newVisible && isNil(controlZIndex.value)) { innerZIndex.value = getZIndex() } }, diff --git a/packages/pro/search/src/ProSearch.tsx b/packages/pro/search/src/ProSearch.tsx index 48ab8265d..b0e0e4c88 100644 --- a/packages/pro/search/src/ProSearch.tsx +++ b/packages/pro/search/src/ProSearch.tsx @@ -55,7 +55,7 @@ export default defineComponent({ searchItems, searchStateContext.tempSearchStateAvailable, ) - const commonOverlayProps = useCommonOverlayProps(props, config, componentCommon, mergedPrefixCls) + const commonOverlayProps = useCommonOverlayProps(props, config, mergedPrefixCls) const { focused, focus, blur } = useFocusedState( props, elementRef, diff --git a/packages/pro/search/src/composables/useCommonOverlayProps.ts b/packages/pro/search/src/composables/useCommonOverlayProps.ts index 07d23ee8b..bb08968f6 100644 --- a/packages/pro/search/src/composables/useCommonOverlayProps.ts +++ b/packages/pro/search/src/composables/useCommonOverlayProps.ts @@ -8,8 +8,6 @@ import { type ComputedRef, computed } from 'vue' import { type ɵOverlayProps } from '@idux/components/_private/overlay' -import { type CommonConfig } from '@idux/components/config' -import { useOverlayContainer } from '@idux/components/utils' import { type ProSearchConfig } from '@idux/pro/config' import { type ProSearchProps } from '../types' @@ -17,12 +15,11 @@ import { type ProSearchProps } from '../types' export function useCommonOverlayProps( props: ProSearchProps, config: ProSearchConfig, - common: CommonConfig, mergedPrefixCls: ComputedRef, ): ComputedRef<ɵOverlayProps> { - const mergedContainer = useOverlayContainer(props, config, common, mergedPrefixCls) return computed(() => ({ - container: mergedContainer.value, + container: props.overlayContainer ?? config.overlayContainer, + containerFallback: `.${mergedPrefixCls.value}-overlay-container`, placement: 'bottomStart', offset: [0, 4], }))