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;
|
z-index: 20;
|
||||||
background: rgba(0, 0, 0, 0.55);
|
background: rgba(0, 0, 0, 0.55);
|
||||||
display: grid;
|
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;
|
place-items: center;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,6 +54,8 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<svelte:window onkeydown={(e) => open && e.key === 'Escape' && close()} />
|
||||||
|
|
||||||
{#if open}
|
{#if open}
|
||||||
<div class="scrim" role="presentation" onclick={(ev) => ev.target === ev.currentTarget && close()}>
|
<div class="scrim" role="presentation" onclick={(ev) => ev.target === ev.currentTarget && close()}>
|
||||||
<form class="slip" aria-labelledby="report-heading" onsubmit={send}>
|
<form class="slip" aria-labelledby="report-heading" onsubmit={send}>
|
||||||
@@ -92,6 +94,8 @@
|
|||||||
z-index: 20;
|
z-index: 20;
|
||||||
background: rgba(0, 0, 0, 0.55);
|
background: rgba(0, 0, 0, 0.55);
|
||||||
display: grid;
|
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;
|
place-items: center;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,8 +20,8 @@
|
|||||||
|
|
||||||
let current = $state('');
|
let current = $state('');
|
||||||
|
|
||||||
// The router holds the address, and the dialog is open exactly while the address names it.
|
// The dialog is open exactly while the router's address names it. Opening, choosing a tab and
|
||||||
// Every change goes through the router too, so it never loses track of where the page is.
|
// closing all navigate, so the address and the dialog cannot disagree.
|
||||||
const [named, tab] = $derived.by(() => {
|
const [named, tab] = $derived.by(() => {
|
||||||
const [name, t] = page.url.hash.slice(1).split('/');
|
const [name, t] = page.url.hash.slice(1).split('/');
|
||||||
return [name === address, t] as const;
|
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.
|
// The address follows the chosen tab, so what is being read can be linked to.
|
||||||
$effect(() => {
|
$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 };
|
const quietly = { replaceState: true, noScroll: true, keepFocus: true };
|
||||||
function show(id: string) {
|
function show(id: string) {
|
||||||
goto(`${bare()}#${address}/${id}`, quietly);
|
goto(`${bare()}#${address}/${id}`, quietly);
|
||||||
@@ -54,7 +54,7 @@
|
|||||||
<svelte:window onkeydown={onKey} />
|
<svelte:window onkeydown={onKey} />
|
||||||
|
|
||||||
{#if named}
|
{#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">
|
<div class="panel" role="dialog" aria-modal="true" aria-labelledby="{address}-title">
|
||||||
<header>
|
<header>
|
||||||
<h2 id="{address}-title">{title}</h2>
|
<h2 id="{address}-title">{title}</h2>
|
||||||
@@ -76,10 +76,17 @@
|
|||||||
z-index: 20;
|
z-index: 20;
|
||||||
background: rgba(0, 0, 0, 0.55);
|
background: rgba(0, 0, 0, 0.55);
|
||||||
display: grid;
|
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;
|
place-items: center;
|
||||||
padding: 16px;
|
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 {
|
.panel {
|
||||||
width: min(100%, 44rem);
|
width: min(100%, 44rem);
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user