closes SKY-155 - adds autopan to workflow editor (#2331)
This commit is contained in:
@@ -30,7 +30,7 @@ import {
|
||||
import "@xyflow/react/dist/style.css";
|
||||
import { AxiosError } from "axios";
|
||||
import { nanoid } from "nanoid";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { useBlocker, useParams } from "react-router-dom";
|
||||
import { stringify as convertToYAML } from "yaml";
|
||||
import {
|
||||
@@ -85,6 +85,7 @@ import {
|
||||
startNode,
|
||||
} from "./workflowEditorUtils";
|
||||
import { parameterIsBitwardenCredential, ParametersState } from "./types";
|
||||
import { useAutoPan } from "./useAutoPan";
|
||||
|
||||
function convertToParametersYAML(
|
||||
parameters: ParametersState,
|
||||
@@ -494,6 +495,10 @@ function FlowRenderer({
|
||||
doLayout(newNodesWithUpdatedParameters, newEdges);
|
||||
}
|
||||
|
||||
const editorElementRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useAutoPan(editorElementRef, nodes);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Dialog
|
||||
@@ -542,6 +547,7 @@ function FlowRenderer({
|
||||
>
|
||||
<DeleteNodeCallbackContext.Provider value={deleteNode}>
|
||||
<ReactFlow
|
||||
ref={editorElementRef}
|
||||
nodes={nodes}
|
||||
edges={edges}
|
||||
onNodesChange={(changes) => {
|
||||
|
||||
58
skyvern-frontend/src/routes/workflows/editor/useAutoPan.ts
Normal file
58
skyvern-frontend/src/routes/workflows/editor/useAutoPan.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import { useReactFlow } from "@xyflow/react";
|
||||
import { useLayoutEffect } from "react";
|
||||
|
||||
import { AppNode } from "./nodes";
|
||||
|
||||
/**
|
||||
* Some facts:
|
||||
* - the workflow editor is rendered as an infinite canvas
|
||||
* - nodes in the editor can have text fields
|
||||
* - users type in those text fields
|
||||
* - the browser will automatically attempt to scroll to a caret position when
|
||||
* the caret leaves the viewport, because it is Being A Good Browser(tm)
|
||||
* - this causes layout artifacts on the infinite canvas
|
||||
*
|
||||
* `useAutoPan` detects when the viewport is scrolled. (But it should never have
|
||||
* a scroll, as it is an infinite canvas!) If a scroll value is detected, then
|
||||
* we pan the viewport to counteract the scroll, and set the scroll to 0.
|
||||
*
|
||||
* The end result is that:
|
||||
* - if a user is typing in any textual HTML element, and they scroll beyond
|
||||
* the viewport, the viewport will animate-pan to counteract the scroll
|
||||
* - if the user pastes large amounts of text into any textual HTML element,
|
||||
* the viewport will animate-pan to counteract the scroll
|
||||
*
|
||||
* `editorElementRef`: a ref to the top-level editor element (the top-level div
|
||||
* for react-flow, at time of writing)
|
||||
*
|
||||
* `nodes`: `AppNode`s; but could be anything that carries state indicative of a
|
||||
* change in the editor
|
||||
*/
|
||||
const useAutoPan = (
|
||||
editorElementRef: React.RefObject<HTMLDivElement>,
|
||||
nodes: AppNode[],
|
||||
) => {
|
||||
const { setViewport, getViewport } = useReactFlow();
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const editorElement = editorElementRef.current;
|
||||
if (editorElement) {
|
||||
const scrollTop = editorElement.scrollTop;
|
||||
|
||||
if (scrollTop === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
editorElement.scrollTop = 0;
|
||||
const { x, y, zoom } = getViewport();
|
||||
const panAmount = editorElement.clientHeight * 0.3;
|
||||
|
||||
setViewport(
|
||||
{ x, y: y - (scrollTop + panAmount), zoom },
|
||||
{ duration: 300 },
|
||||
);
|
||||
}
|
||||
}, [nodes, editorElementRef, setViewport, getViewport]);
|
||||
};
|
||||
|
||||
export { useAutoPan };
|
||||
Reference in New Issue
Block a user