* Make Grid rows draggable (#8396) * Add @author and @since tags, javadocs and minor fixesreviewable/pr8979/r1
@@ -0,0 +1,104 @@ | |||
/* | |||
* 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.client.connectors.grid; | |||
import com.google.gwt.dom.client.NativeEvent; | |||
import com.google.gwt.dom.client.TableRowElement; | |||
import com.vaadin.client.ServerConnector; | |||
import com.vaadin.client.extensions.DragSourceExtensionConnector; | |||
import com.vaadin.client.widget.escalator.RowContainer; | |||
import com.vaadin.client.widgets.Escalator; | |||
import com.vaadin.shared.Range; | |||
import com.vaadin.shared.ui.Connect; | |||
import com.vaadin.shared.ui.grid.GridDragSourceExtensionState; | |||
import com.vaadin.ui.GridDragSourceExtension; | |||
import elemental.events.Event; | |||
import elemental.json.JsonObject; | |||
/** | |||
* Adds HTML5 drag and drop functionality to a {@link com.vaadin.client.widgets.Grid | |||
* Grid}'s rows. This is the client side counterpart of {@link | |||
* GridDragSourceExtension}. | |||
* | |||
* @author Vaadin Ltd | |||
* @since | |||
*/ | |||
@Connect(GridDragSourceExtension.class) | |||
public class GridDragSourceExtensionConnector extends | |||
DragSourceExtensionConnector { | |||
private GridConnector gridConnector; | |||
@Override | |||
protected void extend(ServerConnector target) { | |||
this.gridConnector = (GridConnector) target; | |||
// Set newly added rows draggable | |||
getGridBody().setNewEscalatorRowCallback( | |||
rows -> rows.forEach(this::setDraggable)); | |||
// Add drag listeners to body element | |||
addDragListeners(getGridBody().getElement()); | |||
} | |||
@Override | |||
protected void onDragStart(Event event) { | |||
super.onDragStart(event); | |||
if (event.getTarget() instanceof TableRowElement) { | |||
TableRowElement row = (TableRowElement) event.getTarget(); | |||
int rowIndex = ((Escalator.AbstractRowContainer) getGridBody()) | |||
.getLogicalRowIndex(row); | |||
JsonObject rowData = gridConnector.getDataSource().getRow(rowIndex); | |||
((NativeEvent) event).getDataTransfer() | |||
.setData(GridDragSourceExtensionState.DATA_TYPE_ROW_DATA, | |||
rowData.toJson()); | |||
} | |||
} | |||
@Override | |||
public void onUnregister() { | |||
super.onUnregister(); | |||
// Remove draggable from all row elements in the escalator | |||
Range visibleRange = getEscalator().getVisibleRowRange(); | |||
for (int i = visibleRange.getStart(); i < visibleRange.getEnd(); i++) { | |||
removeDraggable(getGridBody().getRowElement(i)); | |||
} | |||
// Remove drag listeners from body element | |||
removeDragListeners(getGridBody().getElement()); | |||
// Remove callback for newly added rows | |||
getGridBody().setNewEscalatorRowCallback(null); | |||
} | |||
private Escalator getEscalator() { | |||
return gridConnector.getWidget().getEscalator(); | |||
} | |||
private RowContainer.BodyRowContainer getGridBody() { | |||
return getEscalator().getBody(); | |||
} | |||
@Override | |||
public GridDragSourceExtensionState getState() { | |||
return (GridDragSourceExtensionState) super.getState(); | |||
} | |||
} |
@@ -56,29 +56,66 @@ public class DragSourceExtensionConnector extends AbstractExtensionConnector { | |||
protected void extend(ServerConnector target) { | |||
dragSourceWidget = ((ComponentConnector) target).getWidget(); | |||
Element dragSourceElement = getDraggableElement(); | |||
setDraggable(getDraggableElement()); | |||
addDragListeners(getDraggableElement()); | |||
} | |||
/** | |||
* Sets the given element draggable and adds class name. | |||
* | |||
* @param element | |||
* Element to be set draggable. | |||
*/ | |||
protected void setDraggable(Element element) { | |||
element.setDraggable(Element.DRAGGABLE_TRUE); | |||
element.addClassName(CLASS_DRAGGABLE); | |||
} | |||
/** | |||
* Removes draggable and class name from the given element. | |||
* | |||
* @param element | |||
* Element to remove draggable from. | |||
*/ | |||
protected void removeDraggable(Element element) { | |||
element.setDraggable(Element.DRAGGABLE_FALSE); | |||
element.removeClassName(CLASS_DRAGGABLE); | |||
} | |||
dragSourceElement.setDraggable(Element.DRAGGABLE_TRUE); | |||
dragSourceElement.addClassName(CLASS_DRAGGABLE); | |||
/** | |||
* Adds dragstart and dragend event listeners to the given DOM element. | |||
* | |||
* @param element | |||
* DOM element to attach event listeners to. | |||
*/ | |||
protected void addDragListeners(Element element) { | |||
EventTarget target = element.cast(); | |||
EventTarget dragSource = dragSourceElement.cast(); | |||
target.addEventListener(Event.DRAGSTART, dragStartListener); | |||
target.addEventListener(Event.DRAGEND, dragEndListener); | |||
} | |||
// dragstart | |||
dragSource.addEventListener(Event.DRAGSTART, dragStartListener); | |||
/** | |||
* Removes dragstart and dragend event listeners from the given DOM element. | |||
* | |||
* @param element | |||
* DOM element to remove event listeners from. | |||
*/ | |||
protected void removeDragListeners(Element element) { | |||
EventTarget target = element.cast(); | |||
// dragend | |||
dragSource.addEventListener(Event.DRAGEND, dragEndListener); | |||
target.removeEventListener(Event.DRAGSTART, dragStartListener); | |||
target.removeEventListener(Event.DRAGEND, dragEndListener); | |||
} | |||
@Override | |||
public void onUnregister() { | |||
super.onUnregister(); | |||
EventTarget dragSource = (EventTarget) getDraggableElement(); | |||
Element dragSource = getDraggableElement(); | |||
// Remove listeners | |||
dragSource.removeEventListener(Event.DRAGSTART, dragStartListener); | |||
dragSource.removeEventListener(Event.DRAGEND, dragEndListener); | |||
removeDraggable(dragSource); | |||
removeDragListeners(dragSource); | |||
} | |||
/** |
@@ -16,6 +16,9 @@ | |||
package com.vaadin.client.widget.escalator; | |||
import java.util.List; | |||
import java.util.function.Consumer; | |||
import com.google.gwt.dom.client.Element; | |||
import com.google.gwt.dom.client.TableRowElement; | |||
import com.google.gwt.dom.client.TableSectionElement; | |||
@@ -116,6 +119,17 @@ public interface RowContainer { | |||
@Override | |||
public void removeRows(int index, int numberOfRows) | |||
throws IndexOutOfBoundsException, IllegalArgumentException; | |||
/** | |||
* Sets a callback function that is executed when new rows are added to | |||
* the escalator. | |||
* | |||
* @param consumer | |||
* A Consumer function that receives the newly added table row | |||
* elements. | |||
*/ | |||
public void setNewEscalatorRowCallback( | |||
Consumer<List<TableRowElement>> consumer); | |||
} | |||
/** |
@@ -25,7 +25,9 @@ import java.util.List; | |||
import java.util.ListIterator; | |||
import java.util.Map; | |||
import java.util.Map.Entry; | |||
import java.util.Optional; | |||
import java.util.TreeMap; | |||
import java.util.function.Consumer; | |||
import java.util.logging.Level; | |||
import java.util.logging.Logger; | |||
@@ -2406,6 +2408,12 @@ public class Escalator extends Widget | |||
@Deprecated | |||
private int topRowLogicalIndex = 0; | |||
/** | |||
* A callback function to be executed after new rows are added to the | |||
* escalator. | |||
*/ | |||
private Consumer<List<TableRowElement>> newEscalatorRowCallback; | |||
private void setTopRowLogicalIndex(int topRowLogicalIndex) { | |||
if (LogConfiguration.loggingIsEnabled(Level.INFO)) { | |||
Logger.getLogger("Escalator.BodyRowContainer") | |||
@@ -2997,6 +3005,10 @@ public class Escalator extends Widget | |||
y += spacerContainer.getSpacerHeight(i); | |||
} | |||
// Execute the registered callback function for newly created rows | |||
Optional.ofNullable(newEscalatorRowCallback) | |||
.ifPresent(callback -> callback.accept(addedRows)); | |||
return addedRows; | |||
} else { | |||
return Collections.emptyList(); | |||
@@ -4005,6 +4017,12 @@ public class Escalator extends Widget | |||
int padding) { | |||
spacerContainer.scrollToSpacer(spacerIndex, destination, padding); | |||
} | |||
@Override | |||
public void setNewEscalatorRowCallback( | |||
Consumer<List<TableRowElement>> callback) { | |||
this.newEscalatorRowCallback = callback; | |||
} | |||
} | |||
private class ColumnConfigurationImpl implements ColumnConfiguration { |
@@ -0,0 +1,48 @@ | |||
/* | |||
* 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 com.vaadin.event.dnd.DragSourceExtension; | |||
import com.vaadin.shared.ui.grid.GridDragSourceExtensionState; | |||
/** | |||
* Makes a Grid's rows draggable for HTML5 drag and drop functionality. | |||
* | |||
* @author Vaadin Ltd. | |||
* @since | |||
*/ | |||
public class GridDragSourceExtension extends DragSourceExtension<Grid> { | |||
/** | |||
* Extends a Grid and makes it's rows draggable. | |||
* | |||
* @param target | |||
* Grid to be extended. | |||
*/ | |||
public GridDragSourceExtension(Grid target) { | |||
super(target); | |||
} | |||
@Override | |||
protected GridDragSourceExtensionState getState() { | |||
return (GridDragSourceExtensionState) super.getState(); | |||
} | |||
@Override | |||
protected GridDragSourceExtensionState getState(boolean markAsDirty) { | |||
return (GridDragSourceExtensionState) super.getState(markAsDirty); | |||
} | |||
} |
@@ -0,0 +1,33 @@ | |||
/* | |||
* 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.shared.ui.grid; | |||
import com.vaadin.shared.ui.dnd.DragSourceState; | |||
/** | |||
* State class containing parameters for GridDragSourceExtension. | |||
* | |||
* @author Vaadin Ltd | |||
* @since | |||
*/ | |||
public class GridDragSourceExtensionState extends DragSourceState { | |||
/** | |||
* Data type for storing the dragged row's data | |||
*/ | |||
public static final String DATA_TYPE_ROW_DATA = "grid-row-data"; | |||
} |
@@ -0,0 +1,95 @@ | |||
/* | |||
* 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.tests.components.grid; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import java.util.stream.IntStream; | |||
import com.vaadin.event.dnd.DropTargetExtension; | |||
import com.vaadin.server.VaadinRequest; | |||
import com.vaadin.shared.ui.grid.GridDragSourceExtensionState; | |||
import com.vaadin.tests.components.AbstractTestUIWithLog; | |||
import com.vaadin.ui.Grid; | |||
import com.vaadin.ui.GridDragSourceExtension; | |||
import com.vaadin.ui.HorizontalLayout; | |||
import com.vaadin.ui.Label; | |||
import com.vaadin.ui.Layout; | |||
public class GridDragAndDrop extends AbstractTestUIWithLog { | |||
@Override | |||
protected void setup(VaadinRequest request) { | |||
// Drag source | |||
Grid<Bean> dragSourceComponent = new Grid<>(); | |||
dragSourceComponent.setItems(createItems(50)); | |||
dragSourceComponent.addColumn(Bean::getId).setCaption("ID"); | |||
dragSourceComponent.addColumn(Bean::getValue).setCaption("Value"); | |||
GridDragSourceExtension dragSource = new GridDragSourceExtension( | |||
dragSourceComponent); | |||
Label dropTargetComponent = new Label("Drop here"); | |||
DropTargetExtension<Label> dropTarget = new DropTargetExtension<>( | |||
dropTargetComponent); | |||
dropTarget.addDropListener(event -> { | |||
log(event.getTransferData( | |||
GridDragSourceExtensionState.DATA_TYPE_ROW_DATA)); | |||
}); | |||
Layout layout = new HorizontalLayout(); | |||
layout.addComponents(dragSourceComponent, dropTargetComponent); | |||
addComponent(layout); | |||
} | |||
private List<Bean> createItems(int num) { | |||
List<Bean> items = new ArrayList<>(num); | |||
IntStream.range(0, num) | |||
.forEach(i -> items.add(new Bean("id_" + i, "value_" + i))); | |||
return items; | |||
} | |||
public static class Bean { | |||
private String id; | |||
private String value; | |||
public Bean(String id, String value) { | |||
this.id = id; | |||
this.value = value; | |||
} | |||
public String getId() { | |||
return id; | |||
} | |||
public void setId(String id) { | |||
this.id = id; | |||
} | |||
public String getValue() { | |||
return value; | |||
} | |||
public void setValue(String value) { | |||
this.value = value; | |||
} | |||
} | |||
} |