diff --git a/packages/cdk/utils/__tests__/zIndex.spec.ts b/packages/cdk/utils/__tests__/zIndex.spec.ts new file mode 100644 index 000000000..ba31a8067 --- /dev/null +++ b/packages/cdk/utils/__tests__/zIndex.spec.ts @@ -0,0 +1,108 @@ +import { mount } from '@vue/test-utils' +import { computed, defineComponent, ref, toRef } from 'vue' + +import { useZIndex } from '../src/zIndex' + +describe('zIndex.ts', () => { + const TestComp = defineComponent({ + props: { + visible: { type: Boolean, required: true }, + zIndex: { type: Number, default: undefined }, + }, + setup(props) { + const visible = toRef(props, 'visible') + const { currentZIndex } = useZIndex(visible, toRef(props, 'zIndex'), 3000) + const style = computed(() => ({ zIndex: currentZIndex.value })) + + return { + style, + } + }, + template: ` +
+ `, + }) + + test('custdom zIndex work', async () => { + const wrapper = mount(TestComp, { + props: { + visible: false, + zIndex: 2022, + }, + }) + + expect(wrapper.find('[utid="test-el"]').attributes('style')).toContain('z-index: 2022') + + await wrapper.setProps({ visible: true }) + expect(wrapper.find('[utid="test-el"]').attributes('style')).toContain('z-index: 2022') + }) + + test('increment zIndex work', async () => { + const wrapper = mount(TestComp, { + props: { + visible: false, + }, + }) + + expect(wrapper.find('[utid="test-el"]').attributes('style')).toContain('z-index: 3000') + + await wrapper.setProps({ visible: true }) + expect(wrapper.find('[utid="test-el"]').attributes('style')).toContain('z-index: 3000') + + const wrapper2 = mount(TestComp, { + props: { + visible: false, + }, + }) + await wrapper2.setProps({ visible: true }) + expect(wrapper2.find('[utid="test-el"]').attributes('style')).toContain('z-index: 3001') + + const wrapper3 = mount(TestComp, { + props: { + visible: true, + }, + }) + expect(wrapper3.find('[utid="test-el"]').attributes('style')).toContain('z-index: 3002') + + expect(wrapper.find('[utid="test-el"]').attributes('style')).toContain('z-index: 3000') + await wrapper.setProps({ visible: false }) + await wrapper.setProps({ visible: true }) + expect(wrapper.find('[utid="test-el"]').attributes('style')).toContain('z-index: 3003') + }) + + test('should work when element does not exist', async () => { + const TestComp = { + props: { + visible: { type: Boolean, required: true }, + }, + setup(props: Record<'visible', boolean>) { + const visible = toRef(props, 'visible') + + const { currentZIndex } = useZIndex(visible, ref(undefined), 4000) + const style = computed(() => ({ zIndex: currentZIndex.value })) + + return { + style, + } + }, + template: ` +
+
+
+ `, + } + + const wrapper = mount(TestComp, { + props: { + visible: true, + }, + }) + expect(wrapper.find('[utid="test-el"]').attributes('style')).toContain('z-index: 4004') + + await wrapper.setProps({ visible: false }) + expect(wrapper.find('[utid="test-el"]').exists()).toBe(false) + + await wrapper.setProps({ visible: true }) + expect(wrapper.find('[utid="test-el"]').attributes('style')).toContain('z-index: 4005') + }) +}) diff --git a/packages/cdk/utils/index.ts b/packages/cdk/utils/index.ts index 4b31faa51..1e02b401b 100644 --- a/packages/cdk/utils/index.ts +++ b/packages/cdk/utils/index.ts @@ -17,3 +17,4 @@ export * from './src/typeof' export * from './src/uniqueId' export * from './src/useEventListener' export * from './src/vNode' +export * from './src/zIndex' diff --git a/packages/cdk/utils/src/zIndex.ts b/packages/cdk/utils/src/zIndex.ts new file mode 100644 index 000000000..ec75e9868 --- /dev/null +++ b/packages/cdk/utils/src/zIndex.ts @@ -0,0 +1,36 @@ +/** + * @license + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://github.com/IDuxFE/idux/blob/main/LICENSE + */ + +import { type ComputedRef, type Ref, computed, ref, watch } from 'vue' + +const zIndexCount = ref(0) +const getZIndex = (commonZIndex: number) => zIndexCount.value + (commonZIndex ?? 1000) + +type UseZIndex = ( + visible: Ref, + controlZIndex: Ref, + commonZIndex: number, +) => { currentZIndex: ComputedRef } +export const useZIndex: UseZIndex = (visible, controlZIndex, commonZIndex) => { + const innerZIndex = ref(getZIndex(commonZIndex)) + const currentZIndex = computed(() => (controlZIndex.value ? controlZIndex.value : innerZIndex.value)) + + watch( + visible, + newVisible => { + if (newVisible && !controlZIndex.value) { + innerZIndex.value = getZIndex(commonZIndex) + zIndexCount.value++ + } + }, + { immediate: true }, + ) + + return { + currentZIndex, + } +} diff --git a/packages/components/_private/overlay/src/Overlay.tsx b/packages/components/_private/overlay/src/Overlay.tsx index cfbc2e1f4..44d610903 100644 --- a/packages/components/_private/overlay/src/Overlay.tsx +++ b/packages/components/_private/overlay/src/Overlay.tsx @@ -7,15 +7,18 @@ import type { OverlayProps } from './types' import type { PopperElement, PopperEvents } from '@idux/cdk/popper' -import type { ComputedRef, Ref, VNode } from 'vue' import { + type ComputedRef, + type Ref, Transition, + type VNode, cloneVNode, computed, defineComponent, onBeforeUnmount, onMounted, + toRef, vShow, watch, withDirectives, @@ -24,7 +27,7 @@ import { import { clickOutside } from '@idux/cdk/click-outside' import { usePopper } from '@idux/cdk/popper' import { CdkPortal } from '@idux/cdk/portal' -import { Logger, callEmit, convertElement, getFirstValidNode } from '@idux/cdk/utils' +import { Logger, callEmit, convertElement, getFirstValidNode, useZIndex } from '@idux/cdk/utils' import { useGlobalConfig } from '@idux/components/config' import { overlayProps } from './types' @@ -53,6 +56,8 @@ export default defineComponent({ destroy, } = usePopper({ ...popperOptions.value, visible: props.visible }) + const { currentZIndex } = useZIndex(visibility, toRef(props, 'zIndex'), common.zIndex) + onMounted(() => initialize()) onBeforeUnmount(() => destroy()) @@ -101,6 +106,7 @@ export default defineComponent({ props, mergedPrefixCls, visibility, + currentZIndex, contentNode!, arrowRef, popperRef, @@ -133,6 +139,7 @@ function renderContent( props: OverlayProps, mergedPrefixCls: ComputedRef, visibility: ComputedRef, + currentZIndex: ComputedRef, contentNode: VNode[], arrowRef: Ref, popperRef: Ref, @@ -143,9 +150,9 @@ function renderContent( return null } const prefixCls = mergedPrefixCls.value - const { triggerId, zIndex } = props + const { triggerId } = props const overlayId = triggerId != null ? `overlay-${triggerId}` : undefined - const style = zIndex != null ? `z-index: ${zIndex}` : undefined + const style = currentZIndex.value ? `z-index: ${currentZIndex.value}` : undefined const overlay = (
{contentNode} diff --git a/packages/components/config/src/defaultConfig.ts b/packages/components/config/src/defaultConfig.ts index a57b4dd6d..dd3b4e500 100644 --- a/packages/components/config/src/defaultConfig.ts +++ b/packages/components/config/src/defaultConfig.ts @@ -13,6 +13,7 @@ import { type GlobalConfig } from './types' export const defaultConfig: GlobalConfig = { common: { prefixCls: 'ix', + zIndex: 1000, }, locale: zhCN, diff --git a/packages/components/config/src/types.ts b/packages/components/config/src/types.ts index c8853bf2c..ec7a890d4 100644 --- a/packages/components/config/src/types.ts +++ b/packages/components/config/src/types.ts @@ -100,6 +100,7 @@ export interface GlobalConfig { export type GlobalConfigKey = keyof GlobalConfig export interface CommonConfig { prefixCls: string + zIndex: number } export interface AlertConfig { @@ -199,7 +200,6 @@ export interface DrawerConfig { maskClosable: boolean target?: PortalTargetType width: string | number - zIndex?: number } export interface DropdownConfig { @@ -294,7 +294,6 @@ export interface ModalConfig { maskClosable: boolean target?: PortalTargetType width: string | number - zIndex?: number } export interface NotificationConfig { diff --git a/packages/components/drawer/src/Drawer.tsx b/packages/components/drawer/src/Drawer.tsx index 2f9a9566b..b2755ac30 100644 --- a/packages/components/drawer/src/Drawer.tsx +++ b/packages/components/drawer/src/Drawer.tsx @@ -9,11 +9,11 @@ import type { DrawerProps } from './types' import type { ScrollStrategy } from '@idux/cdk/scroll' import type { ComputedRef, Ref } from 'vue' -import { computed, defineComponent, inject, onBeforeUnmount, onMounted, provide, ref, watch } from 'vue' +import { computed, defineComponent, inject, onBeforeUnmount, onMounted, provide, ref, toRef, watch } from 'vue' import { CdkPortal } from '@idux/cdk/portal' import { BlockScrollStrategy } from '@idux/cdk/scroll' -import { callEmit, useControlledProp } from '@idux/cdk/utils' +import { callEmit, useControlledProp, useZIndex } from '@idux/cdk/utils' import { ɵMask } from '@idux/components/_private/mask' import { useGlobalConfig } from '@idux/components/config' @@ -30,9 +30,9 @@ export default defineComponent({ const mergedPrefixCls = computed(() => `${common.prefixCls}-drawer`) const config = useGlobalConfig('drawer') const mask = computed(() => props.mask ?? config.mask) - const zIndex = computed(() => props.zIndex ?? config.zIndex) const { visible, setVisible, animatedVisible, mergedVisible } = useVisible(props) + const { currentZIndex } = useZIndex(visible, toRef(props, 'zIndex'), common.zIndex) const { open, close } = useTrigger(props, setVisible) const { level, levelAction, push, pull } = useLevel(visible) @@ -46,6 +46,7 @@ export default defineComponent({ visible, animatedVisible, mergedVisible, + currentZIndex, level, levelAction, push, @@ -69,7 +70,7 @@ export default defineComponent({ class={`${mergedPrefixCls.value}-mask`} mask={mask.value} visible={visible.value} - zIndex={zIndex.value} + zIndex={currentZIndex.value} /> diff --git a/packages/components/drawer/src/DrawerWrapper.tsx b/packages/components/drawer/src/DrawerWrapper.tsx index 4942a2afd..eaf08e567 100644 --- a/packages/components/drawer/src/DrawerWrapper.tsx +++ b/packages/components/drawer/src/DrawerWrapper.tsx @@ -41,10 +41,20 @@ const defaultDistance = 160 export default defineComponent({ inheritAttrs: false, setup(_, { attrs }) { - const { props, slots, common, config, mergedPrefixCls, visible, animatedVisible, level, levelAction } = - inject(drawerToken)! + const { + props, + slots, + common, + config, + mergedPrefixCls, + visible, + animatedVisible, + currentZIndex, + level, + levelAction, + } = inject(drawerToken)! const { close } = inject(DRAWER_TOKEN)! - const { closable, closeIcon, closeOnEsc, mask, maskClosable, zIndex } = useConfig(props, config) + const { closable, closeIcon, closeOnEsc, mask, maskClosable } = useConfig(props, config) const transitionName = computed(() => `${common.prefixCls}-${drawerTransitionMap[props.placement]}`) const isHorizontal = computed(() => horizontalPlacement.includes(props.placement)) @@ -95,7 +105,7 @@ export default defineComponent({ const wrapperStyle = computed(() => { const placement = mask.value ? undefined : placementStyle.value - return { zIndex: zIndex.value, transform: transformStyle.value, ...placement } + return { zIndex: currentZIndex.value, transform: transformStyle.value, ...placement } }) const contentStyle = computed(() => { @@ -178,9 +188,8 @@ function useConfig(props: DrawerProps, config: DrawerConfig) { const closeOnEsc = computed(() => props.closeOnEsc ?? config.closeOnEsc) const mask = computed(() => props.mask ?? config.mask) const maskClosable = computed(() => props.maskClosable ?? config.maskClosable) - const zIndex = computed(() => props.zIndex ?? config.zIndex) - return { closable, closeIcon, closeOnEsc, mask, maskClosable, zIndex } + return { closable, closeIcon, closeOnEsc, mask, maskClosable } } function watchVisibleChange( diff --git a/packages/components/drawer/src/token.ts b/packages/components/drawer/src/token.ts index 3857543ab..9fe1b732e 100644 --- a/packages/components/drawer/src/token.ts +++ b/packages/components/drawer/src/token.ts @@ -18,6 +18,7 @@ export interface DrawerContext { visible: ComputedRef animatedVisible: Ref mergedVisible: ComputedRef + currentZIndex: ComputedRef level: Ref levelAction: Ref<'push' | 'pull' | undefined> push: () => void diff --git a/packages/components/message/__tests__/__snapshots__/message.spec.ts.snap b/packages/components/message/__tests__/__snapshots__/message.spec.ts.snap index 4d803e844..7d7fed905 100644 --- a/packages/components/message/__tests__/__snapshots__/message.spec.ts.snap +++ b/packages/components/message/__tests__/__snapshots__/message.spec.ts.snap @@ -1,7 +1,7 @@ // Vitest Snapshot v1 exports[`Message > render work 1`] = ` -"
+"
This is a message
" `; diff --git a/packages/components/message/src/Message.tsx b/packages/components/message/src/Message.tsx index c255fee9d..4c5750c09 100644 --- a/packages/components/message/src/Message.tsx +++ b/packages/components/message/src/Message.tsx @@ -8,11 +8,11 @@ import type { MessageProps } from './types' import type { MessageConfig } from '@idux/components/config' -import { computed, defineComponent, onBeforeUnmount, onMounted, watchEffect } from 'vue' +import { computed, defineComponent, onBeforeUnmount, onMounted, ref, watchEffect } from 'vue' import { isString } from 'lodash-es' -import { callEmit, useControlledProp } from '@idux/cdk/utils' +import { callEmit, useControlledProp, useZIndex } from '@idux/cdk/utils' import { useGlobalConfig } from '@idux/components/config' import { IxIcon } from '@idux/components/icon' @@ -37,13 +37,22 @@ export default defineComponent({ }) const { visible, onMouseEnter, onMouseLeave } = useEvents(props, config) + const { currentZIndex } = useZIndex(visible, ref(undefined), common.zIndex) return () => { const icon = mergedIcon.value const iconNode = isString(icon) ? : icon const prefixCls = mergedPrefixCls.value + const style = { zIndex: currentZIndex.value } + return ( -
+
{iconNode} {slots.default?.()} diff --git a/packages/components/modal/src/Modal.tsx b/packages/components/modal/src/Modal.tsx index 73c570eeb..deb5db20e 100644 --- a/packages/components/modal/src/Modal.tsx +++ b/packages/components/modal/src/Modal.tsx @@ -5,11 +5,22 @@ * found in the LICENSE file at https://github.com/IDuxFE/idux/blob/main/LICENSE */ -import { type ComputedRef, computed, defineComponent, onBeforeUnmount, onMounted, provide, ref, watch } from 'vue' +import { + type ComputedRef, + computed, + defineComponent, + onBeforeUnmount, + onMounted, + provide, + ref, + toRef, + watch, +} from 'vue' import { CdkPortal } from '@idux/cdk/portal' import { BlockScrollStrategy, type ScrollStrategy } from '@idux/cdk/scroll' import { callEmit, isPromise, useControlledProp } from '@idux/cdk/utils' +import { useZIndex } from '@idux/cdk/utils/src/zIndex' import { ɵMask } from '@idux/components/_private/mask' import { useGlobalConfig } from '@idux/components/config' @@ -27,8 +38,8 @@ export default defineComponent({ const locale = useGlobalConfig('locale') const config = useGlobalConfig('modal') const mask = computed(() => props.mask ?? config.mask) - const zIndex = computed(() => props.zIndex ?? config.zIndex) const { visible, setVisible, animatedVisible, mergedVisible } = useVisible(props) + const { currentZIndex } = useZIndex(visible, toRef(props, 'zIndex'), common.zIndex) const { cancelLoading, okLoading, open, close, cancel, ok } = useTrigger(props, setVisible) @@ -44,6 +55,7 @@ export default defineComponent({ mergedVisible, cancelLoading, okLoading, + currentZIndex, }) const apis = { open, close, cancel, ok } @@ -64,7 +76,7 @@ export default defineComponent({ class={`${mergedPrefixCls.value}-mask`} mask={mask.value} visible={visible.value} - zIndex={zIndex.value} + zIndex={currentZIndex.value} /> diff --git a/packages/components/modal/src/ModalWrapper.tsx b/packages/components/modal/src/ModalWrapper.tsx index 557dd1d47..7ee6eb56d 100644 --- a/packages/components/modal/src/ModalWrapper.tsx +++ b/packages/components/modal/src/ModalWrapper.tsx @@ -45,9 +45,10 @@ export default defineComponent({ mergedVisible, cancelLoading, okLoading, + currentZIndex, } = inject(modalToken)! const { close, cancel, ok } = inject(MODAL_TOKEN)! - const { centered, closable, closeIcon, closeOnEsc, width, mask, maskClosable, zIndex } = useConfig(props, config) + const { centered, closable, closeIcon, closeOnEsc, width, mask, maskClosable } = useConfig(props, config) const cancelVisible = computed(() => props.type === 'default' || props.type === 'confirm') @@ -77,7 +78,7 @@ export default defineComponent({ }) const wrapperStyle = computed(() => { - return { zIndex: zIndex.value } + return { zIndex: currentZIndex.value } }) const modalTransformOrigin = ref() @@ -200,9 +201,8 @@ function useConfig(props: ModalProps, config: ModalConfig) { const mask = computed(() => props.mask ?? config.mask) const maskClosable = computed(() => props.maskClosable ?? config.maskClosable) const width = computed(() => convertCssPixel(props.width ?? config.width)) - const zIndex = computed(() => props.zIndex ?? config.zIndex) - return { centered, closable, closeIcon, closeOnEsc, width, mask, maskClosable, zIndex } + return { centered, closable, closeIcon, closeOnEsc, width, mask, maskClosable } } function watchVisibleChange( diff --git a/packages/components/modal/src/token.ts b/packages/components/modal/src/token.ts index 10c577705..bd41619d7 100644 --- a/packages/components/modal/src/token.ts +++ b/packages/components/modal/src/token.ts @@ -22,6 +22,7 @@ export interface ModalContext { mergedVisible: ComputedRef cancelLoading: Ref okLoading: Ref + currentZIndex: ComputedRef } export const modalToken: InjectionKey = Symbol('modalToken') diff --git a/packages/components/notification/__tests__/__snapshots__/notification.spec.ts.snap b/packages/components/notification/__tests__/__snapshots__/notification.spec.ts.snap index 86789edaa..8a4731df7 100644 --- a/packages/components/notification/__tests__/__snapshots__/notification.spec.ts.snap +++ b/packages/components/notification/__tests__/__snapshots__/notification.spec.ts.snap @@ -1,7 +1,7 @@ // Vitest Snapshot v1 exports[`Notification > render work 1`] = ` -"
+"
diff --git a/packages/components/notification/src/Notification.tsx b/packages/components/notification/src/Notification.tsx index 0d296879c..da97cfa90 100644 --- a/packages/components/notification/src/Notification.tsx +++ b/packages/components/notification/src/Notification.tsx @@ -7,13 +7,12 @@ import type { NotificationNodePropKey, NotificationProps, SlotProps } from './types' import type { NotificationConfig } from '@idux/components/config' -import type { ComputedRef, Slots } from 'vue' -import { computed, defineComponent, onBeforeUnmount, onMounted, watchEffect } from 'vue' +import { ComputedRef, Slots, computed, defineComponent, onBeforeUnmount, onMounted, ref, watchEffect } from 'vue' import { isArray, isString } from 'lodash-es' -import { callEmit, useControlledProp } from '@idux/cdk/utils' +import { callEmit, useControlledProp, useZIndex } from '@idux/cdk/utils' import { IxButton } from '@idux/components/button' import { useGlobalConfig } from '@idux/components/config' import { IxIcon } from '@idux/components/icon' @@ -41,6 +40,7 @@ export default defineComponent({ const icon = useIcon(props, config) const closeIcon = useCloseIcon(props, config) const { visible, close, onMouseEnter, onMouseLeave } = useVisible(props, config) + const { currentZIndex } = useZIndex(visible, ref(undefined), commonCfg.zIndex) return () => { const iconNode = icon.value && getIconNode(icon.value) @@ -57,8 +57,10 @@ export default defineComponent({ mainCls += ` ${comPrefix.value}-main-with-content` } + const style = { zIndex: currentZIndex.value } + return ( -
+
{iconNode} diff --git a/packages/components/popover/__tests__/__snapshots__/popover.spec.ts.snap b/packages/components/popover/__tests__/__snapshots__/popover.spec.ts.snap index f8d9666d9..08d352e32 100644 --- a/packages/components/popover/__tests__/__snapshots__/popover.spec.ts.snap +++ b/packages/components/popover/__tests__/__snapshots__/popover.spec.ts.snap @@ -1,6 +1,6 @@ // Vitest Snapshot v1 -exports[`Popover > header props work 1`] = `"
TitlesubTitle
"`; +exports[`Popover > header props work 1`] = `"
TitlesubTitle
"`; exports[`Popover > render work 1`] = ` "
trigger
diff --git a/packages/components/popover/__tests__/popover.spec.ts b/packages/components/popover/__tests__/popover.spec.ts index 64132562c..afe32b7f5 100644 --- a/packages/components/popover/__tests__/popover.spec.ts +++ b/packages/components/popover/__tests__/popover.spec.ts @@ -37,6 +37,7 @@ describe('Popover', () => { test('header props work', async () => { PopoverWrapper({ props: { + zIndex: 2022, visible: true, header: { title: 'Title',