Unverified Commit bd041f8c authored by AlexKarpov's avatar AlexKarpov Committed by GitHub
Browse files

NAS-117474 / 22.12 / Datasets table header sticky (#7091)

* NAS-117474: Datasets table header sticky update

* NAS-117474: JARVIS-117747: Datasets table sticky header fix

* NAS-117474: Datasets sticky header & side stats height fix

* NAS-117474: header sticky initially done

* NAS-117474: Sticky header on datasets table (fix)

* NAS-117474: reverted some changes & some fixes made
parent c16cb006
base DOCS NAS-010101 NAS-110800 NAS-117028-22.12-BETA.2 NAS-118113 NAS-118303 NAS-118454-22.12 NAS-118505-22.12 NAS-118545 NAS-118548 NAS-119131 NAS-119140 NAS-119180-22.12.1 NAS-119431 NAS-119556-23.10 NAS-119615-22.12.1 NAS-119668 NAS-119695 NAS-119749-bluefin NAS-119750-22.12.1 NAS-119806 NAS-119812 NAS-119886-22.12.1 NAS-119996 NAS-119996-bluefin NAS-120045 NAS-120047 NAS-120057 NAS-120173-22.12.1 NAS-120181-22.12.1 NAS-120264-22.12.1 NAS-120274 NAS-120296-22.12.1 NAS-120326-22.12.1 NAS-120490_ NAS-120503 NAS-121006-22.12.2 NAS-121124 NAS-121128-22.12.2 NAS-121128-release-22.12.2 NAS-121136 NAS-121177 NAS-121218-22.12.3 NAS-121300 NAS-121316 NAS-121541 NAS-121542 NAS-121686 NAS-121721-22.12.3 NAS-121778 NAS-121827 NAS-121884-22.12.3 NAS-122267-22.12.4 NAS-122372 NAS-122601 NAS-122686 NAS-122706 NAS-122721 NAS-122751-23.10-BETA.1 NAS-122759 NAS-122781 NAS-122794-23.10 NAS-122855 NAS-122870-bluefin NAS-122969 NAS-122993-22.12.4 NAS-123055-22.12.4 NAS-123278 NAS-123295 NAS-123437 NAS-123478-22.12.4 NAS-123484 NAS-123492 NAS-123526-22.12.4 NAS-123651 NAS-123651-23.10-BETA.1 NAS-123666 NAS-123723 NAS-123723-cobia NAS-123762 NAS-123762-23.10 NAS-123778-23.10 NAS-123778-23.10-RC.1 NAS-123778-RC.1 NAS-123801 NAS-123810-23.10 NAS-123813-22.12.4 NAS-123836 NAS-123836-23.10-BETA.1 NAS-123836-24.04 NAS-123861 NAS-123911-23.10-BETA.1 NAS-123928-23.10 NAS-123931-22.12.4 NAS-123945 NAS-124044 NAS-124077 NAS-124131-23.10 NAS-124137 NAS-124183 NAS-124231 NAS-124232 NAS-124237-23.10 NAS-124325-23.10.0 NAS-124335 NAS-124354 NAS-124430 NAS-124454 NAS-124481-23.10.0 NAS-124481-23.10.1 NAS-124555-23.10.0 NAS-124555-23.10.1 NAS-124666 NAS-124707-23.10.0 NAS-124707-23.10.1 NAS-124716 NAS-124846-23.10.0 NAS-124846-23.10.1 NAS-124892 NAS-124895-23.10.1 NAS-124908 NAS-124951 NAS-124964 NAS-124999 NAS-125092 NAS-125213-23.10.1 NAS-125307 NAS-125532 NAS-125568 NAS-125607-validator NAS-125616-23.10.2 NAS-125654 NAS-125703-23.10.2 NAS-125728 NAS-125931 NAS-126699 NAS-126774-24.04-RC.1 NAS-126774-dragonfish NAS-126795 NAS-126795-test NAS-126795-test2 NAS-127001 NAS-127002-24.04-RC.1 NAS-127022 NAS-127041-24.04-BETA.1 NAS-127049-24.04-RC.1 NAS-127297-24.04-RC.1 NAS-127297-24.10 NAS-127369 NAS-127551 NAS-127551-alt NAS-127589-24.04.0 NAS-127593 NAS-127615-24.04.0 NAS-127660 NAS-127794 NAS-127829-24.10 NAS-127854-24.04.0 NAS-128030 NAS-128045 NAS-128071 NAS-128173 NAS-128209 NAS-128287 NAS-128289 TE-1553-dragonfish TE-1628 auto-129 bugfix/NAS-117941-error-when-removing-pools-and-visit-datasets bugfix/NAS-118282-search-input-fixes bugfix/NAS-118404-dataset-icon-role-double-toooltip bugfix/NAS-118414-warning-modal-icon bugfix/NAS-118415-tree-select-undefined bugfix/NAS-118454-acl-manager-after-dataset-creation-fix bugfix/NAS-118470-multiselext-styles-are-broken bugfix/NAS-118503-datasets-glitch-fix bugfix/NAS-118504-redirect-to-correct-dataset-after-permissions-submit bugfix/NAS-118510-redirect-url-fix-after-manual-change bugfix/NAS-118530-advanced-settings-box-duplicates bugfix/NAS-118541-progress-bar-oberflows bugfix/NAS-118557-replication-task-forbid-custom-retention-policy-cases bugfix/NAS-118600-smb-share-redirect bugfix/NAS-118601-remove-mixed-for-zfs-datasets dataset-tree-tooltips developer/lyy feature/NAS-118058-improve-dashboard-icons-sync-pool-and-storage feature/NAS-118303 feature/NAS-118333-storage-dashboard-icons-update feature/NAS-118334-screentype-enum feature/NAS-118335-improve-spinners-look feature/NAS-118349-datasets-long-names feature/NAS-118360-handle-clipboard-api-not-available feature/NAS-118412-pool-processing-modal feature/NAS-118466-root-path-mnt feature/NAS-118543-user-password-field llll master metrics-enable patch-1 patch-235 rel-v0.0.1 release/22.12 release/22.12-BETA.2 release/22.12-RC.1 release/22.12.1 release/22.12.2 release/22.12.3 release/22.12.4 release/23.10-BETA.1 release/23.10-RC.1 release/23.10.0 release/23.10.1 release/23.10.1.1 release/23.10.1.2 release/23.10.1.3 release/23.10.2 release/24.04-BETA.1 release/24.04-RC.1 release/24.04.0 renediepenbroek/master revert-7745-NAS-120274 stable/bluefin stable/cobia stable/dragonfish test-xxxyyy testing-refine-branchout-process testing-refine-branchout-process2 v0.0.2 TS-24.04-RC.1 TS-24.04-BETA.1 TS-23.10.2 TS-23.10.1.3 TS-23.10.1.2 TS-23.10.1.1 TS-23.10.1 TS-23.10.0.1 TS-23.10.0 TS-23.10-RC.1 TS-23.10-BETA.1 TS-22.12.4.2 TS-22.12.4.1 TS-22.12.4 TS-22.12.3.3 TS-22.12.3.2 TS-22.12.3.1 TS-22.12.3 TS-22.12.2 TS-22.12.1 TS-22.12.0 TS-22.12-RC.1 TS-22.12-BETA.2 TS-12.12.3 DN110M-CS-v2.0
No related merge requests found
Showing with 11369 additions and 11873 deletions
+11369 -11873
......@@ -24,23 +24,33 @@
></ix-search-input>
</div>
<div class="dataset-tree-wrapper">
<div class="dataset-tree-inner">
<div class="dataset-tree-header">
<div>
<span class="dataset-name-header">
{{ 'Dataset Name' | translate }}
</span>
</div>
<div>
{{ 'Used' | translate }}
/
{{ 'Available' | translate }}
</div>
<div>{{ 'Encryption' | translate }}</div>
<div>{{ 'Roles' | translate }}</div>
<div
class="sticky-header"
#ixTreeHeader
(scroll)="updateScroll(scrollTypes.IxTreeHeader)"
>
<div class="dataset-tree-header" [style.width.px]="ixTreeHeaderWidth">
<div>
<span class="dataset-name-header">
{{ 'Dataset Name' | translate }}
</span>
</div>
<div>
{{ 'Used' | translate }}
/
{{ 'Available' | translate }}
</div>
<div>{{ 'Encryption' | translate }}</div>
<div>{{ 'Roles' | translate }}</div>
</div>
</div>
<div
#ixTree
class="dataset-tree-wrapper"
(scroll)="updateScroll(scrollTypes.IxTree)"
>
<div class="dataset-tree-inner" (resized)="onIxTreeWidthChange($event)">
<ix-tree
class="dataset-tree"
[dataSource]="dataSource"
......
......@@ -172,14 +172,18 @@ ix-dataset-details-panel {
width: 100%;
}
.dataset-tree-search {
background-color: var(--bg2);
padding: 16px;
.sticky-header {
overflow: hidden;
position: sticky;
top: -15px;
z-index: 1;
}
.dataset-tree-search {
background-color: var(--bg2);
padding: 16px;
}
.dataset-tree-wrapper {
display: flex;
flex: 1;
......@@ -201,17 +205,13 @@ ix-dataset-details-panel {
}
}
.dataset-tree {
border-top: 2px solid var(--lines);
}
.dataset-tree-header {
@include grid-row();
align-items: center;
background: var(--bg1);
border-bottom: 2px solid var(--lines);
color: var(--fg2);
// Override default fraction based values to avoid misaligned columns
grid-template-columns: auto 125px 125px 125px 40px;
min-height: 48px;
padding-left: 48px;
......@@ -223,7 +223,6 @@ ix-dataset-details-panel {
@media (min-width: $breakpoint-singlecolumn) {
grid-template-columns: auto 125px 125px 125px;
overflow-x: hidden;
}
> div {
......@@ -246,8 +245,10 @@ ix-dataset-details-panel {
.dataset-name-header {
background: linear-gradient(90deg, var(--bg1) 0%, var(--bg1) calc(100% - 13px), transparent 100%);
padding-left: 8px;
left: 15px;
padding-left: 0;
padding-right: 15px;
position: sticky;
}
}
......
......@@ -10,46 +10,71 @@ import {
Component,
OnInit,
AfterViewInit,
OnDestroy,
ViewChild,
ElementRef,
} from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
import { TranslateService } from '@ngx-translate/core';
import { filter, map } from 'rxjs/operators';
import { ResizedEvent } from 'angular-resize-event';
import { Subject, Subscription } from 'rxjs';
import {
debounceTime,
distinctUntilChanged,
filter,
map,
} from 'rxjs/operators';
import { DatasetDetails } from 'app/interfaces/dataset.interface';
import { Job } from 'app/interfaces/job.interface';
import { WebsocketError } from 'app/interfaces/websocket-error.interface';
import { EmptyConfig, EmptyType } from 'app/modules/entity/entity-empty/entity-empty.component';
import {
EmptyConfig,
EmptyType,
} from 'app/modules/entity/entity-empty/entity-empty.component';
import { IxNestedTreeDataSource } from 'app/modules/ix-tree/ix-nested-tree-datasource';
import { flattenTreeWithFilter } from 'app/modules/ix-tree/utils/flattern-tree-with-filter';
import { DatasetTreeStore } from 'app/pages/datasets/store/dataset-store.service';
import { isRootDataset } from 'app/pages/datasets/utils/dataset.utils';
import { DialogService, WebSocketService } from 'app/services';
enum ScrollType {
IxTree = 'ixTree',
IxTreeHeader = 'ixTreeHeader',
}
@UntilDestroy()
@Component({
templateUrl: './dataset-management.component.html',
styleUrls: ['./dataset-management.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class DatasetsManagementComponent implements OnInit, AfterViewInit {
export class DatasetsManagementComponent implements OnInit, AfterViewInit, OnDestroy {
@ViewChild('ixTreeHeader', { static: false }) ixTreeHeader: ElementRef;
@ViewChild('ixTree', { static: false }) ixTree: ElementRef;
isLoading$ = this.datasetStore.isLoading$;
selectedDataset$ = this.datasetStore.selectedDataset$;
dataSource: IxNestedTreeDataSource<DatasetDetails>;
treeControl = new NestedTreeControl<DatasetDetails, string>((dataset) => dataset.children, {
trackBy: (dataset) => dataset.id,
});
readonly hasNestedChild = (_: number, dataset: DatasetDetails): boolean => Boolean(dataset.children?.length);
treeControl = new NestedTreeControl<DatasetDetails, string>(
(dataset) => dataset.children,
{ trackBy: (dataset) => dataset.id },
);
showMobileDetails = false;
isMobileView = false;
systemDataset: string;
isLoading = true;
subscription = new Subscription();
scrollTypes = ScrollType;
ixTreeHeaderWidth: number | null = null;
entityEmptyConf: EmptyConfig = {
type: EmptyType.NoPageData,
large: true,
title: this.translate.instant('No Datasets'),
message: `${this.translate.instant(
'It seems you haven\'t configured pools yet.',
"It seems you haven't configured pools yet.",
)} ${this.translate.instant(
'Please click the button below to create a pool.',
)}`,
......@@ -59,7 +84,8 @@ export class DatasetsManagementComponent implements OnInit, AfterViewInit {
},
};
isLoading = true;
readonly hasNestedChild = (_: number, dataset: DatasetDetails): boolean => Boolean(dataset.children?.length);
private readonly scrollSubject = new Subject<number>();
constructor(
private ws: WebSocketService,
......@@ -70,37 +96,15 @@ export class DatasetsManagementComponent implements OnInit, AfterViewInit {
protected translate: TranslateService,
private dialogService: DialogService,
private breakpointObserver: BreakpointObserver,
) { }
) {}
ngOnInit(): void {
this.datasetStore.loadDatasets();
this.listenForRouteChanges();
this.setupTree();
this.ws.call('systemdataset.config').pipe(
map((config) => config.pool),
untilDestroyed(this),
).subscribe({
next: (systemDataset) => {
this.systemDataset = systemDataset;
},
error: this.handleError,
});
this.isLoading$
.pipe(untilDestroyed(this))
.subscribe((isLoading) => {
this.isLoading = isLoading;
this.cdr.markForCheck();
});
}
handleError = (error: WebsocketError | Job<null, unknown[]>): void => {
this.dialogService.errorReportMiddleware(error);
};
isSystemDataset(dataset: DatasetDetails): boolean {
return isRootDataset(dataset) && this.systemDataset === dataset.name;
this.listenForRouteChanges();
this.loadSystemDatasetConfig();
this.listenForLoading();
this.listenForDatasetScrolling();
}
ngAfterViewInit(): void {
......@@ -118,34 +122,89 @@ export class DatasetsManagementComponent implements OnInit, AfterViewInit {
});
}
onSearch(query: string): void {
this.dataSource.filter(query);
ngOnDestroy(): void {
this.subscription.unsubscribe();
}
private setupTree(): void {
this.datasetStore.datasets$
.pipe(untilDestroyed(this))
loadSystemDatasetConfig(): void {
this.ws
.call('systemdataset.config')
.pipe(
map((config) => config.pool),
untilDestroyed(this),
)
.subscribe({
next: (datasets) => {
this.sortDatasetsByName(datasets);
this.createDataSource(datasets);
this.treeControl.dataNodes = datasets;
this.cdr.markForCheck();
if (!datasets.length) {
return;
}
const routeDatasetId = this.activatedRoute.snapshot.paramMap.get('datasetId');
if (routeDatasetId) {
this.datasetStore.selectDatasetById(routeDatasetId);
} else {
const firstNode = this.treeControl.dataNodes[0];
this.router.navigate(['/datasets', firstNode.id]);
}
next: (systemDataset) => {
this.systemDataset = systemDataset;
},
error: this.handleError,
});
}
listenForLoading(): void {
this.isLoading$.pipe(untilDestroyed(this)).subscribe((isLoading) => {
this.isLoading = isLoading;
this.cdr.markForCheck();
});
}
handleError = (error: WebsocketError | Job<null, unknown[]>): void => {
this.dialogService.errorReportMiddleware(error);
};
isSystemDataset(dataset: DatasetDetails): boolean {
return isRootDataset(dataset) && this.systemDataset === dataset.name;
}
updateScroll(type: ScrollType): void {
this.scrollSubject.next(
type === ScrollType.IxTree ? this.ixTree.nativeElement.scrollLeft : this.ixTreeHeader.nativeElement.scrollLeft,
);
}
onIxTreeWidthChange(event: ResizedEvent): void {
this.ixTreeHeaderWidth = Math.round(event.newRect.width);
}
private listenForDatasetScrolling(): void {
this.subscription.add(
this.scrollSubject
.pipe(debounceTime(0), distinctUntilChanged(), untilDestroyed(this))
.subscribe({
next: (scrollLeft: number) => {
this.ixTreeHeader.nativeElement.scrollLeft = scrollLeft;
this.ixTree.nativeElement.scrollLeft = scrollLeft;
},
}),
);
}
onSearch(query: string): void {
this.dataSource.filter(query);
}
private setupTree(): void {
this.datasetStore.datasets$.pipe(untilDestroyed(this)).subscribe({
next: (datasets) => {
this.sortDatasetsByName(datasets);
this.createDataSource(datasets);
this.treeControl.dataNodes = datasets;
this.cdr.markForCheck();
if (!datasets.length) {
return;
}
const routeDatasetId = this.activatedRoute.snapshot.paramMap.get('datasetId');
if (routeDatasetId) {
this.datasetStore.selectDatasetById(routeDatasetId);
} else {
const firstNode = this.treeControl.dataNodes[0];
this.router.navigate(['/datasets', firstNode.id]);
}
},
error: this.handleError,
});
this.datasetStore.selectedBranch$
.pipe(filter(Boolean), untilDestroyed(this))
......@@ -158,13 +217,15 @@ export class DatasetsManagementComponent implements OnInit, AfterViewInit {
}
private listenForRouteChanges(): void {
this.activatedRoute.params.pipe(
map((params) => params.datasetId),
filter(Boolean),
untilDestroyed(this),
).subscribe((datasetId: string) => {
this.datasetStore.selectDatasetById(datasetId);
});
this.activatedRoute.params
.pipe(
map((params) => params.datasetId),
filter(Boolean),
untilDestroyed(this),
)
.subscribe((datasetId: string) => {
this.datasetStore.selectDatasetById(datasetId);
});
}
private createDataSource(datasets: DatasetDetails[]): void {
......
......@@ -33,7 +33,7 @@
}
&:first-child {
left: 0;
left: 15px;
position: sticky;
@media (max-width: $breakpoint-tablet) {
......
......@@ -10,6 +10,7 @@ import { MatSlideToggleModule } from '@angular/material/slide-toggle';
import { MatSortModule } from '@angular/material/sort';
import { MatTooltipModule } from '@angular/material/tooltip';
import { TranslateModule } from '@ngx-translate/core';
import { AngularResizeEventModule } from 'angular-resize-event';
import { ChartsModule } from 'ng2-charts';
import { NgxFilesizeModule } from 'ngx-filesize';
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
......@@ -81,6 +82,7 @@ import { DatasetNodeComponent } from './components/dataset-node/dataset-node.com
CoreComponents,
AppLoaderModule,
SnapshotsModule,
AngularResizeEventModule,
],
declarations: [
DatasetsManagementComponent,
......
......@@ -34,7 +34,7 @@
}
&:first-child {
left: 0;
left: 15px;
position: sticky;
@media (max-width: $breakpoint-tablet) {
......
......@@ -23,7 +23,7 @@ $columns: 7;
min-height: 48px;
&:first-child {
left: 0;
left: 15px;
position: sticky;
}
......
This diff is collapsed.
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment