// createScroll is a HEADLESS scroll primitive: it owns only the geometry - the
// clamped offset, re-clamped in onLayout against the measured content-vs-viewport
// overflow - and nothing with a UI opinion. Input handling, momentum, and
// scrollbars are policy you supply. (@solidrt/components ScrollView is one such
// skin built on top of this.)
//
// The shape it expects:
// - a VIEWPORT node: the clipping box, overflow="hidden".
// - a CONTENT node inside it: an inner wrapper that takes the children's natural
// size (flexShrink={0} so it can exceed the viewport).
// Capture both with refs, pass their accessors to createScroll, then apply the
// returned offset to the viewport's scrollX/scrollY. createScroll does no input,
// so wire an event (here onWheel) to scroll.scrollBy - positive dy moves content
// up. scrollTo(x, y) jumps to an absolute, clamped offset.
import { render, For, createScroll } from "@solidrt/core"
import type { WheelEvent } from "@solidrt/core"
function App() {
let viewport: { id: number } | undefined
let content: { id: number } | undefined
// Default axis is "vertical"; pass { axis: "horizontal" } or "both" for others.
let scroll = createScroll(() => viewport, () => content)
let onWheel = (e: WheelEvent) => scroll.scrollBy(e.deltaX, e.deltaY)
let rows = Array.from({ length: 30 }, (_, i) => i)
return (
(viewport = n)}
width={220}
height={280}
overflow="hidden"
clipRadius={12}
scrollY={scroll.offset().y}
onWheel={onWheel}
>
(content = n)} flexShrink={0} padding={12} gap={8}>
{(i) => (
Row {i}
)}
)
}
render(() => )