diff --git a/components/_util/PortalWrapper.tsx b/components/_util/PortalWrapper.tsx index 083ed4c81..a5594ffb7 100644 --- a/components/_util/PortalWrapper.tsx +++ b/components/_util/PortalWrapper.tsx @@ -1,6 +1,4 @@ import PropTypes from './vue-types'; -import switchScrollingEffect from './switchScrollingEffect'; -import setStyle from './setStyle'; import Portal from './Portal'; import { defineComponent, @@ -11,10 +9,12 @@ import { onUpdated, getCurrentInstance, nextTick, + computed, } from 'vue'; import canUseDom from './canUseDom'; -import ScrollLocker from '../vc-util/Dom/scrollLocker'; import raf from './raf'; +import { booleanType } from './type'; +import useScrollLocker from './hooks/useScrollLocker'; let openCount = 0; const supportDom = canUseDom(); @@ -24,10 +24,6 @@ export function getOpenCount() { return process.env.NODE_ENV === 'test' ? openCount : 0; } -// https://github.com/ant-design/ant-design/issues/19340 -// https://github.com/ant-design/ant-design/issues/19332 -let cacheOverflow = {}; - const getParent = (getContainer: GetContainer) => { if (!supportDom) { return null; @@ -57,20 +53,20 @@ export default defineComponent({ forceRender: { type: Boolean, default: undefined }, getContainer: PropTypes.any, visible: { type: Boolean, default: undefined }, + autoLock: booleanType(), + didUpdate: Function, }, setup(props, { slots }) { const container = shallowRef(); const componentRef = shallowRef(); const rafId = shallowRef(); - const scrollLocker = new ScrollLocker({ - container: getParent(props.getContainer) as HTMLElement, - }); const removeCurrentContainer = () => { // Portal will remove from `parentNode`. // Let's handle this again to avoid refactor issue. container.value?.parentNode?.removeChild(container.value); + container.value = null; }; const attachToParent = (force = false) => { if (force || (container.value && !container.value.parentNode)) { @@ -86,13 +82,13 @@ export default defineComponent({ return true; }; // attachToParent(); - + const defaultContainer = document.createElement('div'); const getContainer = () => { if (!supportDom) { return null; } if (!container.value) { - container.value = document.createElement('div'); + container.value = defaultContainer; attachToParent(true); } setWrapperClassName(); @@ -108,30 +104,19 @@ export default defineComponent({ setWrapperClassName(); attachToParent(); }); - /** - * Enhance ./switchScrollingEffect - * 1. Simulate document body scroll bar with - * 2. Record body has overflow style and recover when all of PortalWrapper invisible - * 3. Disable body scroll when PortalWrapper has open - * - * @memberof PortalWrapper - */ - const switchScrolling = () => { - if (openCount === 1 && !Object.keys(cacheOverflow).length) { - switchScrollingEffect(); - // Must be set after switchScrollingEffect - cacheOverflow = setStyle({ - overflow: 'hidden', - overflowX: 'hidden', - overflowY: 'hidden', - }); - } else if (!openCount) { - setStyle(cacheOverflow); - cacheOverflow = {}; - switchScrollingEffect(true); - } - }; + const instance = getCurrentInstance(); + + useScrollLocker( + computed(() => { + return ( + props.autoLock && + props.visible && + canUseDom() && + (container.value === document.body || container.value === defaultContainer) + ); + }), + ); onMounted(() => { let init = false; watch( @@ -157,17 +142,6 @@ export default defineComponent({ ) { removeCurrentContainer(); } - // updateScrollLocker - if ( - visible && - visible !== prevVisible && - supportDom && - getParent(getContainer) !== scrollLocker.getContainer() - ) { - scrollLocker.reLock({ - container: getParent(getContainer) as HTMLElement, - }); - } } init = true; }, @@ -192,22 +166,30 @@ export default defineComponent({ removeCurrentContainer(); raf.cancel(rafId.value); }); - + watch( + [() => props.visible, () => props.forceRender], + () => { + const { forceRender, visible } = props; + if (visible === false && !forceRender) { + removeCurrentContainer(); + } + }, + { flush: 'post' }, + ); return () => { const { forceRender, visible } = props; let portal = null; const childProps = { getOpenCount: () => openCount, getContainer, - switchScrollingEffect: switchScrolling, - scrollLocker, }; - + if (visible === false && !forceRender) return null; if (forceRender || visible || componentRef.value) { portal = ( slots.default?.(childProps) }} > ); diff --git a/components/vc-tour/hooks/useScrollLocker.tsx b/components/_util/hooks/useScrollLocker.ts similarity index 66% rename from components/vc-tour/hooks/useScrollLocker.tsx rename to components/_util/hooks/useScrollLocker.ts index 5d5f1cfa1..993ff7d1c 100644 --- a/components/vc-tour/hooks/useScrollLocker.tsx +++ b/components/_util/hooks/useScrollLocker.ts @@ -19,26 +19,30 @@ export function isBodyOverflowing() { export default function useScrollLocker(lock?: Ref) { const mergedLock = computed(() => !!lock && !!lock.value); - const id = computed(() => { - uuid += 1; - return `${UNIQUE_ID}_${uuid}`; - }); + uuid += 1; + const id = `${UNIQUE_ID}_${uuid}`; - watchEffect(() => { - if (mergedLock.value) { - const scrollbarSize = getScrollBarSize(); - const isOverflow = isBodyOverflowing(); + watchEffect( + onClear => { + if (mergedLock.value) { + const scrollbarSize = getScrollBarSize(); + const isOverflow = isBodyOverflowing(); - updateCSS( - ` + updateCSS( + ` html body { overflow-y: hidden; ${isOverflow ? `width: calc(100% - ${scrollbarSize}px);` : ''} }`, - id.value, - ); - } else { - removeCSS(id.value); - } - }); + id, + ); + } else { + removeCSS(id); + } + onClear(() => { + removeCSS(id); + }); + }, + { flush: 'post' }, + ); } diff --git a/components/_util/switchScrollingEffect.ts b/components/_util/switchScrollingEffect.ts deleted file mode 100644 index e87985bdd..000000000 --- a/components/_util/switchScrollingEffect.ts +++ /dev/null @@ -1,42 +0,0 @@ -import getScrollBarSize from './getScrollBarSize'; -import setStyle from './setStyle'; - -function isBodyOverflowing() { - return ( - document.body.scrollHeight > (window.innerHeight || document.documentElement.clientHeight) && - window.innerWidth > document.body.offsetWidth - ); -} - -let cacheStyle = {}; - -export default (close?: boolean) => { - if (!isBodyOverflowing() && !close) { - return; - } - - // https://github.com/ant-design/ant-design/issues/19729 - const scrollingEffectClassName = 'ant-scrolling-effect'; - const scrollingEffectClassNameReg = new RegExp(`${scrollingEffectClassName}`, 'g'); - const bodyClassName = document.body.className; - - if (close) { - if (!scrollingEffectClassNameReg.test(bodyClassName)) return; - setStyle(cacheStyle); - cacheStyle = {}; - document.body.className = bodyClassName.replace(scrollingEffectClassNameReg, '').trim(); - return; - } - - const scrollBarSize = getScrollBarSize(); - if (scrollBarSize) { - cacheStyle = setStyle({ - position: 'relative', - width: `calc(100% - ${scrollBarSize}px)`, - }); - if (!scrollingEffectClassNameReg.test(bodyClassName)) { - const addClassName = `${bodyClassName} ${scrollingEffectClassName}`; - document.body.className = addClassName.trim(); - } - } -}; diff --git a/components/tour/demo/basic.vue b/components/tour/demo/basic.vue index 020cb8a3e..5e62c8227 100644 --- a/components/tour/demo/basic.vue +++ b/components/tour/demo/basic.vue @@ -28,7 +28,7 @@ The most basic usage. - +