Improve filter dialog style
Signed-off-by: trivernis <trivernis@protonmail.com>pull/4/head
parent
6cb91bf263
commit
05c2aa3507
@ -1,16 +1,33 @@
|
||||
.remove-button {
|
||||
.remove-button, .remove-button-inner-list {
|
||||
position: absolute;
|
||||
top: calc(0.5em - 15px);
|
||||
right: 0;
|
||||
z-index: 999;
|
||||
top: calc(0.5em - 15px);
|
||||
}
|
||||
|
||||
.remove-button {
|
||||
right: 16px;
|
||||
}
|
||||
|
||||
mat-list {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: block;
|
||||
background-color: #353535;
|
||||
border-radius: 0.25em;
|
||||
}
|
||||
|
||||
mat-list-item.or-filter-list-item {
|
||||
padding: 0.5em 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
::ng-deep .mat-list-item-content {
|
||||
padding-right: 0;
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.or-span {
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
|
@ -0,0 +1,15 @@
|
||||
<mat-form-field>
|
||||
<mat-label>
|
||||
Enter a tag
|
||||
</mat-label>
|
||||
<input #tagInput
|
||||
[formControl]="formControl"
|
||||
matInput
|
||||
(keydown.enter)="addTagByInput($event)"
|
||||
[matAutocomplete]="auto">
|
||||
<mat-autocomplete #auto (optionSelected)="addTagByAutocomplete($event)">
|
||||
<mat-option *ngFor="let tag of autosuggestTags | async" [value]="tag">
|
||||
{{tag}}
|
||||
</mat-option>
|
||||
</mat-autocomplete>
|
||||
</mat-form-field>
|
@ -0,0 +1,3 @@
|
||||
mat-form-field {
|
||||
width: 100%;
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { TagInputComponent } from './tag-input.component';
|
||||
|
||||
describe('TagInputComponent', () => {
|
||||
let component: TagInputComponent;
|
||||
let fixture: ComponentFixture<TagInputComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ TagInputComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(TagInputComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,114 @@
|
||||
import {
|
||||
Component, ElementRef,
|
||||
EventEmitter,
|
||||
Input, OnChanges,
|
||||
OnInit,
|
||||
Output, SimpleChanges,
|
||||
ViewChild
|
||||
} from '@angular/core';
|
||||
import {Tag} from "../../../models/Tag";
|
||||
import {FormControl} from "@angular/forms";
|
||||
import {MatAutocompleteSelectedEvent} from "@angular/material/autocomplete";
|
||||
import {Observable} from "rxjs";
|
||||
import {debounceTime, delay, map, startWith} from "rxjs/operators";
|
||||
|
||||
@Component({
|
||||
selector: 'app-tag-input',
|
||||
templateUrl: './tag-input.component.html',
|
||||
styleUrls: ['./tag-input.component.scss']
|
||||
})
|
||||
export class TagInputComponent implements OnChanges{
|
||||
|
||||
@Input() availableTags: Tag[] = [];
|
||||
@Input() allowNegation: boolean = false;
|
||||
@Input() allowInvalid: boolean = false;
|
||||
@Output() tagAdded = new EventEmitter<string>();
|
||||
|
||||
@ViewChild("tagInput") tagInput!: ElementRef<HTMLInputElement>;
|
||||
public formControl = new FormControl();
|
||||
public autosuggestTags: Observable<string[]>;
|
||||
private tagsForAutocomplete: string[] = [];
|
||||
|
||||
constructor() {
|
||||
this.tagsForAutocomplete = this.availableTags.map(t => t.getNormalizedOutput());
|
||||
this.autosuggestTags = this.formControl.valueChanges.pipe(
|
||||
startWith(null),
|
||||
debounceTime(250),
|
||||
map((tag: string | null) => tag ? this.filterSuggestionTag(tag) : this.tagsForAutocomplete.slice(0, 20)));
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
if (changes["availableTags"]) {
|
||||
this.tagsForAutocomplete = this.availableTags.map(t => t.getNormalizedOutput());
|
||||
}
|
||||
}
|
||||
|
||||
public addTagByInput(event: any): void {
|
||||
this.addTag(this.formControl.value);
|
||||
}
|
||||
|
||||
public addTagByAutocomplete(event: MatAutocompleteSelectedEvent): void {
|
||||
this.addTag(event.option.value);
|
||||
}
|
||||
|
||||
private addTag(value: string) {
|
||||
const tag = this.normalizeTag(value);
|
||||
if (tag.length > 0 && (this.allowInvalid || this.checkTagValid(tag))) {
|
||||
this.tagAdded.emit(tag);
|
||||
this.formControl.setValue("");
|
||||
this.tagInput.nativeElement.value = "";
|
||||
}
|
||||
}
|
||||
|
||||
private filterSuggestionTag(tag: string) {
|
||||
let normalizedTag = this.normalizeTag(tag);
|
||||
const negated = normalizedTag.startsWith("-") && this.allowNegation;
|
||||
normalizedTag = this.allowNegation? normalizedTag.replace(/^-/, "") : normalizedTag;
|
||||
|
||||
return this.tagsForAutocomplete.filter(
|
||||
t => t.includes(normalizedTag))
|
||||
.map(t => negated ? "-" + t: t)
|
||||
.sort((l, r) => this.compareSuggestionTags(normalizedTag, l, r))
|
||||
.slice(0, 20);
|
||||
}
|
||||
|
||||
private checkTagValid(tag: string): boolean {
|
||||
if (this.allowNegation) {
|
||||
tag = tag.replace(/^-/, "");
|
||||
}
|
||||
return this.tagsForAutocomplete.includes(tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes the tag by removing whitespaces
|
||||
* @param {string} tag
|
||||
* @returns {string}
|
||||
* @private
|
||||
*/
|
||||
private normalizeTag(tag: string): string {
|
||||
let normalizedTag = tag.trim();
|
||||
let parts = normalizedTag.split(":");
|
||||
|
||||
if (parts.length > 1) {
|
||||
const namespace = parts.shift()!.trim();
|
||||
const name = parts.join(":").trim();
|
||||
return namespace + ":" + name;
|
||||
} else {
|
||||
return normalizedTag;
|
||||
}
|
||||
}
|
||||
|
||||
private compareSuggestionTags(query: string, l: string, r: string): number {
|
||||
if (l.startsWith(query) && !r.startsWith(query)) {
|
||||
return -1;
|
||||
} else if (!l.startsWith(query) && r.startsWith(query)) {
|
||||
return 1;
|
||||
} else if (l.length < r.length) {
|
||||
return -1;
|
||||
} else if (l.length > r.length) {
|
||||
return 1;
|
||||
} else {
|
||||
return l.localeCompare(r)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue