You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@nifi.apache.org by "mcgilman (via GitHub)" <gi...@apache.org> on 2026/05/04 17:17:51 UTC
[PR] NIFI-15819: Adding the navigation card and birdseye control to the co… [nifi]
mcgilman opened a new pull request, #11202:
URL: https://github.com/apache/nifi/pull/11202
…nnector canvas graph controls panel.
# Summary
[NIFI-15819](https://issues.apache.org/jira/browse/NIFI-15819)
Adds a navigation panel (Zoom In/Out/Fit/Actual, Leave Group) and an embedded birdseye minimap to the connector canvas graph-controls side panel, sitting directly above the existing Connector card. Both are implemented as new reusable presentation components under `apps/nifi/src/app/ui/common/` so they can be hosted by any page that already mounts the reusable `<reusable-canvas>`.
### What changed
**New reusable components (`apps/nifi/src/app/ui/common/`)**
- `birdseye/` — `CanvasBirdseyeComponent`, a hybrid Canvas + SVG minimap. Uses Canvas to draw simplified component representations (efficient for thousands of nodes) and SVG to host an interactive viewport brush with native D3 drag. Recalculates bounds to include both component extents and the current viewport so the brush stays meaningful when the user pans far from any component. Retina-aware (`devicePixelRatio` scaling) and auto-sizes via `ResizeObserver`. Effects react to changes in `components`, `transform`, and `canvasDimensions` inputs (drag-aware to avoid mid-drag re-renders).
- `birdseye/birdseye.types.ts` — extracts the `BirdseyeComponentData`, `BirdseyeTransform`, and `BirdseyeBounds` interfaces that previously lived inline in `canvas.component.ts`. `canvas.component.ts` now imports them from the new file (no re-exports).
- `navigation-control/` — `CanvasNavigationControl`, a `mat-expansion-panel` shell with Zoom In/Out/Fit to Screen/Zoom to Actual Size buttons, an optional Leave Group button (shown when `canNavigateToParent` is true), and the embedded `<canvas-birdseye>`. Persists collapsed state in `localStorage` under `graph-control-visibility[<storageKey>]` via the shared `Storage` service. The `storageKey` input is configurable so independent instances on different pages do not collide.
**Wiring into the connector canvas (`pages/connectors/ui/connector-canvas/`)**
- `connector-graph-controls` now hosts `<canvas-navigation-control storageKey="connector-navigation-control" …>` above the existing Connector info card and bubbles its zoom / birdseye / leave-group events.
- `connector-canvas.component.ts` adds three signals — `birdseyeComponents`, `birdseyeTransform`, `canvasDimensions` — and:
- Seeds them on `(initialized)` from the canvas's public `getBirdseyeComponentData()` / `getCanvasDimensions()` API.
- Refreshes them after every `loadConnectorFlowSuccess` action (initial load, refresh, process-group navigation), so the minimap stays in sync with the underlying flow.
- Updates `birdseyeTransform` from the canvas's `(transformChange)` output.
- Refreshes `canvasDimensions` on `window:resize` (e.g. when the side panel toggles).
- Delegates zoom / birdseye / leave-group events back into the `CanvasComponent` API and the existing `leaveGroupAction()`.
**Architectural note for reviewers**
NiFi already has a flow-designer-specific navigation control at `pages/flow-designer/ui/canvas/graph-controls/navigation-control/`, but it is tightly coupled to flow-designer NgRx state and the legacy `BirdseyeView` / `CanvasView` services. Rather than refactor the legacy stack, the new components were built fresh against the reusable `CanvasComponent` public API (signals + outputs). They are presentation-only and have no coupling to flow-designer state, which keeps the legacy flow-designer code path untouched and lets future canvases (e.g. the connector canvas here) opt in.
The connector page passes `storageKey="connector-navigation-control"` so the new panel persists independently of the legacy flow-designer `navigation-control` key — both can coexist without interference.
--
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: issues-unsubscribe@nifi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] NIFI-15819: Adding the navigation card and birdseye control to the co… [nifi]
Posted by "rfellows (via GitHub)" <gi...@apache.org>.
rfellows commented on code in PR #11202:
URL: https://github.com/apache/nifi/pull/11202#discussion_r3221557959
##########
nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/canvas/canvas.component.ts:
##########
@@ -2983,7 +2972,8 @@ export class CanvasComponent implements OnInit, AfterViewInit, OnDestroy {
id: p.entity.id,
type: ComponentType.Processor,
position: { x: p.entity.position.x, y: p.entity.position.y },
- dimensions: { width: p.ui.dimensions.width, height: p.ui.dimensions.height }
+ dimensions: { width: p.ui.dimensions.width, height: p.ui.dimensions.height },
+ fillColor: p.entity.component?.style?.['background-color'] || undefined
Review Comment:
should we gate this with `canRead` as we do when rendering color in the flow designer birdseye-view.service?
##########
nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/canvas/canvas.component.ts:
##########
@@ -3034,7 +3024,8 @@ export class CanvasComponent implements OnInit, AfterViewInit, OnDestroy {
id: l.entity.id,
type: ComponentType.Label,
position: { x: l.entity.position.x, y: l.entity.position.y },
- dimensions: { width: l.ui.dimensions.width, height: l.ui.dimensions.height }
+ dimensions: { width: l.ui.dimensions.width, height: l.ui.dimensions.height },
+ fillColor: l.entity.component?.style?.['background-color'] || undefined
Review Comment:
should we gate this on `canRead` as well?
--
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: issues-unsubscribe@nifi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] NIFI-15819: Adding the navigation card and birdseye control to the co… [nifi]
Posted by "rfellows (via GitHub)" <gi...@apache.org>.
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: issues-unsubscribe@nifi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] NIFI-15819: Adding the navigation card and birdseye control to the co… [nifi]
Posted by "mcgilman (via GitHub)" <gi...@apache.org>.
mcgilman merged PR #11202:
URL: https://github.com/apache/nifi/pull/11202
--
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: issues-unsubscribe@nifi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org