feat: start of scroller

This commit is contained in:
TrèsAbhi committed 2026-02-01 12:24:55 -06:00
1 parent 015d457d23
commit ddb869e767
4 files changed
+100 -5

No files matched your search

@@ -2,6 +2,7 @@
import { api } from "../core/api/dynamic";
import { tools } from "../core/tools/tools";
import { Heading } from "./Heading";
import { Scroller } from "./Scroller";
import { Section } from "./Section";
import { TankCard } from "./TankCard";
import ToolCard from "./ToolCard.astro";
@@ -35,8 +36,6 @@ const popular = [
}
.tanks {
/* overflow-x: scroll; */
overflow-x: hidden;
z-index: 1;
.content {
@@ -50,11 +49,11 @@ const popular = [
<Section>
<Heading size="2" className="title">{strings.home.tanks}</Heading>
<div class="tanks">
<Scroller client:load className="tanks">
<div class="content">
{popular.map((tank) => <TankCard {tank} />)}
</div>
</div>
</Scroller>
<ToolCard gradient {locale} tool={tools.tanks} />
@@ -1 +0,0 @@
export function Scroller() {}
@@ -0,0 +1,42 @@
.scroller {
display: flex;
flex-direction: column;
.content {
overflow: auto;
scrollbar-width: none;
&::-webkit-scrollbar {
display: none;
width: 0;
height: 0;
}
}
.bar-wrapper {
padding: var(--space-1);
background-color: blue;
.bar-container {
background-color: red;
height: 10px;
position: relative;
.bar {
height: 100%;
min-width: min(100%, var(--space-6));
position: absolute;
background-color: green;
cursor: grab;
&:active {
cursor: grabbing;
}
}
}
}
}
@@ -0,0 +1,55 @@
import { useEffect, useRef, type ComponentProps } from "react";
import { classNames } from "../../core/ui/classNames";
import styles from "./index.module.css";
interface ScrollerProps extends ComponentProps<"div"> {}
export function Scroller({ children, className, ...props }: ScrollerProps) {
const bar = useRef<HTMLDivElement>(null!);
const container = useRef<HTMLDivElement>(null!);
const content = useRef<HTMLDivElement>(null!);
useEffect(() => {
function updateSize() {
bar.current.style.width = `${(content.current.clientWidth / content.current.scrollWidth) * 100}%`;
}
let lastX = 0;
function handlePointerDown(event: PointerEvent) {
lastX = event.clientX;
window.addEventListener("pointermove", handlePointerMove);
window.addEventListener("pointerup", handlePointerUp);
}
function handlePointerMove(event: PointerEvent) {
const dx = event.clientX - lastX;
lastX = event.clientX;
}
function handlePointerUp() {
window.removeEventListener("pointermove", handlePointerMove);
window.removeEventListener("pointerup", handlePointerUp);
}
updateSize();
bar.current.addEventListener("pointerdown", handlePointerDown);
return () => {
bar.current.removeEventListener("pointerdown", handlePointerDown);
};
}, []);
return (
<div className={classNames(styles.scroller, className)} {...props}>
<div className={styles.content} ref={content}>
{children}
</div>
<div className={styles["bar-wrapper"]}>
<div className={styles["bar-container"]} ref={container}>
<div className={styles.bar} ref={bar} />
</div>
</div>
</div>
);
}