org.dominokit.domino.ui.datatable.TableConfig Maven / Gradle / Ivy
/*
* Copyright © 2019 Dominokit
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dominokit.domino.ui.datatable;
import static java.util.Objects.nonNull;
import static org.dominokit.domino.ui.datatable.ColumnUtils.fixElementWidth;
import static org.jboss.elemento.Elements.*;
import elemental2.dom.*;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import org.dominokit.domino.ui.datatable.plugins.DataTablePlugin;
import org.dominokit.domino.ui.grid.flex.FlexAlign;
import org.dominokit.domino.ui.grid.flex.FlexItem;
import org.dominokit.domino.ui.grid.flex.FlexJustifyContent;
import org.dominokit.domino.ui.grid.flex.FlexLayout;
import org.dominokit.domino.ui.popover.Tooltip;
import org.dominokit.domino.ui.utils.DominoElement;
import org.dominokit.domino.ui.utils.HasMultiSelectionSupport;
/**
* This class is responsible of configuring the data table
*
*
* TableConfig<Contact> tableConfig = new TableConfig<>();
* tableConfig
* .addColumn(ColumnConfig.
*
* @param the type of the data table records
*/
public class TableConfig implements HasMultiSelectionSupport> {
private List> columns = new LinkedList<>();
private List> plugins = new LinkedList<>();
private DataTable dataTable;
private boolean fixed = false;
private String fixedDefaultColumnWidth = "100px";
private String fixedBodyHeight = "400px";
private boolean lazyLoad = true;
private boolean multiSelect = true;
private RowAppender rowAppender =
(dataTable, tableRow) -> dataTable.bodyElement().appendChild(tableRow.element());
private DirtyRecordProvider dirtyRecordProvider = original -> original;
private SaveDirtyRecordHandler saveDirtyRecordHandler = (originalRecord, dirtyRecord) -> {};
private final ColumnConfig pluginUtilityColumn =
ColumnConfig.create("plugin-utility-column")
.setShowTooltip(false)
.setSortable(true)
.setDrawTitle(true)
.setPluginColumn(true)
.setCellRenderer(
cellInfo -> {
DominoElement.of(cellInfo.getElement()).css("dt-cm-utility");
FlexLayout flexLayout =
FlexLayout.create()
.setAlignItems(FlexAlign.CENTER)
.setJustifyContent(FlexJustifyContent.START);
getPlugins().stream()
.map(plugin -> plugin.getUtilityElements(dataTable, cellInfo))
.filter(Optional::isPresent)
.map(Optional::get)
.flatMap(Collection::stream)
.forEach(
node -> {
String order =
Optional.ofNullable(DominoElement.of(node).getAttribute("order"))
.orElse("0");
flexLayout.appendChild(
FlexItem.create()
.setOrder(Integer.parseInt(order))
.setAlignSelf(FlexAlign.CENTER)
.appendChild(node));
});
return flexLayout.element();
});
private UtilityColumnHandler utilityColumnHandler = utilityColumn -> {};
/**
* This method will draw the table columns header elements for all columns and append them to the
* table head element
*
* @param dataTable the {@link DataTable} initialized with this configuration
* @param thead the {@link DominoElement} of {@link HTMLTableSectionElement} that is the table
* header element
*/
public void drawHeaders(DataTable dataTable, DominoElement thead) {
this.dataTable = dataTable;
DominoElement tr = DominoElement.of(tr());
thead.appendChild(tr.element());
columns.forEach(
columnConfig -> {
FlexLayout flexLayout = FlexLayout.create().setAlignItems(FlexAlign.CENTER);
if (columnConfig.isDrawTitle() && nonNull(columnConfig.getTitle())) {
flexLayout.appendChild(
FlexItem.of(DominoElement.div())
.setOrder(50)
.setFlexGrow(1)
.appendChild(
columnConfig
.getHeaderElementSupplier()
.asElement(columnConfig.getTitle())));
}
DominoElement th =
DominoElement.of(th())
.addCss(DataTableStyles.TABLE_CM_HEADER, "dt-cm-utility")
.appendChild(flexLayout);
columnConfig.applyScreenMedia(th.element());
tr.appendChild(th);
columnConfig.setHeadElement(th.element());
columnConfig.setHeaderLayout(flexLayout);
if (dataTable.getTableConfig().isFixed() || columnConfig.isFixed()) {
fixElementWidth(columnConfig, th.element(), fixedDefaultColumnWidth);
}
if (columnConfig.isShowTooltip()) {
Tooltip.create(th.element(), columnConfig.getTooltipNode());
}
columnConfig.applyHeaderStyle();
columnConfig.addShowHideListener(DefaultColumnShowHideListener.of(th.element(), true));
DominoElement.of(th).toggleDisplay(!columnConfig.isHidden());
plugins.forEach(plugin -> plugin.onHeaderAdded(dataTable, columnConfig));
});
dataTable.tableElement().appendChild(thead);
}
/**
* Draw a record as a row in the data table, row information is obtained from the TableRow
*
* @param dataTable the {@link DataTable} initialized with this configuration
* @param tableRow the {@link TableRow} we are adding to the table
*/
public void drawRecord(DataTable dataTable, TableRow tableRow) {
tableRow.render();
tableRow.addCss(isOdd(tableRow.getIndex()) ? "dom-ui-dt-tr-odd" : "dom-ui-dt-tr-even");
rowAppender.appendRow(dataTable, tableRow);
plugins.forEach(plugin -> plugin.onRowAdded(dataTable, tableRow));
}
private boolean isOdd(int index) {
return index % 2 > 0;
}
/**
* Adds a configuration for a column in the data table
*
* @param column {@link ColumnConfig}
* @return same TableConfig instance
*/
public TableConfig addColumn(ColumnConfig column) {
this.columns.add(column);
return this;
}
/**
* Adds a configuration for a column in the data table as the first column over the existing
* columns list
*
* @param column {@link ColumnConfig}
* @return same TableConfig instance
*/
public TableConfig insertColumnFirst(ColumnConfig column) {
this.columns.add(0, column);
return this;
}
/**
* Adds a configuration for a column in the data table as the last column after the existing
* columns list
*
* @param column {@link ColumnConfig}
* @return same TableConfig instance
*/
public TableConfig insertColumnLast(ColumnConfig column) {
this.columns.add(this.columns.size() - 1, column);
return this;
}
/**
* Adds a new plugin to the data table
*
* @param plugin {@link DataTablePlugin}
* @return same TableConfig instance
*/
public TableConfig addPlugin(DataTablePlugin plugin) {
this.plugins.add(plugin);
if (plugin.requiresUtilityColumn() && !columns.contains(pluginUtilityColumn)) {
utilityColumnHandler.handle(pluginUtilityColumn);
insertColumnFirst(pluginUtilityColumn);
}
return this;
}
public TableConfig onUtilityColumn(UtilityColumnHandler utilityColumnHandler) {
this.utilityColumnHandler = utilityColumnHandler;
return this;
}
@FunctionalInterface
public interface UtilityColumnHandler {
void handle(ColumnConfig utilityColumn);
}
/**
* @return boolean, if true then this table will have a fixed width and wont change the columns
* width when resized, otherwise columns will stretch to match the table root element width
*/
public boolean isFixed() {
return fixed;
}
/**
* @param fixed boolean, if true then this table will have a fixed width and wont change the
* columns width when resized, otherwise columns will stretch to match the table root element
* width
* @return same TableConfig instance
*/
public TableConfig setFixed(boolean fixed) {
this.fixed = fixed;
return this;
}
/**
* @return boolean, if true the table will only start loading the data from the data store if load
* is called manually, otherwise it will automatically load the data when it is initialized
*/
public boolean isLazyLoad() {
return lazyLoad;
}
/**
* @param lazyLoad boolean, if true the table will only start loading the data from the data store
* if load is called manually, otherwise it will automatically load the data when it is
* initialized
* @return same TableConfig instance
*/
public TableConfig setLazyLoad(boolean lazyLoad) {
this.lazyLoad = lazyLoad;
return this;
}
/**
* @return String, the height of the data table body, this is the value we set with {@link
* #setFixedBodyHeight(String)} not the actual current table body height
*/
public String getFixedBodyHeight() {
return fixedBodyHeight;
}
/**
* @param fixedBodyHeight boolean, if true the height of the table body will be fixed to the
* specified value and while adding records to the table if the total height of rows exceed
* this height scroll bars will show up, otherwise the table body will not fixed and will grow
* to match the rows height and wont show scrollbars
* @return same TableConfig instance
*/
public TableConfig setFixedBodyHeight(String fixedBodyHeight) {
this.fixedBodyHeight = fixedBodyHeight;
return this;
}
/** @return String default value for a fixed column width */
public String getFixedDefaultColumnWidth() {
return fixedDefaultColumnWidth;
}
/**
* @param fixedDefaultColumnWidth String default value to be used as width for the fixed width
* columns
* @return same TableConfig instance
*/
public TableConfig setFixedDefaultColumnWidth(String fixedDefaultColumnWidth) {
this.fixedDefaultColumnWidth = fixedDefaultColumnWidth;
return this;
}
/** {@inheritDoc} */
@Override
public boolean isMultiSelect() {
return this.multiSelect;
}
/** {@inheritDoc} */
@Override
public TableConfig setMultiSelect(boolean multiSelect) {
this.multiSelect = multiSelect;
return this;
}
/**
* Change the default RowAppender for the data table
*
* @param rowAppender {@link RowAppender}
*/
public void setRowAppender(RowAppender rowAppender) {
if (nonNull(rowAppender)) {
this.rowAppender = rowAppender;
}
}
/** @return the {@link List} of plugins added to the table */
public List> getPlugins() {
return plugins;
}
/**
* Run the {@link DataTablePlugin#onBeforeAddHeaders(DataTable)} for all plugin added to the data
* table
*
* @param dataTable the {@link DataTable} initialized with this configuration
*/
void onBeforeHeaders(DataTable dataTable) {
plugins.forEach(plugin -> plugin.onBeforeAddHeaders(dataTable));
}
/**
* Run the {@link DataTablePlugin#onAfterAddHeaders(DataTable)} for all plugin added to the data
* table
*
* @param dataTable the {@link DataTable} initialized with this configuration
*/
void onAfterHeaders(DataTable dataTable) {
plugins.forEach(plugin -> plugin.onAfterAddHeaders(dataTable));
}
/** @return a {@link List} of all {@link ColumnConfig} added to the table */
public List> getColumns() {
return columns;
}
/** @return a {@link List} of all currently visible {@link ColumnConfig} of the table */
public List> getVisibleColumns() {
return columns.stream().filter(column -> !column.isHidden()).collect(Collectors.toList());
}
/**
* get a column config by the column name
*
* @param name String name of the column
* @return the {@link ColumnConfig} if exists otherwise throw {@link ColumnNofFoundException}
*/
public ColumnConfig getColumnByName(String name) {
Optional> first =
getColumns().stream()
.filter(columnConfig -> columnConfig.getName().equals(name))
.findFirst();
if (first.isPresent()) {
return first.get();
} else {
throw new ColumnNofFoundException(name);
}
}
public TableConfig setUtilityColumnTitle(String title) {
if (nonNull(title)) {
pluginUtilityColumn.setTitle(title);
}
return this;
}
/** @return the {@link DataTable} initialized with this configuration */
public DataTable getDataTable() {
return dataTable;
}
/**
* sets the dirty record handlers for editable tables
*
* @param dirtyRecordProvider {@link DirtyRecordProvider}
* @param saveDirtyRecordHandler {@link SaveDirtyRecordHandler}
* @return same TableConfig istance
*/
public TableConfig setDirtyRecordHandlers(
DirtyRecordProvider dirtyRecordProvider,
SaveDirtyRecordHandler saveDirtyRecordHandler) {
this.dirtyRecordProvider = dirtyRecordProvider;
this.saveDirtyRecordHandler = saveDirtyRecordHandler;
return this;
}
/** @return the {@link DirtyRecordProvider} */
DirtyRecordProvider getDirtyRecordProvider() {
return dirtyRecordProvider;
}
/** @return the {@link SaveDirtyRecordHandler} */
SaveDirtyRecordHandler getSaveDirtyRecordHandler() {
return saveDirtyRecordHandler;
}
/**
* An interface to provide an alternative implementation of how rows should be appended to the
* table
*
* e.g
*
*
The {@link org.dominokit.domino.ui.datatable.plugins.GroupingPlugin} defines an appender
* that appends a row into the appropriate group instead of appending row sequentially
*
* @param the type of the row record
*/
@FunctionalInterface
public interface RowAppender {
/**
* Appends a row to the data table
*
* @param dataTable the {@link DataTable}
* @param tableRow the {@link TableRow} being appended
*/
void appendRow(DataTable dataTable, TableRow tableRow);
}
/**
* This exception is thrown when performing action that looks up a column by its name but the
* column does not exist in the current {@link TableConfig}
*/
public static class ColumnNofFoundException extends RuntimeException {
public ColumnNofFoundException(String name) {
super(name);
}
}
}