Refactored a lot of code to move towards signals. Everything is generic. Only thing left is the logic for determining which controls to show and the localization pipes.
This commit is contained in:
parent
a4c31debd0
commit
ba9bf47a41
9 changed files with 166 additions and 93 deletions
12
UI/Web/src/app/_pipes/generic-filter-field.pipe.ts
Normal file
12
UI/Web/src/app/_pipes/generic-filter-field.pipe.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
|
||||
@Pipe({
|
||||
name: 'genericFilterField'
|
||||
})
|
||||
export class GenericFilterFieldPipe implements PipeTransform {
|
||||
|
||||
transform(value: unknown, ...args: unknown[]): unknown {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -24,7 +24,7 @@
|
|||
@for (filterStmt of filter.statements; track filterStmt; let i = $index) {
|
||||
<div class="row mb-2">
|
||||
<div class="col-md-10">
|
||||
<app-metadata-row-filter [index]="i + 100" [preset]="filterStmt" [availableFields]="availableFilterFields" (filterStatement)="updateFilter(i, $event)">
|
||||
<app-metadata-row-filter [preset]="filterStmt" [entityType]="entityType()" (filterStatement)="updateFilter(i, $event)">
|
||||
<div class="col-md-1 ms-2">
|
||||
@if (i < (filter.statements.length - 1) && filter.statements.length > 1) {
|
||||
<button type="button" class="btn btn-icon" #removeBtn [ngbTooltip]="t('remove-rule', {num: i})" (click)="removeFilter(i)">
|
||||
|
|
@ -59,7 +59,7 @@
|
|||
@for (filterStmt of filter.statements; track filterStmt; let i = $index) {
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-12">
|
||||
<app-metadata-row-filter [index]="i" [preset]="filterStmt" [availableFields]="availableFilterFields" (filterStatement)="updateFilter(i, $event)">
|
||||
<app-metadata-row-filter [preset]="filterStmt" [entityType]="entityType()" (filterStatement)="updateFilter(i, $event)">
|
||||
<div class="col-md-1 ms-2 col-1">
|
||||
@if (i < (filter.statements.length - 1) && filter.statements.length > 1) {
|
||||
<button type="button" class="btn btn-icon" #removeBtn [ngbTooltip]="t('remove-rule')" (click)="removeFilter(i)">
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import {
|
|||
DestroyRef,
|
||||
EventEmitter,
|
||||
inject,
|
||||
input,
|
||||
Input,
|
||||
OnInit,
|
||||
Output
|
||||
|
|
@ -18,10 +19,10 @@ import {FormControl, FormGroup, FormsModule, ReactiveFormsModule} from "@angular
|
|||
import {NgbTooltip} from "@ng-bootstrap/ng-bootstrap";
|
||||
import {FilterCombination} from "../../../_models/metadata/v2/filter-combination";
|
||||
import {FilterUtilitiesService} from "../../../shared/_services/filter-utilities.service";
|
||||
import {allSeriesFilterFields} from "../../../_models/metadata/v2/filter-field";
|
||||
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
|
||||
import {distinctUntilChanged, tap} from "rxjs/operators";
|
||||
import {translate, TranslocoDirective} from "@jsverse/transloco";
|
||||
import {ValidFilterEntity} from "../../filter-settings";
|
||||
|
||||
@Component({
|
||||
selector: 'app-metadata-builder',
|
||||
|
|
@ -36,14 +37,14 @@ import {translate, TranslocoDirective} from "@jsverse/transloco";
|
|||
],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class MetadataBuilderComponent implements OnInit {
|
||||
export class MetadataBuilderComponent<TFilter extends number = number, TSort extends number = number> implements OnInit {
|
||||
|
||||
@Input({required: true}) filter!: FilterV2<number>;
|
||||
/**
|
||||
* The number of statements that can be. 0 means unlimited. -1 means none.
|
||||
*/
|
||||
@Input() statementLimit = 0;
|
||||
@Input() availableFilterFields = allSeriesFilterFields;
|
||||
entityType = input.required<ValidFilterEntity>();
|
||||
@Output() update: EventEmitter<FilterV2<number>> = new EventEmitter<FilterV2<number>>();
|
||||
@Output() apply: EventEmitter<void> = new EventEmitter<void>();
|
||||
|
||||
|
|
@ -52,7 +53,6 @@ export class MetadataBuilderComponent implements OnInit {
|
|||
protected readonly utilityService = inject(UtilityService);
|
||||
protected readonly filterUtilityService = inject(FilterUtilitiesService);
|
||||
private readonly destroyRef = inject(DestroyRef);
|
||||
protected readonly Breakpoint = Breakpoint;
|
||||
|
||||
formGroup: FormGroup = new FormGroup({});
|
||||
|
||||
|
|
@ -84,4 +84,6 @@ export class MetadataBuilderComponent implements OnInit {
|
|||
this.update.emit(this.filter);
|
||||
}
|
||||
|
||||
protected readonly Breakpoint = Breakpoint;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
<div class="row g-0">
|
||||
<div class="col-md-3 me-2 col-10 mb-2">
|
||||
<select class="form-select me-2" formControlName="input">
|
||||
@for (field of availableFields; track field) {
|
||||
@for (field of filterFieldOptions(); track field) {
|
||||
<option [value]="field">{{field | filterField}}</option>
|
||||
}
|
||||
</select>
|
||||
|
|
@ -18,7 +18,7 @@
|
|||
</div>
|
||||
|
||||
<div class="col-md-4 col-10 mb-2">
|
||||
@if (IsEmptySelected) {
|
||||
@if (isEmptySelected()) {
|
||||
@if (predicateType$ | async; as predicateType) {
|
||||
@switch (predicateType) {
|
||||
@case (PredicateType.Text) {
|
||||
|
|
@ -50,7 +50,7 @@
|
|||
<select2 [data]="opts"
|
||||
formControlName="filterValue"
|
||||
[hideSelectedItems]="true"
|
||||
[multiple]="MultipleDropdownAllowed"
|
||||
[multiple]="isMultiSelectDropdownAllowed()"
|
||||
[infiniteScroll]="true"
|
||||
[resettable]="true">
|
||||
</select2>
|
||||
|
|
@ -62,10 +62,11 @@
|
|||
</div>
|
||||
|
||||
<div class="col pt-2 ms-2">
|
||||
@if (UiLabel !== null) {
|
||||
<span class="text-muted">{{t(UiLabel.unit)}}</span>
|
||||
@if (UiLabel.tooltip) {
|
||||
<i class="fa fa-info-circle ms-1" aria-hidden="true" [ngbTooltip]="t(UiLabel.tooltip)"></i>
|
||||
@let label = uiLabel();
|
||||
@if (label !== null) {
|
||||
<span class="text-muted">{{t(label.unit)}}</span>
|
||||
@if (label.tooltip) {
|
||||
<i class="fa fa-info-circle ms-1" aria-hidden="true" [ngbTooltip]="t(label.tooltip)"></i>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -2,28 +2,34 @@ import {
|
|||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
computed,
|
||||
DestroyRef,
|
||||
EventEmitter,
|
||||
inject,
|
||||
Injector,
|
||||
input,
|
||||
Input,
|
||||
OnInit,
|
||||
Output,
|
||||
Signal,
|
||||
} from '@angular/core';
|
||||
import {FormControl, FormGroup, ReactiveFormsModule} from '@angular/forms';
|
||||
import {FilterStatement} from '../../../_models/metadata/v2/filter-statement';
|
||||
import {BehaviorSubject, distinctUntilChanged, filter, Observable, of, startWith, switchMap, tap} from 'rxjs';
|
||||
import {BehaviorSubject, distinctUntilChanged, filter, map, Observable, of, startWith, switchMap, tap} from 'rxjs';
|
||||
import {MetadataService} from 'src/app/_services/metadata.service';
|
||||
import {FilterComparison} from 'src/app/_models/metadata/v2/filter-comparison';
|
||||
import {allSeriesFilterFields, FilterField} from 'src/app/_models/metadata/v2/filter-field';
|
||||
import {FilterField} from 'src/app/_models/metadata/v2/filter-field';
|
||||
import {AsyncPipe} from "@angular/common";
|
||||
import {FilterFieldPipe} from "../../../_pipes/filter-field.pipe";
|
||||
import {FilterComparisonPipe} from "../../../_pipes/filter-comparison.pipe";
|
||||
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
|
||||
import {takeUntilDestroyed, toSignal} from "@angular/core/rxjs-interop";
|
||||
import {Select2, Select2Option} from "ng-select2-component";
|
||||
import {NgbDate, NgbDateParserFormatter, NgbInputDatepicker, NgbTooltip} from "@ng-bootstrap/ng-bootstrap";
|
||||
import {TranslocoDirective, TranslocoService} from "@jsverse/transloco";
|
||||
import {MangaFormatPipe} from "../../../_pipes/manga-format.pipe";
|
||||
import {AgeRatingPipe} from "../../../_pipes/age-rating.pipe";
|
||||
import {ValidFilterEntity} from "../../filter-settings";
|
||||
import {FilterUtilitiesService} from "../../../shared/_services/filter-utilities.service";
|
||||
|
||||
enum PredicateType {
|
||||
Text = 1,
|
||||
|
|
@ -131,31 +137,25 @@ const BooleanComparisons = [
|
|||
],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class MetadataFilterRowComponent implements OnInit {
|
||||
|
||||
protected readonly FilterComparison = FilterComparison;
|
||||
protected readonly PredicateType = PredicateType;
|
||||
export class MetadataFilterRowComponent<TFilter extends number = number, TSort extends number = number> implements OnInit {
|
||||
|
||||
private readonly cdRef = inject(ChangeDetectorRef);
|
||||
private readonly destroyRef = inject(DestroyRef);
|
||||
private readonly dateParser = inject(NgbDateParserFormatter);
|
||||
private readonly metadataService = inject(MetadataService);
|
||||
private readonly translocoService = inject(TranslocoService);
|
||||
private readonly filterUtilitiesService = inject(FilterUtilitiesService);
|
||||
private readonly injector = inject(Injector);
|
||||
|
||||
|
||||
@Input() index: number = 0; // This is only for debugging
|
||||
/**
|
||||
* Slightly misleading as this is the initial state and will be updated on the filterStatement event emitter
|
||||
*/
|
||||
@Input() preset!: FilterStatement<number>;
|
||||
@Input() availableFields: Array<FilterField> = allSeriesFilterFields;
|
||||
@Output() filterStatement = new EventEmitter<FilterStatement<number>>();
|
||||
@Input() preset!: FilterStatement<TFilter>;
|
||||
entityType = input.required<ValidFilterEntity>();
|
||||
@Output() filterStatement = new EventEmitter<FilterStatement<TFilter>>();
|
||||
|
||||
|
||||
formGroup: FormGroup = new FormGroup({
|
||||
'comparison': new FormControl<FilterComparison>(FilterComparison.Equal, []),
|
||||
'filterValue': new FormControl<string | number>('', []),
|
||||
});
|
||||
formGroup!: FormGroup;
|
||||
validComparisons$: BehaviorSubject<FilterComparison[]> = new BehaviorSubject([FilterComparison.Equal] as FilterComparison[]);
|
||||
predicateType$: BehaviorSubject<PredicateType> = new BehaviorSubject(PredicateType.Text as PredicateType);
|
||||
dropdownOptions$ = of<Select2Option[]>([]);
|
||||
|
|
@ -164,25 +164,57 @@ export class MetadataFilterRowComponent implements OnInit {
|
|||
private readonly mangaFormatPipe = new MangaFormatPipe(this.translocoService);
|
||||
private readonly ageRatingPipe = new AgeRatingPipe();
|
||||
|
||||
get IsEmptySelected() {
|
||||
return parseInt(this.formGroup.get('comparison')?.value + '', 10) !== FilterComparison.IsEmpty;
|
||||
}
|
||||
|
||||
private comparisonSignal!: Signal<FilterComparison>;
|
||||
private inputSignal!: Signal<TFilter>;
|
||||
|
||||
get UiLabel(): FilterRowUi | null {
|
||||
const field = parseInt(this.formGroup.get('input')!.value, 10) as FilterField;
|
||||
if (!unitLabels.has(field)) return null;
|
||||
return unitLabels.get(field) as FilterRowUi;
|
||||
}
|
||||
|
||||
get MultipleDropdownAllowed() {
|
||||
const comp = parseInt(this.formGroup.get('comparison')?.value, 10) as FilterComparison;
|
||||
return comp === FilterComparison.Contains || comp === FilterComparison.NotContains || comp === FilterComparison.MustContains;
|
||||
}
|
||||
isEmptySelected: Signal<boolean> = computed(() => false);
|
||||
uiLabel: Signal<FilterRowUi | null> = computed(() => null);
|
||||
isMultiSelectDropdownAllowed: Signal<boolean> = computed(() => false);
|
||||
|
||||
sortFieldOptions = computed(() => []);
|
||||
filterFieldOptions = computed(() => []);
|
||||
|
||||
ngOnInit() {
|
||||
this.formGroup.addControl('input', new FormControl<FilterField>(FilterField.SeriesName, []));
|
||||
|
||||
this.formGroup = new FormGroup({
|
||||
'comparison': new FormControl<FilterComparison>(FilterComparison.Equal, []),
|
||||
'filterValue': new FormControl<string | number>('', []),
|
||||
'input': new FormControl<TFilter>(this.filterUtilitiesService.getDefaultFilterField<TFilter>(this.entityType()), [])
|
||||
});
|
||||
|
||||
this.comparisonSignal = toSignal<FilterComparison>(
|
||||
this.formGroup.get('comparison')!.valueChanges.pipe(
|
||||
startWith(this.formGroup.get('comparison')!.value),
|
||||
map(d => parseInt(d + '', 10) as FilterComparison)
|
||||
)
|
||||
, {requireSync: true, injector: this.injector});
|
||||
this.inputSignal = toSignal<TFilter>(
|
||||
this.formGroup.get('input')!.valueChanges.pipe(
|
||||
startWith(this.formGroup.get('input')!.value),
|
||||
map(d => parseInt(d + '', 10) as TFilter)
|
||||
)
|
||||
, {requireSync: true, injector: this.injector});
|
||||
|
||||
this.isEmptySelected = computed(() => this.comparisonSignal() !== FilterComparison.IsEmpty);
|
||||
this.uiLabel = computed(() => {
|
||||
if (!unitLabels.has(this.inputSignal())) return null;
|
||||
return unitLabels.get(this.inputSignal()) as FilterRowUi;
|
||||
});
|
||||
|
||||
this.isMultiSelectDropdownAllowed = computed(() => {
|
||||
return this.comparisonSignal() === FilterComparison.Contains || this.comparisonSignal() === FilterComparison.NotContains || this.comparisonSignal() === FilterComparison.MustContains;
|
||||
});
|
||||
|
||||
this.sortFieldOptions = computed(() => {
|
||||
return this.filterUtilitiesService.getSortFields(this.entityType());
|
||||
});
|
||||
this.filterFieldOptions = computed(() => {
|
||||
return this.filterUtilitiesService.getFilterFields(this.entityType());
|
||||
});
|
||||
|
||||
|
||||
//this.formGroup.addControl('input', new FormControl<FilterField>(FilterField.SeriesName, []));
|
||||
|
||||
this.formGroup.get('input')?.valueChanges.pipe(distinctUntilChanged(), takeUntilDestroyed(this.destroyRef)).subscribe((val: string) => this.handleFieldChange(val));
|
||||
this.populateFromPreset();
|
||||
|
|
@ -215,7 +247,7 @@ export class MetadataFilterRowComponent implements OnInit {
|
|||
propagateFilterUpdate() {
|
||||
const stmt = {
|
||||
comparison: parseInt(this.formGroup.get('comparison')?.value, 10) as FilterComparison,
|
||||
field: parseInt(this.formGroup.get('input')?.value, 10) as FilterField,
|
||||
field: parseInt(this.formGroup.get('input')?.value, 10) as TFilter,
|
||||
value: this.formGroup.get('filterValue')?.value!
|
||||
};
|
||||
|
||||
|
|
@ -252,7 +284,7 @@ export class MetadataFilterRowComponent implements OnInit {
|
|||
this.formGroup.get('filterValue')?.patchValue(this.dateParser.parse(val));
|
||||
}
|
||||
else if (DropdownFields.includes(this.preset.field)) {
|
||||
if (this.MultipleDropdownAllowed || val.includes(',')) {
|
||||
if (this.isMultiSelectDropdownAllowed() || val.includes(',')) {
|
||||
this.formGroup.get('filterValue')?.patchValue(val.split(',').map(d => parseInt(d, 10)));
|
||||
} else {
|
||||
if (this.preset.field === FilterField.Languages) {
|
||||
|
|
@ -383,4 +415,7 @@ export class MetadataFilterRowComponent implements OnInit {
|
|||
updateIfDateFilled() {
|
||||
this.propagateFilterUpdate();
|
||||
}
|
||||
|
||||
protected readonly FilterComparison = FilterComparison;
|
||||
protected readonly PredicateType = PredicateType;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,11 @@ import {PersonSortField} from "../_models/metadata/v2/person-sort-field";
|
|||
import {PersonFilterField} from "../_models/metadata/v2/person-filter-field";
|
||||
import {FilterField} from "../_models/metadata/v2/filter-field";
|
||||
|
||||
/**
|
||||
* The set of entities that are supported for rich filtering. Each entity must have its own distinct SortField and FilterField enums.
|
||||
*/
|
||||
export type ValidFilterEntity = 'series' | 'person';
|
||||
|
||||
export class FilterSettingsBase<TFilter extends number = number, TSort extends number = number> {
|
||||
presetsV2: FilterV2<TFilter, TSort> | undefined;
|
||||
sortDisabled = false;
|
||||
|
|
@ -12,20 +17,21 @@ export class FilterSettingsBase<TFilter extends number = number, TSort extends n
|
|||
*/
|
||||
statementLimit: number = 0;
|
||||
saveDisabled: boolean = false;
|
||||
type: ValidFilterEntity = 'series';
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter Settings for Series entity
|
||||
*/
|
||||
export class SeriesFilterSettings extends FilterSettingsBase<FilterField, SortField> {
|
||||
type = 'sortField';
|
||||
type: ValidFilterEntity = 'series';
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter Settings for People entity
|
||||
*/
|
||||
export class PersonFilterSettings extends FilterSettingsBase<PersonFilterField, PersonSortField> {
|
||||
type = 'personSortField';
|
||||
type: ValidFilterEntity = 'person';
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -26,8 +26,8 @@
|
|||
<div class="filter-section mx-auto pb-3">
|
||||
<div class="row justify-content-center g-0">
|
||||
<app-metadata-builder [filter]="filterV2"
|
||||
[availableFilterFields]="allFilterFields"
|
||||
[statementLimit]="filterSettings.statementLimit"
|
||||
[entityType]="filterSettings().type"
|
||||
[statementLimit]="filterSettings().statementLimit"
|
||||
(update)="handleFilters($event)">
|
||||
</app-metadata-builder>
|
||||
</div>
|
||||
|
|
@ -41,7 +41,7 @@
|
|||
</div>
|
||||
<div class="col-md-3 col-sm-9">
|
||||
<label for="sort-options" class="form-label">{{t('sort-by-label')}}</label>
|
||||
<app-sort-button [disabled]="filterSettings.sortDisabled" (update)="updateSortOrder($event)" [isAscending]="isAscendingSort" />
|
||||
<app-sort-button [disabled]="filterSettings().sortDisabled" (update)="updateSortOrder($event)" [isAscending]="isAscendingSort" />
|
||||
<select id="sort-options" class="form-select" formControlName="sortField" style="height: 38px;">
|
||||
@for(field of allSortFields; track field.value) {
|
||||
<option [value]="field.value">{{field.title}}</option>
|
||||
|
|
@ -76,7 +76,7 @@
|
|||
<button class="btn btn-primary col-6" (click)="apply()"><i class="fa-solid fa-play me-1" aria-hidden="true"></i>{{t('apply')}}</button>
|
||||
</div>
|
||||
<div class="col-md-2 col-sm-6 mt-4 pt-2">
|
||||
<button class="btn btn-primary col-12" (click)="save()" [disabled]="filterSettings.saveDisabled || !this.sortGroup.get('name')?.value">
|
||||
<button class="btn btn-primary col-12" (click)="save()" [disabled]="filterSettings().saveDisabled || !this.sortGroup.get('name')?.value">
|
||||
<i class="fa-solid fa-floppy-disk" aria-hidden="true"></i>
|
||||
{{t('save')}}
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import {
|
|||
computed,
|
||||
ContentChild,
|
||||
DestroyRef,
|
||||
effect,
|
||||
EventEmitter,
|
||||
inject,
|
||||
input,
|
||||
|
|
@ -25,15 +26,14 @@ import {AsyncPipe, NgClass, NgTemplateOutlet} from '@angular/common';
|
|||
import {translate, TranslocoModule, TranslocoService} from "@jsverse/transloco";
|
||||
import {SortFieldPipe} from "../_pipes/sort-field.pipe";
|
||||
import {MetadataBuilderComponent} from "./_components/metadata-builder/metadata-builder.component";
|
||||
import {allSeriesFilterFields, FilterField} from "../_models/metadata/v2/filter-field";
|
||||
import {FilterField} from "../_models/metadata/v2/filter-field";
|
||||
import {FilterService} from "../_services/filter.service";
|
||||
import {ToastrService} from "ngx-toastr";
|
||||
import {animate, style, transition, trigger} from "@angular/animations";
|
||||
import {SortButtonComponent} from "../_single-module/sort-button/sort-button.component";
|
||||
import {FilterUtilitiesService} from "../shared/_services/filter-utilities.service";
|
||||
import {FilterSettingsBase} from "./filter-settings";
|
||||
import {allPersonSortFields} from "../_models/metadata/v2/person-sort-field";
|
||||
import {allPersonFilterFields} from "../_models/metadata/v2/person-filter-field";
|
||||
import {FilterUtilitiesService} from "../shared/_services/filter-utilities.service";
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-metadata-filter',
|
||||
|
|
@ -57,16 +57,16 @@ import {allPersonFilterFields} from "../_models/metadata/v2/person-filter-field"
|
|||
MetadataBuilderComponent, NgClass, SortButtonComponent]
|
||||
})
|
||||
export class MetadataFilterComponent<TFilter extends number = number, TSort extends number = number> implements OnInit {
|
||||
protected readonly allFilterFields = allSeriesFilterFields;
|
||||
|
||||
|
||||
private readonly destroyRef = inject(DestroyRef);
|
||||
private readonly filterUtilityService = inject(FilterUtilitiesService);
|
||||
public readonly utilityService = inject(UtilityService);
|
||||
private readonly cdRef = inject(ChangeDetectorRef);
|
||||
private readonly toastr = inject(ToastrService);
|
||||
private readonly filterService = inject(FilterService);
|
||||
protected readonly toggleService = inject(ToggleService);
|
||||
protected readonly translocoService = inject(TranslocoService);
|
||||
protected readonly filterUtilitiesService = inject(FilterUtilitiesService);
|
||||
private readonly sortFieldPipe = new SortFieldPipe(this.translocoService);
|
||||
|
||||
protected readonly allSortFields = allSeriesSortFields.map(f => {
|
||||
|
|
@ -78,26 +78,8 @@ export class MetadataFilterComponent<TFilter extends number = number, TSort exte
|
|||
* This toggles the opening/collapsing of the metadata filter code
|
||||
*/
|
||||
@Input() filterOpen: EventEmitter<boolean> = new EventEmitter();
|
||||
/**
|
||||
* Should filtering be shown on the page
|
||||
*/
|
||||
@Input() filteringDisabled: boolean = false;
|
||||
//@Input({required: true}) filterSettings!: FilterSettings<T>;
|
||||
|
||||
@Input({required: true}) filterSettings!: FilterSettingsBase<TFilter, TSort>;
|
||||
/**
|
||||
* Entity type derives the Sort and Filter fields
|
||||
*/
|
||||
entityType = input<'series' | 'person'>('series');
|
||||
|
||||
sortFieldOptions = computed(() => {
|
||||
if (this.entityType() === 'series') return allSeriesSortFields;
|
||||
return allPersonSortFields;
|
||||
});
|
||||
filterFieldOptions = computed(() => {
|
||||
if (this.entityType() === 'series') return allSeriesFilterFields;
|
||||
return allPersonFilterFields;
|
||||
});
|
||||
filterSettings = input.required<FilterSettingsBase<TFilter, TSort>>();
|
||||
|
||||
@Output() applyFilter: EventEmitter<FilterEvent> = new EventEmitter();
|
||||
@ContentChild('[ngbCollapse]') collapse!: NgbCollapse;
|
||||
|
|
@ -116,16 +98,22 @@ export class MetadataFilterComponent<TFilter extends number = number, TSort exte
|
|||
|
||||
fullyLoaded: boolean = false;
|
||||
filterV2: FilterV2<FilterField> | undefined;
|
||||
sortFieldOptions = computed(() => {
|
||||
return this.filterUtilitiesService.getSortFields(this.filterSettings().type);
|
||||
});
|
||||
filterFieldOptions = computed(() => {
|
||||
return this.filterUtilitiesService.getFilterFields(this.filterSettings().type);
|
||||
});
|
||||
|
||||
|
||||
constructor() {
|
||||
effect(() => {
|
||||
console.log('setting type: ', this.filterSettings().type);
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
ngOnInit(): void {
|
||||
if (this.filterSettings === undefined) {
|
||||
this.filterSettings = new FilterSettingsBase<TFilter, TSort>();
|
||||
this.cdRef.markForCheck();
|
||||
}
|
||||
|
||||
if (this.filterOpen) {
|
||||
this.filterOpen.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(openState => {
|
||||
this.filteringCollapsed = !openState;
|
||||
|
|
@ -179,17 +167,17 @@ export class MetadataFilterComponent<TFilter extends number = number, TSort exte
|
|||
loadFromPresetsAndSetup() {
|
||||
this.fullyLoaded = false;
|
||||
|
||||
this.filterV2 = this.deepClone(this.filterSettings.presetsV2);
|
||||
this.filterV2 = this.deepClone(this.filterSettings().presetsV2);
|
||||
|
||||
const defaultSortField = this.sortFieldOptions()[0];
|
||||
|
||||
this.sortGroup = new FormGroup({
|
||||
sortField: new FormControl({value: this.filterV2?.sortOptions?.sortField || defaultSortField, disabled: this.filterSettings.sortDisabled}, []),
|
||||
sortField: new FormControl({value: this.filterV2?.sortOptions?.sortField || defaultSortField, disabled: this.filterSettings().sortDisabled}, []),
|
||||
limitTo: new FormControl(this.filterV2?.limitTo || 0, []),
|
||||
name: new FormControl(this.filterV2?.name || '', [])
|
||||
});
|
||||
if (this.filterSettings?.presetsV2?.sortOptions) {
|
||||
this.isAscendingSort = this.filterSettings?.presetsV2?.sortOptions!.isAscending;
|
||||
if (this.filterSettings()?.presetsV2?.sortOptions) {
|
||||
this.isAscendingSort = this.filterSettings()?.presetsV2?.sortOptions!.isAscending || true;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -212,7 +200,7 @@ export class MetadataFilterComponent<TFilter extends number = number, TSort exte
|
|||
|
||||
|
||||
updateSortOrder(isAscending: boolean) {
|
||||
if (this.filterSettings.sortDisabled) return;
|
||||
if (this.filterSettings().sortDisabled) return;
|
||||
this.isAscendingSort = isAscending;
|
||||
|
||||
if (this.filterV2?.sortOptions === null) {
|
||||
|
|
@ -258,10 +246,6 @@ export class MetadataFilterComponent<TFilter extends number = number, TSort exte
|
|||
this.cdRef.markForCheck();
|
||||
}
|
||||
|
||||
setToggle(event: any) {
|
||||
this.toggleService.set(!this.filteringCollapsed);
|
||||
}
|
||||
|
||||
|
||||
protected readonly Breakpoint = Breakpoint;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,20 @@
|
|||
import {inject, Injectable} from '@angular/core';
|
||||
import {ActivatedRouteSnapshot, Params, Router} from '@angular/router';
|
||||
import {SortField} from 'src/app/_models/metadata/series-filter';
|
||||
import {allSeriesSortFields, SortField} from 'src/app/_models/metadata/series-filter';
|
||||
import {MetadataService} from "../../_services/metadata.service";
|
||||
import {FilterV2} from "../../_models/metadata/v2/filter-v2";
|
||||
import {FilterStatement} from "../../_models/metadata/v2/filter-statement";
|
||||
import {FilterCombination} from "../../_models/metadata/v2/filter-combination";
|
||||
import {FilterField} from "../../_models/metadata/v2/filter-field";
|
||||
import {allSeriesFilterFields, FilterField} from "../../_models/metadata/v2/filter-field";
|
||||
import {FilterComparison} from "../../_models/metadata/v2/filter-comparison";
|
||||
import {HttpClient} from "@angular/common/http";
|
||||
import {TextResonse} from "../../_types/text-response";
|
||||
import {environment} from "../../../environments/environment";
|
||||
import {map, tap} from "rxjs/operators";
|
||||
import {Observable, of, switchMap} from "rxjs";
|
||||
import {PersonFilterField} from "../../_models/metadata/v2/person-filter-field";
|
||||
import {allPersonFilterFields, PersonFilterField} from "../../_models/metadata/v2/person-filter-field";
|
||||
import {allPersonSortFields} from "../../_models/metadata/v2/person-sort-field";
|
||||
import {ValidFilterEntity} from "../../metadata-filter/filter-settings";
|
||||
|
||||
|
||||
@Injectable({
|
||||
|
|
@ -119,4 +121,35 @@ export class FilterUtilitiesService {
|
|||
field: PersonFilterField.Name
|
||||
}
|
||||
}
|
||||
|
||||
getSortFields<T extends number>(type: ValidFilterEntity) {
|
||||
switch (type) {
|
||||
case 'series':
|
||||
return allSeriesSortFields as unknown as T[];
|
||||
case 'person':
|
||||
return allPersonSortFields as unknown as T[];
|
||||
default:
|
||||
return [] as T[];
|
||||
}
|
||||
}
|
||||
|
||||
getFilterFields<T extends number>(type: ValidFilterEntity) {
|
||||
switch (type) {
|
||||
case 'series':
|
||||
return allSeriesFilterFields as unknown as T[];
|
||||
case 'person':
|
||||
return allPersonFilterFields as unknown as T[];
|
||||
default:
|
||||
return [] as T[];
|
||||
}
|
||||
}
|
||||
|
||||
getDefaultFilterField<T extends number>(type: ValidFilterEntity) {
|
||||
switch (type) {
|
||||
case 'series':
|
||||
return FilterField.SeriesName as unknown as T;
|
||||
case 'person':
|
||||
return PersonFilterField.Role as unknown as T;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue