diff --git a/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkLayer.tsx b/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkLayer.tsx index 81f943b883c908aadc27263e22273821529e041d..64a608dc6a1c468ad7aca0e7aad20c0ef77c7b9b 100644 --- a/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkLayer.tsx +++ b/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkLayer.tsx @@ -32,7 +32,7 @@ export class NodeLinkLayer extends CompositeLayer<CompositeLayerType> { this._layers[layerId] = new LineLayer({ id: layerId, data: edgeData, - visible: !layerSettings.edges[label].hidden, + visible: !layerSettings?.edges[label]?.hidden, pickable: true, getWidth: layerSettings.edges[label].width, getSourcePosition: (d) => getNodeLocation(d.from), @@ -76,26 +76,34 @@ export class NodeLinkLayer extends CompositeLayer<CompositeLayerType> { }); }); - const textLayerId = 'label-target'; + if (layerSettings?.showLabels) { + const textLayerId = 'label-target'; - this._layers[textLayerId] = new TextLayer({ - id: textLayerId, - data: data.nodes, - getPosition: (d: any) => getNodeLocation(d._id), - getText: (d: any) => d.id, - getSize: 15, - visible: true, - getAlignmentBaseline: 'top', - background: true, - getPixelOffset: [10, 10], - extensions: [collisionFilter], - collisionEnabled: true, - getCollisionPriority: (d: any) => d.id, - collisionTestProps: { sizeScale: 10 }, - getRadius: 10, - radiusUnits: 'pixels', - collisionGroup: 'text', - }); + this._layers[textLayerId] = new TextLayer({ + id: textLayerId, + data: data.nodes, + getPosition: (d) => getNodeLocation(d._id), + getText: (d) => `${d.attributes[layerSettings?.labelAttribute] ?? d._id}`, + getSize: 20, + visible: true, + getAlignmentBaseline: 'top', + background: true, + getRadius: 10, + radiusUnits: 'pixels', + characterSet: 'auto', + fontFamily: 'monospace', + billboard: false, + getAngle: () => 0, + collisionGroup: 'textLabels', + extensions: [collisionFilter], + collisionEnabled: layerSettings?.collisionEnabled ?? true, + getCollisionPriority: () => 100, + collisionTestProps: { sizeScale: layerSettings?.labelSpace ?? 5 }, + updateTriggers: { + getText: [layerSettings.labelAttribute], + }, + }); + } return Object.values(this._layers); } diff --git a/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx b/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx index 306774b916bb3d26875f68e5f42164826024ee72..cf1f304dea7a7eab1f3609621d4fafb1e8473dfc 100644 --- a/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx +++ b/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx @@ -1,4 +1,4 @@ -import React, { useEffect } from 'react'; +import React, { useEffect, useMemo } from 'react'; import ColorPicker from '@graphpolaris/shared/lib/components/colorComponents/colorPicker'; import { Button, DropdownColorLegend, EntityPill, Icon, Input, RelationPill } from '@graphpolaris/shared/lib/components'; import { MapProps } from '../../mapvis'; @@ -16,7 +16,15 @@ export function NodeLinkOptions({ useEffect(() => { if (!layerSettings) { - const initialSettingsObject = { enableBrushing: false, nodes: {}, edges: {} }; + const initialSettingsObject = { + showLabels: true, + collisionEnabled: true, + labelSpace: 5, + layerSettings: '_id', + enableBrushing: false, + nodes: {}, + edges: {}, + }; graphMetadata.nodes.labels.forEach((node) => { initialSettingsObject.nodes = { @@ -50,6 +58,19 @@ export function NodeLinkOptions({ } }, [graphMetadata, settings, updateLayerSettings]); + const commonNodeAttributes = useMemo(() => { + const nodeTypes = Object.keys(graphMetadata.nodes.types); + if (nodeTypes.length === 0) return []; + + return nodeTypes.reduce( + (commonAttrs, nodeType) => { + const attributes = Object.keys(graphMetadata.nodes.types[nodeType]?.attributes || {}); + return commonAttrs.filter((attr) => attributes.includes(attr)); + }, + Object.keys(graphMetadata.nodes.types[nodeTypes[0]].attributes || {}), + ); + }, [graphMetadata.nodes.types]); + const handleCollapseToggle = (type: string, itemType: 'nodes' | 'edges') => { if (layerSettings) { updateLayerSettings({ @@ -67,6 +88,41 @@ export function NodeLinkOptions({ return ( layerSettings && ( <div> + <Input + label="Show labels" + type="boolean" + value={layerSettings?.showLabels} + onChange={(val) => updateLayerSettings({ showLabels: val as boolean })} + /> + + <Input + inline + label="Label text" + type="dropdown" + value={layerSettings?.labelAttribute} + options={[...commonNodeAttributes]} + disabled={!layerSettings?.showLabels} + onChange={(val) => updateLayerSettings({ labelAttribute: val as string })} + /> + + <Input + label="Prevent collision" + type="boolean" + value={layerSettings?.collisionEnabled} + onChange={(val) => updateLayerSettings({ collisionEnabled: val as boolean })} + /> + {layerSettings?.collisionEnabled && ( + <Input + label="Label space" + type="slider" + min={0} + max={10} + step={0.5} + value={layerSettings?.labelSpace} + onChange={(val) => updateLayerSettings({ labelSpace: val })} + /> + )} + {graphMetadata.nodes.labels.map((nodeType) => { const nodeSettings = layerSettings?.nodes?.[nodeType] || {};