From b828edd2b0bb5f32add72467d5bc18147ca38d75 Mon Sep 17 00:00:00 2001 From: "Ahmad K. Bawaneh" Date: Thu, 15 Dec 2022 15:31:08 +0300 Subject: [PATCH] fix: #730, #731, #732, #733, #734, #735 fix pin columns plugin --- .github/workflows/deploy.yaml | 2 +- .../domino/ui/datatable/ColumnConfig.java | 319 +++++++++++++++- .../domino/ui/datatable/ColumnHeaderMeta.java | 52 +++ .../domino/ui/datatable/ColumnMeta.java | 20 + .../domino/ui/datatable/ColumnUtils.java | 48 ++- .../domino/ui/datatable/DataTable.java | 36 +- .../domino/ui/datatable/RowCell.java | 2 - .../domino/ui/datatable/TableConfig.java | 108 +++--- .../domino/ui/datatable/TableRow.java | 14 +- .../datatable/events/ColumnResizedEvent.java | 21 ++ .../datatable/events/TableBorderedEvent.java | 47 +++ .../datatable/plugins/BodyScrollPlugin.java | 12 +- .../datatable/plugins/ColumnFilterMeta.java | 51 +++ .../plugins/ColumnHeaderFilterPlugin.java | 18 +- .../ui/datatable/plugins/DataTablePlugin.java | 20 +- .../ui/datatable/plugins/DragDropPlugin.java | 3 +- .../ui/datatable/plugins/HasPluginConfig.java | 21 ++ .../ui/datatable/plugins/PluginConfig.java | 18 + .../datatable/plugins/ResizeColumnMeta.java | 77 ++++ .../plugins/ResizeColumnsPlugin.java | 190 ++++++---- .../ui/datatable/plugins/SortPlugin.java | 2 +- .../plugins/StickyColumnsPlugin.java | 226 +---------- .../plugins/pincolumns/PinColumnFunction.java | 22 ++ .../plugins/pincolumns/PinColumnMeta.java | 206 ++++++++++ .../plugins/pincolumns/PinColumnsConfig.java | 84 +++++ .../plugins/pincolumns/PinColumnsPlugin.java | 355 ++++++++++++++++++ .../pincolumns/PinElementToColumn.java | 23 ++ .../dominokit/domino/ui/forms/ValueBox.java | 1 - .../dominokit/domino/ui/icons/BaseIcon.java | 12 +- .../domino/ui/menu/AbstractMenu.java | 43 +++ .../domino/ui/utils/BaseDominoElement.java | 4 + .../domino/ui/utils/BodyObserver.java | 39 +- .../domino/ui/utils/ElementUtil.java | 4 + .../ui/public/css/cards/domino-ui-card.css | 4 + .../css/datatable/domino-ui-datatable.css | 143 +++++-- .../ui/public/css/icons/domino-ui-icons.css | 2 + 36 files changed, 1806 insertions(+), 443 deletions(-) create mode 100644 domino-ui/src/main/java/org/dominokit/domino/ui/datatable/ColumnHeaderMeta.java create mode 100644 domino-ui/src/main/java/org/dominokit/domino/ui/datatable/ColumnMeta.java create mode 100644 domino-ui/src/main/java/org/dominokit/domino/ui/datatable/events/TableBorderedEvent.java create mode 100644 domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/ColumnFilterMeta.java create mode 100644 domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/HasPluginConfig.java create mode 100644 domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/PluginConfig.java create mode 100644 domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/ResizeColumnMeta.java create mode 100644 domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/pincolumns/PinColumnFunction.java create mode 100644 domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/pincolumns/PinColumnMeta.java create mode 100644 domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/pincolumns/PinColumnsConfig.java create mode 100644 domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/pincolumns/PinColumnsPlugin.java create mode 100644 domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/pincolumns/PinElementToColumn.java diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index ed3bfc67a..fa5204d5e 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -18,7 +18,7 @@ name: Deploy on: push: - branches: [ master , development] + branches: [ master , development ] jobs: verify: diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/ColumnConfig.java b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/ColumnConfig.java index 9bf1af83d..41b267bda 100644 --- a/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/ColumnConfig.java +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/ColumnConfig.java @@ -17,17 +17,27 @@ import static java.util.Objects.isNull; import static java.util.Objects.nonNull; +import static org.dominokit.domino.ui.datatable.ColumnUtils.fixElementWidth; +import static org.jboss.elemento.Elements.th; import elemental2.dom.HTMLTableCellElement; +import elemental2.dom.HTMLTableRowElement; import elemental2.dom.Node; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; +import java.util.*; +import java.util.function.Consumer; +import java.util.function.Predicate; +import org.dominokit.domino.ui.grid.flex.FlexAlign; import org.dominokit.domino.ui.grid.flex.FlexItem; import org.dominokit.domino.ui.grid.flex.FlexLayout; +import org.dominokit.domino.ui.icons.Icons; +import org.dominokit.domino.ui.icons.MdiIcon; +import org.dominokit.domino.ui.menu.Menu; +import org.dominokit.domino.ui.menu.direction.BestSideUpDownDropDirection; +import org.dominokit.domino.ui.popover.Tooltip; import org.dominokit.domino.ui.utils.DominoElement; import org.dominokit.domino.ui.utils.ScreenMedia; import org.dominokit.domino.ui.utils.TextNode; +import org.jboss.elemento.IsElement; /** * Class to define a column in the data table @@ -50,8 +60,9 @@ public class ColumnConfig { private HeaderElementSupplier headerElementSupplier = columnTitle -> { return FlexLayout.create() + .css("dui-th-title-wrapper") .appendChild( - FlexItem.of(DominoElement.div()) + FlexItem.of(DominoElement.div().css("dui-th-title-text-wrapper")) .setOrder(50) .setFlexGrow(1) .styler(style -> style.setCssProperty("text-indent", "2px")) @@ -75,6 +86,15 @@ public class ColumnConfig { private final List showHideListeners = new ArrayList<>(); private final List permanentHideListeners = new ArrayList<>(); + private final List> subColumns = new ArrayList<>(); + private ColumnConfig parent; + + private final Map columnMeta = new HashMap<>(); + + private final Menu menu; + private MdiIcon menuIcon; + private FlexLayout flexLayout; + /** * Creates an instance with a name which will also be used as a title * @@ -107,6 +127,13 @@ public static ColumnConfig create(String name, String title) { public ColumnConfig(String name, String title) { this.name = name; this.title = title; + menuIcon = Icons.ALL.dots_vertical_mdi(); + this.menu = + Menu.create() + .setTargetElement(menuIcon) + .setDropDirection(new BestSideUpDownDropDirection()) + .addItemAddedHandler(menuItem -> menuIcon.show()); + menuIcon.hide(); } /** @@ -715,6 +742,290 @@ public ColumnConfig setDrawTitle(boolean drawTitle) { return this; } + /** + * Adds a configuration for a column in the data table + * + * @param column {@link ColumnConfig} + * @return same TableConfig instance + */ + public ColumnConfig addColumn(ColumnConfig column) { + column.parent = this; + column.applyMeta(ColumnHeaderMeta.create()); + this.subColumns.add(column); + return this; + } + + public boolean isColumnGroup() { + return !this.subColumns.isEmpty(); + } + + public int getColumnsCount() { + if (!isColumnGroup()) { + return 1; + } + return this.subColumns.stream().map(ColumnConfig::getColumnsCount).reduce(0, Integer::sum); + } + + public int getColumnsDepth() { + if (!isColumnGroup()) { + return getGroupLevel(); + } + return this.subColumns.stream().mapToInt(ColumnConfig::getColumnsDepth).max().orElse(0); + } + + public int getGroupLevel() { + if (isNull(parent)) { + return 0; + } + return 1 + parent.getGroupLevel(); + } + + private int getColSpan() { + if (!isColumnGroup()) { + return 1; + } + return subColumns.stream().mapToInt(ColumnConfig::getColSpan).sum(); + } + + public List> flattenColumns() { + List> cols = new ArrayList<>(); + cols.add(this); + if (isColumnGroup()) { + subColumns.forEach(col -> cols.addAll(col.flattenColumns())); + } + return cols; + } + + public List> leafColumns() { + List> cols = new ArrayList<>(); + if (!isColumnGroup()) { + cols.add(this); + } else { + subColumns.forEach(col -> cols.addAll(col.leafColumns())); + } + return cols; + } + + public List> getSubColumns() { + return subColumns; + } + + public boolean hasParent() { + return nonNull(parent); + } + + public ColumnConfig applyMeta(ColumnMeta meta) { + columnMeta.put(meta.getKey(), meta); + return this; + } + + public Optional getMeta(String key) { + return Optional.ofNullable((C) columnMeta.get(key)); + } + + public ColumnConfig removeMeta(String key) { + columnMeta.remove(key); + return this; + } + + void renderHeader( + DataTable dataTable, TableConfig tableConfig, HTMLTableRowElement[] headers) { + int depth = getColumnsDepth(); + int startIndex = headers.length - 1 - depth; + + if (startIndex == 0) { + DominoElement.of(headers[0]).appendChild(createColumnElement(tableConfig)); + } else { + DominoElement fillHeader = + createColumnElement(tableConfig) + .clearElement() + .apply(self -> self.setAttribute("rowspan", startIndex + "")); + ColumnHeaderMeta.get(this) + .ifPresent(columnHeaderMeta -> columnHeaderMeta.addExtraHeadElement(fillHeader)); + DominoElement.of(headers[0]).appendChild(fillHeader); + DominoElement.of(headers[startIndex]).appendChild(createColumnElement(tableConfig)); + } + + if (isColumnGroup()) { + renderChildColumns(dataTable, tableConfig, headers, startIndex + 1); + } + tableConfig.getPlugins().forEach(plugin -> plugin.onHeaderAdded(dataTable, this)); + } + + private void renderChildColumns( + DataTable dataTable, + TableConfig tableConfig, + HTMLTableRowElement[] headers, + int startIndex) { + getSubColumns() + .forEach( + col -> { + if (col.isColumnGroup()) { + DominoElement.of(headers[startIndex]) + .appendChild( + col.createColumnElement(tableConfig) + .apply( + self -> { + if (startIndex < (headers.length - col.getColumnsDepth())) { + int diff = headers.length - col.getColumnsDepth() - 1; + self.setAttribute("rowspan", diff + 1 + ""); + } + })); + col.renderChildColumns(dataTable, tableConfig, headers, startIndex + 1); + } else { + int index = headers.length - 1; + if (index > startIndex) { + int diff = startIndex - index; + DominoElement fillHeader = + col.createColumnElement(tableConfig) + .clearElement() + .setAttribute("rowspan", diff + ""); + ColumnHeaderMeta.get(col) + .ifPresent( + columnHeaderMeta -> columnHeaderMeta.addExtraHeadElement(fillHeader)); + DominoElement.of(headers[startIndex]).appendChild(fillHeader); + } + DominoElement.of(headers[index]).appendChild(col.createColumnElement(tableConfig)); + tableConfig.getPlugins().forEach(plugin -> plugin.onHeaderAdded(dataTable, col)); + } + }); + } + + private DominoElement createColumnElement(TableConfig tableConfig) { + + flexLayout = FlexLayout.create().setAlignItems(FlexAlign.CENTER); + if (isDrawTitle() && nonNull(getTitle())) { + flexLayout.appendChild( + FlexItem.of(DominoElement.div()) + .setOrder(50) + .appendChild(getHeaderElementSupplier().asElement(getTitle()))); + + flexLayout.appendChild( + FlexItem.of(DominoElement.div().css("dui-th-filler")).setOrder(60).setFlexGrow(1)); + } + + flexLayout.appendChild(FlexItem.of(menuIcon.size18().clickable().removeWaves()).setOrder(9980)); + + DominoElement th = + DominoElement.of(th().attr("colspan", getColSpan() + "")) + .addCss(DataTableStyles.TABLE_CM_HEADER) + .appendChild(flexLayout); + + if (isColumnGroup()) { + th.addCss("dui-column-group"); + } + + applyScreenMedia(th.element()); + + setHeadElement(th.element()); + setHeaderLayout(flexLayout); + if (tableConfig.isFixed() || isFixed()) { + fixElementWidth(this, th.element()); + } + + if (isShowTooltip()) { + Tooltip.create(th.element(), getTooltipNode()); + } + applyHeaderStyle(); + addShowHideListener(DefaultColumnShowHideListener.of(th.element(), true)); + DominoElement.of(th).toggleDisplay(!isHidden()); + return th; + } + + public ColumnConfig applyAndOnSubColumns(Consumer> handler) { + handler.accept(this); + if (isColumnGroup()) { + getSubColumns().forEach(col -> col.applyAndOnSubColumns(handler)); + } + return this; + } + + public ColumnConfig applyAndOnSubColumns( + Predicate> predicate, Consumer> handler) { + handler.accept(this); + if (isColumnGroup()) { + getSubColumns().forEach(col -> col.applyAndOnSubColumns(handler)); + } + return this; + } + + public ColumnConfig applyAndOnFirstSubColumn(Consumer> handler) { + handler.accept(this); + if (isColumnGroup()) { + getSubColumns().get(0).applyAndOnFirstSubColumn(handler); + } + return this; + } + + public ColumnConfig onFirstSubColumn(Consumer> handler) { + if (isColumnGroup()) { + getSubColumns().get(0).applyAndOnFirstSubColumn(handler); + } + return this; + } + + public ColumnConfig applyAndOnLastSubColumn(Consumer> handler) { + handler.accept(this); + if (isColumnGroup()) { + getSubColumns().get(getSubColumns().size() - 1).applyAndOnLastSubColumn(handler); + } + return this; + } + + public ColumnConfig onEachLastSubColumn(Consumer> handler) { + if (isColumnGroup()) { + getSubColumns().get(getSubColumns().size() - 1).applyAndOnLastSubColumn(handler); + } + return this; + } + + public ColumnConfig applyAndOnParents(Consumer> handler) { + handler.accept(this); + if (this.hasParent()) { + getParent().applyAndOnParents(handler); + } + return this; + } + + public ColumnConfig getGrandParent() { + if (hasParent()) { + return getParent().getGrandParent(); + } + return this; + } + + public ColumnConfig getLastGrandSiblingColumn() { + if (!isColumnGroup()) { + return this; + } + return this.getSubColumns().get(this.getSubColumns().size() - 1).getLastGrandSiblingColumn(); + } + + public ColumnConfig getFirstGrandSiblingColumn() { + if (!isColumnGroup()) { + return this; + } + return this.getSubColumns().get(0).getFirstGrandSiblingColumn(); + } + + public Menu getMenu() { + return menu; + } + + public ColumnConfig getParent() { + return parent; + } + + public ColumnConfig appendChild(IsElement element) { + flexLayout.appendChild(FlexItem.of(element)); + return this; + } + + public ColumnConfig appendChild(FlexItem element) { + flexLayout.appendChild(element); + return this; + } + /** * A hook interface to style a cell being rendered on the table * diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/ColumnHeaderMeta.java b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/ColumnHeaderMeta.java new file mode 100644 index 000000000..3ed1b8138 --- /dev/null +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/ColumnHeaderMeta.java @@ -0,0 +1,52 @@ +/* + * 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 elemental2.dom.HTMLTableCellElement; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import org.dominokit.domino.ui.utils.DominoElement; + +public class ColumnHeaderMeta implements ColumnMeta { + + public static final String DOMINO_COLUMN_HEADER_META = "domino-column-header-meta"; + + private List> extraHeadElements = new ArrayList<>(); + + public static ColumnHeaderMeta create() { + return new ColumnHeaderMeta(); + } + + @Override + public String getKey() { + return DOMINO_COLUMN_HEADER_META; + } + + public static Optional get(ColumnConfig column) { + return column.getMeta(DOMINO_COLUMN_HEADER_META); + } + + public List> getExtraHeadElements() { + return extraHeadElements; + } + + public ColumnHeaderMeta addExtraHeadElement( + DominoElement extraHeadElement) { + this.extraHeadElements.add(extraHeadElement); + return this; + } +} diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/ColumnMeta.java b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/ColumnMeta.java new file mode 100644 index 000000000..b7e7e758c --- /dev/null +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/ColumnMeta.java @@ -0,0 +1,20 @@ +/* + * 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; + +public interface ColumnMeta { + String getKey(); +} diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/ColumnUtils.java b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/ColumnUtils.java index 300d3263f..33bbb8ff9 100644 --- a/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/ColumnUtils.java +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/ColumnUtils.java @@ -18,36 +18,52 @@ import static java.util.Objects.nonNull; import elemental2.dom.HTMLElement; +import java.util.Optional; import org.dominokit.domino.ui.style.Style; +import org.dominokit.domino.ui.utils.DominoElement; public class ColumnUtils { - public static void fixElementWidth( - ColumnConfig column, HTMLElement element, String fixedDefaultColumnWidth) { - String fixedWidth = bestFitWidth(column, fixedDefaultColumnWidth); - Style.of(element) - .setWidth(fixedWidth) - .setMinWidth(fixedWidth) - .setMaxWidth(fixedWidth) - .addCss(DataTableStyles.FIXED_WIDTH); + public static void fixElementWidth(ColumnConfig column, HTMLElement element) { + bestFitWidth(column) + .ifPresent( + fixedWidth -> + Style.of(element) + .setWidth(fixedWidth) + .setMinWidth(fixedWidth) + .setMaxWidth(fixedWidth) + .addCss(DataTableStyles.FIXED_WIDTH)); + ; + } + + public static void fixElementWidth(DataTable table, HTMLElement element) { + + TableConfig config = table.getTableConfig(); + if (nonNull(config.getWidth()) && !config.getWidth().isEmpty()) { + DominoElement.of(element).setWidth(config.getWidth()); + } + if (nonNull(config.getMinWidth()) && !config.getMinWidth().isEmpty()) { + DominoElement.of(element).setMinWidth(config.getMinWidth()); + } + if (nonNull(config.getMaxWidth()) && !config.getMaxWidth().isEmpty()) { + DominoElement.of(element).setMaxWidth(config.getMaxWidth()); + } } /** * @param columnConfig String value of preferred width to be used for a column from its width. - * min-width, max-width or default fixedDefaultColumnWidth - * @param fixedDefaultColumnWidth + * min-width, max-width or default none * @return same TableConfig instance */ - private static String bestFitWidth( - ColumnConfig columnConfig, String fixedDefaultColumnWidth) { + private static Optional bestFitWidth(ColumnConfig columnConfig) { if (nonNull(columnConfig.getWidth()) && !columnConfig.getWidth().isEmpty()) { - return columnConfig.getWidth(); + return Optional.of(columnConfig.getWidth()); } else if (nonNull(columnConfig.getMinWidth()) && !columnConfig.getMinWidth().isEmpty()) { - return columnConfig.getMinWidth(); + return Optional.of(columnConfig.getMinWidth()); } else if (nonNull(columnConfig.getMaxWidth()) && !columnConfig.getMaxWidth().isEmpty()) { - return columnConfig.getMaxWidth(); + return Optional.of(columnConfig.getMaxWidth()); } else { - return fixedDefaultColumnWidth; + return Optional.empty(); } } } diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/DataTable.java b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/DataTable.java index 0294da442..ca94c0dda 100644 --- a/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/DataTable.java +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/DataTable.java @@ -20,13 +20,10 @@ import static org.dominokit.domino.ui.datatable.DataTableStyles.TABLE; import static org.dominokit.domino.ui.datatable.DataTableStyles.TABLE_BORDERED; import static org.dominokit.domino.ui.datatable.DataTableStyles.TABLE_CONDENSED; -import static org.dominokit.domino.ui.datatable.DataTableStyles.TABLE_FIXED; import static org.dominokit.domino.ui.datatable.DataTableStyles.TABLE_HOVER; import static org.dominokit.domino.ui.datatable.DataTableStyles.TABLE_RESPONSIVE; import static org.dominokit.domino.ui.datatable.DataTableStyles.TABLE_ROW_FILTERED; import static org.dominokit.domino.ui.datatable.DataTableStyles.TABLE_STRIPED; -import static org.dominokit.domino.ui.datatable.DataTableStyles.TBODY_FIXED; -import static org.dominokit.domino.ui.datatable.DataTableStyles.THEAD_FIXED; import static org.dominokit.domino.ui.style.Unit.px; import static org.jboss.elemento.Elements.div; import static org.jboss.elemento.Elements.table; @@ -44,11 +41,7 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; -import org.dominokit.domino.ui.datatable.events.DataSortEvent; -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.events.*; import org.dominokit.domino.ui.datatable.model.SearchContext; import org.dominokit.domino.ui.datatable.store.DataStore; import org.dominokit.domino.ui.utils.BaseDominoElement; @@ -72,7 +65,7 @@ public class DataTable extends BaseDominoElement 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); + DominoElement.of(table()).css(TABLE, TABLE_HOVER, TABLE_STRIPED, "table-width-full"); private TableConfig tableConfig; private DominoElement tbody = DominoElement.of(tbody()); private DominoElement thead = DominoElement.of(thead()); @@ -149,6 +142,7 @@ private DataTable init() { plugin.init(DataTable.this); plugin.onBeforeAddTable(DataTable.this); }); + thead.clearElement(); tableConfig.onBeforeHeaders(this); tableConfig.drawHeaders(this, thead); tableConfig.onAfterHeaders(this); @@ -161,9 +155,7 @@ private DataTable init() { } if (tableConfig.isFixed()) { - root.addCss(TABLE_FIXED); - thead.addCss(THEAD_FIXED); - tbody.addCss(TBODY_FIXED).setMaxHeight(tableConfig.getFixedBodyHeight()); + tableElement.setMaxHeight(tableConfig.getFixedBodyHeight()); tableElement.addEventListener(EventType.scroll, e -> updateTableWidth()); EventListener resizeListener = e -> { @@ -188,6 +180,15 @@ private DataTable init() { }); }); + if (tableConfig.isFixed()) { + tableElement.removeCss("table-width-full"); + root.addCss("table-fixed"); + ColumnUtils.fixElementWidth(this, tableElement.element()); + } + + if (tableConfig.isFixed()) { + ColumnUtils.fixElementWidth(this, tableElement.element()); + } return this; } @@ -203,9 +204,6 @@ private void updateTableWidth() { tableElement.element().offsetWidth + Math.round(tableElement.element().scrollLeft); thead.setWidth(px.of(w - 2)); tbody.setWidth(px.of(w - 2)); - if (tableConfig.isFixed()) { - updateHeadWidth(false); - } } /** Force loading the data into the table */ @@ -238,10 +236,6 @@ private void updateHeadWidth(boolean scrollTop) { if (scrollTop) { tbody.element().scrollTop = 0.0; } - if (tableConfig.isFixed()) { - thead.setWidth(px.of(tbody.element().offsetWidth - getScrollWidth())); - tbody.setWidth(px.of(tbody.element().offsetWidth)); - } } }); }); @@ -361,7 +355,9 @@ public DataTable hovered() { */ public DataTable noBorder() { tableElement.removeCss(TABLE_BORDERED); + removeCss(TABLE_BORDERED); this.bordered = false; + fireTableEvent(new TableBorderedEvent(false)); return this; } @@ -373,7 +369,9 @@ public DataTable noBorder() { public DataTable bordered() { noBorder(); tableElement.addCss(TABLE_BORDERED); + addCss(TABLE_BORDERED); this.bordered = true; + fireTableEvent(new TableBorderedEvent(true)); return this; } diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/RowCell.java b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/RowCell.java index 4273ba07d..3bb899ea7 100644 --- a/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/RowCell.java +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/RowCell.java @@ -60,12 +60,10 @@ public void updateCell() { cellElement.clearElement(); if (nonNull(columnConfig.getMinWidth())) { - cellElement.setMinWidth(columnConfig.getMinWidth()); columnConfig.getHeadElement().style().setMinWidth(columnConfig.getMinWidth()); } if (nonNull(columnConfig.getMaxWidth())) { - cellElement.setMaxWidth(columnConfig.getMaxWidth()); columnConfig.getHeadElement().style().setMaxWidth(columnConfig.getMaxWidth()); } diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/TableConfig.java b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/TableConfig.java index babf00787..505523867 100644 --- a/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/TableConfig.java +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/TableConfig.java @@ -16,26 +16,22 @@ 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.*; import java.util.stream.Collectors; import org.dominokit.domino.ui.datatable.plugins.DataTablePlugin; +import org.dominokit.domino.ui.datatable.plugins.ResizeColumnMeta; 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 + * This class is responsible for configuring the data table * *
  *     TableConfig<Contact> tableConfig = new TableConfig<>();
@@ -65,7 +61,7 @@
 public class TableConfig implements HasMultiSelectionSupport> {
 
   private List> columns = new LinkedList<>();
-  private List> plugins = new LinkedList<>();
+  private List> plugins = new ArrayList<>();
   private DataTable dataTable;
   private boolean fixed = false;
   private String fixedDefaultColumnWidth = "100px";
@@ -77,6 +73,10 @@ public class TableConfig implements HasMultiSelectionSupport>
   private DirtyRecordProvider dirtyRecordProvider = original -> original;
   private SaveDirtyRecordHandler saveDirtyRecordHandler = (originalRecord, dirtyRecord) -> {};
 
+  private String width;
+  private String maxWidth;
+  private String minWidth;
+
   private final ColumnConfig pluginUtilityColumn =
       ColumnConfig.create("plugin-utility-column")
           .setShowTooltip(false)
@@ -84,6 +84,7 @@ public class TableConfig implements HasMultiSelectionSupport>
           .setDrawTitle(true)
           .setPluginColumn(true)
           .setWidth("100px")
+          .applyMeta(ResizeColumnMeta.create().setResizable(false))
           .setCellRenderer(
               cellInfo -> {
                 DominoElement.of(cellInfo.getElement()).css("dt-cm-utility");
@@ -121,46 +122,18 @@ public class TableConfig implements HasMultiSelectionSupport>
    */
   public void drawHeaders(DataTable dataTable, DominoElement thead) {
     this.dataTable = dataTable;
-    thead.clearElement();
-    DominoElement tr = DominoElement.of(tr());
-    thead.appendChild(tr.element());
+
+    int maxDepth = columns.stream().mapToInt(ColumnConfig::getColumnsDepth).max().orElse(0);
+
+    HTMLTableRowElement[] headers = new HTMLTableRowElement[maxDepth + 1];
+    for (int i = 0; i < headers.length; i++) {
+      headers[i] = tr().element();
+      thead.appendChild(headers[i]);
+    }
 
     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));
+        col -> {
+          col.renderHeader(dataTable, this, headers);
         });
     if (!thead.isAttached()) {
       dataTable.tableElement().appendChild(thead);
@@ -192,6 +165,7 @@ private boolean isOdd(int index) {
    * @return same TableConfig instance
    */
   public TableConfig addColumn(ColumnConfig column) {
+    column.applyMeta(ColumnHeaderMeta.create());
     this.columns.add(column);
     return this;
   }
@@ -367,8 +341,19 @@ void onAfterHeaders(DataTable dataTable) {
     plugins.forEach(plugin -> plugin.onAfterAddHeaders(dataTable));
   }
 
-  /** @return a {@link List} of all {@link ColumnConfig} added to the table */
+  /** @return a {@link List} of all non grouping {@link ColumnConfig} added to the table */
   public List> getColumns() {
+    return columns.stream().flatMap(col -> col.leafColumns().stream()).collect(Collectors.toList());
+  }
+
+  /** @return a {@link List} of all {@link ColumnConfig} added to the table */
+  public List> getFlattenColumns() {
+    return columns.stream()
+        .flatMap(col -> col.flattenColumns().stream())
+        .collect(Collectors.toList());
+  }
+
+  public List> getColumnsGrouped() {
     return columns;
   }
 
@@ -385,7 +370,7 @@ public List> getVisibleColumns() {
    */
   public ColumnConfig getColumnByName(String name) {
     Optional> first =
-        getColumns().stream()
+        getFlattenColumns().stream()
             .filter(columnConfig -> columnConfig.getName().equals(name))
             .findFirst();
     if (first.isPresent()) {
@@ -423,6 +408,33 @@ public TableConfig setDirtyRecordHandlers(
     return this;
   }
 
+  public String getWidth() {
+    return width;
+  }
+
+  public TableConfig setWidth(String width) {
+    this.width = width;
+    return this;
+  }
+
+  public String getMaxWidth() {
+    return maxWidth;
+  }
+
+  public TableConfig setMaxWidth(String maxWidth) {
+    this.maxWidth = maxWidth;
+    return this;
+  }
+
+  public String getMinWidth() {
+    return minWidth;
+  }
+
+  public TableConfig setMinWidth(String minWidth) {
+    this.minWidth = minWidth;
+    return this;
+  }
+
   /** @return the {@link DirtyRecordProvider} */
   DirtyRecordProvider getDirtyRecordProvider() {
     return dirtyRecordProvider;
diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/TableRow.java b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/TableRow.java
index 0f6790728..0d6960747 100644
--- a/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/TableRow.java
+++ b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/TableRow.java
@@ -17,7 +17,6 @@
 
 import static java.util.Objects.isNull;
 import static java.util.Objects.nonNull;
-import static org.dominokit.domino.ui.datatable.ColumnUtils.fixElementWidth;
 import static org.jboss.elemento.Elements.*;
 
 import elemental2.dom.HTMLTableCellElement;
@@ -300,11 +299,6 @@ public void renderCell(ColumnConfig columnConfig) {
       cellElement = DominoElement.of(td()).css("dt-td-cell").element();
     }
 
-    if (dataTable.getTableConfig().isFixed() || columnConfig.isFixed()) {
-      fixElementWidth(
-          columnConfig, cellElement, dataTable.getTableConfig().getFixedDefaultColumnWidth());
-    }
-
     RowCell rowCell =
         new RowCell<>(new CellRenderer.CellInfo<>(this, cellElement), columnConfig);
     rowCell.updateCell();
@@ -316,7 +310,15 @@ public void renderCell(ColumnConfig columnConfig) {
     if (columnConfig.isHidden()) {
       DominoElement.of(cellElement).hide();
     }
+    dataTable
+        .getTableConfig()
+        .getPlugins()
+        .forEach(plugin -> plugin.onBeforeAddCell(dataTable, this, rowCell));
     element().appendChild(cellElement);
+    dataTable
+        .getTableConfig()
+        .getPlugins()
+        .forEach(plugin -> plugin.onAfterAddCell(dataTable, this, rowCell));
     columnConfig.addShowHideListener(DefaultColumnShowHideListener.of(cellElement));
   }
 
diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/events/ColumnResizedEvent.java b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/events/ColumnResizedEvent.java
index 43e0ed74a..9dbcd802b 100644
--- a/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/events/ColumnResizedEvent.java
+++ b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/events/ColumnResizedEvent.java
@@ -15,9 +15,30 @@
  */
 package org.dominokit.domino.ui.datatable.events;
 
+import org.dominokit.domino.ui.datatable.ColumnConfig;
+
 /** This event will be fired when a column gets resized */
 public class ColumnResizedEvent implements TableEvent {
   public static final String COLUMN_RESIZED = "column-resized";
+  private final ColumnConfig column;
+  private final double sizeDiff;
+
+  public static ColumnResizedEvent of(ColumnConfig column, double sizeDiff) {
+    return new ColumnResizedEvent(column, sizeDiff);
+  }
+
+  public ColumnResizedEvent(ColumnConfig column, double sizeDiff) {
+    this.column = column;
+    this.sizeDiff = sizeDiff;
+  }
+
+  public ColumnConfig getColumn() {
+    return column;
+  }
+
+  public double getSizeDiff() {
+    return sizeDiff;
+  }
 
   @Override
   public String getType() {
diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/events/TableBorderedEvent.java b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/events/TableBorderedEvent.java
new file mode 100644
index 000000000..9bdafabd0
--- /dev/null
+++ b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/events/TableBorderedEvent.java
@@ -0,0 +1,47 @@
+/*
+ * 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.events;
+
+import org.dominokit.domino.ui.datatable.DataTable;
+
+/**
+ * This event will be fired when the date table bordered is changed
+ *
+ * 

{@link DataTable#bordered()} + * + *

{@link DataTable#noBorder()} + */ +public class TableBorderedEvent implements TableEvent { + + /** A constant string to define a unique type for this event */ + public static final String TABLE_BORDERED_EVENT = "table-bordered-event"; + + private final boolean bordered; + + public TableBorderedEvent(boolean bordered) { + this.bordered = bordered; + } + + public boolean isBordered() { + return bordered; + } + + /** {@inheritDoc} */ + @Override + public String getType() { + return TABLE_BORDERED_EVENT; + } +} diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/BodyScrollPlugin.java b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/BodyScrollPlugin.java index 4e1e36f81..aea50e886 100644 --- a/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/BodyScrollPlugin.java +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/BodyScrollPlugin.java @@ -16,7 +16,7 @@ package org.dominokit.domino.ui.datatable.plugins; import elemental2.core.JsMath; -import elemental2.dom.HTMLTableSectionElement; +import elemental2.dom.HTMLTableElement; import org.dominokit.domino.ui.datatable.DataTable; import org.dominokit.domino.ui.datatable.events.BodyScrollEvent; @@ -31,16 +31,16 @@ public class BodyScrollPlugin implements DataTablePlugin { /** {@inheritDoc} */ @Override public void onBodyAdded(DataTable dataTable) { - HTMLTableSectionElement tbody = dataTable.bodyElement().element(); - tbody.addEventListener( + HTMLTableElement scrollElement = dataTable.tableElement().element(); + scrollElement.addEventListener( "scroll", evt -> { - double scrollTop = new Double(tbody.scrollTop).intValue(); + double scrollTop = new Double(scrollElement.scrollTop).intValue(); if (scrollTop == 0) { dataTable.fireTableEvent(new BodyScrollEvent(ScrollPosition.TOP)); } - int offsetHeight = new Double(tbody.offsetHeight).intValue(); - int scrollHeight = new Double(tbody.scrollHeight).intValue(); + int offsetHeight = new Double(scrollElement.offsetHeight).intValue(); + int scrollHeight = new Double(scrollElement.scrollHeight).intValue(); if (JsMath.abs(offsetHeight) + JsMath.abs(scrollTop) == new Double(scrollHeight).intValue()) { diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/ColumnFilterMeta.java b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/ColumnFilterMeta.java new file mode 100644 index 000000000..7c3acde92 --- /dev/null +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/ColumnFilterMeta.java @@ -0,0 +1,51 @@ +/* + * 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.plugins; + +import elemental2.dom.HTMLTableCellElement; +import java.util.Optional; +import org.dominokit.domino.ui.datatable.ColumnConfig; +import org.dominokit.domino.ui.datatable.ColumnMeta; +import org.dominokit.domino.ui.utils.DominoElement; + +public class ColumnFilterMeta implements ColumnMeta { + + public static final String DOMINO_COLUMN_HEADER_FILTER_META = "domino-column-header-filter-meta"; + + private ColumnHeaderFilterPlugin.HeaderFilter headerFilter; + private DominoElement headerElement; + + public ColumnFilterMeta(ColumnHeaderFilterPlugin.HeaderFilter headerFilter) { + this.headerFilter = headerFilter; + } + + public static ColumnFilterMeta of(ColumnHeaderFilterPlugin.HeaderFilter headerFilter) { + return new ColumnFilterMeta<>(headerFilter); + } + + @Override + public String getKey() { + return DOMINO_COLUMN_HEADER_FILTER_META; + } + + public static Optional> get(ColumnConfig column) { + return column.getMeta(DOMINO_COLUMN_HEADER_FILTER_META); + } + + public ColumnHeaderFilterPlugin.HeaderFilter getHeaderFilter() { + return headerFilter; + } +} diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/ColumnHeaderFilterPlugin.java b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/ColumnHeaderFilterPlugin.java index 9e4704367..60399022b 100644 --- a/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/ColumnHeaderFilterPlugin.java +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/ColumnHeaderFilterPlugin.java @@ -79,15 +79,24 @@ public void onAfterAddHeaders(DataTable dataTable) { filtersRowElement.appendChild(th); - if (dataTable.getTableConfig().isFixed() || columnConfig.isFixed()) { + if (columnConfig.isFixed()) { fixElementWidth(columnConfig, th.element()); } + + ColumnFilterMeta.get(columnConfig) + .ifPresent( + meta -> { + meta.getHeaderFilter().init(dataTable.getSearchContext(), columnConfig); + th.add(meta.getHeaderFilter()); + }); if (headerFilters.containsKey(columnConfig.getName())) { headerFilters .get(columnConfig.getName()) .init(dataTable.getSearchContext(), columnConfig); th.add(headerFilters.get(columnConfig.getName())); } + ColumnHeaderMeta.get(columnConfig) + .ifPresent(columnHeaderMeta -> columnHeaderMeta.addExtraHeadElement(th)); columnConfig.addShowHideListener(DefaultColumnShowHideListener.of(th.element(), true)); DominoElement.of(th).toggleDisplay(!columnConfig.isHidden()); @@ -123,7 +132,9 @@ private String bestFitWidth(ColumnConfig columnConfig) { * @param columnName String, the name of the column we are adding the header filter to. * @param headerFilter the {@link HeaderFilter} * @return same instance + * @deprecated use {@link ColumnConfig#applyMeta(ColumnMeta)} and pass {@link ColumnFilterMeta} */ + @Deprecated public ColumnHeaderFilterPlugin addHeaderFilter(String columnName, HeaderFilter headerFilter) { headerFilters.put(columnName, headerFilter); return this; @@ -142,6 +153,11 @@ public DominoElement getFiltersRowElement() { return filtersRowElement; } + @Override + public int order() { + return 110; + } + /** * An interface for implementing HeaderFilters * diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/DataTablePlugin.java b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/DataTablePlugin.java index 8aa080cb3..e741fa327 100644 --- a/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/DataTablePlugin.java +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/DataTablePlugin.java @@ -18,10 +18,7 @@ import elemental2.dom.HTMLElement; import java.util.List; import java.util.Optional; -import org.dominokit.domino.ui.datatable.CellRenderer; -import org.dominokit.domino.ui.datatable.ColumnConfig; -import org.dominokit.domino.ui.datatable.DataTable; -import org.dominokit.domino.ui.datatable.TableRow; +import org.dominokit.domino.ui.datatable.*; import org.dominokit.domino.ui.datatable.events.TableEvent; import org.dominokit.domino.ui.datatable.events.TableEventListener; @@ -33,7 +30,7 @@ * * @param the type of the datatable records */ -public interface DataTablePlugin extends TableEventListener { +public interface DataTablePlugin extends TableEventListener, Comparable> { /** * this method is used to initialise the plugin with the datatable instance @@ -108,6 +105,10 @@ default void onAllRowsAdded(DataTable dataTable) {} */ default void onAfterAddTable(DataTable dataTable) {} + default void onBeforeAddCell(DataTable dataTable, TableRow tableRow, RowCell rowCell) {} + + default void onAfterAddCell(DataTable dataTable, TableRow tableRow, RowCell rowCell) {} + /** {@inheritDoc} */ @Override default void handleEvent(TableEvent event) {} @@ -117,6 +118,15 @@ default boolean requiresUtilityColumn() { return false; } + default int order() { + return 100; + } + + @Override + default int compareTo(DataTablePlugin o) { + return Integer.compare(this.order(), o.order()); + } + /** * @param dataTable {@link DataTable} * @param cellInfo {@link org.dominokit.domino.ui.datatable.CellRenderer.CellInfo} diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/DragDropPlugin.java b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/DragDropPlugin.java index 600e3d2ee..9cc739683 100644 --- a/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/DragDropPlugin.java +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/DragDropPlugin.java @@ -95,7 +95,8 @@ public boolean requiresUtilityColumn() { @Override public Optional> getUtilityElements( DataTable dataTable, CellRenderer.CellInfo cellInfo) { - return Optional.of(Collections.singletonList(dragDropIconSupplier.get().element())); + return Optional.of( + Collections.singletonList(dragDropIconSupplier.get().css("dui-row-dnd-element").element())); } /** {@inheritDoc} */ diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/HasPluginConfig.java b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/HasPluginConfig.java new file mode 100644 index 000000000..3ad251f89 --- /dev/null +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/HasPluginConfig.java @@ -0,0 +1,21 @@ +/* + * 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.plugins; + +public interface HasPluginConfig { + + T setConfig(C config); +} diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/PluginConfig.java b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/PluginConfig.java new file mode 100644 index 000000000..2df01dcc7 --- /dev/null +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/PluginConfig.java @@ -0,0 +1,18 @@ +/* + * 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.plugins; + +public interface PluginConfig {} diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/ResizeColumnMeta.java b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/ResizeColumnMeta.java new file mode 100644 index 000000000..cd2b3a2ea --- /dev/null +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/ResizeColumnMeta.java @@ -0,0 +1,77 @@ +/* + * 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.plugins; + +import java.util.Optional; +import org.dominokit.domino.ui.datatable.ColumnConfig; +import org.dominokit.domino.ui.datatable.ColumnMeta; + +public class ResizeColumnMeta implements ColumnMeta { + + public static final String RESIZE_COLUMN_META = "resize-column-meta"; + + private double initialWidth; + private double startPosition; + private boolean resizable = true; + + public static ResizeColumnMeta create() { + return new ResizeColumnMeta(true); + } + + public static ResizeColumnMeta create(boolean resizable) { + return new ResizeColumnMeta(resizable); + } + + public ResizeColumnMeta(boolean resizable) { + this.resizable = resizable; + } + + public double getInitialWidth() { + return initialWidth; + } + + public ResizeColumnMeta setInitialWidth(double initialWidth) { + this.initialWidth = initialWidth; + return this; + } + + public double getStartPosition() { + return startPosition; + } + + public ResizeColumnMeta setStartPosition(double startPosition) { + this.startPosition = startPosition; + return this; + } + + public boolean isResizable() { + return resizable; + } + + public ResizeColumnMeta setResizable(boolean resizable) { + this.resizable = resizable; + return this; + } + + @Override + public String getKey() { + return RESIZE_COLUMN_META; + } + + public static Optional get(ColumnConfig column) { + return column.getMeta(RESIZE_COLUMN_META); + } +} diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/ResizeColumnsPlugin.java b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/ResizeColumnsPlugin.java index 5f7df3f80..5228d8d5a 100644 --- a/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/ResizeColumnsPlugin.java +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/ResizeColumnsPlugin.java @@ -22,16 +22,10 @@ import elemental2.dom.EventListener; import elemental2.dom.HTMLDivElement; import elemental2.dom.MouseEvent; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; import jsinterop.base.Js; -import org.dominokit.domino.ui.datatable.ColumnConfig; -import org.dominokit.domino.ui.datatable.DataTable; -import org.dominokit.domino.ui.datatable.RowCell; -import org.dominokit.domino.ui.datatable.TableRow; +import org.dominokit.domino.ui.datatable.*; import org.dominokit.domino.ui.datatable.events.ColumnResizedEvent; +import org.dominokit.domino.ui.grid.flex.FlexItem; import org.dominokit.domino.ui.utils.DominoElement; import org.jboss.elemento.EventType; @@ -41,69 +35,137 @@ * @param the type of data table records */ public class ResizeColumnsPlugin implements DataTablePlugin { - private final Map, List>> columnCells = new HashMap<>(); + + @Override + public void init(DataTable dataTable) { + dataTable + .getTableConfig() + .getColumnsGrouped() + .forEach( + column -> + column.applyAndOnSubColumns( + col -> { + if (!ResizeColumnMeta.get(col).isPresent()) { + col.applyMeta(ResizeColumnMeta.create()); + } + })); + } /** {@inheritDoc} */ @Override public void onHeaderAdded(DataTable dataTable, ColumnConfig column) { - DominoElement resizeElement = DominoElement.div().css(COLUMN_RESIZER); - final double[] initialStartPosition = {0}; - final double[] initialWidth = {0}; - EventListener resizeListener = - evt -> { - MouseEvent mouseEvent = Js.uncheckedCast(evt); + ResizeColumnMeta.get(column) + .ifPresent( + resizeColumnMeta -> { + if (resizeColumnMeta.isResizable()) { + DominoElement resizeElement = + DominoElement.div().css(COLUMN_RESIZER); - double currentPosition = mouseEvent.clientX; - double diff = currentPosition - initialStartPosition[0]; - String width = px.of(initialWidth[0] + diff); - column.getHeadElement().setWidth(width); - column.getHeadElement().setMaxWidth(width); - column.getHeadElement().setMinWidth(width); - if (dataTable.getTableConfig().isFixed()) { - columnCells - .get(column) - .forEach( - tRowCell -> - DominoElement.of(tRowCell.getCellInfo().getElement()) - .setWidth(width) - .setMaxWidth(width) - .setMinWidth(width)); - } - dataTable.fireTableEvent(new ColumnResizedEvent()); - }; + EventListener resizeListener = + evt -> { + MouseEvent mouseEvent = Js.uncheckedCast(evt); + column.applyAndOnParents( + col -> { + ResizeColumnMeta.get(col) + .ifPresent( + meta -> { + double currentPosition = mouseEvent.clientX; + double diff = currentPosition - meta.getStartPosition(); + String width = px.of(meta.getInitialWidth() + diff); + col.getHeadElement().setWidth(width); + col.getHeadElement().setMaxWidth(width); + col.getHeadElement().setMinWidth(width); + col.setWidth(width).minWidth(width).maxWidth(width); - resizeElement.addEventListener( - EventType.mousedown.getName(), - evt -> { - MouseEvent mouseEvent = Js.uncheckedCast(evt); - if (mouseEvent.buttons == 1) { - mouseEvent.preventDefault(); - initialStartPosition[0] = mouseEvent.clientX; - initialWidth[0] = column.getHeadElement().getBoundingClientRect().width; - document.body.addEventListener(EventType.mousemove.getName(), resizeListener); - } - }); - resizeElement.addEventListener( - EventType.mouseup.getName(), - evt -> document.body.removeEventListener(EventType.mousemove.getName(), resizeListener)); - document.body.addEventListener( - EventType.mouseup.getName(), - evt -> document.body.removeEventListener(EventType.mousemove.getName(), resizeListener)); - column.getHeadElement().appendChild(resizeElement); - column.getHeadElement().setPosition("relative"); - } + ColumnHeaderMeta.get(col) + .ifPresent( + headersMeta -> + headersMeta + .getExtraHeadElements() + .forEach( + header -> { + header.setWidth(width); + header.setMaxWidth(width); + header.setMinWidth(width); + })); + }); + }); - /** {@inheritDoc} */ - @Override - public void onRowAdded(DataTable dataTable, TableRow tableRow) { - if (dataTable.getTableConfig().isFixed()) { - for (RowCell cell : tableRow.getRowCells().values()) { - if (!columnCells.containsKey(cell.getColumnConfig())) { - columnCells.put(cell.getColumnConfig(), new ArrayList<>()); - } - columnCells.get(cell.getColumnConfig()).add(cell); - } - } + column.onEachLastSubColumn( + col -> { + ResizeColumnMeta.get(col) + .ifPresent( + meta -> { + double currentPosition = mouseEvent.clientX; + double diff = currentPosition - meta.getStartPosition(); + String width = px.of(meta.getInitialWidth() + diff); + col.getHeadElement().setWidth(width); + col.getHeadElement().setMaxWidth(width); + col.getHeadElement().setMinWidth(width); + col.setWidth(width).minWidth(width).maxWidth(width); + + ColumnHeaderMeta.get(col) + .ifPresent( + headersMeta -> + headersMeta + .getExtraHeadElements() + .forEach( + header -> { + header.setWidth(width); + header.setMaxWidth(width); + header.setMinWidth(width); + })); + }); + }); + + ColumnConfig dataColumn = column.getLastGrandSiblingColumn(); + ResizeColumnMeta.get(dataColumn) + .ifPresent( + meta -> { + double currentPosition = mouseEvent.clientX; + double diff = currentPosition - meta.getStartPosition(); + + dataTable.fireTableEvent(ColumnResizedEvent.of(column, diff)); + }); + }; + + resizeElement.addEventListener( + EventType.mousedown.getName(), + evt -> { + MouseEvent mouseEvent = Js.uncheckedCast(evt); + if (mouseEvent.buttons == 1) { + mouseEvent.preventDefault(); + column + .getGrandParent() + .applyAndOnSubColumns( + col -> + ResizeColumnMeta.get(col) + .ifPresent( + meta -> + meta.setInitialWidth( + col.getHeadElement() + .getBoundingClientRect() + .width) + .setStartPosition(mouseEvent.clientX))); + document.body.addEventListener( + EventType.mousemove.getName(), resizeListener); + } + }); + resizeElement.addEventListener( + EventType.mouseup.getName(), + evt -> { + document.body.removeEventListener( + EventType.mousemove.getName(), resizeListener); + }); + document.body.addEventListener( + EventType.mouseup.getName(), + evt -> { + document.body.removeEventListener( + EventType.mousemove.getName(), resizeListener); + }); + column.appendChild(FlexItem.of(resizeElement)); + } + }); } } diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/SortPlugin.java b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/SortPlugin.java index 0113b9781..0d7d85b8c 100644 --- a/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/SortPlugin.java +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/SortPlugin.java @@ -53,7 +53,7 @@ public void init(DataTable dataTable) { /** {@inheritDoc} */ @Override public void onHeaderAdded(DataTable dataTable, ColumnConfig column) { - if (column.isSortable()) { + if (column.isSortable() && !column.isUtilityColumn()) { SortContainer sortContainer = new SortContainer(column.getSortKey()); sortContainers.put(column.getSortKey(), sortContainer); diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/StickyColumnsPlugin.java b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/StickyColumnsPlugin.java index 5bbacf6bf..cb360351a 100644 --- a/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/StickyColumnsPlugin.java +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/StickyColumnsPlugin.java @@ -15,231 +15,13 @@ */ package org.dominokit.domino.ui.datatable.plugins; -import static java.util.Objects.nonNull; -import static org.dominokit.domino.ui.datatable.events.ColumnResizedEvent.COLUMN_RESIZED; -import static org.dominokit.domino.ui.datatable.events.TableDataUpdatedEvent.DATA_UPDATED; -import static org.dominokit.domino.ui.datatable.plugins.StickyColumnsPlugin.Direction.LEFT; -import static org.dominokit.domino.ui.datatable.plugins.StickyColumnsPlugin.Direction.RIGHT; - -import elemental2.dom.HTMLElement; -import java.util.*; -import java.util.function.BiConsumer; -import java.util.function.Consumer; -import java.util.function.Predicate; -import java.util.stream.IntStream; -import java.util.stream.Stream; -import org.dominokit.domino.ui.datatable.ColumnConfig; -import org.dominokit.domino.ui.datatable.DataTable; -import org.dominokit.domino.ui.datatable.TableRow; -import org.dominokit.domino.ui.datatable.events.StickyColumnsEvent; -import org.dominokit.domino.ui.datatable.events.TableEvent; -import org.dominokit.domino.ui.menu.Menu; -import org.dominokit.domino.ui.menu.MenuItem; -import org.dominokit.domino.ui.menu.direction.TopMiddleDropDirection; -import org.dominokit.domino.ui.utils.DominoElement; +import org.dominokit.domino.ui.datatable.plugins.pincolumns.PinColumnsPlugin; /** * this plugin allows marking columns as sticky ones * * @param the type of data table records + * @deprecated use {@link PinColumnsPlugin} */ -public class StickyColumnsPlugin implements DataTablePlugin { - - private static final String STICKY_COL = "sticky-col"; - private static final String PX = "px"; - private static final String UNSET = "unset"; - private final Map, ColumnMenu> columnMenus = new HashMap<>(); - private String leftPinText = "Pin left"; - private String rightPinText = "Pin right"; - private String noPinText = "No pin"; - private ColumnMenu currentSticked; - private Direction currentDirection; - - /** {@inheritDoc} */ - @Override - public void init(DataTable dataTable) { - dataTable.bodyElement().setOverFlow(UNSET, true); - } - - @Override - public void onHeaderAdded(DataTable dataTable, ColumnConfig column) { - columnMenus.put(column, new ColumnMenu(dataTable, column)); - } - - /** @param leftPinText the text for left pin text */ - public void setLeftPinText(String leftPinText) { - this.leftPinText = leftPinText; - } - - /** @param rightPinText the text for right pin text */ - public void setRightPinText(String rightPinText) { - this.rightPinText = rightPinText; - } - - /** @param noPinText the text for no pin text */ - public void setNoPinText(String noPinText) { - this.noPinText = noPinText; - } - - @Override - public void handleEvent(TableEvent event) { - if (DATA_UPDATED.equals(event.getType())) { - for (ColumnMenu columnMenu : columnMenus.values()) { - columnMenu.unstickColumn(); - } - } - if (COLUMN_RESIZED.equals(event.getType()) && nonNull(currentSticked)) { - currentSticked.stickColumn(currentDirection); - } - } - - /** - * This enum identify the direction of marking columns as sticky. - * - *

{@link Direction#LEFT} starts marking columns from left to right. {@link Direction#RIGHT} - * starts marking columns from right to left. - */ - public enum Direction { - LEFT((intStream, size) -> intStream, (element, distance) -> element.setLeft(distance)), - RIGHT( - (intStream, size) -> intStream.map(i -> size - i - 1), - (element, distance) -> element.setRight(distance)); - - private final OrderFunction orderFunction; - private final DistanceFunction distanceHandler; - - Direction(OrderFunction orderFunction, DistanceFunction distanceHandler) { - this.orderFunction = orderFunction; - this.distanceHandler = distanceHandler; - } - - private void forEach(List list, Predicate stopPredicate, Consumer valueConsumer) { - Stream stream = - orderFunction.apply(IntStream.range(0, list.size()), list.size()).mapToObj(list::get); - forEach( - stream, - (value, breaker) -> { - valueConsumer.accept(value); - if (stopPredicate.test(value)) { - breaker.stop(); - } - }); - } - - private void stick(HTMLElement element, String distance) { - DominoElement dominoElement = DominoElement.of(element); - dominoElement.addCss(STICKY_COL); - distanceHandler.apply(dominoElement, distance); - } - - public void unstick(HTMLElement element) { - DominoElement dominoElement = DominoElement.of(element); - dominoElement.removeCss(STICKY_COL); - distanceHandler.apply(dominoElement, UNSET); - } - - private static class Breaker { - private boolean shouldBreak = false; - - public void stop() { - shouldBreak = true; - } - - boolean get() { - return shouldBreak; - } - } - - private void forEach(Stream stream, BiConsumer consumer) { - Spliterator spliterator = stream.spliterator(); - boolean hadNext = true; - Breaker breaker = new Breaker(); - - while (hadNext && !breaker.get()) { - hadNext = spliterator.tryAdvance(elem -> consumer.accept(elem, breaker)); - } - } - - @FunctionalInterface - private interface OrderFunction { - IntStream apply(IntStream intStream, int size); - } - - @FunctionalInterface - private interface DistanceFunction { - void apply(DominoElement element, String distance); - } - } - - private class ColumnMenu { - - private final DataTable dataTable; - private final ColumnConfig column; - - private ColumnMenu(DataTable dataTable, ColumnConfig column) { - this.dataTable = dataTable; - this.column = column; - column - .getHeaderLayout() - .setDropMenu( - Menu.create() - .setContextMenu(true) - .setDropDirection(new TopMiddleDropDirection()) - .appendChild( - MenuItem.create(leftPinText).addClickListener(evt -> stickColumn(LEFT))) - .appendChild( - MenuItem.create(rightPinText).addClickListener(evt -> stickColumn(RIGHT))) - .appendChild( - MenuItem.create(noPinText).addClickListener(evt -> unstickColumn()))); - } - - private void stickColumn(Direction direction) { - unstickColumn(); - final double[] summedLeft = {0}; - List> columns = dataTable.getTableConfig().getColumns(); - Map> effectedColumns = new LinkedHashMap<>(); - direction.forEach( - columns, - c -> c.getName().equals(column.getName()), - column -> { - effectedColumns.put(column.getName(), column); - direction.stick(column.getHeadElement().element(), summedLeft[0] + PX); - summedLeft[0] += columnWidth(column); - }); - dataTable.fireTableEvent(new StickyColumnsEvent<>(new ArrayList<>(effectedColumns.values()))); - for (TableRow row : dataTable.getRows()) { - final double[] summedLeftCell = {0}; - direction.forEach( - new ArrayList<>(row.getRowCells().values()), - rowCell -> rowCell.getColumnConfig().getName().equals(column.getName()), - cell -> { - direction.stick(cell.getCellInfo().getElement(), summedLeftCell[0] + PX); - summedLeftCell[0] += columnWidth(cell.getColumnConfig()); - }); - } - currentSticked = this; - currentDirection = direction; - } - - private double columnWidth(ColumnConfig column) { - return column.getHeadElement().getBoundingClientRect().width; - } - - private void unstickColumn() { - if (currentDirection != null) { - List> columns = dataTable.getTableConfig().getColumns(); - currentDirection.forEach( - columns, - c -> false, - column -> currentDirection.unstick(column.getHeadElement().element())); - for (TableRow row : dataTable.getRows()) { - currentDirection.forEach( - new ArrayList<>(row.getRowCells().values()), - rowCell -> false, - cell -> currentDirection.unstick(cell.getCellInfo().getElement())); - } - currentSticked = null; - } - } - } -} +@Deprecated +public class StickyColumnsPlugin extends PinColumnsPlugin {} diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/pincolumns/PinColumnFunction.java b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/pincolumns/PinColumnFunction.java new file mode 100644 index 000000000..834556aa9 --- /dev/null +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/pincolumns/PinColumnFunction.java @@ -0,0 +1,22 @@ +/* + * 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.plugins.pincolumns; + +import org.dominokit.domino.ui.datatable.ColumnConfig; + +interface PinColumnFunction { + double pin(ColumnConfig column, double position); +} diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/pincolumns/PinColumnMeta.java b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/pincolumns/PinColumnMeta.java new file mode 100644 index 000000000..27ac0adab --- /dev/null +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/pincolumns/PinColumnMeta.java @@ -0,0 +1,206 @@ +/* + * 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.plugins.pincolumns; + +import elemental2.dom.HTMLElement; +import java.util.List; +import java.util.Optional; +import org.dominokit.domino.ui.datatable.ColumnConfig; +import org.dominokit.domino.ui.datatable.ColumnHeaderMeta; +import org.dominokit.domino.ui.datatable.ColumnMeta; +import org.dominokit.domino.ui.utils.DominoElement; + +public class PinColumnMeta implements ColumnMeta, PinColumnFunction, PinElementToColumn { + + public static final String dui_pinned_cell = "dui-pinned-cell"; + public static final String dui_pin_right_col = "dui-pin-right-col"; + public static final String dui_pin_left_col = "dui-pin-left-col"; + public static final String dui_pinned_left = "dui-pinned-left"; + public static final String dui_pinned_right = "dui-pinned-right"; + + private final PinDirection direction; + + public static final String PIN_COLUMN = "pin-column"; + + public static PinColumnMeta left() { + return new PinColumnMeta(PinDirection.LEFT); + } + + public static PinColumnMeta right() { + return new PinColumnMeta(PinDirection.RIGHT); + } + + public static Optional get(ColumnConfig column) { + return column.getMeta(PIN_COLUMN); + } + + public static boolean isPinLeft(ColumnConfig column) { + return PinColumnMeta.get(column).isPresent() && PinColumnMeta.get(column).get().isLeftPin(); + } + + public static boolean isPinRight(ColumnConfig column) { + return PinColumnMeta.get(column).isPresent() && PinColumnMeta.get(column).get().isRightPin(); + } + + public static boolean isPinned(ColumnConfig column) { + return !(PinColumnMeta.isPinLeft(column) || PinColumnMeta.isPinRight(column)); + } + + public PinColumnMeta(PinDirection direction) { + this.direction = direction; + } + + public boolean isLeftPin() { + return PinDirection.LEFT.equals(direction); + } + + public boolean isRightPin() { + return PinDirection.RIGHT.equals(direction); + } + + @Override + public String getKey() { + return PIN_COLUMN; + } + + @Override + public double pin(ColumnConfig column, double position) { + return direction.pin(column, position); + } + + @Override + public void pinElement(ColumnConfig column, DominoElement element) { + direction.pinElement(column, element); + } + + public static void clearPinningCss(DominoElement element) { + element.removeCss(dui_pinned_cell, dui_pin_left_col, dui_pin_right_col); + } + + public enum PinDirection implements PinColumnFunction, PinElementToColumn { + LEFT( + PinDirection::pinHeaderLeft, + (column, element) -> { + element.addCss(dui_pinned_cell); + column.getHeadElement().hasCssClass(dui_pin_left_col).ifPresent(element::addCss); + element.removeCss(dui_pin_right_col); + element.setCssProperty( + "left", column.getHeadElement().getAttribute("pin-left-data") + "px"); + }), + RIGHT( + PinColumnMeta::pinHeaderRight, + (column, element) -> { + element.addCss(dui_pinned_cell); + column.getHeadElement().hasCssClass(dui_pin_right_col).ifPresent(element::addCss); + element.removeCss(dui_pin_left_col); + element.setCssProperty( + "right", column.getHeadElement().getAttribute("pin-right-data") + "px"); + }); + + private PinColumnFunction pinColumnFunction; + private PinElementToColumn pinElementToColumn; + + PinDirection(PinColumnFunction pinColumnFunction, PinElementToColumn pinElementToColumn) { + this.pinColumnFunction = pinColumnFunction; + this.pinElementToColumn = pinElementToColumn; + } + + private static double pinHeaderLeft(ColumnConfig column, double left) { + column.getHeadElement().addCss(dui_pinned_left); + column.getHeadElement().setAttribute("pin-left-data", left); + + if (column.isColumnGroup()) { + double[] childOffset = new double[] {left}; + column + .getSubColumns() + .forEach( + subColumn -> { + subColumn.applyMeta(PinColumnMeta.get(column).get()); + childOffset[0] = pinHeaderLeft(subColumn, childOffset[0]); + }); + } + ColumnHeaderMeta.get(column) + .ifPresent( + meta -> + meta.getExtraHeadElements() + .forEach( + headElement -> { + headElement.addCss(dui_pinned_left); + column + .getHeadElement() + .hasCssClass(dui_pin_left_col) + .ifPresent(headElement::addCss); + pinElementLeft(headElement, left); + })); + return pinElementLeft(column.getHeadElement(), left); + } + + @Override + public double pin(ColumnConfig column, double position) { + return pinColumnFunction.pin(column, position); + } + + @Override + public void pinElement(ColumnConfig column, DominoElement element) { + pinElementToColumn.pinElement(column, element); + } + } + + private static double pinElementLeft(DominoElement element, double left) { + if (!element.containsCss(dui_pinned_cell)) { + element.addCss(dui_pinned_cell); + } + element.setCssProperty("left", left + "px"); + return left + Double.valueOf(element.getBoundingClientRect().width); + } + + private static double pinHeaderRight(ColumnConfig column, double right) { + column.getHeadElement().addCss(dui_pinned_right); + column.getHeadElement().setAttribute("pin-right-data", right); + if (column.isColumnGroup()) { + double[] childOffset = new double[] {right}; + List> subColumns = column.getSubColumns(); + for (int i = subColumns.size() - 1; i >= 0; i--) { + ColumnConfig subColumn = subColumns.get(i); + subColumn.applyMeta(PinColumnMeta.get(column).get()); + childOffset[0] = pinHeaderRight(subColumn, childOffset[0]); + } + } + ColumnHeaderMeta.get(column) + .ifPresent( + meta -> + meta.getExtraHeadElements() + .forEach( + headElement -> { + headElement.addCss(dui_pinned_right); + column + .getHeadElement() + .hasCssClass(dui_pin_right_col) + .ifPresent(headElement::addCss); + pinElementRight(headElement, right); + })); + return pinElementRight(column.getHeadElement(), right); + } + + private static double pinElementRight( + DominoElement element, double right) { + if (!element.containsCss(dui_pinned_cell)) { + element.addCss(dui_pinned_cell); + } + element.setCssProperty("right", right + "px"); + return right + Double.valueOf(element.getBoundingClientRect().width); + } +} diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/pincolumns/PinColumnsConfig.java b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/pincolumns/PinColumnsConfig.java new file mode 100644 index 000000000..423c9b582 --- /dev/null +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/pincolumns/PinColumnsConfig.java @@ -0,0 +1,84 @@ +/* + * 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.plugins.pincolumns; + +import org.dominokit.domino.ui.datatable.plugins.PluginConfig; + +public class PinColumnsConfig implements PluginConfig { + private boolean showPinIcon = false; + private boolean showPinMenu = false; + + private String pinLeftText = "Pin left"; + private String pinRightText = "Pin right"; + private String unpinText = "Unpin"; + + public PinColumnsConfig() {} + + public PinColumnsConfig(boolean showPinIcon, boolean showPinMenu) { + this.showPinIcon = showPinIcon; + this.showPinMenu = showPinMenu; + } + + public static PinColumnsConfig of() { + return new PinColumnsConfig(); + } + + public static PinColumnsConfig of(boolean showPinIcon, boolean showPinMenu) { + return new PinColumnsConfig(showPinIcon, showPinMenu); + } + + public boolean isShowPinIcon() { + return showPinIcon; + } + + public PinColumnsConfig setShowPinIcon(boolean showPinIcon) { + this.showPinIcon = showPinIcon; + return this; + } + + public boolean isShowPinMenu() { + return showPinMenu; + } + + public PinColumnsConfig setShowPinMenu(boolean showPinMenu) { + this.showPinMenu = showPinMenu; + return this; + } + + public String getPinLeftText() { + return pinLeftText; + } + + public void setPinLeftText(String pinLeftText) { + this.pinLeftText = pinLeftText; + } + + public String getPinRightText() { + return pinRightText; + } + + public void setPinRightText(String pinRightText) { + this.pinRightText = pinRightText; + } + + public String getUnpinText() { + return unpinText; + } + + public void setUnpinText(String unpinText) { + this.unpinText = unpinText; + } +} diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/pincolumns/PinColumnsPlugin.java b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/pincolumns/PinColumnsPlugin.java new file mode 100644 index 000000000..04e606ec1 --- /dev/null +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/pincolumns/PinColumnsPlugin.java @@ -0,0 +1,355 @@ +/* + * 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.plugins.pincolumns; + +import elemental2.dom.DomGlobal; +import elemental2.dom.HTMLTableCellElement; +import java.util.List; +import java.util.function.Consumer; +import org.dominokit.domino.ui.datatable.ColumnConfig; +import org.dominokit.domino.ui.datatable.DataTable; +import org.dominokit.domino.ui.datatable.RowCell; +import org.dominokit.domino.ui.datatable.events.ColumnResizedEvent; +import org.dominokit.domino.ui.datatable.events.RowRecordUpdatedEvent; +import org.dominokit.domino.ui.datatable.events.TableBorderedEvent; +import org.dominokit.domino.ui.datatable.events.TableEvent; +import org.dominokit.domino.ui.datatable.plugins.DataTablePlugin; +import org.dominokit.domino.ui.datatable.plugins.HasPluginConfig; +import org.dominokit.domino.ui.grid.flex.FlexItem; +import org.dominokit.domino.ui.icons.Icons; +import org.dominokit.domino.ui.menu.MenuItem; +import org.dominokit.domino.ui.utils.DominoElement; +import org.dominokit.domino.ui.utils.ElementUtil; + +/** + * This plugin allow the user to pin a specific column or columns to the right or left of the table. + * pinned columns will not scroll horizontally with the table. + * + *

pinning a column to the left will also pin all columns that are to the left of that column to + * left too pinning a column to the right will also pin all columns that are to the right of that + * column to the right. pinning a column in a column group will pin all columns in that group. + * + *

to pin a column to the left apply a {@link PinColumnMeta} to the column with left direction + * + *

+ *     
+ *     tableConfig.addColumn(ColumnConfig.create("name", "label").applyMeta(PinColumnMeta.left()))
+ *     
+ * 
+ * + * to pin a column to the left apply a {@link PinColumnMeta} to the column with right direction + * + *
+ *     
+ *     tableConfig.addColumn(ColumnConfig.create("name", "label").applyMeta(PinColumnMeta.right()))
+ *     
+ * 
+ * + * The pin menu and pin icon are both configurable and are disabled by default. + * + * @param + */ +public class PinColumnsPlugin + implements DataTablePlugin, HasPluginConfig, PinColumnsConfig> { + + public static final String dui_pinned_cell = "dui-pinned-cell"; + public static final String dui_pin_right_col = "dui-pin-right-col"; + public static final String dui_pin_left_col = "dui-pin-left-col"; + public static final String dui_pinned_left = "dui-pinned-left"; + public static final String dui_pinned_right = "dui-pinned-right"; + private DataTable datatable; + + private FlexItem pinLeftIcon = FlexItem.of(Icons.ALL.pin_mdi().size18()).setOrder(100); + private FlexItem pinRightIcon = FlexItem.of(Icons.ALL.pin_mdi().size18()).setOrder(100); + private PinColumnsConfig config = PinColumnsConfig.of(); + + @Override + public void init(DataTable dataTable) { + this.datatable = dataTable; + } + + @Override + public void onAfterAddHeaders(DataTable dataTable) { + dataTable + .getTableConfig() + .getColumnsGrouped() + .forEach( + column -> { + if (config.isShowPinMenu()) { + column + .getMenu() + .appendChild( + MenuItem.create(config.getPinLeftText()) + .setValue("left") + .addLeftAddOn(FlexItem.of(Icons.ALL.dock_left_mdi())) + .addSelectionHandler( + value -> { + setPinLeftColumn(column); + applyPinnedColumns(); + })) + .appendChild( + MenuItem.create(config.getPinRightText()) + .setValue("right") + .addLeftAddOn(FlexItem.of(Icons.ALL.dock_right_mdi())) + .addSelectionHandler( + value -> { + setPinRightColumn(column); + applyPinnedColumns(); + })) + .appendChild( + MenuItem.create(config.getUnpinText()) + .setValue("right") + .addLeftAddOn(FlexItem.of(Icons.ALL.pin_off_mdi())) + .addSelectionHandler( + value -> { + unpinColumn(column); + })); + } + }); + + List> columns = onBeforeSetPinColumn(); + + columns.stream() + .filter(PinColumnMeta::isPinLeft) + .reduce((first, second) -> second) + .ifPresent(this::setPinLeftColumn); + + columns.stream() + .filter(PinColumnMeta::isPinRight) + .findFirst() + .ifPresent(this::setPinRightColumn); + dataTable.onResize((element, observer, entries) -> applyPinnedColumns()); + } + + private List> onBeforeSetPinColumn() { + List> columns = datatable.getTableConfig().getColumns(); + + datatable + .headerElement() + .querySelectorAll("." + dui_pinned_cell + ",." + dui_pinned_left + ",." + dui_pinned_right) + .forEach(element -> element.removeCss(dui_pinned_cell, dui_pinned_left, dui_pinned_right)); + return columns; + } + + public void setPinRightColumn(ColumnConfig pinRightColumn) { + onBeforeSetPinColumn(); + datatable + .headerElement() + .querySelectorAll("." + dui_pin_right_col) + .forEach(element -> element.removeCss(dui_pin_right_col)); + if (config.isShowPinIcon()) { + pinRightColumn.getGrandParent().appendChild(pinRightIcon); + } + pinRightColumn + .getGrandParent() + .applyAndOnFirstSubColumn(col -> col.getHeadElement().addCss(dui_pin_right_col)); + pinColumnsRight(pinRightColumn); + } + + public void unpinColumn(ColumnConfig column) { + if (PinColumnMeta.isPinLeft(column)) { + unpinLeftColumns(); + } + + if (PinColumnMeta.isPinRight(column)) { + unpinRightColumns(); + } + } + + public void unpinLeftColumns() { + onBeforeSetPinColumn(); + datatable + .headerElement() + .querySelectorAll("." + dui_pin_left_col + ",." + dui_pinned_left) + .forEach(element -> element.removeCss(dui_pin_left_col, dui_pinned_left)); + datatable.getTableConfig().getColumnsGrouped().stream() + .filter(PinColumnMeta::isPinLeft) + .forEach( + col -> col.applyAndOnSubColumns(column -> column.removeMeta(PinColumnMeta.PIN_COLUMN))); + pinLeftIcon.remove(); + applyPinnedColumns(); + } + + public void unpinRightColumns() { + onBeforeSetPinColumn(); + datatable + .headerElement() + .querySelectorAll("." + dui_pin_right_col + ",." + dui_pinned_right) + .forEach(element -> element.removeCss(dui_pin_right_col, dui_pinned_right)); + datatable.getTableConfig().getColumnsGrouped().stream() + .filter(PinColumnMeta::isPinRight) + .forEach( + col -> col.applyAndOnSubColumns(column -> column.removeMeta(PinColumnMeta.PIN_COLUMN))); + pinRightIcon.remove(); + applyPinnedColumns(); + } + + public void setPinLeftColumn(ColumnConfig pinLeftColumn) { + onBeforeSetPinColumn(); + datatable + .headerElement() + .querySelectorAll("." + dui_pin_left_col) + .forEach(element -> element.removeCss(dui_pin_left_col)); + if (config.isShowPinIcon()) { + pinLeftColumn.getGrandParent().appendChild(pinLeftIcon); + } + pinLeftColumn + .getGrandParent() + .applyAndOnLastSubColumn(col -> col.getHeadElement().addCss(dui_pin_left_col)); + pinColumnsLeft(pinLeftColumn); + } + + private void pinColumnsLeft(ColumnConfig pinLeftColumn) { + List> columns = datatable.getTableConfig().getColumnsGrouped(); + int columnIndex = columns.indexOf(pinLeftColumn.getGrandParent()); + for (int i = 0; i <= columnIndex; i++) { + columns.get(i).applyAndOnSubColumns(column -> column.applyMeta(PinColumnMeta.left())); + } + for (int i = columnIndex + 1; i < columns.size(); i++) { + columns + .get(i) + .applyAndOnSubColumns( + column -> { + if (PinColumnMeta.isPinLeft(column)) { + column.removeMeta(PinColumnMeta.PIN_COLUMN); + } + }); + } + } + + private void pinColumnsRight(ColumnConfig pinRightColumn) { + List> columns = datatable.getTableConfig().getColumnsGrouped(); + int columnIndex = columns.indexOf(pinRightColumn.getGrandParent()); + for (int i = 0; i < columnIndex; i++) { + columns + .get(i) + .applyAndOnSubColumns( + column -> { + if (PinColumnMeta.isPinRight(column)) { + column.removeMeta(PinColumnMeta.PIN_COLUMN); + } + }); + } + for (int i = columnIndex; i < columns.size(); i++) { + columns.get(i).applyAndOnSubColumns(column -> column.applyMeta(PinColumnMeta.right())); + } + } + + @Override + public void handleEvent(TableEvent event) { + switch (event.getType()) { + case TableBorderedEvent.TABLE_BORDERED_EVENT: + case RowRecordUpdatedEvent.RECORD_UPDATED: + applyPinnedColumns(); + break; + case ColumnResizedEvent.COLUMN_RESIZED: + DomGlobal.requestAnimationFrame( + timestamp -> { + applyPinnedColumns(); + }); + break; + } + } + + @Override + public void onAllRowsAdded(DataTable dataTable) { + applyPinnedColumns(); + } + + private void applyPinnedColumns() { + pinColumns(); + } + + private void pinColumns() { + if (datatable.isAttached()) { + pinColumnsForAttachedTable(); + } else { + datatable.onAttached( + mutationRecord -> DomGlobal.setTimeout(p0 -> pinColumnsForAttachedTable())); + } + } + + private void pinColumnsForAttachedTable() { + ElementUtil.withBodyObserverPaused( + () -> { + double[] leftHeaderOffset = new double[] {0}; + double[] rightHeaderOffset = new double[] {0}; + datatable + .getTableConfig() + .getColumnsGrouped() + .forEach( + column -> { + if (PinColumnMeta.isPinLeft(column)) { + leftHeaderOffset[0] = + PinColumnMeta.get(column).get().pin(column, leftHeaderOffset[0]); + } + }); + + List> groupedColumns = datatable.getTableConfig().getColumnsGrouped(); + for (int i = groupedColumns.size() - 1; i >= 0; i--) { + ColumnConfig column = groupedColumns.get(i); + if (PinColumnMeta.isPinRight(column)) { + rightHeaderOffset[0] = + PinColumnMeta.get(column).get().pin(column, rightHeaderOffset[0]); + } + } + List> columns = datatable.getTableConfig().getColumns(); + + datatable + .getRows() + .forEach( + tableRow -> { + for (int i = 0; i < columns.size(); i++) { + ColumnConfig column = columns.get(i); + RowCell cell = tableRow.getCell(column.getName()); + DominoElement td = + DominoElement.of(cell.getCellInfo().getElement()); + DominoElement th = + cell.getColumnConfig().getHeadElement(); + + if (PinColumnMeta.get(column).isPresent()) { + PinColumnMeta.get(column).get().pinElement(column, td); + } else { + PinColumnMeta.clearPinningCss(td); + } + } + }); + }); + } + + /** + * Set the configuration for this plugin instance + * + * @param config {@link PinColumnsConfig} + * @return same plugion instance + */ + @Override + public PinColumnsPlugin setConfig(PinColumnsConfig config) { + this.config = config; + return this; + } + + /** + * Use to update the configuration in the current plugin configuration + * + * @param handler {@link Consumer} of {@link PinColumnsConfig} + * @return same plugin instance. + */ + public PinColumnsPlugin configure(Consumer handler) { + handler.accept(config); + return this; + } +} diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/pincolumns/PinElementToColumn.java b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/pincolumns/PinElementToColumn.java new file mode 100644 index 000000000..c7277a7c3 --- /dev/null +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/pincolumns/PinElementToColumn.java @@ -0,0 +1,23 @@ +/* + * 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.plugins.pincolumns; + +import org.dominokit.domino.ui.datatable.ColumnConfig; +import org.dominokit.domino.ui.utils.DominoElement; + +interface PinElementToColumn { + void pinElement(ColumnConfig column, DominoElement element); +} diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/forms/ValueBox.java b/domino-ui/src/main/java/org/dominokit/domino/ui/forms/ValueBox.java index d6387b84c..08de47692 100644 --- a/domino-ui/src/main/java/org/dominokit/domino/ui/forms/ValueBox.java +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/forms/ValueBox.java @@ -495,7 +495,6 @@ public T setLabel(String label) { if (nonNull(label) && !label.isEmpty() || allowEmptyLabel()) { labelInitializer.apply(); super.setLabel(label); - DomGlobal.console.info(getLabelTextElement().get().element()); if ((getLabelTextElement().isPresent() && !getLabelTextElement().get().isEmptyElement())) { hidePlaceholder(); } diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/icons/BaseIcon.java b/domino-ui/src/main/java/org/dominokit/domino/ui/icons/BaseIcon.java index d0dc6ddd4..953f85fb3 100644 --- a/domino-ui/src/main/java/org/dominokit/domino/ui/icons/BaseIcon.java +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/icons/BaseIcon.java @@ -143,8 +143,18 @@ public T toggleIcon() { * @return same instance */ public T clickable() { + return clickable(true); + } + /** + * Adds a clickable style to the icon + * + * @return same instance + */ + public T clickable(boolean withWaves) { addCss(IconsStyles.CLICKABLE_ICON); - withWaves(); + if (withWaves) { + withWaves(); + } setAttribute("tabindex", "0"); setAttribute("aria-expanded", "true"); setAttribute("href", "#"); diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/menu/AbstractMenu.java b/domino-ui/src/main/java/org/dominokit/domino/ui/menu/AbstractMenu.java index 809525edb..207a7fdb5 100644 --- a/domino-ui/src/main/java/org/dominokit/domino/ui/menu/AbstractMenu.java +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/menu/AbstractMenu.java @@ -96,6 +96,7 @@ public abstract class AbstractMenu> protected List> menuItems = new ArrayList<>(); protected boolean autoCloseOnSelect = true; protected final List> selectionHandlers = new ArrayList<>(); + protected final List> addHandlers = new ArrayList<>(); protected boolean headerVisible = false; private AbstractMenu currentOpen; @@ -337,6 +338,7 @@ public T appendChild(AbstractMenuItem menuItem) { itemsContainer.appendChild(menuItem); menuItems.add(menuItem); menuItem.setParent(this); + addHandlers.forEach(handler -> handler.onItemAdded(menuItem)); } return (T) this; } @@ -732,6 +734,32 @@ public T removeSelectionHandler(MenuItemSelectionHandler selectionHandler) { return (T) this; } + /** + * Adds a global add handler that will apply to all menu items + * + * @param addHandler {@link MenuItemAddedHandler} + * @return same menu instance + */ + public T addItemAddedHandler(MenuItemAddedHandler addHandler) { + if (nonNull(addHandler)) { + addHandlers.add(addHandler); + } + return (T) this; + } + + /** + * removes a global add handler that will apply to all menu items + * + * @param addHandler {@link MenuItemAddedHandler} + * @return same menu instance + */ + public T removeItemAddedHandler(MenuItemAddedHandler addHandler) { + if (nonNull(addHandler)) { + addHandlers.remove(addHandler); + } + return (T) this; + } + /** * Opens a sub menu that has this menu as its parent * @@ -1125,4 +1153,19 @@ public interface MenuItemSelectionHandler { */ void onItemSelected(AbstractMenuItem menuItem); } + + /** + * A functional interface to implement menu items add handlers + * + * @param V the type of the menu item value + */ + @FunctionalInterface + public interface MenuItemAddedHandler { + /** + * Will be called when a menu item is added to the menu + * + * @param menuItem The {@link AbstractMenuItem} added + */ + void onItemAdded(AbstractMenuItem menuItem); + } } diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/utils/BaseDominoElement.java b/domino-ui/src/main/java/org/dominokit/domino/ui/utils/BaseDominoElement.java index ec579251f..2e71c0c03 100644 --- a/domino-ui/src/main/java/org/dominokit/domino/ui/utils/BaseDominoElement.java +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/utils/BaseDominoElement.java @@ -1774,6 +1774,10 @@ public boolean containsCss(String cssClass) { return style().containsCss(cssClass); } + public Optional hasCssClass(String cssClass) { + return style().containsCss(cssClass) ? Optional.of(cssClass) : Optional.empty(); + } + @Override public T pullRight() { style().pullRight(); diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/utils/BodyObserver.java b/domino-ui/src/main/java/org/dominokit/domino/ui/utils/BodyObserver.java index c58c35b5b..d05df9474 100644 --- a/domino-ui/src/main/java/org/dominokit/domino/ui/utils/BodyObserver.java +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/utils/BodyObserver.java @@ -46,32 +46,49 @@ final class BodyObserver { private static List detachObservers = new ArrayList<>(); private static List attachObservers = new ArrayList<>(); private static boolean ready = false; + private static boolean paused = false; + private static MutationObserver mutationObserver; private BodyObserver() {} + static void pauseFor(Runnable handler) { + mutationObserver.disconnect(); + try { + handler.run(); + } finally { + observe(); + } + } + private static void startObserving() { - MutationObserver mutationObserver = + mutationObserver = new MutationObserver( (JsArray records, MutationObserver observer) -> { - MutationRecord[] recordsArray = - Js.uncheckedCast(records.asArray(new MutationRecord[records.length])); - for (MutationRecord record : recordsArray) { - if (!record.removedNodes.asList().isEmpty()) { - onElementsRemoved(record); - } - - if (!record.addedNodes.asList().isEmpty()) { - onElementsAppended(record); + if (!paused) { + MutationRecord[] recordsArray = + Js.uncheckedCast(records.asArray(new MutationRecord[records.length])); + for (MutationRecord record : recordsArray) { + if (!record.removedNodes.asList().isEmpty()) { + onElementsRemoved(record); + } + + if (!record.addedNodes.asList().isEmpty()) { + onElementsAppended(record); + } } } return null; }); + observe(); + ready = true; + } + + private static void observe() { MutationObserverInit mutationObserverInit = MutationObserverInit.create(); mutationObserverInit.setChildList(true); mutationObserverInit.setSubtree(true); mutationObserver.observe(document.body, mutationObserverInit); - ready = true; } private static void onElementsAppended(MutationRecord record) { diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/utils/ElementUtil.java b/domino-ui/src/main/java/org/dominokit/domino/ui/utils/ElementUtil.java index 3f4120992..0f70ec4a5 100644 --- a/domino-ui/src/main/java/org/dominokit/domino/ui/utils/ElementUtil.java +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/utils/ElementUtil.java @@ -149,6 +149,10 @@ public static Optional onAttach(HTMLElement element, ObserverCa return Optional.empty(); } + public static void withBodyObserverPaused(Runnable handler) { + BodyObserver.pauseFor(handler); + } + /** * {@link #onAttach(HTMLElement, ObserverCallback)} * diff --git a/domino-ui/src/main/resources/org/dominokit/domino/ui/public/css/cards/domino-ui-card.css b/domino-ui/src/main/resources/org/dominokit/domino/ui/public/css/cards/domino-ui-card.css index f3d7db6c3..5785f169b 100644 --- a/domino-ui/src/main/resources/org/dominokit/domino/ui/public/css/cards/domino-ui-card.css +++ b/domino-ui/src/main/resources/org/dominokit/domino/ui/public/css/cards/domino-ui-card.css @@ -116,6 +116,10 @@ padding: 20px; } +.card .body:not[class*="bg-"] { + background-color: inherit; +} + .card.fit-content .body{ padding: 0px; } diff --git a/domino-ui/src/main/resources/org/dominokit/domino/ui/public/css/datatable/domino-ui-datatable.css b/domino-ui/src/main/resources/org/dominokit/domino/ui/public/css/datatable/domino-ui-datatable.css index c2c77ad72..aa6b63864 100644 --- a/domino-ui/src/main/resources/org/dominokit/domino/ui/public/css/datatable/domino-ui-datatable.css +++ b/domino-ui/src/main/resources/org/dominokit/domino/ui/public/css/datatable/domino-ui-datatable.css @@ -1,8 +1,6 @@ - table { border-spacing: 0; - border-collapse: collapse; - background-color: transparent; + background-color: inherit; } td, @@ -22,12 +20,15 @@ th { } .table { - width: 100%; - max-width: 100%; border-bottom: 1px solid #eee; margin-bottom: 0px; } +.table-width-full { + width: 100%; + max-width: 100%; +} + .table > thead > tr > th, .table > tbody > tr > th, .table > tfoot > tr > th, @@ -36,12 +37,18 @@ th { .table > tfoot > tr > td { padding: 8px; line-height: 1.42857143; - border-top: 1px solid #ddd; } - +.table > thead { + position: sticky; + top: 0; + z-index: 1; +} .table > thead > tr > th { vertical-align: bottom; - border-bottom: 2px solid #ddd; +} + +.table thead tr th.dui-pinned-cell { + z-index: 1; } .table > caption + thead > tr:first-child > th, @@ -70,8 +77,11 @@ th { padding: 5px; } -.table-bordered { - border: 1px solid #ddd; +.table-bordered thead tr td:last-child, +.table-bordered thead tr th:last-child, +.table-bordered tbody tr th:last-child, +.table-bordered tbody tr td:last-child{ + border-right: 1px solid #ddd; } .table-bordered > thead > tr > th, @@ -80,12 +90,6 @@ th { .table-bordered > thead > tr > td, .table-bordered > tbody > tr > td, .table-bordered > tfoot > tr > td { - border: 1px solid #ddd; -} - -.table-bordered > thead > tr > th, -.table-bordered > thead > tr > td { - border-bottom-width: 2px; } table col[class*="col-"] { @@ -219,6 +223,12 @@ table th[class*="col-"] { .table-responsive { min-height: .01%; overflow-x: auto; + background-color: inherit; +} + +.table-responsive.fixed-width{ + overflow-x: hidden; + overflow-y: hidden; } .table-responsive[disabled] { @@ -226,14 +236,11 @@ table th[class*="col-"] { opacity: 0.5; } -.table-responsive.table-fixed { - /*position: relative;*/ - overflow: hidden !important; -} .table-responsive.table-fixed .table { display: block; overflow-x: auto; + max-width: 100%; } .table .fixed-width { @@ -246,7 +253,6 @@ table th[class*="col-"] { .table-responsive { width: 100%; margin-bottom: 15px; - overflow-y: hidden; -ms-overflow-style: -ms-autohiding-scrollbar; border: 1px solid #ddd; } @@ -303,8 +309,6 @@ table th[class*="col-"] { .table tbody tr td, .table tbody tr th { padding: 10px; - border-top: 1px solid #eee; - border-bottom: 1px solid #eee; } .table tbody tr.primary td, .table tbody tr.primary th { @@ -335,11 +339,9 @@ table th[class*="col-"] { .table thead tr th { vertical-align: middle; padding: 10px; - border-bottom: 1px solid #eee; } .table-bordered { - border-top: 1px solid #eee; } .table-hover > tbody > tr:hover:not([class*="details-tr"]):not([class*="bg-"]) { @@ -348,16 +350,15 @@ table th[class*="col-"] { .table-bordered tbody tr td, .table-bordered tbody tr th { padding: 10px; - border: 1px solid #eee; } .table-bordered thead tr th { padding: 10px; - border: 1px solid #eee; } .table-cm-header:HOVER { - background-color: rgba(0, 0, 0, 0.04) !important; + background-color: inherit !important; + filter: brightness(95%); } .table thead tr th.table-cm-filter { @@ -384,7 +385,7 @@ table th[class*="col-"] { } .table-responsive .table-header:not([class*="bg-"]) { - border-bottom: 1px solid rgba(204, 204, 204, 0.35); + border-bottom: 1px solid #dddddd; } .table-responsive .table-header:not([class*="bg-"]) h2, @@ -423,7 +424,6 @@ table th[class*="col-"] { display: block; overflow-y: auto !important; overflow-x: hidden !important; - /*border-bottom: 1px solid #eee;*/ } .thead-fixed > tr, .tbody-fixed > tr { @@ -559,12 +559,15 @@ table th[class*="col-"] { } .col-resizer { - position: absolute; - top: 0; - right: 0; - width: 5px; + width: 3px; + min-height: 20px; cursor: col-resize; height: 100%; + background-color: #f3f3f3; + order: 9999999; + margin-left: 5px; + border-radius: 2px; + flex-shrink: 0; } .sticky-col { @@ -588,4 +591,76 @@ table th[class*="col-"] { .table-responsive[disabled] { pointer-events: none; +} +.dui-pinned-cell { + position: sticky; +} +.table-cm-header { + white-space: nowrap; +} + +.table-cm-filter, +.table-cm-header, +.dt-th-cell, +.dt-td-cell { + background-color: inherit; +} + +.card .body:not([class*="bg-"]) { + background-color: inherit; +} + +.table-responsive thead, +.table-responsive thead tr, +.table-responsive tbody { + background-color: inherit; +} + +.table-responsive.table-bordered { + border: 1px solid #ddd; +} + +.table-responsive thead tr th, +.table-responsive tbody tr:not(:last-child) th, +.table-responsive tbody tr:not(:last-child) td { + border-bottom: 1px solid #ddd; +} + +.table-responsive thead tr td.dui-pinned-cell.dui-pin-right-col, +.table-responsive thead tr th.dui-pinned-cell.dui-pin-right-col, +.table-responsive tbody tr td.dui-pinned-cell.dui-pin-right-col, +.table-responsive tbody tr th.dui-pinned-cell.dui-pin-right-col { + border-left: 1px solid #ddd; +} + +.table-responsive thead tr td.dui-pinned-cell.dui-pin-left-col, +.table-responsive thead tr th.dui-pinned-cell.dui-pin-left-col, +.table-responsive tbody tr td.dui-pinned-cell.dui-pin-left-col, +.table-responsive tbody tr th.dui-pinned-cell.dui-pin-left-col { + border-right: 1px solid #ddd; +} + +.table-responsive .table-bordered thead tr td:not(:last-child), +.table-responsive .table-bordered thead tr th:not(:last-child), +.table-responsive .table-bordered tbody tr td:not(:last-child), +.table-responsive .table-bordered tbody tr th:not(:last-child) { + border-right: 1px solid #ddd; +} + +.table-responsive .table { + border-collapse: separate !important; +} + +.dui-row-dnd-element { + cursor: grab; +} + +.dui-th-title-wrapper { + overflow: hidden; +} + +.dui-th-title-text-wrapper { + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; } \ No newline at end of file diff --git a/domino-ui/src/main/resources/org/dominokit/domino/ui/public/css/icons/domino-ui-icons.css b/domino-ui/src/main/resources/org/dominokit/domino/ui/public/css/icons/domino-ui-icons.css index 780e61169..25dfb58c2 100644 --- a/domino-ui/src/main/resources/org/dominokit/domino/ui/public/css/icons/domino-ui-icons.css +++ b/domino-ui/src/main/resources/org/dominokit/domino/ui/public/css/icons/domino-ui-icons.css @@ -5,6 +5,8 @@ border-radius: 50%; cursor: pointer; text-align: center; + display: inline-block; + overflow: hidden; } .clickable-icon.small-icon {