General improvements (#8627)

* [NIFI-13021] - Fix - Status History dialog changes size when switching selected metric

* [NIFI-13018] - Fix - Refreshing Node Status History when open for the cluster, makes the incorrect backend call

* [NIFI-13023] - Fix - Right click on canvas while search results open shows browser right-click menu

* [NIFI-13024] - Improvement - Support Enter to create selected extension type

* [NIFI-13000] - Improvement - Prevent text selection in header, flow status, canvas, extension creation dialog table.

* prettier

* color udpates for new theme classes, turn of text selection on the status history charts
This commit is contained in:
Rob Fellows 2024-04-11 13:48:36 -04:00 committed by GitHub
parent 44852fb5d5
commit d78e817fe8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 161 additions and 68 deletions

View File

@ -18,7 +18,10 @@
<div class="flex flex-col h-screen justify-between">
<fd-header></fd-header>
<div class="flex-1">
<div id="canvas-container" class="canvas-background h-full" [cdkContextMenuTriggerFor]="contextMenu.menu"></div>
<div
id="canvas-container"
class="canvas-background h-full select-none"
[cdkContextMenuTriggerFor]="contextMenu.menu"></div>
<fd-context-menu #contextMenu [menuProvider]="canvasContextMenu" menuId="root"></fd-context-menu>
<graph-controls></graph-controls>
</div>

View File

@ -15,7 +15,7 @@
~ limitations under the License.
-->
<div class="h-8 flow-status">
<div class="h-8 flow-status select-none">
<div class="flex justify-between">
<div class="flex flex-1 justify-around pr-20">
@if (clusterSummary?.clustered) {

View File

@ -14,3 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
new-canvas-item:focus-visible {
outline: none;
}

View File

@ -32,6 +32,10 @@
}
}
button:focus-visible {
outline: none;
}
.icon-import-from-registry-add:before {
margin-left: 1px;
}

View File

@ -18,15 +18,6 @@
<div class="h-8 flex justify-around search-container" [class.open]="searchInputVisible">
<button class="w-8" (click)="toggleSearchVisibility()"><i class="fa fa-search primary-color"></i></button>
<form [formGroup]="searchForm">
<input
type="text"
matInput
placeholder="Search"
class="search-input on-surface-default"
[class.open]="searchInputVisible"
cdkOverlayOrigin
#searchInput="cdkOverlayOrigin"
formControlName="searchBar" />
<ng-template
cdkConnectedOverlay
[cdkConnectedOverlayOrigin]="searchInput"
@ -34,7 +25,7 @@
[cdkConnectedOverlayPositions]="positions"
[cdkConnectedOverlayHasBackdrop]="true"
[cdkConnectedOverlayBackdropClass]="'cdk-overlay-transparent-backdrop'"
(backdropClick)="backdropClicked()">
(overlayOutsideClick)="backdropClicked($event)">
<div class="search-results w-96 border p-2 text-sm max-h-96 overflow-y-auto">
@if (searching) {
<div class="unset nifi-surface-default italic">Searching</div>
@ -185,6 +176,15 @@
}
</div>
</ng-template>
<input
type="text"
matInput
placeholder="Search"
class="search-input on-surface-default"
[class.open]="searchInputVisible"
cdkOverlayOrigin
#searchInput="cdkOverlayOrigin"
formControlName="searchBar" />
<ng-template #renderResults let-results let-header="header" let-icon="icon" let-path="path">
<li class="flex items-center">
<span class="icon mr-1" [class]="icon"></span>

View File

@ -170,7 +170,9 @@ export class Search implements OnInit {
);
}
backdropClicked() {
backdropClicked(event: MouseEvent): void {
event.stopPropagation();
event.preventDefault();
this.searchingResultsVisible = false;
this.searchForm.get('searchBar')?.setValue('');

View File

@ -31,9 +31,7 @@
<mat-progress-bar
mode="determinate"
[value]="versionChangeRequest.request.percentCompleted"></mat-progress-bar>
<div class="accent-color font-medium">
{{ versionChangeRequest.request.percentCompleted }}%
</div>
<div class="accent-color font-medium">{{ versionChangeRequest.request.percentCompleted }}%</div>
</div>
}
</div>

View File

@ -30,7 +30,7 @@
{{ updateStep.description }}
</div>
@if (updateStep.failureReason) {
<div class="fa fa-times mat-warn"></div>
<div class="fa fa-times warn-color"></div>
} @else {
@if (updateStep.complete) {
<div class="fa fa-check nifi-success-default"></div>

View File

@ -24,7 +24,7 @@
formatBytes(queueSizeByteCount)
}}) bytes
</div>
<div class="listing-message mat-warn">
<div class="listing-message warn-color">
@if (sourceRunning && destinationRunning) {
The source and destination of this queue are currently running. This listing may no longer be accurate.
} @else {

View File

@ -38,9 +38,7 @@
<td mat-cell *matCellDef="let item">
<div class="flex items-center gap-x-3">
@if (isSyncedToParameterContext(item)) {
<div
class="fa fa-star primary-color"
title="Synced to a parameter context."></div>
<div class="fa fa-star primary-color" title="Synced to a parameter context."></div>
}
</div>
</td>

View File

@ -34,9 +34,7 @@
@if (canRead(item)) {
<div class="flex items-center gap-x-3">
<!-- TODO: open details -->
<div
class="pointer fa fa-info-circle primary-color"
title="View details"></div>
<div class="pointer fa fa-info-circle primary-color" title="View details"></div>
<div
class="pointer fa fa-book primary-color"

View File

@ -22,7 +22,7 @@ import { NiFiState } from '../index';
import { StatusHistoryService } from '../../service/status-history.service';
import * as StatusHistoryActions from './status-history.actions';
import { StatusHistoryRequest } from './index';
import { catchError, from, map, of, switchMap, tap } from 'rxjs';
import { catchError, filter, from, map, of, switchMap, tap } from 'rxjs';
import { MatDialog } from '@angular/material/dialog';
import { StatusHistory } from '../../ui/common/status-history/status-history.component';
@ -35,10 +35,11 @@ export class StatusHistoryEffects {
private dialog: MatDialog
) {}
reloadStatusHistory$ = createEffect(() =>
reloadComponentStatusHistory$ = createEffect(() =>
this.actions$.pipe(
ofType(StatusHistoryActions.reloadStatusHistory),
map((action) => action.request),
filter((request) => !!request.componentId && !!request.componentType),
switchMap((request: StatusHistoryRequest) =>
from(
this.statusHistoryService
@ -67,6 +68,37 @@ export class StatusHistoryEffects {
)
);
reloadNodeStatusHistory$ = createEffect(() =>
this.actions$.pipe(
ofType(StatusHistoryActions.reloadStatusHistory),
map((action) => action.request),
filter((request) => !request.componentId && !request.componentType),
switchMap(() =>
from(
this.statusHistoryService.getNodeStatusHistory().pipe(
map((response: any) =>
StatusHistoryActions.reloadStatusHistorySuccess({
response: {
statusHistory: {
canRead: response.canRead,
statusHistory: response.statusHistory
}
}
})
),
catchError((error) =>
of(
StatusHistoryActions.statusHistoryApiError({
error: error.error
})
)
)
)
)
)
)
);
getStatusHistoryAndOpenDialog$ = createEffect(() =>
this.actions$.pipe(
ofType(StatusHistoryActions.getStatusHistoryAndOpenDialog),

View File

@ -33,9 +33,7 @@
</div>
<div>
<form [formGroup]="filterForm" class="flex flex-col gap-y-2">
<div class="accent-color font-medium">
Displaying {{ filteredEntries }} of {{ totalEntries }}
</div>
<div class="accent-color font-medium">Displaying {{ filteredEntries }} of {{ totalEntries }}</div>
<div class="flex justify-between items-center">
<mat-form-field>
<mat-label>Filter</mat-label>

View File

@ -121,11 +121,7 @@
<ng-template #service let-references>
@if (references.length > 0) {
<li>
<h4>
<span class="accent-color font-medium">Controller Services</span> ({{
references.length
}})
</h4>
<h4><span class="accent-color font-medium">Controller Services</span> ({{ references.length }})</h4>
<div class="references">
@for (service of references; track service) {
<div class="flex flex-col">
@ -179,9 +175,7 @@
<ng-template #unauthorized let-references>
@if (references.length > 0) {
<li>
<h4>
<span class="accent-color font-medium">Unauthorized</span> ({{ references.length }})
</h4>
<h4><span class="accent-color font-medium">Unauthorized</span> ({{ references.length }})</h4>
<div class="references">
@for (reference of references; track reference) {
<div class="flex">

View File

@ -114,9 +114,7 @@
</div>
}
<div class="flex justify-between items-center">
<div class="accent-color font-medium">
Disabling this controller service
</div>
<div class="accent-color font-medium">Disabling this controller service</div>
<ng-container
*ngTemplateOutlet="
getTemplateForStep(SetEnableStep.DisableService, disableRequest)
@ -165,6 +163,6 @@
<div class="complete fa fa-check nifi-success-default"></div>
</ng-template>
<ng-template #stepError>
<div class="fa fa-times mat-warn"></div>
<div class="fa fa-times warn-color"></div>
</ng-template>
<ng-template #stepNotStarted><div class="w-3.5"></div></ng-template>

View File

@ -94,9 +94,7 @@
</div>
<div class="flex flex-col gap-y-1.5">
<div class="flex justify-between items-center">
<div class="accent-color font-medium">
Enabling this controller service
</div>
<div class="accent-color font-medium">Enabling this controller service</div>
<ng-container
*ngTemplateOutlet="
getTemplateForStep(SetEnableStep.EnableService, enableRequest)
@ -186,6 +184,6 @@
<div class="complete fa fa-check nifi-success-default"></div>
</ng-template>
<ng-template #stepError>
<div class="fa fa-times mat-warn"></div>
<div class="fa fa-times warn-color"></div>
</ng-template>
<ng-template #stepNotStarted><div class="w-3.5"></div></ng-template>

View File

@ -15,7 +15,7 @@
~ limitations under the License.
-->
<section tabindex="0">
<section tabindex="0" (keyup)="navigateSelectionList($event)">
<div class="extension-creation-dialog p-4 flex flex-col justify-between gap-y-3">
<div class="flex justify-between items-center">
<h3 class="text-lg">Add {{ componentType }}</h3>
@ -32,7 +32,7 @@
</mat-form-field>
</div>
</div>
<div class="listing-table">
<div class="listing-table select-none">
<div class="h-96 overflow-y-auto overflow-x-hidden">
<table
mat-table
@ -46,14 +46,27 @@
<ng-container matColumnDef="type">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Type</th>
<td mat-cell *matCellDef="let item">
<div
class="fa"
[class.fa-shield]="item.restricted"
[class.mat-warn]="item.restricted"
nifiTooltip
[tooltipComponentType]="RestrictionsTip"
[tooltipInputData]="getRestrictionTipData(item)"></div>
{{ formatType(item) }}
@if (isSelected(item)) {
<div #selectedRow>
<div
class="fa"
[class.fa-shield]="item.restricted"
[class.warn-color]="item.restricted"
nifiTooltip
[tooltipComponentType]="RestrictionsTip"
[tooltipInputData]="getRestrictionTipData(item)"></div>
{{ formatType(item) }}
</div>
} @else {
<div
class="fa"
[class.fa-shield]="item.restricted"
[class.warn-color]="item.restricted"
nifiTooltip
[tooltipComponentType]="RestrictionsTip"
[tooltipInputData]="getRestrictionTipData(item)"></div>
{{ formatType(item) }}
}
</td>
</ng-container>

View File

@ -15,7 +15,7 @@
* limitations under the License.
*/
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { Component, ElementRef, EventEmitter, Input, Output, ViewChild } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatDialogModule } from '@angular/material/dialog';
import { MatTableDataSource, MatTableModule } from '@angular/material/table';
@ -68,6 +68,8 @@ export class ExtensionCreation {
@Output() extensionTypeSelected: EventEmitter<DocumentedType> = new EventEmitter<DocumentedType>();
@ViewChild('selectedRow', { static: false }) selectedRow: ElementRef | null | undefined;
protected readonly RestrictionsTip = RestrictionsTip;
protected readonly ControllerServiceApiTip = ControllerServiceApiTip;
@ -133,10 +135,23 @@ export class ExtensionCreation {
return '';
}
filterTypes(event: Event): void {
filterTypes(event: KeyboardEvent): void {
switch (event.key) {
case 'Enter':
case 'ArrowUp':
case 'ArrowDown':
// handled in navigateSelectionList
return;
}
const filterText: string = (event.target as HTMLInputElement).value;
this.dataSource.filter = filterText.trim().toLowerCase();
this.selectedType = null;
if (this.dataSource.filteredData.length > 0) {
this.selectType(this.dataSource.filteredData[0]);
} else {
this.selectedType = null;
}
}
selectType(documentedType: DocumentedType): void {
@ -156,6 +171,45 @@ export class ExtensionCreation {
}
}
navigateSelectionList(event: KeyboardEvent): void {
if (this.selectedType !== null) {
switch (event.key) {
case 'Enter':
this.createExtension(this.selectedType);
break;
case 'ArrowUp':
this.selectRow(-1);
break;
case 'ArrowDown':
this.selectRow(1);
break;
}
if (this.selectedRow) {
this.selectedRow?.nativeElement.scrollIntoView({
behavior: 'instant',
block: 'center',
inline: 'nearest'
});
}
}
}
private selectRow(offset: number) {
if (this.selectedType && this.dataSource.filteredData.length > 0) {
// find the index of the currently selected row
const selectedIndex = this.dataSource.filteredData.findIndex(
(data) => data.type === this.selectedType?.type
);
if (selectedIndex > -1) {
const newSelectedIndex = selectedIndex + offset;
if (newSelectedIndex > -1 && newSelectedIndex < this.dataSource.filteredData.length) {
this.selectType(this.dataSource.filteredData[newSelectedIndex]);
}
}
}
}
sortData(sort: Sort) {
this.activeSort = sort;
this.dataSource.data = this.sortEntities(this.dataSource.data, sort);

View File

@ -15,7 +15,7 @@
~ limitations under the License.
-->
<nav class="nifi-navigation">
<nav class="nifi-navigation select-none">
<div class="flex justify-between items-center h-16 pl-4">
<div class="flex">
<div class="h-16 w-28 mr-6 relative">

View File

@ -90,11 +90,7 @@
<ng-template #service let-references>
@if (references.length > 0) {
<li>
<h4>
<span class="accent-color font-medium">Controller Services</span> ({{
references.length
}})
</h4>
<h4><span class="accent-color font-medium">Controller Services</span> ({{ references.length }})</h4>
<div class="references">
@for (service of references; track service) {
<div class="flex flex-col">
@ -132,9 +128,7 @@
<ng-template #unauthorized let-references>
@if (references.length > 0) {
<li>
<h4>
<span class="accent-color font-medium">Unauthorized</span> ({{ references.length }})
</h4>
<h4><span class="accent-color font-medium">Unauthorized</span> ({{ references.length }})</h4>
<div class="references">
@for (reference of references; track reference) {
<div class="flex">

View File

@ -15,7 +15,7 @@
~ limitations under the License.
-->
<div id="status-history-container" class="flex-1 flex flex-col">
<div id="status-history-container" class="flex-1 flex flex-col select-none">
<div id="status-history-chart-container" class="grow"></div>
<div id="status-history-chart-control-container"></div>
</div>

View File

@ -41,6 +41,11 @@
.chart-panel {
min-width: 495px;
}
.component-details {
min-width: 300px;
max-width: 300px;
}
}
}