fix: preserve lidar review scroll
This commit is contained in:
parent
796b9306f9
commit
4c83e8a4e7
|
|
@ -2825,6 +2825,7 @@
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 0.72rem;
|
gap: 0.72rem;
|
||||||
margin-top: 1.2rem;
|
margin-top: 1.2rem;
|
||||||
|
overflow-anchor: none;
|
||||||
padding-top: 1rem;
|
padding-top: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,11 @@
|
||||||
import { useEffect, useMemo, useState } from "react";
|
import {
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useLayoutEffect,
|
||||||
|
useMemo,
|
||||||
|
useRef,
|
||||||
|
useState,
|
||||||
|
} from "react";
|
||||||
import { StatusBadge } from "@nodedc/ui-react";
|
import { StatusBadge } from "@nodedc/ui-react";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
|
@ -35,6 +42,8 @@ export function LidarLocalSurfacePanel({
|
||||||
selectedWindowKey: string | null;
|
selectedWindowKey: string | null;
|
||||||
reloadGeneration: number;
|
reloadGeneration: number;
|
||||||
}) {
|
}) {
|
||||||
|
const panelRef = useRef<HTMLElement | null>(null);
|
||||||
|
const pendingScrollTopRef = useRef<number | null>(null);
|
||||||
const [model, setModel] = useState<LidarLocalSurfaceModel | null>(null);
|
const [model, setModel] = useState<LidarLocalSurfaceModel | null>(null);
|
||||||
const [frame, setFrame] = useState<LidarLocalSurfaceFrame | null>(null);
|
const [frame, setFrame] = useState<LidarLocalSurfaceFrame | null>(null);
|
||||||
const [timeline, setTimeline] = useState<Timeline | null>(null);
|
const [timeline, setTimeline] = useState<Timeline | null>(null);
|
||||||
|
|
@ -47,6 +56,27 @@ export function LidarLocalSurfacePanel({
|
||||||
>("local-surface");
|
>("local-surface");
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
const captureFrameSelectionScroll = useCallback(() => {
|
||||||
|
const scrollContainer = panelRef.current?.closest<HTMLElement>(
|
||||||
|
".nodedc-application-panel__body",
|
||||||
|
);
|
||||||
|
pendingScrollTopRef.current = scrollContainer?.scrollTop ?? null;
|
||||||
|
}, []);
|
||||||
|
const selectFrame = useCallback((frameIndex: number) => {
|
||||||
|
if (pendingScrollTopRef.current === null) {
|
||||||
|
captureFrameSelectionScroll();
|
||||||
|
}
|
||||||
|
setSelectedFrameIndex(frameIndex);
|
||||||
|
}, [captureFrameSelectionScroll]);
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
const scrollTop = pendingScrollTopRef.current;
|
||||||
|
if (scrollTop === null) return;
|
||||||
|
const scrollContainer = panelRef.current?.closest<HTMLElement>(
|
||||||
|
".nodedc-application-panel__body",
|
||||||
|
);
|
||||||
|
scrollContainer?.scrollTo({ top: scrollTop, behavior: "auto" });
|
||||||
|
pendingScrollTopRef.current = null;
|
||||||
|
}, [selectedFrameIndex]);
|
||||||
|
|
||||||
const anchor = useMemo(() => {
|
const anchor = useMemo(() => {
|
||||||
if (!model) return null;
|
if (!model) return null;
|
||||||
|
|
@ -205,6 +235,7 @@ export function LidarLocalSurfacePanel({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section
|
<section
|
||||||
|
ref={panelRef}
|
||||||
className="lidar-local-surface"
|
className="lidar-local-surface"
|
||||||
aria-label="Динамическая локальная поверхность LiDAR"
|
aria-label="Динамическая локальная поверхность LiDAR"
|
||||||
>
|
>
|
||||||
|
|
@ -289,12 +320,14 @@ export function LidarLocalSurfacePanel({
|
||||||
timeline={timeline}
|
timeline={timeline}
|
||||||
review={review}
|
review={review}
|
||||||
selectedFrameIndex={selectedFrameIndex}
|
selectedFrameIndex={selectedFrameIndex}
|
||||||
onSelectFrame={setSelectedFrameIndex}
|
onSelectionStart={captureFrameSelectionScroll}
|
||||||
|
onSelectFrame={selectFrame}
|
||||||
/>
|
/>
|
||||||
<LidarLocalSurfaceReviewQueue
|
<LidarLocalSurfaceReviewQueue
|
||||||
review={review}
|
review={review}
|
||||||
selectedFrameIndex={selectedFrameIndex}
|
selectedFrameIndex={selectedFrameIndex}
|
||||||
onSelectFrame={setSelectedFrameIndex}
|
onSelectionStart={captureFrameSelectionScroll}
|
||||||
|
onSelectFrame={selectFrame}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
|
||||||
|
|
@ -42,11 +42,13 @@ export function LidarLocalSurfaceTimeline({
|
||||||
timeline,
|
timeline,
|
||||||
review,
|
review,
|
||||||
selectedFrameIndex,
|
selectedFrameIndex,
|
||||||
|
onSelectionStart,
|
||||||
onSelectFrame,
|
onSelectFrame,
|
||||||
}: {
|
}: {
|
||||||
timeline: Timeline;
|
timeline: Timeline;
|
||||||
review: LidarLocalSurfaceReview;
|
review: LidarLocalSurfaceReview;
|
||||||
selectedFrameIndex: number;
|
selectedFrameIndex: number;
|
||||||
|
onSelectionStart: () => void;
|
||||||
onSelectFrame: (frameIndex: number) => void;
|
onSelectFrame: (frameIndex: number) => void;
|
||||||
}) {
|
}) {
|
||||||
const plot = useMemo(() => {
|
const plot = useMemo(() => {
|
||||||
|
|
@ -131,6 +133,10 @@ export function LidarLocalSurfaceTimeline({
|
||||||
role="img"
|
role="img"
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
aria-label="Ошибка предсказания локальной поверхности по всей записи"
|
aria-label="Ошибка предсказания локальной поверхности по всей записи"
|
||||||
|
onMouseDown={(event) => {
|
||||||
|
onSelectionStart();
|
||||||
|
event.preventDefault();
|
||||||
|
}}
|
||||||
onClick={(event) => selectAtPointer(event.clientX, event.currentTarget)}
|
onClick={(event) => selectAtPointer(event.clientX, event.currentTarget)}
|
||||||
onKeyDown={(event) => {
|
onKeyDown={(event) => {
|
||||||
if (event.key === "ArrowLeft") {
|
if (event.key === "ArrowLeft") {
|
||||||
|
|
@ -221,10 +227,12 @@ function hasReasonKind(
|
||||||
export function LidarLocalSurfaceReviewQueue({
|
export function LidarLocalSurfaceReviewQueue({
|
||||||
review,
|
review,
|
||||||
selectedFrameIndex,
|
selectedFrameIndex,
|
||||||
|
onSelectionStart,
|
||||||
onSelectFrame,
|
onSelectFrame,
|
||||||
}: {
|
}: {
|
||||||
review: LidarLocalSurfaceReview;
|
review: LidarLocalSurfaceReview;
|
||||||
selectedFrameIndex: number;
|
selectedFrameIndex: number;
|
||||||
|
onSelectionStart: () => void;
|
||||||
onSelectFrame: (frameIndex: number) => void;
|
onSelectFrame: (frameIndex: number) => void;
|
||||||
}) {
|
}) {
|
||||||
const [filter, setFilter] = useState<ReviewFilter>("high");
|
const [filter, setFilter] = useState<ReviewFilter>("high");
|
||||||
|
|
@ -281,6 +289,7 @@ export function LidarLocalSurfaceReviewQueue({
|
||||||
type="button"
|
type="button"
|
||||||
key={item.key}
|
key={item.key}
|
||||||
data-active={filter === item.key ? "true" : undefined}
|
data-active={filter === item.key ? "true" : undefined}
|
||||||
|
onMouseDown={(event) => event.preventDefault()}
|
||||||
onClick={() => setFilter(item.key)}
|
onClick={() => setFilter(item.key)}
|
||||||
>
|
>
|
||||||
{item.label} <span>{item.count}</span>
|
{item.label} <span>{item.count}</span>
|
||||||
|
|
@ -297,6 +306,10 @@ export function LidarLocalSurfaceReviewQueue({
|
||||||
item.frameIndex === selectedFrameIndex ? "true" : undefined
|
item.frameIndex === selectedFrameIndex ? "true" : undefined
|
||||||
}
|
}
|
||||||
data-attention={item.attention}
|
data-attention={item.attention}
|
||||||
|
onMouseDown={(event) => {
|
||||||
|
onSelectionStart();
|
||||||
|
event.preventDefault();
|
||||||
|
}}
|
||||||
onClick={() => onSelectFrame(item.frameIndex)}
|
onClick={() => onSelectFrame(item.frameIndex)}
|
||||||
>
|
>
|
||||||
<span>
|
<span>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue