From 039c038cd70a61251cd6ba495d34ca2a97f651a2 Mon Sep 17 00:00:00 2001 From: Kia Lam Date: Tue, 22 Feb 2022 09:36:43 -0800 Subject: [PATCH] Move zoom methods into a hook. --- .../src/screens/TopologyView/TopologyView.js | 52 ++----------- awx/ui/src/screens/TopologyView/constants.js | 2 + .../src/screens/TopologyView/utils/useZoom.js | 74 +++++++++++++++++++ 3 files changed, 82 insertions(+), 46 deletions(-) create mode 100644 awx/ui/src/screens/TopologyView/utils/useZoom.js diff --git a/awx/ui/src/screens/TopologyView/TopologyView.js b/awx/ui/src/screens/TopologyView/TopologyView.js index b689390090..bcb1fb4a62 100644 --- a/awx/ui/src/screens/TopologyView/TopologyView.js +++ b/awx/ui/src/screens/TopologyView/TopologyView.js @@ -1,5 +1,4 @@ import React, { useEffect, useCallback, useState } from 'react'; -import * as d3 from 'd3'; import { t } from '@lingui/macro'; import { PageSection, Card, CardBody } from '@patternfly/react-core'; import ContentError from 'components/ContentError'; @@ -7,6 +6,8 @@ import useRequest from 'hooks/useRequest'; import { MeshAPI } from 'api'; import Header from './Header'; import MeshGraph from './MeshGraph'; +import useZoom from './utils/useZoom'; +import { CHILDSELECTOR, PARENTSELECTOR } from './constants'; function TopologyView() { const [showLegend, setShowLegend] = useState(true); @@ -27,52 +28,11 @@ function TopologyView() { useEffect(() => { fetchMeshVisualizer(); }, [fetchMeshVisualizer]); + const { zoom, zoomFit, zoomIn, zoomOut, resetZoom } = useZoom( + PARENTSELECTOR, + CHILDSELECTOR + ); - const zoom = d3.zoom().on('zoom', ({ transform }) => { - d3.select('.mesh').attr('transform', transform); - }); - const zoomIn = () => { - d3.select('.mesh-svg').transition().call(zoom.scaleBy, 2); - }; - const zoomOut = () => { - d3.select('.mesh-svg').transition().call(zoom.scaleBy, 0.5); - }; - const resetZoom = () => { - const parent = d3.select('.mesh').node().parentElement; - const width = parent.clientWidth; - const height = parent.clientHeight; - d3.select('.mesh-svg') - .transition() - .duration(750) - .call( - zoom.transform, - d3.zoomIdentity, - d3 - .zoomTransform(d3.select('.mesh-svg').node()) - .invert([width / 2, height / 2]) - ); - }; - - const zoomFit = () => { - const bounds = d3.select('.mesh').node().getBBox(); - const parent = d3.select('.mesh').node().parentElement; - const fullWidth = parent.clientWidth; - const fullHeight = parent.clientHeight; - const { width, height } = bounds; - const midX = bounds.x + width / 2; - const midY = bounds.y + height / 2; - if (width === 0 || height === 0) return; // nothing to fit - const scale = 0.8 / Math.max(width / fullWidth, height / fullHeight); - const translate = [ - fullWidth / 2 - scale * midX, - fullHeight / 2 - scale * midY, - ]; - const [x, y] = translate; - d3.select('.mesh-svg') - .transition() - .duration(750) - .call(zoom.transform, d3.zoomIdentity.translate(x, y).scale(scale)); - }; return ( <>
+ * <-- parent --> + * <-- child --> + * + * + * Returns: { + * zoom: d3 zoom behavior/object/function to apply on selected elements + * zoomIn: function that zooms in + * zoomOut: function that zooms out + * zoomFit: function that scales child element to fit within parent element + * resetZoom: function resets the zoom level to its initial value + * } + */ + +export default function useZoom(parentSelector, childSelector) { + const zoom = d3.zoom().on('zoom', ({ transform }) => { + d3.select(childSelector).attr('transform', transform); + }); + const zoomIn = () => { + d3.select(parentSelector).transition().call(zoom.scaleBy, 2); + }; + const zoomOut = () => { + d3.select(parentSelector).transition().call(zoom.scaleBy, 0.5); + }; + const resetZoom = () => { + const parent = d3.select(parentSelector).node(); + const width = parent.clientWidth; + const height = parent.clientHeight; + d3.select(parentSelector) + .transition() + .duration(750) + .call( + zoom.transform, + d3.zoomIdentity, + d3 + .zoomTransform(d3.select(parentSelector).node()) + .invert([width / 2, height / 2]) + ); + }; + const zoomFit = () => { + const bounds = d3.select(childSelector).node().getBBox(); + const fullWidth = getWidth(parentSelector); + const fullHeight = getHeight(parentSelector); + const { width, height } = bounds; + const midX = bounds.x + width / 2; + const midY = bounds.y + height / 2; + if (width === 0 || height === 0) return; // nothing to fit + const scale = 0.8 / Math.max(width / fullWidth, height / fullHeight); + const translate = [ + fullWidth / 2 - scale * midX, + fullHeight / 2 - scale * midY, + ]; + const [x, y] = translate; + d3.select(parentSelector) + .transition() + .duration(750) + .call(zoom.transform, d3.zoomIdentity.translate(x, y).scale(scale)); + }; + + return { + zoom, + zoomIn, + zoomOut, + zoomFit, + resetZoom, + }; +}