The template's about panel is a TabbedDialog, opened by its address
About.svelte goes; #about opens the panel through the router, so it closes and reopens cleanly and a tab can be linked to. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
c3fca6c5c7
commit
2810076677
@@ -1,91 +0,0 @@
|
||||
<script lang="ts" module>
|
||||
import type { Tab } from './Tabs.svelte';
|
||||
|
||||
/** One section of the about panel: a tab and what it shows. */
|
||||
export type AboutTab = Tab;
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
// The about panel: the game's story, its keeping and its company, a tab
|
||||
// at a time rather than one long page. Opened from the hall's "about this
|
||||
// game" link (and #about); closes with its button, a click outside it, or
|
||||
// Escape. The tab last read is the one it reopens on.
|
||||
import Tabs from './Tabs.svelte';
|
||||
|
||||
let { open = $bindable(false), tabs, title }: { open?: boolean; tabs: AboutTab[]; title: string } = $props();
|
||||
|
||||
let current = $state('');
|
||||
|
||||
function onKey(e: KeyboardEvent) {
|
||||
if (open && e.key === 'Escape') open = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window onkeydown={onKey} />
|
||||
|
||||
{#if open}
|
||||
<div class="about-scrim" role="presentation" onclick={(ev) => ev.target === ev.currentTarget && (open = false)}>
|
||||
<div class="panel" role="dialog" aria-modal="true" aria-labelledby="about-title">
|
||||
<header>
|
||||
<h2 id="about-title">{title}</h2>
|
||||
<button type="button" class="close" aria-label="Close" onclick={() => (open = false)}>×</button>
|
||||
</header>
|
||||
<div class="body">
|
||||
<Tabs {tabs} label="About this game" bind:current />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
/* Its own backdrop: not every game has another dialog to share one with. */
|
||||
.about-scrim {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 20;
|
||||
background: rgba(0, 0, 0, 0.55);
|
||||
display: grid;
|
||||
place-items: center;
|
||||
padding: 16px;
|
||||
}
|
||||
.panel {
|
||||
width: min(100%, 44rem);
|
||||
max-height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: var(--slate);
|
||||
border: 1px solid var(--rule-strong);
|
||||
border-radius: 6px;
|
||||
padding: 1rem 1.4rem 1.2rem;
|
||||
}
|
||||
header {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.3rem;
|
||||
margin: 0 0 0.6rem;
|
||||
}
|
||||
.close {
|
||||
background: none;
|
||||
border: none;
|
||||
color: inherit;
|
||||
font-size: 1.5rem;
|
||||
line-height: 1;
|
||||
cursor: pointer;
|
||||
padding: 0.2rem 0.4rem;
|
||||
}
|
||||
.body {
|
||||
overflow: auto;
|
||||
min-height: 12rem;
|
||||
}
|
||||
/* The tab strip stays in view while a long tab scrolls beneath it. */
|
||||
.body :global([role='tablist']) {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
background: var(--slate);
|
||||
}
|
||||
</style>
|
||||
@@ -4,7 +4,7 @@
|
||||
// about panel with the rules, the guide and a way to reach the keeper.
|
||||
import { goto } from '$app/navigation';
|
||||
import { loadFamily, type FamilyGame } from '$lib/family';
|
||||
import About from './About.svelte';
|
||||
import TabbedDialog from './TabbedDialog.svelte';
|
||||
import TallyBoard from './Tally.svelte';
|
||||
import Preferences from './Preferences.svelte';
|
||||
import { prefs, setPref } from '$lib/prefs.svelte';
|
||||
@@ -27,9 +27,6 @@
|
||||
let seats = $state(allSeats());
|
||||
let games = $state<Record<string, RoomView<State> | null>>({});
|
||||
let aboutOpen = $state(false);
|
||||
$effect(() => {
|
||||
if (location.hash === '#about') aboutOpen = true;
|
||||
});
|
||||
/** The ledgers' tally, fetched when the about panel opens. */
|
||||
let tally = $state<Tally | null>(null);
|
||||
$effect(() => {
|
||||
@@ -195,7 +192,7 @@
|
||||
<h1>__NAME__</h1>
|
||||
<p class="pitch">One line that says what the game is.</p>
|
||||
<p class="tag">Two or three sentences for someone who has never heard of it: what you do on a turn, what makes it fun, and that it is free to play here against the bot or with friends.</p>
|
||||
<p class="links"><a href="/guide">how to play</a> · <a href="/rules">the rules</a> · <a class="about-link" href="#about" onclick={() => (aboutOpen = true)}>about this game — a labor of love</a> · <button type="button" class="link" onclick={() => (prefsOpen = true)}>preferences</button></p>
|
||||
<p class="links"><a href="/guide">how to play</a> · <a href="/rules">the rules</a> · <a class="about-link" href="#about">about this game — a labor of love</a> · <button type="button" class="link" onclick={() => (prefsOpen = true)}>preferences</button></p>
|
||||
<Preferences bind:open={prefsOpen} />
|
||||
</div>
|
||||
|
||||
@@ -325,7 +322,18 @@
|
||||
<p>A rule read wrong, a bug, a game you would like to tell of: write to <a href="mailto:eric@ericwagoner.com">eric@ericwagoner.com</a>, <a href="https://bsky.app/profile/kestrelsnest.social" target="_blank" rel="noreferrer">@kestrelsnest.social</a> on Bluesky, or <a href="https://toots.kestrelsnest.social/@eric" target="_blank" rel="noreferrer">@eric@toots.kestrelsnest.social</a> on Mastodon. The keeper of this hall roosts at <a href="https://kestrelsnest.social" target="_blank" rel="noreferrer">kestrelsnest.social</a>.</p>
|
||||
</div>
|
||||
{/snippet}
|
||||
<About bind:open={aboutOpen} title="About __NAME__" tabs={[{ id: 'game', label: 'The game', body: aboutGame }, { id: 'tally', label: 'The tally', body: aboutTally }, { id: 'family', label: 'More games', body: aboutFamily }, { id: 'word', label: 'Send word', body: aboutWord }]} />
|
||||
<TabbedDialog
|
||||
address="about"
|
||||
bind:open={aboutOpen}
|
||||
title="About __NAME__"
|
||||
label="About this game"
|
||||
tabs={[
|
||||
{ id: 'game', label: 'The game', body: aboutGame },
|
||||
{ id: 'tally', label: 'The tally', body: aboutTally },
|
||||
{ id: 'family', label: 'More games', body: aboutFamily },
|
||||
{ id: 'word', label: 'Send word', body: aboutWord }
|
||||
]}
|
||||
/>
|
||||
</section>
|
||||
|
||||
<style>
|
||||
@@ -570,7 +578,7 @@
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
/* about: the panel's own words; About.svelte is the dialog around them */
|
||||
/* about: the panel's own words; TabbedDialog.svelte is the dialog around them */
|
||||
.about-body {
|
||||
max-width: 44em;
|
||||
color: var(--bone-dim);
|
||||
@@ -581,17 +589,6 @@
|
||||
margin-top: 0.6rem;
|
||||
}
|
||||
|
||||
.about-body a {
|
||||
color: var(--frost);
|
||||
}
|
||||
|
||||
.about-body h3 {
|
||||
font-size: 1rem;
|
||||
font-style: italic;
|
||||
margin: 0.9rem 0 0.2rem;
|
||||
color: var(--bone);
|
||||
}
|
||||
|
||||
.about-body .made {
|
||||
font-size: 0.85rem;
|
||||
color: var(--bone-faint);
|
||||
|
||||
Reference in New Issue
Block a user