perf: the zone's own box carries the margin-band hit area
A second element per zone doubled the header/footer layer — 150 absolutely placed boxes in the 75-page window, laid out again on every pass. The zone's ::after grows out to the page edges instead, which keeps the hover hint and the double-click on the element that already handles them. Claude-Session: https://claude.ai/code/session_01Gpw1ctsR8FrdNMrpqePtik
This commit is contained in:
1 parent
6d53e4f5c4
commit
01501398ad
1 file changed
+18
-21
@@ -155,18 +155,14 @@
|
||||
if (zone === 'footer') return { ...b, top: b.top + b.height - height, height };
|
||||
return { ...b, height };
|
||||
}
|
||||
// Double-click target: the whole margin band the zone sits in, edge to edge, not just
|
||||
// the zone box — the band above the body is the header's, as it is in LibreOffice.
|
||||
function hitBox(zone: HfZone, page: number) {
|
||||
// Double-click target: the zone's ::after grows the box out to the page edges, so the
|
||||
// whole margin band opens the zone (as in LibreOffice) without a box of its own — a
|
||||
// second element per zone is a hundred more to lay out on every pass.
|
||||
function hitVars(zone: HfZone, page: number, z: { top: number; left: number; width: number; height: number }) {
|
||||
const box = boxOf(page);
|
||||
const z = zoneBox(zone, page);
|
||||
const m = marginsOf(page);
|
||||
if (zone === 'header') {
|
||||
const bottom = Math.max(box.top + cmToPx(m.top), z.top + z.height);
|
||||
return { top: box.top, left: box.left, width: box.width, height: bottom - box.top };
|
||||
}
|
||||
const top = Math.min(box.top + box.height - cmToPx(m.bottom), z.top);
|
||||
return { top, left: box.left, width: box.width, height: box.top + box.height - top };
|
||||
const edge = zone === 'header' ? z.top - box.top : box.top + box.height - z.top - z.height;
|
||||
return ` --hit-${zone === 'header' ? 'top' : 'bottom'}: ${-edge}px;` +
|
||||
` --hit-left: ${box.left - z.left}px; --hit-right: ${box.left + box.width - z.left - z.width}px;`;
|
||||
}
|
||||
const boxStyle = (b: { top: number; left: number; width: number; height: number }) =>
|
||||
`top: ${b.top}px; left: ${b.left}px; width: ${b.width}px; height: ${b.height}px;`;
|
||||
@@ -488,18 +484,16 @@
|
||||
<div class="hf-layer">
|
||||
{#each pages as p}
|
||||
{#each ['header', 'footer'] as const as zone}
|
||||
{#if interactive}
|
||||
<div class="hf-hit" style={boxStyle(hitBox(zone, p))} ondblclick={() => startEdit(zone, p)} role="button" tabindex="-1"></div>
|
||||
{/if}
|
||||
{#if !(interactive && hfActive === zone && editingPage === p)}
|
||||
{@const html = zoneHtml(zone, p)}
|
||||
{@const total = html.includes('data-page-field="count"') ? numPages : 0}
|
||||
{@const chapters = html.includes('data-page-field="chapter"') ? chapterStarts : null}
|
||||
{@const zb = zoneBox(zone, p)}
|
||||
<div
|
||||
class="hf-zone hf-{zone}"
|
||||
class:hf-empty={!html}
|
||||
data-hf-label={zone === 'header' ? t().hf.addHeaderHint : t().hf.addFooterHint}
|
||||
style={boxStyle(zoneBox(zone, p))}
|
||||
style={boxStyle(zb) + (interactive ? hitVars(zone, p, zb) : '')}
|
||||
ondblclick={() => interactive && startEdit(zone, p)}
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
@@ -602,11 +596,15 @@
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
/* The margin band around a zone, double-clickable like the zone itself. Painted
|
||||
before it, so the zone keeps the events inside its own box. */
|
||||
.hf-hit {
|
||||
/* The margin band around the zone, double-clickable like the zone itself. Not on the
|
||||
edited zone, where it would swallow the clicks into the live editor. */
|
||||
.hf-zone:not(.hf-active)::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
pointer-events: auto;
|
||||
top: var(--hit-top, 0);
|
||||
bottom: var(--hit-bottom, 0);
|
||||
left: var(--hit-left, 0);
|
||||
right: var(--hit-right, 0);
|
||||
}
|
||||
|
||||
/* Empty zone: invisible until hovered, then show a faint double-click hint. */
|
||||
@@ -621,8 +619,7 @@
|
||||
.hf-footer.hf-empty::before {
|
||||
margin-top: auto;
|
||||
}
|
||||
.hf-empty:hover::before,
|
||||
.hf-hit:hover + .hf-zone.hf-empty::before {
|
||||
.hf-empty:hover::before {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user