diff --git a/packages/website-ue/src/components/Scroller/index.module.css b/packages/website-ue/src/components/Scroller/index.module.css index 9bc2bb39d..2fbd01ea9 100644 --- a/packages/website-ue/src/components/Scroller/index.module.css +++ b/packages/website-ue/src/components/Scroller/index.module.css @@ -3,6 +3,8 @@ flex-direction: column; gap: var(--space-3); + position: relative; + .content { overflow: auto; @@ -25,7 +27,7 @@ border-radius: var(--max-size); .bar-container { - height: 10px; + height: var(--space-3); position: relative; @@ -52,4 +54,30 @@ opacity: 1; } } + + .curtain { + width: min(25%, var(--space-7)); + height: calc(100% - var(--space-3) - 2 * var(--space-1)); + + position: absolute; + top: 0; + + opacity: 0; + + transition: opacity var(--duration-brief); + + &[data-side="left"] { + left: 0; + background: linear-gradient(90deg, var(--gray-1), transparent); + } + + &[data-side="right"] { + right: 0; + background: linear-gradient(90deg, transparent, var(--gray-1)); + } + + &[data-visible="true"] { + opacity: 1; + } + } } diff --git a/packages/website-ue/src/components/Scroller/index.tsx b/packages/website-ue/src/components/Scroller/index.tsx index e704c81c5..d457ac739 100644 --- a/packages/website-ue/src/components/Scroller/index.tsx +++ b/packages/website-ue/src/components/Scroller/index.tsx @@ -8,6 +8,8 @@ export function Scroller({ children, className, ...props }: ScrollerProps) { const bar = useRef(null!); const container = useRef(null!); const content = useRef(null!); + const curtainLeft = useRef(null!); + const curtainRight = useRef(null!); useEffect(() => { function updateBarSize() { @@ -19,7 +21,7 @@ export function Scroller({ children, className, ...props }: ScrollerProps) { bar.current.style.width = `${(visible / total) * 100}%`; } - function syncBarToScroll() { + function syncBar() { const maxTravel = container.current.clientWidth - bar.current.clientWidth; const maxScroll = content.current.scrollWidth - content.current.clientWidth; @@ -33,6 +35,16 @@ export function Scroller({ children, className, ...props }: ScrollerProps) { bar.current.style.left = `${ratio * maxTravel}px`; } + function syncCurtains() { + const el = content.current; + + const showLeft = el.scrollLeft > 0; + const showRight = el.scrollLeft + el.clientWidth < el.scrollWidth; + + curtainLeft.current.dataset.visible = showLeft ? "true" : "false"; + curtainRight.current.dataset.visible = showRight ? "true" : "false"; + } + let lastX = 0; function handlePointerDown(event: PointerEvent) { @@ -56,7 +68,7 @@ export function Scroller({ children, className, ...props }: ScrollerProps) { content.current.scrollLeft += delta; - syncBarToScroll(); + syncBar(); } function handlePointerUp(event: PointerEvent) { @@ -65,17 +77,31 @@ export function Scroller({ children, className, ...props }: ScrollerProps) { window.removeEventListener("pointerup", handlePointerUp); } + function handleScroll() { + syncBar(); + syncCurtains(); + } + + function handleResize() { + updateBarSize(); + syncBar(); + syncCurtains(); + } + updateBarSize(); - syncBarToScroll(); + syncBar(); + syncCurtains(); bar.current.addEventListener("pointerdown", handlePointerDown); - content.current.addEventListener("scroll", syncBarToScroll); - window.addEventListener("resize", updateBarSize); + content.current.addEventListener("scroll", syncBar); + window.addEventListener("resize", handleResize); + content.current.addEventListener("scroll", handleScroll); return () => { bar.current.removeEventListener("pointerdown", handlePointerDown); - content.current.removeEventListener("scroll", syncBarToScroll); - window.removeEventListener("resize", updateBarSize); + content.current.removeEventListener("scroll", syncBar); + window.removeEventListener("resize", handleResize); + content.current.removeEventListener("scroll", handleScroll); }; }, []); @@ -90,6 +116,9 @@ export function Scroller({ children, className, ...props }: ScrollerProps) {
+ +
+
); }