All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy