[AAE-7819] Change column order - enable drag and drop for datatable - [1/3] (#7567)

* [AAE-7819] Enable drag and drop for datatable [1/3]

* [AAE-7819] Change column order - load and save columns order preferences for PROCESSES - [2/3] (#7568)

* [AAE-7819] Load and save column order preferences for processes

* [AAE-7819] Load and save column order preferences for tasks [3/3] (#7569)

* fix css

* fix icon module import

* Fix unit tests

* Fix test

* Fix e2e

* Fix C279927
This commit is contained in:
Bartosz Sekuła
2022-04-11 08:01:02 +02:00
committed by GitHub
parent e89cea79be
commit 48c3fac018
33 changed files with 614 additions and 136 deletions

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="currentColor"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M11 18c0 1.1-.9 2-2 2s-2-.9-2-2 .9-2 2-2 2 .9 2 2zm-2-8c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0-6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm6 4c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"/></svg>

After

Width:  |  Height:  |  Size: 456 B

View File

@@ -25,6 +25,9 @@ import { Component, ContentChild, Input, OnInit, TemplateRef } from '@angular/co
})
export class DataColumnComponent implements OnInit {
@Input()
id: string = '';
/** Data source key. Can be either a column/property key like `title`
* or a property path like `createdBy.name`.
*/
@@ -45,6 +48,10 @@ export class DataColumnComponent implements OnInit {
@Input()
sortable: boolean = true;
/* Enable drag and drop for header column */
@Input()
draggable: boolean = false;
/** Display title of the column, typically used for column headers. You can use the
* i18n resource key to get it translated automatically.
*/

View File

@@ -8,21 +8,26 @@
[class.adf-datatable--empty--header-visible]="isEmpty() && isHeaderVisible()">
<div *ngIf="isHeaderVisible()" class="adf-datatable-header" role="rowgroup" [ngClass]="{ 'adf-sr-only': !isHeaderVisible() }">
<adf-datatable-row
cdkDropList
cdkDropListOrientation="horizontal"
data-automation-id="datatable-row-header"
[disabled]="!isHeaderVisible()"
class="adf-datatable-row"
*ngIf="display === 'list'"
role="row">
<!-- Actions (left) -->
<div *ngIf="actions && actionsPosition === 'left'" class="adf-actions-column adf-datatable-cell-header">
<span class="adf-sr-only">{{ 'ADF-DATATABLE.ACCESSIBILITY.ACTIONS' | translate }}</span>
</div>
<!-- Columns -->
<div *ngIf="multiselect" class="adf-datatable-cell-header adf-datatable-checkbox">
<mat-checkbox [indeterminate]="isSelectAllIndeterminate" [checked]="isSelectAllChecked" (change)="onSelectAllClick($event)" class="adf-checkbox-sr-only">{{ 'ADF-DATATABLE.ACCESSIBILITY.SELECT_ALL' | translate }}</mat-checkbox>
</div>
<div class="adf-datatable-cell--{{col.type || 'text'}} {{col.cssClass}} adf-datatable-cell-header"
*ngFor="let col of data.getColumns()"
*ngFor="let col of data.getColumns(); let columnIndex = index"
[class.adf-sortable]="col.sortable"
[attr.data-automation-id]="'auto_id_' + col.key"
[class.adf-datatable__header--sorted-asc]="isColumnSorted(col, 'asc')"
@@ -32,14 +37,59 @@
role="columnheader"
[attr.tabindex]="isHeaderVisible() ? 0 : null"
[attr.aria-sort]="col.sortable ? (getAriaSort(col) | translate) : null"
cdkDrag
cdkDragBoundary="adf-datatable-row"
cdkDragLockAxis="x"
(cdkDragStarted)="isDraggingHeaderColumn = true"
(cdkDragDropped)="onDropHeaderColumn($event)"
[cdkDragDisabled]="!col.draggable"
(mouseenter)="hoveredHeaderColumnIndex = columnIndex"
(mouseleave)="hoveredHeaderColumnIndex = -1"
adf-drop-zone dropTarget="header" [dropColumn]="col">
<ng-container *ngIf="!col.header">
<span *ngIf="col.title" class="adf-datatable-cell-value">{{ col.title | translate}}</span>
<span *ngIf="col.title && col.sortable" class="adf-sr-only" aria-live="polite">{{ getSortLiveAnnouncement(col) | translate: { string: col.title | translate } }}</span>
<div
*ngIf="!col.header"
[attr.data-automation-id]="'auto_header_content_id_' + col.key"
class="adf-datatable-cell-header-content"
[class.adf-datatable-cell-header-content--hovered]="hoveredHeaderColumnIndex === columnIndex && !isDraggingHeaderColumn"
>
<span
*ngIf="hoveredHeaderColumnIndex === columnIndex && col.draggable"
class="adf-datatable-cell-header-drag-icon-placeholder"
[attr.data-automation-id]="'adf-datatable-cell-header-drag-icon-placeholder-'+col.key"
></span>
<span *ngIf="col.title" class="adf-datatable-cell-value"> {{col.title | translate}}</span>
<span *ngIf="col.title && col.sortable && isDraggingHeaderColumn" class="adf-sr-only" aria-live="polite">
{{ getSortLiveAnnouncement(col) | translate: { string: col.title | translate } }}
</span>
<ng-template *ngIf="allowFiltering" [ngTemplateOutlet]="headerFilterTemplate" [ngTemplateOutletContext]="{$implicit: col}"></ng-template>
</ng-container>
<span
[class.adf-datatable__header--sorted-asc]="isColumnSorted(col, 'asc')"
[class.adf-datatable__header--sorted-desc]="isColumnSorted(col, 'desc')">
</span>
</div>
<ng-template *ngIf="col.header" [ngTemplateOutlet]="col.header" [ngTemplateOutletContext]="{$implicit: col}"></ng-template>
<div
*ngIf="col.draggable"
cdkDragHandle
class="adf-datatable-cell-header-drag-icon"
>
<adf-icon
*ngIf="hoveredHeaderColumnIndex === columnIndex"
value="adf:drag_indicator"
[attr.data-automation-id]="'adf-datatable-cell-header-drag-icon-'+col.key">
</adf-icon>
</div>
<div class="adf-drop-header-cell-placeholder" *cdkDragPlaceholder></div>
</div>
<!-- Actions (right) -->
<div *ngIf="actions && actionsPosition === 'right'" class="adf-actions-column adf-datatable-cell-header adf-datatable__actions-cell">
<span class="adf-sr-only">{{ 'ADF-DATATABLE.ACCESSIBILITY.ACTIONS' | translate }}</span>
@@ -58,7 +108,10 @@
</mat-form-field>
</div>
<div class="adf-datatable-body" role="rowgroup">
<div
class="adf-datatable-body"
[class.adf-blur-datatable-body]="isDraggingHeaderColumn"
role="rowgroup">
<ng-container *ngIf="!loading && !noPermission">
<adf-datatable-row *ngFor="let row of data.getRows(); let idx = index"
[row]="row"
@@ -171,7 +224,7 @@
[tooltip]="getCellTooltip(row, col)">
</adf-filesize-cell>
</div>
<div *ngSwitchCase="'text'" [attr.tabindex]="data.getValue(row, col, resolverFn)? 0 : -1" class="adf-cell-value"
<div *ngSwitchCase="'text'" [attr.tabindex]="data.getValue(row, col, resolverFn)? 0 : -1" class="adf-cell-value"
[attr.data-automation-id]="'text_' + data.getValue(row, col, resolverFn)">
<adf-datatable-cell
[copyContent]="col.copyContent"

View File

@@ -9,8 +9,8 @@ $data-table-column-spacing: 36px !default;
$data-table-column-padding: 18px !default;
$data-table-card-padding: var(--theme-headline-line-height) !default;
$data-table-cell-top: calc($data-table-card-padding / 2);
$data-table-thumbnail-width: 50px !default;
$data-table-cell-min-width: 50px !default;
$data-table-thumbnail-width: 65px !default;
$data-table-cell-min-width: 65px !default;
$data-table-cell-min-width-no-grow: 100px !default;
$data-table-cell-min-width-file-size: $data-table-cell-min-width !default;
@@ -208,6 +208,13 @@ $data-table-cell-min-width-file-size: $data-table-cell-min-width !default;
width: fit-content;
min-width: 100%;
box-sizing: border-box;
.adf-datatable-row {
&:hover,
&:focus {
background-color: inherit;
}
}
}
.adf-datatable-cell {
@@ -234,6 +241,10 @@ $data-table-cell-min-width-file-size: $data-table-cell-min-width !default;
}
}
.adf-blur-datatable-body {
filter: blur(3px);
}
.adf-datatable-body {
display: flex;
flex-direction: column;
@@ -315,66 +326,6 @@ $data-table-cell-min-width-file-size: $data-table-cell-min-width !default;
}
}
.adf-datatable-cell-header {
@include adf-no-select;
cursor: pointer;
position: relative;
vertical-align: bottom;
text-overflow: ellipsis;
font-weight: bold;
line-height: 24px;
letter-spacing: 0;
min-height: $data-table-row-height !important;
font-size: $data-table-header-font-size;
color: var(--theme-text-fg-color);
padding-bottom: 8px;
box-sizing: border-box;
padding-top: 12px !important;
&.adf-sortable {
@include adf-no-select;
&:hover {
cursor: pointer;
}
display: flex;
align-items: center;
}
&.adf-datatable__header--sorted-asc,
&.adf-datatable__header--sorted-desc {
color: var(--theme-text-fg-color);
&::before {
@include typo-icon;
font-size: $data-table-header-sort-icon-size;
content: '\e5d8';
left: 5px;
right: 5px;
position: relative;
vertical-align: sub;
}
}
&.adf-datatable__header--sorted-desc::before {
content: '\e5db';
}
&.adf-datatable-cell--fileSize.adf-datatable__header--sorted-asc::before,
&.adf-datatable-cell--fileSize.adf-datatable__header--sorted-desc::before {
left: -3px;
right: -3px;
}
&.adf-datatable-checkbox {
display: flex;
align-items: center;
}
}
.adf-datatable-cell-header.adf-expand-cell-1,
.adf-datatable-cell.adf-expand-cell-1 {
flex-grow: 1;
@@ -421,11 +372,12 @@ $data-table-cell-min-width-file-size: $data-table-cell-min-width !default;
align-items: center;
display: flex;
width: 100%;
padding: 0 10px;
}
.adf-datatable-cell-value {
word-break: break-word;
padding: 10px;
padding-right: 10px;
display: block;
@media screen and (-ms-high-contrast: active), screen and (-ms-high-contrast: none) {
@@ -519,7 +471,6 @@ $data-table-cell-min-width-file-size: $data-table-cell-min-width !default;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
padding: 0 4px;
&.adf-datatable-cell-header,
.adf-datatable-content-cell {
@@ -606,6 +557,136 @@ $data-table-cell-min-width-file-size: $data-table-cell-min-width !default;
}
}
.adf-datatable-cell-header {
@include adf-no-select;
cursor: pointer;
position: relative;
display: flex;
align-items: center;
text-overflow: ellipsis;
font-weight: bold;
line-height: 24px;
letter-spacing: 0;
min-height: $data-table-row-height !important;
font-size: $data-table-header-font-size;
color: var(--theme-text-fg-color);
box-sizing: border-box;
&.adf-sortable {
@include adf-no-select;
&:hover {
cursor: pointer;
}
display: flex;
align-items: center;
}
.adf-datatable__header--sorted-asc,
.adf-datatable__header--sorted-desc {
color: var(--theme-text-fg-color);
&::after {
@include typo-icon;
font-size: $data-table-header-sort-icon-size;
content: '\e5d8';
left: 5px;
right: 5px;
position: relative;
vertical-align: sub;
}
}
.adf-datatable__header--sorted-desc::after {
content: '\e5db';
}
.adf-datatable-cell--fileSize.adf-datatable__header--sorted-asc::before,
.adf-datatable-cell--fileSize.adf-datatable__header--sorted-desc::before {
left: -3px;
right: -3px;
}
&.adf-datatable-checkbox {
display: flex;
align-items: center;
}
.adf-datatable-cell-header-content {
display: flex;
flex-grow: 1;
align-items: center;
margin: 0 2px;
padding: 0 8px;
column-gap: 5px;
.adf-datatable-cell-header-drag-icon-placeholder {
min-width: 15px;
}
&--hovered {
background-color: var(--theme-bg-hover-color);
border-radius: 6px;
}
}
.adf-datatable-cell-header-drag-icon {
position: absolute;
top: 0;
bottom: 0;
left: 3px;
margin: auto;
color: #ccc;
cursor: move;
width: 24px;
height: 24px;
}
.adf-datatable-cell-value {
padding: 10px 0 !important;
line-height: 24px;
word-break: break-word;
}
}
.adf-drop-header-cell-placeholder {
display: flex;
flex: 1;
flex-grow: 1;
background: var(--theme-bg-hover-color);
border: dotted 1px rgba(0, 0, 0, 0.25);
min-height: 55px;
transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
}
.adf-datatable-cell-header.adf-ellipsis-cell {
.adf-datatable-cell-header-content {
white-space: nowrap;
overflow: hidden;
}
.adf-datatable-cell-header-content--hovered {
.adf-datatable-cell-value {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
}
.cdk-drag-preview {
&.adf-datatable-cell-header {
border-radius: 6px;
background-color: var(--theme-background-color);
@include mat-elevation-transition;
@include mat-overridable-elevation(4);
}
}
/* [Accessibility] Material checkbox labels */
.adf-checkbox-sr-only .mat-checkbox-label {
position: absolute;

View File

@@ -29,6 +29,9 @@ import { CoreTestingModule } from '../../../testing/core.testing.module';
import { DataColumnListComponent } from '../../../data-column/data-column-list.component';
import { DataColumnComponent } from '../../../data-column/data-column.component';
import { TranslateModule } from '@ngx-translate/core';
import { domSanitizerMock } from 'content-services/src/lib/testing/dom-sanitizer-mock';
import { matIconRegistryMock } from 'content-services/src/lib/testing/mat-icon-registry-mock';
import { CdkDragDrop } from '@angular/cdk/drag-drop';
@Component({selector: 'adf-custom-column-template-component', template: `
<ng-template #tmplRef></ng-template>
@@ -722,7 +725,7 @@ describe('DataTable', () => {
});
it('should initialize default adapter', () => {
const table = new DataTableComponent(null, null);
const table = new DataTableComponent(null, null, matIconRegistryMock, domSanitizerMock);
expect(table.data).toBeUndefined();
table.ngOnChanges({ data: new SimpleChange('123', {}, true) });
expect(table.data).toEqual(jasmine.any(ObjectDataTableAdapter));
@@ -1605,3 +1608,115 @@ describe('Accesibility', () => {
expect(cell.getAttribute('tabindex')).toBe('0');
});
});
describe('Drag&Drop column header', () => {
let fixture: ComponentFixture<DataTableComponent>;
let dataTable: DataTableComponent;
let data: { id: number; name: string }[] = [];
let dataTableSchema: DataColumn[] = [];
setupTestBed({
imports: [
TranslateModule.forRoot(),
CoreTestingModule
],
declarations: [CustomColumnTemplateComponent],
schemas: [NO_ERRORS_SCHEMA]
});
beforeEach(() => {
fixture = TestBed.createComponent(DataTableComponent);
dataTable = fixture.componentInstance;
data = [
{ id: 1, name: 'name1' },
{ id: 2, name: 'name2' }
];
dataTableSchema = [
new ObjectDataColumn({ key: 'id', title: 'ID', draggable: false }),
new ObjectDataColumn({ key: 'name', title: 'Name', draggable: true })
];
dataTable.data = new ObjectDataTableAdapter(
[...data],
[...dataTableSchema]
);
});
it('should show/hide drag indicator icon', () => {
fixture.detectChanges();
const hedaderColumn = fixture.debugElement.nativeElement.querySelector('[data-automation-id="auto_id_name"]');
hedaderColumn.dispatchEvent(new MouseEvent('mouseenter'));
fixture.detectChanges();
let dragIcon = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-datatable-cell-header-drag-icon-name"]');
let dragIconPlaceholder = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-datatable-cell-header-drag-icon-placeholder-name"]');
expect(dragIcon).toBeTruthy();
expect(dragIconPlaceholder).toBeTruthy();
hedaderColumn.dispatchEvent(new MouseEvent('mouseleave'));
fixture.detectChanges();
dragIcon = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-datatable-cell-header-drag-icon-name"]');
dragIconPlaceholder = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-datatable-cell-header-drag-icon-placeholder-name"]');
expect(dragIcon).toBeFalsy();
expect(dragIconPlaceholder).toBeFalsy();
});
it('should not show drag indicator icon, when drag and drop is disabled', () => {
fixture.detectChanges();
const hedaderColumn = fixture.debugElement.nativeElement.querySelector('[data-automation-id="auto_id_id"]');
hedaderColumn.dispatchEvent(new MouseEvent('mouseenter'));
fixture.detectChanges();
const dragIcon = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-datatable-cell-header-drag-icon-id"]');
const dragIconPlaceholder = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-datatable-cell-header-drag-icon-placeholder-id"]');
expect(dragIcon).toBeFalsy();
expect(dragIconPlaceholder).toBeFalsy();
});
it('should emit on change column order', () => {
const columnOrderChangedSpy = spyOn(dataTable.columnOrderChanged, 'emit');
const dropEvent: CdkDragDrop<unknown> = {
previousIndex: 0,
currentIndex: 1,
item: undefined,
container: undefined,
previousContainer: undefined,
isPointerOverContainer: true,
distance: { x: 0, y: 0 }
};
dataTable.onDropHeaderColumn(dropEvent);
expect(columnOrderChangedSpy).toHaveBeenCalledWith(dataTableSchema.reverse());
});
it('should change columns order', () => {
const dropEvent: CdkDragDrop<unknown> = {
previousIndex: 0,
currentIndex: 1,
item: undefined,
container: undefined,
previousContainer: undefined,
isPointerOverContainer: true,
distance: { x: 0, y: 0 }
};
dataTable.onDropHeaderColumn(dropEvent);
fixture.detectChanges();
const columns = dataTable.data.getColumns();
const headerCells = fixture.debugElement.nativeElement.querySelectorAll('.adf-datatable-cell--text.adf-datatable-cell-header');
expect(columns[0].key).toEqual(dataTableSchema[1].key);
expect(columns[1].key).toEqual(dataTableSchema[0].key);
expect(headerCells[0].innerText).toBe(dataTableSchema[1].title);
expect(headerCells[1].innerText).toBe(dataTableSchema[0].title);
});
});

View File

@@ -20,7 +20,7 @@
import {
ViewChildren, QueryList, HostListener,
AfterContentInit, Component, ContentChild, DoCheck, ElementRef, EventEmitter, Input,
IterableDiffers, OnChanges, Output, SimpleChange, SimpleChanges, TemplateRef, ViewEncapsulation, OnDestroy, AfterViewInit
IterableDiffers, OnChanges, Output, SimpleChange, SimpleChanges, TemplateRef, ViewEncapsulation, OnDestroy, AfterViewInit, OnInit
} from '@angular/core';
import { FocusKeyManager } from '@angular/cdk/a11y';
import { MatCheckboxChange } from '@angular/material/checkbox';
@@ -40,6 +40,9 @@ import { ObjectDataTableAdapter } from '../../data/object-datatable-adapter';
import { DataCellEvent } from '../data-cell.event';
import { DataRowActionEvent } from '../data-row-action.event';
import { share, buffer, map, filter, debounceTime } from 'rxjs/operators';
import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';
import { MatIconRegistry } from '@angular/material/icon';
import { DomSanitizer } from '@angular/platform-browser';
// eslint-disable-next-line no-shadow
export enum DisplayMode {
@@ -61,7 +64,7 @@ export enum ShowHeaderMode {
encapsulation: ViewEncapsulation.None,
host: { class: 'adf-datatable' }
})
export class DataTableComponent implements AfterContentInit, OnChanges, DoCheck, OnDestroy, AfterViewInit {
export class DataTableComponent implements OnInit, AfterContentInit, OnChanges, DoCheck, OnDestroy, AfterViewInit {
@ViewChildren(DataTableRowComponent)
rowsList: QueryList<DataTableRowComponent>;
@@ -160,6 +163,9 @@ export class DataTableComponent implements AfterContentInit, OnChanges, DoCheck,
@Output()
executeRowAction = new EventEmitter<DataRowActionEvent>();
@Output()
columnOrderChanged = new EventEmitter<DataColumn[]>();
/** Flag that indicates if the datatable is in loading state and needs to show the
* loading template (see the docs to learn how to configure a loading template).
*/
@@ -199,6 +205,9 @@ export class DataTableComponent implements AfterContentInit, OnChanges, DoCheck,
isSelectAllChecked: boolean = false;
selection = new Array<DataRow>();
isDraggingHeaderColumn = false;
hoveredHeaderColumnIndex = -1;
/** This array of fake rows fix the flex layout for the gallery view */
fakeRows = [];
@@ -220,14 +229,21 @@ export class DataTableComponent implements AfterContentInit, OnChanges, DoCheck,
}
constructor(private elementRef: ElementRef,
differs: IterableDiffers) {
differs: IterableDiffers,
private matIconRegistry: MatIconRegistry,
private sanitizer: DomSanitizer) {
if (differs) {
this.differ = differs.find([]).create(null);
}
this.click$ = new Observable<DataRowEvent>((observer) => this.clickObserver = observer)
.pipe(share());
}
ngOnInit(): void {
this.registerDragHandleIcon();
}
ngAfterContentInit() {
if (this.columnList) {
this.subscriptions.push(
@@ -260,13 +276,18 @@ export class DataTableComponent implements AfterContentInit, OnChanges, DoCheck,
if (dataChanges) {
this.data = changes['data'].currentValue;
this.resetSelection();
} else if (rowChanges) {
}
if (rowChanges) {
this.setTableRows(changes['rows'].currentValue);
this.setTableSorting(this.sorting);
} else {
}
if (columnChanges) {
this.setTableColumns(changes['columns'].currentValue);
}
}
return;
}
@@ -291,6 +312,14 @@ export class DataTableComponent implements AfterContentInit, OnChanges, DoCheck,
return column.key === this.data.getSorting().key;
}
onDropHeaderColumn(event: CdkDragDrop<unknown>): void {
const columns = this.data.getColumns();
moveItemInArray(columns, event.previousIndex, event.currentIndex);
this.columnOrderChanged.emit(columns);
this.isDraggingHeaderColumn = false;
}
ngDoCheck() {
const changes = this.differ.diff(this.rows);
if (changes) {
@@ -849,6 +878,18 @@ export class DataTableComponent implements AfterContentInit, OnChanges, DoCheck,
'ADF-DATATABLE.ACCESSIBILITY.SORT_ASCENDING_BY' :
'ADF-DATATABLE.ACCESSIBILITY.SORT_DESCENDING_BY';
}
private registerDragHandleIcon(): void {
const iconUrl = this.sanitizer.bypassSecurityTrustResourceUrl(
'./assets/images/drag_indicator_24px.svg'
);
this.matIconRegistry.addSvgIconInNamespace(
'adf',
'drag_indicator',
iconUrl
);
}
}
export interface DataTableDropEvent {

View File

@@ -30,6 +30,7 @@ export interface DataColumnTypes {
export type DataColumnType = keyof DataColumnTypes;
export interface DataColumn {
id?: string;
key: string;
type: DataColumnType;
format?: string;
@@ -44,4 +45,5 @@ export interface DataColumn {
focus?: boolean;
sortingKey?: string;
header?: TemplateRef<any>;
draggable?: boolean;
}

View File

@@ -16,6 +16,7 @@
*/
import { ContentChild, Input, Directive } from '@angular/core';
import { ReplaySubject } from 'rxjs';
import { AppConfigService } from '../../app-config/app-config.service';
import { DataColumnListComponent } from '../../data-column/data-column-list.component';
import { DataColumn } from './data-column.model';
@@ -34,19 +35,34 @@ export abstract class DataTableSchema {
columns: any;
protected columnsOrder: string[] | undefined;
protected columnsOrderedByKey: string = 'id';
private layoutPresets = {};
private columnsSchemaSubject$ = new ReplaySubject<boolean>();
isColumnSchemaCreated$ = this.columnsSchemaSubject$.asObservable();
constructor(private appConfigService: AppConfigService,
protected presetKey: string,
protected presetsModel: any) { }
public createDatatableSchema(): void {
this.loadLayoutPresets();
if (!this.columns || this.columns.length === 0) {
this.columns = this.mergeJsonAndHtmlSchema();
this.createColumns();
this.columnsSchemaSubject$.next(true);
} else {
this.columnsSchemaSubject$.next(false);
}
}
public createColumns(): void {
const columns = this.mergeJsonAndHtmlSchema();
this.columns = this.sortColumnsByKey(columns);
}
public loadLayoutPresets(): void {
const externalSettings = this.appConfigService.get(this.presetKey, null);
if (externalSettings) {
@@ -57,10 +73,18 @@ export abstract class DataTableSchema {
}
public mergeJsonAndHtmlSchema(): any {
let customSchemaColumns = this.getSchemaFromConfig(this.presetColumn).concat(this.getSchemaFromHtml(this.columnList));
const configSchemaColumns = this.getSchemaFromConfig(this.presetColumn);
const htmlSchemaColumns = this.getSchemaFromHtml(this.columnList);
let customSchemaColumns = [
...configSchemaColumns,
...htmlSchemaColumns
];
if (customSchemaColumns.length === 0) {
customSchemaColumns = this.getDefaultLayoutPreset();
}
return customSchemaColumns;
}
@@ -87,4 +111,20 @@ export abstract class DataTableSchema {
public setPresetsModel(presetsModel: any) {
this.presetsModel = presetsModel;
}
private sortColumnsByKey(columns: any[]): any[] {
const defaultColumns = [...columns];
const columnsWithProperOrder = [];
(this.columnsOrder ?? []).forEach(columnKey => {
const originalColumnIndex = defaultColumns.findIndex(defaultColumn => defaultColumn[this.columnsOrderedByKey] === columnKey);
if (originalColumnIndex > -1) {
columnsWithProperOrder.push(defaultColumns[originalColumnIndex]);
defaultColumns.splice(originalColumnIndex, 1);
}
});
return [...columnsWithProperOrder, ...defaultColumns];
}
}

View File

@@ -20,7 +20,7 @@ import { DataColumn, DataColumnType } from './data-column.model';
// Simple implementation of the DataColumn interface.
export class ObjectDataColumn implements DataColumn {
id?: string;
key: string;
type: DataColumnType;
format: string;
@@ -33,8 +33,10 @@ export class ObjectDataColumn implements DataColumn {
focus?: boolean;
sortingKey?: string;
header?: TemplateRef<any>;
draggable: boolean;
constructor(input: any) {
this.id = input.id ?? '';
this.key = input.key;
this.type = input.type || 'text';
this.format = input.format;
@@ -47,5 +49,6 @@ export class ObjectDataColumn implements DataColumn {
this.focus = input.focus;
this.sortingKey = input.sortingKey;
this.header = input.header;
this.draggable = input.draggable ?? false;
}
}

View File

@@ -46,6 +46,8 @@ import { JsonCellComponent } from './components/json-cell/json-cell.component';
import { ClipboardModule } from '../clipboard/clipboard.module';
import { DropZoneDirective } from './directives/drop-zone.directive';
import { DataColumnModule } from '../data-column/data-column.module';
import { DragDropModule } from '@angular/cdk/drag-drop';
import { IconModule } from '../icon/icon.module';
@NgModule({
imports: [
@@ -57,7 +59,9 @@ import { DataColumnModule } from '../data-column/data-column.module';
ContextMenuModule,
PipeModule,
DirectiveModule,
ClipboardModule
ClipboardModule,
DragDropModule,
IconModule
],
declarations: [
DataTableComponent,
@@ -101,6 +105,5 @@ import { DataColumnModule } from '../data-column/data-column.module';
CustomNoPermissionTemplateDirective,
DropZoneDirective
]
})
export class DataTableModule {}