1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.ui.contentapp.browser;
35
36 import static java.util.stream.Collectors.toSet;
37
38 import info.magnolia.ui.ValueContext;
39 import info.magnolia.ui.contentapp.browser.actions.ContextAwareActionPopup;
40 import info.magnolia.ui.contentapp.browser.actions.ShortcutActionsExecutor;
41 import info.magnolia.ui.contentapp.browser.drop.RowDragger;
42 import info.magnolia.ui.contentapp.configuration.GridViewDefinition;
43
44 import java.util.HashSet;
45 import java.util.Iterator;
46 import java.util.Objects;
47 import java.util.Optional;
48 import java.util.stream.Collectors;
49 import java.util.stream.Stream;
50
51 import com.vaadin.data.provider.DataProvider;
52 import com.vaadin.shared.ui.grid.DropMode;
53 import com.vaadin.shared.ui.grid.ScrollDestination;
54 import com.vaadin.ui.Component;
55 import com.vaadin.ui.Composite;
56 import com.vaadin.ui.Grid;
57 import com.vaadin.ui.components.grid.Editor;
58
59
60
61
62
63
64
65 public abstract class GridView<T, P extends GridViewPresenter<T>> extends Composite implements ContentView, Component.Focusable {
66
67 private boolean scrolled;
68
69 protected final ValueContext<T> valueContext;
70 protected final GridViewDefinition<T> definition;
71 protected final Grid<T> grid;
72
73 protected final P presenter;
74 protected final Editor<T> editor;
75
76 protected GridView(ValueContext<T> valueContext, GridViewDefinition<T> definition) {
77 this.valueContext = valueContext;
78 this.definition = definition;
79 this.presenter = createPresenter();
80 this.grid = presenter.grid();
81 this.editor = grid.getEditor();
82
83 ContextAwareActionPopup<T> actionPopup = null;
84 if (!definition.isReadOnly()) {
85 actionPopup = create(ContextAwareActionPopup.class, grid);
86 }
87 final ShortcutActionsExecutor actionsExecutor =
88 actionPopup != null ? actionPopup : ShortcutActionsExecutor.NOOP;
89
90 grid.setSizeFull();
91 grid.setSelectionMode(definition.isMultiSelect() ? Grid.SelectionMode.MULTI : Grid.SelectionMode.SINGLE);
92 grid.addSelectionListener(event -> {
93 scrolled = false;
94 if (!valueContext.get().collect(Collectors.toSet()).equals(event.getAllSelectedItems())) {
95 valueContext.set(event.getAllSelectedItems());
96 } else if (!event.isUserOriginated()) {
97 scrollToSelectedItem();
98 }
99 });
100
101 editor.setEnabled(false);
102 grid.getColumns().stream()
103 .map(Grid.Column::getEditorBinding)
104 .filter(Objects::nonNull)
105 .findFirst()
106 .ifPresent(any -> {
107 editor.setBuffered(false);
108 editor.setEnabled(true);
109 });
110
111 setLastColumnAsNotResizable(grid);
112
113 Optional.ofNullable(definition.getDropConstraint())
114 .ifPresent(dropConstraint -> create(RowDragger.class, grid, DropMode.BETWEEN, create(dropConstraint.getImplementationClass(), dropConstraint)));
115
116
117 valueContext.observe(items -> {
118 if (!grid.getSelectedItems().equals(items)) {
119 grid.deselectAll();
120 items.forEach(grid::select);
121 }
122 });
123 setCompositionRoot(create(GridWithShortcuts.class, grid, actionsExecutor));
124 grid.focus();
125 setSizeFull();
126 }
127
128 @Override
129 public void setVisible(boolean visible) {
130 super.setVisible(visible);
131 if (visible) {
132
133
134 scrollToSelectedItem();
135 } else {
136 scrolled = false;
137 }
138 }
139
140 @Override
141 public void focus() {
142 scrolled = false;
143 scrollToSelectedItem();
144 }
145
146 protected void scrollToSelectedItem() {
147 if (!scrolled && definition.isScrollToSelectedItem() && isVisible()) {
148 grid.getSelectedItems().stream().findFirst()
149 .ifPresent(selectedItem -> {
150 int index = 0;
151 final DataProvider<T, ?> dataProvider = grid.getDataProvider();
152 final Object selectedItemId = dataProvider.getId(selectedItem);
153 for (Iterator<T> it = fetchItems().iterator(); it.hasNext(); ) {
154 T t = it.next();
155 if (dataProvider.getId(t).equals(selectedItemId)) {
156 scrolled = true;
157 break;
158 } else {
159 index++;
160 }
161 }
162 grid.scrollTo(index, ScrollDestination.MIDDLE);
163 });
164 }
165 }
166
167 @Override
168 public int getTabIndex() {
169 return 0;
170 }
171
172 @Override
173 public void setTabIndex(int tabIndex) {
174
175 }
176
177 Stream<T> fetchItems() {
178 return grid.getDataCommunicator().fetchItemsWithRange(0, Integer.MAX_VALUE).stream();
179 }
180
181 protected abstract P createPresenter();
182
183 protected void select(Stream<T> values) {
184 if (this.definition.isMultiSelect()) {
185 this.grid.asMultiSelect().updateSelection(values.collect(toSet()), new HashSet<>());
186 } else {
187 values.findFirst().ifPresent(this.grid.asSingleSelect()::setValue);
188 }
189 }
190
191
192
193
194 private void setLastColumnAsNotResizable(Grid<T> grid) {
195 if (grid.getColumns().size() > 0) {
196 grid.getColumns().get(grid.getColumns().size() - 1).setResizable(false);
197 }
198 }
199 }