Browse Source

UI: Improve image grid loading.

pull/9542/head
Igor Kulikov 3 years ago
parent
commit
414d758429
  1. 4
      ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-widget-select.component.html
  2. 6
      ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-widget-select.component.ts
  3. 3
      ui-ngx/src/app/modules/home/components/grid/scroll-grid.component.html
  4. 52
      ui-ngx/src/app/modules/home/components/grid/scroll-grid.component.ts
  5. 10
      ui-ngx/src/app/modules/home/components/image/image-gallery.component.html
  6. 15
      ui-ngx/src/app/modules/home/components/image/image-gallery.component.scss
  7. 7
      ui-ngx/src/app/modules/home/components/image/image-gallery.component.ts
  8. 2
      ui-ngx/src/app/shared/components/image-input.component.html

4
ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-widget-select.component.html

@ -27,7 +27,7 @@
<ng-template #widgets let-fetchFunction="fetchFunction" let-filter="filter"> <ng-template #widgets let-fetchFunction="fetchFunction" let-filter="filter">
<tb-scroll-grid <tb-scroll-grid
[columns]="columns" [columns]="columns"
[itemSize]="160" [itemSize]="gridWidgetsItemSizeStrategy"
[fetchFunction]="fetchFunction" [fetchFunction]="fetchFunction"
[filter]="filter" [filter]="filter"
[itemCard]="widgetCard" [itemCard]="widgetCard"
@ -74,7 +74,7 @@
<ng-template #bundles> <ng-template #bundles>
<tb-scroll-grid <tb-scroll-grid
[columns]="columns" [columns]="columns"
[itemSize]="160" [itemSize]="gridWidgetsItemSizeStrategy"
[fetchFunction]="widgetBundlesFetchFunction" [fetchFunction]="widgetBundlesFetchFunction"
[filter]="widgetsBundleFilter" [filter]="widgetsBundleFilter"
[itemCard]="widgetsBundleCard" [itemCard]="widgetsBundleCard"

6
ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-widget-select.component.ts

@ -32,6 +32,7 @@ import { isObject } from '@core/utils';
import { PageLink } from '@shared/models/page/page-link'; import { PageLink } from '@shared/models/page/page-link';
import { Direction } from '@shared/models/page/sort-order'; import { Direction } from '@shared/models/page/sort-order';
import { GridEntitiesFetchFunction, ScrollGridColumns } from '@home/models/datasource/scroll-grid-datasource'; import { GridEntitiesFetchFunction, ScrollGridColumns } from '@home/models/datasource/scroll-grid-datasource';
import { ItemSizeStrategy } from '@home/components/grid/scroll-grid.component';
type selectWidgetMode = 'bundles' | 'allWidgets'; type selectWidgetMode = 'bundles' | 'allWidgets';
@ -148,6 +149,11 @@ export class DashboardWidgetSelectComponent implements OnInit {
} }
}; };
gridWidgetsItemSizeStrategy: ItemSizeStrategy = {
defaultItemSize: 160,
itemSizeFunction: itemWidth => (itemWidth - 24) * 0.8 + 76
};
widgetBundlesFetchFunction: GridEntitiesFetchFunction<WidgetsBundle, string>; widgetBundlesFetchFunction: GridEntitiesFetchFunction<WidgetsBundle, string>;
allWidgetsFetchFunction: GridEntitiesFetchFunction<WidgetTypeInfo, WidgetsFilter>; allWidgetsFetchFunction: GridEntitiesFetchFunction<WidgetTypeInfo, WidgetsFilter>;
widgetsFetchFunction: GridEntitiesFetchFunction<WidgetTypeInfo, BundleWidgetsFilter>; widgetsFetchFunction: GridEntitiesFetchFunction<WidgetTypeInfo, BundleWidgetsFilter>;

3
ui-ngx/src/app/modules/home/components/grid/scroll-grid.component.html

@ -15,7 +15,8 @@
limitations under the License. limitations under the License.
--> -->
<cdk-virtual-scroll-viewport #viewport class="tb-scroll-grid-viewport" [itemSize]="itemSize" appendOnly> <cdk-virtual-scroll-viewport #viewport class="tb-scroll-grid-viewport" [itemSize]="calculatedItemSize"
[minBufferPx]="minBuffer" [maxBufferPx]="maxBuffer" appendOnly>
<ng-container *cdkVirtualFor="let itemsRow of dataSource; let row = index; trackBy: trackByItemsRow"> <ng-container *cdkVirtualFor="let itemsRow of dataSource; let row = index; trackBy: trackByItemsRow">
<div *ngIf="itemsRow" class="tb-scroll-grid-items-row" [style]="{gap: gap+'px'}"> <div *ngIf="itemsRow" class="tb-scroll-grid-items-row" [style]="{gap: gap+'px'}">
<div *ngFor="let item of itemsRow; let col = index; trackBy: trackByItem" class="tb-scroll-grid-item-container"> <div *ngFor="let item of itemsRow; let col = index; trackBy: trackByItem" class="tb-scroll-grid-item-container">

52
ui-ngx/src/app/modules/home/components/grid/scroll-grid.component.ts

@ -15,10 +15,10 @@
/// ///
import { import {
AfterViewInit, AfterViewInit, ChangeDetectorRef,
Component, Component,
Input, Input,
OnChanges, OnChanges, OnDestroy,
OnInit, OnInit,
Renderer2, Renderer2,
SimpleChanges, SimpleChanges,
@ -34,6 +34,14 @@ import {
import { BreakpointObserver } from '@angular/cdk/layout'; import { BreakpointObserver } from '@angular/cdk/layout';
import { isObject } from '@app/core/utils'; import { isObject } from '@app/core/utils';
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling'; import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
import { ResizeObserver } from '@juggle/resize-observer';
export type ItemSizeFunction = (itemWidth: number) => number;
export interface ItemSizeStrategy {
defaultItemSize: number;
itemSizeFunction: ItemSizeFunction;
}
@Component({ @Component({
selector: 'tb-scroll-grid', selector: 'tb-scroll-grid',
@ -41,7 +49,7 @@ import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
styleUrls: ['./scroll-grid.component.scss'], styleUrls: ['./scroll-grid.component.scss'],
encapsulation: ViewEncapsulation.None encapsulation: ViewEncapsulation.None
}) })
export class ScrollGridComponent<T, F> implements OnInit, AfterViewInit, OnChanges { export class ScrollGridComponent<T, F> implements OnInit, AfterViewInit, OnChanges, OnDestroy {
@ViewChild('viewport') @ViewChild('viewport')
viewport: CdkVirtualScrollViewport; viewport: CdkVirtualScrollViewport;
@ -56,7 +64,7 @@ export class ScrollGridComponent<T, F> implements OnInit, AfterViewInit, OnChang
filter: F; filter: F;
@Input() @Input()
itemSize = 200; itemSize: number | ItemSizeStrategy = 200;
@Input() @Input()
gap = 12; gap = 12;
@ -75,17 +83,37 @@ export class ScrollGridComponent<T, F> implements OnInit, AfterViewInit, OnChang
dataSource: ScrollGridDatasource<T, F>; dataSource: ScrollGridDatasource<T, F>;
calculatedItemSize: number;
minBuffer: number;
maxBuffer: number;
private contentResize$: ResizeObserver;
constructor(private breakpointObserver: BreakpointObserver, constructor(private breakpointObserver: BreakpointObserver,
private cd: ChangeDetectorRef,
private renderer: Renderer2) { private renderer: Renderer2) {
} }
ngOnInit(): void { ngOnInit(): void {
if (typeof this.itemSize === 'number') {
this.calculatedItemSize = this.itemSize;
} else {
this.calculatedItemSize = this.itemSize.defaultItemSize;
}
this.minBuffer = this.calculatedItemSize;
this.maxBuffer = this.calculatedItemSize * 2;
this.dataSource = new ScrollGridDatasource<T, F>(this.breakpointObserver, this.columns, this.fetchFunction, this.filter); this.dataSource = new ScrollGridDatasource<T, F>(this.breakpointObserver, this.columns, this.fetchFunction, this.filter);
} }
ngAfterViewInit() { ngAfterViewInit() {
this.renderer.setStyle(this.viewport._contentWrapper.nativeElement, 'gap', this.gap + 'px'); this.renderer.setStyle(this.viewport._contentWrapper.nativeElement, 'gap', this.gap + 'px');
this.renderer.setStyle(this.viewport._contentWrapper.nativeElement, 'padding', this.gap + 'px'); this.renderer.setStyle(this.viewport._contentWrapper.nativeElement, 'padding', this.gap + 'px');
if (!(typeof this.itemSize === 'number')) {
this.contentResize$ = new ResizeObserver(() => {
this.onContentResize();
});
this.contentResize$.observe(this.viewport._contentWrapper.nativeElement);
}
} }
ngOnChanges(changes: SimpleChanges): void { ngOnChanges(changes: SimpleChanges): void {
@ -97,6 +125,12 @@ export class ScrollGridComponent<T, F> implements OnInit, AfterViewInit, OnChang
} }
} }
ngOnDestroy() {
if (this.contentResize$) {
this.contentResize$.disconnect();
}
}
isObject(value: any): boolean { isObject(value: any): boolean {
return isObject(value); return isObject(value);
} }
@ -120,4 +154,14 @@ export class ScrollGridComponent<T, F> implements OnInit, AfterViewInit, OnChang
public deleteItem(index: number) { public deleteItem(index: number) {
this.dataSource.deleteItem(index); this.dataSource.deleteItem(index);
} }
private onContentResize() {
const contentWidth = this.viewport._contentWrapper.nativeElement.getBoundingClientRect().width;
const columns = this.dataSource.currentColumns;
const itemWidth = (contentWidth - this.gap * (columns + 1)) / columns;
this.calculatedItemSize = (this.itemSize as ItemSizeStrategy).itemSizeFunction(itemWidth);
this.minBuffer = this.calculatedItemSize;
this.maxBuffer = this.calculatedItemSize * 2;
this.cd.markForCheck();
}
} }

10
ui-ngx/src/app/modules/home/components/image/image-gallery.component.html

@ -260,7 +260,7 @@
<div fxFlex class="mat-padding" *ngIf="mode === 'grid'" fxLayout="column"> <div fxFlex class="mat-padding" *ngIf="mode === 'grid'" fxLayout="column">
<tb-scroll-grid fxFlex <tb-scroll-grid fxFlex
[columns]="gridColumns" [columns]="gridColumns"
[itemSize]="150" [itemSize]="gridImagesItemSizeStrategy"
[fetchFunction]="gridImagesFetchFunction" [fetchFunction]="gridImagesFetchFunction"
[filter]="gridImagesFilter" [filter]="gridImagesFilter"
[itemCard]="imageCard" [itemCard]="imageCard"
@ -339,5 +339,11 @@
</div> </div>
</ng-template> </ng-template>
<ng-template #imageLoadingCard> <ng-template #imageLoadingCard>
<div> TODO </div> <div class="tb-image-card tb-primary-fill loading-cell">
<div class="tb-image-preview-container">
<div class="tb-image-preview-spacer"></div>
<div class="tb-image-preview"></div>
</div>
<div class="tb-image-details"></div>
</div>
</ng-template> </ng-template>

15
ui-ngx/src/app/modules/home/components/image/image-gallery.component.scss

@ -153,6 +153,7 @@ $tb-button-selected-color: rgb(255, 110, 64) !default;
} }
.tb-image-details { .tb-image-details {
min-height: 52px;
flex: 1; flex: 1;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -205,6 +206,14 @@ $tb-button-selected-color: rgb(255, 110, 64) !default;
} }
} }
} }
&.loading-cell {
.tb-image-preview-container, .tb-image-details {
background: linear-gradient(110deg, #ececec 8%, #f5f5f5 18%, #ececec 33%);
border-radius: 5px;
background-size: 200% 100%;
animation: 1s shine linear infinite;
}
}
} }
.table-container { .table-container {
@ -229,3 +238,9 @@ $tb-button-selected-color: rgb(255, 110, 64) !default;
} }
} }
} }
@keyframes shine {
to {
background-position-x: -200%;
}
}

7
ui-ngx/src/app/modules/home/components/image/image-gallery.component.ts

@ -51,7 +51,7 @@ import { NULL_UUID } from '@shared/models/id/has-uuid';
import { getCurrentAuthUser } from '@core/auth/auth.selectors'; import { getCurrentAuthUser } from '@core/auth/auth.selectors';
import { Authority } from '@shared/models/authority.enum'; import { Authority } from '@shared/models/authority.enum';
import { GridEntitiesFetchFunction, ScrollGridColumns } from '@home/models/datasource/scroll-grid-datasource'; import { GridEntitiesFetchFunction, ScrollGridColumns } from '@home/models/datasource/scroll-grid-datasource';
import { ScrollGridComponent } from '@home/components/grid/scroll-grid.component'; import { ItemSizeStrategy, ScrollGridComponent } from '@home/components/grid/scroll-grid.component';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { import {
UploadImageDialogComponent, UploadImageDialogComponent,
@ -112,6 +112,11 @@ export class ImageGalleryComponent extends PageComponent implements OnInit, OnDe
gridImagesFetchFunction: GridEntitiesFetchFunction<ImageResourceInfo, string>; gridImagesFetchFunction: GridEntitiesFetchFunction<ImageResourceInfo, string>;
gridImagesFilter = ''; gridImagesFilter = '';
gridImagesItemSizeStrategy: ItemSizeStrategy = {
defaultItemSize: 200,
itemSizeFunction: itemWidth => itemWidth + 72
};
authUser = getCurrentAuthUser(this.store); authUser = getCurrentAuthUser(this.store);
private updateDataSubscription: Subscription; private updateDataSubscription: Subscription;

2
ui-ngx/src/app/shared/components/image-input.component.html

@ -64,7 +64,7 @@
</div> </div>
</ng-container> </ng-container>
<div *ngIf="(showFileName && fileName) || (maxSizeByte && !disabled)" class="tb-image-info-container"> <div *ngIf="(showFileName && fileName) || (maxSizeByte && !disabled)" class="tb-image-info-container">
<div *ngIf="showFileName && fileName" class="tb-image-file-name">{{ fileName }}</div> <div *ngIf="showFileName && fileName" class="tb-image-file-name" [title]="fileName">{{ fileName }}</div>
<div *ngIf="maxSizeByte && !disabled" class="tb-image-file-hint" translate [translateParams]="{ size: maxSizeByte | fileSize}">dashboard.maximum-upload-file-size</div> <div *ngIf="maxSizeByte && !disabled" class="tb-image-file-hint" translate [translateParams]="{ size: maxSizeByte | fileSize}">dashboard.maximum-upload-file-size</div>
</div> </div>
</div> </div>

Loading…
Cancel
Save