Asymmetrik/ngx-starter

View on GitHub
src/app/common/table/filter/asy-header-text-filter/asy-header-text-filter.component.ts

Summary

Maintainability
A
0 mins
Test Coverage
import { A11yModule } from '@angular/cdk/a11y';
import { CdkConnectedOverlay, CdkOverlayOrigin, OverlayModule } from '@angular/cdk/overlay';
import { NgClass } from '@angular/common';
import { ChangeDetectionStrategy, Component, Inject, Optional, signal } from '@angular/core';
import { FormsModule } from '@angular/forms';

import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap';
import { NgSelectModule } from '@ng-select/ng-select';
import escapeRegExp from 'lodash/escapeRegExp';

import { SearchInputComponent } from '../../../search-input/search-input.component';
import {
    AsyAbstractHeaderFilterComponent,
    AsyFilterHeaderColumnDef
} from '../asy-abstract-header-filter.component';

export type TextFilterOption = 'Equals' | 'Contains' | 'Starts with' | 'Ends with';

type BuildFilterFunction = (search: string, option: TextFilterOption) => object;

@Component({
    selector: 'asy-header-filter[text-filter]',
    templateUrl: './asy-header-text-filter.component.html',
    styleUrls: ['./asy-header-text-filter.component.scss'],
    changeDetection: ChangeDetectionStrategy.OnPush,
    standalone: true,
    imports: [
        NgClass,
        NgSelectModule,
        FormsModule,
        SearchInputComponent,
        A11yModule,
        OverlayModule,
        CdkConnectedOverlay,
        CdkOverlayOrigin,
        NgbTooltip
    ]
})
export class AsyHeaderTextFilterComponent<T> extends AsyAbstractHeaderFilterComponent<T> {
    readonly search = signal('');
    option = signal<TextFilterOption>('Contains');

    buildFilterFunc?: BuildFilterFunction;

    constructor(
        @Inject('MAT_SORT_HEADER_COLUMN_DEF')
        @Optional()
        _columnDef: AsyFilterHeaderColumnDef
    ) {
        super(_columnDef);
    }

    onSearchTypeChange() {
        if (this.search()) {
            this.onFilterChange();
        }
    }

    onSearchText(search: string) {
        this.search.set(search);
        this.onFilterChange();
    }

    _buildState(): { search: string; option: TextFilterOption } | undefined {
        if (this.search()) {
            return { search: this.search(), option: this.option() };
        }
        return undefined;
    }

    _restoreState(state?: { search: string; option: TextFilterOption }) {
        if (state) {
            this.search.set(state.search);
            this.option.set(state.option);
            this.onFilterChange();
        }
    }

    _clearState() {
        this.search.set('');
        this.option.set('Contains');
    }

    _buildFilter() {
        if (this.search()) {
            if (this.buildFilterFunc) {
                return this.buildFilterFunc(this.search(), this.option());
            }

            return {
                [this.id]: { $regex: this.buildRegex(this.search(), this.option()), $options: 'i' }
            };
        }
        return {};
    }

    public buildRegex(search: string, option: TextFilterOption) {
        if (option === 'Equals') {
            return `^${escapeRegExp(search)}$`;
        }
        if (option === 'Starts with') {
            return `^${escapeRegExp(search)}`;
        }
        if (option === 'Ends with') {
            return `${escapeRegExp(search)}$`;
        }
        return escapeRegExp(search);
    }
}