Skip to content

Instantly share code, notes, and snippets.

@zaydek
Created July 27, 2022 01:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zaydek/3f8a3cc34261e067b012e0dd7cd13ed9 to your computer and use it in GitHub Desktop.
Save zaydek/3f8a3cc34261e067b012e0dd7cd13ed9 to your computer and use it in GitHub Desktop.
/* @refresh reload */
import "./css/reset.scss"
import "./css/duomo.scss"
import "./css/theme.scss"
import * as Solid from "solid-js"
import * as SolidWeb from "solid-js/web"
import * as helpers from "./helpers"
import * as utils from "./utils"
////////////////////////////////////////////////////////////////////////////////
function createLocalWindowHeight() {
// Cache value as a heuristic
const [windowHeight, setWindowHeight] = helpers.createLocalSignal(window.innerHeight, { storageKey: "window.innerHeight" })
function handleResize(e?: UIEvent) {
setWindowHeight(window.innerHeight)
}
handleResize()
window.addEventListener("resize", handleResize, false)
Solid.onCleanup(() => {
window.removeEventListener("resize", handleResize, false)
})
return windowHeight
}
function createLocalScrollY() {
// Cache value as a heuristic
const [scrollY, setScrollY] = helpers.createLocalSignal(window.scrollY, { storageKey: "window.scrollY" })
function handleScroll(e?: Event) {
setScrollY(window.scrollY)
}
handleScroll()
document.addEventListener("scroll", handleScroll, false)
Solid.onCleanup(() => {
document.removeEventListener("scroll", handleScroll, false)
})
return scrollY
}
function zeroOrMore(value: number) {
if (value <= 0) {
return 0
} else {
return value
}
}
const Debugger = (props: { children: string }) => {
return <>
<div class="fixed inset-bl no-pointer-events" style="bottom: 24px; left: 24px;">
<div class="p-16 w-448 rounded-16 pointer-events" style="background-color: hsl(0deg 0% 100%); box-shadow: var(--basic-box-shadow);">
<div
style={`
white-space: pre-wrap;
tab-size: 2;
font: 400 16px / normal Consolas;
`}
>
{props.children}
</div>
</div>
</div>
</>
}
const VScrollGrid = (props: {
__DEBUG?: boolean
each: any[]
gridItemMinWidth: number
gridItemHeight: number
scrollBuffer?:
| 0
| "1x"
| "2x"
| "3x"
| "4x"
children: (_: any, index: () => number) => Solid.JSXElement
}) => {
const [gridRef, setGridRef] = Solid.createSignal<HTMLElement>()
const windowHeight = createLocalWindowHeight()
const scrollY = createLocalScrollY()
const scrollBuffer = () => {
if (typeof props.scrollBuffer === "string") {
return windowHeight() * +props.scrollBuffer[0]
} else {
return 0
}
}
const [gridWidth, setGridWidth] = Solid.createSignal(0)
const columnCount = () => Math.floor(gridWidth() / props.gridItemMinWidth)
const [gridTop, setGridTop] = Solid.createSignal(0)
const staticGridHeight = () => Math.ceil((props.gridItemHeight * props.each.length) /
columnCount())
const rowStart = () => {
const scrollPos = scrollY() - scrollBuffer()
return zeroOrMore(
Math.trunc(
Math.min(
scrollPos - gridTop(),
staticGridHeight(),
) / props.gridItemHeight,
),
)
}
const rowEndVisible = () => {
const scrollPos = scrollY() + windowHeight() + scrollBuffer()
return zeroOrMore(
Math.ceil( // Use Math.ceil here (not Math.trunc)
Math.min(
scrollPos - gridTop(),
staticGridHeight(),
) / props.gridItemHeight,
),
)
}
const rowEnd = () => {
const scrollPos = scrollY() + windowHeight()
return zeroOrMore(
-1 * Math.trunc(
Math.max(
scrollPos - (gridTop() + staticGridHeight()),
-1 * staticGridHeight(),
) / props.gridItemHeight,
),
)
}
const gridItemStart = () => rowStart() * columnCount()
const gridItemEnd = () => rowEndVisible() * columnCount()
Solid.onMount(() => {
function handleResizeW(e?: UIEvent) {
Solid.batch(() => {
setGridWidth(gridRef()!.offsetWidth)
setGridTop(gridRef()!.offsetTop)
})
}
handleResizeW()
window.addEventListener("resize", handleResizeW, false)
Solid.onCleanup(() => {
window.removeEventListener("resize", handleResizeW, false)
})
})
return <>
<Solid.Show when={props.__DEBUG}>
<Debugger>
{JSON.stringify({
rowStart: rowStart(),
rowEnd: rowEnd(),
gridItemStart: gridItemStart(),
gridItemEnd: gridItemEnd(),
}, null, 2)}
</Debugger>
</Solid.Show>
<div
ref={setGridRef}
style={`
padding-top: ${rowStart() * props.gridItemHeight}px;
padding-bottom: ${rowEnd() * props.gridItemHeight}px;
display: grid;
grid-template-columns:
repeat(auto-fill, minmax(${props.gridItemMinWidth}px, 1fr));
`}
>
<Solid.For each={props.each.slice(gridItemStart(), gridItemEnd())}>
{props.children}
</Solid.For>
</div>
</>
}
const App = () => {
return <>
<>
<div class="py-224 row center">
<div class="w-full max-w-1024">
<VScrollGrid
each={utils.range(100_000)}
gridItemHeight={96}
gridItemMinWidth={96}
scrollBuffer="1x"
__DEBUG
>
{element => <>
<div
class="row center h-96"
style={utils.decomment(`
background-color: hsl(0deg 100% 50%);
outline: 1px solid hsl(0deg 0% 100%);
`)}
>
{element}
</div>
</>}
</VScrollGrid>
</div>
</div>
</>
</>
}
SolidWeb.render(() => <App />, document.getElementById("root")!)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment