ngx-mat-multi-sort
Version:
An extension for the angular material table to support multi-sorting.
632 lines (621 loc) • 39.6 kB
JavaScript
import * as i0 from '@angular/core';
import { Directive, Input, Optional, Inject, ChangeDetectionStrategy, ViewEncapsulation, Component, ContentChild, ViewChild, NgModule } from '@angular/core';
import * as i1 from '@angular/material/sort';
import { MatSort, MatSortHeader, matSortAnimations } from '@angular/material/sort';
import * as i3 from '@angular/cdk/a11y';
import { moveItemInArray, CdkDropList, CdkDrag, CdkDragHandle, DragDropModule } from '@angular/cdk/drag-drop';
import * as i1$1 from '@angular/cdk/overlay';
import { BlockScrollStrategy } from '@angular/cdk/overlay';
import { TemplatePortal } from '@angular/cdk/portal';
import * as i2 from '@angular/material/checkbox';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatChipSet, MatChipRow, MatChipsModule } from '@angular/material/chips';
import { NgTemplateOutlet, CommonModule } from '@angular/common';
import { MatTooltip, MatTooltipModule } from '@angular/material/tooltip';
import { MatIcon, MatIconModule } from '@angular/material/icon';
import * as i3$1 from '@angular/forms';
import { FormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatCommonModule } from '@angular/material/core';
import { MatDividerModule } from '@angular/material/divider';
import { MatMenuModule } from '@angular/material/menu';
import { MatDialogModule } from '@angular/material/dialog';
import { Subject, BehaviorSubject, delay, filter, tap } from 'rxjs';
import { DataSource } from '@angular/cdk/collections';
class MatMultiSort extends MatSort {
constructor() {
super(...arguments);
this.start = 'asc';
this.actives = [];
this.directions = [];
}
ngOnInit() {
super.ngOnInit();
}
sort(sortable) {
this.updateMultipleSorts(sortable);
super.sort(sortable);
}
updateMultipleSorts(sortable) {
const i = this.actives.findIndex(activeId => activeId === sortable.id);
if (this.isActive(sortable)) {
if (this.activeDirection(sortable) === (sortable.start ? sortable.start : this.start)) {
this.directions.splice(i, 1, this.getNextSortDirection(sortable));
}
else {
this.actives.splice(i, 1);
this.directions.splice(i, 1);
}
}
else {
this.actives.push(sortable.id);
this.directions.push(sortable.start ? sortable.start : this.start);
}
}
isActive(sortable) {
const i = this.actives.findIndex(activeId => activeId === sortable.id);
return i > -1;
}
activeDirection(sortable) {
const i = this.actives.findIndex(activeId => activeId === sortable.id);
return this.directions[i] || (sortable.start ? sortable.start : this.start);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: MatMultiSort, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.0.4", type: MatMultiSort, isStandalone: true, selector: "[matMultiSort]", exportAs: ["matMultiSort"], usesInheritance: true, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: MatMultiSort, decorators: [{
type: Directive,
args: [{
selector: '[matMultiSort]',
exportAs: 'matMultiSort'
}]
}] });
class MatMultiSortHeaderComponent extends MatSortHeader {
constructor(_intl, changeDetectorRef, _sort, _columnDef, _focusMonitor, _elementRef) {
super(_intl, changeDetectorRef, _sort, _columnDef, _focusMonitor, _elementRef);
this._intl = _intl;
this._sort = _sort;
this._columnDef = _columnDef;
this.start = 'asc';
this._disableViewStateAnimation = false;
}
_isSorted() {
return this._sort.actives.findIndex(activeId => activeId === this.id) > -1;
}
_sortId() {
return this._sort.actives.findIndex(activeId => activeId === this.id) + 1;
}
_renderArrow() {
return !this._isDisabled() || this._isSorted();
}
getSortDirection() {
const i = this._sort.actives.findIndex(activeIds => activeIds === this.id);
const direction = this._sort.directions[i];
return this._isSorted() ? direction : (this.start || this._sort.start);
}
_getArrowViewState() {
return undefined;
}
_getArrowDirectionState() {
return undefined;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: MatMultiSortHeaderComponent, deps: [{ token: i1.MatSortHeaderIntl }, { token: i0.ChangeDetectorRef }, { token: MatMultiSort, optional: true }, { token: 'C2_SORT_HEADER_COLUMN_DEF', optional: true }, { token: i3.FocusMonitor }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.4", type: MatMultiSortHeaderComponent, isStandalone: true, selector: "[mat-multi-sort-header]", inputs: { id: ["mat-multi-sort-header", "id"] }, providers: [{ provide: MatSort, useExisting: MatMultiSort }], exportAs: ["matMultiSortHeader"], usesInheritance: true, ngImport: i0, template: "<div class=\"mat-sort-header-container\"\n [class.mat-sort-header-sorted]=\"_isSorted()\"\n [class.mat-sort-header-position-before]=\"arrowPosition === 'before'\"\n [class.mat-sort-header-ascending]=\"getSortDirection() === 'asc'\"\n [class.mat-sort-header-deascending]=\"getSortDirection() === 'desc'\">\n\n <div class=\"mat-sort-header-content\">\n <ng-content></ng-content>\n </div>\n <!-- Disable animations while a current animation is running -->\n @if (_renderArrow() && _isSorted()) {\n <div class=\"mat-sort-header-arrow\"\n [@arrowOpacity]=\"_getArrowViewState()\"\n [@arrowPosition]=\"_getArrowViewState()\"\n [@allowChildren]=\"_getArrowDirectionState()\"\n (@arrowPosition.start)=\"_disableViewStateAnimation = true\"\n (@arrowPosition.done)=\"_disableViewStateAnimation = false\">\n <svg [class.asc]=\"getSortDirection() === 'asc'\"\n [class.desc]=\"getSortDirection() === 'desc'\"\n viewBox=\"0 -960 960 960\" focusable=\"false\" aria-hidden=\"true\">\n <path d=\"M440-240v-368L296-464l-56-56 240-240 240 240-56 56-144-144v368h-80Z\"></path>\n </svg>\n </div>\n }\n @if (_isSorted()) {\n <div>{{_sortId()}}</div>\n }\n</div>\n", styles: [".mat-sort-header-container{display:flex;cursor:pointer;align-items:center}.mat-sort-header-disabled .mat-sort-header-container{cursor:default}.mat-sort-header-position-before{flex-direction:row-reverse}.mat-sort-header-content{text-align:center;display:flex;align-items:center}.mat-sort-header-arrow{height:12px;width:12px;min-width:12px;position:relative;display:flex}.mat-sort-header-arrow svg{width:24px;height:24px;position:absolute;top:50%;left:50%;transform:translate(-50%) translateY(-50%);transform-origin:top left}.mat-sort-header-arrow svg.asc{rotate:0}.mat-sort-header-arrow svg.desc{rotate:180deg}.mat-sort-header-arrow,[dir=rtl] .mat-sort-header-position-before .mat-sort-header-arrow{margin:0 0 0 6px}.mat-sort-header-position-before .mat-sort-header-arrow,[dir=rtl] .mat-sort-header-arrow{margin:0 6px 0 0}\n"], animations: [
matSortAnimations.indicator,
matSortAnimations.leftPointer,
matSortAnimations.rightPointer,
matSortAnimations.arrowOpacity,
matSortAnimations.arrowPosition,
matSortAnimations.allowChildren
], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: MatMultiSortHeaderComponent, decorators: [{
type: Component,
args: [{ selector: '[mat-multi-sort-header]', exportAs: 'matMultiSortHeader', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, imports: [], animations: [
matSortAnimations.indicator,
matSortAnimations.leftPointer,
matSortAnimations.rightPointer,
matSortAnimations.arrowOpacity,
matSortAnimations.arrowPosition,
matSortAnimations.allowChildren
], providers: [{ provide: MatSort, useExisting: MatMultiSort }], template: "<div class=\"mat-sort-header-container\"\n [class.mat-sort-header-sorted]=\"_isSorted()\"\n [class.mat-sort-header-position-before]=\"arrowPosition === 'before'\"\n [class.mat-sort-header-ascending]=\"getSortDirection() === 'asc'\"\n [class.mat-sort-header-deascending]=\"getSortDirection() === 'desc'\">\n\n <div class=\"mat-sort-header-content\">\n <ng-content></ng-content>\n </div>\n <!-- Disable animations while a current animation is running -->\n @if (_renderArrow() && _isSorted()) {\n <div class=\"mat-sort-header-arrow\"\n [@arrowOpacity]=\"_getArrowViewState()\"\n [@arrowPosition]=\"_getArrowViewState()\"\n [@allowChildren]=\"_getArrowDirectionState()\"\n (@arrowPosition.start)=\"_disableViewStateAnimation = true\"\n (@arrowPosition.done)=\"_disableViewStateAnimation = false\">\n <svg [class.asc]=\"getSortDirection() === 'asc'\"\n [class.desc]=\"getSortDirection() === 'desc'\"\n viewBox=\"0 -960 960 960\" focusable=\"false\" aria-hidden=\"true\">\n <path d=\"M440-240v-368L296-464l-56-56 240-240 240 240-56 56-144-144v368h-80Z\"></path>\n </svg>\n </div>\n }\n @if (_isSorted()) {\n <div>{{_sortId()}}</div>\n }\n</div>\n", styles: [".mat-sort-header-container{display:flex;cursor:pointer;align-items:center}.mat-sort-header-disabled .mat-sort-header-container{cursor:default}.mat-sort-header-position-before{flex-direction:row-reverse}.mat-sort-header-content{text-align:center;display:flex;align-items:center}.mat-sort-header-arrow{height:12px;width:12px;min-width:12px;position:relative;display:flex}.mat-sort-header-arrow svg{width:24px;height:24px;position:absolute;top:50%;left:50%;transform:translate(-50%) translateY(-50%);transform-origin:top left}.mat-sort-header-arrow svg.asc{rotate:0}.mat-sort-header-arrow svg.desc{rotate:180deg}.mat-sort-header-arrow,[dir=rtl] .mat-sort-header-position-before .mat-sort-header-arrow{margin:0 0 0 6px}.mat-sort-header-position-before .mat-sort-header-arrow,[dir=rtl] .mat-sort-header-arrow{margin:0 6px 0 0}\n"] }]
}], ctorParameters: () => [{ type: i1.MatSortHeaderIntl }, { type: i0.ChangeDetectorRef }, { type: MatMultiSort, decorators: [{
type: Optional
}] }, { type: undefined, decorators: [{
type: Inject,
args: ['C2_SORT_HEADER_COLUMN_DEF']
}, {
type: Optional
}] }, { type: i3.FocusMonitor }, { type: i0.ElementRef }], propDecorators: { id: [{
type: Input,
args: ['mat-multi-sort-header']
}] } });
class MatMultiSortTableSettingsComponent {
set tableData(tableData) {
this._tableData = tableData;
}
constructor(overlay, viewContainerRef, viewportRuler) {
this.overlay = overlay;
this.viewContainerRef = viewContainerRef;
this.viewportRuler = viewportRuler;
this.sort = [];
this.sortToolTip = '';
this.closeDialogOnChoice = true;
this.scrollStrategy = new BlockScrollStrategy(this.viewportRuler, document);
this.disableSortIndicators = false;
}
ngOnInit() {
this.sort = this.getSort();
this._tableData.sortObservable.subscribe(() => this.sort = this.getSort());
this._tableData.onColumnsChange().subscribe(() => this.sort = this.getSort());
}
openDialog() {
const button = this.buttonRef.nativeElement;
const positionStrategyBuilder = this.overlay.position();
const positionStrategy = positionStrategyBuilder
.flexibleConnectedTo(button)
.withFlexibleDimensions(true)
.withViewportMargin(10)
.withGrowAfterOpen(true)
.withPush(true)
.withPositions([{
originX: 'end',
originY: 'bottom',
overlayX: 'end',
overlayY: 'top'
}]);
this.overlayRef = this.overlay.create({
hasBackdrop: true,
backdropClass: 'cdk-overlay-transparent-backdrop',
panelClass: 'column-overlay',
positionStrategy,
scrollStrategy: this.scrollStrategy
});
const templatePortal = new TemplatePortal(this.templateRef, this.viewContainerRef);
this.overlayRef.attach(templatePortal);
this.overlayRef.backdropClick().subscribe(() => {
this.overlayRef.dispose();
});
}
drop(event) {
moveItemInArray(this._tableData.columns, event.previousIndex, event.currentIndex);
this._tableData.displayedColumns = this._tableData.columns.filter(c => c.isActive).map(c => c.id);
this._tableData.storeTableSettings();
}
toggle() {
this._tableData.displayedColumns = this._tableData.columns.filter(c => {
if (!c.isActive) {
this.sort = this.sort.filter(s => s.id !== c.id);
}
return c.isActive;
}).map(c => c.id);
this.updateSort();
if (this.closeDialogOnChoice) {
this.overlayRef.dispose();
}
}
dropSort(event) {
moveItemInArray(this.sort, event.previousIndex, event.currentIndex);
this.updateSort();
}
getSort() {
const sorting = [];
for (let i = 0; i < this._tableData.sortParams.length; i++) {
sorting.push({
id: this._tableData.sortParams[i],
name: this._tableData.columns.find(c => c.id === this._tableData.sortParams[i])?.name ?? '',
direction: this._tableData.sortDirs[i]
});
}
return sorting;
}
remove(id) {
this.sort = this.sort.filter(v => v.id !== id);
this.updateSort();
}
updateDirection(id) {
const i = this.sort.findIndex(v => v.id === id);
if (this.sort[i].direction === 'asc') {
this.sort[i].direction = 'desc';
}
else {
this.sort[i].direction = 'asc';
}
this.updateSort();
}
updateSort() {
this._tableData.sortParams = this.sort.map(v => v.id);
this._tableData.sortDirs = this.sort.map(v => v.direction);
this._tableData.updateSortHeaders();
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: MatMultiSortTableSettingsComponent, deps: [{ token: i1$1.Overlay }, { token: i0.ViewContainerRef }, { token: i1$1.ViewportRuler }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.4", type: MatMultiSortTableSettingsComponent, isStandalone: true, selector: "mat-multi-sort-table-settings", inputs: { sortToolTip: "sortToolTip", closeDialogOnChoice: "closeDialogOnChoice", scrollStrategy: "scrollStrategy", tableData: "tableData", disableSortIndicators: "disableSortIndicators" }, queries: [{ propertyName: "sortIndicatorRef", first: true, predicate: ["sortIndicator"], descendants: true }], viewQueries: [{ propertyName: "templateRef", first: true, predicate: ["templateRef"], descendants: true, static: true }, { propertyName: "buttonRef", first: true, predicate: ["settingsMenu"], descendants: true }], ngImport: i0, template: "<div class=\"table-settings\">\n <div class=\"table-settings-sort\">\n <mat-chip-set class=\"drag-chip-list\" cdkDropList cdkDropListOrientation='horizontal'\n (cdkDropListDropped)=\"dropSort($event)\" [disabled]=\"disableSortIndicators\">\n @for (item of sort; track item) {\n <mat-chip-row class=\"drag-chip\" cdkDrag (removed)=\"remove(item.id)\"\n (click)=\"updateDirection(item.id)\">\n <div class=\"chip-content\">\n @if (sortIndicatorRef) {\n <ng-container\n [ngTemplateOutlet]=\"sortIndicatorRef\"\n [ngTemplateOutletContext]=\"{direction:item.direction, columnName: item.name }\">\n </ng-container>\n } @else {\n <div>\n {{item.name}}:\n <div class=\"sorting\" [matTooltip]=\"sortToolTip\">\n {{item.direction}}\n </div>\n </div>\n }\n <mat-icon class=\"mat-mdc-chip-trailing-icon\" (click)=\"remove(item.id)\">cancel</mat-icon>\n </div>\n </mat-chip-row>\n }\n </mat-chip-set>\n </div>\n <div style=\"flex: 1 1 auto;\"></div>\n <div #settingsMenu (click)=\"openDialog()\" class=\"table-settings-menu\">\n <ng-content #menuRef></ng-content>\n </div>\n</div>\n\n<ng-template #templateRef>\n <div cdkDropList class=\"column-list\" (cdkDropListDropped)=\"drop($event)\">\n @for (column of _tableData.columns; track column) {\n <div class=\"column-item\" cdkDrag>\n <mat-icon cdkDragHandle>drag_indicator</mat-icon>\n <mat-checkbox [(ngModel)]=\"column.isActive\" (change)=\"toggle()\">{{column.name}}</mat-checkbox>\n </div>\n }\n </div>\n</ng-template>\n", styles: [".table-settings{display:flex}.table-settings .table-settings-menu{margin:8px 16px}.table-settings-sort{margin:auto 0}.chip-content{display:flex;align-items:center}.chip-content mat-icon{opacity:.6}.sorting{display:inline-block;margin:0 6px}.sorting:hover{cursor:pointer}.drag-chip{border:solid 1px rgba(0,0,0,.12);background-color:#fff}.drag-chip:hover{cursor:move;background-color:#fff}.drag-chip:hover:after{opacity:0}.drag-chip:focus:after{opacity:0}.drag-chip-list.cdk-drop-list-dragging .drag-chip:not(.cdk-drag-placeholder){transition:transform .25s cubic-bezier(0,0,.2,1)}.column-list{max-height:70vh;overflow:auto;border-radius:4px;padding:1rem;box-shadow:0 11px 15px -7px #0003,0 24px 38px 3px #00000024,0 9px 46px 8px #0000001f;background-color:#fff;color:#000000de}.column-item{height:48px;display:flex;justify-content:flex-start;align-items:center;margin:1px;padding:0 16px 0 8px}.column-item mat-icon{margin-right:16px}.column-item mat-checkbox{line-height:48px;color:#000000de;font-size:14px;font-weight:400}.column-item>mat-icon:hover{cursor:grab;border-top:solid 1px rgba(0,0,0,.12);border-bottom:solid 1px rgba(0,0,0,.12)}.cdk-drag-preview{box-sizing:border-box;border-radius:var(--mat-chip-container-shape-radius, 8px);box-shadow:0 5px 5px -3px #0003,0 8px 10px 1px #00000024,0 3px 14px 2px #0000001f;overflow:hidden}.cdk-drag-placeholder{opacity:0}.cdk-drag-animating{transition:transform .25s cubic-bezier(0,0,.2,1)}.column-item:last-child{border:none}.column-list.cdk-drop-list-dragging .column-item:not(.cdk-drag-placeholder){transition:transform .25s cubic-bezier(0,0,.2,1)}\n"], dependencies: [{ kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i2.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "directive", type: CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i3$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: MatChipSet, selector: "mat-chip-set", inputs: ["disabled", "role", "tabIndex"] }, { kind: "component", type: MatChipRow, selector: "mat-chip-row, [mat-chip-row], mat-basic-chip-row, [mat-basic-chip-row]", inputs: ["editable"], outputs: ["edited"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: MatMultiSortTableSettingsComponent, decorators: [{
type: Component,
args: [{ selector: 'mat-multi-sort-table-settings', imports: [MatCheckboxModule, CdkDropList, NgTemplateOutlet, MatTooltip, MatIcon, CdkDrag, CdkDragHandle, FormsModule, MatChipSet, MatChipRow], template: "<div class=\"table-settings\">\n <div class=\"table-settings-sort\">\n <mat-chip-set class=\"drag-chip-list\" cdkDropList cdkDropListOrientation='horizontal'\n (cdkDropListDropped)=\"dropSort($event)\" [disabled]=\"disableSortIndicators\">\n @for (item of sort; track item) {\n <mat-chip-row class=\"drag-chip\" cdkDrag (removed)=\"remove(item.id)\"\n (click)=\"updateDirection(item.id)\">\n <div class=\"chip-content\">\n @if (sortIndicatorRef) {\n <ng-container\n [ngTemplateOutlet]=\"sortIndicatorRef\"\n [ngTemplateOutletContext]=\"{direction:item.direction, columnName: item.name }\">\n </ng-container>\n } @else {\n <div>\n {{item.name}}:\n <div class=\"sorting\" [matTooltip]=\"sortToolTip\">\n {{item.direction}}\n </div>\n </div>\n }\n <mat-icon class=\"mat-mdc-chip-trailing-icon\" (click)=\"remove(item.id)\">cancel</mat-icon>\n </div>\n </mat-chip-row>\n }\n </mat-chip-set>\n </div>\n <div style=\"flex: 1 1 auto;\"></div>\n <div #settingsMenu (click)=\"openDialog()\" class=\"table-settings-menu\">\n <ng-content #menuRef></ng-content>\n </div>\n</div>\n\n<ng-template #templateRef>\n <div cdkDropList class=\"column-list\" (cdkDropListDropped)=\"drop($event)\">\n @for (column of _tableData.columns; track column) {\n <div class=\"column-item\" cdkDrag>\n <mat-icon cdkDragHandle>drag_indicator</mat-icon>\n <mat-checkbox [(ngModel)]=\"column.isActive\" (change)=\"toggle()\">{{column.name}}</mat-checkbox>\n </div>\n }\n </div>\n</ng-template>\n", styles: [".table-settings{display:flex}.table-settings .table-settings-menu{margin:8px 16px}.table-settings-sort{margin:auto 0}.chip-content{display:flex;align-items:center}.chip-content mat-icon{opacity:.6}.sorting{display:inline-block;margin:0 6px}.sorting:hover{cursor:pointer}.drag-chip{border:solid 1px rgba(0,0,0,.12);background-color:#fff}.drag-chip:hover{cursor:move;background-color:#fff}.drag-chip:hover:after{opacity:0}.drag-chip:focus:after{opacity:0}.drag-chip-list.cdk-drop-list-dragging .drag-chip:not(.cdk-drag-placeholder){transition:transform .25s cubic-bezier(0,0,.2,1)}.column-list{max-height:70vh;overflow:auto;border-radius:4px;padding:1rem;box-shadow:0 11px 15px -7px #0003,0 24px 38px 3px #00000024,0 9px 46px 8px #0000001f;background-color:#fff;color:#000000de}.column-item{height:48px;display:flex;justify-content:flex-start;align-items:center;margin:1px;padding:0 16px 0 8px}.column-item mat-icon{margin-right:16px}.column-item mat-checkbox{line-height:48px;color:#000000de;font-size:14px;font-weight:400}.column-item>mat-icon:hover{cursor:grab;border-top:solid 1px rgba(0,0,0,.12);border-bottom:solid 1px rgba(0,0,0,.12)}.cdk-drag-preview{box-sizing:border-box;border-radius:var(--mat-chip-container-shape-radius, 8px);box-shadow:0 5px 5px -3px #0003,0 8px 10px 1px #00000024,0 3px 14px 2px #0000001f;overflow:hidden}.cdk-drag-placeholder{opacity:0}.cdk-drag-animating{transition:transform .25s cubic-bezier(0,0,.2,1)}.column-item:last-child{border:none}.column-list.cdk-drop-list-dragging .column-item:not(.cdk-drag-placeholder){transition:transform .25s cubic-bezier(0,0,.2,1)}\n"] }]
}], ctorParameters: () => [{ type: i1$1.Overlay }, { type: i0.ViewContainerRef }, { type: i1$1.ViewportRuler }], propDecorators: { templateRef: [{
type: ViewChild,
args: ['templateRef', { static: true }]
}], buttonRef: [{
type: ViewChild,
args: ['settingsMenu']
}], sortIndicatorRef: [{
type: ContentChild,
args: ['sortIndicator', { static: false }]
}], sortToolTip: [{
type: Input
}], closeDialogOnChoice: [{
type: Input
}], scrollStrategy: [{
type: Input
}], tableData: [{
type: Input
}], disableSortIndicators: [{
type: Input
}] } });
class MatMultiSortModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: MatMultiSortModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.0.4", ngImport: i0, type: MatMultiSortModule, imports: [CommonModule,
MatCommonModule,
MatDividerModule,
DragDropModule,
MatIconModule,
MatCheckboxModule,
MatMenuModule,
MatButtonModule,
FormsModule,
MatChipsModule,
MatTooltipModule,
MatDialogModule,
MatMultiSort,
MatMultiSortHeaderComponent,
MatMultiSortTableSettingsComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: MatMultiSortModule, imports: [CommonModule,
MatCommonModule,
MatDividerModule,
DragDropModule,
MatIconModule,
MatCheckboxModule,
MatMenuModule,
MatButtonModule,
FormsModule,
MatChipsModule,
MatTooltipModule,
MatDialogModule,
MatMultiSortTableSettingsComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: MatMultiSortModule, decorators: [{
type: NgModule,
args: [{
declarations: [],
exports: [],
imports: [
CommonModule,
MatCommonModule,
MatDividerModule,
DragDropModule,
MatIconModule,
MatCheckboxModule,
MatMenuModule,
MatButtonModule,
FormsModule,
MatChipsModule,
MatTooltipModule,
MatDialogModule,
MatMultiSort,
MatMultiSortHeaderComponent,
MatMultiSortTableSettingsComponent
]
}]
}] });
class Settings {
constructor(key) {
this._key = key;
this._columns = [];
this._sortParams = [];
this._sortDirs = [];
}
load() {
const storedValue = localStorage.getItem(this._key);
const value = storedValue ? JSON.parse(storedValue) : undefined;
if (value) {
this._columns = value._columns || [];
this._sortDirs = value._sortDirs || [];
this._sortParams = value._sortParams || [];
}
}
save() {
const settingsString = JSON.stringify(this);
localStorage.setItem(this._key, settingsString);
}
get columns() {
return this._columns;
}
get sortParams() {
return this._sortParams;
}
get sortDirs() {
return this._sortDirs;
}
get key() {
return this._key;
}
set columns(columns) {
this._columns = columns;
}
set sortParams(sortParams) {
this._sortParams = sortParams;
}
set sortDirs(sortDirs) {
this._sortDirs = sortDirs;
}
}
class TableData {
// TODO refactor
constructor(columns, options) {
this._displayedColumns = [];
this.pageSize = 0;
this.pageIndex = 0;
this._totalElements = 0;
this._key = '';
this._nextObservable = new Subject();
this._previousObservable = new Subject();
this._sizeObservable = new Subject();
this._sortObservable = new Subject();
this._sortHeadersObservable = new Subject();
this._columns = new BehaviorSubject(columns.map(c => { if (c.isActive === undefined) {
c.isActive = true;
} return c; }));
if (options) {
if (options.pageSizeOptions && options.pageSizeOptions.length < 1) {
throw Error('Array of pageSizeOptions must contain at least one entry');
}
if (options.defaultSortParams) {
options.defaultSortParams.map(s => {
if (this._columns.value.find(column => column.id === s) === undefined) {
throw Error(`Provided sort parameter "${s}" is not a column.`);
}
});
}
this._sortParams = options.defaultSortParams || [];
this._sortDirs = options.defaultSortDirs || [];
if (this._sortParams.length !== this._sortDirs.length) {
this._sortDirs = this._sortParams.map(() => 'asc');
}
this._totalElements = options.totalElements || 0;
this._pageSizeOptions = options.pageSizeOptions || [10, 20, 50, 100];
this._key = options.localStorageKey ?? '';
}
else {
this._pageSizeOptions = [10, 20, 50, 100];
this._sortParams = [];
this._sortDirs = [];
}
this.init();
}
onSortEvent() {
this._sortParams = this._dataSource.sort['actives'];
this._sortDirs = this._dataSource.sort['directions'];
this._clientSideSort();
this._sortObservable.next();
this.storeTableSettings();
}
onPaginationEvent($event) {
const tmpPageSize = this.pageSize;
this.pageSize = $event.pageSize;
this.pageIndex = $event.pageIndex;
if (tmpPageSize !== this.pageSize) {
this._sizeObservable.next();
}
else if ($event.previousPageIndex !== undefined && $event.previousPageIndex < $event.pageIndex) {
this._nextObservable.next();
}
else if ($event.previousPageIndex !== undefined && $event.previousPageIndex > $event.pageIndex) {
this._previousObservable.next();
}
}
updateSortHeaders() {
// Dirty hack to display default sort column(s)
const temp = Object.assign([], this._displayedColumns);
this._sortHeadersObservable.next([]);
this._sortHeadersObservable.next(temp);
this._clientSideSort();
this._sortObservable.next();
this.storeTableSettings();
}
// this fixes an infine loop of rerendering
subscribeSortHeaders() {
this._sortHeadersObservable.pipe(delay(0),
// ignore when there is no update in the sort (params or dirs)
filter(() => this._displayedSortDirs !== this.sortDirs && this._displayedSortParams !== this.sortParams), tap((column) => {
// update the displayed sort when it is not the empty array
if (column.length > 0) {
this._displayedSortDirs = this.sortDirs;
this._displayedSortParams = this.sortParams;
}
})).subscribe(columns => this._displayedColumns = columns);
}
init() {
this.subscribeSortHeaders();
if (this._key) {
const settings = new Settings(this._key);
settings.load();
if (this._isLocalStorageSettingsValid(settings)) {
// load column configuration from localstorage and update the name
this.columns = settings.columns.map(storedColumn => {
return { ...storedColumn, name: this.columns.find(column => column.id === storedColumn.id)?.name ?? storedColumn.name };
});
this._sortDirs = settings.sortDirs;
this._sortParams = settings.sortParams;
}
else {
console.warn("Stored tableSettings are invalid. Using default");
}
}
this.displayedColumns = this.columns.filter(c => c.isActive).map(c => c.id);
}
_clientSideSort() {
this._dataSource.orderData();
}
_isLocalStorageSettingsValid(settings) {
// check if number of columns matching
if (settings.columns.length !== this._columns.value.length) {
return false;
}
// check if columns are the same
for (const column of settings.columns) {
const match = this._columns.value.find(c => c.id === column.id);
if (match === undefined) {
return false;
}
}
return true;
}
storeTableSettings() {
if (this._key) {
const settings = new Settings(this._key);
settings.columns = this._columns.value;
settings.sortParams = this._sortParams;
settings.sortDirs = this._sortDirs;
settings.save();
}
}
set totalElements(totalElements) {
this._totalElements = totalElements;
}
get totalElements() {
return this._totalElements;
}
set displayedColumns(displayedColumns) {
this._displayedColumns = displayedColumns;
this._columns.next(this._columns.value.map(c => {
c.isActive = this._displayedColumns.includes(c.id);
return c;
}));
}
get displayedColumns() {
return this._displayedColumns;
}
set dataSource(dataSource) {
this._dataSource = dataSource;
if (this._sortParams.length > 0) {
this._dataSource.sort.actives = this._sortParams;
this._dataSource.sort.directions = this._sortDirs.map(v => v);
this.updateSortHeaders();
}
}
get dataSource() {
return this._dataSource;
}
set data(data) {
this._dataSource.data = data;
this._clientSideSort();
}
set columns(v) {
this._columns.next(v.map(c => { if (c.isActive === undefined) {
c.isActive = true;
} return c; }));
}
onColumnsChange() {
return this._columns;
}
updateColumnNames(v) {
const dict = {};
v.forEach(c => dict[c.id] = c.name);
this._columns.next(this._columns.value.map(c => { c.name = dict[c.id] || c.name; return c; }));
}
get nextObservable() {
return this._nextObservable;
}
get previousObservable() {
return this._previousObservable;
}
get sizeObservable() {
return this._sizeObservable;
}
get sortObservable() {
return this._sortObservable;
}
get sortParams() {
return this._sortParams;
}
get sortDirs() {
return this._sortDirs;
}
get columns() {
return this._columns.value;
}
get pageSizeOptions() {
return this._pageSizeOptions;
}
set sortParams(v) {
this._sortParams = v;
this._dataSource.sort.actives = this._sortParams;
}
set sortDirs(v) {
this._sortDirs = v;
this._dataSource.sort.directions = this._sortDirs.map(elem => elem);
}
}
class MatMultiSortTableDataSource extends DataSource {
constructor(sort, clientSideSorting = false) {
super();
this._data = new BehaviorSubject([]);
this.sort = sort;
this.clientSideSorting = clientSideSorting;
}
set data(data) {
this._data.next(data);
}
get data() {
return this._data.value;
}
connect() {
return this._data;
}
disconnect() {
this._data.complete();
}
orderData() {
this._data.next(this.sortData(this._data.value, this.sort.actives, this.sort.directions));
}
sortData(data, actives, directions) {
const _data = Object.assign(new Array(), data);
if (this.clientSideSorting) {
return _data.sort((i1, i2) => {
return this._sortData(i1, i2, actives, directions);
});
}
return _data;
}
_sortData(d1, d2, params, dirs) {
// @ts-expect-error -- need a typesafe way to express these accessor operations, ts-ignore could be a solution
// if there's not a suitable solution offered by typescript
if (d1[params[0]] > d2[params[0]]) {
return dirs[0] === 'asc' ? 1 : -1;
// @ts-expect-error --- same
}
else if (d1[params[0]] < d2[params[0]]) {
return dirs[0] === 'asc' ? -1 : 1;
}
else {
if (params.length > 1) {
params = params.slice(1, params.length);
dirs = dirs.slice(1, dirs.length);
return this._sortData(d1, d2, params, dirs);
}
else {
return 0;
}
}
}
}
/*
* Public API Surface of mat-multi-sort
*/
/**
* Generated bundle index. Do not edit.
*/
export { MatMultiSort, MatMultiSortHeaderComponent, MatMultiSortModule, MatMultiSortTableDataSource, MatMultiSortTableSettingsComponent, TableData };
//# sourceMappingURL=ngx-mat-multi-sort.mjs.map