diff --git a/src/components/network-area-diagram-viewer/dynamic-css-utils.ts b/src/components/network-area-diagram-viewer/dynamic-css-utils.ts index 31017b96..07458d4c 100644 --- a/src/components/network-area-diagram-viewer/dynamic-css-utils.ts +++ b/src/components/network-area-diagram-viewer/dynamic-css-utils.ts @@ -82,4 +82,34 @@ export const DEFAULT_DYNAMIC_CSS_RULES: CSS_RULE[] = [ threshold: 20000, thresholdStatus: THRESHOLD_STATUS.BELOW, }, + // TODO using svg at the begining in selectors to increase specificity + // because the svg already contains the same selectors. Is there a better way ? + { + cssSelector: 'svg .nad-disconnected .nad-edge-path', + belowThresholdCssDeclaration: { 'stroke-dasharray': '10, 10' }, + aboveThresholdCssDeclaration: { 'stroke-dasharray': '0.5%, 0.5%' }, + threshold: 2500, + thresholdStatus: THRESHOLD_STATUS.ABOVE, + }, + { + cssSelector: 'svg .nad-branch-edges .nad-edge-path, svg .nad-3wt-edges .nad-edge-path', + belowThresholdCssDeclaration: { 'stroke-width': '3' }, + aboveThresholdCssDeclaration: { 'stroke-width': '0.25%' }, + threshold: 1000, + thresholdStatus: THRESHOLD_STATUS.ABOVE, + }, + { + cssSelector: 'svg .nad-branch-edges .nad-winding, svg .nad-3wt-nodes .nad-winding', + belowThresholdCssDeclaration: { 'stroke-width': '3' }, + aboveThresholdCssDeclaration: { 'stroke-width': '0.25%' }, + threshold: 1000, + thresholdStatus: THRESHOLD_STATUS.ABOVE, + }, + { + cssSelector: 'svg .nad-vl-nodes circle.nad-unknown-busnode', + belowThresholdCssDeclaration: { 'stroke-width': '3' }, + aboveThresholdCssDeclaration: { 'stroke-width': '0.25%' }, + threshold: 1000, + thresholdStatus: THRESHOLD_STATUS.ABOVE, + }, ]; 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 00830257..911802be 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 @@ -48,6 +48,18 @@ export type OnToggleNadHoverCallbackType = ( equipmentType: string ) => void; +// update css rules when zoom changes by this amount. This allows to not +// update when only translating (when translating, round errors lead to +// epsilon changes in the float values), or not too often a bit when smooth +// scrolling (the update may be entirely missed when smooth scrolling if +// you don't go over the threshold but that's ok, the user doesn't see rule +// threshold values so he will continue to zoom in or out to trigger the +// rule update. Using a debounce that ensure the last update is done +// eventually may be even worse as it could introduce flicker after the +// delay after the last zoom change. We need a value that gives good +// performance but doesn't change the user experience +const dynamicCssRulesUpdateThreshold = 0.01; + export class NetworkAreaDiagramViewer { container: HTMLElement; svgContent: string; @@ -73,6 +85,7 @@ export class NetworkAreaDiagramViewer { onSelectNodeCallback: OnSelectNodeCallbackType | null; dynamicCssRules: CSS_RULE[]; onToggleHoverCallback: OnToggleNadHoverCallbackType | null; + previousMaxDisplayedSize: number; constructor( container: HTMLElement, @@ -113,6 +126,7 @@ export class NetworkAreaDiagramViewer { this.onMoveTextNodeCallback = onMoveTextNodeCallback; this.onSelectNodeCallback = onSelectNodeCallback; this.onToggleHoverCallback = onToggleHoverCallback; + this.previousMaxDisplayedSize = 0; } public setWidth(width: number): void { @@ -171,6 +185,14 @@ export class NetworkAreaDiagramViewer { this.svgDraw?.viewbox(viewBox); } + public setPreviousMaxDisplayedSize(previousMaxDisplayedSize: number): void { + this.previousMaxDisplayedSize = previousMaxDisplayedSize; + } + + public getPreviousMaxDisplayedSize(): number { + return this.previousMaxDisplayedSize; + } + public getDynamicCssRules() { return this.dynamicCssRules; } @@ -1371,6 +1393,15 @@ export class NetworkAreaDiagramViewer { public checkAndUpdateLevelOfDetail(svg: SVGSVGElement) { const maxDisplayedSize = this.getCurrentlyMaxDisplayedSize(); + const previousMaxDisplayedSize = this.getPreviousMaxDisplayedSize(); + // in case of bad or unset values NaN or Infinity, this condition is skipped and the function behaves as if zoom changed + if ( + Math.abs(previousMaxDisplayedSize - maxDisplayedSize) / previousMaxDisplayedSize < + dynamicCssRulesUpdateThreshold + ) { + return; + } + this.setPreviousMaxDisplayedSize(maxDisplayedSize); // We will check each dynamic css rule to see if we crossed a zoom threshold. If this is the case, we // update the rule's threshold status and trigger the CSS change in the SVG. this.getDynamicCssRules().forEach((rule) => { @@ -1388,5 +1419,33 @@ export class NetworkAreaDiagramViewer { this.updateSvgCssDisplayValue(svg, rule.cssSelector, rule.aboveThresholdCssDeclaration); } }); + //Workaround chromium (tested on edge and google-chrome 131) doesn't + //redraw things with percentages on viewbox changes but it should, so + //we force it. This is not strictly related to the enableLevelOfDetail + //and dynamic css feature, but it turns out that we use percentages in + //css only in the case where enableLevelOfDetail=true, so we can do the + //workaround here at each viewbox change until we have other needs or + //until we remove the workaround entirely. Firefox does correctly + //redraw, but we force for everyone to have the same behavior + //everywhere and detect problems more easily. We can't use + //innerHtml+='' on the