Skip to content

Commit b7d0946

Browse files
authored
Merge pull request #5 from paodb/enhanced-grid-flow-filter-updates
Column resizing and filtering issues fixes
2 parents 5c81ff6 + 3d56f33 commit b7d0946

File tree

8 files changed

+314
-68
lines changed

8 files changed

+314
-68
lines changed

enhanced-grid-flow-demo/src/main/java/com/vaadin/componentfactory/enhancedgrid/SimpleSingleSelectView.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,14 @@ public SimpleSingleSelectView() {
4343

4444
// add columns
4545
// first name column with filtering button on header
46-
EnhancedColumn<Person> firstNameColumn = grid.addColumn(Person::getFirstName).setHeader("First Name", new TextFilterField());
46+
EnhancedColumn<Person> firstNameColumn = grid.addColumn(Person::getFirstName).setHeader("First Name", new TextFilterField()).setResizable(true);
4747
// last name column with filtering button and pre-selected filter by last name = "Allen"
48-
grid.addColumn(Person::getLastName).setHeader("Last Name", new TextFilterField(new TextFieldFilterDto("Allen")));
48+
grid.addColumn(Person::getLastName).setHeader("Last Name", new TextFilterField(new TextFieldFilterDto("Allen"))).setResizable(true);
4949
// age column with renderer
5050
NumberRenderer<Person> ageRenderer = new NumberRenderer<Person>(Person::getAge, "Age: %d");
5151
EnhancedColumn<Person> ageColumn = grid.addColumn(ageRenderer).setComparator(Comparator.comparing(Person::getAge)).setHeader("Age", new TextFilterField());
5252
ageColumn.setValueProvider(p -> String.valueOf(p.getAge()));
53-
53+
5454
// add pre-selected descendent order for first name column
5555
List<GridSortOrder<Person>> sortByFirstName = new GridSortOrderBuilder<Person>()
5656
.thenDesc(firstNameColumn).build();

enhanced-grid-flow/src/main/java/com/vaadin/componentfactory/enhancedgrid/EnhancedColumn.java

Lines changed: 89 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,7 @@
2424

2525
import com.vaadin.flow.component.Component;
2626
import com.vaadin.flow.component.HasValueAndElement;
27-
import com.vaadin.flow.component.button.Button;
28-
import com.vaadin.flow.component.dependency.CssImport;
27+
import com.vaadin.flow.component.dependency.JsModule;
2928
import com.vaadin.flow.component.grid.ColumnPathRenderer;
3029
import com.vaadin.flow.component.grid.FilterField;
3130
import com.vaadin.flow.component.grid.FilterFieldDto;
@@ -34,19 +33,18 @@
3433
import com.vaadin.flow.component.grid.GridSorterFilterComponentRenderer;
3534
import com.vaadin.flow.component.grid.SortOrderProvider;
3635
import com.vaadin.flow.component.html.Div;
37-
import com.vaadin.flow.component.icon.Icon;
38-
import com.vaadin.flow.component.icon.VaadinIcon;
36+
import com.vaadin.flow.component.html.Span;
3937
import com.vaadin.flow.data.renderer.Renderer;
4038
import com.vaadin.flow.function.ValueProvider;
39+
import com.vaadin.flow.internal.HtmlUtils;
4140

4241
/**
4342
*
4443
* {@link Grid.Column Column} that extends setHeader methods to add a filter button
4544
* and a {@link FilterField filter component} to perform column's filtering.
4645
*
4746
*/
48-
@CssImport(value = "./styles/enhanced-column.css")
49-
@CssImport(value = "./styles/enhanced-column-sorter.css", themeFor = "vaadin-grid-sorter")
47+
@JsModule(value = "./src/enhanced-grid-sorter.js")
5048
public class EnhancedColumn<T> extends Grid.Column<T> {
5149

5250
private HasValueAndElement<?, ? extends FilterFieldDto> filter;
@@ -55,10 +53,10 @@ public class EnhancedColumn<T> extends Grid.Column<T> {
5553

5654
private EnhancedGrid<T> grid;
5755

58-
private Button filterButton;
59-
6056
private FilterField filterField;
6157

58+
private Component headerComponent;
59+
6260
/**
6361
* @see Column#Column(Grid, String, Renderer)
6462
*
@@ -73,7 +71,7 @@ public EnhancedColumn(EnhancedGrid<T> grid, String columnId, Renderer<T> rendere
7371

7472
public EnhancedColumn<T> setHeader(String labelText, HasValueAndElement<?, ? extends FilterFieldDto> filter) {
7573
if(filter != null) {
76-
Component headerComponent = new Div();
74+
Component headerComponent = new Span();
7775
headerComponent.getElement().setText(labelText);
7876
addFilterButtonToHeader(headerComponent, filter);
7977
return setHeader(headerComponent);
@@ -108,18 +106,10 @@ public EnhancedColumn<T> setHeader(String labelText) {
108106

109107
private void addFilterButtonToHeader(Component headerComponent, HasValueAndElement<?, ? extends FilterFieldDto> filter) {
110108
this.filter = filter;
111-
112-
// add filter button
113-
filterButton = new Button(new Icon(VaadinIcon.FILTER));
114-
filterButton.setId("filter-button");
115-
filterButton.addClassName("filter-not-selected");
116-
filterButton.getElement().addEventListener("click", click -> {
117-
//do nothing
118-
}).addEventData("event.stopPropagation()");
109+
this.headerComponent = headerComponent;
119110

120-
// add filter field popup and set filter as it's filter component
111+
// add filter field (popup component) and set filter as it's filter component
121112
filterField = new FilterField();
122-
filterField.setFor(filterButton.getId().get());
123113
filterField.addApplyFilterListener(grid);
124114
filterField.addFilterComponent(filter.getElement().getComponent().get());
125115

@@ -132,36 +122,55 @@ private void addFilterButtonToHeader(Component headerComponent, HasValueAndEleme
132122
}
133123
}
134124
});
125+
126+
// need to add a not visible component so filterField (popup component) can be open
127+
Div div = new Div();
128+
div.setId(getInternalId());
129+
div.getElement().getStyle().set("display", "inline-block");
130+
filterField.setFor(div.getId().get());
131+
headerComponent.getElement().appendChild(div.getElement());
135132

136-
headerComponent.getElement().appendChild(filterButton.getElement());
137133
// this is needed to avoid js issues when adding popup
138134
headerComponent.getElement().executeJs("return").then(ignore -> {
139135
headerComponent.getElement().appendChild(filterField.getElement());
140-
});
136+
});
137+
138+
grid.addFilterClickedEventListener(e -> {
139+
if(e.buttonId.equals(getInternalId())) {
140+
if(filterField.isOpened()) {
141+
filterField.hide();
142+
} else {
143+
filterField.show();
144+
}
145+
}
146+
});
141147
}
142-
148+
143149
HasValueAndElement<?, ? extends FilterFieldDto> getFilter() {
144150
return filter;
145151
}
146152

147153
void updateFilterButtonStyle(){
148-
if(filter.isEmpty()) {
149-
filterButton.getClassNames().remove("filter-selected");
150-
filterButton.getClassNames().add("filter-not-selected");
151-
} else {
152-
filterButton.getClassNames().remove("filter-not-selected");
153-
filterButton.getClassNames().add("filter-selected");
154-
}
154+
if(headerComponent != null) {
155+
headerComponent.getElement().executeJs("return").then(ignore -> {
156+
if(hasFilterSelected()) {
157+
headerComponent.getElement().executeJs("this.parentElement.parentElement._setProperty('filtered', true)");
158+
} else {
159+
headerComponent.getElement().executeJs("this.parentElement.parentElement._setProperty('filtered', false);");
160+
}
161+
});
162+
}
155163
}
156-
164+
157165
ValueProvider<T, ?> getValueProvider(){
158-
if (this.getRenderer() instanceof ColumnPathRenderer) {
159-
valueProvider = ((ColumnPathRenderer<T>)this.getRenderer()).getValueProviders().values().iterator().next();
160-
} else if(valueProvider == null){
166+
if (this.valueProvider != null) {
167+
return this.valueProvider;
168+
} else if (this.getRenderer() instanceof ColumnPathRenderer) {
169+
return ((ColumnPathRenderer<T>)this.getRenderer()).getValueProviders().values().iterator().next();
170+
} else {
161171
throw new UnsupportedOperationException("Value provider for column is unknown. "
162172
+ "Please set one calling setValueProvider method.");
163173
}
164-
return valueProvider;
165174
}
166175

167176
public void setValueProvider(ValueProvider<T, ?> valueProvider) {
@@ -234,5 +243,50 @@ public EnhancedColumn<T> setComparator(Comparator<T> comparator) {
234243
protected void setHeaderComponent(Component component) {
235244
super.setHeaderRenderer(new GridSorterFilterComponentRenderer<>(this, component));
236245
}
237-
246+
247+
/**
248+
* Return if column shows filter field
249+
*
250+
* @return
251+
*/
252+
public boolean isFilterable() {
253+
return filterField != null;
254+
}
255+
256+
/**
257+
* Returns if column is filtered
258+
*
259+
* @return
260+
*/
261+
public boolean hasFilterSelected() {
262+
return filter != null && !filter.isEmpty();
263+
}
264+
265+
/**
266+
* Add enhanced-grid-sorter element to header template.
267+
*
268+
* This element is an extension of vaadin-grid-sorter that also
269+
* adds the filtering button to the header.
270+
*
271+
* @param templateInnerHtml
272+
* @return
273+
*/
274+
public String addEnhancedGridSorter(String templateInnerHtml) {
275+
String escapedColumnId = HtmlUtils
276+
.escape(this.getInternalId());
277+
String sortable = isSortable() ? " sortable" : "";
278+
String filtered = hasFilterSelected() ? " filtered" : "";
279+
return String.format(
280+
"<enhanced-grid-sorter path='%s'" + sortable + filtered +">%s</enhanced-grid-sorter>",
281+
escapedColumnId, templateInnerHtml);
282+
}
283+
284+
/**
285+
* @see Column#setKey(String)
286+
*
287+
*/
288+
@Override
289+
public EnhancedColumn<T> setKey(String key) {
290+
return (EnhancedColumn<T>) super.setKey(key);
291+
}
238292
}

enhanced-grid-flow/src/main/java/com/vaadin/componentfactory/enhancedgrid/EnhancedGrid.java

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,15 @@
2828

2929
import com.vaadin.flow.component.Component;
3030
import com.vaadin.flow.component.ComponentEvent;
31+
import com.vaadin.flow.component.ComponentEventListener;
3132
import com.vaadin.flow.component.ComponentUtil;
3233
import com.vaadin.flow.component.dependency.CssImport;
3334
import com.vaadin.flow.component.grid.ApplyFilterListener;
3435
import com.vaadin.flow.component.grid.CancelEditConfirmDialog;
3536
import com.vaadin.flow.component.grid.CustomAbstractGridMultiSelectionModel;
3637
import com.vaadin.flow.component.grid.CustomAbstractGridSingleSelectionModel;
3738
import com.vaadin.flow.component.grid.Filter;
39+
import com.vaadin.flow.component.grid.FilterClickedEvent;
3840
import com.vaadin.flow.component.grid.FilterField;
3941
import com.vaadin.flow.component.grid.Grid;
4042
import com.vaadin.flow.component.grid.GridArrayUpdater;
@@ -54,6 +56,7 @@
5456
import com.vaadin.flow.router.BeforeLeaveEvent;
5557
import com.vaadin.flow.router.BeforeLeaveEvent.ContinueNavigationAction;
5658
import com.vaadin.flow.router.BeforeLeaveObserver;
59+
import com.vaadin.flow.shared.Registration;
5760

5861
import elemental.json.JsonObject;
5962

@@ -378,7 +381,7 @@ public void beforeLeave(BeforeLeaveEvent event) {
378381
*/
379382
@Override
380383
protected BiFunction<Renderer<T>, String, Column<T>> getDefaultColumnFactory() {
381-
return (renderer, columnId) -> new EnhancedColumn(this, columnId, renderer);
384+
return (renderer, columnId) -> new EnhancedColumn<>(this, columnId, renderer);
382385
}
383386

384387
/**
@@ -387,10 +390,18 @@ protected BiFunction<Renderer<T>, String, Column<T>> getDefaultColumnFactory() {
387390
*/
388391
@Override
389392
public EnhancedColumn<T> addColumn(ValueProvider<T, ?> valueProvider) {
390-
BiFunction<Renderer<T>, String, Column<T>> defaultFactory = getDefaultColumnFactory();
391-
return (EnhancedColumn<T>) super.addColumn(valueProvider, defaultFactory);
393+
return (EnhancedColumn<T>) super.addColumn(valueProvider);
392394
}
393395

396+
@SuppressWarnings("unchecked")
397+
@Override
398+
protected <C extends Column<T>> C addColumn(ValueProvider<T, ?> valueProvider,
399+
BiFunction<Renderer<T>, String, C> columnFactory) {
400+
EnhancedColumn<T> column = (EnhancedColumn<T>) super.addColumn(valueProvider, columnFactory);
401+
column.setValueProvider(valueProvider);
402+
return (C) column;
403+
}
404+
394405
/**
395406
* @see Grid#addColumn(ValueProvider, String...)
396407
*
@@ -428,6 +439,19 @@ public <V extends Component> EnhancedColumn<T> addComponentColumn(ValueProvider<
428439
return (EnhancedColumn<T>) super.addComponentColumn(componentProvider);
429440
}
430441

442+
/**
443+
* @see Grid#getColumnByKey(String)
444+
*
445+
*/
446+
@Override
447+
public EnhancedColumn<T> getColumnByKey(String columnKey) {
448+
return (EnhancedColumn<T>) super.getColumnByKey(columnKey);
449+
}
450+
451+
protected void setColumnKey(String key, EnhancedColumn<T> column) {
452+
super.setColumnKey(key, column);
453+
}
454+
431455
@Override
432456
public void onApplyFilter(Object filter) {
433457
applyFilter();
@@ -487,7 +511,18 @@ public void clearAllFilters() {
487511
}
488512
}
489513
applyFilter();
490-
}
491-
514+
}
515+
516+
/**
517+
* Add listener on filter-clicked event.
518+
*
519+
* @param listener
520+
* @return registration which can remove the listener.
521+
*/
522+
@SuppressWarnings({ "unchecked", "rawtypes" })
523+
public Registration addFilterClickedEventListener(ComponentEventListener<FilterClickedEvent<T>> listener) {
524+
return addListener(FilterClickedEvent.class, (ComponentEventListener) listener);
525+
}
526+
492527
}
493528

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package com.vaadin.flow.component.grid;
2+
3+
/*-
4+
* #%L
5+
* Enhanced Grid
6+
* %%
7+
* Copyright (C) 2020 - 2021 Vaadin Ltd
8+
* %%
9+
* Licensed under the Apache License, Version 2.0 (the "License");
10+
* you may not use this file except in compliance with the License.
11+
* You may obtain a copy of the License at
12+
*
13+
* http://www.apache.org/licenses/LICENSE-2.0
14+
*
15+
* Unless required by applicable law or agreed to in writing, software
16+
* distributed under the License is distributed on an "AS IS" BASIS,
17+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18+
* See the License for the specific language governing permissions and
19+
* limitations under the License.
20+
* #L%
21+
*/
22+
23+
import javax.validation.constraints.NotNull;
24+
25+
import com.vaadin.componentfactory.enhancedgrid.EnhancedGrid;
26+
import com.vaadin.flow.component.ComponentEvent;
27+
import com.vaadin.flow.component.DomEvent;
28+
import com.vaadin.flow.component.EventData;
29+
30+
/**
31+
* Custom event triggered by enhanced-grid-sorter element.
32+
*
33+
*/
34+
@DomEvent("filter-clicked")
35+
public class FilterClickedEvent<T> extends ComponentEvent<EnhancedGrid<T>> {
36+
37+
@NotNull
38+
public final String buttonId;
39+
40+
public FilterClickedEvent(EnhancedGrid<T> source, boolean fromClient, @EventData("event.detail.id") @NotNull String id) {
41+
super(source, fromClient);
42+
this.buttonId = id;
43+
}
44+
45+
}

enhanced-grid-flow/src/main/java/com/vaadin/flow/component/grid/GridSorterFilterComponentRenderer.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,16 +96,22 @@ private void setupTemplateWhenAttached(UI ui, Element owner,
9696
}
9797

9898
/*
99-
* The renderer must set the base header template back to the column, so
99+
* The renderer must set the base header template back to the column, so
100100
* if/when the sortable state is changed by the developer, the column
101101
* knows how to add or remove the grid sorter.
102102
*/
103103
column.setBaseHeaderTemplate(templateInnerHtml);
104-
if (column.hasSortingIndicators()) {
105-
templateInnerHtml = column.addGridSorter(templateInnerHtml)
106-
.replace("<vaadin-grid-sorter", "<vaadin-grid-sorter class='enhanced-grid-sorter'");
104+
if (column.hasSortingIndicators() && !column.isFilterable()) {
105+
templateInnerHtml = column.addGridSorter(templateInnerHtml);
107106
}
108107

108+
if(column.isFilterable()) {
109+
String gridSorter = column.isSortable() ? column.addGridSorter("")
110+
.replaceFirst(">", "style='display:none' slot='direction'>") : "";
111+
templateInnerHtml = column.addEnhancedGridSorter(templateInnerHtml);
112+
templateInnerHtml = templateInnerHtml.replaceFirst(">", ">" + gridSorter);
113+
}
114+
109115
templateElement.setProperty("innerHTML", templateInnerHtml);
110116
}
111117

0 commit comments

Comments
 (0)