aboutsummaryrefslogtreecommitdiffstats
path: root/server/src/main/java/com/vaadin/ui/GridDragSource.java
blob: ef9a608871d62e931d8cce4e461d3247c193171d (plain)
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
/*
 * Copyright 2000-2016 Vaadin Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.vaadin.ui;

import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import com.vaadin.data.provider.DataGenerator;
import com.vaadin.event.dnd.DragSourceExtension;
import com.vaadin.event.dnd.grid.GridDragEndEvent;
import com.vaadin.event.dnd.grid.GridDragEndListener;
import com.vaadin.event.dnd.grid.GridDragStartEvent;
import com.vaadin.event.dnd.grid.GridDragStartListener;
import com.vaadin.server.SerializableFunction;
import com.vaadin.shared.Registration;
import com.vaadin.shared.ui.dnd.DragSourceState;
import com.vaadin.shared.ui.dnd.DropEffect;
import com.vaadin.shared.ui.grid.GridDragSourceRpc;
import com.vaadin.shared.ui.grid.GridDragSourceState;

import elemental.json.JsonObject;

/**
 * Makes a Grid's rows draggable for HTML5 drag and drop functionality.
 * <p>
 * When dragging a selected row, all the visible selected rows are dragged. Note
 * that ONLY visible rows are taken into account.
 *
 * @param <T>
 *         The Grid bean type.
 * @author Vaadin Ltd.
 * @since
 */
public class GridDragSource<T> extends DragSourceExtension<Grid<T>> {

    /**
     * Drag data generator that appends drag data to each row.
     */
    private DataGenerator<T> dragDataGenerator;

    /**
     * Drag data generator function that is executed for each row.
     */
    private SerializableFunction<T, JsonObject> generatorFunction;

    /**
     * Extends a Grid and makes it's rows draggable.
     *
     * @param target
     *         Grid to be extended.
     */
    public GridDragSource(Grid<T> target) {
        super(target);

        // Create drag data generator
        dragDataGenerator = this::generateDragData;

        // Add drag data generator to Grid
        target.addDataGenerator(dragDataGenerator);
    }

    @Override
    protected void registerDragSourceRpc(Grid<T> target) {
        registerRpc(new GridDragSourceRpc() {
            @Override
            public void dragStart(List<String> draggedItemKeys) {

                GridDragStartEvent<T> event = new GridDragStartEvent<>(target,
                        getState(false).effectAllowed,
                        getDraggedItems(target, draggedItemKeys));

                fireEvent(event);
            }

            @Override
            public void dragEnd(DropEffect dropEffect,
                    List<String> draggedItemKeys) {

                GridDragEndEvent<T> event = new GridDragEndEvent<>(target,
                        dropEffect, getDraggedItems(target, draggedItemKeys));

                fireEvent(event);
            }
        });
    }

    /**
     * Collects the dragged items of a Grid given the list of item keys.
     */
    private Set<T> getDraggedItems(Grid<T> grid, List<String> draggedItemKeys) {
        if (draggedItemKeys == null || draggedItemKeys.isEmpty()) {
            throw new IllegalStateException(
                    "The drag event does not contain dragged items");
        }

        return draggedItemKeys.stream()
                .map(key -> grid.getDataCommunicator().getKeyMapper().get(key))
                .collect(Collectors.toSet());
    }

    /**
     * Drag data generator. Appends drag data to row data json if generator
     * function is set by the user of this extension.
     *
     * @param item
     *         Row item for data generation.
     * @param jsonObject
     *         Row data in json format.
     */
    private void generateDragData(T item, JsonObject jsonObject) {
        Optional.ofNullable(generatorFunction).ifPresent(generator -> jsonObject
                .put(GridDragSourceState.JSONKEY_DRAG_DATA,
                        generator.apply(item)));
    }

    /**
     * Sets a generator function for customizing drag data. The function is
     * executed for each item in the Grid during data generation. Return a
     * {@link JsonObject} to be appended to the row data.
     * <p>
     * Example:
     * <pre>
     *     dragSourceExtension.setDragDataGenerator(item -> {
     *         JsonObject dragData = Json.createObject();
     *         dragData.put("someKey", item.getValue());
     *         return dragData;
     *     });
     * </pre>
     *
     * @param generator
     *         Function to be executed on row data generation.
     */
    public void setDragDataGenerator(
            SerializableFunction<T, JsonObject> generator) {
        generatorFunction = generator;
    }

    /**
     * Setting the data transfer text for this drag source is not supported.
     *
     * @throws UnsupportedOperationException
     *         Setting dataTransferText is not supported, since the drag data is
     *         set for each row based on the data provided by the generator.
     * @see #setDragDataGenerator(SerializableFunction)
     */
    @Override
    public void setDataTransferText(String data) throws
            UnsupportedOperationException {
        throw new UnsupportedOperationException(
                "Setting dataTransferText is not supported");
    }

    /**
     * Attaches dragstart listener for the current drag source grid.
     *
     * @param listener
     *         Listener to handle the dragstart event.
     * @return Handle to be used to remove this listener.
     * @see GridDragStartEvent
     */
    public Registration addGridDragStartListener(
            GridDragStartListener<T> listener) {
        return addListener(DragSourceState.EVENT_DRAGSTART,
                GridDragStartEvent.class, listener,
                GridDragStartListener.DRAG_START_METHOD);
    }

    /**
     * Attaches dragend listener for the current drag source grid.
     *
     * @param listener
     *         Listener to handle the dragend event.
     * @return Handle to be used to remove this listener.
     * @see GridDragEndEvent
     */
    public Registration addGridDragEndListener(
            GridDragEndListener<T> listener) {
        return addListener(DragSourceState.EVENT_DRAGEND,
                GridDragEndEvent.class, listener,
                GridDragEndListener.DRAG_END_METHOD);
    }

    /**
     * Returns the generator function for customizing drag data.
     *
     * @return Drag data generator function.
     */
    public SerializableFunction<T, JsonObject> getDragDataGenerator() {
        return generatorFunction;
    }

    @Override
    public void remove() {
        super.remove();

        getParent().removeDataGenerator(dragDataGenerator);
    }

    @Override
    protected GridDragSourceState getState() {
        return (GridDragSourceState) super.getState();
    }

    @Override
    protected GridDragSourceState getState(boolean markAsDirty) {
        return (GridDragSourceState) super.getState(markAsDirty);
    }
}