File

projects/angular/components/ui-grid/src/components/ui-grid-toggle-columns/ui-grid-toggle-columns.component.ts

Implements

AfterViewInit OnDestroy

Metadata

Index

Properties
Methods
Inputs
Outputs
HostBindings
Accessors

Constructor

constructor(_elementRef: ElementRef, _cd: ChangeDetectorRef)
Parameters :
Name Type Optional
_elementRef ElementRef<HTMLElement> No
_cd ChangeDetectorRef No

Inputs

dirty
Type : boolean
Default value : false
options
resetToDefaults
Type : string
showDivider
Type : boolean
Default value : false
togglePlaceholderTitle
Type : string
toggleTitle
Type : string
toggleTooltip
Type : string
useLegacyDesign
Type : boolean
Default value : false

Outputs

resetColumns
Type : EventEmitter
visibleColumns
Type : EventEmitter
visibleColumnsToggled
Type : EventEmitter

HostBindings

class
Type : string
Default value : COMPONENT_SELECTOR

Methods

ngAfterViewInit
ngAfterViewInit()
Returns : void
ngOnDestroy
ngOnDestroy()
Returns : void
reset
reset()
Returns : void
resetKeyDown
resetKeyDown(e: KeyboardEvent)
Parameters :
Name Type Optional
e KeyboardEvent No
Returns : void
selectionChange
selectionChange(undefined: MatSelectChange)
Parameters :
Name Type Optional
MatSelectChange No
Returns : void

Properties

hostClass
Default value : COMPONENT_SELECTOR
Decorators :
@HostBinding('class')
Optional resetBtn
Type : MatAnchor
Decorators :
@ViewChild('resetBtn', {static: false})
Optional selectColumns
Type : MatSelect
Decorators :
@ViewChild(MatSelect, {static: false})

Accessors

options
getoptions()
setoptions(options: IVisibleModel<T>[] | null)
Parameters :
Name Type Optional
options IVisibleModel<T>[] | null No
Returns : void
selected
getselected()
import isEqual from 'lodash-es/isEqual';
import {
    fromEvent,
    Subject,
} from 'rxjs';
import {
    filter,
    takeUntil,
} from 'rxjs/operators';

import {
    AfterViewInit,
    ChangeDetectionStrategy,
    ChangeDetectorRef,
    Component,
    ElementRef,
    EventEmitter,
    HostBinding,
    Input,
    OnDestroy,
    Output,
    ViewChild,
    ViewEncapsulation,
} from '@angular/core';
import type { MatAnchor } from '@angular/material/button';
import {
    MatSelect,
    MatSelectChange,
} from '@angular/material/select';

import {
    IGridDataEntry,
    IVisibleModel,
} from '../../models';

const COMPONENT_SELECTOR = 'ui-grid-toggle-columns';

@Component({
    selector: COMPONENT_SELECTOR,
    templateUrl: './ui-grid-toggle-columns.component.html',
    styleUrls: ['./ui-grid-toggle-columns.component.scss'],
    encapsulation: ViewEncapsulation.None,
    changeDetection: ChangeDetectionStrategy.OnPush,
})
export class UiGridToggleColumnsComponent<T extends IGridDataEntry> implements AfterViewInit, OnDestroy {
    @HostBinding('class')
    hostClass = COMPONENT_SELECTOR;

    @HostBinding(`class.${COMPONENT_SELECTOR}-dirty`)
    @Input()
    dirty = false;

    @Input()
    showDivider = false;

    @Input()
    set options(options: IVisibleModel<T>[] | null) {
        if (!options || isEqual(this._options, options)) { return; }

        this._options = options;
        this._selected = options
            .filter(({ checked }) => checked)
            .map(o => o.property);
    }
    get options() {
        return this._options;
    }

    @Input()
    useLegacyDesign = false;

    @Input()
    toggleTooltip?: string;

    @Input()
    toggleTitle?: string;

    @Input()
    resetToDefaults?: string;

    @Input()
    togglePlaceholderTitle?: string;

    @Output()
    visibleColumns = new EventEmitter<(string | keyof T)[]>();

    @Output()
    resetColumns = new EventEmitter<void>();

    @Output()
    visibleColumnsToggled = new EventEmitter<boolean>();

    @ViewChild(MatSelect, { static: false })
    selectColumns?: MatSelect;

    @ViewChild('resetBtn', { static: false })
    resetBtn?: MatAnchor;

    get selected() {
        return this._selected;
    }

    private get _currentIndex() {
        if (!this.selectColumns) { return null; }
        // eslint-disable-next-line no-underscore-dangle
        return this.selectColumns._keyManager.activeItemIndex;
    }

    private set _currentIndex(i: number | null) {
        if (i == null || !this.selectColumns) { return; }
        // eslint-disable-next-line no-underscore-dangle
        this.selectColumns._keyManager.setActiveItem(i);
    }

    private get _isFirstValidIndex() {
        return !this._isResetIndex &&
            this._currentIndex === this._options.findIndex(o => !o.disabled);
    }

    private get _isResetIndex() {
        return this._currentIndex === -1;
    }

    private _selected: (string | keyof T)[] = [];
    private _options: IVisibleModel<T>[] = [];
    private _destroyed$ = new Subject<void>();

    constructor(
        private _elementRef: ElementRef<HTMLElement>,
        private _cd: ChangeDetectorRef,
    ) { }

    ngAfterViewInit() {
        fromEvent<KeyboardEvent>(
            this._elementRef.nativeElement,
            'keydown',
            { capture: true },
        ).pipe(
            filter(_ => this.dirty),
            takeUntil(this._destroyed$),
        ).subscribe(this._onKeyDown);

        this.selectColumns!.openedChange
            .pipe(
                takeUntil(this._destroyed$),
            )
            .subscribe((open) => {
                this.visibleColumnsToggled.emit(open);
            });
    }

    ngOnDestroy() {
        this._destroyed$.next();
        this._destroyed$.complete();
    }

    selectionChange({ value }: MatSelectChange) {
        this._selected = value;
        this._options
            .forEach(c => c.checked = value.includes(c.property));

        this.visibleColumns.emit(value);
    }

    reset() {
        this.resetColumns.emit();
        this.selectColumns!.close();
        this.selectColumns!.focus();
    }

    resetKeyDown(e: KeyboardEvent) {
        if (this._isArrowUp(e)) {
            e.stopImmediatePropagation();
            return;
        }

        if (this._isArrowDown(e)) {
            this.selectColumns?.focus();
        }
    }

    private _onKeyDown = (e: KeyboardEvent) => {
        if (
            this._isResetIndex &&
            this._isArrowDown(e)
        ) {
            e.preventDefault();
            e.stopImmediatePropagation();

            this.selectColumns?.focus();
            // eslint-disable-next-line no-underscore-dangle
            this.selectColumns?._keyManager.setFirstItemActive();
            this._cd.detectChanges();
        }

        if (
            this._isArrowUp(e) &&
            this._isFirstValidIndex
        ) {
            e.stopPropagation();
            this._focusOnReset();
        }
    };

    private _isArrowUp(e: KeyboardEvent) {
        return ['Up', 'ArrowUp'].includes(e.key);
    }

    private _isArrowDown(e: KeyboardEvent) {
        return ['Down', 'ArrowDown'].includes(e.key);
    }

    private _focusOnReset() {
        this._currentIndex = -1;
        this.resetBtn?.focus('keyboard');
        this._cd.detectChanges();
    }
}
<ng-container *ngIf="options!.length">
    <button *ngIf="!useLegacyDesign; else classicColumnToggleTmpl"
            [color]="dirty ? 'primary' : undefined"
            (click)="selectColumns!.open(); selectColumns!.focus()"
            type="button"
            mat-button>
        <mat-icon class="material-icons-outlined">table_chart</mat-icon>
        <span>{{togglePlaceholderTitle}}</span>
        <mat-icon class="material-icons-outlined">keyboard_arrow_down</mat-icon>
    </button>

    <mat-select [value]="selected"
                [aria-label]="togglePlaceholderTitle ?? ''"
                [disableOptionCentering]="true"
                [class.use-alternate]="!useLegacyDesign"
                [panelClass]="['ui-grid-toggle-panel', useLegacyDesign ?  '' : 'ui-grid-toggle-panel-alternate']"
                (selectionChange)="selectionChange($event)"
                tabIndex="-1"
                multiple>
        <mat-optgroup>
            <span>
                <mat-icon class="material-icons-outlined">view_column</mat-icon> {{ toggleTitle }}
            </span>

            <a #resetBtn
               *ngIf="dirty"
               [matTooltip]="resetToDefaults ?? ''"
               (click)="reset()"
               (keydown.enter)="reset()"
               (keydown.space)="reset()"
               (keydown)="resetKeyDown($event)"
               class="ui-grid-toggle-reset"
               role="button"
               color="primary"
               tabindex="-1"
               mat-button>
                {{resetToDefaults}}
            </a>

            <mat-option *ngFor="let o of options"
                        [matTooltip]="o.label"
                        [disabled]="o.disabled"
                        [value]="o.property">{{ o.label }}</mat-option>
        </mat-optgroup>
    </mat-select>

    <mat-divider *ngIf="showDivider"
                 [vertical]="true"></mat-divider>
</ng-container>

<ng-template #classicColumnToggleTmpl>
    <button [matTooltip]="togglePlaceholderTitle ?? ''"
            [color]="dirty ? 'primary' : undefined"
            (click)="selectColumns!.open(); selectColumns!.focus()"
            matTooltipPosition="right"
            type="button"
            mat-icon-button>
        <mat-icon class="material-icons-outlined">table_chart</mat-icon>
    </button>
</ng-template>

./ui-grid-toggle-columns.component.scss

$toggle-col-width: 10em;

.ui-grid-toggle-columns {
    margin-right: 6px;
    padding-left: 0;
    display: flex;

    mat-divider {
        margin-left: 6px;
        height: 32px;
        align-self: center;
    }

    mat-select {
        overflow: hidden;
        width: 0;
        // adjustments in order to match trigger icon with optgroup-label icon
        position: relative;
        top: -10px;
        left: -4px;

        &.use-alternate {
            top: -10px;
            left: -67px;
        }
    }
}

// overlay panel
.ui-grid-toggle-panel {
    // IE fix - min-width with calc does not render properly so we add a fallback in case min-width is evaluated even lower
    width: 210px !important;

    .mat-optgroup-label {
        padding-left: 12px;
        display: flex;
        padding-right: 6px;
        justify-content: space-between;
        align-items: center;

        .mat-icon {
            margin-right: 0;
        }
    }

    &-alternate {
        .mat-optgroup-label {
            padding-right: 3px;
        }
    }
}
Legend
Html element
Component
Html element with directive

results matching ""

    No results matching ""