Empty states, table updates
This commit is contained in:
parent
1b7104ba14
commit
a659dcecd7
@ -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';
|
||||
|
||||
@ -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];
|
||||
|
||||
15
src/lib/empty-states/empty-state.module.ts
Normal file
15
src/lib/empty-states/empty-state.module.ts
Normal 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 {}
|
||||
21
src/lib/empty-states/empty-state/empty-state.component.html
Normal file
21
src/lib/empty-states/empty-state/empty-state.component.html
Normal 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>
|
||||
27
src/lib/empty-states/empty-state/empty-state.component.scss
Normal file
27
src/lib/empty-states/empty-state/empty-state.component.scss
Normal 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;
|
||||
}
|
||||
}
|
||||
26
src/lib/empty-states/empty-state/empty-state.component.ts
Normal file
26
src/lib/empty-states/empty-state/empty-state.component.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
2
src/lib/empty-states/index.ts
Normal file
2
src/lib/empty-states/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './empty-state.module';
|
||||
export * from './empty-state/empty-state.component';
|
||||
@ -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
|
||||
];
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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"-->
|
||||
<!--></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"-->
|
||||
<!--></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>
|
||||
|
||||
|
||||
@ -1,6 +1,3 @@
|
||||
//iqser-table-header::ng-deep .header-item {
|
||||
// padding-right: 16px;
|
||||
//}
|
||||
@import '../../../assets/styles/common';
|
||||
|
||||
cdk-virtual-scroll-viewport {
|
||||
|
||||
@ -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);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user