select filter
This commit is contained in:
parent
26eaa39411
commit
7a0ecf281a
@ -1,3 +1,17 @@
|
||||
<mat-toolbar style="background-color: red;">
|
||||
<button mat-icon-button class="example-icon" aria-label="Example icon-button with menu icon">
|
||||
<mat-icon>menu</mat-icon>
|
||||
</button>
|
||||
<span>My App</span>
|
||||
<span class="example-spacer"></span>
|
||||
<button mat-icon-button class="example-icon favorite-icon" aria-label="Example icon-button with heart icon">
|
||||
<mat-icon>favorite</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button class="example-icon" aria-label="Example icon-button with share icon">
|
||||
<mat-icon>share</mat-icon>
|
||||
</button>
|
||||
</mat-toolbar>
|
||||
|
||||
<app-progress-stepper
|
||||
[steps]="['Bla', 'Noch ein Test', 'Abgeschlossen', '2', '4', '5', '2', '4', '5']"></app-progress-stepper>
|
||||
|
||||
@ -23,4 +37,15 @@
|
||||
|
||||
<div>Comment Value: {{parseComment(form)}}</div>
|
||||
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Choose an option</mat-label>
|
||||
<mat-select multiple [formControl]="selectedItemsList">
|
||||
<app-select-filter [array]="allOptions" (filteredReturn)="filteredOptions=$event" (selectAll)="selectAll()"
|
||||
(unselectAll)="unselectAll()"></app-select-filter>
|
||||
@for (item of filteredOptions; track $index) {
|
||||
<mat-option [value]="item">{{item}}</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<router-outlet />
|
@ -3,12 +3,21 @@ import { RouterOutlet } from '@angular/router';
|
||||
import { CommentBoxComponent } from "./comment-box/comment-box.component";
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { FormControl, ReactiveFormsModule } from '@angular/forms';
|
||||
import { FormControl, FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { ProgressStepperComponent } from "./progress-stepper/progress-stepper.component";
|
||||
import { Observable, startWith, map } from 'rxjs';
|
||||
import { MatOptionModule } from '@angular/material/core';
|
||||
import { MatAutocompleteModule } from '@angular/material/autocomplete';
|
||||
import { MatChipsModule } from '@angular/material/chips';
|
||||
import { AsyncPipe } from '@angular/common';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { MatToolbarModule } from '@angular/material/toolbar';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { SelectFilterComponent } from "./select-filter/select-filter.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
imports: [RouterOutlet, CommentBoxComponent, MatFormFieldModule, MatInputModule, ReactiveFormsModule, ProgressStepperComponent],
|
||||
imports: [RouterOutlet, MatToolbarModule, MatIconModule, CommentBoxComponent, MatFormFieldModule, MatInputModule, ReactiveFormsModule, ProgressStepperComponent, MatOptionModule, MatSelectModule, MatAutocompleteModule, MatChipsModule, AsyncPipe, FormsModule, SelectFilterComponent],
|
||||
templateUrl: './app.component.html',
|
||||
styleUrl: './app.component.scss'
|
||||
})
|
||||
@ -24,4 +33,16 @@ export class AppComponent {
|
||||
if (comment.value)
|
||||
return comment.value
|
||||
}
|
||||
|
||||
allOptions = ['Apple', 'Banana', 'Orange', 'Pear', 'Grape'];
|
||||
filteredOptions = this.allOptions.slice();
|
||||
selectedItemsList = new FormControl();
|
||||
|
||||
selectAll(): void {
|
||||
this.selectedItemsList.setValue(this.allOptions);
|
||||
}
|
||||
|
||||
unselectAll(): void {
|
||||
this.selectedItemsList.setValue([]);
|
||||
}
|
||||
}
|
||||
|
18
src/app/select-filter/select-filter.component.html
Normal file
18
src/app/select-filter/select-filter.component.html
Normal file
@ -0,0 +1,18 @@
|
||||
<form [formGroup]="searchForm" class="mat-filter">
|
||||
<div>
|
||||
<input #input class="mat-filter-input" matInput placeholder="Suchen..." formControlName="value"
|
||||
(keydown)="handleKeydown($event)">
|
||||
@if (localSpinner) {
|
||||
<mat-spinner class="spinner" diameter="16"></mat-spinner>
|
||||
}
|
||||
</div>
|
||||
@if (noResults) {
|
||||
<div class="noResultsMessage">
|
||||
{{noResultsMessage}}
|
||||
</div>
|
||||
}
|
||||
</form>
|
||||
<div class="selection-buttons">
|
||||
<div class="button" matRipple (click)="selectAll.emit()">Alle auswählen</div>
|
||||
<div class="button" matRipple (click)="unselectAll.emit()">Alle abwählen</div>
|
||||
</div>
|
69
src/app/select-filter/select-filter.component.scss
Normal file
69
src/app/select-filter/select-filter.component.scss
Normal file
@ -0,0 +1,69 @@
|
||||
.mat-filter {
|
||||
position: sticky;
|
||||
top: -8px;
|
||||
border-bottom-width: 1px;
|
||||
border-bottom-style: solid;
|
||||
border-bottom-color: grey;
|
||||
z-index: 100;
|
||||
font-size: inherit;
|
||||
box-shadow: none;
|
||||
border-radius: 0;
|
||||
padding: 8px;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
|
||||
background-color: var(--mat-sys-surface-container);
|
||||
}
|
||||
|
||||
.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: 10px;
|
||||
font-family: Roboto, "Helvetica Neue", sans-serif;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.selection-buttons {
|
||||
display: grid;
|
||||
grid-template-columns: 50% 50%;
|
||||
justify-content: center;
|
||||
font-size: 0.8rem;
|
||||
|
||||
border-bottom-width: 1px;
|
||||
border-bottom-style: solid;
|
||||
border-bottom-color: grey;
|
||||
|
||||
.button {
|
||||
display: flex;
|
||||
padding: 0.15rem;
|
||||
justify-content: center;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
|
||||
background-color: var(
|
||||
--mat-option-hover-state-layer-color,
|
||||
color-mix(
|
||||
in srgb,
|
||||
var(--mat-sys-on-surface) calc(var(--mat-sys-hover-state-layer-opacity) * 100%),
|
||||
transparent
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
23
src/app/select-filter/select-filter.component.spec.ts
Normal file
23
src/app/select-filter/select-filter.component.spec.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SelectFilterComponent } from './select-filter.component';
|
||||
|
||||
describe('SelectFilterComponent', () => {
|
||||
let component: SelectFilterComponent;
|
||||
let fixture: ComponentFixture<SelectFilterComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [SelectFilterComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(SelectFilterComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
85
src/app/select-filter/select-filter.component.ts
Normal file
85
src/app/select-filter/select-filter.component.ts
Normal file
@ -0,0 +1,85 @@
|
||||
import { A, Z, ZERO, NINE, SPACE } from '@angular/cdk/keycodes';
|
||||
import { Component, ElementRef, EventEmitter, Input, Output, ViewChild } from '@angular/core';
|
||||
import { FormGroup, FormBuilder, ReactiveFormsModule } from '@angular/forms';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
import { MatRippleModule } from '@angular/material/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-select-filter',
|
||||
imports: [ReactiveFormsModule, MatProgressSpinnerModule, MatRippleModule],
|
||||
templateUrl: './select-filter.component.html',
|
||||
styleUrl: './select-filter.component.scss'
|
||||
})
|
||||
export class SelectFilterComponent {
|
||||
|
||||
private searchFormValueChangesSubscription!: Subscription;
|
||||
@ViewChild('input', { static: true }) input!: ElementRef;
|
||||
|
||||
@Input('array') array: any[] = [];
|
||||
@Input('showSpinner') showSpinner = true;
|
||||
@Input('noResultsMessage') noResultsMessage = 'No results';
|
||||
|
||||
noResults = false;
|
||||
|
||||
localSpinner = false;
|
||||
@Output() filteredReturn = new EventEmitter<any>();
|
||||
@Output() selectAll = new EventEmitter<void>();
|
||||
@Output() unselectAll = new EventEmitter<void>();
|
||||
|
||||
public filteredItems: any = [];
|
||||
public searchForm: FormGroup;
|
||||
|
||||
constructor(fb: FormBuilder) {
|
||||
this.searchForm = fb.group({
|
||||
value: ''
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.searchFormValueChangesSubscription = this.searchForm.valueChanges.subscribe(change => {
|
||||
if (this.showSpinner) {
|
||||
this.localSpinner = true;
|
||||
}
|
||||
if (change.value) {
|
||||
// IF THE DISPLAY MEMBER INPUT IS SET WE CHECK THE SPECIFIC PROPERTY
|
||||
|
||||
this.filteredItems = this.array.filter((name: string) => name.toLowerCase().includes(change.value.toLowerCase()));
|
||||
// OTHERWISE, WE CHECK THE ENTIRE STRING
|
||||
|
||||
// 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);
|
||||
setTimeout(() => {
|
||||
if (this.showSpinner) {
|
||||
this.localSpinner = false;
|
||||
}
|
||||
}, 2000);
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
this.input.nativeElement.focus();
|
||||
}, 500);
|
||||
}
|
||||
|
||||
handleKeydown(event: KeyboardEvent) {
|
||||
// PREVENT PROPAGATION FOR ALL ALPHANUMERIC CHARACTERS IN ORDER TO AVOID SELECTION ISSUES
|
||||
if ((event.key && event.key.length === 1) ||
|
||||
(event.keyCode >= A && event.keyCode <= Z) ||
|
||||
(event.keyCode >= ZERO && event.keyCode <= NINE) ||
|
||||
(event.keyCode === SPACE)) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
}
|
||||
ngOnDestroy() {
|
||||
this.filteredReturn.emit(this.array);
|
||||
this.searchFormValueChangesSubscription.unsubscribe();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user