Empty states, table updates

This commit is contained in:
Adina Țeudan 2021-08-27 16:51:13 +03:00
parent 1b7104ba14
commit a659dcecd7
12 changed files with 142 additions and 30 deletions

View File

@ -12,3 +12,4 @@ export * from './lib/misc';
export * from './lib/loading';
export * from './lib/error';
export * from './lib/search';
export * from './lib/empty-states';

View File

@ -15,6 +15,7 @@ import { IqserHelpModeModule } from './help-mode';
import { IqserIconsModule } from './icons';
import { IqserButtonsModule } from './buttons';
import { IqserScrollbarModule } from './scrollbar';
import { IqserEmptyStatesModule } from './empty-states';
const matModules = [MatIconModule, MatProgressSpinnerModule];
const modules = [
@ -25,7 +26,8 @@ const modules = [
IqserFiltersModule,
IqserInputsModule,
IqserHelpModeModule,
IqserScrollbarModule
IqserScrollbarModule,
IqserEmptyStatesModule
];
const components = [StatusBarComponent, FullPageLoadingIndicatorComponent, FullPageErrorComponent];
const pipes = [SortByPipe, HumanizePipe];

View File

@ -0,0 +1,15 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { IqserIconsModule } from '../icons';
import { EmptyStateComponent } from './empty-state/empty-state.component';
import { IqserButtonsModule } from '../buttons';
const modules = [IqserIconsModule, IqserButtonsModule];
const components = [EmptyStateComponent];
@NgModule({
declarations: [...components],
imports: [CommonModule, ...modules],
exports: [...components]
})
export class IqserEmptyStatesModule {}

View File

@ -0,0 +1,21 @@
<div
[ngStyle]="{
'padding-top': verticalPadding + 'px',
'padding-left': horizontalPadding + 'px',
'padding-right': horizontalPadding + 'px'
}"
class="empty-state"
>
<mat-icon *ngIf="icon" [svgIcon]="icon"></mat-icon>
<div class="ng-content-wrapper heading-l">
<ng-content></ng-content>
</div>
<div class="heading-l">{{ text }}</div>
<iqser-icon-button
(action)="action.emit()"
*ngIf="showButton"
[icon]="buttonIcon"
[label]="buttonLabel"
[type]="iconButtonTypes.primary"
></iqser-icon-button>
</div>

View File

@ -0,0 +1,27 @@
@import '../../../assets/styles/common';
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
> mat-icon {
height: 60px;
width: 60px;
opacity: 0.1;
}
.heading-l {
color: $grey-7;
}
> .heading-l,
iqser-icon-button {
margin-top: 24px;
}
.ng-content-wrapper:not(:empty) + .heading-l {
display: none;
}
}

View File

@ -0,0 +1,26 @@
import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { IconButtonTypes } from '../../buttons';
import { Required } from '../../utils';
@Component({
selector: 'iqser-empty-state',
templateUrl: './empty-state.component.html',
styleUrls: ['./empty-state.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class EmptyStateComponent implements OnInit {
readonly iconButtonTypes = IconButtonTypes;
@Input() @Required() text!: string;
@Input() icon?: string;
@Input() showButton = true;
@Input() buttonIcon = 'red:plus';
@Input() buttonLabel?: string;
@Input() horizontalPadding = 100;
@Input() verticalPadding = 120;
@Output() readonly action = new EventEmitter();
ngOnInit(): void {
this.showButton = this.showButton && this.action.observers.length > 0;
}
}

View File

@ -0,0 +1,2 @@
export * from './empty-state.module';
export * from './empty-state/empty-state.component';

View File

@ -13,6 +13,7 @@ import { ScrollingModule } from '@angular/cdk/scrolling';
import { IqserIconsModule } from '../icons';
import { IqserScrollbarModule } from '../scrollbar';
import { RouterModule } from '@angular/router';
import { IqserEmptyStatesModule } from '../empty-states';
const matModules = [MatTooltipModule];
const components = [TableHeaderComponent, TableComponent, TableColumnNameComponent, ScrollButtonComponent];
@ -22,6 +23,7 @@ const modules = [
IqserInputsModule,
IqserIconsModule,
IqserScrollbarModule,
IqserEmptyStatesModule,
ScrollingModule,
RouterModule
];

View File

@ -9,6 +9,7 @@ export interface TableColumnConfig<T> {
readonly rightIcon?: string;
readonly rightIconTooltip?: string;
readonly notTranslatable?: boolean;
readonly width?: string; // TODO: make required
readonly width?: string;
readonly template?: TemplateRef<unknown>; // TODO: make required
last?: boolean;
}

View File

@ -4,22 +4,20 @@
[selectionEnabled]="selectionEnabled"
[tableColumnConfigs]="tableColumnConfigs"
[tableHeaderLabel]="tableHeaderLabel"
></iqser-table-header>
>
<ng-container *ngTemplateOutlet="headerTemplate"></ng-container>
</iqser-table-header>
<!--TODO: Empty states-->
<!--<redaction-empty-state-->
<!-- (action)="openAddEditDictionaryDialog()"-->
<!-- *ngIf="entitiesService.noData$ | async"-->
<!-- [buttonLabel]="'dictionary-listing.no-data.action' | translate"-->
<!-- [showButton]="currentUser.isAdmin"-->
<!-- [text]="'dictionary-listing.no-data.title' | translate"-->
<!-- icon="red:dictionary"-->
<!--&gt;</redaction-empty-state>-->
<iqser-empty-state
(action)="noDataAction.emit()"
*ngIf="listingComponent.entitiesService.noData$ | async"
[buttonLabel]="noDataButtonLabel"
[icon]="noDataIcon"
[showButton]="showNoDataButton"
[text]="noDataText"
></iqser-empty-state>
<!--<redaction-empty-state-->
<!-- *ngIf="noMatch$ | async"-->
<!-- [text]="'dictionary-listing.no-match.title' | translate"-->
<!--&gt;</redaction-empty-state>-->
<iqser-empty-state *ngIf="listingComponent.noMatch$ | async" [text]="noMatchText"></iqser-empty-state>
<cdk-virtual-scroll-viewport #scrollViewport [itemSize]="itemSize" iqserHasScrollbar>
<div
@ -33,15 +31,10 @@
</div>
<ng-container *ngFor="let column of tableColumnConfigs">
<ng-container *ngIf="!!column.template">
<ng-container *ngTemplateOutlet="column.template; context: { entity: entity }"></ng-container>
</ng-container>
<!-- TODO: Remove-->
<div *ngIf="!column.template">n</div>
<ng-container *ngTemplateOutlet="column.template; context: { entity: entity }"></ng-container>
</ng-container>
<div class="actions-container">
<div *ngIf="!!actionsTemplate" class="actions-container">
<ng-container *ngTemplateOutlet="actionsTemplate; context: { entity: entity }"></ng-container>
</div>

View File

@ -1,6 +1,3 @@
//iqser-table-header::ng-deep .header-item {
// padding-right: 16px;
//}
@import '../../../assets/styles/common';
cdk-virtual-scroll-viewport {

View File

@ -1,9 +1,22 @@
import { ChangeDetectionStrategy, Component, forwardRef, Inject, Input, OnInit, TemplateRef, ViewChild } from '@angular/core';
import {
ChangeDetectionStrategy,
Component,
EventEmitter,
forwardRef,
Inject,
Input,
OnInit,
Output,
TemplateRef,
ViewChild
} from '@angular/core';
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
import { Required } from '../../utils';
import { Listable, TableColumnConfig } from '../models';
import { ListingComponent } from '../listing-component.directive';
const SCROLLBAR_WIDTH = 11;
@Component({
selector: 'iqser-table',
templateUrl: './table.component.html',
@ -13,6 +26,7 @@ import { ListingComponent } from '../listing-component.directive';
export class TableComponent<T extends Listable> implements OnInit {
@Input() bulkActions?: TemplateRef<unknown>;
@Input() actionsTemplate?: TemplateRef<unknown>;
@Input() headerTemplate?: TemplateRef<unknown>;
@Input() @Required() itemSize!: number;
@Input() @Required() tableColumnConfigs!: readonly TableColumnConfig<T>[];
@Input() @Required() tableHeaderLabel!: string;
@ -21,6 +35,12 @@ export class TableComponent<T extends Listable> implements OnInit {
@Input() emptyColumnWidth?: string;
@Input() classes?: string;
@Input() routerLinkFn?: (entity: T) => string | string[];
@Input() noDataText?: string;
@Input() noDataIcon?: string;
@Input() noDataButtonLabel?: string;
@Input() showNoDataButton = false;
@Output() readonly noDataAction = new EventEmitter<void>();
@Input() noMatchText?: string;
@ViewChild(CdkVirtualScrollViewport, { static: true }) private readonly _viewport!: CdkVirtualScrollViewport;
constructor(@Inject(forwardRef(() => ListingComponent)) private _parent: ListingComponent<T>) {}
@ -30,9 +50,14 @@ export class TableComponent<T extends Listable> implements OnInit {
}
ngOnInit(): void {
this._patchConfig();
this._setStyles();
}
private _patchConfig() {
this.tableColumnConfigs[this.tableColumnConfigs.length - 1].last = true;
}
private _setStyles(): void {
const element = this._viewport.elementRef.nativeElement;
this._setColumnsWidth(element);
@ -46,10 +71,10 @@ export class TableComponent<T extends Listable> implements OnInit {
gridTemplateColumnsHover += 'auto ';
}
for (const config of this.tableColumnConfigs) {
gridTemplateColumnsHover += `${config.width as string} `; // TODO remove cast
gridTemplateColumnsHover += `${config.width || '1fr'} `;
}
gridTemplateColumnsHover += this.emptyColumnWidth;
const gridTemplateColumns = gridTemplateColumnsHover + ' 11px';
gridTemplateColumnsHover += this.emptyColumnWidth || ''; // TODO: Check if it's always 1fr
const gridTemplateColumns = `${gridTemplateColumnsHover} ${SCROLLBAR_WIDTH}px`;
element.style.setProperty('--gridTemplateColumns', gridTemplateColumns);
element.style.setProperty('--gridTemplateColumnsHover', gridTemplateColumnsHover);