From bd3ba1c7117031a8b838a414b4dd83c1da171b4d Mon Sep 17 00:00:00 2001 From: "massimo.ferraro" Date: Fri, 26 Jul 2024 12:44:22 +0200 Subject: [PATCH 1/2] Allow to select nodes in network area diagram viewer Signed-off-by: massimo.ferraro --- demo/src/diagram-viewers/add-diagrams.ts | 37 ++++++++- .../network-area-diagram-viewer.test.ts | 1 + .../network-area-diagram-viewer.ts | 79 +++++++++++++++---- src/index.ts | 4 + 4 files changed, 103 insertions(+), 18 deletions(-) diff --git a/demo/src/diagram-viewers/add-diagrams.ts b/demo/src/diagram-viewers/add-diagrams.ts index 68fc9361..b0382c32 100644 --- a/demo/src/diagram-viewers/add-diagrams.ts +++ b/demo/src/diagram-viewers/add-diagrams.ts @@ -24,6 +24,8 @@ import { OnBusCallbackType, OnFeederCallbackType, OnNextVoltageCallbackType, + OnMoveNodeCallbackType, + OnSelectNodeCallbackType, } from '../../../src'; export const addNadToDemo = () => { @@ -38,6 +40,7 @@ export const addNadToDemo = () => { 1000, 1200, handleNodeMove, + handleNodeSelect, true ); @@ -58,6 +61,7 @@ export const addNadToDemo = () => { 1000, 1200, handleNodeMove, + handleNodeSelect, false ); @@ -78,6 +82,7 @@ export const addNadToDemo = () => { 1000, 1200, handleNodeMove, + handleNodeSelect, true ); @@ -100,6 +105,7 @@ export const addNadToDemo = () => { 1000, 1200, handleNodeMove, + handleNodeSelect, true ); @@ -120,6 +126,7 @@ export const addNadToDemo = () => { 1000, 1200, handleNodeMove, + handleNodeSelect, true ); @@ -140,6 +147,7 @@ export const addNadToDemo = () => { 1000, 1200, handleNodeMove, + handleNodeSelect, true ); @@ -160,6 +168,7 @@ export const addNadToDemo = () => { 1000, 1200, handleNodeMove, + handleNodeSelect, true ); @@ -307,7 +316,14 @@ const handleTogglePopover: HandleTogglePopoverType = ( } }; -const handleNodeMove = (equipmentId, nodeId, x, y, xOrig, yOrig) => { +const handleNodeMove: OnMoveNodeCallbackType = ( + equipmentId, + nodeId, + x, + y, + xOrig, + yOrig +) => { const msg = 'Node ' + nodeId + @@ -324,3 +340,22 @@ const handleNodeMove = (equipmentId, nodeId, x, y, xOrig, yOrig) => { ']'; console.log(msg); }; + +const handleNodeSelect: OnSelectNodeCallbackType = ( + equipmentId, + nodeId, + x, + y +) => { + const msg = + 'Node ' + + nodeId + + ' equipment ' + + equipmentId + + ' position [' + + x + + ', ' + + y + + '] selected'; + console.log(msg); +}; diff --git a/src/components/network-area-diagram-viewer/network-area-diagram-viewer.test.ts b/src/components/network-area-diagram-viewer/network-area-diagram-viewer.test.ts index 7e1a3e3d..f629dd51 100644 --- a/src/components/network-area-diagram-viewer/network-area-diagram-viewer.test.ts +++ b/src/components/network-area-diagram-viewer/network-area-diagram-viewer.test.ts @@ -20,6 +20,7 @@ describe('Test network-area-diagram-viewer', () => { 0, 0, null, + null, false ); diff --git a/src/components/network-area-diagram-viewer/network-area-diagram-viewer.ts b/src/components/network-area-diagram-viewer/network-area-diagram-viewer.ts index a9100f18..7697e04b 100644 --- a/src/components/network-area-diagram-viewer/network-area-diagram-viewer.ts +++ b/src/components/network-area-diagram-viewer/network-area-diagram-viewer.ts @@ -22,6 +22,13 @@ export type OnMoveNodeCallbackType = ( yOrig: number ) => void; +export type OnSelectNodeCallbackType = ( + equipmentId: string, + nodeId: string, + x: number, + y: number +) => void; + export class NetworkAreaDiagramViewer { container: HTMLElement; svgContent: string; @@ -37,7 +44,8 @@ export class NetworkAreaDiagramViewer { initialPosition: Point = new Point(0, 0); svgParameters: SvgParameters; edgeAngles: Map = new Map(); - onNodeCallback: OnMoveNodeCallbackType | null; + onMoveNodeCallback: OnMoveNodeCallbackType | null; + onSelectNodeCallback: OnSelectNodeCallbackType | null; constructor( container: HTMLElement, @@ -46,7 +54,8 @@ export class NetworkAreaDiagramViewer { minHeight: number, maxWidth: number, maxHeight: number, - onNodeCallback: OnMoveNodeCallbackType | null, + onMoveNodeCallback: OnMoveNodeCallbackType | null, + onSelectNodeCallback: OnSelectNodeCallbackType | null, enableNodeMoving: boolean ) { this.container = container; @@ -57,7 +66,8 @@ export class NetworkAreaDiagramViewer { this.originalHeight = 0; this.init(minWidth, minHeight, maxWidth, maxHeight, enableNodeMoving); this.svgParameters = this.getSvgParameters(); - this.onNodeCallback = onNodeCallback; + this.onMoveNodeCallback = onMoveNodeCallback; + this.onSelectNodeCallback = onSelectNodeCallback; } public setWidth(width: number): void { @@ -272,27 +282,41 @@ export class NetworkAreaDiagramViewer { if (svg != null) { svg.style.cursor = 'grabbing'; } - this.initialPosition = DiagramUtils.getPosition(this.selectedElement); // used for the offset - this.edgeAngles = new Map(); + if (!(event as MouseEvent).shiftKey) { + this.initialPosition = DiagramUtils.getPosition( + this.selectedElement + ); // used for the offset + this.edgeAngles = new Map(); + } } private drag(event: Event) { if (this.selectedElement) { event.preventDefault(); - this.ctm = this.svgDraw?.node.getScreenCTM(); - const mousePosition = this.getMousePosition(event as MouseEvent); - this.updateGraph(mousePosition); - this.initialPosition = DiagramUtils.getPosition( - this.selectedElement - ); + if (!(event as MouseEvent).shiftKey) { + this.ctm = this.svgDraw?.node.getScreenCTM(); + const mousePosition = this.getMousePosition( + event as MouseEvent + ); + this.updateGraph(mousePosition); + this.initialPosition = DiagramUtils.getPosition( + this.selectedElement + ); + } } } private endDrag(event: Event) { if (this.selectedElement) { - const mousePosition = this.getMousePosition(event as MouseEvent); - this.updateGraph(mousePosition); - this.updateMetadataCallCallback(mousePosition); + if (!(event as MouseEvent).shiftKey) { + const mousePosition = this.getMousePosition( + event as MouseEvent + ); + this.updateGraph(mousePosition); + this.updateMetadataCallCallback(mousePosition); + } else { + this.callSelectNodeCallback(); + } const svg: HTMLElement = ( this.svgDraw?.node.firstElementChild?.parentElement ); @@ -1276,9 +1300,9 @@ export class NetworkAreaDiagramViewer { // update node position in metadata node.setAttribute('x', xNew); node.setAttribute('y', yNew); - // call the callback, if defined - if (this.onNodeCallback != null) { - this.onNodeCallback( + // call the move node callback, if defined + if (this.onMoveNodeCallback != null) { + this.onMoveNodeCallback( node.getAttribute('equipmentid') ?? '', node.getAttribute('svgid') ?? '', +xNew, @@ -1289,4 +1313,25 @@ export class NetworkAreaDiagramViewer { } } } + + private callSelectNodeCallback() { + // call the select node callback, if defined + if (this.onSelectNodeCallback != null) { + // get selected node from metadata + const node: SVGGraphicsElement | null = + this.container.querySelector( + 'nad\\:node[svgid="' + this.selectedElement?.id + '"]' + ); + if (node != null) { + const x = node.getAttribute('x') ?? '0'; + const y = node.getAttribute('y') ?? '0'; + this.onSelectNodeCallback( + node.getAttribute('equipmentid') ?? '', + node.getAttribute('svgid') ?? '', + +x, + +y + ); + } + } + } } diff --git a/src/index.ts b/src/index.ts index 4bfcb4b7..d12f9fac 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,6 +6,10 @@ */ export { NetworkAreaDiagramViewer } from './components/network-area-diagram-viewer/network-area-diagram-viewer'; +export type { + OnMoveNodeCallbackType, + OnSelectNodeCallbackType, +} from './components/network-area-diagram-viewer/network-area-diagram-viewer'; export { SingleLineDiagramViewer } from './components/single-line-diagram-viewer/single-line-diagram-viewer'; export type { HandleTogglePopoverType, From d346cb0daad4c387627a583bfb805a4c1fe14f99 Mon Sep 17 00:00:00 2001 From: "massimo.ferraro" Date: Fri, 2 Aug 2024 16:43:25 +0200 Subject: [PATCH 2/2] Refactoring after review Signed-off-by: massimo.ferraro --- demo/src/diagram-viewers/add-diagrams.ts | 18 ++-------- .../network-area-diagram-viewer.ts | 36 +++++++++++-------- 2 files changed, 23 insertions(+), 31 deletions(-) diff --git a/demo/src/diagram-viewers/add-diagrams.ts b/demo/src/diagram-viewers/add-diagrams.ts index b0382c32..09d26cee 100644 --- a/demo/src/diagram-viewers/add-diagrams.ts +++ b/demo/src/diagram-viewers/add-diagrams.ts @@ -341,21 +341,7 @@ const handleNodeMove: OnMoveNodeCallbackType = ( console.log(msg); }; -const handleNodeSelect: OnSelectNodeCallbackType = ( - equipmentId, - nodeId, - x, - y -) => { - const msg = - 'Node ' + - nodeId + - ' equipment ' + - equipmentId + - ' position [' + - x + - ', ' + - y + - '] selected'; +const handleNodeSelect: OnSelectNodeCallbackType = (equipmentId, nodeId) => { + const msg = 'Node ' + nodeId + ' equipment ' + equipmentId + ' selected'; console.log(msg); }; diff --git a/src/components/network-area-diagram-viewer/network-area-diagram-viewer.ts b/src/components/network-area-diagram-viewer/network-area-diagram-viewer.ts index 7697e04b..466b5da4 100644 --- a/src/components/network-area-diagram-viewer/network-area-diagram-viewer.ts +++ b/src/components/network-area-diagram-viewer/network-area-diagram-viewer.ts @@ -24,9 +24,7 @@ export type OnMoveNodeCallbackType = ( export type OnSelectNodeCallbackType = ( equipmentId: string, - nodeId: string, - x: number, - y: number + nodeId: string ) => void; export class NetworkAreaDiagramViewer { @@ -46,6 +44,7 @@ export class NetworkAreaDiagramViewer { edgeAngles: Map = new Map(); onMoveNodeCallback: OnMoveNodeCallbackType | null; onSelectNodeCallback: OnSelectNodeCallbackType | null; + shiftKeyOnMouseDown: boolean = false; constructor( container: HTMLElement, @@ -273,9 +272,9 @@ export class NetworkAreaDiagramViewer { if (!draggableElem) { return; } - this.disablePanzoom(); // to avoid panning the whole SVG when moving a node - this.ctm = this.svgDraw?.node.getScreenCTM(); // used to compute mouse movement - this.selectedElement = draggableElem as SVGGraphicsElement; // element to be moved + this.disablePanzoom(); // to avoid panning the whole SVG when moving or selecting a node + this.selectedElement = draggableElem as SVGGraphicsElement; // element to be moved or selected + // change cursor style const svg: HTMLElement = ( this.svgDraw?.node.firstElementChild?.parentElement ); @@ -283,18 +282,25 @@ export class NetworkAreaDiagramViewer { svg.style.cursor = 'grabbing'; } if (!(event as MouseEvent).shiftKey) { + // moving node + this.shiftKeyOnMouseDown = false; + this.ctm = this.svgDraw?.node.getScreenCTM(); // used to compute mouse movement this.initialPosition = DiagramUtils.getPosition( this.selectedElement ); // used for the offset this.edgeAngles = new Map(); + } else { + // selecting node + this.shiftKeyOnMouseDown = true; } } private drag(event: Event) { if (this.selectedElement) { event.preventDefault(); - if (!(event as MouseEvent).shiftKey) { - this.ctm = this.svgDraw?.node.getScreenCTM(); + if (!this.shiftKeyOnMouseDown) { + // moving node + this.ctm = this.svgDraw?.node.getScreenCTM(); // used to compute mouse movement const mousePosition = this.getMousePosition( event as MouseEvent ); @@ -308,15 +314,20 @@ export class NetworkAreaDiagramViewer { private endDrag(event: Event) { if (this.selectedElement) { - if (!(event as MouseEvent).shiftKey) { + if (!this.shiftKeyOnMouseDown) { + // moving node const mousePosition = this.getMousePosition( event as MouseEvent ); this.updateGraph(mousePosition); this.updateMetadataCallCallback(mousePosition); + this.initialPosition = new Point(0, 0); } else { + // selecting node this.callSelectNodeCallback(); + this.shiftKeyOnMouseDown = false; } + // change cursor style const svg: HTMLElement = ( this.svgDraw?.node.firstElementChild?.parentElement ); @@ -324,7 +335,6 @@ export class NetworkAreaDiagramViewer { svg.style.removeProperty('cursor'); } this.selectedElement = null; - this.initialPosition = new Point(0, 0); this.enablePanzoom(); } } @@ -1323,13 +1333,9 @@ export class NetworkAreaDiagramViewer { 'nad\\:node[svgid="' + this.selectedElement?.id + '"]' ); if (node != null) { - const x = node.getAttribute('x') ?? '0'; - const y = node.getAttribute('y') ?? '0'; this.onSelectNodeCallback( node.getAttribute('equipmentid') ?? '', - node.getAttribute('svgid') ?? '', - +x, - +y + node.getAttribute('svgid') ?? '' ); } }