1
0
Fork 0

feat(map): tile-mode basemap mosaic previews on map picker cards

This commit is contained in:
Jason Fraley 2026-09-22 21:09:02 -04:00
parent 8c95c1ee82
commit d51c8445bb

View file

@ -23,6 +23,21 @@ function basemapUrl(map: MapDoc): string | null {
return null; return null;
} }
/**
* Static preview for tile-mode maps: a 2x2 mosaic of the map's own zoom-1
* tiles covering the whole world square, center-cropped by the card frame.
*/
function tilePreviewUrls(template: string): string[] | null {
if (!template.includes("{z}") || !template.includes("{x}") || !template.includes("{y}")) {
return null;
}
return [0, 1].flatMap((y) =>
[0, 1].map((x) =>
template.replace("{z}", "1").replace("{x}", String(x)).replace("{y}", String(y)),
),
);
}
export default async function MapPickerPage() { export default async function MapPickerPage() {
const payload = await getPayload({ config: await config }); const payload = await getPayload({ config: await config });
@ -70,6 +85,10 @@ export default async function MapPickerPage() {
? (map.factionControl as Faction) ? (map.factionControl as Faction)
: null; : null;
const url = basemapUrl(map); const url = basemapUrl(map);
const tileUrls =
map.basemapMode === "tiles" && map.tileUrlTemplate
? tilePreviewUrls(map.tileUrlTemplate)
: null;
const stats = [ const stats = [
map.weather map.weather
? { ? {
@ -101,6 +120,18 @@ export default async function MapPickerPage() {
alt={map.name} alt={map.name}
className="size-full object-cover opacity-80 transition-opacity group-hover:opacity-100" className="size-full object-cover opacity-80 transition-opacity group-hover:opacity-100"
/> />
) : tileUrls ? (
<div
aria-hidden="true"
className="absolute inset-0 flex items-center justify-center overflow-hidden"
>
<div className="grid size-[512px] max-w-none grid-cols-2 opacity-80 transition-opacity group-hover:opacity-100">
{tileUrls.map((tileUrl) => (
// eslint-disable-next-line @next/next/no-img-element
<img key={tileUrl} src={tileUrl} alt="" className="size-[256px]" />
))}
</div>
</div>
) : ( ) : (
<div className="flex size-full items-center justify-center text-muted-foreground"> <div className="flex size-full items-center justify-center text-muted-foreground">
<MapIcon className="size-8 opacity-40" /> <MapIcon className="size-8 opacity-40" />