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"