When an item is dragged and dropped, an event with an end date will be created. The end date is the start date + 10% of the timeline width. This default algorithm fits the most use cases. Tip: if events are editable, you can adjust the start / end date later by double clicking on an event.
To activate the built-in drag-and-drop feature, simple add p:ajax with event="drop" to the timeline tag.
<div style="float:left;"> <strong>Drag and drop events</strong> <p/> <p:dataList id="eventsList" value="#{dndTimelineController.events}" var="event" itemType="circle"> <h:panelGroup id="eventBox" layout="box" style="z-index:9999; cursor:move;"> #{event.name} </h:panelGroup> <p:draggable for="eventBox" revert="true" helper="clone" cursor="move"/> </p:dataList> </div> <pe:timeline id="timeline" value="#{dndTimelineController.model}" var="event" editable="true" eventMargin="10" eventMarginAxis="0" minHeight="250" start="#{dndTimelineController.start}" end="#{dndTimelineController.end}" timeZone="#{dndTimelineController.localTimeZone}" style="margin-left:135px;" snapEvents="false" showNavigation="true" dropActiveStyleClass="ui-state-highlight" dropHoverStyleClass="ui-state-hover"> <p:ajax event="drop" listener="#{dndTimelineController.onDrop}" global="false" update="eventsList"/> <h:outputText value="#{event.name}"/> ... other properties can be displayed too </pe:timeline>In this example, the objects in the list (on the left side) have properties name, start date, end date.
public class Event implements Serializable { private String name; private Date start; private Date end; // constructors, getter / setter ... @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } Event event = (Event) o; if (name != null ? !name.equals(event.name) : event.name != null) { return false; } return true; } @Override public int hashCode() { return name != null ? name.hashCode() : 0; } }The bean class defines the AJAX listener onDrop. The listener gets an instance of the class TimelineDragDropEvent. Besides start / end date and group, this event object also contains a client ID of the dragged component and dragged model object if draggable item is within a data iteration component.
public class DndTimelineController implements Serializable { private TimelineModel model; private TimeZone localTimeZone = TimeZone.getTimeZone("Europe/Berlin"); private List<Event> events = new ArrayList<Event>(); @PostConstruct protected void initialize() { // create timeline model model = new TimelineModel(); // create available events for drag-&-drop for (int i = 1; i <= 13; i++) { events.add(new Event("Event " + i)); } } public void onDrop(TimelineDragDropEvent e) { // get dragged model object (event class) if draggable item is within a data iteration component, // update event's start / end dates. Event dndEvent = (Event) e.getData(); dndEvent.setStart(e.getStartDate()); dndEvent.setEnd(e.getEndDate()); // create a timeline event (not editable) TimelineEvent event = new TimelineEvent(dndEvent, e.getStartDate(), e.getEndDate(), false, e.getGroup()); // add a new event TimelineUpdater timelineUpdater = TimelineUpdater.getCurrentInstance(":mainForm:timeline"); model.add(event, timelineUpdater); // remove from the list of all events events.remove(dndEvent); FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_INFO, "The " + dndEvent.getName() + " was added", null); FacesContext.getCurrentInstance().addMessage(null, msg); } public TimelineModel getModel() { return model; } public TimeZone getLocalTimeZone() { return localTimeZone; } public List<Event> getEvents() { return events; } public Date getStart() { return start; } public Date getEnd() { return end; } }Please also consider the drag-and-drop related attributes dropHoverStyleClass, dropActiveStyleClass, dropAccept, dropScope. They are similar to corresponding attributes in p:droppable. There is only one difference - the datasource attribute is not needed. p:droppable in PrimeFaces has this attribute which represents an ID of an UIData component to connect with. I have never understood why we need this. It is possible to get the UIData component with a simple trick. The reading below is for advanced developers.
The drop callback from the jQuery Droppable gets an ui object with a reference to the draggable element. So we can look for a closest DOM element which is a container for an UIData component.
var params = []; // Check if draggable is within a data iteration component. // Note for PrimeFaces team: an additional unified style class ".ui-data" for all UIData components would be welcome here! var uiData = ui.draggable.closest(".ui-datatable, .ui-datagrid, .ui-datalist, .ui-carousel"); if (uiData.length > 0) { params.push({ name: this.id + '_uiDataId', value: uiData.attr('id') }); } params.push({ name: this.id + '_dragId', value: ui.draggable.attr('id') }); // call the drop listener this.getBehavior("drop").call(this, evt, {params: params, ...});In the component itself, the current dragged object is extracted by this snippet
FacesContext context = FacesContext.getCurrentInstance(); Map<String, String> params = context.getExternalContext().getRequestParameterMap(); String clientId = this.getClientId(context); Object data = null; String dragId = params.get(clientId + "_dragId"); String uiDataId = params.get(clientId + "_uiDataId"); if (dragId != null && uiDataId != null) { // draggable is within a data iteration component UIDataContextCallback contextCallback = new UIDataContextCallback(dragId); context.getViewRoot().invokeOnComponent(context, uiDataId, contextCallback); data = contextCallback.getData(); } TimelineDragDropEvent te = new TimelineDragDropEvent(this, behaviorEvent.getBehavior(), ... dragId, data);The JSF standard method invokeOnComponent does the job. Now the data object is available in TimelineDragDropEvent. The class UIDataContextCallback is a simple implementation of the JSF ContextCallback interface.
public class UIDataContextCallback implements ContextCallback { private String dragId; private Object data; public UIDataContextCallback(String dragId) { this.dragId = dragId; } public void invokeContextCallback(FacesContext fc, UIComponent component) { UIData uiData = (UIData) component; String[] idTokens = dragId.split(String.valueOf(UINamingContainer.getSeparatorChar(fc))); int rowIndex = Integer.parseInt(idTokens[idTokens.length - 2]); uiData.setRowIndex(rowIndex); data = uiData.getRowData(); uiData.setRowIndex(-1); } public Object getData() { return data; } }
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.