mirror of
https://github.com/Patiun/com.gregkilmer.www.git
synced 2026-11-08 04:08:24 +00:00
Merge pull request #11 from Patiun/claude/patiun-rebrand-gjoo9f
Link roundup posts from Shelf cards and keep long link lists short
This commit is contained in:
@@ -6,8 +6,12 @@ import SidePanel from "./SidePanel.astro";
|
|||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
items: Connected[];
|
items: Connected[];
|
||||||
|
/** A roundup links many pages; past this many the panel counts the rest. */
|
||||||
|
max?: number;
|
||||||
}
|
}
|
||||||
const { items } = Astro.props;
|
const { items: all, max = 8 } = Astro.props;
|
||||||
|
const items = all.length > max + 1 ? all.slice(0, max) : all;
|
||||||
|
const more = all.length - items.length;
|
||||||
---
|
---
|
||||||
{
|
{
|
||||||
items.length > 0 && (
|
items.length > 0 && (
|
||||||
@@ -26,11 +30,17 @@ const { items } = Astro.props;
|
|||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
|
{more > 0 && <p class="more">And {more} more, each linked in the post.</p>}
|
||||||
</SidePanel>
|
</SidePanel>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
.more {
|
||||||
|
margin: 10px 0 0;
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--gk-muted);
|
||||||
|
}
|
||||||
.cp {
|
.cp {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
* blog's seal art stands in.
|
* blog's seal art stands in.
|
||||||
*/
|
*/
|
||||||
import Button from "@gk/ui/components/Button.astro";
|
import Button from "@gk/ui/components/Button.astro";
|
||||||
import Chip from "@gk/ui/components/Chip.astro";
|
|
||||||
import GiltCorners from "@gk/ui/components/GiltCorners.astro";
|
import GiltCorners from "@gk/ui/components/GiltCorners.astro";
|
||||||
import Seal from "@gk/ui/components/Seal.astro";
|
import Seal from "@gk/ui/components/Seal.astro";
|
||||||
import Star from "@gk/ui/components/Star.astro";
|
import Star from "@gk/ui/components/Star.astro";
|
||||||
@@ -16,6 +15,7 @@ import TickScale from "@gk/ui/components/TickScale.astro";
|
|||||||
import { filamentBanner } from "@gk/ui/components/seals.ts";
|
import { filamentBanner } from "@gk/ui/components/seals.ts";
|
||||||
import { dateLine, nowStatuses, pageLinks, type Post } from "../../lib/blog.ts";
|
import { dateLine, nowStatuses, pageLinks, type Post } from "../../lib/blog.ts";
|
||||||
import { mediaSrc, type SiteIndex } from "../../lib/site.ts";
|
import { mediaSrc, type SiteIndex } from "../../lib/site.ts";
|
||||||
|
import PageChips from "./PageChips.astro";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
post: Post;
|
post: Post;
|
||||||
@@ -62,7 +62,7 @@ const now = nowStatuses(s, post);
|
|||||||
<h2 id="lead-title" class="title"><a href={href}>{post.title}</a></h2>
|
<h2 id="lead-title" class="title"><a href={href}>{post.title}</a></h2>
|
||||||
<span class="underline" aria-hidden="true"></span>
|
<span class="underline" aria-hidden="true"></span>
|
||||||
{post.data.summary && <p class="summary">{post.data.summary}</p>}
|
{post.data.summary && <p class="summary">{post.data.summary}</p>}
|
||||||
{links.length > 0 && <span class="chips">{links.map((l) => <Chip href={l.href}>{l.label}</Chip>)}</span>}
|
{links.length > 0 && <span class="chips"><PageChips links={links} max={4} /></span>}
|
||||||
<Button href={href} arrow class="go">{post.data.postType === "build log" ? "Read the build log" : "Read the post"}</Button>
|
<Button href={href} arrow class="go">{post.data.postType === "build log" ? "Read the build log" : "Read the post"}</Button>
|
||||||
</div>
|
</div>
|
||||||
<GiltCorners />
|
<GiltCorners />
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
---
|
||||||
|
/**
|
||||||
|
* A post's page chips. A roundup can link a hundred pages, so past the first
|
||||||
|
* few the rest are counted; the post itself links every one.
|
||||||
|
*/
|
||||||
|
import Chip from "@gk/ui/components/Chip.astro";
|
||||||
|
import type { PageLink } from "../../lib/blog.ts";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
links: PageLink[];
|
||||||
|
max?: number;
|
||||||
|
}
|
||||||
|
const { links, max = 5 } = Astro.props;
|
||||||
|
const shown = links.length > max + 1 ? links.slice(0, max) : links;
|
||||||
|
const more = links.length - shown.length;
|
||||||
|
---
|
||||||
|
{shown.map((l) => <Chip href={l.href}>{l.label}</Chip>)}
|
||||||
|
{more > 0 && <span class="more">and {more} more</span>}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.more {
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--gk-muted);
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -16,6 +16,7 @@ import { connectedPages, neighbours, nowStatuses, pageLinks, readMinutes, relate
|
|||||||
import { formatDate, mediaSrc, type SiteIndex } from "../../lib/site.ts";
|
import { formatDate, mediaSrc, type SiteIndex } from "../../lib/site.ts";
|
||||||
import ConnectedPages from "./ConnectedPages.astro";
|
import ConnectedPages from "./ConnectedPages.astro";
|
||||||
import CrumbHero from "./CrumbHero.astro";
|
import CrumbHero from "./CrumbHero.astro";
|
||||||
|
import PageChips from "./PageChips.astro";
|
||||||
import PostList from "./PostList.astro";
|
import PostList from "./PostList.astro";
|
||||||
import SidePanel from "./SidePanel.astro";
|
import SidePanel from "./SidePanel.astro";
|
||||||
import StarHeading from "./StarHeading.astro";
|
import StarHeading from "./StarHeading.astro";
|
||||||
@@ -67,7 +68,7 @@ const making = d.topic === "Making";
|
|||||||
<span>{readMinutes(body)} min read</span>
|
<span>{readMinutes(body)} min read</span>
|
||||||
{(links.length > 0 || now.length > 0) && <span aria-hidden="true">·</span>}
|
{(links.length > 0 || now.length > 0) && <span aria-hidden="true">·</span>}
|
||||||
{now.map((st) => <StatusPill>Now · {st.data.kind}</StatusPill>)}
|
{now.map((st) => <StatusPill>Now · {st.data.kind}</StatusPill>)}
|
||||||
{links.map((l) => <Chip href={l.href}>{l.label}</Chip>)}
|
<PageChips links={links} max={8} />
|
||||||
</div>
|
</div>
|
||||||
</CrumbHero>
|
</CrumbHero>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
|
|||||||
@@ -3,11 +3,11 @@
|
|||||||
* One post as a fixed-height row: picture, topic and date, title, a
|
* One post as a fixed-height row: picture, topic and date, title, a
|
||||||
* two-line summary, Now chips for linked notes and page-link chips.
|
* two-line summary, Now chips for linked notes and page-link chips.
|
||||||
*/
|
*/
|
||||||
import Chip from "@gk/ui/components/Chip.astro";
|
|
||||||
import Star from "@gk/ui/components/Star.astro";
|
import Star from "@gk/ui/components/Star.astro";
|
||||||
import StatusPill from "@gk/ui/components/StatusPill.astro";
|
import StatusPill from "@gk/ui/components/StatusPill.astro";
|
||||||
import { dateLine, nowStatuses, pageLinks, type Post } from "../../lib/blog.ts";
|
import { dateLine, nowStatuses, pageLinks, type Post } from "../../lib/blog.ts";
|
||||||
import { mediaSrc, type SiteIndex } from "../../lib/site.ts";
|
import { mediaSrc, type SiteIndex } from "../../lib/site.ts";
|
||||||
|
import PageChips from "./PageChips.astro";
|
||||||
import PostThumb from "./PostThumb.astro";
|
import PostThumb from "./PostThumb.astro";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -37,7 +37,7 @@ const now = nowStatuses(s, post);
|
|||||||
<H class="title"><a href={href}>{post.title}</a></H>
|
<H class="title"><a href={href}>{post.title}</a></H>
|
||||||
{post.data.summary && <p class="summary">{post.data.summary}</p>}
|
{post.data.summary && <p class="summary">{post.data.summary}</p>}
|
||||||
<span class="chips">
|
<span class="chips">
|
||||||
{links.length ? links.map((l) => <Chip href={l.href}>{l.label}</Chip>) : <span class="none">Not tied to a page</span>}
|
{links.length ? <PageChips links={links} max={3} /> : <span class="none">Not tied to a page</span>}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@@ -25,7 +25,9 @@ const status = site.statusFor(game);
|
|||||||
// Players and play time sync from BoardGameGeek; nothing shows until they have.
|
// Players and play time sync from BoardGameGeek; nothing shows until they have.
|
||||||
const facts = [gamePlayers(game), gamePlayTime(game)].filter(Boolean);
|
const facts = [gamePlayers(game), gamePlayTime(game)].filter(Boolean);
|
||||||
const seats = playersOf(game);
|
const seats = playersOf(game);
|
||||||
const hasPost = site.backlinks(game, { type: "post" }).length > 0;
|
const post = site.backlinks(game, { type: "post" })[0];
|
||||||
|
const postHref = post && site.href(post);
|
||||||
|
const hasPost = !!post;
|
||||||
---
|
---
|
||||||
<li
|
<li
|
||||||
id={game.slug}
|
id={game.slug}
|
||||||
@@ -61,9 +63,19 @@ const hasPost = site.backlinks(game, { type: "post" }).length > 0;
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
<span class="links">
|
||||||
|
{
|
||||||
|
post && postHref && (
|
||||||
|
<a href={postHref} class="blog">
|
||||||
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 5h16v14H4zM8 9h8M8 13h5" /></svg>
|
||||||
|
Blog: {post.title}
|
||||||
|
</a>
|
||||||
|
)
|
||||||
|
}
|
||||||
<a class="gk-text gk-quiet bgg" href={`https://boardgamegeek.com/boardgame/${d.bggId}`} rel="external noopener">
|
<a class="gk-text gk-quiet bgg" href={`https://boardgamegeek.com/boardgame/${d.bggId}`} rel="external noopener">
|
||||||
BoardGameGeek<span class="gk-visually-hidden">: {game.title}</span>
|
BoardGameGeek<span class="gk-visually-hidden">: {game.title}</span>
|
||||||
</a>
|
</a>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
</li>
|
</li>
|
||||||
@@ -134,10 +146,24 @@ const hasPost = site.backlinks(game, { type: "post" }).length > 0;
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: var(--gk-muted);
|
color: var(--gk-muted);
|
||||||
}
|
}
|
||||||
.bgg {
|
.links {
|
||||||
align-self: flex-start;
|
|
||||||
margin-top: auto;
|
margin-top: auto;
|
||||||
padding-top: 4px;
|
padding-top: 4px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
.blog {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--gk-teal-bright);
|
||||||
|
}
|
||||||
|
.bgg {
|
||||||
|
align-self: flex-start;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -52,6 +52,12 @@ export function buildLogs(s: SiteIndex, craft: Note<"craft">): Note<"post">[] {
|
|||||||
return s.backlinks(craft, { type: "post" }) as Note<"post">[];
|
return s.backlinks(craft, { type: "post" }) as Note<"post">[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** The post a make links to: its own build log first, else the newest post about it. */
|
||||||
|
export function makeLog(s: SiteIndex, make: Note<"make">): Note<"post"> | undefined {
|
||||||
|
const posts = s.backlinks(make, { type: "post" }) as Note<"post">[];
|
||||||
|
return posts.find((p) => p.data.postType === "build log") ?? posts[0];
|
||||||
|
}
|
||||||
|
|
||||||
/** The tools of a craft, as notes where published, in the craft's order. */
|
/** The tools of a craft, as notes where published, in the craft's order. */
|
||||||
export function toolsOf(s: SiteIndex, craft: Note<"craft">): { title: string; note?: Note<"tool"> }[] {
|
export function toolsOf(s: SiteIndex, craft: Note<"craft">): { title: string; note?: Note<"tool"> }[] {
|
||||||
return (craft.data.tools ?? []).map((t) => {
|
return (craft.data.tools ?? []).map((t) => {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import WorldSection from "../../components/world/WorldSection.astro";
|
|||||||
import type { Note } from "../../lib/content.ts";
|
import type { Note } from "../../lib/content.ts";
|
||||||
import { plural, site } from "../../lib/page.ts";
|
import { plural, site } from "../../lib/page.ts";
|
||||||
import { formatDate, mediaSrc } from "../../lib/site.ts";
|
import { formatDate, mediaSrc } from "../../lib/site.ts";
|
||||||
import { buildLogs, byDateDesc, byOrder, craftPill, craftSeal, makePhotos, makesOf, sentence, toolsOf } from "../../lib/world.ts";
|
import { buildLogs, byDateDesc, byOrder, craftPill, craftSeal, makeLog, makePhotos, makesOf, sentence, toolsOf } from "../../lib/world.ts";
|
||||||
|
|
||||||
const s = await site();
|
const s = await site();
|
||||||
const slug = Astro.params.slug ?? "";
|
const slug = Astro.params.slug ?? "";
|
||||||
@@ -115,7 +115,7 @@ const postCover = (p: Note<"post">) => mediaSrc((p.data as { cover?: string }).c
|
|||||||
<ul class="makes">
|
<ul class="makes">
|
||||||
{makes.map((m) => {
|
{makes.map((m) => {
|
||||||
const photos = makePhotos(m);
|
const photos = makePhotos(m);
|
||||||
const log = s.backlinks(m, { type: "post" })[0];
|
const log = makeLog(s, m);
|
||||||
return (
|
return (
|
||||||
<li id={m.slug} class="make" data-k={kindsOf(m).map((k) => k.toLowerCase()).join("|") || "-"}>
|
<li id={m.slug} class="make" data-k={kindsOf(m).map((k) => k.toLowerCase()).join("|") || "-"}>
|
||||||
<StarChartArt seal={artSeal} ratio="auto" src={photos[0]} alt={m.data.blurb ?? m.title} class="m-art" />
|
<StarChartArt seal={artSeal} ratio="auto" src={photos[0]} alt={m.data.blurb ?? m.title} class="m-art" />
|
||||||
@@ -132,7 +132,7 @@ const postCover = (p: Note<"post">) => mediaSrc((p.data as { cover?: string }).c
|
|||||||
{(m.data.date || log) && (
|
{(m.data.date || log) && (
|
||||||
<span class="m-meta">
|
<span class="m-meta">
|
||||||
{m.data.date && <time datetime={m.data.date}>{formatDate(m.data.date)}</time>}
|
{m.data.date && <time datetime={m.data.date}>{formatDate(m.data.date)}</time>}
|
||||||
{log && <Chip href={s.href(log) ?? undefined}>Build log</Chip>}
|
{log && <Chip href={s.href(log) ?? undefined}>{log.data.postType === "build log" ? "Build log" : "Blog"}</Chip>}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import NowStrip, { type NowItem } from "../../components/world/NowStrip.astro";
|
|||||||
import WorldSection from "../../components/world/WorldSection.astro";
|
import WorldSection from "../../components/world/WorldSection.astro";
|
||||||
import type { Note } from "../../lib/content.ts";
|
import type { Note } from "../../lib/content.ts";
|
||||||
import { plural, site } from "../../lib/page.ts";
|
import { plural, site } from "../../lib/page.ts";
|
||||||
import { buildLogs, byDateDesc, byOrder, craftPill, craftSeal, isSword, makePhotos, makesOf, statusesFor, toolsAt, toolsOf } from "../../lib/world.ts";
|
import { buildLogs, byDateDesc, byOrder, craftPill, craftSeal, isSword, makeLog, makePhotos, makesOf, statusesFor, toolsAt, toolsOf } from "../../lib/world.ts";
|
||||||
|
|
||||||
const s = await site();
|
const s = await site();
|
||||||
const text = s.pageText("/workshop");
|
const text = s.pageText("/workshop");
|
||||||
@@ -153,7 +153,7 @@ const nowItems: NowItem[] = statusesFor(s, "/workshop").map((st) => {
|
|||||||
{recent.map((m) => {
|
{recent.map((m) => {
|
||||||
const found = s.byName(m.data.craft);
|
const found = s.byName(m.data.craft);
|
||||||
const craft = found?.type === "craft" ? (found as Note<"craft">) : undefined;
|
const craft = found?.type === "craft" ? (found as Note<"craft">) : undefined;
|
||||||
const log = s.backlinks(m, { type: "post" })[0];
|
const log = makeLog(s, m);
|
||||||
return (
|
return (
|
||||||
<li class="r-card">
|
<li class="r-card">
|
||||||
<a href={s.href(m) ?? undefined} class="r-art">
|
<a href={s.href(m) ?? undefined} class="r-art">
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import Button from "@gk/ui/components/Button.astro";
|
|||||||
import type { Note } from "../../lib/content.ts";
|
import type { Note } from "../../lib/content.ts";
|
||||||
import { plural, site } from "../../lib/page.ts";
|
import { plural, site } from "../../lib/page.ts";
|
||||||
import { formatDate } from "../../lib/site.ts";
|
import { formatDate } from "../../lib/site.ts";
|
||||||
import { byDateDesc, byOrder, craftSeal, makePhotos } from "../../lib/world.ts";
|
import { byDateDesc, byOrder, craftSeal, makeLog, makePhotos } from "../../lib/world.ts";
|
||||||
|
|
||||||
const s = await site();
|
const s = await site();
|
||||||
const text = s.pageText("/workshop/makes");
|
const text = s.pageText("/workshop/makes");
|
||||||
@@ -46,7 +46,7 @@ const pills =
|
|||||||
{makes.map((m) => {
|
{makes.map((m) => {
|
||||||
const craft = craftOf(m);
|
const craft = craftOf(m);
|
||||||
const photos = makePhotos(m);
|
const photos = makePhotos(m);
|
||||||
const log = s.backlinks(m, { type: "post" })[0];
|
const log = makeLog(s, m);
|
||||||
return (
|
return (
|
||||||
<li id={m.slug} class="make" data-c={craft?.slug ?? "-"}>
|
<li id={m.slug} class="make" data-c={craft?.slug ?? "-"}>
|
||||||
<StarChartArt seal={(craft && craftSeal(craft)) ?? pageSeals.workshop} ratio="auto" src={photos[0]} alt={m.data.blurb ?? m.title} class="m-art" />
|
<StarChartArt seal={(craft && craftSeal(craft)) ?? pageSeals.workshop} ratio="auto" src={photos[0]} alt={m.data.blurb ?? m.title} class="m-art" />
|
||||||
@@ -61,7 +61,7 @@ const pills =
|
|||||||
{(m.data.date || log) && (
|
{(m.data.date || log) && (
|
||||||
<span class="m-meta">
|
<span class="m-meta">
|
||||||
{m.data.date && <time datetime={m.data.date}>{formatDate(m.data.date)}</time>}
|
{m.data.date && <time datetime={m.data.date}>{formatDate(m.data.date)}</time>}
|
||||||
{log && <Chip href={s.href(log) ?? undefined}>Build log</Chip>}
|
{log && <Chip href={s.href(log) ?? undefined}>{log.data.postType === "build log" ? "Build log" : "Blog"}</Chip>}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
Reference in New Issue
Block a user