Implement NGrids filter function
- Implements NGrids new "filteredData" function, to restore old filter behavior. Export a quick-filtered list is now possible. - More controll about List-View-Table UI - Use NGrid target-events for better mutli-select behavior - Changes the behavior of hidden headers to ignore paddings-changes - filters are more resistant to errors and storage loss
This commit is contained in:
parent
450819c035
commit
2372408e4c
@ -226,27 +226,29 @@ export abstract class BaseFilterListService<V extends BaseViewModel> {
|
||||
const newDefinitions = this.getFilterDefinitions();
|
||||
|
||||
this.store.get<OsFilter[]>('filter_' + this.name).then(storedFilter => {
|
||||
for (const newDef of newDefinitions) {
|
||||
let count = 0;
|
||||
const matchingExistingFilter = storedFilter.find(oldDef => oldDef.property === newDef.property);
|
||||
for (const option of newDef.options) {
|
||||
if (typeof option === 'object') {
|
||||
if (matchingExistingFilter && matchingExistingFilter.options) {
|
||||
const existingOption = matchingExistingFilter.options.find(
|
||||
o =>
|
||||
typeof o !== 'string' &&
|
||||
JSON.stringify(o.condition) === JSON.stringify(option.condition)
|
||||
) as OsFilterOption;
|
||||
if (existingOption) {
|
||||
option.isActive = existingOption.isActive;
|
||||
}
|
||||
if (option.isActive) {
|
||||
count++;
|
||||
if (!!storedFilter) {
|
||||
for (const newDef of newDefinitions) {
|
||||
let count = 0;
|
||||
const matchingExistingFilter = storedFilter.find(oldDef => oldDef.property === newDef.property);
|
||||
for (const option of newDef.options) {
|
||||
if (typeof option === 'object') {
|
||||
if (matchingExistingFilter && matchingExistingFilter.options) {
|
||||
const existingOption = matchingExistingFilter.options.find(
|
||||
o =>
|
||||
typeof o !== 'string' &&
|
||||
JSON.stringify(o.condition) === JSON.stringify(option.condition)
|
||||
) as OsFilterOption;
|
||||
if (existingOption) {
|
||||
option.isActive = existingOption.isActive;
|
||||
}
|
||||
if (option.isActive) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
newDef.count = count;
|
||||
}
|
||||
newDef.count = count;
|
||||
}
|
||||
|
||||
this.filterDefinitions = newDefinitions;
|
||||
|
@ -11,25 +11,30 @@
|
||||
<!-- vScrollFixed="110" -->
|
||||
<!-- vScrollAuto () -->
|
||||
<pbl-ngrid
|
||||
[ngClass]="showFilterBar ? 'virtual-scroll-with-head-bar' : 'virtual-scroll-full-page'"
|
||||
[ngClass]="{
|
||||
'virtual-scroll-with-head-bar ngrid-hide-head': showFilterBar,
|
||||
'virtual-scroll-full-page': !showFilterBar,
|
||||
'multiselect': multiSelect
|
||||
}"
|
||||
cellTooltip
|
||||
showHeader="!showFilterBar"
|
||||
[showHeader]="!showFilterBar"
|
||||
matCheckboxSelection="selection"
|
||||
vScrollFixed="110"
|
||||
[dataSource]="dataSource"
|
||||
[columns]="columnSet"
|
||||
[hideColumns]="hiddenColumns"
|
||||
(rowClick)="onSelectRow($event)"
|
||||
>
|
||||
<!-- "row" has the view model -->
|
||||
<!-- "value" has the property, that was defined in the columnDefinition -->
|
||||
<!-- "col" has a column reference -->
|
||||
|
||||
<!-- Projector column -->
|
||||
<div *pblNgridCellDef="'projector'; row as viewModel" class="fill">
|
||||
<div *pblNgridCellDef="'projector'; row as viewModel" class="fill ngrid-lg">
|
||||
<os-projector-button class="projector-button" [object]="getProjectable(viewModel)"></os-projector-button>
|
||||
</div>
|
||||
|
||||
<!-- Slot transclusion for the individual cells -->
|
||||
<ng-content select=".cell-slot"></ng-content>
|
||||
<ng-content class="ngrid-lg" select=".cell-slot"></ng-content>
|
||||
</pbl-ngrid>
|
||||
</mat-drawer-container>
|
||||
|
@ -3,3 +3,17 @@
|
||||
.projector-button {
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.pbl-ngrid-row {
|
||||
height: 110px;
|
||||
}
|
||||
|
||||
.pbl-ngrid-cell {
|
||||
height: inherit;
|
||||
}
|
||||
|
||||
.multiselect {
|
||||
.pbl-ngrid-cell {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
@ -9,17 +9,19 @@ import {
|
||||
ViewEncapsulation,
|
||||
ChangeDetectorRef
|
||||
} from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
import { PblDataSource, columnFactory, PblNgridComponent, createDS } from '@pebula/ngrid';
|
||||
import { PblColumnDefinition, PblNgridColumnSet } from '@pebula/ngrid/lib/table';
|
||||
import { PblNgridDataMatrixRow } from '@pebula/ngrid/target-events';
|
||||
|
||||
import { BaseViewModel } from 'app/site/base/base-view-model';
|
||||
import { BaseProjectableViewModel } from 'app/site/base/base-projectable-view-model';
|
||||
import { BaseSortListService } from 'app/core/ui-services/base-sort-list.service';
|
||||
import { BaseViewModelWithContentObject } from 'app/site/base/base-view-model-with-content-object';
|
||||
import { PblDataSource, columnFactory, PblNgridComponent, createDS } from '@pebula/ngrid';
|
||||
import { BaseFilterListService } from 'app/core/ui-services/base-filter-list.service';
|
||||
import { Observable } from 'rxjs';
|
||||
import { BaseRepository } from 'app/core/repositories/base-repository';
|
||||
import { BaseModel } from 'app/shared/models/base/base-model';
|
||||
import { PblColumnDefinition, PblNgridColumnSet } from '@pebula/ngrid/lib/table';
|
||||
import { Permission, OperatorService } from 'app/core/core-services/operator.service';
|
||||
import { StorageService } from 'app/core/core-services/storage.service';
|
||||
import { ViewportService } from 'app/core/ui-services/viewport.service';
|
||||
@ -221,7 +223,7 @@ export class ListViewTableComponent<V extends BaseViewModel, M extends BaseModel
|
||||
* Gets the amount of filtered data
|
||||
*/
|
||||
public get countFilter(): number {
|
||||
return this.dataSource.source.length;
|
||||
return this.dataSource.filteredData.length;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -358,7 +360,7 @@ export class ListViewTableComponent<V extends BaseViewModel, M extends BaseModel
|
||||
// Define the columns. Has to be in the OnInit cause "columns" is slower than
|
||||
// the constructor of this class
|
||||
this.columnSet = columnFactory()
|
||||
.default({ width: this.columnMinWidth, css: 'ngrid-lg' })
|
||||
.default({ width: this.columnMinWidth })
|
||||
.table(...this.defaultColumns, ...this.columns)
|
||||
.build();
|
||||
|
||||
@ -368,6 +370,22 @@ export class ListViewTableComponent<V extends BaseViewModel, M extends BaseModel
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic click handler for rows. Allow so (multi) select anywhere
|
||||
* @param event the clicked row
|
||||
*/
|
||||
public onSelectRow(event: PblNgridDataMatrixRow<V>): void {
|
||||
if (this.multiSelect) {
|
||||
const clickedModel: V = event.row;
|
||||
const alreadySelected = this.dataSource.selection.isSelected(clickedModel);
|
||||
if (alreadySelected) {
|
||||
this.dataSource.selection.deselect(clickedModel);
|
||||
} else {
|
||||
this.dataSource.selection.select(clickedModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Depending on the view, the view model in the row can either be a
|
||||
* `BaseViewModelWithContentObject` or a `BaseViewModelWithContentObject`.
|
||||
|
@ -58,6 +58,7 @@ import { AutofocusDirective } from './directives/autofocus.directive';
|
||||
// PblNgrid. Cleanup Required.
|
||||
import { PblNgridModule } from '@pebula/ngrid';
|
||||
import { PblNgridMaterialModule } from '@pebula/ngrid-material';
|
||||
import { PblNgridTargetEventsModule } from '@pebula/ngrid/target-events';
|
||||
|
||||
// components
|
||||
import { HeadBarComponent } from './components/head-bar/head-bar.component';
|
||||
@ -152,7 +153,8 @@ import { AttachmentControlComponent } from './components/attachment-control/atta
|
||||
CdkTreeModule,
|
||||
ScrollingModule,
|
||||
PblNgridModule,
|
||||
PblNgridMaterialModule
|
||||
PblNgridMaterialModule,
|
||||
PblNgridTargetEventsModule
|
||||
],
|
||||
exports: [
|
||||
FormsModule,
|
||||
@ -226,6 +228,7 @@ import { AttachmentControlComponent } from './components/attachment-control/atta
|
||||
SpeakerButtonComponent,
|
||||
PblNgridModule,
|
||||
PblNgridMaterialModule,
|
||||
PblNgridTargetEventsModule,
|
||||
ListViewTableComponent,
|
||||
AgendaContentObjectFormComponent,
|
||||
ExtensionFieldComponent
|
||||
|
@ -303,7 +303,7 @@ export class AgendaListComponent extends BaseListViewComponent<ViewItem> impleme
|
||||
* Export all items as CSV
|
||||
*/
|
||||
public csvExportItemList(): void {
|
||||
this.csvExport.exportItemList(this.dataSource.source);
|
||||
this.csvExport.exportItemList(this.dataSource.filteredData);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -312,7 +312,7 @@ export class AgendaListComponent extends BaseListViewComponent<ViewItem> impleme
|
||||
*/
|
||||
public onDownloadPdf(): void {
|
||||
const filename = this.translate.instant('Agenda');
|
||||
this.pdfService.download(this.agendaPdfService.agendaListToDocDef(this.dataSource.source), filename);
|
||||
this.pdfService.download(this.agendaPdfService.agendaListToDocDef(this.dataSource.filteredData), filename);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -89,7 +89,7 @@ export abstract class BaseListViewComponent<V extends BaseViewModel> extends Bas
|
||||
* Select all files in the current data source
|
||||
*/
|
||||
public selectAll(): void {
|
||||
this.dataSource.selection.select(...this.dataSource.source);
|
||||
this.dataSource.selection.select(...this.dataSource.filteredData);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -66,7 +66,7 @@
|
||||
</div>
|
||||
|
||||
<!-- the actual file manager -->
|
||||
<pbl-ngrid class="file-manager-table" showHeader="false" vScrollAuto [dataSource]="dataSource" [columns]="columnSet">
|
||||
<pbl-ngrid class="file-manager-table ngrid-hide-head" showHeader="false" vScrollAuto [dataSource]="dataSource" [columns]="columnSet">
|
||||
<!-- Icon column -->
|
||||
<div *pblNgridCellDef="'icon'; row as mediafile" class="fill clickable">
|
||||
<a class="detail-link" target="_blank" [routerLink]="mediafile.url" *ngIf="mediafile.is_file"> </a>
|
||||
|
@ -57,7 +57,7 @@
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
height: calc(100vh - 170px);
|
||||
height: calc(100vh - 105px);
|
||||
|
||||
.pbl-ngrid-row {
|
||||
$size: 60px;
|
||||
@ -67,11 +67,4 @@
|
||||
height: $size !important;
|
||||
}
|
||||
}
|
||||
|
||||
// For some reason, hiding the table header adds an empty meta bar.
|
||||
.pbl-ngrid-container {
|
||||
> div {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -38,6 +38,7 @@
|
||||
|
||||
<mat-card class="os-card">
|
||||
<os-list-view-table
|
||||
class="block-list"
|
||||
[repo]="repo"
|
||||
[showFilterBar]="false"
|
||||
[columns]="tableColumnDefinition"
|
||||
|
@ -1,5 +1,13 @@
|
||||
@import '~assets/styles/tables.scss';
|
||||
|
||||
::ng-deep .mat-form-field {
|
||||
.block-list {
|
||||
display: block;
|
||||
|
||||
.virtual-scroll-full-page {
|
||||
height: calc(100vh - 150px);
|
||||
}
|
||||
}
|
||||
|
||||
.mat-form-field {
|
||||
width: 50%;
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Component, OnInit, ViewEncapsulation } from '@angular/core';
|
||||
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
@ -23,7 +23,8 @@ import { ViewMotionBlock } from 'app/site/motions/models/view-motion-block';
|
||||
@Component({
|
||||
selector: 'os-motion-block-list',
|
||||
templateUrl: './motion-block-list.component.html',
|
||||
styleUrls: ['./motion-block-list.component.scss']
|
||||
styleUrls: ['./motion-block-list.component.scss'],
|
||||
encapsulation: ViewEncapsulation.None
|
||||
})
|
||||
export class MotionBlockListComponent extends BaseListViewComponent<ViewMotionBlock> implements OnInit {
|
||||
/**
|
||||
|
@ -308,7 +308,7 @@ export class MotionListComponent extends BaseListViewComponent<ViewMotion> imple
|
||||
|
||||
exportDialogRef.afterClosed().subscribe((exportInfo: ExportFormData) => {
|
||||
if (exportInfo && exportInfo.format) {
|
||||
const data = this.isMultiSelect ? this.selectedRows : this.dataSource.source;
|
||||
const data = this.isMultiSelect ? this.selectedRows : this.dataSource.filteredData;
|
||||
if (exportInfo.format === FileFormat.PDF) {
|
||||
try {
|
||||
this.pdfExport.exportMotionCatalog(data, exportInfo);
|
||||
|
@ -245,7 +245,7 @@ export class UserListComponent extends BaseListViewComponent<ViewUser> implement
|
||||
*/
|
||||
public csvExportUserList(): void {
|
||||
this.csvExport.export(
|
||||
this.dataSource.source,
|
||||
this.dataSource.filteredData,
|
||||
[
|
||||
{ property: 'title' },
|
||||
{ property: 'first_name', label: 'Given name' },
|
||||
@ -270,7 +270,7 @@ export class UserListComponent extends BaseListViewComponent<ViewUser> implement
|
||||
* (access information, including personal information such as initial passwords)
|
||||
*/
|
||||
public onDownloadAccessPdf(): void {
|
||||
this.userPdf.exportMultipleUserAccessPDF(this.dataSource.source);
|
||||
this.userPdf.exportMultipleUserAccessPDF(this.dataSource.filteredData);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -278,7 +278,7 @@ export class UserListComponent extends BaseListViewComponent<ViewUser> implement
|
||||
* with all users currently matching the filter
|
||||
*/
|
||||
public pdfExportUserList(): void {
|
||||
this.userPdf.exportUserList(this.dataSource.source);
|
||||
this.userPdf.exportUserList(this.dataSource.filteredData);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -191,11 +191,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.ngrid-lg {
|
||||
height: 110px;
|
||||
min-height: 90px;
|
||||
}
|
||||
|
||||
/** Define the general style-rules */
|
||||
* {
|
||||
font-family: OSFont, Fira Sans, Roboto, Arial, Helvetica, sans-serif;
|
||||
@ -629,6 +624,7 @@ button.mat-menu-item.selected {
|
||||
* Depending in mobile-mode and desktop mode we need to subtract different values
|
||||
* from 100vh
|
||||
*/
|
||||
// no os-sort-filter-bar
|
||||
.virtual-scroll-full-page {
|
||||
height: calc(100vh - 64px);
|
||||
}
|
||||
@ -638,14 +634,16 @@ button.mat-menu-item.selected {
|
||||
display: inline-block;
|
||||
line-height: 150%;
|
||||
}
|
||||
|
||||
// with os-sort-filter-bar
|
||||
.virtual-scroll-with-head-bar {
|
||||
height: calc(100vh - 125px);
|
||||
}
|
||||
|
||||
.ngrid-hide-head {
|
||||
// For some reason, hiding the table header adds an empty meta bar.
|
||||
.pbl-ngrid-container {
|
||||
> div {
|
||||
display: none;
|
||||
height: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user