fix: fixed centering for jumptonode from warningSidebar

ref: N25B-450
This commit is contained in:
JGerla
2026-01-22 16:14:46 +01:00
parent e5b438c17e
commit 84d9cbb19d
6 changed files with 584 additions and 20 deletions

View File

@@ -135,6 +135,7 @@ const VisProgUI = () => {
onNodeDragStop={endBatchAction}
preventScrolling={scrollable}
onMove={(_, viewport) => setZoom(viewport.zoom)}
reconnectRadius={15}
snapToGrid
fitView
proOptions={{hideAttribution: true}}

View File

@@ -40,6 +40,7 @@
.warning-group-header {
background: ButtonFace;
padding: 4px;
}
.warnings-list {
@@ -53,9 +54,10 @@
.warning-item {
display: flex;
flex-direction: column;
margin: 5px;
gap: 8px;
padding: 8px 12px;
gap: 2px;
padding: 0;
border-radius: 5px;
cursor: pointer;
}
@@ -66,17 +68,61 @@
.warning-item--error {
border: 2px solid red;
background-color: hsl(from red h s 96%);
.item-header{
background-color: red;
.type{
color: hsl(from red h s 96%);
}
}
}
.warning-item--error:hover {
background-color: hsl(from red h s 75%);
}
.warning-item--warning {
border: 3px solid orange;
border: 2px solid orange;
background-color: hsl(from orange h s 96%);
.item-header{
background-color: orange;
.type{
color: hsl(from orange h s 96%);
}
}
}
.warning-item--warning:hover {
background-color: hsl(from orange h s 75%);
}
.warning-item--info {
border: 3px solid steelblue;
border: 2px solid steelblue;
background-color: hsl(from steelblue h s 96%);
.item-header{
background-color: steelblue;
.type{
color: hsl(from steelblue h s 96%);
}
}
}
.warning-item .meta {
font-size: 11px;
opacity: 0.6;
.warning-item--info:hover {
background-color: hsl(from steelblue h s 75%);
}
.warning-item .item-header {
padding: 8px 8px;
opacity: 1;
font-weight: bolder;
}
.warning-item .item-header .type{
padding: 2px 8px;
font-size: 0.9rem;
}
.warning-item .description {
padding: 5px 10px;
font-size: 0.8rem;
}

View File

@@ -20,7 +20,7 @@ export function WarningsSidebar() {
: warnings.filter(w => w.severity === severityFilter);
return (
<aside className={styles.warningsSidebar}>
<aside id="warningSidebar" className={styles.warningsSidebar}>
<WarningsHeader
severityFilter={severityFilter}
onChange={setSeverityFilter}
@@ -117,38 +117,47 @@ function WarningListItem(props: { warning: EditorWarning, key: string}) {
className={clsx(styles.warningItem, styles[`warning-item--${props.warning.severity.toLowerCase()}`],)}
onClick={() => jumpToNode(props.warning.scope.id)}
>
<div className={styles.description}>
{props.warning.description}
<div className={styles.itemHeader}>
<span className={styles.type}>{props.warning.type}</span>
</div>
<div className={styles.meta}>
{props.warning.type}
<div className={styles.description}>
{props.warning.description}
</div>
</div>
);
}
function useJumpToNode() {
const { getNode, setCenter } = useReactFlow();
const { getNode, setCenter, getViewport } = useReactFlow();
const { addSelectedNodes } = useStoreApi().getState();
return (nodeId: string) => {
// select the node
// user can't jump to global warning, so prevent further logic from running
if (nodeId === globalWarning) return;
const node = getNode(nodeId);
if (!node) return;
const { position, width = 0, height = 0} = node;
const nodeElement = document.querySelector(`.react-flow__node[data-id="${nodeId}"]`) as HTMLElement;
const { position } = node;
const viewport = getViewport();
const { width, height } = nodeElement.getBoundingClientRect();
// move to node
//move to node
setCenter(
position!.x + width / 2,
position!.y + height / 2,
{ zoom: 2, duration: 300 }
position!.x + ((width / viewport.zoom) / 2),
position!.y + ((height / viewport.zoom) / 2),
{duration: 300, interpolate: "smooth" }
).then(() => {
// select the node
addSelectedNodes([nodeId]);
});
};
}

View File

@@ -3,7 +3,7 @@ import {
Position,
type Node, useNodeConnections
} from '@xyflow/react';
import {useEffect} from "react";
import {useEffect, useRef} from "react";
import {type EditorWarning} from "../components/EditorWarnings.tsx";
import { Toolbar } from '../components/NodeComponents';
import styles from '../../VisProg.module.css';
@@ -131,7 +131,15 @@ export default function PhaseNode(props: NodeProps<PhaseNode>) {
unregisterWarning(props.id, `${noOutgoingPhaseWarning.type}:${noOutgoingPhaseWarning.scope.handleId}`);
unregisterWarning(props.id, `${noIncomingPhaseWarning.type}:${noIncomingPhaseWarning.scope.handleId}`);
}, [phaseInCons.length, phaseOutCons.length, props.id, registerWarning, unregisterWarning]);
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
if (ref.current) {
const { width, height } = ref.current.getBoundingClientRect();
console.log('Node width:', width, 'height:', height);
}
}, []);
return (
<>
<Toolbar nodeId={props.id} allowDelete={true}/>