Skip to content

Commit

Permalink
feat: add drag and drop to Chart API (#7026)
Browse files Browse the repository at this point in the history
Co-authored-by: Diego Cardoso <[email protected]>
  • Loading branch information
bwajtr and DiegoCardoso authored Jan 17, 2025
1 parent f333a72 commit 3656506
Show file tree
Hide file tree
Showing 10 changed files with 1,223 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
/**
* Copyright 2000-2025 Vaadin Ltd.
*
* This program is available under Vaadin Commercial License and Service Terms.
*
* See {@literal <https://vaadin.com/commercial-license-and-service-terms>} for the full
* license.
*/
package com.vaadin.flow.component.charts.examples.gantt;

import java.time.Instant;
import java.util.ArrayList;

import com.vaadin.flow.component.charts.Chart;
import com.vaadin.flow.component.charts.examples.AbstractChartExample;
import com.vaadin.flow.component.charts.examples.SkipFromDemo;
import com.vaadin.flow.component.charts.model.AxisType;
import com.vaadin.flow.component.charts.model.ChartType;
import com.vaadin.flow.component.charts.model.Configuration;
import com.vaadin.flow.component.charts.model.DataLabels;
import com.vaadin.flow.component.charts.model.DragDrop;
import com.vaadin.flow.component.charts.model.GanttSeries;
import com.vaadin.flow.component.charts.model.GanttSeriesItem;
import com.vaadin.flow.component.charts.model.HorizontalAlign;
import com.vaadin.flow.component.charts.model.PlotOptionsGantt;
import com.vaadin.flow.component.charts.model.Tooltip;
import com.vaadin.flow.component.charts.model.XAxis;
import com.vaadin.flow.component.charts.model.YAxis;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;

@SuppressWarnings("unused")
@SkipFromDemo
public class GanttInteractiveDemo extends AbstractChartExample {

private VerticalLayout logPane;

@Override
public void initDemo() {
Chart chart = new Chart(ChartType.GANTT);
configureEvents(chart);

final Configuration configuration = chart.getConfiguration();
var tooltip = new Tooltip(true);
tooltip.setXDateFormat("%a %b %d, %H:%M");
configuration.setTooltip(tooltip);

configuration.setTitle("Interactive Gantt Chart");
configuration.setSubTitle("Drag and drop points to edit");

XAxis xAxis = configuration.getxAxis();
xAxis.setCurrentDateIndicator(true);

YAxis yAxis = configuration.getyAxis();
yAxis.setType(AxisType.CATEGORY);
yAxis.setCategories("Tech", "Marketing", "Sales");
yAxis.setMin(0);
yAxis.setMax(2);

PlotOptionsGantt plotOptionsGantt = new PlotOptionsGantt();
plotOptionsGantt.setAnimation(false); // Do not animate dependency
// connectors
configureDragAndDrop(plotOptionsGantt);

plotOptionsGantt.setAllowPointSelect(true);
configuration.setPlotOptions(plotOptionsGantt);

final GanttSeries series = createSeries();
configureDataLabels(series);
configuration.addSeries(series);

add(chart);
logPane = new VerticalLayout();
logPane.setSpacing(false);
add(logPane);
}

private void configureEvents(Chart chart) {
chart.addPointSelectListener(event -> {
final GanttSeriesItem ganttSeriesItem = ((GanttSeries) event
.getSeries()).get(event.getItemIndex());
var taskId = ganttSeriesItem.getId();
logMessage("Task with id " + taskId + " was selected");
});

chart.addPointUnselectListener(event -> {
final GanttSeriesItem ganttSeriesItem = ((GanttSeries) event
.getSeries()).get(event.getItemIndex());
var taskId = ganttSeriesItem.getId();
logMessage("Task with id " + taskId + " was deselected");
});

chart.addPointDragStartListener(event -> {
final GanttSeriesItem ganttSeriesItem = ((GanttSeries) event
.getSeries()).get(event.getItemIndex());
var taskId = ganttSeriesItem.getId();
logMessage("Task with id " + taskId + " dragging started: "
+ event.getStart() + " - " + event.getEnd());
});

chart.addPointDropListener(event -> {
final GanttSeriesItem ganttSeriesItem = ((GanttSeries) event
.getSeries()).get(event.getItemIndex());
var taskId = ganttSeriesItem.getId();
logMessage("Task with id " + taskId + " was dropped: "
+ event.getStart() + " - " + event.getEnd());
});
}

private void logMessage(String message) {
logPane.add(new Div(message));
if (logPane.getComponentCount() > 10) {
logPane.remove(logPane.getComponentAt(0));
}
}

private void configureDataLabels(GanttSeries series) {
var plotOptions = new PlotOptionsGantt();
var dataLabels = new ArrayList<DataLabels>();
var pointNameDataLabel = new DataLabels(true);
pointNameDataLabel.setAlign(HorizontalAlign.CENTER);
pointNameDataLabel.setFormat("{point.name}");
dataLabels.add(pointNameDataLabel);
plotOptions.setDataLabels(dataLabels);
series.setPlotOptions(plotOptions);
}

private void configureDragAndDrop(PlotOptionsGantt plotOptions) {
final DragDrop dragDrop = plotOptions.getDragDrop();
dragDrop.setDraggableX(true);
dragDrop.setDraggableY(true);
dragDrop.setDragMinY(0);
dragDrop.setDragMaxY(2);
dragDrop.setDragPrecisionX(1000 * 60 * 60 * 8); // Snap to eight hours
}

private GanttSeries createSeries() {
GanttSeries series = new GanttSeries();
final GanttSeriesItem tech1 = new GanttSeriesItem(0,
Instant.parse("2014-10-18T00:00:00Z"),
Instant.parse("2014-10-20T00:00:00Z"));
tech1.setId("tech1");
tech1.setName("Prototype");
series.add(tech1);

final GanttSeriesItem tech2 = new GanttSeriesItem();
tech2.setMilestone(true);
tech2.setStart(Instant.parse("2014-10-22T00:00:00Z"));
tech2.setY(0);
tech2.setId("tech2");
tech2.addDependency("tech1");
tech1.setName("Prototype done");
series.add(tech2);

final GanttSeriesItem tech3 = new GanttSeriesItem(0,
Instant.parse("2014-10-24T00:00:00Z"),
Instant.parse("2014-10-27T00:00:00Z"));
tech3.setId("tech3");
tech3.setName("Testing");
tech3.addDependency("tech2");

series.add(tech3);

final GanttSeriesItem marketing1 = new GanttSeriesItem(1,
Instant.parse("2014-10-20T00:00:00Z"),
Instant.parse("2014-10-23T00:00:00Z"));
marketing1.setId("marketing1");
marketing1.setName("Product pages");
series.add(marketing1);

final GanttSeriesItem marketing2 = new GanttSeriesItem(1,
Instant.parse("2014-10-25T00:00:00Z"),
Instant.parse("2014-10-29T00:00:00Z"));
marketing2.setId("marketing2");
marketing2.setName("Newsletter");
series.add(marketing2);

final GanttSeriesItem sales1 = new GanttSeriesItem(2,
Instant.parse("2014-10-23T00:00:00Z"),
Instant.parse("2014-10-26T00:00:00Z"));
sales1.setId("sales1");
sales1.setName("Licensing");
series.add(sales1);
return series;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@
import com.vaadin.flow.component.charts.events.ChartSelectionEvent;
import com.vaadin.flow.component.charts.events.DrilldownEvent;
import com.vaadin.flow.component.charts.events.PointClickEvent;
import com.vaadin.flow.component.charts.events.PointDragEvent;
import com.vaadin.flow.component.charts.events.PointDragStartEvent;
import com.vaadin.flow.component.charts.events.PointDropEvent;
import com.vaadin.flow.component.charts.events.PointLegendItemClickEvent;
import com.vaadin.flow.component.charts.events.PointMouseOutEvent;
import com.vaadin.flow.component.charts.events.PointMouseOverEvent;
Expand Down Expand Up @@ -596,6 +599,38 @@ public Registration addPointUpdateListener(
return addListener(PointUpdateEvent.class, listener);
}

/**
* Adds a point drag start listener, which will be notified when starting to
* drag a point.
*
* @param listener
*/
public Registration addPointDragStartListener(
ComponentEventListener<PointDragStartEvent> listener) {
return addListener(PointDragStartEvent.class, listener);
}

/**
* Adds a point drop listener, which will be notified point is dropped.
*
* @param listener
*/
public Registration addPointDropListener(
ComponentEventListener<PointDropEvent> listener) {
return addListener(PointDropEvent.class, listener);
}

/**
* Adds a point drag listener, which will be notified while point is
* dragged.
*
* @param listener
*/
public Registration addPointDragListener(
ComponentEventListener<PointDragEvent> listener) {
return addListener(PointDragEvent.class, listener);
}

/**
* Adds a x axes extremes set listener, which will be notified when an x
* axis extremes are set
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/**
* Copyright 2000-2025 Vaadin Ltd.
*
* This program is available under Vaadin Commercial License and Service Terms.
*
* See {@literal <https://vaadin.com/commercial-license-and-service-terms>} for the full
* license.
*/
package com.vaadin.flow.component.charts.events;

import java.time.Instant;

import com.vaadin.flow.component.ComponentEvent;
import com.vaadin.flow.component.DomEvent;
import com.vaadin.flow.component.EventData;
import com.vaadin.flow.component.charts.Chart;
import com.vaadin.flow.component.charts.util.Util;

/**
* Fired while dragging a point.
*/
@DomEvent("point-drag")
public class PointDragEvent extends ComponentEvent<Chart> implements HasItem {

private final String category;
private final Instant start;
private final Instant end;
private final String parent;
private final Double x;
private final Double y;
private final int seriesIndex;
private final int pointIndex;
private final String pointId;

public PointDragEvent(Chart source, boolean fromClient,
@EventData("event.detail.originalEvent.target.series.index") int seriesIndex,
@EventData("event.detail.originalEvent.target.index") int pointIndex,
@EventData("event.detail.originalEvent.target.id") String pointId,
@EventData("event.detail.originalEvent.target.category") String category,
@EventData("event.detail.originalEvent.target.start") Double start,
@EventData("event.detail.originalEvent.target.end") Double end,
@EventData("event.detail.originalEvent.target.parent") String parent,
@EventData("event.detail.originalEvent.target.x") Double x,
@EventData("event.detail.originalEvent.target.y") Double y) {
super(source, fromClient);
this.seriesIndex = seriesIndex;
this.pointIndex = pointIndex;
this.pointId = pointId;
this.category = category;
this.start = start != null ? Util.toServerInstant(start) : null;
this.end = end != null ? Util.toServerInstant(end) : null;
this.parent = parent;
this.x = x;
this.y = y;
}

@Override
public String getCategory() {
return category;
}

@Override
public int getItemIndex() {
return pointIndex;
}

@Override
public String getItemId() {
return pointId;
}

@Override
public int getSeriesItemIndex() {
return seriesIndex;
}

/**
* @return Supported only in Gantt charts: Returns the start date of the
* point at the moment the Drag event was fired.
*/
public Instant getStart() {
return start;
}

/**
* @return Supported only in Gantt charts: Returns the end date of the point
* at the moment the Drag event was fired.
*/
public Instant getEnd() {
return end;
}

/**
* @return Returns the parent id of the point at the moment the Drag event
* was fired.
*/
public String getParent() {
return parent;
}

/**
* @return Returns the x value of the point at the moment the dragging
* started.
*/
public Double getxValue() {
return x;
}

/**
* @return Returns the y value of the point at the moment the dragging
* started.
*/
public Double getyValue() {
return y;
}
}
Loading

0 comments on commit 3656506

Please sign in to comment.