From bf5100d4c839872edbea09c056655d3153a8504a Mon Sep 17 00:00:00 2001 From: Sheik Althaf Date: Sat, 17 Feb 2024 19:40:32 +0530 Subject: [PATCH] fix: sonar issue fixes and address comments --- projects/flow/src/lib/flow-child.component.ts | 2 - projects/flow/src/lib/flow-interface.ts | 15 -- projects/flow/src/lib/flow.component.ts | 5 +- projects/flow/src/lib/flow.service.ts | 8 +- .../flow/src/lib/plugins/arrangements.spec.ts | 29 +--- projects/flow/src/lib/plugins/arrangements.ts | 137 +----------------- .../flow/src/lib/plugins/connections.spec.ts | 14 +- projects/flow/src/lib/plugins/connections.ts | 63 ++++---- .../flow/src/lib/plugins/fit-to-window.ts | 3 +- projects/flow/src/lib/plugins/plugin.ts | 14 ++ .../flow/src/lib/plugins/scroll-into-view.ts | 3 +- projects/flow/src/public-api.ts | 8 +- src/app/demo/demo-one.component.ts | 4 - 13 files changed, 59 insertions(+), 246 deletions(-) create mode 100644 projects/flow/src/lib/plugins/plugin.ts diff --git a/projects/flow/src/lib/flow-child.component.ts b/projects/flow/src/lib/flow-child.component.ts index 914505c..d681c75 100644 --- a/projects/flow/src/lib/flow-child.component.ts +++ b/projects/flow/src/lib/flow-child.component.ts @@ -95,8 +95,6 @@ export class FlowChildComponent implements OnInit, OnChanges, OnDestroy { }); this.positionChange.subscribe((x) => { - // const { left, top } = this.flow.zRect; - // if (!this.position) console.log(this.position); this.updatePosition(this.position.x, this.position.y); }); } diff --git a/projects/flow/src/lib/flow-interface.ts b/projects/flow/src/lib/flow-interface.ts index 162c6ed..335edc2 100644 --- a/projects/flow/src/lib/flow-interface.ts +++ b/projects/flow/src/lib/flow-interface.ts @@ -1,5 +1,3 @@ -import { FlowComponent } from './flow.component'; - export interface ChildInfo { position: FlowOptions; dots?: DOMRect[]; @@ -25,12 +23,6 @@ export interface DotOptions extends FlowOptions { dotIndex: number; } -export class FlowConfig { - Arrows = true; - ArrowSize = 20; - Plugins: { [x: string]: FlowPlugin } = {}; -} - export type FlowDirection = 'horizontal' | 'vertical'; export type ArrowPathFn = ( @@ -39,10 +31,3 @@ export type ArrowPathFn = ( arrowSize: number, strokeWidth: number ) => string; - -export interface FlowPlugin { - onInit?(data: FlowComponent): void; - afterInit?(data: FlowComponent): void; - beforeUpdate?(data: FlowComponent): void; - afterUpdate?(data: FlowComponent): void; -} diff --git a/projects/flow/src/lib/flow.component.ts b/projects/flow/src/lib/flow.component.ts index b5fff42..079910b 100644 --- a/projects/flow/src/lib/flow.component.ts +++ b/projects/flow/src/lib/flow.component.ts @@ -20,12 +20,9 @@ import { FlowOptions, ChildInfo, FlowDirection, - DotOptions, ArrowPathFn, - FlowConfig, - FlowPlugin, } from './flow-interface'; -import { blendCorners, flowPath, bezierPath, blendCorners1 } from './svg'; +import { FlowConfig, FlowPlugin } from './plugins/plugin'; const BASE_SCALE_AMOUNT = 0.05; diff --git a/projects/flow/src/lib/flow.service.ts b/projects/flow/src/lib/flow.service.ts index 92abfb7..e3317c5 100644 --- a/projects/flow/src/lib/flow.service.ts +++ b/projects/flow/src/lib/flow.service.ts @@ -1,12 +1,8 @@ import { Injectable, NgZone } from '@angular/core'; import { BehaviorSubject, Subject } from 'rxjs'; -import { - ArrowPathFn, - FlowConfig, - FlowDirection, - FlowOptions, -} from './flow-interface'; +import { ArrowPathFn, FlowDirection, FlowOptions } from './flow-interface'; import { blendCorners } from './svg'; +import { FlowConfig } from './plugins/plugin'; @Injectable() export class FlowService { diff --git a/projects/flow/src/lib/plugins/arrangements.spec.ts b/projects/flow/src/lib/plugins/arrangements.spec.ts index b1e8354..a63cc84 100644 --- a/projects/flow/src/lib/plugins/arrangements.spec.ts +++ b/projects/flow/src/lib/plugins/arrangements.spec.ts @@ -1,4 +1,4 @@ -import { ArrangementsOld, Arrangements } from './arrangements'; +import { Arrangements } from './arrangements'; import { ChildInfo } from '../flow-interface'; import { FlowComponent } from '../flow.component'; @@ -14,33 +14,6 @@ export const FLOW_LIST = [ ]; describe('Arrangements', () => { - let arrangements: ArrangementsOld; - - it('should be created', () => { - const childObj: ChildInfo[] = FLOW_LIST.map((x) => ({ - position: x, - elRect: { width: 200, height: 200 } as any, - })); - - arrangements = new ArrangementsOld(childObj); - arrangements.verticalPadding = 20; - arrangements.groupPadding = 100; - const expected = { - '1': { x: 0, y: 370, id: '1', deps: [] }, - '2': { x: 300, y: 110, id: '2', deps: ['1'] }, - '3': { x: 600, y: 0, id: '3', deps: ['2'] }, - '4': { x: 600, y: 220, id: '4', deps: ['2'] }, - '5': { x: 300, y: 630, id: '5', deps: ['1'] }, - '6': { x: 600, y: 520, id: '6', deps: ['5'] }, - '7': { x: 600, y: 740, id: '7', deps: ['5'] }, - '8': { x: 900, y: 550, id: '8', deps: ['6', '7'] }, - }; - const actual = Object.fromEntries(arrangements.autoArrange()); - expect(actual).toEqual(expected); - }); -}); - -describe('Arrangements2', () => { let arrangements: Arrangements; it('should be created', () => { diff --git a/projects/flow/src/lib/plugins/arrangements.ts b/projects/flow/src/lib/plugins/arrangements.ts index 7004638..e0feef6 100644 --- a/projects/flow/src/lib/plugins/arrangements.ts +++ b/projects/flow/src/lib/plugins/arrangements.ts @@ -1,127 +1,6 @@ -import { - FlowOptions, - ChildInfo, - FlowDirection, - FlowPlugin, -} from '../flow-interface'; +import { FlowOptions, ChildInfo, FlowDirection } from '../flow-interface'; import { FlowComponent } from '../flow.component'; - -export class ArrangementsOld { - constructor( - private list: ChildInfo[], - private direction: 'horizontal' | 'vertical' = 'horizontal', - public horizontalPadding = 100, - public verticalPadding = 20, - public groupPadding = 20 - ) {} - - public autoArrange(): Map { - const newItems = new Map(); - let currentX = 0; - let currentY = 0; - - // Handle both horizontal and vertical directions - const baseNodes = this.list.filter( - (node) => node.position.deps.length === 0 - ); - - for (const baseNode of baseNodes) { - if (this.direction === 'horizontal') { - this.positionDependents(baseNode, 0, currentY, newItems); - currentY += baseNode.elRect.height + this.verticalPadding; - } else { - // Vertical arrangement - this.positionDependents(baseNode, 0, currentX, newItems); - currentX += baseNode.elRect.width + this.verticalPadding; - } - } - - return newItems; - } - - private positionDependents( - baseNode: ChildInfo, - baseX: number, - baseY: number, - newItems: Map, - config: { first: boolean; gp: number; maxDepLength: number } = { - first: true, - gp: -this.groupPadding * 2, - maxDepLength: 0, - } - ): { consumedSpace: number; dep: boolean } { - const dependents = this.list.filter((child) => - child.position.deps.includes(baseNode.position.id) - ); - - const isV = this.direction === 'vertical'; - - let startY = baseY; - const { width: w, height: h } = baseNode.elRect; - let newX = baseX + (isV ? h : w) + this.horizontalPadding; - const height = isV ? w : h; - - const childC: { first: boolean; gp: number; maxDepLength: number } = { - first: true, - gp: 0, - maxDepLength: 0, - }; - for (let i = 0; i < dependents.length; i++) { - const depLast = i === dependents.length - 1; - childC.first = i === 0; - const dependent = dependents[i]; - const { consumedSpace, dep } = this.positionDependents( - dependent, - newX, - startY, - newItems, - childC - ); - - startY = 0; - - if (childC.maxDepLength > 1 && dep && !depLast) { - startY += this.groupPadding; - config.gp += this.groupPadding; - } - startY += consumedSpace + (!depLast ? this.verticalPadding : 0); - } - - // baseY += childC.gp; - config.maxDepLength = Math.max(config.maxDepLength, childC.maxDepLength); - - let y = 0; - if (dependents.length > 1) { - // find the first and last dependent and there y position - const firstDepId = dependents[0].position.id; - const lastDepId = dependents[dependents.length - 1].position.id; - const firstDep = newItems.get(firstDepId)!; - const lastDep = newItems.get(lastDepId)!; - // find the center of the first and last dependent - y = (isV ? firstDep.x + lastDep.x : firstDep.y + lastDep.y) / 2; - } else { - y = baseY + (dependents.length ? (startY - baseY) / 2 - height / 2 : 0); - - // TODO: This is not working as expected - // If there are more than one dependency, We need to center the node based on the parents - if (baseNode.position.deps.length > 1) { - const len = baseNode.position.deps.length / 2; - const halfVerticalPadding = (this.verticalPadding * len) / 2; - y -= baseNode.elRect.height * len - halfVerticalPadding; - } - } - newItems.set(baseNode.position.id, { - ...baseNode.position, - x: isV ? y : baseX, - y: isV ? baseX : y, - }); - // add groupPadding if there are more than one dependency - const groupPad = - dependents.length > 1 ? this.groupPadding - this.verticalPadding : 0; - const consumedSpace = startY + (dependents.length ? 0 : height) + groupPad; - return { consumedSpace, dep: dependents.length > 0 }; - } -} +import { FlowPlugin } from './plugin'; const ROOT_DATA = new Map(); const ROOT_DEPS = new Map(); @@ -137,8 +16,6 @@ export class Arrangements implements FlowPlugin { public verticalPadding = 20; public groupPadding = 20; - constructor() {} - onInit(data: FlowComponent): void { this.data = data; } @@ -194,7 +71,6 @@ export class Arrangements implements FlowPlugin { for (const item of this.list) { newItems.set(item.position.id, item.position); } - console.log([...newItems.values()]); return newItems; } } @@ -213,7 +89,7 @@ export class ArrangeNode { // we need to recursively call this method to get all the dependents of the node // and then we need to position them - arrange(x = 0, y = 0, direction: FlowDirection): Coordinates { + arrange(x: number, y: number, direction: FlowDirection): Coordinates { const dependents = ROOT_DEPS.get(this.position.id) || []; let startX = x; let startY = y; @@ -225,7 +101,8 @@ export class ArrangeNode { } else { startY += this.elRect.height + HORIZONTAL_PADDING; } - let first, last: Coordinates; + let first: Coordinates = { x: 0, y: 0 }; + let last: Coordinates = { x: 0, y: 0 }; for (let i = 0; i < len; i++) { const dep = dependents[i]; const dependent = ROOT_DATA.get(dep)!; @@ -242,10 +119,10 @@ export class ArrangeNode { } if (direction === 'horizontal') { startY -= VERTICAL_PADDING + this.elRect.height; - y = first!.y + (last!.y - first!.y) / 2; + y = first.y + (last.y - first.y) / 2; } else { startX -= VERTICAL_PADDING + this.elRect.width; - x = first!.x + (last!.x - first!.x) / 2; + x = first.x + (last.x - first.x) / 2; } } this.position.x = x; diff --git a/projects/flow/src/lib/plugins/connections.spec.ts b/projects/flow/src/lib/plugins/connections.spec.ts index 5b46dff..2372ad7 100644 --- a/projects/flow/src/lib/plugins/connections.spec.ts +++ b/projects/flow/src/lib/plugins/connections.spec.ts @@ -15,7 +15,8 @@ describe('Connections', () => { ]; check(list, [1, 3]); - check(list.reverse(), [3, 1]); + list.reverse(); + check(list, [3, 1]); list = [ t({ x: 300, y: -150, id: '2', deps: ['1'] }), @@ -50,21 +51,13 @@ describe('Connections', () => { ]; check(list, 0, '2', [1, 3]); - // connections = new Connections(list); - // let actual = connections.getClosestDotsSimplified(list[0], '2'); - // expect(actual).toEqual([1, 3]); check(list, 1, '1', [3, 1]); - // actual = connections.getClosestDotsSimplified(list[1], '1'); - // expect(actual).toEqual([3, 1]); list = [ t({ x: 40, y: 40, id: '1', deps: [] }), t({ x: 173.203125, y: -33, id: '2', deps: ['1'] }), ]; check(list, 0, '2', [1, 3]); - // connections = new Connections(list); - // actual = connections.getClosestDotsSimplified(list[0], '2'); - // expect(actual).toEqual([1, 3]); list = [ t({ x: 40, y: 40, id: '1', deps: [] }), @@ -72,9 +65,6 @@ describe('Connections', () => { ]; check(list, 0, '2', [1, 3]); - // actual = connections.getClosestDotsSimplified(list[0], '2'); - // expect(actual).toEqual([1, 3]); - function check( list: ChildInfo[], index: number, diff --git a/projects/flow/src/lib/plugins/connections.ts b/projects/flow/src/lib/plugins/connections.ts index 02d7a89..fed2f8d 100644 --- a/projects/flow/src/lib/plugins/connections.ts +++ b/projects/flow/src/lib/plugins/connections.ts @@ -1,11 +1,7 @@ -import { - ChildInfo, - DotOptions, - FlowOptions, - FlowPlugin, -} from '../flow-interface'; +import { ChildInfo, DotOptions, FlowOptions } from '../flow-interface'; import { FlowChildComponent } from '../flow-child.component'; import { FlowComponent } from '../flow.component'; +import { FlowPlugin } from './plugin'; export class Connections implements FlowPlugin { // key = id of the item @@ -134,7 +130,6 @@ export class Connections implements FlowPlugin { child: ChildInfo ): [number, number] { // sides dot index order: [top, right, bottom, left] - const thresholdDistance = 10; // Example distance threshold. Adjust as needed. let swapped = false; const isV = this.direction === 'vertical'; // correct the parent based on the deps @@ -145,25 +140,7 @@ export class Connections implements FlowPlugin { swapped = true; } - const childDirection: 'right' | 'left' | 'bottom' | 'top' = (() => { - // consider width and height of the child - const { width, height } = child.elRect; - const { x, y } = child.position; - const { x: px, y: py } = parent.position; - - if (!isV) { - if (x + width < px) return 'left'; - if (x - width > px) return 'right'; - if (y + height < py) return 'top'; - if (y - height > py) return 'bottom'; - } else { - if (y + height < py) return 'top'; - if (y - height > py) return 'bottom'; - if (x + width < px) return 'left'; - if (x - width > px) return 'right'; - } - return 'right'; - })(); + const childDirection = this.getDirection(child, parent, isV); const parentIndex = (() => { if (childDirection === 'right') return 1; @@ -178,20 +155,36 @@ export class Connections implements FlowPlugin { return 2; })(); - // console.log( - // `parentIndex ${parent.position.id}-${child.position.id}:`, - // `${parent.position.id}-${parentIndex}`, - // `${child.position.id}-${childIndex}`, - // [structuredClone(parent), structuredClone(child)] - // ); - return swapped ? [childIndex, parentIndex] : [parentIndex, childIndex]; } + private getDirection( + child: ChildInfo, + parent: ChildInfo, + isV: boolean + ): 'right' | 'left' | 'bottom' | 'top' { + // consider width and height of the child + const { width, height } = child.elRect; + const { x, y } = child.position; + const { x: px, y: py } = parent.position; + + if (!isV) { + if (x + width < px) return 'left'; + if (x - width > px) return 'right'; + if (y + height < py) return 'top'; + if (y - height > py) return 'bottom'; + } else { + if (y + height < py) return 'top'; + if (y - height > py) return 'bottom'; + if (x + width < px) return 'left'; + if (x - width > px) return 'right'; + } + return 'right'; + } + private updateDotVisibility(childObj: Record) { Object.keys(childObj).forEach((id) => { const child = childObj[id]; - const position = child.position; const dots = child.dots.toArray(); dots.forEach((dot, index) => { @@ -203,7 +196,6 @@ export class Connections implements FlowPlugin { dot.nativeElement.style.visibility = isClosestForAnyDep ? 'visible' : 'hidden'; - // dot.nativeElement.style.visibility = 'hidden'; }); }); } @@ -225,7 +217,6 @@ export class Connections implements FlowPlugin { const rect = childDots[dotIndex]; const { left, top } = this.data.flow.zRect; - // const rect = dotEl.nativeElement.getBoundingClientRect(); const x = (rect.x + rect.width / 2 - panX - left) / scale; const y = (rect.y + rect.height / 2 - panY - top) / scale; diff --git a/projects/flow/src/lib/plugins/fit-to-window.ts b/projects/flow/src/lib/plugins/fit-to-window.ts index 336c13d..88bd277 100644 --- a/projects/flow/src/lib/plugins/fit-to-window.ts +++ b/projects/flow/src/lib/plugins/fit-to-window.ts @@ -1,5 +1,6 @@ -import { ChildInfo, FlowPlugin } from '../flow-interface'; +import { ChildInfo } from '../flow-interface'; import { FlowComponent } from '../flow.component'; +import { FlowPlugin } from './plugin'; export class FitToWindow implements FlowPlugin { private cRect: CPosition; diff --git a/projects/flow/src/lib/plugins/plugin.ts b/projects/flow/src/lib/plugins/plugin.ts new file mode 100644 index 0000000..6a44350 --- /dev/null +++ b/projects/flow/src/lib/plugins/plugin.ts @@ -0,0 +1,14 @@ +import { FlowComponent } from '../flow.component'; + +export class FlowConfig { + Arrows = true; + ArrowSize = 20; + Plugins: { [x: string]: FlowPlugin } = {}; +} + +export interface FlowPlugin { + onInit?(data: FlowComponent): void; + afterInit?(data: FlowComponent): void; + beforeUpdate?(data: FlowComponent): void; + afterUpdate?(data: FlowComponent): void; +} diff --git a/projects/flow/src/lib/plugins/scroll-into-view.ts b/projects/flow/src/lib/plugins/scroll-into-view.ts index d967f7d..d49e9ba 100644 --- a/projects/flow/src/lib/plugins/scroll-into-view.ts +++ b/projects/flow/src/lib/plugins/scroll-into-view.ts @@ -1,5 +1,5 @@ -import { FlowPlugin } from '../flow-interface'; import { FlowComponent } from '../flow.component'; +import { FlowPlugin } from './plugin'; export class ScrollIntoView implements FlowPlugin { private data: FlowComponent; @@ -16,7 +16,6 @@ export class ScrollIntoView implements FlowPlugin { if (item) { const { x, y } = item.position; const { width, height } = item.elRect; - // this.flow.panX = -x * this.flow.scale + this.flow.zRect.width / 2; if (x + width * this.data.flow.scale > this.data.flow.zRect.width) { this.data.flow.panX = -x * this.data.flow.scale + (this.data.flow.zRect.width - width); diff --git a/projects/flow/src/public-api.ts b/projects/flow/src/public-api.ts index f10b0cc..84e378b 100644 --- a/projects/flow/src/public-api.ts +++ b/projects/flow/src/public-api.ts @@ -5,14 +5,10 @@ export * from './lib/flow.service'; export * from './lib/flow.component'; export * from './lib/flow-child.component'; -export { - ArrowPathFn, - FlowOptions, - FlowDirection, - FlowConfig, -} from './lib/flow-interface'; +export { ArrowPathFn, FlowOptions, FlowDirection } from './lib/flow-interface'; export * from './lib/svg'; export { FitToWindow } from './lib/plugins/fit-to-window'; export { ScrollIntoView } from './lib/plugins/scroll-into-view'; export { Arrangements } from './lib/plugins/arrangements'; export { Connections } from './lib/plugins/connections'; +export { FlowConfig, FlowPlugin } from './lib/plugins/plugin'; diff --git a/src/app/demo/demo-one.component.ts b/src/app/demo/demo-one.component.ts index 3336982..1f4bb4b 100644 --- a/src/app/demo/demo-one.component.ts +++ b/src/app/demo/demo-one.component.ts @@ -106,10 +106,6 @@ export class DemoOneComponent implements AfterViewInit { this.selectedNode.valueChanges.subscribe((id) => { this.plugins.scroll.focus(id); }); - - // setTimeout(() => { - // this.flowComponent.scrollIntoView('3'); - // }); } ngAfterViewInit(): void {