@devlukaszmichalak/mat-select-filter
Version:
A filter for mat-select
181 lines (175 loc) • 11.7 kB
JavaScript
import * as i0 from '@angular/core';
import { EventEmitter, Component, ViewChild, Input, Output, NgModule } from '@angular/core';
import * as i1 from '@angular/forms';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { A, Z, ZERO, NINE, SPACE } from '@angular/cdk/keycodes';
import { debounceTime, timer, take } from 'rxjs';
import * as i3 from '@angular/material/progress-spinner';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import * as i2 from '@angular/common';
import { CommonModule } from '@angular/common';
import * as i4 from '@angular/material/input';
import { MatInputModule } from '@angular/material/input';
class MatSelectFilterComponent {
constructor(fb) {
this.fb = fb;
this.array = [];
this.placeholder = 'Search...';
this.showSpinner = true;
this.noResultsMessage = 'No results';
this.filterDebounceTime = 0;
this.filteredReturn = new EventEmitter();
this.noResults = false;
this.localSpinner = false;
this.filteredItems = [];
this.searchForm = this.fb.group({
value: ''
});
}
ngOnInit() {
this.searchFormValueChangesSubscription = this.searchForm.valueChanges
.pipe(debounceTime(this.filterDebounceTime))
.subscribe(value => {
this.localSpinner = true;
const userInputLowerCase = value['value'].toLowerCase();
if (userInputLowerCase) {
this.filterArray(userInputLowerCase);
// NO RESULTS VALIDATION
this.noResults = this.filteredItems == null || this.filteredItems.length === 0;
}
else {
this.filteredItems = this.array.slice();
this.noResults = false;
}
this.filteredReturn.emit(this.filteredItems);
timer(2000)
.pipe(take(1))
.subscribe(() => this.localSpinner = false);
});
timer(500)
.pipe(take(1))
.subscribe(() => this.input.nativeElement.focus());
}
filterArray(userInputLowerCase) {
// IF THE DISPLAY MEMBER INPUT IS SET WE CHECK THE SPECIFIC PROPERTY
if (this.displayMember == null) {
this.filteredItems = this.array.filter((name) => name.toLowerCase().includes(userInputLowerCase));
}
else if (this.hasGroup && this.groupArrayName && this.displayMember) {
this.filteredItems = this.array
.map((a) => {
const objCopy = { ...a };
objCopy[this.groupArrayName] = objCopy[this.groupArrayName].filter((g) => g[this.displayMember].toLowerCase().includes(userInputLowerCase));
return objCopy;
})
.filter((x) => x[this.groupArrayName].length > 0);
// OTHERWISE, WE CHECK THE ENTIRE STRING
}
else {
this.filteredItems = this.array.filter((item) => item[this.displayMember].toLowerCase().includes(userInputLowerCase));
}
}
handleKeydown(event) {
const isAlphanumeric = (event.key && event.key.length === 1) ||
(event.keyCode >= A && event.keyCode <= Z) ||
(event.keyCode >= ZERO && event.keyCode <= NINE) ||
(event.keyCode === SPACE);
// PREVENT PROPAGATION FOR ALL ALPHANUMERIC CHARACTERS IN ORDER TO AVOID SELECTION ISSUES
if (isAlphanumeric) {
event.stopPropagation();
}
}
ngOnDestroy() {
this.filteredReturn.emit(this.array);
this.searchFormValueChangesSubscription.unsubscribe();
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.0", ngImport: i0, type: MatSelectFilterComponent, deps: [{ token: i1.UntypedFormBuilder }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.0.0", type: MatSelectFilterComponent, isStandalone: true, selector: "mat-select-filter", inputs: { array: "array", placeholder: "placeholder", color: "color", displayMember: "displayMember", showSpinner: "showSpinner", noResultsMessage: "noResultsMessage", hasGroup: "hasGroup", groupArrayName: "groupArrayName", filterDebounceTime: "filterDebounceTime" }, outputs: { filteredReturn: "filteredReturn" }, viewQueries: [{ propertyName: "input", first: true, predicate: ["input"], descendants: true, static: true }], ngImport: i0, template: `
<form [formGroup]="searchForm" class="mat-filter" [ngStyle]="{'background-color': color ? color : 'white'}">
<div>
<input #input class="mat-filter-input" matInput placeholder="{{placeholder}}" formControlName="value"
(keydown)="handleKeydown($event)">
<mat-spinner *ngIf="localSpinner && showSpinner" class="spinner" diameter="16"></mat-spinner>
</div>
<div *ngIf="noResults"
class="noResultsMessage">
{{ noResultsMessage }}
</div>
</form>
`, isInline: true, styles: [".mat-filter{position:sticky;top:-8px;margin-top:-8px;border-bottom-width:1px;border-bottom-style:solid;border-bottom-color:gray;z-index:100;font-size:inherit;box-shadow:none;border-radius:0;padding:16px;-webkit-box-sizing:border-box;box-sizing:border-box}.mat-filter:has(.noResultsMessage){border:0}.mat-filter-input{-webkit-appearance:none;-moz-appearance:none;appearance:none;outline:none;border:0;background-color:unset;color:gray;width:100%}.spinner{position:absolute;right:16px;top:calc(50% - 8px)}.noResultsMessage{margin-top:16px;font-family:Roboto,Helvetica Neue,sans-serif;font-size:16px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i3.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i4.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.0", ngImport: i0, type: MatSelectFilterComponent, decorators: [{
type: Component,
args: [{ selector: 'mat-select-filter', template: `
<form [formGroup]="searchForm" class="mat-filter" [ngStyle]="{'background-color': color ? color : 'white'}">
<div>
<input #input class="mat-filter-input" matInput placeholder="{{placeholder}}" formControlName="value"
(keydown)="handleKeydown($event)">
<mat-spinner *ngIf="localSpinner && showSpinner" class="spinner" diameter="16"></mat-spinner>
</div>
<div *ngIf="noResults"
class="noResultsMessage">
{{ noResultsMessage }}
</div>
</form>
`, imports: [
CommonModule,
FormsModule,
ReactiveFormsModule,
MatProgressSpinnerModule,
MatInputModule
], styles: [".mat-filter{position:sticky;top:-8px;margin-top:-8px;border-bottom-width:1px;border-bottom-style:solid;border-bottom-color:gray;z-index:100;font-size:inherit;box-shadow:none;border-radius:0;padding:16px;-webkit-box-sizing:border-box;box-sizing:border-box}.mat-filter:has(.noResultsMessage){border:0}.mat-filter-input{-webkit-appearance:none;-moz-appearance:none;appearance:none;outline:none;border:0;background-color:unset;color:gray;width:100%}.spinner{position:absolute;right:16px;top:calc(50% - 8px)}.noResultsMessage{margin-top:16px;font-family:Roboto,Helvetica Neue,sans-serif;font-size:16px}\n"] }]
}], ctorParameters: () => [{ type: i1.UntypedFormBuilder }], propDecorators: { input: [{
type: ViewChild,
args: ['input', { static: true }]
}], array: [{
type: Input,
args: ['array']
}], placeholder: [{
type: Input,
args: ['placeholder']
}], color: [{
type: Input,
args: ['color']
}], displayMember: [{
type: Input,
args: ['displayMember']
}], showSpinner: [{
type: Input,
args: ['showSpinner']
}], noResultsMessage: [{
type: Input,
args: ['noResultsMessage']
}], hasGroup: [{
type: Input,
args: ['hasGroup']
}], groupArrayName: [{
type: Input,
args: ['groupArrayName']
}], filterDebounceTime: [{
type: Input,
args: ['filterDebounceTime']
}], filteredReturn: [{
type: Output
}] } });
class MatSelectFilterModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.0", ngImport: i0, type: MatSelectFilterModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.0.0", ngImport: i0, type: MatSelectFilterModule, imports: [MatSelectFilterComponent], exports: [MatSelectFilterComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.0.0", ngImport: i0, type: MatSelectFilterModule, imports: [MatSelectFilterComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.0", ngImport: i0, type: MatSelectFilterModule, decorators: [{
type: NgModule,
args: [{
declarations: [],
imports: [MatSelectFilterComponent],
exports: [MatSelectFilterComponent]
}]
}] });
/*
* Public API Surface of mat-select-filter
*/
/**
* Generated bundle index. Do not edit.
*/
export { MatSelectFilterComponent, MatSelectFilterModule };
//# sourceMappingURL=devlukaszmichalak-mat-select-filter.mjs.map