A dialog in a short window scrolls within it, the tabbed one stays put as its tabs change, and the report slip closes with Escape
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
8ba02006ca
commit
f131f292ae
@@ -48,6 +48,8 @@
|
||||
z-index: 20;
|
||||
background: rgba(0, 0, 0, 0.55);
|
||||
display: grid;
|
||||
/* One row the window's height, so the dialog's max-height: 100% is a real limit and it scrolls within. */
|
||||
grid-template-rows: minmax(0, 1fr);
|
||||
place-items: center;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
@@ -54,6 +54,8 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window onkeydown={(e) => open && e.key === 'Escape' && close()} />
|
||||
|
||||
{#if open}
|
||||
<div class="scrim" role="presentation" onclick={(ev) => ev.target === ev.currentTarget && close()}>
|
||||
<form class="slip" aria-labelledby="report-heading" onsubmit={send}>
|
||||
@@ -92,6 +94,8 @@
|
||||
z-index: 20;
|
||||
background: rgba(0, 0, 0, 0.55);
|
||||
display: grid;
|
||||
/* One row the window's height, so the dialog's max-height: 100% is a real limit and it scrolls within. */
|
||||
grid-template-rows: minmax(0, 1fr);
|
||||
place-items: center;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
@@ -20,8 +20,8 @@
|
||||
|
||||
let current = $state('');
|
||||
|
||||
// The router holds the address, and the dialog is open exactly while the address names it.
|
||||
// Every change goes through the router too, so it never loses track of where the page is.
|
||||
// The dialog is open exactly while the router's address names it. Opening, choosing a tab and
|
||||
// closing all navigate, so the address and the dialog cannot disagree.
|
||||
const [named, tab] = $derived.by(() => {
|
||||
const [name, t] = page.url.hash.slice(1).split('/');
|
||||
return [name === address, t] as const;
|
||||
@@ -34,10 +34,10 @@
|
||||
});
|
||||
// The address follows the chosen tab, so what is being read can be linked to.
|
||||
$effect(() => {
|
||||
if (named && current && current !== untrack(() => tab)) show(current);
|
||||
if (named && current) untrack(() => current !== tab && show(current));
|
||||
});
|
||||
|
||||
const bare = () => `${location.pathname}${location.search}`;
|
||||
const bare = () => `${page.url.pathname}${page.url.search}`;
|
||||
const quietly = { replaceState: true, noScroll: true, keepFocus: true };
|
||||
function show(id: string) {
|
||||
goto(`${bare()}#${address}/${id}`, quietly);
|
||||
@@ -54,7 +54,7 @@
|
||||
<svelte:window onkeydown={onKey} />
|
||||
|
||||
{#if named}
|
||||
<div class="scrim" role="presentation" onclick={(ev) => ev.target === ev.currentTarget && close()}>
|
||||
<div class="scrim anchored" role="presentation" onclick={(ev) => ev.target === ev.currentTarget && close()}>
|
||||
<div class="panel" role="dialog" aria-modal="true" aria-labelledby="{address}-title">
|
||||
<header>
|
||||
<h2 id="{address}-title">{title}</h2>
|
||||
@@ -76,10 +76,17 @@
|
||||
z-index: 20;
|
||||
background: rgba(0, 0, 0, 0.55);
|
||||
display: grid;
|
||||
/* One row the window's height, so the dialog's max-height: 100% is a real limit and it scrolls within. */
|
||||
grid-template-rows: minmax(0, 1fr);
|
||||
place-items: center;
|
||||
padding: 16px;
|
||||
}
|
||||
/* min-width 0 all the way down, so wide content (the notation's table) scrolls in place instead of widening the dialog past a phone's screen. */
|
||||
/* Held near the top, not centered: a tab of another length must not move the tabs under the pointer. */
|
||||
.anchored {
|
||||
place-items: start center;
|
||||
padding-top: max(16px, 6vh);
|
||||
}
|
||||
/* min-width 0 all the way down, so wide content scrolls in place instead of widening the dialog past a phone's screen. */
|
||||
.panel {
|
||||
width: min(100%, 44rem);
|
||||
min-width: 0;
|
||||
|
||||
Reference in New Issue
Block a user