grafana/public/app/plugins/panel/nodeGraph/useZoom.ts
Andrej Ocenas fdd6620d0a
NodeGraph: Exploration mode (#33623)
* Add exploration option to node layout

* Add hidden node count

* Add grid layout option

* Fix panning bounds calculation

* Add legend with sorting

* Allow sorting on any stats or arc value

* Fix merge

* Make sorting better

* Reset focused node on layout change

* Refactor limit hook a bit

* Disable selected layout button

* Don't show markers if only 1 node is hidden

* Move legend to the bottom

* Fix text backgrounds

* Add show in graph layout action in grid layout

* Center view on the focused node, fix perf issue when expanding big graph

* Limit the node counting

* Comment and linting fixes

* Bit of code cleanup and comments

* Add state for computing layout

* Prevent computing map with partial data

* Add rollup plugin for worker

* Add rollup plugin for worker

* Enhance data from worker

* Fix perf issues with reduce and object creation

* Improve comment

* Fix tests

* Css fixes

* Remove worker plugin

* Add comments

* Fix test

* Add test for exploration

* Add test switching to grid layout

* Apply suggestions from code review

Co-authored-by: Zoltán Bedi <zoltan.bedi@gmail.com>

* Remove unused plugin

* Fix function name

* Remove unused rollup plugin

* Review fixes

* Fix context menu shown on layout change

* Make buttons bigger

* Moved NodeGraph to core grafana

Co-authored-by: Zoltán Bedi <zoltan.bedi@gmail.com>
2021-05-12 16:04:21 +02:00

92 lines
2.6 KiB
TypeScript

import { useCallback, useEffect, useRef, useState } from 'react';
const defaultOptions: Options = {
stepDown: (s) => s / 1.5,
stepUp: (s) => s * 1.5,
min: 0.13,
max: 2.25,
};
interface Options {
/**
* Allows you to specify how the step up will be handled so you can do fractional steps based on previous value.
*/
stepUp: (scale: number) => number;
stepDown: (scale: number) => number;
/**
* Set max and min values. If stepUp/down overshoots these bounds this will return min or max but internal scale value
* will still be what ever the step functions returned last.
*/
min?: number;
max?: number;
}
/**
* Keeps state and returns handlers that can be used to implement zooming functionality ideally by using it with
* 'transform: scale'. It returns handler for manual buttons with zoom in/zoom out function and a ref that can be
* used to zoom in/out with mouse wheel.
*/
export function useZoom({ stepUp, stepDown, min, max } = defaultOptions) {
const ref = useRef<HTMLElement>(null);
const [scale, setScale] = useState(1);
const onStepUp = useCallback(() => {
if (scale < (max ?? Infinity)) {
setScale(stepUp(scale));
}
}, [scale, stepUp, max]);
const onStepDown = useCallback(() => {
if (scale > (min ?? -Infinity)) {
setScale(stepDown(scale));
}
}, [scale, stepDown, min]);
const onWheel = useCallback(
function (event: Event) {
// Seems like typing for the addEventListener is lacking a bit
const wheelEvent = event as WheelEvent;
// Only do this with special key pressed similar to how google maps work.
// TODO: I would guess this won't work very well with touch right now
if (wheelEvent.ctrlKey || wheelEvent.metaKey) {
event.preventDefault();
if (wheelEvent.deltaY < 0) {
onStepUp();
} else if (wheelEvent.deltaY > 0) {
onStepDown();
}
}
},
[onStepDown, onStepUp]
);
useEffect(() => {
if (!ref.current) {
return;
}
const zoomRef = ref.current;
// Adds listener for wheel event, we need the passive: false to be able to prevent default otherwise that
// cannot be used with passive listeners.
zoomRef.addEventListener('wheel', onWheel, { passive: false });
return () => {
if (zoomRef) {
zoomRef.removeEventListener('wheel', onWheel);
}
};
}, [onWheel]);
return {
onStepUp,
onStepDown,
scale: Math.max(Math.min(scale, max ?? Infinity), min ?? -Infinity),
isMax: scale >= (max ?? Infinity),
isMin: scale <= (min ?? -Infinity),
ref,
};
}