feat(recipe-studio): auto-fit editor viewport on tab switch, track manual viewport adjustments
This commit is contained in:
parent
bf594c89de
commit
4d718db5a0
2 changed files with 57 additions and 1 deletions
|
|
@ -104,7 +104,7 @@ export function ExecutionOverviewTab({
|
|||
<span className="font-semibold">{nullRate?.toFixed(1) ?? "--"}%</span>
|
||||
</p>
|
||||
<p className="flex items-center justify-between gap-3">
|
||||
<span className="text-muted-foreground">Dropped columns</span>
|
||||
<span className="text-muted-foreground">Side-effect columns</span>
|
||||
<span className="font-semibold">{formatMetricValue(sideEffects.length)}</span>
|
||||
</p>
|
||||
{sideEffects.length > 0 && (
|
||||
|
|
|
|||
|
|
@ -164,6 +164,9 @@ export function RecipeStudioPage({
|
|||
ReactFlowInstance<Node<RecipeNodeData | RecipeGraphAuxNodeData>, Edge> | null
|
||||
>(null);
|
||||
const lastProcessedFitTickRef = useRef(0);
|
||||
const previousActiveViewRef = useRef<RecipeStudioView>("editor");
|
||||
const pendingEditorTabFitRef = useRef(false);
|
||||
const viewportMovedSinceAutoFitRef = useRef(true);
|
||||
const handleExecutionStart = useCallback(() => {
|
||||
setActiveView("executions");
|
||||
}, []);
|
||||
|
|
@ -336,6 +339,53 @@ export function RecipeStudioPage({
|
|||
const runDialogLoading =
|
||||
runDialogKind === "preview" ? previewLoading : fullLoading;
|
||||
|
||||
useEffect(() => {
|
||||
if (previousActiveViewRef.current !== activeView && activeView === "editor") {
|
||||
pendingEditorTabFitRef.current = true;
|
||||
}
|
||||
previousActiveViewRef.current = activeView;
|
||||
}, [activeView]);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
!reactFlowInstance ||
|
||||
activeView !== "editor" ||
|
||||
!pendingEditorTabFitRef.current
|
||||
) {
|
||||
return;
|
||||
}
|
||||
pendingEditorTabFitRef.current = false;
|
||||
if (!viewportMovedSinceAutoFitRef.current) {
|
||||
return;
|
||||
}
|
||||
const targetNodes = getFitNodeIdsIgnoringNotes(reactFlowInstance.getNodes());
|
||||
if (targetNodes.length === 0) {
|
||||
return;
|
||||
}
|
||||
viewportMovedSinceAutoFitRef.current = false;
|
||||
let frame2 = 0;
|
||||
let frame3 = 0;
|
||||
const frame1 = window.requestAnimationFrame(() => {
|
||||
frame2 = window.requestAnimationFrame(() => {
|
||||
frame3 = window.requestAnimationFrame(() => {
|
||||
reactFlowInstance.fitView({
|
||||
duration: 320,
|
||||
nodes: targetNodes,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
return () => {
|
||||
window.cancelAnimationFrame(frame1);
|
||||
if (frame2) {
|
||||
window.cancelAnimationFrame(frame2);
|
||||
}
|
||||
if (frame3) {
|
||||
window.cancelAnimationFrame(frame3);
|
||||
}
|
||||
};
|
||||
}, [activeView, reactFlowInstance]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!reactFlowInstance || fitViewTick === 0 || activeView !== "editor") {
|
||||
return;
|
||||
|
|
@ -348,6 +398,7 @@ export function RecipeStudioPage({
|
|||
if (targetNodes.length === 0) {
|
||||
return;
|
||||
}
|
||||
viewportMovedSinceAutoFitRef.current = false;
|
||||
let frame2 = 0;
|
||||
let frame3 = 0;
|
||||
const frame1 = window.requestAnimationFrame(() => {
|
||||
|
|
@ -410,6 +461,11 @@ export function RecipeStudioPage({
|
|||
onNodeClick={handleNodeClick}
|
||||
onNodeDoubleClick={handleNodeDoubleClick}
|
||||
isValidConnection={isValidConnection}
|
||||
onMoveEnd={(event) => {
|
||||
if (event) {
|
||||
viewportMovedSinceAutoFitRef.current = true;
|
||||
}
|
||||
}}
|
||||
nodesDraggable={canvasInteractive}
|
||||
nodesConnectable={canvasInteractive}
|
||||
elementsSelectable={canvasInteractive}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue