mirror of
https://github.com/Patiun/com.gregkilmer.www.git
synced 2026-11-08 04:08:24 +00:00
Match the site to the approved designs
Denser star field, the page nebula, a favicon package, the filament banner without its box, the About portrait, Kashi and tea photos and Scorpius, the Store's own header, a header logo glow, a resume PDF that fills its page, one divider on the Library and Shelf, star-chart art for coverless favorites, Shelf Players, Play time and blog filters with a Show all fold, make kind pills on craft pages, and full-width closing bands. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012aSbo2D1qP8cMFQCykf7kx
This commit is contained in:
@@ -128,6 +128,7 @@ export const sorts = [
|
||||
{ key: "played", label: "Last played" },
|
||||
{ key: "az", label: "A to Z" },
|
||||
{ key: "added", label: "Recently added" },
|
||||
{ key: "time", label: "Play time" },
|
||||
] as const;
|
||||
export type SortKey = (typeof sorts)[number]["key"];
|
||||
|
||||
@@ -138,6 +139,8 @@ export function sortGames(games: Game[], sort: SortKey): Game[] {
|
||||
played: (a, b) => desc(a.data.lastPlayed, b.data.lastPlayed) || az(a, b),
|
||||
az,
|
||||
added: (a, b) => desc(a.data.created, b.data.created) || az(a, b),
|
||||
// Shortest first; games BoardGameGeek has not timed go last.
|
||||
time: (a, b) => (minutesOf(a) ?? Infinity) - (minutesOf(b) ?? Infinity) || az(a, b),
|
||||
};
|
||||
return [...games].sort(cmp[sort]);
|
||||
}
|
||||
@@ -148,9 +151,29 @@ export interface ShelfFilter {
|
||||
owned: boolean;
|
||||
want: boolean;
|
||||
favorites: boolean;
|
||||
blog: boolean;
|
||||
players: PlayersKey | "";
|
||||
time: TimeKey | "";
|
||||
sort: SortKey;
|
||||
}
|
||||
|
||||
/** The Players pills: who a game has to seat. */
|
||||
export const playerOptions = [
|
||||
{ key: "1", label: "1", lo: 1, hi: 1 },
|
||||
{ key: "2", label: "2", lo: 2, hi: 2 },
|
||||
{ key: "3-4", label: "3–4", lo: 3, hi: 4 },
|
||||
{ key: "5+", label: "5+", lo: 5, hi: Infinity },
|
||||
] as const;
|
||||
export type PlayersKey = (typeof playerOptions)[number]["key"];
|
||||
|
||||
/** The Play time pills, in minutes. */
|
||||
export const timeOptions = [
|
||||
{ key: "short", label: "Under 30 min", lo: 0, hi: 29 },
|
||||
{ key: "mid", label: "30–60", lo: 30, hi: 60 },
|
||||
{ key: "long", label: "60+", lo: 61, hi: Infinity },
|
||||
] as const;
|
||||
export type TimeKey = (typeof timeOptions)[number]["key"];
|
||||
|
||||
export function shelfFilter(params: URLSearchParams): ShelfFilter {
|
||||
const submitted = params.has("f");
|
||||
return {
|
||||
@@ -159,19 +182,27 @@ export function shelfFilter(params: URLSearchParams): ShelfFilter {
|
||||
owned: submitted ? params.get("owned") === "1" : true,
|
||||
want: params.get("want") === "1",
|
||||
favorites: params.get("fav") === "1",
|
||||
blog: params.get("blog") === "1",
|
||||
players: playerOptions.find((o) => o.key === params.get("players"))?.key ?? "",
|
||||
time: timeOptions.find((o) => o.key === params.get("time"))?.key ?? "",
|
||||
sort: sorts.find((s) => s.key === params.get("sort"))?.key ?? "played",
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Owned and Want to play add games to the list (either one); with neither
|
||||
* ticked, every game shows. Favorites only and How it plays narrow it.
|
||||
* ticked, every game shows. Favorites only, Has a blog post, How it plays,
|
||||
* Players and Play time narrow it. A game BoardGameGeek has not synced yet
|
||||
* drops out while Players or Play time is set.
|
||||
*/
|
||||
export function gameShows(g: Game, f: ShelfFilter, text: string): boolean {
|
||||
export function gameShows(g: Game, f: ShelfFilter, text: string, hasPost = false): boolean {
|
||||
const owned = !!g.data.owned;
|
||||
const want = !!g.data.wantToPlay;
|
||||
if ((f.owned || f.want) && !((f.owned && owned) || (f.want && want))) return false;
|
||||
if (f.favorites && !g.data.favorite) return false;
|
||||
if (f.blog && !hasPost) return false;
|
||||
if (f.players && !seats(g, playerOptions.find((o) => o.key === f.players)!)) return false;
|
||||
if (f.time && !lasts(g, timeOptions.find((o) => o.key === f.time)!)) return false;
|
||||
if (f.plays.size && !(g.data.howItPlays ?? []).some((p) => f.plays.has(p.toLowerCase()))) return false;
|
||||
return !f.q || text.includes(f.q);
|
||||
}
|
||||
@@ -185,6 +216,30 @@ interface GameSynced {
|
||||
maxPlayTime?: number;
|
||||
}
|
||||
|
||||
/** The player range BoardGameGeek gives, when it has synced. */
|
||||
export function playersOf(g: Game): [number, number] | undefined {
|
||||
const d = g.data as GameSynced;
|
||||
const lo = d.minPlayers || d.maxPlayers;
|
||||
const hi = d.maxPlayers || d.minPlayers;
|
||||
return lo && hi ? [lo, hi] : undefined;
|
||||
}
|
||||
|
||||
/** A game's usual length in minutes: the listed play time, else the longest. */
|
||||
export function minutesOf(g: Game): number | undefined {
|
||||
const d = g.data as GameSynced;
|
||||
return d.playTime || d.maxPlayTime || d.minPlayTime || undefined;
|
||||
}
|
||||
|
||||
/** Whether a game plays at some count in the option's range. */
|
||||
const seats = (g: Game, o: { lo: number; hi: number }) => {
|
||||
const r = playersOf(g);
|
||||
return !!r && r[0] <= o.hi && r[1] >= o.lo;
|
||||
};
|
||||
const lasts = (g: Game, o: { lo: number; hi: number }) => {
|
||||
const m = minutesOf(g);
|
||||
return m !== undefined && m >= o.lo && m <= o.hi;
|
||||
};
|
||||
|
||||
const range = (lo?: number, hi?: number) => (lo && hi && lo !== hi ? `${lo}–${hi}` : String(lo || hi || ""));
|
||||
|
||||
/** "2–4 players", "1 player" or undefined when BoardGameGeek has not been synced. */
|
||||
|
||||
Reference in New Issue
Block a user