Browse Source

feat(admin-ui): Implement column sorting in DataTable2

Michael Bromley 2 years ago
parent
commit
ddbf02d080

+ 5 - 4
packages/admin-ui/src/lib/catalog/src/components/collection-data-table/collection-data-table.component.html

@@ -18,7 +18,7 @@
                         (change)="onToggleAllClick()"
                     />
                 </th>
-                <th *ngFor="let column of visibleColumns; last as isLast" [class.expand]="column.expand">
+                <th *ngFor="let column of visibleSortedColumns; last as isLast" [class.expand]="column.expand">
                     <div class="cell-content" [ngClass]="column.align">
                         <span>{{ column.heading }}</span>
                         <div *ngIf="column.sort as sort" class="sort-toggle">
@@ -35,13 +35,14 @@
                         <vdr-data-table-colum-picker
                             [uiLanguage]="uiLanguage$ | async"
                             [columns]="allColumns"
+                            (reorder)="onColumnReorder($event)"
                         ></vdr-data-table-colum-picker>
                     </div>
                 </th>
             </tr>
             <tr *ngIf="searchComponent || customSearchTemplate || filters?.length">
                 <th
-                    [attr.colspan]="visibleColumns.length + (selectionManager ? 1 : 0)"
+                    [attr.colspan]="visibleSortedColumns.length + (selectionManager ? 1 : 0)"
                     class="filter-row"
                     [class.active]="showSearchFilterRow"
                 >
@@ -102,7 +103,7 @@
             </ng-container>
             <ng-container>
                 <tr *ngIf="!items?.length">
-                    <td [attr.colspan]="visibleColumns.length + (selectionManager ? 1 : 0)">
+                    <td [attr.colspan]="visibleSortedColumns.length + (selectionManager ? 1 : 0)">
                         <vdr-empty-placeholder [emptyStateLabel]="emptyStateLabel"></vdr-empty-placeholder>
                     </td>
                 </tr>
@@ -143,7 +144,7 @@
                 (click)="onRowClick(item, $event)"
             />
         </td>
-        <td *ngFor="let column of visibleColumns" [class.active]="activeIndex === i">
+        <td *ngFor="let column of visibleSortedColumns" [class.active]="activeIndex === i">
             <div class="cell-content" [ngClass]="column.align">
                 <ng-container
                     *ngTemplateOutlet="column.template; context: { item: item, index: i, depth: depth }"

+ 0 - 5
packages/admin-ui/src/lib/catalog/src/components/facet-list/facet-list.component.html

@@ -48,11 +48,6 @@
                     {{ facet.createdAt | localeDate : 'short' }}
                 </ng-template>
             </vdr-dt2-column>
-            <vdr-dt2-column [heading]="'common.created-at' | translate" [hiddenByDefault]="true" [sort]="sorts.get('createdAt')">
-                <ng-template let-facet="item">
-                    {{ facet.createdAt | localeDate : 'short' }}
-                </ng-template>
-            </vdr-dt2-column>
             <vdr-dt2-column [heading]="'common.updated-at' | translate" [hiddenByDefault]="true" [sort]="sorts.get('updatedAt')">
                 <ng-template let-facet="item">
                     {{ facet.updatedAt | localeDate : 'short' }}

+ 1 - 0
packages/admin-ui/src/lib/core/src/providers/local-storage/local-storage.service.ts

@@ -8,6 +8,7 @@ import { WidgetLayoutDefinition } from '../dashboard-widget/dashboard-widget-typ
 export type DataTableConfig = {
     [id: string]: {
         visibility: string[];
+        order: { [id: string]: number };
         showSearchFilterRow: boolean;
     };
 };

+ 6 - 5
packages/admin-ui/src/lib/core/src/shared/components/data-table-2/data-table2.component.html

@@ -17,7 +17,7 @@
                         (change)="onToggleAllClick()"
                     />
                 </th>
-                <th *ngFor="let column of visibleColumns; last as isLast" [class.expand]="column.expand">
+                <th *ngFor="let column of visibleSortedColumns; last as isLast" [class.expand]="column.expand">
                     <div class="cell-content" [ngClass]="column.align">
                         <span>{{ column.heading }}</span>
                         <div *ngIf="column.sort as sort" class="sort-toggle">
@@ -33,14 +33,15 @@
                     <div *ngIf="isLast" class="column-picker">
                         <vdr-data-table-colum-picker
                             [uiLanguage]="uiLanguage$ | async"
-                            [columns]="allColumns"
+                            [columns]="sortedColumns"
+                            (reorder)="onColumnReorder($event)"
                         ></vdr-data-table-colum-picker>
                     </div>
                 </th>
             </tr>
             <tr *ngIf="searchComponent || customSearchTemplate || filters?.length">
                 <th
-                    [attr.colspan]="visibleColumns.length + (selectionManager ? 1 : 0)"
+                    [attr.colspan]="visibleSortedColumns.length + (selectionManager ? 1 : 0)"
                     class="filter-row"
                     [class.active]="showSearchFilterRow"
                 >
@@ -97,7 +98,7 @@
                         (click)="onRowClick(item, $event)"
                     />
                 </td>
-                <td *ngFor="let column of visibleColumns" [class.active]="activeIndex === i">
+                <td *ngFor="let column of visibleSortedColumns" [class.active]="activeIndex === i">
                     <div class="cell-content" [ngClass]="column.align">
                         <ng-container
                             *ngTemplateOutlet="column.template; context: { item: item, index: i }"
@@ -107,7 +108,7 @@
             </tr>
             <ng-container>
                 <tr *ngIf="!items?.length">
-                    <td [attr.colspan]="visibleColumns.length + (selectionManager ? 1 : 0)">
+                    <td [attr.colspan]="visibleSortedColumns.length + (selectionManager ? 1 : 0)">
                         <vdr-empty-placeholder [emptyStateLabel]="emptyStateLabel"></vdr-empty-placeholder>
                     </td>
                 </tr>

+ 38 - 10
packages/admin-ui/src/lib/core/src/shared/components/data-table-2/data-table2.component.ts

@@ -18,6 +18,7 @@ import {
 import {
     BulkActionMenuComponent,
     DataService,
+    DataTableConfig,
     LanguageCode,
     LocalStorageService,
 } from '@vendure/admin-ui/core';
@@ -143,11 +144,22 @@ export class DataTable2Component<T> implements AfterContentInit, OnChanges, OnIn
         return [...(this.columns ?? []), ...(this.customFieldColumns ?? [])];
     }
 
-    get visibleColumns() {
-        return [
-            ...(this.columns?.filter(c => c.visible) ?? []),
-            ...(this.customFieldColumns?.filter(c => c.visible) ?? []),
-        ];
+    get visibleSortedColumns() {
+        return this.sortedColumns.filter(c => c.visible);
+    }
+
+    get sortedColumns() {
+        const columns = this.allColumns;
+        const dataTableConfig = this.getDataTableConfig();
+        for (const [id, index] of Object.entries(dataTableConfig[this.id].order)) {
+            const column = columns.find(c => c.id === id);
+            const currentIndex = columns.findIndex(c => c.id === id);
+            if (currentIndex !== -1 && column) {
+                columns.splice(currentIndex, 1);
+                columns.splice(index, 0, column);
+            }
+        }
+        return columns;
     }
 
     private shiftDownHandler = (event: KeyboardEvent) => {
@@ -195,7 +207,7 @@ export class DataTable2Component<T> implements AfterContentInit, OnChanges, OnIn
         }
         const updateColumnVisibility = () => {
             if (!dataTableConfig[this.id]) {
-                dataTableConfig[this.id] = { visibility: [], showSearchFilterRow: false };
+                dataTableConfig[this.id] = { visibility: [], order: {}, showSearchFilterRow: false };
             }
             dataTableConfig[this.id].visibility = this.allColumns
                 .filter(c => (c.visible && c.hiddenByDefault) || (!c.visible && !c.hiddenByDefault))
@@ -220,12 +232,20 @@ export class DataTable2Component<T> implements AfterContentInit, OnChanges, OnIn
         this.showSearchFilterRow = dataTableConfig?.[this.id]?.showSearchFilterRow ?? false;
     }
 
+    onColumnReorder(event: { column: DataTable2ColumnComponent<any>; newIndex: number }) {
+        const naturalIndex = this.allColumns.findIndex(c => c.id === event.column.id);
+        const dataTableConfig = this.getDataTableConfig();
+        if (naturalIndex === event.newIndex) {
+            delete dataTableConfig[this.id].order[event.column.id];
+        } else {
+            dataTableConfig[this.id].order[event.column.id] = event.newIndex;
+        }
+        this.localStorageService.set('dataTableConfig', dataTableConfig);
+    }
+
     toggleSearchFilterRow() {
         this.showSearchFilterRow = !this.showSearchFilterRow;
-        const dataTableConfig = this.localStorageService.get('dataTableConfig') ?? {};
-        if (!dataTableConfig[this.id]) {
-            dataTableConfig[this.id] = { visibility: [], showSearchFilterRow: false };
-        }
+        const dataTableConfig = this.getDataTableConfig();
         dataTableConfig[this.id].showSearchFilterRow = this.showSearchFilterRow;
         this.localStorageService.set('dataTableConfig', dataTableConfig);
     }
@@ -245,4 +265,12 @@ export class DataTable2Component<T> implements AfterContentInit, OnChanges, OnIn
     onRowClick(item: T, event: MouseEvent) {
         this.selectionManager?.toggleSelection(item, event);
     }
+
+    private getDataTableConfig(): DataTableConfig {
+        const dataTableConfig = this.localStorageService.get('dataTableConfig') ?? {};
+        if (!dataTableConfig[this.id]) {
+            dataTableConfig[this.id] = { visibility: [], order: {}, showSearchFilterRow: false };
+        }
+        return dataTableConfig;
+    }
 }

+ 22 - 11
packages/admin-ui/src/lib/core/src/shared/components/data-table-column-picker/data-table-column-picker.component.html

@@ -3,17 +3,28 @@
         <clr-icon shape="view-columns"></clr-icon>
     </button>
     <vdr-dropdown-menu vdrPosition="bottom-left">
-        <div *ngFor="let column of columns; index as i" class="mx-2">
-            <label>
-                <input
-                    type="checkbox"
-                    [disabled]="column.optional === false"
-                    [checked]="column.visible"
-                    (change)="toggleColumn(column)"
-                    class="mr-1"
-                />
-                <span>{{ column.heading | translate }}</span>
-            </label>
+        <div cdkDropList (cdkDropListDropped)="drop($event)">
+            <div
+                *ngFor="let column of columns; index as i"
+                class="mx-2 column-list"
+                cdkDrag
+                cdkDragLockAxis="y"
+                [cdkDragData]="column"
+            >
+                <div cdkDragHandle class="drag-handle">
+                    <clr-icon shape="drag-handle"></clr-icon>
+                </div>
+                <label>
+                    <input
+                        type="checkbox"
+                        [disabled]="column.optional === false"
+                        [checked]="column.visible"
+                        (change)="toggleColumn(column)"
+                        class="mr-1"
+                    />
+                    <span>{{ column.heading | translate }}</span>
+                </label>
+            </div>
         </div>
     </vdr-dropdown-menu>
 </vdr-dropdown>

+ 16 - 0
packages/admin-ui/src/lib/core/src/shared/components/data-table-column-picker/data-table-column-picker.component.scss

@@ -5,3 +5,19 @@ button {
         opacity: 1;
     }
 }
+.column-list {
+    display: flex;
+    align-items: center;
+}
+
+.drag-handle {
+    cursor: grab;
+}
+
+.cdk-drop-list-dragging .cdk-drag {
+  transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
+}
+
+.cdk-drag-preview {
+    opacity: 0;
+}

+ 10 - 1
packages/admin-ui/src/lib/core/src/shared/components/data-table-column-picker/data-table-column-picker.component.ts

@@ -1,4 +1,5 @@
-import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
+import { CdkDragDrop } from '@angular/cdk/drag-drop';
+import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
 import { LanguageCode } from '../../../common/generated-types';
 import { DataTable2ColumnComponent } from '../data-table-2/data-table-column.component';
 
@@ -11,8 +12,16 @@ import { DataTable2ColumnComponent } from '../data-table-2/data-table-column.com
 export class DataTableColumnPickerComponent {
     @Input() columns: Array<DataTable2ColumnComponent<any>>;
     @Input() uiLanguage: LanguageCode;
+    @Output() reorder = new EventEmitter<{ column: DataTable2ColumnComponent<any>; newIndex: number }>();
 
     toggleColumn(column: DataTable2ColumnComponent<any>) {
         column.setVisibility(!column.visible);
     }
+
+    drop(event: CdkDragDrop<Array<DataTable2ColumnComponent<any>>>) {
+        this.reorder.emit({
+            column: event.item.data,
+            newIndex: event.currentIndex,
+        });
+    }
 }