UNPKG

veui

Version:

Baidu Enterprise UI for Vue.js.

124 lines (98 loc) 3.13 kB
import { getScrollbarWidth, cssSupports } from '../utils/dom' let hasCSSSupport = null function getCSSSupport () { if (hasCSSSupport === null) { hasCSSSupport = cssSupports('scrollbar-gutter', 'stable') } return hasCSSSupport } export class ModalManager { count = 0 originalPaddingRight = '' originalOverflowY = '' unlockCallbacks = [] open () { if (this.count === 0) { this.lock() } this.count++ } close () { if (this.count === 0) { return } if (this.count === 1) { this.unlock() } this.count-- } lock () { // See https://www.w3.org/TR/CSS22/visufx.html#propdef-overflow let { documentElement: html, body } = document let htmlOverflowY = getComputedStyle(html).overflowY if (htmlOverflowY === 'visible') { // overflow of <body> is propagated to the viewport // check <body> & lock <html> this.onUnlock(lockScroll(body, html)) } else if (htmlOverflowY === 'hidden') { // potential scroll will happen inside <body> // check <body> & lock <body> this.onUnlock(lockScroll(body)) } else { // overflow of <html> is propagated to the viewport // check both & lock both, order matters because changes made on body will // impact scrollHeight for html this.onUnlock(lockScroll(html)) this.onUnlock(lockScroll(body)) } } onUnlock (fn) { this.unlockCallbacks.push(fn) } unlock () { this.unlockCallbacks.forEach((fn) => fn()) this.unlockCallbacks = [] } } /** * Lock scroll based on trigger and target element. * The trigger element and the target element can be different due to the special * behavior for `overflow` on <html> and <body>. * We need to set trigger element's `overflow-y` to `hidden` and add an extra `padding-right` * for the width of the scrollbar. * * @param {HTMLElement} trigger the element whose overflow value may trigger scroll * @param {HTMLElement} target the element where scrollbar appears */ function lockScroll (trigger, target = trigger) { let { scrollHeight, clientHeight } = target if (scrollHeight <= clientHeight) { return () => {} } let triggerStyle = trigger.getAttribute('style') || '' let { overflowY } = getComputedStyle(trigger) let isRoot = target === document.documentElement if (overflowY !== 'hidden' && (isRoot || overflowY !== 'visible')) { let targetStyle = target.getAttribute('style') || '' let scrollbarWidth = getScrollbarWidth() if (getCSSSupport()) { target.setAttribute( 'style', `${targetStyle};scrollbar-gutter:stable;overflow-y:hidden` ) } else { let { paddingRight } = getComputedStyle(target) let newPaddingRight = `${parseInt(paddingRight, 10) + scrollbarWidth}px` target.setAttribute( 'style', `${targetStyle};padding-right:${newPaddingRight};overflow-y:hidden` ) } return () => { trigger.setAttribute('style', triggerStyle) target.setAttribute('style', targetStyle) } } return () => {} } export default new ModalManager()