This is an automated email from the ASF dual-hosted git repository. potiuk pushed a commit to branch v2-8-test in repository https://gitbox.apache.org/repos/asf/airflow.git
commit d9934cf5666824c770807c634c04d1ff69fda8be Author: Brent Bovenzi <[email protected]> AuthorDate: Tue Feb 6 17:24:49 2024 -0500 Adjust graph node layout (#37207) (cherry picked from commit 7ea7abc75d9bb8349e95fc330b43657950837d30) --- airflow/www/static/js/components/Graph/Edge.tsx | 6 +- .../www/static/js/dag/details/graph/Node.test.tsx | 1 + airflow/www/static/js/dag/details/graph/Node.tsx | 79 ++++++++++++++-------- airflow/www/static/js/dag/details/graph/index.tsx | 14 ++++ airflow/www/static/js/dag/details/graph/utils.ts | 8 +++ airflow/www/static/js/types/index.ts | 1 + 6 files changed, 80 insertions(+), 29 deletions(-) diff --git a/airflow/www/static/js/components/Graph/Edge.tsx b/airflow/www/static/js/components/Graph/Edge.tsx index 3d9e144795..99b6259055 100644 --- a/airflow/www/static/js/components/Graph/Edge.tsx +++ b/airflow/www/static/js/components/Graph/Edge.tsx @@ -32,6 +32,10 @@ const CustomEdge = ({ data }: EdgeProps) => { const { colors } = useTheme(); if (!data) return null; const { rest } = data; + let strokeWidth = 2; + if (rest.isSelected) strokeWidth = 3; + if (rest.isZoomedOut) strokeWidth = 5; + if (rest.isZoomedOut && rest.isSelected) strokeWidth = 7; return ( <> {rest?.labels?.map(({ id, x, y, text, width, height }) => { @@ -48,7 +52,7 @@ const CustomEdge = ({ data }: EdgeProps) => { <LinePath key={s.id} stroke={rest.isSelected ? colors.blue[400] : colors.gray[400]} - strokeWidth={rest.isSelected ? 3 : 2} + strokeWidth={strokeWidth} x={(d) => d.x || 0} y={(d) => d.y || 0} data={[s.startPoint, ...(s.bendPoints || []), s.endPoint]} diff --git a/airflow/www/static/js/dag/details/graph/Node.test.tsx b/airflow/www/static/js/dag/details/graph/Node.test.tsx index 6e1a4bca13..251f3c6aeb 100644 --- a/airflow/www/static/js/dag/details/graph/Node.test.tsx +++ b/airflow/www/static/js/dag/details/graph/Node.test.tsx @@ -52,6 +52,7 @@ const mockNode: NodeProps<CustomNodeProps> = { latestDagRunId: "run_id", onToggleCollapse: () => {}, isActive: true, + isZoomedOut: false, }, selected: false, zIndex: 0, diff --git a/airflow/www/static/js/dag/details/graph/Node.tsx b/airflow/www/static/js/dag/details/graph/Node.tsx index 6c72f041ac..7be5ae7b88 100644 --- a/airflow/www/static/js/dag/details/graph/Node.tsx +++ b/airflow/www/static/js/dag/details/graph/Node.tsx @@ -46,6 +46,7 @@ export interface CustomNodeProps { setupTeardownType?: "setup" | "teardown"; labelStyle?: string; style?: string; + isZoomedOut: boolean; } export const BaseNode = ({ @@ -65,6 +66,7 @@ export const BaseNode = ({ setupTeardownType, labelStyle, style, + isZoomedOut, }, }: NodeProps<CustomNodeProps>) => { const { colors } = useTheme(); @@ -92,6 +94,8 @@ export const BaseNode = ({ if (labelStyle) { [, operatorTextColor] = labelStyle.split(":"); } + if (!operatorTextColor || operatorTextColor === "#000;") + operatorTextColor = "gray.500"; const nodeBorderColor = instance?.state && stateColors[instance.state] @@ -111,10 +115,15 @@ export const BaseNode = ({ openDelay={hoverDelay} > <Box - borderRadius={5} - borderWidth={isSelected ? 2.5 : 1.5} + borderRadius={isZoomedOut ? 10 : 5} + borderWidth={(isSelected ? 4 : 2) * (isZoomedOut ? 3 : 1)} borderColor={nodeBorderColor} - bg={isSelected ? "blue.50" : bg} + bg={ + !task.children?.length && operatorBG + ? // Fade the operator color to clash less with the task instance status + `color-mix(in srgb, ${operatorBG.replace(";", "")} 80%, white)` + : bg + } height={`${height}px`} width={`${width}px`} cursor={latestDagRunId ? "cursor" : "default"} @@ -134,13 +143,16 @@ export const BaseNode = ({ justifyContent="space-between" width={width} p={2} - flexWrap="wrap" + flexWrap={isZoomedOut ? "nowrap" : "wrap"} + flexDirection={isZoomedOut ? "row" : "column"} > <Flex flexDirection="column" width="100%"> <Flex justifyContent="space-between" alignItems="center" width="100%" + fontSize={isZoomedOut ? 25 : undefined} + fontWeight="bold" > <Text noOfLines={1} maxWidth={`calc(${width}px - 8px)`}> {taskName} @@ -152,37 +164,48 @@ export const BaseNode = ({ <ImArrowDownRight2 size={15} color={colors.gray[800]} /> )} </Flex> - {!!instance && instance.state && ( - <Flex alignItems="center"> - <SimpleStatus state={instance.state} /> - <Text ml={2} color="gray.500" fontSize="lg"> - {instance.state} - </Text> - </Flex> - )} - {task?.operator && ( - <Text - noOfLines={1} - maxWidth={`calc(${width}px - 12px)`} - fontWeight={400} - fontSize="md" - width="fit-content" - borderRadius={5} - bg={operatorBG} - color={operatorTextColor || "gray.500"} - px={1} - > - {task.operator} - </Text> + {!isZoomedOut && ( + <> + {!!instance && instance.state && ( + <Flex alignItems="center"> + <SimpleStatus state={instance.state} /> + <Text + ml={2} + color="gray.500" + fontWeight={400} + fontSize="md" + > + {instance.state} + </Text> + </Flex> + )} + {task?.operator && ( + <Text + noOfLines={1} + maxWidth={`calc(${width}px - 12px)`} + fontWeight={400} + fontSize="md" + width="fit-content" + color={operatorTextColor} + px={1} + > + {task.operator} + </Text> + )} + </> )} </Flex> {!!childCount && ( <Text + noOfLines={1} + fontSize={isZoomedOut ? 25 : undefined} color="blue.600" cursor="pointer" + width="150px" + fontWeight="bold" // Increase the target area to expand/collapse a group - p={3} - m={-3} + p={2} + m={-2} onClick={(e) => { e.stopPropagation(); onToggleCollapse(); diff --git a/airflow/www/static/js/dag/details/graph/index.tsx b/airflow/www/static/js/dag/details/graph/index.tsx index 29e07d65aa..4fb3d21f6c 100644 --- a/airflow/www/static/js/dag/details/graph/index.tsx +++ b/airflow/www/static/js/dag/details/graph/index.tsx @@ -26,6 +26,8 @@ import ReactFlow, { MiniMap, useReactFlow, Panel, + useOnViewportChange, + Viewport, } from "reactflow"; import { useGraphData, useGridData } from "src/api"; @@ -51,6 +53,7 @@ const Graph = ({ openGroupIds, onToggleGroups, hoveredTaskState }: Props) => { const { data } = useGraphData(); const [arrange, setArrange] = useState(data?.arrange || "LR"); const [hasRendered, setHasRendered] = useState(false); + const [isZoomedOut, setIsZoomedOut] = useState(false); useEffect(() => { setArrange(data?.arrange || "LR"); @@ -71,6 +74,13 @@ const Graph = ({ openGroupIds, onToggleGroups, hoveredTaskState }: Props) => { const latestDagRunId = dagRuns[dagRuns.length - 1]?.runId; const offsetTop = useOffsetTop(graphRef); + useOnViewportChange({ + onEnd: (viewport: Viewport) => { + if (viewport.zoom < 0.5 && !isZoomedOut) setIsZoomedOut(true); + if (viewport.zoom >= 0.5 && isZoomedOut) setIsZoomedOut(false); + }, + }); + const { nodes, edges: nodeEdges } = useMemo( () => flattenNodes({ @@ -81,6 +91,7 @@ const Graph = ({ openGroupIds, onToggleGroups, hoveredTaskState }: Props) => { latestDagRunId, groups, hoveredTaskState, + isZoomedOut, }), [ graphData?.children, @@ -90,6 +101,7 @@ const Graph = ({ openGroupIds, onToggleGroups, hoveredTaskState }: Props) => { latestDagRunId, groups, hoveredTaskState, + isZoomedOut, ] ); @@ -97,6 +109,7 @@ const Graph = ({ openGroupIds, onToggleGroups, hoveredTaskState }: Props) => { useEffect(() => { if (hasRendered) { const zoom = getZoom(); + if (zoom < 0.5) setIsZoomedOut(true); fitView({ duration: 750, nodes: selected.taskId ? [{ id: selected.taskId }] : undefined, @@ -116,6 +129,7 @@ const Graph = ({ openGroupIds, onToggleGroups, hoveredTaskState }: Props) => { edges: flatEdges, nodes, selectedTaskId: selected.taskId, + isZoomedOut, }); return ( diff --git a/airflow/www/static/js/dag/details/graph/utils.ts b/airflow/www/static/js/dag/details/graph/utils.ts index ac940d46c7..901b47ab18 100644 --- a/airflow/www/static/js/dag/details/graph/utils.ts +++ b/airflow/www/static/js/dag/details/graph/utils.ts @@ -36,6 +36,7 @@ interface FlattenNodesProps { openGroupIds: string[]; onToggleGroups: (groupIds: string[]) => void; hoveredTaskState?: string | null; + isZoomedOut: boolean; } // Generate a flattened list of nodes for react-flow to render @@ -48,6 +49,7 @@ export const flattenNodes = ({ openGroupIds, parent, hoveredTaskState, + isZoomedOut, }: FlattenNodesProps) => { let nodes: ReactFlowNode<CustomNodeProps>[] = []; let edges: ElkExtendedEdge[] = []; @@ -76,6 +78,7 @@ export const flattenNodes = ({ isSelected, latestDagRunId, isActive, + isZoomedOut, onToggleCollapse: () => { let newGroupIds = []; if (!node.value.isOpen) { @@ -115,6 +118,7 @@ export const flattenNodes = ({ openGroupIds, parent: newNode, hoveredTaskState, + isZoomedOut, }); nodes = [...nodes, ...childNodes]; edges = [...edges, ...childEdges]; @@ -153,6 +157,7 @@ interface BuildEdgesProps { edges?: Edge[]; nodes: ReactFlowNode<CustomNodeProps>[]; selectedTaskId?: string | null; + isZoomedOut?: boolean; } // Format edge data to what react-flow needs to render successfully @@ -160,6 +165,7 @@ export const buildEdges = ({ edges = [], nodes, selectedTaskId, + isZoomedOut, }: BuildEdgesProps) => edges .map((edge) => ({ @@ -184,6 +190,7 @@ export const buildEdges = ({ ...e, data: { rest: { + isZoomedOut, ...e.data.rest, labels: e.data.rest.labels?.map((l) => l.x && l.y ? { ...l, x: l.x + parentX, y: l.y + parentY } : l @@ -215,6 +222,7 @@ export const buildEdges = ({ rest: { ...e.data.rest, isSelected, + isZoomedOut, }, }, }; diff --git a/airflow/www/static/js/types/index.ts b/airflow/www/static/js/types/index.ts index a75c1ace23..b9ada90370 100644 --- a/airflow/www/static/js/types/index.ts +++ b/airflow/www/static/js/types/index.ts @@ -165,6 +165,7 @@ export interface EdgeData { layoutOptions?: LayoutOptions; isSetupTeardown?: boolean; parentNode?: string; + isZoomedOut?: boolean; }; }
