When moving one timeline, the other moves along. Use mouse scroll wheel for zoom-in and zoom-out. A click on an event in the first timeline selects an event in the second timeline (Project A or Project B).
Documentation<script type="text/javascript"> //<![CDATA[ function onrangechange1() { var second = PF('timelineSecondWdgt'); if (second.jq.data("rangeFired")) { second.jq.data("rangeFired", null); return; } var first = PF('timelineFirstWdgt'); var range = first.getVisibleRange(); first.jq.data("rangeFired", true); PF('timelineSecondWdgt').setVisibleRange(range.start, range.end); } function onrangechange2() { var first = PF('timelineFirstWdgt'); if (first.jq.data("rangeFired")) { first.jq.data("rangeFired", null); return; } var second = PF('timelineSecondWdgt'); var range = second.getVisibleRange(); second.jq.data("rangeFired", true); PF('timelineFirstWdgt').setVisibleRange(range.start, range.end); } //]]> </script> <h:form id="form"> <p:growl id="growl" showSummary="true" showDetail="false"> <p:autoUpdate /> </p:growl> <h3 style="margin-top: 0">Events</h3> <p:timeline id="timelineFirst" value="#{linkedTimelinesView.modelFirst}" var="task" height="250px" widgetVar="timelineFirstWdgt"> <p:ajax event="rangechange" process="@none" onstart="onrangechange1(); return false;"/> <p:ajax event="select" listener="#{linkedTimelinesView.onSelect}"/> <h:panelGroup layout="block" rendered="#{not task.period}" style="padding-bottom:5px"> <h:outputText value="#{task.title}"/> </h:panelGroup> <p:graphicImage library="demo" name="#{task.imagePath}" height="26px" /> <h:panelGroup rendered="#{task.period}" style="padding:8px"> <h:outputText value="#{task.title}"/> </h:panelGroup> </p:timeline> <h3>Projects</h3> <p:timeline id="timelineSecond" value="#{linkedTimelinesView.modelSecond}" height="150px" widgetVar="timelineSecondWdgt"> <p:ajax event="rangechange" process="@none" onstart="onrangechange2(); return false;"/> </p:timeline> </h:form>
@Named("linkedTimelinesView") @ViewScoped public class LinkedTimelinesView implements Serializable { private TimelineModel<Task, ?> modelFirst; // model of the first timeline private TimelineModel<String, ?> modelSecond; // model of the second timeline private boolean aSelected; // flag if the project A is selected (for test of select() call on the 2. model) @PostConstruct public void init() { createFirstTimeline(); createSecondTimeline(); } private void createFirstTimeline() { modelFirst = new TimelineModel<>(); modelFirst.add(TimelineEvent.<Task>builder().data(new Task("Mail from boss", "images/timeline/mail.png", false)).startDate(LocalDateTime.of(2015, 8, 22, 17, 30)).build()); modelFirst.add(TimelineEvent.<Task>builder().data(new Task("Call back my boss", "images/timeline/callback.png", false)).startDate(LocalDateTime.of(2015, 8, 23, 23, 0)).build()); modelFirst.add(TimelineEvent.<Task>builder().data(new Task("Travel to Spain", "images/timeline/location.png", false)).startDate(LocalDateTime.of(2015, 8, 24, 21, 45)).build()); modelFirst.add(TimelineEvent.<Task>builder().data(new Task("Do homework", "images/timeline/homework.png", true)).startDate(LocalDate.of(2015, 8, 26)).endDate(LocalDate.of(2015, 9, 2)).build()); modelFirst.add(TimelineEvent.<Task>builder().data(new Task("Create memo", "images/timeline/memo.png", false)).startDate(LocalDate.of(2015, 8, 28)).build()); modelFirst.add(TimelineEvent.<Task>builder().data(new Task("Create report", "images/timeline/report.png", true)).startDate(LocalDate.of(2015, 8, 31)).endDate(LocalDate.of(2015,9, 3)).build()); } private void createSecondTimeline() { modelSecond = new TimelineModel<>(); modelSecond.add(TimelineEvent.<String>builder().data("Project A").startDate(LocalDate.of(2015, 8, 23)).endDate(LocalDate.of(2015, 8, 30)).build()); modelSecond.add(TimelineEvent.<String>builder().data("Project B").startDate(LocalDate.of(2015, 8, 27)).endDate(LocalDate.of(2015, 8, 31)).build()); } public void onSelect(TimelineSelectEvent<?> e) { // get a thread-safe TimelineUpdater instance for the second timeline TimelineUpdater timelineUpdater = TimelineUpdater.getCurrentInstance(":form:timelineSecond"); if (aSelected) { // select project B visually (index in the event's list is 1) timelineUpdater.select("projectB"); } else { // select project A visually (index in the event's list is 0) timelineUpdater.select("projectA"); } aSelected = !aSelected; FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_INFO, "Selected project: " + (aSelected ? "A" : "B"), null); FacesContext.getCurrentInstance().addMessage(null, msg); } public TimelineModel<Task, ?> getModelFirst() { return modelFirst; } public TimelineModel<String, ?> getModelSecond() { return modelSecond; } public class Task implements Serializable { private final String title; private final String imagePath; private final boolean period; public Task(String title, String imagePath, boolean period) { this.title = title; this.imagePath = imagePath; this.period = period; } public String getTitle() { return title; } public String getImagePath() { return imagePath; } public boolean isPeriod() { return period; } } }