-
Notifications
You must be signed in to change notification settings - Fork 171
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add ScrollLocker #179
Changes from all commits
54457b2
7b4cf45
b731092
23e8db1
90ad23e
2ed3c27
ed386e6
f861bee
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
import getScrollBarSize from '../getScrollBarSize'; | ||
import setStyle from '../setStyle'; | ||
|
||
export interface scrollLockOptions { | ||
container: HTMLElement; | ||
} | ||
|
||
let passiveSupported = false; | ||
if (typeof window !== 'undefined') { | ||
const passiveTestOption = { | ||
get passive() { | ||
passiveSupported = true; | ||
return null; | ||
}, | ||
}; | ||
|
||
window.addEventListener('testPassive', null, passiveTestOption); | ||
// @ts-ignore compatible passive | ||
window.removeEventListener('testPassive', null, passiveTestOption); | ||
} | ||
|
||
const preventDefault = (event: React.TouchEvent | TouchEvent): boolean => { | ||
const e = event || window.event; | ||
|
||
// If more than one touch we don't prevent | ||
if ((e as TouchEvent).touches.length > 1) return true; | ||
|
||
if (e.preventDefault) e.preventDefault(); | ||
|
||
return false; | ||
}; | ||
|
||
let uuid = 0; | ||
|
||
interface Ilocks { | ||
target: typeof uuid; | ||
cacheStyle?: React.CSSProperties; | ||
options: scrollLockOptions; | ||
} | ||
|
||
let locks: Ilocks[] = []; | ||
const scrollingEffectClassName = 'ant-scrolling-effect'; | ||
const scrollingEffectClassNameReg = new RegExp( | ||
`${scrollingEffectClassName}`, | ||
'g', | ||
); | ||
|
||
export default class ScrollLocker { | ||
lockTarget: typeof uuid; | ||
|
||
options: scrollLockOptions; | ||
|
||
constructor(options?: scrollLockOptions) { | ||
// eslint-disable-next-line no-plusplus | ||
this.lockTarget = uuid++; | ||
this.options = options; | ||
} | ||
|
||
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; | ||
|
||
if (window.innerWidth - document.documentElement.clientWidth > 0) { | ||
scrollBarSize = getScrollBarSize(); | ||
} | ||
|
||
const container = this.options?.container || document.body; | ||
const containerClassName = container.className; | ||
|
||
// https://github.com/ant-design/ant-design/issues/19340 | ||
// https://github.com/ant-design/ant-design/issues/19332 | ||
const cacheStyle = setStyle( | ||
{ | ||
paddingRight: `${scrollBarSize}px`, | ||
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(); | ||
|
||
document.addEventListener( | ||
'touchmove', | ||
preventDefault, | ||
passiveSupported ? { passive: false } : undefined, | ||
); | ||
} | ||
|
||
locks = [ | ||
...locks, | ||
{ target: this.lockTarget, options: this.options, cacheStyle }, | ||
]; | ||
}; | ||
|
||
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( | ||
// @ts-ignore position should be empty string | ||
findLock.cacheStyle || { | ||
paddingRight: '', | ||
overflow: '', | ||
overflowX: '', | ||
overflowY: '', | ||
}, | ||
{ element: container }, | ||
); | ||
container.className = container.className | ||
.replace(scrollingEffectClassNameReg, '') | ||
.trim(); | ||
|
||
// @ts-ignore compatible passive | ||
document.removeEventListener( | ||
'touchmove', | ||
preventDefault, | ||
passiveSupported ? { passive: false } : undefined, | ||
); | ||
}; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,9 +2,10 @@ | |
import * as React from 'react'; | ||
import raf from './raf'; | ||
import Portal, { PortalRef } from './Portal'; | ||
import canUseDom from './Dom/canUseDom'; | ||
import switchScrollingEffect from './switchScrollingEffect'; | ||
import setStyle from './setStyle'; | ||
import canUseDom from './Dom/canUseDom'; | ||
import ScrollLocker from './Dom/scrollLocker'; | ||
|
||
let openCount = 0; | ||
const supportDom = canUseDom(); | ||
|
@@ -50,6 +51,7 @@ export interface PortalWrapperProps { | |
getOpenCount: () => number; | ||
getContainer: () => HTMLElement; | ||
switchScrollingEffect: () => void; | ||
scrollLocker: ScrollLocker; | ||
ref?: (c: any) => void; | ||
}) => React.ReactNode; | ||
} | ||
|
@@ -61,6 +63,15 @@ class PortalWrapper extends React.Component<PortalWrapperProps> { | |
|
||
rafId?: number; | ||
|
||
scrollLocker: ScrollLocker; | ||
|
||
constructor(props: PortalWrapperProps) { | ||
super(props); | ||
this.scrollLocker = new ScrollLocker({ | ||
container: getParent(props.getContainer) as HTMLElement, | ||
}); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. switchScrollingEffect 里的 overflow 相关代码是不是应该清理掉。 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 另外,这里的滚动条是否出现是依赖 openCount 的。 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
不依赖 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Drawer 是依赖的,多层抽屉只有关掉所有才会 unLock。 https://ant.design/components/drawer-cn/#components-drawer-demo-multi-level-drawer 这个之前是好的,后来某个版本给改挂了。 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 嗯,现在改成不依赖了,多层抽屉就是多个 Dialog,�底层都是用的这个 portal,改挂了算是意料之中,因为 |
||
|
||
renderComponent?: (info: { | ||
afterClose: Function; | ||
onClose: Function; | ||
|
@@ -199,6 +210,7 @@ class PortalWrapper extends React.Component<PortalWrapperProps> { | |
getOpenCount: () => openCount, | ||
getContainer: this.getContainer, | ||
switchScrollingEffect: this.switchScrollingEffect, | ||
scrollLocker: this.scrollLocker, | ||
}; | ||
|
||
if (forceRender || visible || this.componentRef.current) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
这里把 master 的 js 体积搞大了,看看是否是必要的。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
等都迁移了就可以把上面的
switchScrollingEffect
删了There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
不能删,老的 antd 版本会挂。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
antd 3 么还是