add filterception
This commit is contained in:
parent
c052a73f50
commit
46b41c15d0
@ -7,14 +7,15 @@ import { IqserButtonsModule } from '../buttons';
|
||||
import { PopupFilterComponent } from './popup-filter/popup-filter.component';
|
||||
import { QuickFiltersComponent } from './quick-filters/quick-filters.component';
|
||||
import { IqserIconsModule } from '../icons';
|
||||
import { IqserInputsModule } from '../inputs';
|
||||
|
||||
const matModules = [MatCheckboxModule, MatMenuModule];
|
||||
const modules = [TranslateModule, IqserButtonsModule];
|
||||
const modules = [TranslateModule, IqserButtonsModule, IqserIconsModule, IqserInputsModule];
|
||||
const components = [QuickFiltersComponent, PopupFilterComponent];
|
||||
|
||||
@NgModule({
|
||||
declarations: [...components],
|
||||
exports: [...components],
|
||||
imports: [CommonModule, IqserIconsModule, ...matModules, ...modules]
|
||||
imports: [CommonModule, ...matModules, ...modules]
|
||||
})
|
||||
export class IqserFiltersModule {}
|
||||
|
||||
@ -7,6 +7,8 @@ export interface FilterGroup {
|
||||
readonly label?: string;
|
||||
readonly icon?: string;
|
||||
readonly filterTemplate?: TemplateRef<unknown>;
|
||||
/** Enables a search bar above each filter popup that will filter filters */
|
||||
readonly filterceptionPlaceholder?: string;
|
||||
readonly hide?: boolean;
|
||||
readonly checker?: (...args: unknown[]) => boolean;
|
||||
readonly matchAll?: boolean;
|
||||
|
||||
@ -23,27 +23,22 @@
|
||||
xPosition="before"
|
||||
>
|
||||
<ng-template matMenuContent>
|
||||
<div class="filter-menu-header">
|
||||
<div class="all-caps-label" translate="filter-menu.filter-types"></div>
|
||||
<div class="actions">
|
||||
<div
|
||||
(click)="activatePrimaryFilters(); $event.stopPropagation()"
|
||||
class="all-caps-label primary pointer"
|
||||
translate="actions.all"
|
||||
></div>
|
||||
<div
|
||||
(click)="deactivateFilters(); $event.stopPropagation()"
|
||||
class="all-caps-label primary pointer"
|
||||
translate="actions.none"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
<iqser-input-with-action
|
||||
(click)="$event.stopPropagation()"
|
||||
[(value)]="searchService.searchValue"
|
||||
*ngIf="primaryGroup.filterceptionPlaceholder"
|
||||
[placeholder]="primaryGroup.filterceptionPlaceholder"
|
||||
[width]="'full'"
|
||||
></iqser-input-with-action>
|
||||
|
||||
<ng-container *ngTemplateOutlet="filterHeader"></ng-container>
|
||||
|
||||
<div class="filter-content">
|
||||
<ng-container
|
||||
*ngFor="let filter of primaryGroup.filters"
|
||||
*ngFor="let filter of primaryFilters$ | async"
|
||||
[ngTemplateOutletContext]="{
|
||||
filter: filter,
|
||||
filterTemplate: primaryGroup.filterTemplate,
|
||||
atLeastOneIsExpandable: atLeastOneFilterIsExpandable$ | async
|
||||
}"
|
||||
[ngTemplateOutlet]="defaultFilterTemplate"
|
||||
@ -55,55 +50,80 @@
|
||||
<div class="all-caps-label" translate="filter-menu.filter-options"></div>
|
||||
</div>
|
||||
|
||||
<div *ngFor="let filter of secondaryGroup.filters">
|
||||
<ng-container
|
||||
[ngTemplateOutletContext]="{
|
||||
filter: filter,
|
||||
atLeastOneIsExpandable: atLeastOneSecondaryFilterIsExpandable$ | async
|
||||
}"
|
||||
[ngTemplateOutlet]="defaultFilterTemplate"
|
||||
></ng-container>
|
||||
</div>
|
||||
<ng-container
|
||||
*ngFor="let filter of secondaryGroup.filters"
|
||||
[ngTemplateOutletContext]="{
|
||||
filter: filter,
|
||||
atLeastOneIsExpandable: atLeastOneSecondaryFilterIsExpandable$ | async
|
||||
}"
|
||||
[ngTemplateOutlet]="defaultFilterTemplate"
|
||||
></ng-container>
|
||||
</div>
|
||||
</ng-template>
|
||||
</mat-menu>
|
||||
</ng-container>
|
||||
|
||||
<ng-template #defaultFilterLabelTemplate let-filter="filter">
|
||||
{{ filter?.label }}
|
||||
</ng-template>
|
||||
<ng-template #defaultFilterLabelTemplate let-filter="filter">
|
||||
{{ filter?.label }}
|
||||
</ng-template>
|
||||
|
||||
<ng-template #defaultFilterTemplate let-atLeastOneIsExpandable="atLeastOneIsExpandable" let-filter="filter">
|
||||
<div (click)="toggleFilterExpanded($event, filter)" class="mat-menu-item flex">
|
||||
<div *ngIf="filter.children?.length > 0" class="arrow-wrapper">
|
||||
<mat-icon *ngIf="filter.expanded" color="accent" svgIcon="iqser:arrow-down"></mat-icon>
|
||||
<mat-icon *ngIf="!filter.expanded" color="accent" svgIcon="red:arrow-right"></mat-icon>
|
||||
</div>
|
||||
<div *ngIf="atLeastOneIsExpandable && filter.children?.length === 0" class="arrow-wrapper spacer"> </div>
|
||||
<mat-checkbox
|
||||
(click)="filterCheckboxClicked($event, filter)"
|
||||
[checked]="filter.checked"
|
||||
[indeterminate]="filter.indeterminate"
|
||||
class="filter-menu-checkbox"
|
||||
>
|
||||
<ng-template #filterHeader>
|
||||
<div class="filter-menu-header">
|
||||
<div class="all-caps-label" translate="filter-menu.filter-types"></div>
|
||||
<div class="actions">
|
||||
<div
|
||||
(click)="activatePrimaryFilters(); $event.stopPropagation()"
|
||||
class="all-caps-label primary pointer"
|
||||
translate="actions.all"
|
||||
></div>
|
||||
<div
|
||||
(click)="deactivateFilters(); $event.stopPropagation()"
|
||||
class="all-caps-label primary pointer"
|
||||
translate="actions.none"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<ng-template
|
||||
#defaultFilterTemplate
|
||||
let-atLeastOneIsExpandable="atLeastOneIsExpandable"
|
||||
let-filter="filter"
|
||||
let-filterTemplate="filterTemplate"
|
||||
>
|
||||
<div (click)="toggleFilterExpanded($event, filter)" class="mat-menu-item flex">
|
||||
<div *ngIf="filter.children?.length > 0" class="arrow-wrapper">
|
||||
<mat-icon *ngIf="filter.expanded" color="accent" svgIcon="iqser:arrow-down"></mat-icon>
|
||||
<mat-icon *ngIf="!filter.expanded" color="accent" svgIcon="red:arrow-right"></mat-icon>
|
||||
</div>
|
||||
|
||||
<div *ngIf="atLeastOneIsExpandable && filter.children?.length === 0" class="arrow-wrapper spacer"> </div>
|
||||
|
||||
<mat-checkbox
|
||||
(click)="filterCheckboxClicked($event, filter)"
|
||||
[checked]="filter.checked"
|
||||
[indeterminate]="filter.indeterminate"
|
||||
class="filter-menu-checkbox"
|
||||
>
|
||||
<ng-container
|
||||
[ngTemplateOutletContext]="{ filter: filter }"
|
||||
[ngTemplateOutlet]="filterTemplate ?? defaultFilterLabelTemplate"
|
||||
></ng-container>
|
||||
</mat-checkbox>
|
||||
|
||||
<ng-container [ngTemplateOutletContext]="{ filter: filter }" [ngTemplateOutlet]="actionsTemplate"></ng-container>
|
||||
</div>
|
||||
|
||||
<div *ngIf="filter.children?.length && filter.expanded">
|
||||
<div (click)="$event.stopPropagation()" *ngFor="let child of filter.children" class="padding-left mat-menu-item">
|
||||
<mat-checkbox (click)="filterCheckboxClicked($event, child, filter)" [checked]="child.checked">
|
||||
<ng-container
|
||||
[ngTemplateOutletContext]="{ filter: filter }"
|
||||
[ngTemplateOutlet]="primaryGroup.filterTemplate ?? defaultFilterLabelTemplate"
|
||||
[ngTemplateOutletContext]="{ filter: child }"
|
||||
[ngTemplateOutlet]="filterTemplate ?? defaultFilterLabelTemplate"
|
||||
></ng-container>
|
||||
</mat-checkbox>
|
||||
<ng-container [ngTemplateOutletContext]="{ filter: filter }" [ngTemplateOutlet]="actionsTemplate"></ng-container>
|
||||
</div>
|
||||
|
||||
<div *ngIf="filter.children?.length && filter.expanded">
|
||||
<div (click)="$event.stopPropagation()" *ngFor="let child of filter.children" class="padding-left mat-menu-item">
|
||||
<mat-checkbox (click)="filterCheckboxClicked($event, child, filter)" [checked]="child.checked">
|
||||
<ng-container
|
||||
[ngTemplateOutletContext]="{ filter: child }"
|
||||
[ngTemplateOutlet]="primaryGroup.filterTemplate ?? defaultFilterLabelTemplate"
|
||||
></ng-container>
|
||||
</mat-checkbox>
|
||||
|
||||
<ng-container [ngTemplateOutletContext]="{ filter: child }" [ngTemplateOutlet]="actionsTemplate"></ng-container>
|
||||
</div>
|
||||
<ng-container [ngTemplateOutletContext]="{ filter: child }" [ngTemplateOutlet]="actionsTemplate"></ng-container>
|
||||
</div>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 8px 16px 16px 16px;
|
||||
width: 350px;
|
||||
min-width: 350px;
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
@ -39,3 +39,7 @@
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
iqser-input-with-action {
|
||||
padding: 0 8px 8px 8px;
|
||||
}
|
||||
|
||||
@ -7,6 +7,8 @@ import { handleCheckedValue } from '../filter-utils';
|
||||
import { FilterService } from '../filter.service';
|
||||
import { FilterGroup } from '../models/filter-group.model';
|
||||
import { NestedFilter } from '../models/nested-filter.model';
|
||||
import { SearchService } from '../../search';
|
||||
import { Filter } from '..';
|
||||
|
||||
const areExpandable = (nestedFilter: NestedFilter) => !!nestedFilter?.children?.length;
|
||||
const atLeastOneIsExpandable = pipe(
|
||||
@ -27,7 +29,8 @@ const atLeastOneIsExpandable = pipe(
|
||||
clickAction: 'noop',
|
||||
color: 'primary'
|
||||
}
|
||||
}
|
||||
},
|
||||
SearchService
|
||||
]
|
||||
})
|
||||
export class PopupFilterComponent implements OnInit {
|
||||
@ -43,8 +46,11 @@ export class PopupFilterComponent implements OnInit {
|
||||
|
||||
primaryFilterGroup$!: Observable<FilterGroup | undefined>;
|
||||
secondaryFilterGroup$!: Observable<FilterGroup | undefined>;
|
||||
primaryFilters$!: Observable<Filter[] | undefined>;
|
||||
|
||||
constructor(readonly filterService: FilterService) {}
|
||||
constructor(readonly filterService: FilterService, readonly searchService: SearchService<Filter>) {
|
||||
this.searchService.setSearchKey('label');
|
||||
}
|
||||
|
||||
private get _hasActiveFilters$() {
|
||||
return combineLatest([this.primaryFilterGroup$, this.secondaryFilterGroup$]).pipe(
|
||||
@ -55,8 +61,9 @@ export class PopupFilterComponent implements OnInit {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.primaryFilterGroup$ = this.filterService.getGroup$(this.primaryFiltersSlug);
|
||||
this.secondaryFilterGroup$ = this.filterService.getGroup$(this.secondaryFiltersSlug);
|
||||
this.primaryFilterGroup$ = this.filterService.getGroup$(this.primaryFiltersSlug).pipe(shareReplay(2));
|
||||
this.primaryFilters$ = this._primaryFilters$;
|
||||
this.secondaryFilterGroup$ = this.filterService.getGroup$(this.secondaryFiltersSlug).pipe(shareReplay(3));
|
||||
|
||||
this.hasActiveFilters$ = this._hasActiveFilters$;
|
||||
this.atLeastOneFilterIsExpandable$ = atLeastOneIsExpandable(this.primaryFilterGroup$);
|
||||
@ -115,4 +122,10 @@ export class PopupFilterComponent implements OnInit {
|
||||
});
|
||||
this.filterService.refresh();
|
||||
}
|
||||
|
||||
private get _primaryFilters$(): Observable<Filter[]> {
|
||||
return combineLatest([this.primaryFilterGroup$, this.searchService.valueChanges$]).pipe(
|
||||
map(([group]) => this.searchService.searchIn(group?.filters ?? []))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user