mirror of
https://github.com/datahub-project/datahub.git
synced 2025-08-11 10:46:52 +00:00

* Releases updated version of datahub-web client UI code * Fix typo in yarn lock * Change yarn lock to match yarn registry directories * Previous commit missed some paths * Even more changes to yarnlock missing in previous commit * Include codegen file for typings * Add files to get parity for datahub-web and current OS datahub-midtier * Add in typo fix from previous commit - change to proper license * Implement proper OS fix for person entity picture url * Workarounds for open source DH issues * Fixes institutional memory api and removes unopensourced tabs for datasets * Fixes search dataset deprecation and user search issue as a result of changes * Remove internal only options in the avatar menu
141 lines
4.0 KiB
TypeScript
141 lines
4.0 KiB
TypeScript
import animate from '@f/animate';
|
|
import { begin as beginLoop, end as endLoop } from '@ember/runloop';
|
|
|
|
/**
|
|
* Animate wrapper that is ember runloop friendly and promise friendly
|
|
* @param start
|
|
* @param end
|
|
* @param fn
|
|
*/
|
|
function animateWrapper<T>(start: T, end: T, fn: (props: T, step: number) => void): Promise<void> {
|
|
// Because animate actually uses RAF, run is not sufficient to make ember aware of this.
|
|
beginLoop();
|
|
return new Promise((success): void => {
|
|
animate(start, end, (props: T, step: number): void => {
|
|
fn(props, step);
|
|
|
|
if (props === end) {
|
|
success();
|
|
endLoop();
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Will transition and zoom into a position in the svg.
|
|
*
|
|
* This fn was extracted from https://github.com/APIs-guru/graphql-voyager/blob/b96ef1387c5f64867db338c1cfc3b070ebdd648a/src/graph/viewport.ts
|
|
*
|
|
* @param x
|
|
* @param y
|
|
* @param zoomEnd
|
|
* @param zoomer
|
|
*/
|
|
export async function animatePanAndZoom(
|
|
x: number,
|
|
y: number,
|
|
zoomEnd: number,
|
|
zoomer: SvgPanZoom.Instance
|
|
): Promise<void> {
|
|
const pan = zoomer.getPan();
|
|
const panEnd = { x, y };
|
|
await animateWrapper(pan, panEnd, (props): void => {
|
|
zoomer.pan({ x: props.x, y: props.y });
|
|
});
|
|
|
|
const zoom = zoomer.getZoom();
|
|
await animateWrapper({ zoom }, { zoom: zoomEnd }, (props): void => {
|
|
zoomer.zoom(props.zoom);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Internal interface for focusElement for describing a DOMRect with different approach
|
|
*/
|
|
interface ICoords {
|
|
// X start of rect
|
|
x1: number;
|
|
// X + width, end of rect
|
|
x2: number;
|
|
// y start of rect
|
|
y1: number;
|
|
// y + height, end of rect
|
|
y2: number;
|
|
}
|
|
|
|
/**
|
|
* Will transition and focus on multiple elements inside a svg
|
|
*
|
|
* extracted from https://github.com/APIs-guru/graphql-voyager/blob/b96ef1387c5f64867db338c1cfc3b070ebdd648a/src/graph/viewport.ts
|
|
* @param el
|
|
* @param svg
|
|
* @param zoomer
|
|
* @param maxZoom
|
|
*/
|
|
export async function focusElements(
|
|
elements: Array<Element>,
|
|
svg: Element,
|
|
zoomer: SvgPanZoom.Instance,
|
|
maxZoom: number,
|
|
animate = true
|
|
): Promise<void> {
|
|
const bbRect = svg.getBoundingClientRect();
|
|
const offsetLeft = bbRect.left;
|
|
const offsetTop = bbRect.top;
|
|
const bbBoxes = elements.map(el => el.getBoundingClientRect());
|
|
const currentPan = zoomer.getPan();
|
|
const viewPortSizes = zoomer.getSizes();
|
|
const toCoord = (bbBox: DOMRect): ICoords => ({
|
|
x1: bbBox.x,
|
|
x2: bbBox.x + bbBox.width,
|
|
y1: bbBox.y,
|
|
y2: bbBox.y + bbBox.height
|
|
});
|
|
const size = bbBoxes.reduce((sizes: ICoords | undefined, bbBox: DOMRect): ICoords => {
|
|
const coords = toCoord(bbBox);
|
|
return sizes
|
|
? {
|
|
x1: Math.min(sizes.x1, coords.x1),
|
|
x2: Math.max(sizes.x2, coords.x2),
|
|
y1: Math.min(sizes.y1, coords.y1),
|
|
y2: Math.max(sizes.y2, coords.y2)
|
|
}
|
|
: coords;
|
|
}, undefined) || { x1: 0, x2: 0, y1: 0, y2: 0 };
|
|
const width = size?.x2 - size?.x1;
|
|
const height = size?.y2 - size?.y1;
|
|
|
|
currentPan.x += viewPortSizes.width / 2 - width / 2;
|
|
currentPan.y += viewPortSizes.height / 2 - height / 2;
|
|
|
|
const zoomUpdateToFit = 1.2 * Math.max(height / viewPortSizes.height, width / viewPortSizes.width);
|
|
let newZoom = zoomer.getZoom() / zoomUpdateToFit;
|
|
const recomendedZoom = maxZoom * 0.6;
|
|
if (newZoom > recomendedZoom) newZoom = recomendedZoom;
|
|
|
|
const newX = currentPan.x - size.x1 + offsetLeft;
|
|
const newY = currentPan.y - size.y1 + offsetTop;
|
|
if (animate) {
|
|
await animatePanAndZoom(newX, newY, newZoom, zoomer);
|
|
} else {
|
|
zoomer.pan({ x: newX, y: newY });
|
|
zoomer.zoom(newZoom);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Will calculate the max zoom for a given svg
|
|
*
|
|
* Inspired in maxZoom fn from https://github.com/APIs-guru/graphql-voyager/blob/b96ef1387c5f64867db338c1cfc3b070ebdd648a/src/graph/viewport.ts
|
|
* @param svgEl
|
|
*/
|
|
export function calculateMaxZoom(svgEl: SVGSVGElement): number {
|
|
const svgWidth = svgEl.width.baseVal.value;
|
|
const svgHeight = svgEl.height.baseVal.value;
|
|
const constantWidth = 1800;
|
|
const constantHeight = 1100;
|
|
|
|
return Math.max(svgWidth / constantWidth, svgHeight / constantHeight);
|
|
}
|