Skip to content

Commit

Permalink
refactor: align event apis with web component
Browse files Browse the repository at this point in the history
  • Loading branch information
ugur-vaadin committed Sep 23, 2024
1 parent e378b08 commit daa359b
Show file tree
Hide file tree
Showing 7 changed files with 507 additions and 191 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

Expand All @@ -24,9 +26,14 @@
import com.vaadin.flow.component.Tag;
import com.vaadin.flow.component.dependency.JsModule;
import com.vaadin.flow.component.dependency.NpmPackage;
import com.vaadin.flow.dom.DomEvent;
import com.vaadin.flow.dom.DomListenerRegistration;
import com.vaadin.flow.dom.Element;
import com.vaadin.flow.shared.Registration;

import elemental.json.JsonArray;
import elemental.json.JsonObject;

/**
* @author Vaadin Ltd
*/
Expand All @@ -49,9 +56,9 @@ public class Dashboard extends Component implements HasWidgets {
*/
public Dashboard() {
childDetachHandler = getChildDetachHandler();
addItemMovedListener(this::onItemMoved);
addItemResizedListener(this::onItemResized);
addItemRemovedListener(this::onItemRemoved);
initItemMovedClientEventListener();
initItemResizedClientEventListener();
initItemRemovedClientEventListener();
}

/**
Expand Down Expand Up @@ -448,44 +455,113 @@ void removeChild(Component child) {
};
}

private void onItemMoved(DashboardItemMovedEvent dashboardItemMovedEvent) {
if (!isEditable()) {
return;
}
reorderItems(dashboardItemMovedEvent.getReorderedItemsParent(),
dashboardItemMovedEvent.getReorderedItems());
updateClient();
}

private void onItemResized(
DashboardItemResizedEvent dashboardItemResizedEvent) {
if (!isEditable()) {
return;
}
DashboardWidget resizedWidget = dashboardItemResizedEvent
.getResizedWidget();
resizedWidget.setRowspan(dashboardItemResizedEvent.getRowspan());
resizedWidget.setColspan(dashboardItemResizedEvent.getColspan());
}

private void onItemRemoved(
DashboardItemRemovedEvent dashboardItemRemovedEvent) {
if (!isEditable()) {
return;
}
dashboardItemRemovedEvent.getRemovedItem().removeFromParent();
}

private void reorderItems(Component reorderedItemParent,
List<Component> items) {
if (reorderedItemParent instanceof DashboardSection parentSection) {
parentSection.removeAll();
items.stream().map(DashboardWidget.class::cast)
.forEach(parentSection::add);
} else {
private void initItemMovedClientEventListener() {
String itemKey = "event.detail.item";
String itemsKey = "event.detail.items";
String sectionKey = "event.detail.section";
getElement().addEventListener("dashboard-item-moved-flow", e -> {
if (!isEditable()) {
return;
}
handleItemMovedClientEvent(e, itemKey, itemsKey, sectionKey);
updateClient();
}).addEventData(itemKey).addEventData(itemsKey)
.addEventData(sectionKey);
}

private void handleItemMovedClientEvent(DomEvent e, String itemKey,
String itemsKey, String sectionKey) {
int itemNodeId = (int) e.getEventData().getNumber(itemKey);
JsonArray itemsNodeIds = e.getEventData().getArray(itemsKey);
Integer sectionNodeId = e.getEventData().hasKey(sectionKey)
? (int) e.getEventData().getNumber(sectionKey)
: null;
DashboardSection section = null;
List<Component> reorderedItems;
if (sectionNodeId == null) {
reorderedItems = getReorderedItemsList(itemsNodeIds, this);
childrenComponents.clear();
childrenComponents.addAll(items);
childrenComponents.addAll(reorderedItems);
} else {
section = getChildren()
.filter(child -> sectionNodeId
.equals(child.getElement().getNode().getId()))
.map(DashboardSection.class::cast).findAny().orElseThrow();
reorderedItems = getReorderedItemsList(
getSectionItems(itemsNodeIds, sectionNodeId), section);
section.removeAll();
reorderedItems.stream().map(DashboardWidget.class::cast)
.forEach(section::add);
}
Component movedItem = reorderedItems.stream().filter(
item -> itemNodeId == item.getElement().getNode().getId())
.findAny().orElseThrow();
fireEvent(new DashboardItemMovedEvent(this, true, movedItem,
getChildren().toList(), section));
}

private void initItemResizedClientEventListener() {
String nodeIdKey = "event.detail.item.nodeid";
String colspanKey = "event.detail.item.colspan";
String rowspanKey = "event.detail.item.rowspan";
getElement().addEventListener("dashboard-item-resized", e -> {
if (!isEditable()) {
return;
}
handleItemResizedClientEvent(e, nodeIdKey, colspanKey, rowspanKey);
updateClient();
}).addEventData(nodeIdKey).addEventData(colspanKey)
.addEventData(rowspanKey);
}

private void handleItemResizedClientEvent(DomEvent e, String nodeIdKey,
String colspanKey, String rowspanKey) {
int nodeId = (int) e.getEventData().getNumber(nodeIdKey);
int colspan = (int) e.getEventData().getNumber(colspanKey);
int rowspan = (int) e.getEventData().getNumber(rowspanKey);
DashboardWidget resizedWidget = getWidgets().stream()
.filter(child -> nodeId == child.getElement().getNode().getId())
.findAny().orElseThrow();
resizedWidget.setRowspan(rowspan);
resizedWidget.setColspan(colspan);
fireEvent(new DashboardItemResizedEvent(this, true, resizedWidget,
getChildren().toList()));
}

private void initItemRemovedClientEventListener() {
String nodeIdKey = "event.detail.item.nodeid";
DomListenerRegistration registration = getElement()
.addEventListener("dashboard-item-removed", e -> {
if (!isEditable()) {
return;
}
handleItemRemovedClientEvent(e, nodeIdKey);
updateClient();
});
registration.addEventData(nodeIdKey);
}

private void handleItemRemovedClientEvent(DomEvent e, String nodeIdKey) {
int nodeId = (int) e.getEventData().getNumber(nodeIdKey);
Component removedItem = getRemovedItem(nodeId);
removedItem.removeFromParent();
fireEvent(new DashboardItemRemovedEvent(this, true, removedItem,
getChildren().toList()));
}

private Component getRemovedItem(int nodeId) {
return getChildren().map(item -> {
if (nodeId == item.getElement().getNode().getId()) {
return item;
}
if (item instanceof DashboardSection section) {
return section.getWidgets().stream()
.filter(sectionItem -> nodeId == sectionItem
.getElement().getNode().getId())
.findAny().orElse(null);
}
return null;
}).filter(Objects::nonNull).findAny().orElseThrow();
}

private void customizeItemMovedEvent() {
Expand All @@ -499,9 +575,46 @@ function mapItems(items) {
}));
}
const flowItemMovedEvent = new CustomEvent('dashboard-item-moved-flow', {
detail: { items: mapItems(e.detail.items), section: e.detail.section?.nodeid }
detail: {
item: e.detail.item.nodeid,
items: mapItems(e.detail.items),
section: e.detail.section?.nodeid
}
});
this.dispatchEvent(flowItemMovedEvent);
});""");
}

private static List<Component> getReorderedItemsList(
JsonArray reorderedItemsFromClient,
Component reorderedItemsParent) {
Objects.requireNonNull(reorderedItemsFromClient);
Map<Integer, Component> nodeIdToItems = reorderedItemsParent
.getChildren()
.collect(Collectors.toMap(
item -> item.getElement().getNode().getId(),
Function.identity()));
List<Component> items = new ArrayList<>();
for (int index = 0; index < reorderedItemsFromClient
.length(); index++) {
int nodeIdFromClient = (int) ((JsonObject) reorderedItemsFromClient
.get(index)).getNumber("nodeid");
items.add(nodeIdToItems.get(nodeIdFromClient));
}
return items;
}

private static JsonArray getSectionItems(JsonArray items,
int sectionNodeId) {
for (int rootLevelIdx = 0; rootLevelIdx < items
.length(); rootLevelIdx++) {
JsonObject item = items.get(rootLevelIdx);
int itemNodeId = (int) item.getNumber("nodeid");
if (sectionNodeId == itemNodeId) {
JsonObject sectionObj = items.get(rootLevelIdx);
return sectionObj.getArray("items");
}
}
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,112 +8,77 @@
*/
package com.vaadin.flow.component.dashboard;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.Optional;

import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.ComponentEvent;
import com.vaadin.flow.component.ComponentEventListener;
import com.vaadin.flow.component.DomEvent;
import com.vaadin.flow.component.EventData;

import elemental.json.JsonArray;
import elemental.json.JsonObject;

/**
* Widget or section moved event of {@link Dashboard}.
*
* @author Vaadin Ltd.
* @see Dashboard#addItemMovedListener(ComponentEventListener)
*/
@DomEvent("dashboard-item-moved-flow")
public class DashboardItemMovedEvent extends ComponentEvent<Dashboard> {

private final List<Component> reorderedItems;
private final Component item;

private final List<Component> items;

private final Component reorderedItemsParent;
private final DashboardSection section;

/**
* Creates a dashboard item moved event.
*
* @param source
* Dashboard that contains the item that was moved
* @param fromClient
* <code>true</code> if the event originated from the client
* side, <code>false</code> otherwise
* {@code true} if the event originated from the client side,
* {@code false} otherwise
* @param item
* The moved item
* @param items
* The ordered items represented by node IDs as a
* {@link JsonArray}
* The root level items of the dashboard
* @param section
* The section that contains the moved item, {@code null} if the
* item is a direct child of the dashboard
*/
public DashboardItemMovedEvent(Dashboard source, boolean fromClient,
@EventData("event.detail.items") JsonArray items,
@EventData("event.detail.section") Integer sectionNodeId) {
Component item, List<Component> items, DashboardSection section) {
super(source, fromClient);
if (sectionNodeId == null) {
reorderedItemsParent = source;
reorderedItems = getReorderedItemsList(items);
} else {
reorderedItemsParent = source.getChildren()
.filter(child -> sectionNodeId
.equals(child.getElement().getNode().getId()))
.map(DashboardSection.class::cast).findAny().orElseThrow();
reorderedItems = getReorderedItemsList(
getSectionItems(items, sectionNodeId));
}
this.item = item;
this.items = items;
this.section = section;
}

/**
* Returns the parent of the reordered items. Either a dashboard or a
* section.
* Returns the moved item
*
* @return the parent of the reordered items
* @return the moved item
*/
public Component getReorderedItemsParent() {
return reorderedItemsParent;
public Component getItem() {
return item;
}

/**
* Returns the list of the reordered item and its sibling items
* Returns the root level items of the dashboard
*
* @return the list of the reordered item and its sibling items
* @return the root level items of the dashboard
*/
public List<Component> getReorderedItems() {
return reorderedItems;
}

private List<Component> getReorderedItemsList(
JsonArray reorderedItemsFromClient) {
Objects.requireNonNull(reorderedItemsFromClient);
Map<Integer, Component> nodeIdToItems = reorderedItemsParent
.getChildren()
.collect(Collectors.toMap(
item -> item.getElement().getNode().getId(),
Function.identity()));
List<Component> items = new ArrayList<>();
for (int index = 0; index < reorderedItemsFromClient
.length(); index++) {
int nodeIdFromClient = (int) ((JsonObject) reorderedItemsFromClient
.get(index)).getNumber("nodeid");
items.add(nodeIdToItems.get(nodeIdFromClient));
}
public List<Component> getItems() {
return items;
}

private static JsonArray getSectionItems(JsonArray items,
int sectionNodeId) {
for (int rootLevelIdx = 0; rootLevelIdx < items
.length(); rootLevelIdx++) {
JsonObject item = items.get(rootLevelIdx);
int itemNodeId = (int) item.getNumber("nodeid");
if (sectionNodeId == itemNodeId) {
JsonObject sectionObj = items.get(rootLevelIdx);
return sectionObj.getArray("items");
}
}
return null;
/**
* Returns the section that contains the moved item, or an empty optional if
* the item is a direct child of the dashboard
*
* @return the section that contains the moved item, or an empty optional if
* the item is a direct child of the dashboard
*/
public Optional<DashboardSection> getSection() {
return Optional.ofNullable(section);
}
}
Loading

0 comments on commit daa359b

Please sign in to comment.