131 lines
3.5 KiB
TypeScript
131 lines
3.5 KiB
TypeScript
import type { CSSProperties } from 'vue';
|
|
import getScrollBarSize from '../../_util/getScrollBarSize';
|
|
import setStyle from '../../_util/setStyle';
|
|
|
|
export interface scrollLockOptions {
|
|
container: HTMLElement;
|
|
}
|
|
|
|
interface Ilocks {
|
|
target: typeof uuid;
|
|
options: scrollLockOptions;
|
|
}
|
|
|
|
let locks: Ilocks[] = [];
|
|
const scrollingEffectClassName = 'ant-scrolling-effect';
|
|
const scrollingEffectClassNameReg = new RegExp(`${scrollingEffectClassName}`, 'g');
|
|
|
|
let uuid = 0;
|
|
|
|
// https://github.com/ant-design/ant-design/issues/19340
|
|
// https://github.com/ant-design/ant-design/issues/19332
|
|
const cacheStyle = new Map<Element, CSSProperties>();
|
|
|
|
export default class ScrollLocker {
|
|
private lockTarget: typeof uuid;
|
|
|
|
private options: scrollLockOptions;
|
|
|
|
constructor(options?: scrollLockOptions) {
|
|
// eslint-disable-next-line no-plusplus
|
|
this.lockTarget = uuid++;
|
|
this.options = options;
|
|
}
|
|
|
|
getContainer = (): HTMLElement | undefined => {
|
|
return this.options?.container;
|
|
};
|
|
|
|
// if options change...
|
|
reLock = (options?: scrollLockOptions) => {
|
|
const findLock = locks.find(({ target }) => target === this.lockTarget);
|
|
|
|
if (findLock) {
|
|
this.unLock();
|
|
}
|
|
|
|
this.options = options;
|
|
|
|
if (findLock) {
|
|
findLock.options = options;
|
|
this.lock();
|
|
}
|
|
};
|
|
|
|
lock = () => {
|
|
// If lockTarget exist return
|
|
if (locks.some(({ target }) => target === this.lockTarget)) {
|
|
return;
|
|
}
|
|
|
|
// If same container effect, return
|
|
if (locks.some(({ options }) => options?.container === this.options?.container)) {
|
|
locks = [...locks, { target: this.lockTarget, options: this.options }];
|
|
return;
|
|
}
|
|
|
|
let scrollBarSize = 0;
|
|
const container = this.options?.container || document.body;
|
|
|
|
if (
|
|
(container === document.body &&
|
|
window.innerWidth - document.documentElement.clientWidth > 0) ||
|
|
container.scrollHeight > container.clientHeight
|
|
) {
|
|
scrollBarSize = getScrollBarSize();
|
|
}
|
|
|
|
const containerClassName = container.className;
|
|
|
|
if (
|
|
locks.filter(({ options }) => options?.container === this.options?.container).length === 0
|
|
) {
|
|
cacheStyle.set(
|
|
container,
|
|
setStyle(
|
|
{
|
|
width: scrollBarSize !== 0 ? `calc(100% - ${scrollBarSize}px)` : undefined,
|
|
overflow: 'hidden',
|
|
overflowX: 'hidden',
|
|
overflowY: 'hidden',
|
|
},
|
|
{
|
|
element: container,
|
|
},
|
|
),
|
|
);
|
|
}
|
|
|
|
// https://github.com/ant-design/ant-design/issues/19729
|
|
if (!scrollingEffectClassNameReg.test(containerClassName)) {
|
|
const addClassName = `${containerClassName} ${scrollingEffectClassName}`;
|
|
container.className = addClassName.trim();
|
|
}
|
|
|
|
locks = [...locks, { target: this.lockTarget, options: this.options }];
|
|
};
|
|
|
|
unLock = () => {
|
|
const findLock = locks.find(({ target }) => target === this.lockTarget);
|
|
|
|
locks = locks.filter(({ target }) => target !== this.lockTarget);
|
|
|
|
if (
|
|
!findLock ||
|
|
locks.some(({ options }) => options?.container === findLock.options?.container)
|
|
) {
|
|
return;
|
|
}
|
|
|
|
// Remove Effect
|
|
const container = this.options?.container || document.body;
|
|
const containerClassName = container.className;
|
|
|
|
if (!scrollingEffectClassNameReg.test(containerClassName)) return;
|
|
|
|
setStyle(cacheStyle.get(container), { element: container });
|
|
cacheStyle.delete(container);
|
|
container.className = container.className.replace(scrollingEffectClassNameReg, '').trim();
|
|
};
|
|
}
|