Merge pull request 'Add click switch to Indicator. Add scrollbar to HideScrollbar. Fix scroll down when horizontal scroll bug. Refactor the HideScrollbar.' (#22) from FatttSnake into dev

Reviewed-on: FatttSnake/fatweb-ui#22
This commit was merged in pull request #22.
This commit is contained in:
FatttSnake
2023-09-24 13:46:34 +08:00
6 changed files with 454 additions and 170 deletions

View File

@@ -1,16 +1,66 @@
@use '@/assets/css/constants' as constants;
.hide-scrollbar-mask { .hide-scrollbar-mask {
width: 100%; width: 100%;
height: 100%; height: 100%;
overflow: hidden; overflow: hidden;
}
.hide-scrollbar-selection { .hide-scrollbar-selection {
position: relative; position: relative;
overflow: scroll; overflow: scroll;
scrollbar-width: none; scrollbar-width: none;
-ms-overflow-style: none; -ms-overflow-style: none;
}
.hide-scrollbar-selection::-webkit-scrollbar { .hide-scrollbar-content {
display: none; display: inline-block;
}
}
::-webkit-scrollbar {
display: none;
}
.scrollbar {
position: absolute;
z-index: 1000;
opacity: .5;
touch-action: none;
.box {
position: relative;
width: 100%;
height: 100%;
border-radius: 8px;
overflow: hidden;
.block {
position: absolute;
width: 100%;
height: 100%;
border-radius: 8px;
background-color: constants.$font-secondary-color;
transition: background-color .2s;
}
:hover {
background-color: constants.$font-main-color;
}
}
}
.vertical-scrollbar {
padding: 12px 4px;
width: 16px;
height: 100%;
right: 0;
top: 0;
}
.horizontal-scrollbar {
padding: 4px 12px;
width: 100%;
height: 16px;
left: 0;
bottom: 0;
}
} }

View File

@@ -9,6 +9,7 @@
.item { .item {
flex: auto; flex: auto;
cursor: pointer;
.dot { .dot {
width: 10px; width: 10px;
@@ -19,10 +20,15 @@
color: constants.$font-secondary-color; color: constants.$font-secondary-color;
style: solid; style: solid;
}; };
transition: all .2s;
}
:hover {
background-color: constants.$focus-color;
} }
} }
.active>* { .active > * {
background-color: constants.$font-secondary-color; background-color: constants.$font-secondary-color !important;
} }
} }

View File

@@ -2,3 +2,4 @@ $main-color: #00D4FF;
$background-color: #F5F5F5; $background-color: #F5F5F5;
$font-main-color: #4D4D4D; $font-main-color: #4D4D4D;
$font-secondary-color: #9E9E9E; $font-secondary-color: #9E9E9E;
$focus-color: #DDDDDD

View File

@@ -43,124 +43,6 @@ export interface HideScrollbarElement {
} }
const HideScrollbar = forwardRef<HideScrollbarElement, HideScrollbarProps>((props, ref) => { const HideScrollbar = forwardRef<HideScrollbarElement, HideScrollbarProps>((props, ref) => {
const rootRef = useRef<HTMLDivElement>(null)
const lastTouchPosition = useRef<{ x: number; y: number }>({ x: -1, y: -1 })
const [verticalScrollbarWidth, setVerticalScrollbarWidth] = useState(0)
const [horizontalScrollbarWidth, setHorizontalScrollbarWidth] = useState(0)
const {
isPreventScroll,
isPreventVerticalScroll,
isPreventHorizontalScroll,
isShowVerticalScrollbar,
isShowHorizontalScrollbar,
..._props
} = props
const handleDefaultWheel = useCallback(
(event: WheelEvent) => {
if (!event.altKey && !event.ctrlKey) {
if (isPreventScroll) {
event.preventDefault()
return
}
if (isPreventVerticalScroll && !event.shiftKey && !event.deltaX) {
event.preventDefault()
return
}
if (isPreventHorizontalScroll && (event.shiftKey || !event.deltaY)) {
event.preventDefault()
return
}
}
},
[isPreventScroll, isPreventHorizontalScroll, isPreventVerticalScroll]
)
const handleDefaultTouchStart = useCallback(
(event: TouchEvent) => {
if (event.touches.length !== 1 || isPreventScroll) {
lastTouchPosition.current = { x: -1, y: -1 }
return
}
const { clientX, clientY } = event.touches[0]
lastTouchPosition.current = { x: clientX, y: clientY }
},
[isPreventScroll]
)
const handleDefaultTouchmove = useCallback(
(event: TouchEvent) => {
event.preventDefault()
if (event.touches.length !== 1 || isPreventScroll) {
lastTouchPosition.current = { x: -1, y: -1 }
return
}
const { clientX, clientY } = event.touches[0]
if (!isPreventVerticalScroll) {
rootRef.current?.scrollTo({
top: rootRef.current?.scrollTop + (lastTouchPosition.current.y - clientY),
behavior: 'instant'
})
}
if (!isPreventHorizontalScroll) {
rootRef.current?.scrollTo({
left: rootRef.current?.scrollLeft + (lastTouchPosition.current.x - clientX),
behavior: 'instant'
})
}
lastTouchPosition.current = { x: clientX, y: clientY }
},
[isPreventScroll, isPreventHorizontalScroll, isPreventVerticalScroll]
)
const handleDefaultClickMiddleMouseButton = useCallback((event: MouseEvent) => {
if (event.button === 1) {
event.preventDefault()
}
}, [])
const handleDefaultKeyDown = useCallback(
(event: KeyboardEvent) => {
if (
isPreventScroll &&
[
'ArrowUp',
'ArrowDown',
'ArrowLeft',
'ArrowRight',
' ',
'',
'PageUp',
'PageDown',
'Home',
'End'
].find((value) => value === event.key)
) {
event.preventDefault()
}
if (
isPreventVerticalScroll &&
['ArrowUp', 'ArrowDown', ' ', '', 'PageUp', 'PageDown', 'Home', 'End'].find(
(value) => value === event.key
)
) {
event.preventDefault()
}
if (
isPreventHorizontalScroll &&
['ArrowLeft', 'ArrowRight'].find((value) => value === event.key)
) {
event.preventDefault()
}
},
[isPreventScroll, isPreventHorizontalScroll, isPreventVerticalScroll]
)
useImperativeHandle<HideScrollbarElement, HideScrollbarElement>( useImperativeHandle<HideScrollbarElement, HideScrollbarElement>(
ref, ref,
() => { () => {
@@ -247,72 +129,378 @@ const HideScrollbar = forwardRef<HideScrollbarElement, HideScrollbarProps>((prop
[] []
) )
useEffect(() => { const maskRef = useRef<HTMLDivElement>(null)
const hideScrollbarElement = rootRef.current const rootRef = useRef<HTMLDivElement>(null)
const contentRef = useRef<HTMLDivElement>(null)
const wheelListenerRef = useRef<(event: WheelEvent) => void>(() => undefined)
const lastScrollbarClickPositionRef = useRef({ x: -1, y: -1 })
const lastScrollbarTouchPositionRef = useRef({ x: -1, y: -1 })
const lastTouchPositionRef = useRef({ x: -1, y: -1 })
const [verticalScrollbarWidth, setVerticalScrollbarWidth] = useState(0)
const [verticalScrollbarLength, setVerticalScrollbarLength] = useState(100)
const [verticalScrollbarPosition, setVerticalScrollbarPosition] = useState(0)
const [verticalScrollbarOnClick, setVerticalScrollbarOnClick] = useState(false)
const [verticalScrollbarOnTouch, setVerticalScrollbarOnTouch] = useState(false)
const [horizontalScrollbarWidth, setHorizontalScrollbarWidth] = useState(0)
const [horizontalScrollbarLength, setHorizontalScrollbarLength] = useState(100)
const [horizontalScrollbarPosition, setHorizontalScrollbarPosition] = useState(0)
const [horizontalScrollbarOnClick, setHorizontalScrollbarOnClick] = useState(false)
const [horizontalScrollbarOnTouch, setHorizontalScrollbarOnTouch] = useState(false)
const {
isPreventScroll,
isPreventVerticalScroll,
isPreventHorizontalScroll,
isShowVerticalScrollbar,
isShowHorizontalScrollbar,
..._props
} = props
const isPreventAnyScroll =
isPreventScroll || isPreventVerticalScroll || isPreventHorizontalScroll
const handleDefaultTouchStart = useCallback(
(event: React.TouchEvent) => {
if (event.touches.length !== 1 || isPreventScroll) {
lastTouchPositionRef.current = { x: -1, y: -1 }
return
}
const { clientX, clientY } = event.touches[0]
lastTouchPositionRef.current = { x: clientX, y: clientY }
},
[isPreventScroll]
)
const handleDefaultTouchmove = useCallback(
(event: React.TouchEvent) => {
if (event.touches.length !== 1 || isPreventScroll) {
lastTouchPositionRef.current = { x: -1, y: -1 }
return
}
const { clientX, clientY } = event.touches[0]
if (!isPreventVerticalScroll) {
rootRef.current?.scrollTo({
top: rootRef.current?.scrollTop + (lastTouchPositionRef.current.y - clientY),
behavior: 'instant'
})
}
if (!isPreventHorizontalScroll) {
rootRef.current?.scrollTo({
left: rootRef.current?.scrollLeft + (lastTouchPositionRef.current.x - clientX),
behavior: 'instant'
})
}
lastTouchPositionRef.current = { x: clientX, y: clientY }
},
[isPreventHorizontalScroll, isPreventScroll, isPreventVerticalScroll]
)
const handleDefaultMouseDown = (event: React.MouseEvent) => {
if (isPreventAnyScroll)
if (event.button === 1) {
event.preventDefault()
}
}
const handleDefaultKeyDown = useCallback(
(event: React.KeyboardEvent) => {
if (
isPreventScroll &&
[
'ArrowUp',
'ArrowDown',
'ArrowLeft',
'ArrowRight',
' ',
'',
'PageUp',
'PageDown',
'Home',
'End'
].find((value) => value === event.key)
) {
event.preventDefault()
}
if (
isPreventVerticalScroll &&
['ArrowUp', 'ArrowDown', ' ', '', 'PageUp', 'PageDown', 'Home', 'End'].find(
(value) => value === event.key
)
) {
event.preventDefault()
}
if (
isPreventHorizontalScroll &&
['ArrowLeft', 'ArrowRight'].find((value) => value === event.key)
) {
event.preventDefault()
}
},
[isPreventHorizontalScroll, isPreventScroll, isPreventVerticalScroll]
)
const handleScrollbarMouseEvent = (eventFlag: string, scrollbarFlag: string) => {
return (event: React.MouseEvent) => {
switch (eventFlag) {
case 'down':
lastScrollbarClickPositionRef.current = { x: event.clientX, y: event.clientY }
switch (scrollbarFlag) {
case 'vertical':
setVerticalScrollbarOnClick(true)
break
case 'horizontal':
setHorizontalScrollbarOnClick(true)
break
}
break
case 'up':
case 'leave':
setVerticalScrollbarOnClick(false)
setHorizontalScrollbarOnClick(false)
break
case 'move':
if (verticalScrollbarOnClick) {
rootRef.current?.scrollTo({
top:
rootRef.current?.scrollTop +
((event.clientY - lastScrollbarClickPositionRef.current.y) /
(rootRef.current?.clientHeight ?? 1)) *
(contentRef.current?.clientHeight ?? 0),
behavior: 'instant'
})
}
if (horizontalScrollbarOnClick) {
rootRef.current?.scrollTo({
left:
rootRef.current?.scrollLeft +
((event.clientX - lastScrollbarClickPositionRef.current.x) /
(rootRef.current?.clientWidth ?? 1)) *
(contentRef.current?.clientWidth ?? 0),
behavior: 'instant'
})
}
lastScrollbarClickPositionRef.current = {
x: event.clientX,
y: event.clientY
}
}
}
}
const handleScrollbarTouchEvent = (eventFlag: string, scrollbarFlag: string) => {
return (event: React.TouchEvent) => {
switch (eventFlag) {
case 'start':
if (event.touches.length !== 1) {
return
}
lastScrollbarTouchPositionRef.current = {
x: event.touches[0].clientX,
y: event.touches[0].clientY
}
switch (scrollbarFlag) {
case 'vertical':
setVerticalScrollbarOnTouch(true)
break
case 'horizontal':
setHorizontalScrollbarOnTouch(true)
break
}
break
case 'end':
case 'cancel':
setVerticalScrollbarOnTouch(false)
setHorizontalScrollbarOnTouch(false)
break
case 'move':
if (event.touches.length !== 1) {
return
}
if (verticalScrollbarOnTouch) {
rootRef.current?.scrollTo({
top:
rootRef.current?.scrollTop +
((event.touches[0].clientY -
lastScrollbarClickPositionRef.current.y) /
(rootRef.current?.clientHeight ?? 1)) *
(contentRef.current?.clientHeight ?? 0),
behavior: 'instant'
})
}
if (horizontalScrollbarOnTouch) {
rootRef.current?.scrollTo({
left:
rootRef.current?.scrollLeft +
((event.touches[0].clientX -
lastScrollbarClickPositionRef.current.x) /
(rootRef.current?.clientWidth ?? 1)) *
(contentRef.current?.clientWidth ?? 0),
behavior: 'instant'
})
}
lastScrollbarClickPositionRef.current = {
x: event.touches[0].clientX,
y: event.touches[0].clientY
}
}
}
}
const handleDefaultScroll = () => {
setVerticalScrollbarPosition(
((rootRef.current?.scrollTop ?? 0) / (contentRef.current?.clientHeight ?? 1)) * 100
)
setHorizontalScrollbarPosition(
((rootRef.current?.scrollLeft ?? 0) / (contentRef.current?.clientWidth ?? 1)) * 100
)
}
useEffect(() => {
const windowResizeListener = () => { const windowResizeListener = () => {
setVerticalScrollbarWidth( setVerticalScrollbarWidth(
(hideScrollbarElement?.offsetWidth ?? 0) - (hideScrollbarElement?.clientWidth ?? 0) (rootRef.current?.offsetWidth ?? 0) - (rootRef.current?.clientWidth ?? 0)
) )
setHorizontalScrollbarWidth( setHorizontalScrollbarWidth(
(hideScrollbarElement?.offsetHeight ?? 0) - (rootRef.current?.offsetHeight ?? 0) - (rootRef.current?.clientHeight ?? 0)
(hideScrollbarElement?.clientHeight ?? 0)
) )
return windowResizeListener return windowResizeListener
} }
setTimeout(() => { setTimeout(() => {
windowResizeListener() windowResizeListener()
}, 1000) }, 1000)
window.addEventListener('resize', windowResizeListener()) window.addEventListener('resize', windowResizeListener())
if (isPreventScroll || isPreventVerticalScroll || isPreventHorizontalScroll) {
rootRef.current?.removeEventListener('wheel', wheelListenerRef.current)
if (isPreventAnyScroll) {
const handleDefaultWheel = (event: WheelEvent) => {
if (!event.altKey && !event.ctrlKey) {
if (isPreventScroll) {
event.preventDefault()
return
}
if (isPreventVerticalScroll && !event.shiftKey && !event.deltaX) {
event.preventDefault()
return
}
if (isPreventHorizontalScroll && (event.shiftKey || !event.deltaY)) {
event.preventDefault()
return
}
}
}
wheelListenerRef.current = handleDefaultWheel
rootRef.current?.addEventListener('wheel', handleDefaultWheel, { passive: false }) rootRef.current?.addEventListener('wheel', handleDefaultWheel, { passive: false })
rootRef.current?.addEventListener('touchstart', handleDefaultTouchStart, {
passive: false
})
rootRef.current?.addEventListener('touchmove', handleDefaultTouchmove, {
passive: false
})
rootRef.current?.addEventListener('mousedown', handleDefaultClickMiddleMouseButton)
rootRef.current?.addEventListener('keydown', handleDefaultKeyDown)
} else {
rootRef.current?.removeEventListener('wheel', handleDefaultWheel)
rootRef.current?.removeEventListener('touchstart', handleDefaultTouchStart)
rootRef.current?.removeEventListener('touchmove', handleDefaultTouchmove)
rootRef.current?.removeEventListener('mousedown', handleDefaultClickMiddleMouseButton)
rootRef.current?.removeEventListener('keydown', handleDefaultKeyDown)
} }
rootRef.current &&
setVerticalScrollbarLength(
(rootRef.current.clientHeight / (contentRef.current?.clientHeight ?? 0)) * 100
)
rootRef.current &&
setHorizontalScrollbarLength(
(rootRef.current.clientWidth / (contentRef.current?.clientWidth ?? 0)) * 100
)
return () => { return () => {
window.removeEventListener('resize', windowResizeListener) window.removeEventListener('resize', windowResizeListener)
} }
}, [ }, [isPreventAnyScroll, isPreventHorizontalScroll, isPreventScroll, isPreventVerticalScroll])
handleDefaultClickMiddleMouseButton,
handleDefaultKeyDown,
handleDefaultTouchStart,
handleDefaultTouchmove,
handleDefaultWheel,
isPreventHorizontalScroll,
isPreventScroll,
isPreventVerticalScroll
])
return ( return (
<> <>
<div className={'hide-scrollbar-mask'}> <div
className={'hide-scrollbar-mask'}
ref={maskRef}
onMouseMove={
!isPreventScroll ? handleScrollbarMouseEvent('move', 'all') : undefined
}
onTouchMove={
!isPreventScroll ? handleScrollbarTouchEvent('move', 'all') : undefined
}
onMouseUp={!isPreventScroll ? handleScrollbarMouseEvent('up', 'all') : undefined}
onTouchEnd={!isPreventScroll ? handleScrollbarTouchEvent('end', 'all') : undefined}
onMouseLeave={
!isPreventScroll ? handleScrollbarMouseEvent('leave', 'all') : undefined
}
onTouchCancel={
!isPreventScroll ? handleScrollbarTouchEvent('cancel', 'all') : undefined
}
>
<div <div
ref={rootRef} ref={rootRef}
className={'hide-scrollbar-selection'} className={'hide-scrollbar-selection'}
tabIndex={0} tabIndex={0}
style={{ style={{
width: `calc(100vw + ${verticalScrollbarWidth}px)`, width: `calc(100vw + ${verticalScrollbarWidth}px)`,
height: `calc(100vh + ${horizontalScrollbarWidth}px` height: `calc(100vh + ${horizontalScrollbarWidth}px)`,
touchAction: isPreventAnyScroll ? 'none' : '',
msTouchAction: isPreventAnyScroll ? 'none' : ''
}} }}
{..._props} {..._props}
onMouseDown={isPreventAnyScroll ? handleDefaultMouseDown : undefined}
onKeyDown={isPreventAnyScroll ? handleDefaultKeyDown : undefined}
onTouchStart={isPreventAnyScroll ? handleDefaultTouchStart : undefined}
onTouchMove={isPreventAnyScroll ? handleDefaultTouchmove : undefined}
onScroll={handleDefaultScroll}
> >
{props.children} <div className={'hide-scrollbar-content'} ref={contentRef}>
{props.children}
</div>
</div>
<div hidden={!isShowVerticalScrollbar} className={'scrollbar vertical-scrollbar'}>
<div className={'box'}>
<div
className={'block'}
style={{
height: `${verticalScrollbarLength}%`,
top: `clamp(0px, ${verticalScrollbarPosition}%, ${
100 - verticalScrollbarLength
}%)`
}}
onMouseDown={
!isPreventScroll && !isPreventVerticalScroll
? handleScrollbarMouseEvent('down', 'vertical')
: undefined
}
onTouchStart={
!isPreventScroll && !isPreventVerticalScroll
? handleScrollbarTouchEvent('start', 'vertical')
: undefined
}
/>
</div>
</div>
<div
hidden={!isShowHorizontalScrollbar}
className={'scrollbar horizontal-scrollbar'}
>
<div className={'box'}>
<div
className={'block'}
style={{
width: `${horizontalScrollbarLength}%`,
left: `clamp(0px, ${horizontalScrollbarPosition}%, ${
100 - horizontalScrollbarLength
}%)`
}}
onMouseDown={
!isPreventScroll && !isPreventHorizontalScroll
? handleScrollbarMouseEvent('down', 'horizontal')
: undefined
}
onTouchStart={
!isPreventScroll && !isPreventHorizontalScroll
? handleScrollbarTouchEvent('start', 'horizontal')
: undefined
}
/>
</div>
</div> </div>
</div> </div>
</> </>

View File

@@ -1,4 +1,4 @@
import React, { useEffect } from 'react' import React from 'react'
import '@/assets/css/components/home/home.scss' import '@/assets/css/components/home/home.scss'
import FitFullScreen from '@/components/common/FitFullScreen' import FitFullScreen from '@/components/common/FitFullScreen'
import FitCenter from '@/components/common/FitCenter' import FitCenter from '@/components/common/FitCenter'
@@ -72,7 +72,7 @@ const Home: React.FC = () => {
} }
const handleWheel = (event: React.WheelEvent) => { const handleWheel = (event: React.WheelEvent) => {
if (event.altKey || event.ctrlKey || event.shiftKey) { if (event.altKey || event.ctrlKey || event.shiftKey || event.deltaY === 0) {
return return
} }
@@ -126,6 +126,11 @@ const Home: React.FC = () => {
} }
} }
const handleIndicatorSwitch = (index: number) => {
setCurrentContent(index)
handleScrollToContent(index)()
}
const content = [ const content = [
{ {
backgroundColor: '#FBFBFB', backgroundColor: '#FBFBFB',
@@ -153,7 +158,11 @@ const Home: React.FC = () => {
</div> </div>
<div hidden={navbarHidden} className={'indicator'}> <div hidden={navbarHidden} className={'indicator'}>
<Indicator total={content.length} current={currentContent} /> <Indicator
total={content.length}
current={currentContent}
onSwitch={handleIndicatorSwitch}
/>
</div> </div>
</> </>
) )

View File

@@ -13,17 +13,33 @@ export const MainFrameworkContext = createContext<{
preventScroll: boolean preventScroll: boolean
setPreventScroll: (newValue: boolean) => void setPreventScroll: (newValue: boolean) => void
} }
showVerticalScrollbarState: {
showVerticalScrollbar: boolean
setShowVerticalScrollbar: (newValue: boolean) => void
}
showHorizontalScrollbarState: {
showHorizontalScrollbar: boolean
setShowHorizontalScrollbar: (newValue: boolean) => void
}
hideScrollbarRef: React.RefObject<HideScrollbarElement> hideScrollbarRef: React.RefObject<HideScrollbarElement>
}>({ }>({
navbarHiddenState: { navbarHiddenState: {
navbarHidden: false, navbarHidden: false,
setNavbarHidden: () => undefined setNavbarHidden: () => undefined
}, },
hideScrollbarRef: createRef(),
preventScrollState: { preventScrollState: {
preventScroll: false, preventScroll: false,
setPreventScroll: () => undefined setPreventScroll: () => undefined
} },
showVerticalScrollbarState: {
showVerticalScrollbar: false,
setShowVerticalScrollbar: () => undefined
},
showHorizontalScrollbarState: {
showHorizontalScrollbar: false,
setShowHorizontalScrollbar: () => undefined
},
hideScrollbarRef: createRef()
}) })
const MainFramework: React.FC = () => { const MainFramework: React.FC = () => {
@@ -36,6 +52,8 @@ const MainFramework: React.FC = () => {
const [navbarHidden, setNavbarHidden] = useState(true) const [navbarHidden, setNavbarHidden] = useState(true)
const [preventScroll, setPreventScroll] = useState(false) const [preventScroll, setPreventScroll] = useState(false)
const [showVerticalScrollbar, setShowVerticalScrollbar] = useState(false)
const [showHorizontalScrollbar, setShowHorizontalScrollbar] = useState(false)
useEffect(() => { useEffect(() => {
setNavbarHidden(false) setNavbarHidden(false)
@@ -43,7 +61,11 @@ const MainFramework: React.FC = () => {
return ( return (
<> <>
<HideScrollbar ref={hideScrollbarRef} isPreventVerticalScroll={preventScroll}> <HideScrollbar
ref={hideScrollbarRef}
isPreventVerticalScroll={preventScroll}
isShowHorizontalScrollbar={true}
>
<div className={'body'}> <div className={'body'}>
<header className={'nav' + (navbarHidden ? ' hide' : '')}> <header className={'nav' + (navbarHidden ? ' hide' : '')}>
<a className={'logo'} href={'https://fatweb.top'}> <a className={'logo'} href={'https://fatweb.top'}>
@@ -73,6 +95,14 @@ const MainFramework: React.FC = () => {
value={{ value={{
navbarHiddenState: { navbarHidden, setNavbarHidden }, navbarHiddenState: { navbarHidden, setNavbarHidden },
preventScrollState: { preventScroll, setPreventScroll }, preventScrollState: { preventScroll, setPreventScroll },
showVerticalScrollbarState: {
showVerticalScrollbar,
setShowVerticalScrollbar
},
showHorizontalScrollbarState: {
showHorizontalScrollbar,
setShowHorizontalScrollbar
},
hideScrollbarRef hideScrollbarRef
}} }}
> >