Add elevation chart day dividers and wire days through SessionView

- ElevationChart: Dashed vertical lines at day boundaries with distance labels
- SessionView: Lift useDays() to top level, pass to both ElevationChart and
  SidebarTabs to avoid duplicate hook calls

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ullrich Schäfer 2026-04-10 23:54:56 +02:00
parent 94016fd4c4
commit 78ee4ef3dc
No known key found for this signature in database
GPG key ID: A32FF691A0F752D9
3 changed files with 38 additions and 8 deletions

View file

@ -1,4 +1,6 @@
import { useEffect, useState, useRef, useCallback } from "react";
import { useTranslation } from "react-i18next";
import type { DayStage } from "@trails-cool/gpx";
import type { YjsState } from "~/lib/use-yjs";
import { elevationColor, type ColorMode } from "~/components/ColoredRoute";
@ -55,9 +57,11 @@ const PADDING = { top: 10, right: 10, bottom: 25, left: 40 };
interface ElevationChartProps {
yjs: YjsState;
onHover?: (position: [number, number] | null) => void;
days?: DayStage[];
}
export function ElevationChart({ yjs, onHover }: ElevationChartProps) {
export function ElevationChart({ yjs, onHover, days }: ElevationChartProps) {
const { t } = useTranslation();
const [points, setPoints] = useState<ElevationPoint[]>([]);
const [hoverIdx, setHoverIdx] = useState<number | null>(null);
const [colorMode, setColorMode] = useState<ColorMode>("plain");
@ -170,6 +174,32 @@ export function ElevationChart({ yjs, onHover }: ElevationChartProps) {
ctx.fillText("0 km", PADDING.left, h - 4);
ctx.fillText(`${(maxDist / 1000).toFixed(1)} km`, PADDING.left + chartW, h - 4);
// Day dividers
if (days && days.length > 1) {
for (let d = 0; d < days.length - 1; d++) {
const day = days[d]!;
// Find the point closest to the day boundary distance
const boundaryDist = days.slice(0, d + 1).reduce((sum, s) => sum + s.distance, 0);
const bx = toX(boundaryDist);
// Dashed vertical line
ctx.beginPath();
ctx.setLineDash([4, 3]);
ctx.moveTo(bx, PADDING.top);
ctx.lineTo(bx, PADDING.top + chartH);
ctx.strokeStyle = "#9A9484";
ctx.lineWidth = 1;
ctx.stroke();
ctx.setLineDash([]);
// Day label at top
ctx.fillStyle = "#9A9484";
ctx.font = "9px sans-serif";
ctx.textAlign = "center";
ctx.fillText(`${(boundaryDist / 1000).toFixed(0)} km`, bx, h - 4);
}
}
// Hover crosshair + info
if (highlightIdx !== null && highlightIdx >= 0 && highlightIdx < points.length) {
const p = points[highlightIdx]!;
@ -203,7 +233,7 @@ export function ElevationChart({ yjs, onHover }: ElevationChartProps) {
ctx.fillText(label, labelX, PADDING.top + 10);
}
},
[points, colorMode],
[points, colorMode, days, t],
);
useEffect(() => {

View file

@ -142,10 +142,9 @@ function ColorModeToggle({ yjs }: { yjs: YjsState }) {
);
}
function SidebarTabs({ yjs, routeStats }: { yjs: YjsState; routeStats: ReturnType<typeof useRouting>["routeStats"] }) {
function SidebarTabs({ yjs, routeStats, days }: { yjs: YjsState; routeStats: ReturnType<typeof useRouting>["routeStats"]; days: ReturnType<typeof useDays> }) {
const { t } = useTranslation("planner");
const [tab, setTab] = useState<"waypoints" | "notes">("waypoints");
const days = useDays(yjs);
return (
<aside className="hidden w-72 border-l border-gray-200 bg-white md:flex md:flex-col">
@ -192,6 +191,7 @@ export function SessionView({ sessionId, callbackUrl, callbackToken, returnUrl,
const { computing, routeError, routeStats, requestRoute } = useRouting(yjs);
const { canUndo, canRedo, undo, redo } = useUndo(yjs?.undoManager ?? null);
useUndoShortcuts(yjs?.undoManager ?? null);
const days = useDays(yjs);
const [highlightPosition, setHighlightPosition] = useState<[number, number] | null>(null);
const { toasts, addToast } = useToasts();
useAwarenessToasts(yjs, t, addToast);
@ -289,10 +289,10 @@ export function SessionView({ sessionId, callbackUrl, callbackToken, returnUrl,
</Suspense>
</div>
<Suspense fallback={null}>
<ElevationChart yjs={yjs} onHover={handleElevationHover} />
<ElevationChart yjs={yjs} onHover={handleElevationHover} days={days} />
</Suspense>
</main>
<SidebarTabs yjs={yjs} routeStats={routeStats} />
<SidebarTabs yjs={yjs} routeStats={routeStats} days={days} />
</div>
<YjsDebugPanel yjs={yjs} />
{toasts.length > 0 && (