mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-21 02:37:05 +02:00
port scroller
This commit is contained in:
1 parent
1416d64e34
commit
0b740e3f60
2 files changed
+116
No files matched your search
@@ -0,0 +1,48 @@
|
|||||||
|
.scroller {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--space-3);
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.content {
|
||||||
|
overflow: auto;
|
||||||
|
|
||||||
|
padding-bottom: var(--space-4);
|
||||||
|
|
||||||
|
transition: scrollbar-color var(--duration-brief);
|
||||||
|
scrollbar-color: transparent transparent;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
scrollbar-color: var(--gray-7) var(--gray-3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus-within {
|
||||||
|
.bar-wrapper {
|
||||||
|
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;
|
||||||
|
pointer-events: none;
|
||||||
|
|
||||||
|
&[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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
import { useEffect, useRef, type ComponentProps } from "react";
|
||||||
|
import { clamp } from "three/src/math/MathUtils.js";
|
||||||
|
import { classNames } from "../../ui/classNames";
|
||||||
|
import styles from "./index.module.css";
|
||||||
|
|
||||||
|
interface ScrollerProps extends ComponentProps<"div"> {}
|
||||||
|
|
||||||
|
export function Scroller({ children, className, ...props }: ScrollerProps) {
|
||||||
|
const content = useRef<HTMLDivElement>(null!);
|
||||||
|
const curtainLeft = useRef<HTMLDivElement>(null!);
|
||||||
|
const curtainRight = useRef<HTMLDivElement>(null!);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
function syncCurtains() {
|
||||||
|
const maxScroll =
|
||||||
|
content.current.scrollWidth - content.current.clientWidth;
|
||||||
|
if (maxScroll <= 0) {
|
||||||
|
curtainLeft.current.style.opacity = "0";
|
||||||
|
curtainRight.current.style.opacity = "0";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fadeDistance = Math.min(60, content.current.clientWidth * 0.15);
|
||||||
|
const leftOpacity = clamp(
|
||||||
|
content.current.scrollLeft / fadeDistance,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
const rightOpacity = clamp(
|
||||||
|
(maxScroll - content.current.scrollLeft) / fadeDistance,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
|
||||||
|
curtainLeft.current.style.opacity = `${leftOpacity}`;
|
||||||
|
curtainRight.current.style.opacity = `${rightOpacity}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleScroll() {
|
||||||
|
syncCurtains();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleResize() {
|
||||||
|
syncCurtains();
|
||||||
|
}
|
||||||
|
|
||||||
|
syncCurtains();
|
||||||
|
|
||||||
|
window.addEventListener("resize", handleResize);
|
||||||
|
content.current.addEventListener("scroll", handleScroll);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener("resize", handleResize);
|
||||||
|
content.current.removeEventListener("scroll", handleScroll);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classNames(styles.scroller, className)} {...props}>
|
||||||
|
<div className={styles.content} ref={content}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div ref={curtainLeft} data-side="left" className={styles.curtain} />
|
||||||
|
<div ref={curtainRight} data-side="right" className={styles.curtain} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in new issue
Block a user