Files
oxygen-ui/src/components/HideScrollbar.tsx

171 lines
6.0 KiB
TypeScript

import '@/assets/css/hide-scrollbar.scss'
export interface HideScrollbarElement {
scrollTo(x: number, y: number): void
scrollX(x: number): void
scrollY(y: number): void
scrollLeft(length: number): void
scrollRight(length: number): void
scrollUp(length: number): void
scrollDown(length: number): void
getX(): number
getY(): number
addEventListenerWithType<K extends keyof HTMLElementEventMap>(
type: K,
listener: (this: HTMLDivElement, ev: HTMLElementEventMap[K]) => never,
options?: boolean | AddEventListenerOptions
): void
addEventListener(
type: string,
listener: EventListenerOrEventListenerObject,
options?: boolean | AddEventListenerOptions
): void
removeEventListenerWithType<K extends keyof HTMLElementEventMap>(
type: K,
listener: (this: HTMLDivElement, ev: HTMLElementEventMap[K]) => never,
options?: boolean | EventListenerOptions
): void
removeEventListener(
type: string,
listener: EventListenerOrEventListenerObject,
options?: boolean | EventListenerOptions
): void
}
const HideScrollbar = forwardRef<HideScrollbarElement, PropsWithChildren>((props, ref) => {
const rootRef = useRef<HTMLDivElement>(null)
const [verticalScrollbarWidth, setVerticalScrollbarWidth] = useState(0)
const [horizontalScrollbarWidth, setHorizontalScrollbarWidth] = useState(0)
useImperativeHandle<HideScrollbarElement, HideScrollbarElement>(
ref,
() => {
return {
scrollTo(x, y) {
rootRef.current?.scrollTo({
left: x,
top: y,
behavior: 'smooth'
})
},
scrollX(x) {
rootRef.current?.scrollTo({
left: x,
behavior: 'smooth'
})
},
scrollY(y) {
rootRef.current?.scrollTo({
top: y,
behavior: 'smooth'
})
},
scrollLeft(length) {
rootRef.current?.scrollTo({
left: rootRef.current?.scrollLeft - length,
behavior: 'smooth'
})
},
scrollRight(length) {
rootRef.current?.scrollTo({
left: rootRef.current?.scrollLeft + length,
behavior: 'smooth'
})
},
scrollUp(length) {
rootRef.current?.scrollTo({
top: rootRef.current?.scrollTop - length,
behavior: 'smooth'
})
},
scrollDown(length) {
rootRef.current?.scrollTo({
top: rootRef.current?.scrollTop + length,
behavior: 'smooth'
})
},
getX() {
return rootRef.current?.scrollLeft ?? 0
},
getY() {
return rootRef.current?.scrollTop ?? 0
},
addEventListenerWithType<K extends keyof HTMLElementEventMap>(
type: K,
listener: (this: HTMLDivElement, ev: HTMLElementEventMap[K]) => never,
options?: boolean | AddEventListenerOptions
): void {
rootRef.current?.addEventListener<K>(type, listener, options)
},
addEventListener(
type: string,
listener: EventListenerOrEventListenerObject,
options?: boolean | AddEventListenerOptions
): void {
rootRef.current?.addEventListener(type, listener, options)
},
removeEventListenerWithType<K extends keyof HTMLElementEventMap>(
type: K,
listener: (this: HTMLDivElement, ev: HTMLElementEventMap[K]) => never,
options?: boolean | EventListenerOptions
): void {
rootRef.current?.removeEventListener<K>(type, listener, options)
},
removeEventListener(
type: string,
listener: EventListenerOrEventListenerObject,
options?: boolean | EventListenerOptions
): void {
rootRef.current?.removeEventListener(type, listener, options)
}
}
},
[]
)
useEffect(() => {
const hideScrollbarElement = rootRef.current
const windowResizeListener = () => {
setVerticalScrollbarWidth(
(hideScrollbarElement?.offsetWidth ?? 0) - (hideScrollbarElement?.clientWidth ?? 0)
)
setHorizontalScrollbarWidth(
(hideScrollbarElement?.offsetHeight ?? 0) -
(hideScrollbarElement?.clientHeight ?? 0)
)
return windowResizeListener
}
setTimeout(() => {
windowResizeListener()
}, 1000)
window.addEventListener('resize', windowResizeListener())
return () => {
window.removeEventListener('resize', windowResizeListener)
}
}, [])
return (
<>
<div className={'hide-scrollbar-mask'}>
<div
ref={rootRef}
className={'hide-scrollbar-selection'}
style={{
width: `calc(100vw + ${verticalScrollbarWidth}px)`,
height: `calc(100vh + ${horizontalScrollbarWidth}px`
}}
>
{props.children}
</div>
</div>
</>
)
})
export default HideScrollbar