Charlie Tran 843a6c5bbb
feat(frontend): update datahub-web client UI code (#1806)
* 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
2020-08-26 15:44:50 -07:00

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);
}