Skip to content

Commit ea24c06

Browse files
committed
feat: reworked list-header
1 parent 35022a6 commit ea24c06

File tree

3 files changed

+112
-40
lines changed

3 files changed

+112
-40
lines changed

packages/ui/src/lib/list/list-header/list-header.component.html

Lines changed: 54 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,46 @@
1+
<ec-pop (toggle)="toggledFilterPop($event)" [hideOnEscape]="false" #filterPop>
2+
<div *ngIf="filteredField" class="btn-group">
3+
<!-- <div tabindex="0" class="dropdown dropdown_left">
4+
<span class="btn btn_clear">
5+
{{fieldLabel(filteredField)}}
6+
</span>
7+
<ul class="dropdown-options">
8+
<li [class.is-hidden]="field.property===filteredField?.property" class="dropdown-option"
9+
*ngFor="let field of list?.filterableFields()">
10+
<a (click)="filterField(field.property)">{{fieldLabel(field)}}</a>
11+
</li>
12+
</ul>
13+
</div> -->
14+
<ec-input (ready)="inputReady($event)" (changed)="setFilter(filteredField, $event)" [lazy]="true"
15+
class="has-width-full" [field]="filteredField" [group]="filterForm?.group" [debounce]="200"
16+
*ngIf="filterPop.active&&filterForm?.group" #filterInput></ec-input>
17+
18+
<a class="btn btn_clear" (click)="resetFilter()" *ngIf="list?.isFiltered(filteredField.property)">
19+
<!-- <a class="btn btn_clear" (click)="filterPop.hide()"> -->
20+
<ec-icon name="close-x"></ec-icon>
21+
</a>
22+
23+
<div tabindex="0" class="dropdown dropdown_right">
24+
<span class="btn btn_clear">
25+
<ec-icon name="filter-2"></ec-icon>
26+
</span>
27+
<ul class="dropdown-options">
28+
<li class="dropdown-option" *ngFor="let field of list?.sortableFields()"
29+
[class.is-active]="list?.isSorted(field.property, true)">
30+
<a (click)="list.toggleSort(field.property)" class="sorting-option"
31+
[class.is-sorted-asc]="list?.isSorted(field.property, false)"
32+
[class.is-sorted-desc]="list?.isSorted(field.property, true)">
33+
<span class="sorting-option-indicator"></span>
34+
{{fieldLabel(field)}}
35+
</a>
36+
</li>
37+
</ul>
38+
</div>
39+
</div>
40+
</ec-pop>
141
<div *ngIf="filterFormConfig">
242
<ec-form [config]="filterFormConfig" [value]="list?.config?.filter" [empty]="true" #filterForm
343
(ready)="initFilterForm($event)" [lazy]="true"></ec-form>
4-
<ec-pop (toggle)="toggledFilterPop($event)" #filterPop>
5-
<div *ngIf="filteredField" class="btn-group">
6-
<nav tabindex="0" class="dropdown dropdown-left">
7-
<span class="btn btn_clear">
8-
<ec-icon name="find">{{fieldLabel(filteredField)}}</ec-icon>
9-
</span>
10-
<ul class="dropdown-options">
11-
<li [class.is-active]="filteredField.property===field.property" class="dropdown-option"
12-
*ngFor="let field of list?.filterableFields()">
13-
<a (click)="editFilter(filterPop, field.property)">{{fieldLabel(field)}}</a>
14-
</li>
15-
</ul>
16-
</nav>
17-
<ec-input (changed)="setFilter(filteredField, $event)" [lazy]="true" class="has-width-full"
18-
[field]="filteredField" [group]="filterForm?.group" [debounce]="200" *ngIf="filterPop.active&&filterForm?.group"
19-
#filterInput></ec-input>
20-
<a class="btn btn_clear" (click)="filterPop.hide()">
21-
<ec-icon name="close-x-big"></ec-icon>
22-
</a>
23-
</div>
24-
</ec-pop>
25-
2644
<div class="ec-list-header">
2745
<div class="ec-list-cell ec-list-item__selector" *ngIf="list?.config?.selectMode&&!list?.isEmpty()">
2846
<input type="checkbox" (click)="selection?.toggleAll(list?.page)" [checked]="selection?.hasAll(list?.page)"
@@ -35,26 +53,34 @@
3553
[class.is-sorted-desc]="list.isSorted(field.property, true)"
3654
[class.is-sorted-asc]="list.isSorted(field.property, false)">
3755
<!-- [class.is-filter-active]="filterPop.active"-->
38-
<a (click)="editFilter(filterPop, field.property)" class="is-filterable-control" *ngIf="field.filterable">
56+
<a (click)="filterField(field.property)" class="is-filterable-control" *ngIf="field.filterable">
3957
<ec-icon name="find"></ec-icon>
4058
</a>
41-
59+
<!-- <a (click)="toggleVisibility(field)" class="is-visibility-control">
60+
<ec-icon name="eye-closed"></ec-icon>
61+
</a> -->
4262
<span class="ec-list-column-title" (click)="field.sortable&&list.toggleSort(field.property)">
4363
{{fieldLabel(field)}}
4464
</span>
45-
<span class="is-sortable-indicator" *ngIf="field.sortable"></span>
65+
<span (click)="list.toggleSort(field.property)" class="is-sortable-indicator" *ngIf="field.sortable"></span>
4666
</div>
4767

48-
4968
<div class="dropdown dropdown_right ec-list-column-filter" tabindex="0" *ngIf="!list?.config?.disableColumnFilter">
5069
<span>&#9776;</span>
5170
<div class="dropdown-options ec-list-column-filter-options">
5271
<ul>
5372
<li class="ec-list-column-filter-option" *ngFor="let field of list?.fields"
5473
[class.is-active]="!field.hideInList" [class.is-hidden]="field.hideInColumnFilter">
55-
<div class="form-boolean" (click)="toggleVisibility(field)">
74+
<span (click)="toggleVisibility(field)">
5675
<ec-icon [name]="field.hideInList?'square':'check-box'">{{fieldLabel(field)}}</ec-icon>
57-
</div>
76+
</span>
77+
<!-- <span (click)="list.toggleSort(field.property)" *ngIf="field.sortable">
78+
<ec-icon name="filter-2"></ec-icon>
79+
</span> -->
80+
<!-- <span (click)="filterField(field.property)" *ngIf="field.filterable"
81+
[class.is-active]="list.isFiltered(field.property)">
82+
<ec-icon name="find"></ec-icon>
83+
</span> -->
5884
</li>
5985
</ul>
6086
</div>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# List Header
2+
3+
This document conceptualizes the ec-list-header.
4+
5+
## Idea
6+
7+
The list-header displays the field labels in columns. If a fields are sortable or filterable, a clickeable icons will be shown.
8+
9+
### Filtering
10+
11+
If a field has the option "filterable" set to true, a search icon will be shown. When clicking the icon, an input field will appear above the list header which can be used to filter the list by the field property.
12+
13+
- The input field will be autofocused
14+
- No validation errors should be shown
15+
- When the input value changes, the list will load after a short debounce.
16+
- When the input is closed, the filter will be cleared.
17+
- When another field's search icon is clicked, all previous filters will be cleared.

packages/ui/src/lib/list/list-header/list-header.component.ts

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
import { Component, Input, QueryList, ViewChild, ViewChildren, ChangeDetectionStrategy, OnInit, OnChanges } from '@angular/core';
2-
import { PopComponent } from '../../pop/pop.component';
1+
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnChanges, ViewChild } from '@angular/core';
2+
import { Field, List, ListConfig, Selection } from '@ec.components/core';
33
import { FormComponent } from '../../form/form.component';
4-
import { List, ListConfig } from '@ec.components/core';
5-
import { Selection } from '@ec.components/core';
6-
import { Field } from '@ec.components/core';
4+
import { PopComponent } from '../../pop/pop.component';
75
import { ListConfigService } from '../list-config.service';
6+
import { InputComponent } from '../../io/input/input.component';
87

98
/** This component renders, as the name states, the header of a list.*/
109
@Component({
@@ -18,24 +17,31 @@ export class ListHeaderComponent implements OnChanges {
1817
/** The selection instance. This is optional. If It is not provided, no checkbox will be visible.*/
1918
@Input() selection: Selection<any>;
2019
/** The pop dropdowns that contain the filtering */
21-
@ViewChildren('filterPop') pops: QueryList<PopComponent>;
20+
@ViewChild('filterPop') filterPop: PopComponent;
2221
/** The config for the filter form */
2322
filterFormConfig: ListConfig<any>;
2423
filteredField: any;
2524
filterForm: FormComponent<any>;
25+
filterInput: InputComponent;
2626

27-
constructor(public listConfig: ListConfigService) {
27+
constructor(public listConfig: ListConfigService, public cdr: ChangeDetectorRef) {
2828
}
2929

3030
setFilter(field, value) {
3131
this.list.setFilter({ [field.property]: value });
3232
}
3333

34+
inputReady(input) {
35+
this.filterInput = input;
36+
input.focus(true);
37+
}
38+
3439
initFilterForm(filterForm) {
3540
// is called when form is ready
3641
this.filterForm = filterForm;
3742
if (this.list.config.defaultFilter) {
3843
this.filterField(this.list.config.defaultFilter);
44+
}
3945
}
4046

4147
ngOnChanges(changes?) {
@@ -45,6 +51,11 @@ export class ListHeaderComponent implements OnChanges {
4551
if (!this.list || !this.list.config || !this.list.config.fields) {
4652
return;
4753
}
54+
/* this.list.change$.subscribe(() => {
55+
if (this.filterInput) {
56+
// this.filterInput.focus(true);
57+
}
58+
}); */
4859
this.filterFormConfig = {
4960
...this.list.config,
5061
fields: this.list.filterableFields().reduce((fields, field) => {
@@ -66,34 +77,52 @@ export class ListHeaderComponent implements OnChanges {
6677
}
6778

6879
/** opens the given filter pop and closes all others */
69-
public editFilter(pop, property) {
80+
public filterField(property) {
7081
if (this.filteredField) {
7182
if (this.filteredField.property === property) {
72-
pop.hide();
83+
/* this.filterPop.hide(); */
84+
if (this.filterInput) {
85+
this.filterInput.focus(true);
86+
}
7387
return;
7488
}
7589
this.clearFilter();
7690
}
7791
// patch current filter value to control
92+
const control = this.filterForm.group.get(property);
93+
if (!control) {
94+
console.warn('no control found for ' + property + '. Is it filterable?', this.list.config);
95+
return;
96+
}
7897
this.filterForm.group.get(property).patchValue(this.list.getFilterValue(property));
7998
this.filteredField = this.filterForm.form.getField(property);
80-
pop.show();
99+
setTimeout(() => this.filterPop.show());
100+
}
101+
102+
resetFilter() {
103+
if (!this.filteredField || !this.list || !this.list.isFiltered(this.filteredField.property)) {
104+
return;
105+
}
106+
this.filterForm.group.get(this.filteredField.property).reset();
107+
this.list.clearFilter();
81108
}
82109

83110
clearFilter() {
84111
if (!this.filteredField || !this.list.isFiltered(this.filteredField.property)) {
85112
delete this.filteredField;
86113
return;
87114
}
88-
this.filterForm.group.get(this.filteredField.property).reset();
89-
this.list.clearFilter();
115+
this.resetFilter();
90116
delete this.filteredField;
91117
}
92118

93119
toggledFilterPop(active) {
94120
if (!active) {
95121
this.clearFilter();
96122
}
123+
if (this.filterInput) {
124+
this.filterInput.focus(true);
125+
}
97126
}
98127

99128
/** Returns the fields label */

0 commit comments

Comments
 (0)