Asymmetrik/ngx-starter

View on GitHub
src/app/common/table/column-chooser/column-chooser.component.ts

Summary

Maintainability
A
3 hrs
Test Coverage
import {
    CdkDrag,
    CdkDragDrop,
    CdkDragHandle,
    CdkDropList,
    moveItemInArray
} from '@angular/cdk/drag-drop';
import { TitleCasePipe } from '@angular/common';
import { Component, Input, OnInit, booleanAttribute, input, output } from '@angular/core';
import { FormsModule } from '@angular/forms';

import { LocalStorageService } from '../../storage/local-storage.service';

export type ColumnDefinition = {
    key: string;
    label?: string;
    selected?: boolean;
};

@Component({
    selector: 'asy-column-chooser',
    templateUrl: './column-chooser.component.html',
    styleUrls: ['./column-chooser.component.scss'],
    standalone: true,
    imports: [CdkDropList, CdkDrag, CdkDragHandle, FormsModule, TitleCasePipe]
})
export class ColumnChooserComponent implements OnInit {
    @Input({ required: true })
    set columns(columns: ColumnDefinition[]) {
        this._columns = columns.map((c) => ({ ...c }));
        this._defaultColumns = columns.map((c) => ({ ...c }));
        this.onChange();
    }
    _columns: ColumnDefinition[] = [];
    _defaultColumns: ColumnDefinition[] = [];

    readonly storageKey = input<string>();

    readonly sortingDisabled = input(false, { transform: booleanAttribute });

    readonly columnsChange = output<string[]>();

    private storage = new LocalStorageService();

    ngOnInit(): void {
        this._loadState();
    }

    onDrop(event: CdkDragDrop<ColumnDefinition[]>) {
        moveItemInArray(this._columns, event.previousIndex, event.currentIndex);
        this.onChange();
    }

    onChange() {
        this._saveState();
        this.columnsChange.emit(this._columns.filter((c) => c.selected).map((c) => c.key));
    }

    restoreDefault() {
        this._columns = this._defaultColumns.map((c) => ({ ...c }));
        this.onChange();
    }

    selectAll() {
        this._columns.forEach((c) => {
            c.selected = true;
        });
        this.onChange();
    }

    _loadState() {
        if (this.storageKey()) {
            const columnsOrder: string[] = this.storage.getValue(
                `${this.storageKey()}-columns-order`,
                []
            );
            const columnsSelected: string[] = this.storage.getValue(
                `${this.storageKey()}-columns-selected`,
                []
            );

            if (Array.isArray(columnsOrder) && columnsOrder.length > 0) {
                this._columns = [...this._columns].sort((a, b) => {
                    let aOrderIndex = columnsOrder.indexOf(a.key);
                    let bOrderIndex = columnsOrder.indexOf(b.key);

                    if (aOrderIndex !== -1 && bOrderIndex !== -1) {
                        return aOrderIndex - bOrderIndex;
                    }

                    if (aOrderIndex === -1 && bOrderIndex === -1) {
                        aOrderIndex = this._columns.findIndex((col) => col.key === a.key);
                        bOrderIndex = this._columns.findIndex((col) => col.key === b.key);
                        return aOrderIndex - bOrderIndex;
                    }

                    if (aOrderIndex === -1) {
                        return 1;
                    }
                    return -1;
                });
            }

            if (Array.isArray(columnsSelected) && columnsSelected.length > 0) {
                this._columns.forEach((c) => {
                    c.selected = columnsSelected.indexOf(c.key) !== -1;
                });
            }

            this.onChange();
        }
    }

    _saveState() {
        if (this.storageKey()) {
            this.storage.setValue(
                `${this.storageKey()}-columns-selected`,
                this._columns.filter((c) => c.selected).map((c) => c.key)
            );

            this.storage.setValue(
                `${this.storageKey()}-columns-order`,
                this._columns.map((c) => c.key)
            );
        }
    }
}