mirror of
https://github.com/captbaritone/webamp.git
synced 2026-01-23 02:15:01 +00:00
Sketch of server rendered table
This commit is contained in:
parent
aa006df23e
commit
afc8b038c8
2 changed files with 178 additions and 0 deletions
161
packages/skin-database/app/(modern)/table/Table.tsx
Normal file
161
packages/skin-database/app/(modern)/table/Table.tsx
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
"use client";
|
||||
import {
|
||||
HEADING_HEIGHT,
|
||||
SCREENSHOT_WIDTH,
|
||||
SKIN_RATIO,
|
||||
} from "../../../legacy-client/src/constants.js";
|
||||
import {
|
||||
useScrollbarWidth,
|
||||
useWindowSize,
|
||||
} from "../../../legacy-client/src/hooks.js";
|
||||
import React, { useEffect, useMemo, useState } from "react";
|
||||
import { FixedSizeGrid as Grid } from "react-window";
|
||||
|
||||
function ClientOnly({ children }: { children: React.ReactNode }) {
|
||||
const [mounted, setMounted] = useState(false);
|
||||
useEffect(() => setMounted(true), []);
|
||||
return mounted ? <>{children}</> : null;
|
||||
}
|
||||
|
||||
export default function WrappedTable({ initialSkins, skinCount }) {
|
||||
return (
|
||||
<ClientOnly>
|
||||
<Table initialSkins={initialSkins} skinCount={skinCount} />
|
||||
</ClientOnly>
|
||||
);
|
||||
}
|
||||
|
||||
function Table({ initialSkins, skinCount }) {
|
||||
const [skins, setSkins] = useState(initialSkins);
|
||||
|
||||
const scale = 0.5; // This can be adjusted based on your needs
|
||||
function getSkinData(data: {
|
||||
columnIndex: number;
|
||||
rowIndex: number;
|
||||
columnCount: number;
|
||||
}) {
|
||||
const index = data.rowIndex * columnCount + data.columnIndex;
|
||||
const skin = skins[index];
|
||||
return { requestToken: skin?.md5, skin };
|
||||
}
|
||||
const scrollbarWidth = useScrollbarWidth();
|
||||
const { windowWidth: windowWidthWithScrollabar, windowHeight } =
|
||||
useWindowSize();
|
||||
|
||||
const { columnWidth, rowHeight, columnCount } = getTableDimensions(
|
||||
windowWidthWithScrollabar - scrollbarWidth,
|
||||
scale
|
||||
);
|
||||
function Cell(props) {
|
||||
const index = props.rowIndex * columnCount + props.columnIndex;
|
||||
const skin = skins[index];
|
||||
if (skin == null) {
|
||||
if (index < skinCount) {
|
||||
// Fetch more skins!
|
||||
}
|
||||
return <div style={props.style}></div>;
|
||||
}
|
||||
if (skin == null) {
|
||||
return <div style={props.style}>Loading...</div>;
|
||||
}
|
||||
const imageUrl = `https://r2.webampskins.org/screenshots/${skin.md5}.png`;
|
||||
return (
|
||||
<div style={props.style}>
|
||||
<img
|
||||
src={imageUrl}
|
||||
style={{ width: props.data.width, height: props.data.height }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
<SkinTableUnbound
|
||||
columnCount={columnCount}
|
||||
columnWidth={columnWidth}
|
||||
rowHeight={rowHeight}
|
||||
windowHeight={windowHeight}
|
||||
windowWidth={windowWidthWithScrollabar}
|
||||
skinCount={skinCount}
|
||||
getSkinData={getSkinData}
|
||||
Cell={Cell}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
const getTableDimensions = (windowWidth: number, scale: number) => {
|
||||
const columnCount = Math.round(windowWidth / (SCREENSHOT_WIDTH * scale));
|
||||
const columnWidth = windowWidth / columnCount; // TODO: Consider flooring this to get things aligned to the pixel
|
||||
const rowHeight = columnWidth * SKIN_RATIO;
|
||||
return { columnWidth, rowHeight, columnCount };
|
||||
};
|
||||
|
||||
function SkinTableUnbound({
|
||||
columnCount,
|
||||
columnWidth,
|
||||
rowHeight,
|
||||
windowHeight,
|
||||
skinCount,
|
||||
windowWidth,
|
||||
getSkinData,
|
||||
Cell,
|
||||
}) {
|
||||
function itemKey({ columnIndex, rowIndex }) {
|
||||
const { requestToken, data: skin } = getSkinData({
|
||||
columnIndex,
|
||||
rowIndex,
|
||||
columnCount,
|
||||
});
|
||||
if (skin == null && requestToken == null) {
|
||||
return `empty-cell-${columnIndex}-${rowIndex}`;
|
||||
}
|
||||
return skin ? skin.hash : `unfectched-index-${requestToken}`;
|
||||
}
|
||||
const gridRef = React.useRef();
|
||||
const itemRef = React.useRef();
|
||||
React.useLayoutEffect(() => {
|
||||
if (gridRef.current == null) {
|
||||
return;
|
||||
}
|
||||
gridRef.current.scrollTo({ scrollLeft: 0, scrollTop: 0 });
|
||||
}, [skinCount]);
|
||||
|
||||
React.useLayoutEffect(() => {
|
||||
if (gridRef.current == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const itemRow = Math.floor(itemRef.current / columnCount);
|
||||
|
||||
gridRef.current.scrollTo({ scrollLeft: 0, scrollTop: rowHeight * itemRow });
|
||||
}, [rowHeight, columnCount]);
|
||||
|
||||
const onScroll = useMemo(() => {
|
||||
const half = Math.round(columnCount / 2);
|
||||
return (scrollData) => {
|
||||
itemRef.current =
|
||||
Math.round(scrollData.scrollTop / rowHeight) * columnCount + half;
|
||||
};
|
||||
}, [columnCount, rowHeight]);
|
||||
|
||||
return (
|
||||
<div id="infinite-skins" style={{ marginTop: HEADING_HEIGHT }}>
|
||||
<Grid
|
||||
ref={gridRef}
|
||||
itemKey={itemKey}
|
||||
itemData={{ columnCount, width: columnWidth, height: rowHeight }}
|
||||
columnCount={columnCount}
|
||||
columnWidth={columnWidth}
|
||||
height={windowHeight - HEADING_HEIGHT}
|
||||
rowCount={Math.ceil(skinCount / columnCount)}
|
||||
rowHeight={rowHeight}
|
||||
width={windowWidth}
|
||||
overscanRowCount={5}
|
||||
onScroll={onScroll}
|
||||
style={{ overflowY: "scroll" }}
|
||||
>
|
||||
{Cell}
|
||||
</Grid>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
17
packages/skin-database/app/(modern)/table/page.tsx
Normal file
17
packages/skin-database/app/(modern)/table/page.tsx
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import * as Skins from "../../../data/skins";
|
||||
import Table from "./Table";
|
||||
|
||||
export default async function TablePage() {
|
||||
const skins = await Skins.getMuseumPage({
|
||||
offset: 0,
|
||||
first: 100,
|
||||
});
|
||||
|
||||
const skinCount = await Skins.getClassicSkinCount();
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
<Table initialSkins={skins} skinCount={skinCount} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue