Browse Source

feat(ui): create gf-tags-selector component

pull/4256/head
KenTandrian 7 months ago
parent
commit
81494b6696
  1. 1
      libs/ui/src/lib/tags-selector/index.ts
  2. 32
      libs/ui/src/lib/tags-selector/tags-selector.component.html
  3. 3
      libs/ui/src/lib/tags-selector/tags-selector.component.scss
  4. 98
      libs/ui/src/lib/tags-selector/tags-selector.component.ts

1
libs/ui/src/lib/tags-selector/index.ts

@ -0,0 +1 @@
export * from './tags-selector.component';

32
libs/ui/src/lib/tags-selector/tags-selector.component.html

@ -0,0 +1,32 @@
<mat-form-field appearance="outline" class="w-100">
<mat-label i18n>Tags</mat-label>
<mat-chip-grid #tagsChipList>
@for (tag of tagsSelected(); track tag.id) {
<mat-chip-row
matChipRemove
[removable]="true"
(removed)="onRemoveTag(tag)"
>
{{ tag.name }}
<ion-icon class="ml-2" matPrefix name="close-outline" />
</mat-chip-row>
}
<input
#tagInput
name="close-outline"
[matAutocomplete]="autocompleteTags"
[matChipInputFor]="tagsChipList"
[matChipInputSeparatorKeyCodes]="separatorKeysCodes"
/>
</mat-chip-grid>
<mat-autocomplete
#autocompleteTags="matAutocomplete"
(optionSelected)="onAddTag($event)"
>
@for (tag of tagsUnselected(); track tag.id) {
<mat-option [value]="tag.id">
{{ tag.name }}
</mat-option>
}
</mat-autocomplete>
</mat-form-field>

3
libs/ui/src/lib/tags-selector/tags-selector.component.scss

@ -0,0 +1,3 @@
:host {
display: block;
}

98
libs/ui/src/lib/tags-selector/tags-selector.component.ts

@ -0,0 +1,98 @@
import { COMMA, ENTER } from '@angular/cdk/keycodes';
import { CommonModule } from '@angular/common';
import {
ChangeDetectionStrategy,
Component,
computed,
CUSTOM_ELEMENTS_SCHEMA,
effect,
ElementRef,
EventEmitter,
Input,
OnInit,
Output,
signal,
ViewChild
} from '@angular/core';
import { FormsModule } from '@angular/forms';
import {
MatAutocompleteModule,
MatAutocompleteSelectedEvent
} from '@angular/material/autocomplete';
import { MatChipsModule } from '@angular/material/chips';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { Tag } from '@prisma/client';
@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [
CommonModule,
FormsModule,
MatAutocompleteModule,
MatFormFieldModule,
MatInputModule,
MatChipsModule,
MatIconModule
],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
selector: 'gf-tags-selector',
styleUrls: ['./tags-selector.component.scss'],
templateUrl: 'tags-selector.component.html'
})
export class GfTagsSelectorComponent implements OnInit {
@Input() tags: Tag[];
@Input() tagsAvailable: Tag[];
@Output() tagsChanged = new EventEmitter<Tag[]>();
@ViewChild('tagInput') tagInput: ElementRef<HTMLInputElement>;
public readonly tagsSelected = signal<Tag[]>([]);
public readonly separatorKeysCodes: number[] = [COMMA, ENTER];
public readonly tagsUnselected = computed(() => {
const tags = this.tagsSelected();
return tags ? this.filterTags(tags) : this.tagsAvailable.slice();
});
public constructor() {
effect(() => {
if (this.tagsSelected()) {
this.tagsChanged.emit(this.tagsSelected());
}
});
}
public ngOnInit() {
this.tagsSelected.set(this.tags);
}
public onAddTag(event: MatAutocompleteSelectedEvent) {
const tag = this.tagsAvailable.find(({ id }) => {
return id === event.option.value;
});
this.tagsSelected.update((tags) => {
return [...(tags ?? []), tag];
});
this.tagInput.nativeElement.value = '';
}
public onRemoveTag(tag: Tag) {
this.tagsSelected.update((tags) => {
return tags.filter(({ id }) => {
return id !== tag.id;
});
});
}
private filterTags(tagsSelected: Tag[]) {
const tagIds = tagsSelected.map(({ id }) => {
return id;
});
return this.tagsAvailable.filter(({ id }) => {
return !tagIds.includes(id);
});
}
}
Loading…
Cancel
Save