rfellows commented on code in PR #11202:
URL: https://github.com/apache/nifi/pull/11202#discussion_r3221094333


##########
nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/birdseye/birdseye.component.ts:
##########
@@ -0,0 +1,469 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {
+    AfterViewInit,
+    ChangeDetectionStrategy,
+    Component,
+    ElementRef,
+    OnDestroy,
+    effect,
+    input,
+    output,
+    viewChild
+} from '@angular/core';
+import * as d3 from 'd3';
+import { ComponentType } from '@nifi/shared';
+import { Dimension, Position } from '../canvas/canvas.types';
+import { BirdseyeBounds, BirdseyeComponentData, BirdseyeTransform } from 
'./birdseye.types';
+
+/**
+ * Canvas Birdseye Component
+ *
+ * A minimap component that provides an overview of the entire canvas
+ * and allows users to navigate by dragging the viewport brush.
+ *
+ * Architecture: Hybrid Canvas + SVG
+ * - Canvas: Renders component representations (efficient for thousands of 
components)
+ * - SVG: Renders the interactive viewport brush (easy drag handling)
+ *
+ * This hybrid approach provides optimal performance:
+ * - Canvas handles static component rendering without DOM overhead
+ * - SVG handles interactive brush with native D3 drag behavior
+ *
+ * Features:
+ * - Renders simplified representations of all canvas components
+ * - Shows current viewport position as a draggable brush
+ * - Drag brush to pan the canvas (delta-based translation)
+ * - Automatically scales to fit all components
+ * - Scales efficiently to thousands of components
+ *
+ * Usage:
+ * ```html
+ * <canvas-birdseye
+ *     [components]="birdseyeComponents()"
+ *     [transform]="canvasTransform()"
+ *     [canvasDimensions]="{ width: canvasWidth, height: canvasHeight }"
+ *     (viewportChange)="onBirdseyeViewportChange($event)">
+ * </canvas-birdseye>
+ * ```
+ */
+@Component({
+    selector: 'canvas-birdseye',
+    standalone: true,
+    templateUrl: './birdseye.component.html',
+    styleUrls: ['./birdseye.component.scss'],
+    changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class CanvasBirdseyeComponent implements AfterViewInit, OnDestroy {
+    private birdseyeContainer = 
viewChild.required<ElementRef<HTMLDivElement>>('birdseyeContainer');
+
+    components = input.required<BirdseyeComponentData[]>();
+
+    transform = input.required<BirdseyeTransform>();
+
+    canvasDimensions = input.required<Dimension>();
+
+    birdseyeDimensions = input<Dimension>({ width: 200, height: 150 });
+
+    // Emitted during drag with the new translate the canvas should adopt.
+    viewportChange = output<Position>();
+
+    // Parent should call canvas.birdseyeDragStart() to skip visibility 
updates.
+    dragStart = output<void>();
+
+    // Parent should call canvas.birdseyeDragEnd() to update visibility.
+    dragEnd = output<void>();
+
+    private canvas: HTMLCanvasElement | null = null;
+    private canvasContext: CanvasRenderingContext2D | null = null;
+
+    private svg: d3.Selection<SVGSVGElement, unknown, null, undefined> | null 
= null;
+    private brushGroup: d3.Selection<SVGGElement, unknown, null, undefined> | 
null = null;
+    private brush: d3.Selection<SVGRectElement, { x: number; y: number }, 
null, undefined> | null = null;
+
+    private readonly DPR = window.devicePixelRatio || 1;
+
+    private birdseyeScale = 1;
+    private offsetX = 0;
+    private offsetY = 0;
+    private bounds: BirdseyeBounds = { minX: 0, minY: 0, maxX: 100, maxY: 100 
};
+
+    private isDragging = false;
+
+    private destroyed = false;
+
+    private resizeObserver: ResizeObserver | null = null;
+
+    private effectiveWidth = 200;
+
+    // Component colors mirror the legacy BirdseyeView palette: only 
processors and labels have a stroke.
+    private readonly COMPONENT_COLORS: Partial<Record<ComponentType, { fill: 
string; hasStroke: boolean }>> = {
+        [ComponentType.Processor]: { fill: '#A3BEF5', hasStroke: true },
+        [ComponentType.ProcessGroup]: { fill: '#A3BEF5', hasStroke: false },
+        [ComponentType.RemoteProcessGroup]: { fill: '#154BB7', hasStroke: 
false },
+        [ComponentType.InputPort]: { fill: '#D1DFFA', hasStroke: false },
+        [ComponentType.OutputPort]: { fill: '#D1DFFA', hasStroke: false },
+        [ComponentType.Funnel]: { fill: '#ad9897', hasStroke: false },
+        [ComponentType.Label]: { fill: '#fff7d7', hasStroke: true },
+        [ComponentType.Connection]: { fill: '#A3BEF5', hasStroke: false }
+    };
+
+    private readonly DEFAULT_COLOR = { fill: '#A3BEF5', hasStroke: false };

Review Comment:
   These aren't nifi-themed colors and they don't align with how the flow 
designer's birdseye colors these components.
   
   **Flow Designer:**
   <img width="1389" height="896" alt="Image" 
src="https://github.com/user-attachments/assets/7c306440-0a7c-473e-bc66-0373e7265263";
 />
   
   **Connector Canvas**
   <img width="1389" height="892" alt="Image" 
src="https://github.com/user-attachments/assets/a783766b-2433-4af3-94c9-bb94833ba627";
 />
   
   based on birdseye-view.service.ts, these should probably be:
   ```suggestion
       private readonly COMPONENT_COLORS: Partial<Record<ComponentType, { fill: 
string; hasStroke: boolean }>> = {
           [ComponentType.Processor]: { fill: '#dde4eb', hasStroke: true },
           [ComponentType.ProcessGroup]: { fill: '#728e9b', hasStroke: false },
           [ComponentType.RemoteProcessGroup]: { fill: '#728e9b', hasStroke: 
false },
           [ComponentType.InputPort]: { fill: '#bbdcde', hasStroke: false },
           [ComponentType.OutputPort]: { fill: '#bbdcde', hasStroke: false },
           [ComponentType.Funnel]: { fill: '#ad9897', hasStroke: false },
           [ComponentType.Label]: { fill: '#fff7d7', hasStroke: true }
           // ComponentType.Connection intentionally omitted — falls through to 
DEFAULT_COLOR
       };
       
       private readonly DEFAULT_COLOR = { fill: '#dde4eb', hasStroke: false };
   ```



##########
nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/connectors/ui/connector-canvas/graph-controls/connector-graph-controls.component.ts:
##########
@@ -15,18 +15,36 @@
  * limitations under the License.
  */
 
-import { Component, input } from '@angular/core';
+import { Component, input, output } from '@angular/core';
 import { ConnectorEntity } from '@nifi/shared';
+import { CanvasNavigationControl } from 
'../../../../../ui/common/navigation-control/canvas-navigation-control.component';
+import { BirdseyeComponentData, BirdseyeTransform } from 
'../../../../../ui/common/birdseye/birdseye.types';
+import { Dimension, Position } from 
'../../../../../ui/common/canvas/canvas.types';
 import { ConnectorInfoControl } from 
'./connector-info-control/connector-info-control.component';
 
 @Component({
     selector: 'connector-graph-controls',
     standalone: true,
-    imports: [ConnectorInfoControl],
+    imports: [CanvasNavigationControl, ConnectorInfoControl],
     templateUrl: './connector-graph-controls.component.html',
     styleUrls: ['./connector-graph-controls.component.scss']
 })
 export class ConnectorGraphControls {
     connectorEntity = input<ConnectorEntity | null>(null);
     entitySaving = input<boolean>(false);
+
+    birdseyeComponents = input<BirdseyeComponentData[]>([]);
+    birdseyeTransform = input<BirdseyeTransform>({ translate: { x: 0, y: 0 }, 
scale: 1 });
+    canvasDimensions = input<Dimension>({ width: 0, height: 0 });

Review Comment:
   should these all be `input.required`? they seem to be passthrough props to 
`canvas-naviagtion-control` where they are required.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to