diff --git a/packages/cdk/scroll/src/strategy/blockScrollStrategy.ts b/packages/cdk/scroll/src/strategy/blockScrollStrategy.ts index 27b3e7d50..a2de11ee7 100644 --- a/packages/cdk/scroll/src/strategy/blockScrollStrategy.ts +++ b/packages/cdk/scroll/src/strategy/blockScrollStrategy.ts @@ -5,9 +5,9 @@ * found in the LICENSE file at https://github.com/IDuxFE/idux/blob/main/LICENSE */ -import { addClass, convertCssPixel, removeClass } from '@idux/cdk/utils' +import { addClass, removeClass } from '@idux/cdk/utils' -import { getScroll, setScroll } from '../utils' +import { getScrollBarSize } from '../utils' import { ScrollStrategy } from './scrollStrategy' export interface BlockScrollStrategyOptions { @@ -17,13 +17,8 @@ export interface BlockScrollStrategyOptions { const defaultClassName = 'cdk-scroll-block' -interface CacheStrategy extends BlockScrollStrategyOptions { - uid: number - cacheScroll: { scrollTop: number; scrollLeft: number } - cacheStyle: { top: string; left: string } -} - -let cacheStrategy: CacheStrategy[] = [] +const cacheStrategy = new Map() +const cacheStyle = new Map() let uuid = 0 @@ -44,58 +39,65 @@ export class BlockScrollStrategy implements ScrollStrategy { /** Blocks scroll while the attached overlay is open. */ enable(): void { - const { uid, target, className } = this - if (cacheStrategy.some(item => item.uid === uid)) { + if (!this.isScrolled()) { return } - let cacheScroll: { scrollTop: number; scrollLeft: number } - let cacheStyle: { top: string; left: string } - - const sameTargetTarget = cacheStrategy.find(item => item.target === target) - if (!sameTargetTarget) { - cacheScroll = getScroll(target) - cacheStyle = { top: target.style.top, left: target.style.left } - } else { - cacheScroll = sameTargetTarget.cacheScroll - cacheStyle = sameTargetTarget.cacheStyle + const { uid, target, className } = this + if (cacheStrategy.has(uid)) { + return } - cacheStrategy.push({ uid, target, className, cacheScroll, cacheStyle }) - - if (!target.classList.contains(className) && this.isScrolled()) { - target.style.top = convertCssPixel(-cacheScroll.scrollTop) - target.style.left = convertCssPixel(-cacheScroll.scrollLeft) - addClass(target, className) + if (!Array.from(cacheStrategy.values()).some(item => item.target === target)) { + const scrollBarSize = getScrollBarSize(target === document.documentElement ? undefined : target) + cacheStyle.set(target, { + width: target.style.width, + overflow: target.style.overflow, + overflowX: target.style.overflowX, + overflowY: target.style.overflowY, + }) + + target.style.width = scrollBarSize !== 0 ? `calc(100% - ${scrollBarSize}px)` : '' + target.style.overflow = 'hidden' + target.style.overflowX = 'hidden' + target.style.overflowY = 'hidden' } + + this.addClassName() + cacheStrategy.set(uid, { target, className }) } /** Unblocks scroll while the attached overlay is open. */ disable(): void { const { uid, target, className } = this - const currStrategy = cacheStrategy.find(item => item.uid === uid) + const currStrategy = cacheStrategy.get(uid) if (!currStrategy) { return } - cacheStrategy = cacheStrategy.filter(item => item.uid !== uid) + cacheStrategy.delete(uid) - if (!cacheStrategy.some(item => item.target === target && item.className === className)) { + const strategyArray = Array.from(cacheStrategy.values()) + if (!strategyArray.some(item => item.target === target && item.className === className)) { removeClass(target, className) } - if (!cacheStrategy.some(item => item.target === target)) { - target.style.top = currStrategy.cacheStyle.top - target.style.left = currStrategy.cacheStyle.left + if (!strategyArray.some(item => item.target === target)) { + const style = cacheStyle.get(target)! - setScroll(currStrategy.cacheScroll, target) + target.style.width = style.width + target.style.overflow = style.overflow + target.style.overflowX = style.overflowX + target.style.overflowY = style.overflowY + + cacheStyle.delete(target) } } /** Re-lock scroll while the options change. */ update(options: BlockScrollStrategyOptions): void { const { uid, target, className } = this - const currStrategy = cacheStrategy.find(item => item.uid === uid) + const currStrategy = cacheStrategy.get(uid) if (currStrategy) { this.disable() @@ -114,6 +116,16 @@ export class BlockScrollStrategy implements ScrollStrategy { private isScrolled(): boolean { const { target } = this - return target.scrollHeight > target.clientHeight + return ( + (target === document.documentElement && document.body.scrollWidth > window.innerWidth) || + target.scrollHeight > target.clientHeight + ) + } + + private addClassName(): void { + const { target, className } = this + if (!target.classList.contains(className)) { + addClass(target, className) + } } } diff --git a/packages/components/style/core/default.less b/packages/components/style/core/default.less index 606102789..3226ba73c 100644 --- a/packages/components/style/core/default.less +++ b/packages/components/style/core/default.less @@ -1,26 +1,2 @@ @import '../themes/default.less'; @import './reset.less'; - -// Used when disabling global scrolling. -.@{cdk-prefix} { - - &-scroll-block { - position: fixed; - - // Necessary for the content not to lose its width. Note that we're using 100%, instead of - // 100vw, because 100vw includes the width plus the scrollbar, whereas 100% is the width - // that the element had before we made it `fixed`. - width: 100%; - - // Note: this will always add a scrollbar to whatever element it is on, which can - // potentially result in double scrollbars. It shouldn't be an issue, because we won't - // block scrolling on a page that doesn't have a scrollbar in the first place. - overflow-y: scroll; - - // https://github.com/angular/material2/issues/15051 - - body { - overflow-x: visible; - } - } -}