mirror of
https://github.com/ansible/awx.git
synced 2026-03-18 01:17:35 -02:30
Move zoom methods into a hook.
This commit is contained in:
@@ -1,5 +1,4 @@
|
|||||||
import React, { useEffect, useCallback, useState } from 'react';
|
import React, { useEffect, useCallback, useState } from 'react';
|
||||||
import * as d3 from 'd3';
|
|
||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { PageSection, Card, CardBody } from '@patternfly/react-core';
|
import { PageSection, Card, CardBody } from '@patternfly/react-core';
|
||||||
import ContentError from 'components/ContentError';
|
import ContentError from 'components/ContentError';
|
||||||
@@ -7,6 +6,8 @@ import useRequest from 'hooks/useRequest';
|
|||||||
import { MeshAPI } from 'api';
|
import { MeshAPI } from 'api';
|
||||||
import Header from './Header';
|
import Header from './Header';
|
||||||
import MeshGraph from './MeshGraph';
|
import MeshGraph from './MeshGraph';
|
||||||
|
import useZoom from './utils/useZoom';
|
||||||
|
import { CHILDSELECTOR, PARENTSELECTOR } from './constants';
|
||||||
|
|
||||||
function TopologyView() {
|
function TopologyView() {
|
||||||
const [showLegend, setShowLegend] = useState(true);
|
const [showLegend, setShowLegend] = useState(true);
|
||||||
@@ -27,52 +28,11 @@ function TopologyView() {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchMeshVisualizer();
|
fetchMeshVisualizer();
|
||||||
}, [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 (
|
return (
|
||||||
<>
|
<>
|
||||||
<Header
|
<Header
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
export const SELECTOR = '#chart';
|
export const SELECTOR = '#chart';
|
||||||
|
export const PARENTSELECTOR = '.mesh-svg';
|
||||||
|
export const CHILDSELECTOR = '.mesh';
|
||||||
export const MESH_FORCE_LAYOUT = {
|
export const MESH_FORCE_LAYOUT = {
|
||||||
defaultCollisionFactor: 80,
|
defaultCollisionFactor: 80,
|
||||||
defaultForceStrength: -100,
|
defaultForceStrength: -100,
|
||||||
|
|||||||
74
awx/ui/src/screens/TopologyView/utils/useZoom.js
Normal file
74
awx/ui/src/screens/TopologyView/utils/useZoom.js
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
import * as d3 from 'd3';
|
||||||
|
import { getWidth, getHeight } from './helpers';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* useZoom provides a collection of zoom behaviors/functions for D3 graphs
|
||||||
|
* Params: string value of parent and child classnames
|
||||||
|
* The following hierarchy should be followed:
|
||||||
|
* <div id="chart">
|
||||||
|
* <svg><-- parent -->
|
||||||
|
* <g><-- child -->
|
||||||
|
* </svg>
|
||||||
|
* </div>
|
||||||
|
* 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,
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user