This is an automated email from the ASF dual-hosted git repository.

pierrejeambrun pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new 3237cb3484 Adjust graph zoom based on selected task (#32792)
3237cb3484 is described below

commit 3237cb3484f0e7ec611ffba13360185e2f6c33f8
Author: Brent Bovenzi <[email protected]>
AuthorDate: Thu Jul 27 06:50:40 2023 +0800

    Adjust graph zoom based on selected task (#32792)
    
    * Center to node on task selection
    
    * Use new fitView options from reactflow
    
    * Maintain zoom, remove focusNode fn
---
 airflow/www/package.json                          |   2 +-
 airflow/www/static/js/dag/details/graph/index.tsx |  99 ++++++++------------
 airflow/www/static/js/dag/details/graph/utils.ts  |   6 +-
 airflow/www/yarn.lock                             | 108 +++++++++++-----------
 4 files changed, 97 insertions(+), 118 deletions(-)

diff --git a/airflow/www/package.json b/airflow/www/package.json
index cad6a6320a..aec3e3509f 100644
--- a/airflow/www/package.json
+++ b/airflow/www/package.json
@@ -137,7 +137,7 @@
     "react-syntax-highlighter": "^15.5.0",
     "react-table": "^7.8.0",
     "react-textarea-autosize": "^8.3.4",
-    "reactflow": "^11.4.0",
+    "reactflow": "^11.7.4",
     "redoc": "^2.0.0-rc.72",
     "remark-gfm": "^3.0.1",
     "swagger-ui-dist": "4.1.3",
diff --git a/airflow/www/static/js/dag/details/graph/index.tsx 
b/airflow/www/static/js/dag/details/graph/index.tsx
index 4dae9477bd..1bdfe5ece7 100644
--- a/airflow/www/static/js/dag/details/graph/index.tsx
+++ b/airflow/www/static/js/dag/details/graph/index.tsx
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-import React, { useRef, useState, useEffect } from "react";
+import React, { useRef, useState, useEffect, useMemo } from "react";
 import { Box, useTheme, Select, Text } from "@chakra-ui/react";
 import ReactFlow, {
   ReactFlowProvider,
@@ -26,18 +26,13 @@ import ReactFlow, {
   MiniMap,
   Node as ReactFlowNode,
   useReactFlow,
-  ControlButton,
   Panel,
 } from "reactflow";
-import { RiFocus3Line } from "react-icons/ri";
 
 import { useGraphData, useGridData } from "src/api";
 import useSelection from "src/dag/useSelection";
 import { useOffsetTop } from "src/utils";
 import { useGraphLayout } from "src/utils/graph";
-import Tooltip from "src/components/Tooltip";
-import { useContainerRef } from "src/context/containerRef";
-import useFilters from "src/dag/useFilters";
 import Edge from "src/components/Graph/Edge";
 
 import Node, { CustomNodeProps } from "./Node";
@@ -54,13 +49,9 @@ interface Props {
 
 const Graph = ({ openGroupIds, onToggleGroups, hoveredTaskState }: Props) => {
   const graphRef = useRef(null);
-  const containerRef = useContainerRef();
   const { data } = useGraphData();
   const [arrange, setArrange] = useState(data?.arrange || "LR");
-
-  const {
-    filters: { root, filterDownstream, filterUpstream },
-  } = useFilters();
+  const [hasRendered, setHasRendered] = useState(false);
 
   useEffect(() => {
     setArrange(data?.arrange || "LR");
@@ -77,45 +68,47 @@ const Graph = ({ openGroupIds, onToggleGroups, 
hoveredTaskState }: Props) => {
     data: { dagRuns, groups },
   } = useGridData();
   const { colors } = useTheme();
-  const { setCenter, setViewport } = useReactFlow();
+  const { getZoom, fitView } = useReactFlow();
   const latestDagRunId = dagRuns[dagRuns.length - 1]?.runId;
-
-  // Reset viewport when tasks are filtered
-  useEffect(() => {
-    setViewport({ x: 0, y: 0, zoom: 1 });
-  }, [root, filterDownstream, filterUpstream, setViewport]);
-
   const offsetTop = useOffsetTop(graphRef);
 
-  let nodes: ReactFlowNode<CustomNodeProps>[] = [];
-
-  if (graphData?.children) {
-    nodes = flattenNodes({
-      children: graphData.children,
+  const nodes: ReactFlowNode<CustomNodeProps>[] = useMemo(
+    () =>
+      graphData?.children
+        ? flattenNodes({
+            children: graphData.children,
+            selected,
+            openGroupIds,
+            onToggleGroups,
+            latestDagRunId,
+            groups,
+            hoveredTaskState,
+          })
+        : [],
+    [
+      graphData?.children,
       selected,
       openGroupIds,
       onToggleGroups,
       latestDagRunId,
       groups,
       hoveredTaskState,
-    });
-  }
+    ]
+  );
 
-  const focusNode = () => {
-    if (selected.taskId) {
-      const node = nodes.find((n) => n.id === selected.taskId);
-      const x = node?.positionAbsolute?.x || node?.position.x;
-      const y = node?.positionAbsolute?.y || node?.position.y;
-      if (!x || !y) return;
-      setCenter(
-        x + (node.data.width || 0) / 2,
-        y + (node.data.height || 0) / 2,
-        {
-          duration: 1000,
-        }
-      );
+  // Zoom to/from nodes when changing selection, maintain zoom level when 
changing task selection
+  useEffect(() => {
+    if (hasRendered) {
+      const zoom = getZoom();
+      fitView({
+        duration: 750,
+        nodes: selected.taskId ? [{ id: selected.taskId }] : undefined,
+        minZoom: selected.taskId ? zoom : undefined,
+        maxZoom: selected.taskId ? zoom : undefined,
+      });
     }
-  };
+    setHasRendered(true);
+  }, [fitView, hasRendered, selected.taskId, getZoom]);
 
   const edges = buildEdges({
     edges: graphData?.edges,
@@ -141,6 +134,11 @@ const Graph = ({ openGroupIds, onToggleGroups, 
hoveredTaskState }: Props) => {
           maxZoom={1}
           onlyRenderVisibleElements
           defaultEdgeOptions={{ zIndex: 1 }}
+          // Fit view to selected task or the whole graph on render
+          fitView
+          fitViewOptions={{
+            nodes: selected.taskId ? [{ id: selected.taskId }] : undefined,
+          }}
         >
           <Panel position="top-right">
             <Box bg="#ffffffdd" p={1}>
@@ -157,28 +155,7 @@ const Graph = ({ openGroupIds, onToggleGroups, 
hoveredTaskState }: Props) => {
             </Box>
           </Panel>
           <Background />
-          <Controls showInteractive={false}>
-            <ControlButton onClick={focusNode} disabled={!selected.taskId}>
-              <Tooltip
-                portalProps={{ containerRef }}
-                label="Center selected task"
-                placement="right"
-              >
-                <Box>
-                  <RiFocus3Line
-                    size={16}
-                    style={{
-                      // override react-flow css
-                      maxWidth: "16px",
-                      maxHeight: "16px",
-                      color: colors.gray[800],
-                    }}
-                    aria-label="Center selected task"
-                  />
-                </Box>
-              </Tooltip>
-            </ControlButton>
-          </Controls>
+          <Controls showInteractive={false} />
           <MiniMap
             nodeStrokeWidth={15}
             nodeStrokeColor={(props) => nodeStrokeColor(props, colors)}
diff --git a/airflow/www/static/js/dag/details/graph/utils.ts 
b/airflow/www/static/js/dag/details/graph/utils.ts
index b1e27fb0ed..aef01e5d53 100644
--- a/airflow/www/static/js/dag/details/graph/utils.ts
+++ b/airflow/www/static/js/dag/details/graph/utils.ts
@@ -125,14 +125,14 @@ export const flattenNodes = ({
 };
 
 export const nodeColor = ({
-  data: { height, width, instance, childCount, isActive },
+  data: { height, width, instance, isActive, isOpen },
 }: ReactFlowNode<CustomNodeProps>) => {
   let opacity = "90";
   let color = "#cccccc";
   if (!height || !width) return "";
-  if (instance?.state && !childCount)
+  if (instance?.state && !isOpen)
     color = Color(stateColors[instance.state]).hex();
-  if (childCount) opacity = "50";
+  if (isOpen) opacity = "50";
   if (!isActive) opacity = "21";
 
   return `${color}${opacity}`;
diff --git a/airflow/www/yarn.lock b/airflow/www/yarn.lock
index a3e6e864b7..a8b4499904 100644
--- a/airflow/www/yarn.lock
+++ b/airflow/www/yarn.lock
@@ -1354,13 +1354,6 @@
   dependencies:
     regenerator-runtime "^0.13.4"
 
-"@babel/runtime@^7.18.9":
-  version "7.20.6"
-  resolved 
"https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.6.tgz#facf4879bfed9b5326326273a64220f099b0fce3";
-  integrity 
sha512-Q+8MqP7TiHMWzSfwiJwXCjyf4GYA4Dgw3emg/7xmwsdLJOZUp+nMqcOwOzzYheuM1rhDu8FSj2l0aoMygEuXuA==
-  dependencies:
-    regenerator-runtime "^0.13.11"
-
 "@babel/runtime@^7.3.1":
   version "7.21.5"
   resolved 
"https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.21.5.tgz#8492dddda9644ae3bda3b45eabe87382caee7200";
@@ -2762,29 +2755,28 @@
   resolved 
"https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.2.tgz#830beaec4b4091a9e9398ac50f865ddea52186b9";
   integrity 
sha512-92FRmppjjqz29VMJ2dn+xdyXZBrMlE42AV6Kq6BwjWV7CNUW1hs2FtxSNLQE+gJhaZ6AAmYuO9y8dshhcBl7vA==
 
-"@reactflow/[email protected]":
-  version "11.1.0"
-  resolved 
"https://registry.yarnpkg.com/@reactflow/background/-/background-11.1.0.tgz#943c799d9f251340e9867ad8f4c6ac291163e401";
-  integrity 
sha512-EgDn3rhK+l8jKmE6KGUZvesRjdh7fOqsz5Hj7STUU5/uGsvgN9KFuudY/Ka8m+yCQxqNK8MAJcRMOZd0mvNFMQ==
+"@reactflow/[email protected]":
+  version "11.2.4"
+  resolved 
"https://registry.yarnpkg.com/@reactflow/background/-/background-11.2.4.tgz#06cd4c9f222dbeb2d3ffb2a530b6d88bf130dd9c";
+  integrity 
sha512-SYQbCRCU0GuxT/40Tm7ZK+l5wByGnNJSLtZhbL9C/Hl7JhsJXV3UGXr0vrlhVZUBEtkWA7XhZM/5S9XEA5XSFA==
   dependencies:
-    "@babel/runtime" "^7.18.9"
-    "@reactflow/core" "11.4.0"
+    "@reactflow/core" "11.7.4"
     classcat "^5.0.3"
-    zustand "^4.1.1"
+    zustand "^4.3.1"
 
-"@reactflow/[email protected]":
-  version "11.1.0"
-  resolved 
"https://registry.yarnpkg.com/@reactflow/controls/-/controls-11.1.0.tgz#6d6f6dd6e53557579c6cfcea3c7376d2d00c2953";
-  integrity 
sha512-5nH1TQ9mkveUOnq7QgohzeAdGR4WwKQJMrWjb5u3Dnm5D5+oRxTE3eGBoaw6B6nYaK1rDrPCcMAuGmEPdEC+Mg==
+"@reactflow/[email protected]":
+  version "11.1.15"
+  resolved 
"https://registry.yarnpkg.com/@reactflow/controls/-/controls-11.1.15.tgz#6dc823eb67f38a50907fffcc21b6a20e4fc00e7c";
+  integrity 
sha512-//33XfBYu8vQ6brfmlZwKrDoh+8hh93xO2d88XiqfIbrPEEb32SYjsb9mS9VuHKNlSIW+eB27fBA1Gt00mEj5w==
   dependencies:
-    "@babel/runtime" "^7.18.9"
-    "@reactflow/core" "11.4.0"
+    "@reactflow/core" "11.7.4"
     classcat "^5.0.3"
+    zustand "^4.3.1"
 
-"@reactflow/[email protected]":
-  version "11.4.0"
-  resolved 
"https://registry.yarnpkg.com/@reactflow/core/-/core-11.4.0.tgz#9af0c812eb9968b75cf55427c6be4a9205d0db48";
-  integrity 
sha512-AfFp685kmxWs2Iiq35TatG9Q8u5W+eftXECQ0ea55Oi37nrMe5jfWhjnGnnl3bSFcHqAe6avqNiFDwqugU6kzQ==
+"@reactflow/[email protected]", "@reactflow/core@^11.6.0":
+  version "11.7.4"
+  resolved 
"https://registry.yarnpkg.com/@reactflow/core/-/core-11.7.4.tgz#1a7e4d6cabbd2ea888547133d507f1ab24896520";
+  integrity 
sha512-nt0T8ERp8TE7YCDQViaoEY9lb0StDPrWHVx3zBjhStFYET3wc88t8QRasZdf99xRTmyNtI3U3M40M5EBLNUpMw==
   dependencies:
     "@types/d3" "^7.4.0"
     "@types/d3-drag" "^3.0.1"
@@ -2794,31 +2786,40 @@
     d3-drag "^3.0.0"
     d3-selection "^3.0.0"
     d3-zoom "^3.0.0"
-    zustand "^4.1.1"
+    zustand "^4.3.1"
 
-"@reactflow/[email protected]":
-  version "11.3.0"
-  resolved 
"https://registry.yarnpkg.com/@reactflow/minimap/-/minimap-11.3.0.tgz#2f89dbab4c10b754c452f70857172d959cca60aa";
-  integrity 
sha512-nvb4qmbsogjhrn7GWXpvLMtmAyE7mjs0BXvtbpcFVpKqQ3Lbf76zCa8c2krUMnBBqu+9yF0Ftkn7mMCTV2gPLQ==
+"@reactflow/[email protected]":
+  version "11.5.4"
+  resolved 
"https://registry.yarnpkg.com/@reactflow/minimap/-/minimap-11.5.4.tgz#b072094f7d827660f0205796d5f22fbbf6e31cc3";
+  integrity 
sha512-1tDBj2zX2gxu2oHU6qvH5RGNrOWRfRxu8369KhDotuuBN5yJrGXJzWIKikwhzjsNsQJYOB+B0cS44yWAfwSwzw==
   dependencies:
-    "@babel/runtime" "^7.18.9"
-    "@reactflow/core" "11.4.0"
+    "@reactflow/core" "11.7.4"
     "@types/d3-selection" "^3.0.3"
     "@types/d3-zoom" "^3.0.1"
     classcat "^5.0.3"
     d3-selection "^3.0.0"
     d3-zoom "^3.0.0"
-    zustand "^4.1.1"
+    zustand "^4.3.1"
 
-"@reactflow/[email protected]":
-  version "1.1.0"
-  resolved 
"https://registry.yarnpkg.com/@reactflow/node-toolbar/-/node-toolbar-1.1.0.tgz#c7de2fb5c7aef02a1e575ce12a35d23cd45c18bd";
-  integrity 
sha512-kibrTGGvwhFGndVSgwr9E6l9Uddr44csr06X+PJ7FJ0SXgeOHbSw4MaM/9dSFxkFoCi77fPXSdMONNTReSBnIg==
+"@reactflow/[email protected]":
+  version "2.1.1"
+  resolved 
"https://registry.yarnpkg.com/@reactflow/node-resizer/-/node-resizer-2.1.1.tgz#8f9b4e362e572dcddb54d43a67b5c5919b25937f";
+  integrity 
sha512-5Q+IBmZfpp/bYsw3+KRVJB1nUbj6W3XAp5ycx4uNWH+K98vbssymyQsW0vvKkIhxEPg6tkiMzO4UWRWvwBwt1g==
   dependencies:
-    "@babel/runtime" "^7.18.9"
-    "@reactflow/core" "11.4.0"
+    "@reactflow/core" "^11.6.0"
+    classcat "^5.0.4"
+    d3-drag "^3.0.0"
+    d3-selection "^3.0.0"
+    zustand "^4.3.1"
+
+"@reactflow/[email protected]":
+  version "1.2.3"
+  resolved 
"https://registry.yarnpkg.com/@reactflow/node-toolbar/-/node-toolbar-1.2.3.tgz#8ff8408dffee7920752479cd19e7ab7c9c47b4d2";
+  integrity 
sha512-uFQy9xpog92s0G1wsPLniwV9nyH4i/MmL7QoMsWdnKaOi7XMhd8SJcCzUdHC3imR21HltsuQITff/XQ51ApMbg==
+  dependencies:
+    "@reactflow/core" "11.7.4"
     classcat "^5.0.3"
-    zustand "^4.1.1"
+    zustand "^4.3.1"
 
 "@redocly/ajv@^8.6.4":
   version "8.6.4"
@@ -4518,7 +4519,7 @@ cjs-module-lexer@^1.0.0:
   resolved 
"https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40";
   integrity 
sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==
 
-classcat@^5.0.3:
+classcat@^5.0.3, classcat@^5.0.4:
   version "5.0.4"
   resolved 
"https://registry.yarnpkg.com/classcat/-/classcat-5.0.4.tgz#e12d1dfe6df6427f260f03b80dc63571a5107ba6";
   integrity 
sha512-sbpkOw6z413p+HDGcBENe498WM9woqWHiJxCq7nvmxe9WmrUmqfAcxpIwAiMtM5Q3AhYkzXcNQHqsWq0mND51g==
@@ -9975,16 +9976,17 @@ react@^18.0.0:
   dependencies:
     loose-envify "^1.1.0"
 
-reactflow@^11.4.0:
-  version "11.4.0"
-  resolved 
"https://registry.yarnpkg.com/reactflow/-/reactflow-11.4.0.tgz#aeb4b030ba93e8e656094f59226e55ec538f55b4";
-  integrity 
sha512-Y+LZ3XZX7UejW4vukeyLwDDfqNT0RxyNNSHD1FJOIu2IvyVMkj+wKTcbp3ehm7brBkMOOaPyugcEWlLwFXcrjg==
+reactflow@^11.7.4:
+  version "11.7.4"
+  resolved 
"https://registry.yarnpkg.com/reactflow/-/reactflow-11.7.4.tgz#b00159c3471d007bc4865b23005c636b1f08ab26";
+  integrity 
sha512-QI6+oc1Ft6oFeLSdHlp+SmgymbI5Tm49wj5JyE84O4A54yN/ImfYaBhLit9Cmfzxn9Tz6tDqmGMGbk4bdtB8/w==
   dependencies:
-    "@reactflow/background" "11.1.0"
-    "@reactflow/controls" "11.1.0"
-    "@reactflow/core" "11.4.0"
-    "@reactflow/minimap" "11.3.0"
-    "@reactflow/node-toolbar" "1.1.0"
+    "@reactflow/background" "11.2.4"
+    "@reactflow/controls" "11.1.15"
+    "@reactflow/core" "11.7.4"
+    "@reactflow/minimap" "11.5.4"
+    "@reactflow/node-resizer" "2.1.1"
+    "@reactflow/node-toolbar" "1.2.3"
 
 read-pkg-up@^8.0.0:
   version "8.0.0"
@@ -11863,10 +11865,10 @@ [email protected]:
   dependencies:
     tslib "2.3.0"
 
-zustand@^4.1.1:
-  version "4.1.5"
-  resolved 
"https://registry.yarnpkg.com/zustand/-/zustand-4.1.5.tgz#7402b511f5b23ccb0f9ba6d20ae01ec817e16eb6";
-  integrity 
sha512-PsdRT8Bvq22Yyh1tvpgdHNE7OAeFKqJXUxtJvj1Ixw2B9O2YZ1M34ImQ+xyZah4wZrR4lENMoDUutKPpyXCQ/Q==
+zustand@^4.3.1:
+  version "4.3.9"
+  resolved 
"https://registry.yarnpkg.com/zustand/-/zustand-4.3.9.tgz#a7d4332bbd75dfd25c6848180b3df1407217f2ad";
+  integrity 
sha512-Tat5r8jOMG1Vcsj8uldMyqYKC5IZvQif8zetmLHs9WoZlntTHmIoNM8TpLRY31ExncuUvUOXehd0kvahkuHjDw==
   dependencies:
     use-sync-external-store "1.2.0"
 

Reply via email to