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:
parent
94016fd4c4
commit
78ee4ef3dc
3 changed files with 38 additions and 8 deletions
|
|
@ -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(() => {
|
||||
|
|
|
|||
|
|
@ -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 && (
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@
|
|||
|
||||
## 4. Elevation Chart
|
||||
|
||||
- [ ] 4.1 Add day divider rendering to `ElevationChart`: dashed vertical lines at overnight waypoint distances with "Day N" labels at top
|
||||
- [ ] 4.2 Show per-day distance ranges on x-axis labels (e.g. "120 km" at each day boundary)
|
||||
- [x] 4.1 Add day divider rendering to `ElevationChart`: dashed vertical lines at overnight waypoint distances with "Day N" labels at top
|
||||
- [x] 4.2 Show per-day distance ranges on x-axis labels (e.g. "120 km" at each day boundary)
|
||||
|
||||
## 5. GPX Roundtrip
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue