org.dominokit.domino.ui.datatable.DataTable Maven / Gradle / Ivy
package org.dominokit.domino.ui.datatable;
import elemental2.dom.HTMLDivElement;
import elemental2.dom.HTMLTableElement;
import elemental2.dom.HTMLTableSectionElement;
import org.dominokit.domino.ui.datatable.events.OnBeforeDataChangeEvent;
import org.dominokit.domino.ui.datatable.events.TableDataUpdatedEvent;
import org.dominokit.domino.ui.datatable.events.TableEvent;
import org.dominokit.domino.ui.datatable.events.TableEventListener;
import org.dominokit.domino.ui.datatable.model.SearchContext;
import org.dominokit.domino.ui.datatable.store.DataStore;
import org.dominokit.domino.ui.utils.BaseDominoElement;
import org.dominokit.domino.ui.utils.DominoElement;
import org.dominokit.domino.ui.utils.HasSelectionSupport;
import java.util.*;
import java.util.stream.Collectors;
import static java.util.Objects.nonNull;
import static org.dominokit.domino.ui.datatable.DataTableStyles.*;
import static org.jboss.gwt.elemento.core.Elements.*;
public class DataTable extends BaseDominoElement> implements HasSelectionSupport> {
public static final String ANY = "*";
public static final String DATA_TABLE_ROW_FILTERED = "data-table-row-filtered";
private final DataStore dataStore;
private DominoElement root = DominoElement.of(div().css(TABLE_RESPONSIVE));
private DominoElement tableElement = DominoElement.of(table().css(TABLE, TABLE_HOVER, TABLE_STRIPED));
private TableConfig tableConfig;
private DominoElement tbody = DominoElement.of(tbody());
private DominoElement thead = DominoElement.of(thead());
private List data = new ArrayList<>();
private boolean selectable = true;
private List> tableRows = new ArrayList<>();
private List> selectionChangeListeners = new ArrayList<>();
private boolean condensed = false;
private boolean hoverable = true;
private boolean striped = true;
private boolean bordered = false;
private Map> events = new HashMap<>();
private final SearchContext searchContext = new SearchContext<>(this);
public DataTable(TableConfig tableConfig, DataStore dataStore) {
this.tableConfig = tableConfig;
this.events.put(ANY, new ArrayList<>());
this.dataStore = dataStore;
this.addTableEventListner(ANY, dataStore);
this.dataStore.onDataChanged(dataChangedEvent -> {
fireTableEvent(new OnBeforeDataChangeEvent<>(this.data, dataChangedEvent.getTotalCount(), dataChangedEvent.isAppend()));
if (dataChangedEvent.isAppend()) {
appendData(dataChangedEvent.getNewData());
} else {
setData(dataChangedEvent.getNewData());
}
fireTableEvent(new TableDataUpdatedEvent<>(this.data, dataChangedEvent.getTotalCount()));
});
init();
}
private DataTable init() {
tableConfig.getPlugins().forEach(plugin -> {
DataTable.this.addTableEventListner("*", plugin);
plugin.init(DataTable.this);
plugin.onBeforeAddTable(DataTable.this);
});
tableConfig.onBeforeHeaders(this);
tableConfig.drawHeaders(this, thead);
tableConfig.onAfterHeaders(this);
tableElement.appendChild(tbody);
tableConfig.getPlugins().forEach(plugin -> plugin.onBodyAdded(DataTable.this));
root.appendChild(tableElement);
tableConfig.getPlugins().forEach(plugin -> plugin.onAfterAddTable(DataTable.this));
if (!tableConfig.isLazyLoad()) {
this.dataStore.load();
}
if (tableConfig.isFixed()) {
root.style().add(TABLE_FIXED);
tbody.style()
.add(TBODY_FIXED)
.setMaxHeight(tableConfig.getFixedBodyHeight());
}
super.init(this);
return this;
}
public void load() {
this.dataStore.load();
}
public void setData(List data) {
this.data = data;
tableRows.clear();
tbody.clearElement();
if (nonNull(data) && !data.isEmpty()) {
addRows(data, 0);
}
tbody.element().scrollTop = 0.0;
}
public void appendData(List newData) {
if (nonNull(this.data)) {
addRows(newData, this.data.size());
this.data.addAll(newData);
} else {
setData(newData);
}
}
private void addRows(List data, int initialIndex) {
tableConfig.getColumns()
.forEach(ColumnConfig::clearShowHideListners);
for (int index = 0; index < data.size(); index++) {
TableRow tableRow = new TableRow<>(data.get(index), initialIndex + index, this);
tableConfig.getPlugins().forEach(plugin -> plugin.onBeforeAddRow(DataTable.this, tableRow));
tableConfig.drawRecord(DataTable.this, tableRow);
tableRows.add(tableRow);
}
tableConfig.getPlugins().forEach(plugin -> plugin.onAllRowsAdded(DataTable.this));
}
public Collection getData() {
return data;
}
@Override
public DataTable show() {
tableElement.style().remove(TABLE_CONDENSED);
this.condensed = false;
return this;
}
public DataTable condense() {
show();
tableElement.style().add(TABLE_CONDENSED);
this.condensed = true;
return this;
}
public DataTable noHover() {
tableElement.style().remove(TABLE_HOVER);
this.hoverable = false;
return this;
}
public DataTable hovered() {
noHover();
tableElement.style().add(TABLE_HOVER);
this.hoverable = true;
return this;
}
public DataTable noBorder() {
tableElement.style().remove(TABLE_BORDERED);
this.bordered = false;
return this;
}
public DataTable bordered() {
noBorder();
tableElement.style().add(TABLE_BORDERED);
this.bordered = true;
return this;
}
public DataTable noStripes() {
tableElement.style().remove(TABLE_STRIPED);
this.striped = false;
return this;
}
public DataTable striped() {
noStripes();
tableElement.style().add(TABLE_STRIPED);
this.striped = true;
return this;
}
public DominoElement tableElement() {
return tableElement;
}
public DominoElement bodyElement() {
return tbody;
}
public DominoElement headerElement() {
return thead;
}
public TableConfig getTableConfig() {
return tableConfig;
}
public boolean isCondensed() {
return condensed;
}
public boolean isHoverable() {
return hoverable;
}
public boolean isStriped() {
return striped;
}
public boolean isBordered() {
return bordered;
}
public void filterRows(LocalRowFilter rowFilter) {
tableRows.forEach(tableRow -> {
if (rowFilter.filter(tableRow)) {
tableRow.style().remove(TABLE_ROW_FILTERED);
tableRow.removeFlag(DATA_TABLE_ROW_FILTERED);
tableRow.fireUpdate();
} else {
tableRow.style().add(TABLE_ROW_FILTERED);
tableRow.setFlag(DATA_TABLE_ROW_FILTERED, "true");
tableRow.deselect();
tableRow.fireUpdate();
}
});
}
public void clearRowFilters() {
tableRows.stream().filter(tableRow -> nonNull(tableRow.getFlag(DATA_TABLE_ROW_FILTERED)))
.forEach(tableRow -> {
tableRow.style().remove(TABLE_ROW_FILTERED);
tableRow.removeFlag(DATA_TABLE_ROW_FILTERED);
tableRow.fireUpdate();
});
}
@Override
public HTMLDivElement element() {
return root.element();
}
@Override
public List> getSelectedItems() {
return tableRows.stream().filter(TableRow::isSelected).collect(Collectors.toList());
}
public List getSelectedRecords() {
return getSelectedItems().stream().map(TableRow::getRecord).collect(Collectors.toList());
}
@Override
public List> getItems() {
return tableRows;
}
@Override
public void onSelectionChange(TableRow source) {
selectionChangeListeners.forEach(selectionChangeListener -> selectionChangeListener.onSelectionChanged(getSelectedItems(), getSelectedRecords()));
}
@Override
public void selectAll() {
selectAll((table, tableRow) -> true);
}
public void selectAll(SelectionCondition selectionCondition) {
if (tableConfig.isMultiSelect() && !tableRows.isEmpty()) {
for (TableRow tableRow : tableRows) {
if (selectionCondition.isAllowSelection(this, tableRow)) {
tableRow.select();
}
}
onSelectionChange(tableRows.get(0));
}
}
@Override
public void deselectAll() {
deselectAll((table, tableRow) -> true);
}
public void deselectAll(SelectionCondition selectionCondition) {
if (!tableRows.isEmpty()) {
for (TableRow tableRow : tableRows) {
if (tableRow.isSelected()) {
if (selectionCondition.isAllowSelection(this, tableRow)) {
tableRow.deselect();
}
}
}
onSelectionChange(tableRows.get(0));
}
}
@Override
public boolean isSelectable() {
return this.selectable;
}
public void addSelectionListener(SelectionChangeListener selectionChangeListener) {
this.selectionChangeListeners.add(selectionChangeListener);
}
public void removeSelectionListener(SelectionChangeListener selectionChangeListener) {
this.selectionChangeListeners.remove(selectionChangeListener);
}
public void addTableEventListner(String type, TableEventListener listener) {
if (!events.containsKey(type)) {
events.put(type, new ArrayList<>());
}
events.get(type).add(listener);
}
public void removeTableListener(String type, TableEventListener listener) {
if (events.containsKey(type)) {
events.get(type).remove(listener);
}
}
public void fireTableEvent(TableEvent tableEvent) {
if (events.containsKey(tableEvent.getType())) {
events.get(tableEvent.getType()).forEach(listener -> listener.handleEvent(tableEvent));
}
events.get(ANY).forEach(listener -> listener.handleEvent(tableEvent));
}
public SearchContext getSearchContext() {
return searchContext;
}
@FunctionalInterface
public interface SelectionChangeListener {
void onSelectionChanged(List> selectedTableRows, List selectedRecords);
}
public interface LocalRowFilter {
boolean filter(TableRow tableRow);
}
}