Pond graph: size the canvas to the viewport, not the width ratio (#131)
The SVG scaled to container width with height following the fixed 800×560 viewBox ratio — on wide windows the graph grew taller than the viewport, the page got a scrollbar, and wheel-zoom scrolled along. The graph page is now a flex column filling the main column; the canvas takes the remaining height (flex: 1, min-height: 0), a ResizeObserver feeds its measured size to ForceGraph as width/height, and the SVG fills it exactly. LocalGraphPanel keeps its fixed defaults. Scope deliberately layout-only (issue comment 1187): with no scrollbar there is nothing for the wheel to scroll, so no non-passive listener needed. The graph pack now asserts the main column does not overflow vertically on the graph route. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Fb2VzvcoBPHkjh8bZ6PzQn
This commit is contained in:
parent
31557f09e5
commit
d23e5dc10c
@ -72,6 +72,15 @@ test('pond graph renders the link structure and creates phantom pages', async ({
|
||||
expect(await page.locator('.force-graph__edge').count()).toBeGreaterThanOrEqual(2);
|
||||
await expect(page.locator('.graph-legend')).toBeVisible();
|
||||
|
||||
// #131: the page fits the viewport — wheel-zoom must never scroll along,
|
||||
// so the main column may not grow a vertical scrollbar on this route.
|
||||
expect(
|
||||
await page.evaluate(() => {
|
||||
const main = document.querySelector('.main')!;
|
||||
return main.scrollHeight - main.clientHeight;
|
||||
}),
|
||||
).toBeLessThanOrEqual(0);
|
||||
|
||||
// #123: the physics/rendering sliders are there and take effect — the
|
||||
// font-size slider writes straight into the SVG labels.
|
||||
const controls = page.locator('.graph-controls');
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import type { PageView, PondGraphView, PondView } from '@dorfteich/shared';
|
||||
import { DEFAULT_LABEL_COLOR } from '@dorfteich/shared';
|
||||
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import { useMemo } from 'react';
|
||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
|
||||
@ -125,11 +125,37 @@ function GraphView({
|
||||
graphSettingsKey(pondId),
|
||||
GRAPH_SETTINGS_DEFAULTS,
|
||||
);
|
||||
// #131: the canvas flexes to the remaining viewport height; its measured
|
||||
// size becomes the SVG viewBox, so the graph fills it exactly instead of
|
||||
// deriving its height from the width (which used to overflow the page).
|
||||
const canvasRef = useRef<HTMLDivElement>(null);
|
||||
const [size, setSize] = useState<{ width: number; height: number } | null>(null);
|
||||
useEffect(() => {
|
||||
const canvas = canvasRef.current;
|
||||
if (!canvas) return;
|
||||
const observer = new ResizeObserver((entries) => {
|
||||
const rect = entries[0]?.contentRect;
|
||||
if (rect && rect.width > 0 && rect.height > 0) {
|
||||
setSize({ width: Math.round(rect.width), height: Math.round(rect.height) });
|
||||
}
|
||||
});
|
||||
observer.observe(canvas);
|
||||
return () => observer.disconnect();
|
||||
}, []);
|
||||
return (
|
||||
<>
|
||||
<GraphControls settings={settings} setSettings={setSettings} />
|
||||
<div className="graph-page__canvas">
|
||||
<ForceGraph nodes={nodes} edges={edges} settings={settings} onNodeClick={onNodeClick} />
|
||||
<div className="graph-page__canvas" ref={canvasRef}>
|
||||
{size !== null && (
|
||||
<ForceGraph
|
||||
nodes={nodes}
|
||||
edges={edges}
|
||||
width={size.width}
|
||||
height={size.height}
|
||||
settings={settings}
|
||||
onNodeClick={onNodeClick}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
@ -1776,6 +1776,16 @@ button {
|
||||
}
|
||||
|
||||
/* Knowledge graph (issue #112) + local graph panel (#113). */
|
||||
|
||||
/* #131: the page fills the main column as a flex column, so the canvas can
|
||||
take exactly the remaining viewport height — the page itself must never
|
||||
grow a scrollbar (wheel-zoom would scroll along with it). */
|
||||
.graph-page {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.graph-page__header {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
@ -1799,6 +1809,14 @@ button {
|
||||
border-radius: var(--radius);
|
||||
background: var(--color-surface);
|
||||
overflow: hidden;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
/* The pond graph SVG gets its measured container size as viewBox (#131), so
|
||||
it may fill the canvas exactly; the local panel keeps width-scaled sizing. */
|
||||
.graph-page__canvas .force-graph {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* Physics/rendering sliders (#123). */
|
||||
|
||||
Loading…
Reference in New Issue
Block a user