mirror of
				https://github.com/datahub-project/datahub.git
				synced 2025-10-31 18:59:23 +00:00 
			
		
		
		
	 843a6c5bbb
			
		
	
	
		843a6c5bbb
		
			
		
	
	
	
	
		
			
			* 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
		
			
				
	
	
		
			215 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			215 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| /**
 | |
|  * Remove inline styles and apply a class
 | |
|  * @param cssClass css class to be applied
 | |
|  * @param el element to remove inline styles
 | |
|  */
 | |
| const removeInlineStylesAndAddClass = (cssClass: string, el: Element | null): void => {
 | |
|   if (el) {
 | |
|     el.removeAttribute('fill');
 | |
|     el.removeAttribute('stroke');
 | |
|     el.removeAttribute('font-size');
 | |
|     el.removeAttribute('font-family');
 | |
|     el.classList.add(cssClass);
 | |
|   }
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Will remove a node but will save the children by appending them
 | |
|  * to the parent element
 | |
|  * @param el element to be removed
 | |
|  */
 | |
| const removeNodeButPreserveChildren = (el: HTMLElement): void => {
 | |
|   const parent = el.parentElement;
 | |
|   if (parent) {
 | |
|     Array.from(el.children).forEach((child): void => {
 | |
|       parent.appendChild(child);
 | |
|     });
 | |
|     parent.removeChild(el);
 | |
|   }
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Simple alias for querySelectorAll and forEach
 | |
|  * @param root Element to apply query
 | |
|  * @param selector query
 | |
|  * @param fn fn to execute on each node
 | |
|  */
 | |
| export const forEachNode = (root: Element | undefined, selector: string, fn: (el: Element) => void): void => {
 | |
|   if (root) {
 | |
|     root.querySelectorAll(selector).forEach(fn);
 | |
|   }
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Process entity node. Will add data-entity-id
 | |
|  * @param node
 | |
|  */
 | |
| const processNode = (node: Element): void => {
 | |
|   const { id } = node;
 | |
|   const [, entityId] = id.split('::');
 | |
| 
 | |
|   node.setAttribute('data-entity-id', entityId);
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Will process title of an entity.
 | |
|  * @param baseClass
 | |
|  */
 | |
| const processNodeTitle = (baseClass: string) => (nodeTitle: Element): void => {
 | |
|   nodeTitle.classList.add(`${baseClass}__title`);
 | |
| 
 | |
|   removeInlineStylesAndAddClass(`${baseClass}__title-bg`, nodeTitle.querySelector('polygon'));
 | |
|   removeInlineStylesAndAddClass(`${baseClass}__title-text`, nodeTitle.querySelector('text'));
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Will process an attribute of an entity
 | |
|  * @param baseClass
 | |
|  */
 | |
| const processNodeAttribute = (baseClass: string) => (nodeAttribute: Element): void => {
 | |
|   const { id } = nodeAttribute;
 | |
|   const [, propertyName, reference] = id.split('::');
 | |
|   const typeElement = nodeAttribute.querySelector('text:nth-child(3)');
 | |
|   const nameElement = nodeAttribute.querySelector('text:nth-child(2)');
 | |
|   const backgroundElement = nodeAttribute.querySelector('polygon');
 | |
| 
 | |
|   nodeAttribute.classList.add(`${baseClass}__property`);
 | |
| 
 | |
|   removeInlineStylesAndAddClass(`${baseClass}__property-bg`, backgroundElement);
 | |
|   removeInlineStylesAndAddClass(`${baseClass}__property-name`, nameElement);
 | |
|   removeInlineStylesAndAddClass(`${baseClass}__property-type`, typeElement);
 | |
| 
 | |
|   if (reference && typeElement) {
 | |
|     typeElement.classList.add(`${baseClass}__property-type--reference`);
 | |
|     typeElement.setAttribute('data-reference-link', reference);
 | |
| 
 | |
|     nodeAttribute.setAttribute('data-reference', reference);
 | |
|     nodeAttribute.setAttribute('data-property-name', propertyName);
 | |
|   }
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Will process an edge
 | |
|  * @param baseClass
 | |
|  */
 | |
| const processEdges = (baseClass: string) => (edge: Element): void => {
 | |
|   const { id } = edge;
 | |
|   const [, from, property, to] = id.split('::');
 | |
| 
 | |
|   edge.setAttribute('data-from', from);
 | |
|   edge.setAttribute('data-property', property);
 | |
|   edge.setAttribute('data-to', to);
 | |
| 
 | |
|   removeInlineStylesAndAddClass(`${baseClass}__connector`, edge.querySelector('path'));
 | |
|   removeInlineStylesAndAddClass(`${baseClass}__connector-arrow`, edge.querySelector('polygon'));
 | |
|   removeInlineStylesAndAddClass(`${baseClass}__connector-text`, edge.querySelector('text'));
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Will process a node toolbar removing extra inline styles and applying a base class
 | |
|  * @param baseClass
 | |
|  */
 | |
| const processNodeToolbar = (baseClass: string) => (toolbar: Element): void => {
 | |
|   toolbar.classList.add(`${baseClass}__toolbar`);
 | |
| 
 | |
|   removeInlineStylesAndAddClass(`${baseClass}__toolbar-bg`, toolbar.querySelector('polygon'));
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Will process a node toolbar action removing extra inline styles and applying a base class
 | |
|  * @param baseClass
 | |
|  */
 | |
| const processNodeToolbarAction = (baseClass: string) => (toolbarAction: Element): void => {
 | |
|   toolbarAction.classList.add(`${baseClass}__action`);
 | |
| 
 | |
|   removeInlineStylesAndAddClass(`${baseClass}__action-bg`, toolbarAction.querySelector('polygon'));
 | |
|   removeInlineStylesAndAddClass(`${baseClass}__action-text`, toolbarAction.querySelector('text'));
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Will process a go to action by adding a data attribute with the data needed to generate a link
 | |
|  * @param baseClass
 | |
|  */
 | |
| const processNodeToolbarActionGoTo = (baseClass: string) => (button: Element): void => {
 | |
|   processNodeToolbarAction(baseClass);
 | |
| 
 | |
|   button.classList.add(`${baseClass}__action-go-to-entity`);
 | |
|   const { id } = button;
 | |
|   const [, to] = id.split('::');
 | |
| 
 | |
|   button.setAttribute('data-entity-urn', to);
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Map of key used in graph to the dom reference of the icon.
 | |
|  */
 | |
| const iconMap: Record<string, string> = {
 | |
|   $1: '#icon-external-link'
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Since GraphViz can't render custom icons, we will use the tag <I> that will generate
 | |
|  * <text style="italic"> tag to replace is with our custom svg icon. See iconMap for icons available
 | |
|  * @param node
 | |
|  */
 | |
| const processIcons = (node: Element): void => {
 | |
|   Object.keys(iconMap).forEach(iconKey => {
 | |
|     if (node.textContent === iconKey) {
 | |
|       const iconSelector = iconMap[iconKey];
 | |
|       const iconSize = 512;
 | |
|       const targetSize = 12;
 | |
|       const x = node.getAttribute('x');
 | |
|       const y = node.getAttribute('y');
 | |
|       const transforms = [
 | |
|         `translate(${iconSize / -2},${iconSize / -2})`,
 | |
|         `translate(${x}, ${y})`,
 | |
|         `translate(${iconSize / 2}, ${iconSize / 2})`,
 | |
|         `scale(${targetSize / iconSize})`,
 | |
|         `translate(0, ${-1 * iconSize})`
 | |
|       ];
 | |
|       const useElement = document.createElementNS('http://www.w3.org/2000/svg', 'use');
 | |
|       useElement.setAttribute('href', iconSelector);
 | |
|       useElement.setAttribute('transform', transforms.join(','));
 | |
|       node.parentElement?.replaceChild(useElement, node);
 | |
|     }
 | |
|   });
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Will 'fix' the svg coming from graphViz. It will make it more suitable for interaction and custom styling
 | |
|  * @param svg
 | |
|  * @param baseClass
 | |
|  */
 | |
| export function processGraphVizSvg(svg: SVGSVGElement, baseClass: string): SVGSVGElement {
 | |
|   // Remove title nodes as they are not useful
 | |
|   forEachNode(svg, 'title', (el): void => el.remove());
 | |
| 
 | |
|   // Remove a but preserve children
 | |
|   forEachNode(svg, 'a', removeNodeButPreserveChildren);
 | |
| 
 | |
|   // Remove prefix 'a_' from ids as GraphViz adds those unexpectly
 | |
|   forEachNode(svg, '[id^="a_"]', (el): void => el.setAttribute('id', el.id.replace('a_', '')));
 | |
| 
 | |
|   // Process nodes root element
 | |
|   forEachNode(svg, '[id^="ENTITY::"]', processNode);
 | |
| 
 | |
|   // Process title of nodes
 | |
|   forEachNode(svg, '[id^="ENTITY-TITLE::"]', processNodeTitle(baseClass));
 | |
| 
 | |
|   // Process attributes of nodes
 | |
|   forEachNode(svg, '[id^="ENTITY-ATTRIBUTE::"]', processNodeAttribute(baseClass));
 | |
| 
 | |
|   // Process edges
 | |
|   forEachNode(svg, '[id^="EDGE::"]', processEdges(baseClass));
 | |
| 
 | |
|   // Process node toolbar
 | |
|   forEachNode(svg, '[id^="ENTITY-ACTIONS::"]', processNodeToolbar(baseClass));
 | |
| 
 | |
|   // Process action 'go to entity'
 | |
|   forEachNode(svg, '[id^="ENTITY-ACTION-GO-TO-ENTITY::"]', processNodeToolbarActionGoTo(baseClass));
 | |
| 
 | |
|   // Generate icons
 | |
|   forEachNode(svg, 'text[font-style="italic"]', processIcons);
 | |
|   return svg;
 | |
| }
 |