From 5df7e6efdc6935fbdfcdc7384c4fd9f2cb493f18 Mon Sep 17 00:00:00 2001 From: "Ahmad K. Bawaneh" Date: Tue, 3 Jan 2023 22:58:26 +0300 Subject: [PATCH] Data table data aggregation #727 --- .../domino/ui/datatable/ColumnConfig.java | 24 +- .../domino/ui/datatable/ColumnCssRule.java | 49 ++++ .../ui/datatable/ColumnCssRuleMeta.java | 49 ++-- .../domino/ui/datatable/DataTable.java | 72 +++--- .../domino/ui/datatable/TableConfig.java | 13 +- .../domino/ui/datatable/TableRow.java | 7 +- .../datatable/events/ColumnResizedEvent.java | 14 ++ .../ui/datatable/plugins/DataTablePlugin.java | 7 + .../datatable/plugins/ResizeColumnMeta.java | 19 ++ .../plugins/ResizeColumnsPlugin.java | 104 ++++---- .../ui/datatable/plugins/RowMarkerPlugin.java | 10 + .../plugins/pincolumns/PinColumnMeta.java | 137 +++-------- .../plugins/pincolumns/PinColumnsPlugin.java | 222 +++++++++++------- .../pincolumns/PinElementToColumn.java | 23 -- .../plugins/summary/SummaryCellRenderer.java | 72 ++++++ .../plugins/summary/SummaryMeta.java | 65 +++++ .../plugins/summary/SummaryPlugin.java | 60 +++++ .../datatable/plugins/summary/SummaryRow.java | 157 +++++++++++++ .../plugins/summary/SummaryRowCell.java | 94 ++++++++ .../domino/ui/utils/DynamicStyleSheet.java | 95 ++++++++ .../css/datatable/domino-ui-datatable.css | 54 +++-- 21 files changed, 996 insertions(+), 351 deletions(-) create mode 100644 domino-ui/src/main/java/org/dominokit/domino/ui/datatable/ColumnCssRule.java delete mode 100644 domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/pincolumns/PinElementToColumn.java create mode 100644 domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/summary/SummaryCellRenderer.java create mode 100644 domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/summary/SummaryMeta.java create mode 100644 domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/summary/SummaryPlugin.java create mode 100644 domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/summary/SummaryRow.java create mode 100644 domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/summary/SummaryRowCell.java create mode 100644 domino-ui/src/main/java/org/dominokit/domino/ui/utils/DynamicStyleSheet.java 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 0abb124df..a6644fe7a 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 @@ -881,6 +881,13 @@ private void renderChildColumns( col.createColumnElement(tableConfig) .clearElement() .setAttribute("rowspan", diff + ""); + ColumnCssRuleMeta.get(this) + .ifPresent( + meta -> + meta.cssRules() + .forEach( + columnCssRule -> + fillHeader.addCss(columnCssRule.getCssClass()))); ColumnHeaderMeta.get(col) .ifPresent( columnHeaderMeta -> columnHeaderMeta.addExtraHeadElement(fillHeader)); @@ -916,6 +923,11 @@ private DominoElement createColumnElement(TableConfig t th.addCss("dui-column-group"); } + ColumnCssRuleMeta.get(this) + .ifPresent( + meta -> + meta.cssRules().forEach(columnCssRule -> th.addCss(columnCssRule.getCssClass()))); + applyScreenMedia(th.element()); setHeadElement(th.element()); @@ -950,32 +962,32 @@ public ColumnConfig applyAndOnSubColumns( return this; } - public ColumnConfig applyAndOnFirstSubColumn(Consumer> handler) { + public ColumnConfig applyAndOnEachFirstSubColumn(Consumer> handler) { handler.accept(this); if (isColumnGroup()) { - getSubColumns().get(0).applyAndOnFirstSubColumn(handler); + getSubColumns().get(0).applyAndOnEachFirstSubColumn(handler); } return this; } public ColumnConfig onFirstSubColumn(Consumer> handler) { if (isColumnGroup()) { - getSubColumns().get(0).applyAndOnFirstSubColumn(handler); + getSubColumns().get(0).applyAndOnEachFirstSubColumn(handler); } return this; } - public ColumnConfig applyAndOnLastSubColumn(Consumer> handler) { + public ColumnConfig applyAndOnEachLastSubColumn(Consumer> handler) { handler.accept(this); if (isColumnGroup()) { - getSubColumns().get(getSubColumns().size() - 1).applyAndOnLastSubColumn(handler); + getSubColumns().get(getSubColumns().size() - 1).applyAndOnEachLastSubColumn(handler); } return this; } public ColumnConfig onEachLastSubColumn(Consumer> handler) { if (isColumnGroup()) { - getSubColumns().get(getSubColumns().size() - 1).applyAndOnLastSubColumn(handler); + getSubColumns().get(getSubColumns().size() - 1).applyAndOnEachLastSubColumn(handler); } return this; } diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/ColumnCssRule.java b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/ColumnCssRule.java new file mode 100644 index 000000000..848bc4612 --- /dev/null +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/ColumnCssRule.java @@ -0,0 +1,49 @@ +/* + * 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.CSSRule; + +public class ColumnCssRule { + + private final String key; + private final String selector; + private final String cssClass; + private final CSSRule cssRule; + + public ColumnCssRule(String key, String selector, String cssClass, CSSRule cssRule) { + this.key = key; + this.selector = selector; + this.cssClass = cssClass; + this.cssRule = cssRule; + } + + public String getKey() { + return key; + } + + public String getSelector() { + return selector; + } + + public String getCssClass() { + return cssClass; + } + + public CSSRule getCssRule() { + return cssRule; + } +} diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/ColumnCssRuleMeta.java b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/ColumnCssRuleMeta.java index 3d434b1d2..a81fefb34 100644 --- a/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/ColumnCssRuleMeta.java +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/ColumnCssRuleMeta.java @@ -15,35 +15,56 @@ */ package org.dominokit.domino.ui.datatable; -import elemental2.dom.CSSRule; +import elemental2.dom.HTMLDivElement; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; import java.util.Optional; +import org.dominokit.domino.ui.utils.DynamicStyleSheet; -public class ColumnCssRuleMeta implements ColumnMeta { +public class ColumnCssRuleMeta implements ColumnMeta { public static final String COLUMN_CSS_RULE_META = "column-css-rule-meta"; + public static final String DEFAULT_RULE = "COLUMN-DEFAULT-CSS-RULE"; - private final CSSRule cssRule; - private final String selector; + private final Map cssRules = new HashMap<>(); + private final DynamicStyleSheet> dynamicStyleSheet; - public static ColumnCssRuleMeta of(CSSRule cssRule, String selector) { - return new ColumnCssRuleMeta(cssRule, selector); + static ColumnCssRuleMeta of( + DynamicStyleSheet> dynamicStyleSheet) { + return new ColumnCssRuleMeta<>(dynamicStyleSheet); } - public ColumnCssRuleMeta(CSSRule cssRule, String selector) { - this.cssRule = cssRule; - this.selector = selector; + private ColumnCssRuleMeta(DynamicStyleSheet> dynamicStyleSheet) { + this.dynamicStyleSheet = dynamicStyleSheet; } - public static Optional get(ColumnConfig column) { + public static Optional> get(ColumnConfig column) { return column.getMeta(COLUMN_CSS_RULE_META); } - public CSSRule getCssRule() { - return cssRule; + public ColumnCssRuleMeta addRule(String key, String cssClass) { + DynamicStyleSheet.DynamicCssRule dynamicCssRule = dynamicStyleSheet.insertRule(cssClass); + cssRules.put( + key, + new ColumnCssRule( + key, + dynamicCssRule.getSelector(), + dynamicCssRule.getClassName(), + dynamicCssRule.getCssRule())); + return this; } - public String getSelector() { - return selector; + public Optional getColumnCssRule(String key) { + return Optional.ofNullable(cssRules.get(key)); + } + + public Map getCssRules() { + return cssRules; + } + + public Collection cssRules() { + return cssRules.values(); } @Override 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 46bc4e4ac..dc953fe3a 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 @@ -15,7 +15,6 @@ */ package org.dominokit.domino.ui.datatable; -import static elemental2.dom.DomGlobal.document; import static java.util.Objects.nonNull; import static org.dominokit.domino.ui.datatable.DataTableStyles.TABLE; import static org.dominokit.domino.ui.datatable.DataTableStyles.TABLE_BORDERED; @@ -24,10 +23,7 @@ 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.jboss.elemento.Elements.div; -import static org.jboss.elemento.Elements.table; -import static org.jboss.elemento.Elements.tbody; -import static org.jboss.elemento.Elements.thead; +import static org.jboss.elemento.Elements.*; import elemental2.dom.*; import java.util.ArrayList; @@ -41,6 +37,7 @@ import org.dominokit.domino.ui.datatable.store.DataStore; import org.dominokit.domino.ui.utils.BaseDominoElement; import org.dominokit.domino.ui.utils.DominoElement; +import org.dominokit.domino.ui.utils.DynamicStyleSheet; import org.dominokit.domino.ui.utils.HasSelectionSupport; import org.jboss.elemento.EventType; @@ -57,7 +54,7 @@ public class DataTable extends BaseDominoElement /** Use this constant as flag value to check if a row in the data tables have been filtered out */ public static final String DATA_TABLE_ROW_FILTERED = "data-table-row-filtered"; - private static final String PARENT_SELECTOR_PREFIX = "dt-"; + private static final String PARENT_SELECTOR_PREFIX = "dui-dt-"; private final DataStore dataStore; private DominoElement root = DominoElement.of(div()).css(TABLE_RESPONSIVE); @@ -66,6 +63,7 @@ public class DataTable extends BaseDominoElement private TableConfig tableConfig; private DominoElement tbody = DominoElement.of(tbody()); private DominoElement thead = DominoElement.of(thead()); + private DominoElement tfoot = DominoElement.of(tfoot()); private List data = new ArrayList<>(); private boolean selectable = true; private List> tableRows = new ArrayList<>(); @@ -89,8 +87,8 @@ public class DataTable extends BaseDominoElement evt.stopPropagation(); } }; - private HTMLStyleElement styleElement; - private CSSStyleSheet styleSheet; + + private DynamicStyleSheet> dynamicStyleSheet; /** * Creates a new data table instance @@ -101,10 +99,21 @@ public class DataTable extends BaseDominoElement public DataTable(TableConfig tableConfig, DataStore dataStore) { super.init(this); this.tableConfig = tableConfig; + this.events.put(ANY, new ArrayList<>()); this.dataStore = dataStore; this.addTableEventListener(ANY, dataStore); + tableElement.setAttribute("dui-data-v-scroll", 0); + tableElement.setAttribute("dui-data-h-scroll", 0); this.addEventListener(EventType.keydown.getName(), disableKeyboardListener, true); + tableElement.addEventListener( + "scroll", + evt -> { + double scrollTop = new Double(tableElement.element().scrollTop).intValue(); + double scrollLeft = new Double(tableElement.element().scrollLeft).intValue(); + tableElement.setAttribute("dui-data-v-scroll", scrollTop); + tableElement.setAttribute("dui-data-h-scroll", scrollLeft); + }); this.dataStore.onDataChanged( dataChangedEvent -> { fireTableEvent( @@ -125,48 +134,26 @@ public DataTable(TableConfig tableConfig, DataStore dataStore) { fireTableEvent(new TableDataUpdatedEvent<>(this.data, dataChangedEvent.getTotalCount())); }); - initStyleSheet(); + initDynamicStyleSheet(); init(); } - private void initStyleSheet() { - this.styleElement = (HTMLStyleElement) document.createElement("style"); - this.styleElement.type = "text/css"; - this.styleElement.id = tableElement.getDominoId() + "styles"; - document.head.append(this.styleElement); - this.styleSheet = (CSSStyleSheet) this.styleElement.sheet; - - tableElement.addCss(PARENT_SELECTOR_PREFIX + tableElement.getDominoId()); - String rule = "." + PARENT_SELECTOR_PREFIX + tableElement.getDominoId() + " {" + "}"; - this.styleSheet.insertRule(rule, 0); + private void initDynamicStyleSheet() { + this.dynamicStyleSheet = new DynamicStyleSheet<>(PARENT_SELECTOR_PREFIX, this); tableConfig .getColumnsGrouped() .forEach( col -> { col.applyAndOnSubColumns( column -> { - int index = - styleSheet.insertRule( - "." - + PARENT_SELECTOR_PREFIX - + tableElement.getDominoId() - + " ." - + PARENT_SELECTOR_PREFIX - + "col-" - + column.getName().replace(" ", "-") - + "{}", - styleSheet.cssRules.length); - column.applyMeta( - ColumnCssRuleMeta.of( - styleSheet.cssRules.item(index), - PARENT_SELECTOR_PREFIX + "col-" + column.getName().replace(" ", "-"))); + ColumnCssRuleMeta columnCssRuleMeta = + ColumnCssRuleMeta.of(this.dynamicStyleSheet); + columnCssRuleMeta.addRule( + ColumnCssRuleMeta.DEFAULT_RULE, + "col-" + column.getName().replace(" ", "-")); + column.applyMeta(columnCssRuleMeta); }); }); - - tableElement.onDetached( - mutationRecord -> { - document.head.removeChild(this.styleElement); - }); } public DataStore getDataStore() { @@ -188,6 +175,8 @@ private DataTable init() { tableConfig.onAfterHeaders(this); tableElement.appendChild(tbody); tableConfig.getPlugins().forEach(plugin -> plugin.onBodyAdded(DataTable.this)); + tableElement.appendChild(tfoot); + tableConfig.getPlugins().forEach(plugin -> plugin.onFooterAdded(DataTable.this)); root.appendChild(tableElement); tableConfig.getPlugins().forEach(plugin -> plugin.onAfterAddTable(DataTable.this)); if (!tableConfig.isLazyLoad()) { @@ -409,6 +398,11 @@ public DominoElement headerElement() { return thead; } + /** @return the {@link HTMLTableSectionElement} -tfoot- wrapped as {@link DominoElement} */ + public DominoElement footerElement() { + return tfoot; + } + /** @return the applied {@link TableConfig} of this table */ public TableConfig getTableConfig() { return tableConfig; 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 3ff1dee6b..09d035a38 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 @@ -131,10 +131,7 @@ public void drawHeaders(DataTable dataTable, DominoElement { - col.renderHeader(dataTable, this, headers); - }); + columns.forEach(col -> col.renderHeader(dataTable, this, headers)); if (!thead.isAttached()) { dataTable.tableElement().appendChild(thead); } @@ -156,7 +153,7 @@ public void drawRecord(DataTable dataTable, TableRow tableRow) { rowAppender.appendRow(dataTable, tableRow); } - plugins.forEach(plugin -> plugin.onRowAdded(dataTable, tableRow)); + getPlugins().forEach(plugin -> plugin.onRowAdded(dataTable, tableRow)); } private boolean isOdd(int index) { @@ -323,7 +320,7 @@ public void setRowAppender(RowAppender rowAppender) { /** @return the {@link List} of plugins added to the table */ public List> getPlugins() { - return plugins; + return plugins.stream().sorted().collect(Collectors.toList()); } /** @@ -333,7 +330,7 @@ public List> getPlugins() { * @param dataTable the {@link DataTable} initialized with this configuration */ void onBeforeHeaders(DataTable dataTable) { - plugins.forEach(plugin -> plugin.onBeforeAddHeaders(dataTable)); + getPlugins().forEach(plugin -> plugin.onBeforeAddHeaders(dataTable)); } /** @@ -343,7 +340,7 @@ void onBeforeHeaders(DataTable dataTable) { * @param dataTable the {@link DataTable} initialized with this configuration */ void onAfterHeaders(DataTable dataTable) { - plugins.forEach(plugin -> plugin.onAfterAddHeaders(dataTable)); + getPlugins().forEach(plugin -> plugin.onAfterAddHeaders(dataTable)); } /** @return a {@link List} of all non grouping {@link ColumnConfig} added to the table */ 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 66d4f10f2..81bec27f3 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 @@ -328,7 +328,12 @@ public void renderCell(ColumnConfig columnConfig) { } ColumnCssRuleMeta.get(columnConfig) - .ifPresent(meta -> DominoElement.of(cellElement).addCss(meta.getSelector())); + .ifPresent( + meta -> + meta.cssRules() + .forEach( + columnCssRule -> + DominoElement.of(cellElement).addCss(columnCssRule.getCssClass()))); RowCell rowCell = new RowCell<>(new CellRenderer.CellInfo<>(this, cellElement), columnConfig); 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 9dbcd802b..a332c3b7b 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 @@ -22,14 +22,24 @@ public class ColumnResizedEvent implements TableEvent { public static final String COLUMN_RESIZED = "column-resized"; private final ColumnConfig column; private final double sizeDiff; + private final boolean completed; public static ColumnResizedEvent of(ColumnConfig column, double sizeDiff) { return new ColumnResizedEvent(column, sizeDiff); } + public static ColumnResizedEvent of(ColumnConfig column, double sizeDiff, boolean completed) { + return new ColumnResizedEvent(column, sizeDiff, completed); + } + public ColumnResizedEvent(ColumnConfig column, double sizeDiff) { + this(column, sizeDiff, false); + } + + public ColumnResizedEvent(ColumnConfig column, double sizeDiff, boolean completed) { this.column = column; this.sizeDiff = sizeDiff; + this.completed = completed; } public ColumnConfig getColumn() { @@ -40,6 +50,10 @@ public double getSizeDiff() { return sizeDiff; } + public boolean isCompleted() { + return completed; + } + @Override public String getType() { return COLUMN_RESIZED; 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 e741fa327..736864b0a 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 @@ -138,4 +138,11 @@ default Optional> getUtilityElements( DataTable dataTable, CellRenderer.CellInfo cellInfo) { return Optional.empty(); } + + /** + * Will be called when the footer element is appended to the table element + * + * @param datatable {@link DataTable} + */ + default void onFooterAdded(DataTable datatable) {} } 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 index 6b52ffed2..8149a1d69 100644 --- 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 @@ -116,6 +116,25 @@ public String suppliedMaxWidthOrOriginal(String maxWidth) { } } + public String suppliedMinWidthOrOriginal(String minWidth) { + if (isNull(originalMinWidth) || !originalMinWidth.contains("px")) { + return minWidth; + } + + try { + int original = JsNumber.parseInt(originalMinWidth, 10); + int supplied = JsNumber.parseInt(minWidth, 10); + + if (supplied < original) { + return originalMinWidth; + } else { + return minWidth; + } + } catch (Exception e) { + return minWidth; + } + } + @Override public String getKey() { return 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 0add84e71..ddd5799a5 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 @@ -79,28 +79,7 @@ public void onHeaderAdded(DataTable dataTable, ColumnConfig column) { meta -> { double currentPosition = mouseEvent.clientX; double diff = currentPosition - meta.getStartPosition(); - String width = px.of(meta.getInitialWidth() + diff); - - col.getHeadElement().setWidth(width); - col.setWidth(width); - if (config.isClipContent()) { - String maxWidth = meta.suppliedMaxWidthOrOriginal(width); - col.getHeadElement().setMaxWidth(maxWidth); - col.maxWidth(maxWidth); - ColumnCssRuleMeta.get(col) - .ifPresent( - cssMeta -> - cssMeta.getCssRule().style.maxWidth = - CSSProperties.MaxWidthUnionType.of( - maxWidth)); - } - - ColumnHeaderMeta.get(col) - .ifPresent( - headersMeta -> - headersMeta - .getExtraHeadElements() - .forEach(header -> header.setWidth(width))); + resizeColumn(col, meta, diff); }); }); @@ -111,26 +90,7 @@ public void onHeaderAdded(DataTable dataTable, ColumnConfig column) { meta -> { double currentPosition = mouseEvent.clientX; double diff = currentPosition - meta.getStartPosition(); - String width = px.of(meta.getInitialWidth() + diff); - col.getHeadElement().setWidth(width); - col.setWidth(width); - if (config.isClipContent()) { - String maxWidth = meta.suppliedMaxWidthOrOriginal(width); - col.getHeadElement().setMaxWidth(maxWidth); - col.maxWidth(maxWidth); - ColumnCssRuleMeta.get(col) - .ifPresent( - cssMeta -> - cssMeta.getCssRule().style.maxWidth = - CSSProperties.MaxWidthUnionType.of( - maxWidth)); - } - ColumnHeaderMeta.get(col) - .ifPresent( - headersMeta -> - headersMeta - .getExtraHeadElements() - .forEach(header -> header.setWidth(width))); + resizeColumn(col, meta, diff); }); }); @@ -167,23 +127,65 @@ public void onHeaderAdded(DataTable dataTable, ColumnConfig column) { EventType.mousemove.getName(), resizeListener); } }); - resizeElement.addEventListener( - EventType.mouseup.getName(), - evt -> { - DominoDom.document.body.removeEventListener( - EventType.mousemove.getName(), resizeListener); - }); - DominoDom.document.body.addEventListener( - EventType.mouseup.getName(), + EventListener stopResizing = evt -> { + ResizeColumnMeta.get(column) + .ifPresent( + meta -> { + MouseEvent mouseEvent = Js.uncheckedCast(evt); + double currentPosition = mouseEvent.clientX; + double diff = currentPosition - meta.getStartPosition(); + + dataTable.fireTableEvent(ColumnResizedEvent.of(column, diff, true)); + }); + DominoDom.document.body.removeEventListener( EventType.mousemove.getName(), resizeListener); - }); + }; + resizeElement.addEventListener(EventType.mouseup.getName(), stopResizing); + DominoDom.document.body.addEventListener(EventType.mouseup.getName(), stopResizing); column.appendChild(FlexItem.of(resizeElement)); } }); } + private void resizeColumn(ColumnConfig col, ResizeColumnMeta meta, double diff) { + + DomGlobal.requestAnimationFrame( + timestamp -> { + String width = px.of(meta.getInitialWidth() + diff); + + col.setWidth(width); + + String minWidth = meta.suppliedMinWidthOrOriginal(width); + + if (config.isClipContent()) { + String maxWidth = meta.suppliedMaxWidthOrOriginal(width); + col.maxWidth(maxWidth); + ColumnCssRuleMeta.get(col) + .flatMap(cssMeta -> cssMeta.getColumnCssRule(ColumnCssRuleMeta.DEFAULT_RULE)) + .ifPresent( + columnCssRule -> + columnCssRule.getCssRule().style.maxWidth = + CSSProperties.MaxWidthUnionType.of(maxWidth)); + } + + ColumnCssRuleMeta.get(col) + .flatMap(cssMeta -> cssMeta.getColumnCssRule(ColumnCssRuleMeta.DEFAULT_RULE)) + .ifPresent( + columnCssRule -> { + CSSStyleDeclaration style = columnCssRule.getCssRule().style; + style.minWidth = CSSProperties.MinWidthUnionType.of(minWidth); + style.width = CSSProperties.WidthUnionType.of(width); + }); + + ColumnHeaderMeta.get(col) + .ifPresent( + headersMeta -> + headersMeta.getExtraHeadElements().forEach(header -> header.setWidth(width))); + }); + } + @Override public void onBeforeAddCell(DataTable dataTable, TableRow tableRow, RowCell rowCell) { if (config.isClipContent()) { diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/RowMarkerPlugin.java b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/RowMarkerPlugin.java index 94c92264a..ed835647c 100644 --- a/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/RowMarkerPlugin.java +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/RowMarkerPlugin.java @@ -16,6 +16,7 @@ package org.dominokit.domino.ui.datatable.plugins; import java.util.Optional; +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.RowRecordUpdatedEvent; @@ -54,6 +55,10 @@ private void setStyle(TableRow tableRow) { Optional.ofNullable(colorScheme) .map(scheme -> colorScheme.color().getHex()) .orElse("transparent"); + ColumnConfig firstColumn = tableRow.getDataTable().getTableConfig().getColumns().get(0); + DominoElement.of(tableRow.getCell(firstColumn.getName()).getCellInfo().getElement()) + .setCssProperty("border-left-color", color); + DominoElement.of(tableRow.element()).setCssProperty("border-left-color", color); } @@ -73,6 +78,11 @@ public RowMarkerPlugin(MarkerColor markerColor) { this.markerColor = markerColor; } + @Override + public int order() { + return Integer.MAX_VALUE; + } + /** * An interface to implement different color markers * 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 index f465636dd..53c7c7e68 100644 --- 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 @@ -15,21 +15,16 @@ */ package org.dominokit.domino.ui.datatable.plugins.pincolumns; -import elemental2.dom.HTMLElement; +import static org.dominokit.domino.ui.datatable.plugins.pincolumns.PinColumnsPlugin.PIN_COLUMNS_CSS_RULE; + +import elemental2.dom.CSSStyleDeclaration; 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.ColumnCssRuleMeta; 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"; +public class PinColumnMeta implements ColumnMeta, PinColumnFunction { private final PinDirection direction; @@ -56,7 +51,7 @@ public static boolean isPinRight(ColumnConfig column) { } public static boolean isPinned(ColumnConfig column) { - return !(PinColumnMeta.isPinLeft(column) || PinColumnMeta.isPinRight(column)); + return (PinColumnMeta.isPinLeft(column) || PinColumnMeta.isPinRight(column)); } public PinColumnMeta(PinDirection direction) { @@ -81,57 +76,26 @@ 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); - if (column.getHeadElement().containsCss(dui_pin_left_col)) { - element.addCss(dui_pin_left_col); - } else { - element.removeCss(dui_pin_left_col); - } - element.removeCss(dui_pin_right_col); - element - .setCssProperty("left", column.getHeadElement().getAttribute("pin-left-data") + "px") - .setCssProperty("right", "auto"); - }), - RIGHT( - PinColumnMeta::pinHeaderRight, - (column, element) -> { - element.addCss(dui_pinned_cell); - if (column.getHeadElement().containsCss(dui_pin_right_col)) { - element.addCss(dui_pin_right_col); - } else { - element.removeCss(dui_pin_right_col); - } - element.removeCss(dui_pin_left_col); - element - .setCssProperty( - "right", column.getHeadElement().getAttribute("pin-right-data") + "px") - .setCssProperty("left", "auto"); - }); + public enum PinDirection implements PinColumnFunction { + LEFT(PinDirection::pinHeaderLeft), + RIGHT(PinColumnMeta::pinHeaderRight); private PinColumnFunction pinColumnFunction; - private PinElementToColumn pinElementToColumn; - PinDirection(PinColumnFunction pinColumnFunction, PinElementToColumn pinElementToColumn) { + PinDirection(PinColumnFunction pinColumnFunction) { 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); + ColumnCssRuleMeta.get(column) + .flatMap(cssMeta -> cssMeta.getColumnCssRule(PIN_COLUMNS_CSS_RULE)) + .ifPresent( + pinCssRule -> { + CSSStyleDeclaration style = pinCssRule.getCssRule().style; + style.right = "auto"; + style.position = "sticky"; + style.left = left + "px"; + }); if (column.isColumnGroup()) { double[] childOffset = new double[] {left}; @@ -143,44 +107,26 @@ private static double pinHeaderLeft(ColumnConfig column, double left) { 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); + + return left + column.getHeadElement().getBoundingClientRect().width; } @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").setCssProperty("right", "auto"); - return left + element.getBoundingClientRect().width; } private static double pinHeaderRight(ColumnConfig column, double right) { - column.getHeadElement().addCss(dui_pinned_right); - column.getHeadElement().setAttribute("pin-right-data", right); + ColumnCssRuleMeta.get(column) + .flatMap(cssMeta -> cssMeta.getColumnCssRule(PIN_COLUMNS_CSS_RULE)) + .ifPresent( + pinCssRule -> { + CSSStyleDeclaration style = pinCssRule.getCssRule().style; + style.left = "auto"; + style.position = "sticky"; + style.right = right + "px"; + }); if (column.isColumnGroup()) { double[] childOffset = new double[] {right}; List> subColumns = column.getSubColumns(); @@ -190,28 +136,7 @@ private static double pinHeaderRight(ColumnConfig column, double right) { 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").setCssProperty("left", "auto"); - return right + element.getBoundingClientRect().width; + return right + column.getHeadElement().getBoundingClientRect().width; } } 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 index 79461b6b7..0cabdd90e 100644 --- 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 @@ -15,12 +15,15 @@ */ package org.dominokit.domino.ui.datatable.plugins.pincolumns; +import static java.util.Objects.nonNull; + +import elemental2.dom.CSSStyleDeclaration; import elemental2.dom.DomGlobal; -import elemental2.dom.HTMLTableCellElement; import java.util.List; +import jsinterop.base.Js; import org.dominokit.domino.ui.datatable.ColumnConfig; +import org.dominokit.domino.ui.datatable.ColumnCssRuleMeta; 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; @@ -30,7 +33,6 @@ 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; /** @@ -49,7 +51,7 @@ * * * - * to pin a column to the left apply a {@link PinColumnMeta} to the column with right direction + *

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

  *     
@@ -57,29 +59,45 @@
  *     
  * 
* - * The pin menu and pin icon are both configurable and are disabled by default. + *

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"; + public static final String PIN_COLUMNS_CSS_RULE = "PIN-COLUMNS-CSS-RULE"; + private DataTable datatable; private FlexItem pinLeftIcon; private FlexItem pinRightIcon; private PinColumnsConfig config = PinColumnsConfig.of(); + private ColumnConfig pinLeftColumn; + private ColumnConfig pinRightColumn; + @Override public void init(DataTable dataTable) { this.datatable = dataTable; this.pinLeftIcon = FlexItem.of(config.getPinLeftIcon()).setOrder(100); this.pinRightIcon = FlexItem.of(config.getPinRightIcon()).setOrder(100); + + datatable + .getTableConfig() + .getColumnsGrouped() + .forEach( + group -> + group.applyAndOnSubColumns( + col -> { + ColumnCssRuleMeta.get(col) + .ifPresent( + meta -> { + meta.addRule( + PIN_COLUMNS_CSS_RULE, + "col-pin-" + col.getName().replace(" ", "_")); + }); + })); } @Override @@ -121,7 +139,6 @@ public void onAfterAddHeaders(DataTable dataTable) { } }); - onBeforeSetPinColumn(); List> columns = datatable.getTableConfig().getColumns(); columns.stream() .filter(PinColumnMeta::isPinLeft) @@ -137,9 +154,20 @@ public void onAfterAddHeaders(DataTable dataTable) { private void onBeforeSetPinColumn() { 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)); + .getTableConfig() + .getColumnsGrouped() + .forEach( + group -> + group.applyAndOnSubColumns( + column -> { + ColumnCssRuleMeta.get(column) + .flatMap(cssMeta -> cssMeta.getColumnCssRule(PIN_COLUMNS_CSS_RULE)) + .ifPresent( + pinCssRule -> { + CSSStyleDeclaration style = pinCssRule.getCssRule().style; + style.removeProperty("position"); + }); + })); } public void setPinRightColumn(ColumnConfig pinRightColumn) { @@ -152,16 +180,10 @@ public void setPinRightColumn(ColumnConfig pinRightColumn) { } }); - datatable - .headerElement() - .querySelectorAll("." + dui_pin_right_col) - .forEach(element -> element.removeCss(dui_pin_right_col)); if (config.isShowPinIcon()) { pinRightColumn.getGrandParent().removeChild(pinLeftIcon).appendChild(pinRightIcon); } - pinRightColumn - .getGrandParent() - .applyAndOnFirstSubColumn(col -> col.getHeadElement().addCss(dui_pin_right_col)); + pinColumnsRight(pinRightColumn); } @@ -177,10 +199,6 @@ public void unpinColumn(ColumnConfig column) { 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( @@ -193,10 +211,6 @@ public void unpinLeftColumns() { 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( @@ -216,22 +230,46 @@ public void setPinLeftColumn(ColumnConfig pinLeftColumn) { unpinRightColumns(); } }); - datatable - .headerElement() - .querySelectorAll("." + dui_pin_left_col) - .forEach(element -> element.removeCss(dui_pin_left_col)); + if (config.isShowPinIcon()) { pinLeftColumn.getGrandParent().removeChild(pinRightIcon).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()); + if (nonNull(this.pinLeftColumn)) { + this.pinLeftColumn + .getGrandParent() + .applyAndOnSubColumns( + column -> { + ColumnCssRuleMeta.get(column) + .flatMap(cssMeta -> cssMeta.getColumnCssRule(PIN_COLUMNS_CSS_RULE)) + .ifPresent( + pinCssRule -> { + CSSStyleDeclaration style = pinCssRule.getCssRule().style; + style.removeProperty("border-left"); + style.removeProperty("border-right"); + }); + }); + } + + this.pinLeftColumn = pinLeftColumn; + this.pinLeftColumn + .getGrandParent() + .applyAndOnEachLastSubColumn( + column -> { + ColumnCssRuleMeta.get(column) + .flatMap(cssMeta -> cssMeta.getColumnCssRule(PIN_COLUMNS_CSS_RULE)) + .ifPresent( + pinCssRule -> { + CSSStyleDeclaration style = pinCssRule.getCssRule().style; + style.borderRight = "1px solid #ddd"; + style.removeProperty("border-left"); + }); + }); for (int i = 0; i <= columnIndex; i++) { columns.get(i).applyAndOnSubColumns(column -> column.applyMeta(PinColumnMeta.left())); } @@ -250,6 +288,36 @@ private void pinColumnsLeft(ColumnConfig pinLeftColumn) { private void pinColumnsRight(ColumnConfig pinRightColumn) { List> columns = datatable.getTableConfig().getColumnsGrouped(); int columnIndex = columns.indexOf(pinRightColumn.getGrandParent()); + if (nonNull(this.pinRightColumn)) { + this.pinRightColumn + .getGrandParent() + .applyAndOnSubColumns( + column -> { + ColumnCssRuleMeta.get(column) + .flatMap(cssMeta -> cssMeta.getColumnCssRule(PIN_COLUMNS_CSS_RULE)) + .ifPresent( + pinCssRule -> { + CSSStyleDeclaration style = pinCssRule.getCssRule().style; + style.removeProperty("border-left"); + style.removeProperty("border-right"); + }); + }); + } + this.pinRightColumn = pinRightColumn; + this.pinRightColumn + .getGrandParent() + .applyAndOnEachFirstSubColumn( + column -> { + ColumnCssRuleMeta.get(column) + .flatMap(cssMeta -> cssMeta.getColumnCssRule(PIN_COLUMNS_CSS_RULE)) + .ifPresent( + pinCssRule -> { + CSSStyleDeclaration style = pinCssRule.getCssRule().style; + style.borderLeft = "1px solid #ddd"; + style.removeProperty("border-right"); + }); + }); + for (int i = 0; i < columnIndex; i++) { columns .get(i) @@ -273,10 +341,18 @@ public void handleEvent(TableEvent event) { applyPinnedColumns(); break; case ColumnResizedEvent.COLUMN_RESIZED: - DomGlobal.requestAnimationFrame( - timestamp -> { + ColumnResizedEvent columnResizedEvent = Js.uncheckedCast(event); + ColumnConfig column = columnResizedEvent.getColumn(); + if (PinColumnMeta.isPinned(column)) { + if (columnResizedEvent.getSizeDiff() > 0) { + if (columnResizedEvent.isCompleted()) { applyPinnedColumns(); - }); + } + } else { + applyPinnedColumns(); + } + } + break; } } @@ -291,6 +367,7 @@ private void applyPinnedColumns() { } private void pinColumns() { + onBeforeSetPinColumn(); if (datatable.isAttached()) { pinColumnsForAttachedTable(); } else { @@ -302,48 +379,31 @@ private void pinColumns() { 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); - } - } - }); + DomGlobal.requestAnimationFrame( + timestamp -> { + 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]); + } + } + }); }); } 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 deleted file mode 100644 index c7277a7c3..000000000 --- a/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/pincolumns/PinElementToColumn.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * 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/datatable/plugins/summary/SummaryCellRenderer.java b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/summary/SummaryCellRenderer.java new file mode 100644 index 000000000..26045e2ba --- /dev/null +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/summary/SummaryCellRenderer.java @@ -0,0 +1,72 @@ +/* + * 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.summary; + +import elemental2.dom.HTMLTableCellElement; +import elemental2.dom.Node; + +/** + * An implementation of this interface will determine in how a table cell content will be displayed + * and rendered in the table + * + * @param the type of the data table records + */ +@FunctionalInterface +public interface SummaryCellRenderer { + /** + * Takes a cell info to determine what content to append to the cell element + * + * @param summaryCellInfo {@link SummaryCellInfo} + * @return the {@link Node} to be appended to the table cell element + */ + Node asElement(SummaryCellInfo summaryCellInfo); + + /** + * A class containing all information required about a cell in a data table + * + * @param the type of the data table records + */ + class SummaryCellInfo { + private final SummaryRow summaryRow; + private final HTMLTableCellElement element; + + /** + * Creates an instance with the row that the belongs to and the cell html element + * + * @param summaryRow {@link SummaryRow} + * @param element {@link HTMLTableCellElement} the td element + */ + public SummaryCellInfo(SummaryRow summaryRow, HTMLTableCellElement element) { + this.summaryRow = summaryRow; + this.element = element; + } + + /** @return the {@link SummaryRow} the cell belongs to */ + public SummaryRow getSummaryRow() { + return summaryRow; + } + + /** @return the {@link HTMLTableCellElement}, the td element */ + public HTMLTableCellElement getElement() { + return element; + } + + /** @return T the record instance being rendered on the row this belongs to. */ + public S getRecord() { + return summaryRow.getRecord(); + } + } +} diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/summary/SummaryMeta.java b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/summary/SummaryMeta.java new file mode 100644 index 000000000..594208fad --- /dev/null +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/summary/SummaryMeta.java @@ -0,0 +1,65 @@ +/* + * 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.summary; + +import java.util.Objects; +import java.util.Optional; +import org.dominokit.domino.ui.datatable.ColumnConfig; +import org.dominokit.domino.ui.datatable.ColumnMeta; + +public class SummaryMeta implements ColumnMeta { + + public static final String COLUMN_SUMMARY_META = "column-summary-meta"; + + private SummaryCellRenderer cellRenderer; + private boolean skip = false; + + public static SummaryMeta of(SummaryCellRenderer cellRenderer) { + return new SummaryMeta<>(cellRenderer); + } + + public SummaryMeta(SummaryCellRenderer cellRenderer) { + Objects.requireNonNull(cellRenderer, "Summary cell renderer cant be null."); + this.cellRenderer = cellRenderer; + } + + public static Optional> get(ColumnConfig column) { + return column.getMeta(COLUMN_SUMMARY_META); + } + + public SummaryCellRenderer getCellRenderer() { + return cellRenderer; + } + + public SummaryMeta setCellRenderer(SummaryCellRenderer cellRenderer) { + this.cellRenderer = cellRenderer; + return this; + } + + public boolean isSkip() { + return skip; + } + + public SummaryMeta setSkip(boolean skip) { + this.skip = skip; + return this; + } + + @Override + public String getKey() { + return COLUMN_SUMMARY_META; + } +} diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/summary/SummaryPlugin.java b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/summary/SummaryPlugin.java new file mode 100644 index 000000000..739371e35 --- /dev/null +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/summary/SummaryPlugin.java @@ -0,0 +1,60 @@ +/* + * 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.summary; + +import elemental2.dom.HTMLTableSectionElement; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import org.dominokit.domino.ui.datatable.DataTable; +import org.dominokit.domino.ui.datatable.TableRow; +import org.dominokit.domino.ui.datatable.plugins.DataTablePlugin; +import org.dominokit.domino.ui.utils.BaseDominoElement; +import org.dominokit.domino.ui.utils.DominoElement; + +public class SummaryPlugin implements DataTablePlugin { + + private List> summaryRows = new ArrayList<>(); + private DataTable dataTable; + private DominoElement footer; + + @Override + public void init(DataTable dataTable) { + this.dataTable = dataTable; + } + + @Override + public void onFooterAdded(DataTable datatable) { + this.footer = datatable.footerElement(); + } + + public SummaryPlugin setSummaryRecords(Collection records) { + summaryRows.forEach(BaseDominoElement::remove); + summaryRows.clear(); + List recordsList = new ArrayList<>(records); + for (int i = 0; i < recordsList.size(); i++) { + SummaryRow summaryRow = new SummaryRow<>(recordsList.get(i), i, this.dataTable); + summaryRow.render(); + footer.appendChild(summaryRow); + } + return this; + } + + @Override + public int order() { + return 10; + } +} diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/summary/SummaryRow.java b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/summary/SummaryRow.java new file mode 100644 index 000000000..89689a4e8 --- /dev/null +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/summary/SummaryRow.java @@ -0,0 +1,157 @@ +/* + * 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.summary; + +import static org.jboss.elemento.Elements.*; + +import elemental2.dom.HTMLTableCellElement; +import elemental2.dom.HTMLTableRowElement; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.dominokit.domino.ui.datatable.ColumnConfig; +import org.dominokit.domino.ui.datatable.ColumnCssRuleMeta; +import org.dominokit.domino.ui.datatable.DataTable; +import org.dominokit.domino.ui.datatable.DefaultColumnShowHideListener; +import org.dominokit.domino.ui.utils.BaseDominoElement; +import org.dominokit.domino.ui.utils.DominoElement; + +public class SummaryRow extends BaseDominoElement> { + private S record; + private final int index; + private DataTable dataTable; + private final Map> rowCells = new HashMap<>(); + private HTMLTableRowElement element = tr().element(); + private SummaryRowRenderer rowRenderer = new DefaultSummaryRowRenderer<>(); + + public SummaryRow(S record, int index, DataTable dataTable) { + this.record = record; + this.index = index; + this.dataTable = dataTable; + init(this); + } + + public void setRecord(S record) { + this.record = record; + } + + public S getRecord() { + return record; + } + + public DataTable getDataTable() { + return dataTable; + } + + @Override + public HTMLTableRowElement element() { + return element; + } + + public void addCell(SummaryRowCell rowCell) { + rowCells.put(rowCell.getColumnConfig().getName(), rowCell); + } + + public SummaryRowCell getCell(String name) { + return rowCells.get(name); + } + + public int getIndex() { + return index; + } + + public void updateRow() { + updateRow(this.record); + } + + public void updateRow(S record) { + this.record = record; + rowCells.values().forEach(SummaryRowCell::updateCell); + } + + public Map> getRowCells() { + return Collections.unmodifiableMap(rowCells); + } + + public void render() { + rowRenderer.render(dataTable, this); + } + + /** + * An interface to implement listeners for Table row changes + * + * @param the type of the data table records + */ + @FunctionalInterface + public interface RowListener { + /** @param summaryRow the changed {@link SummaryRow} */ + void onChange(SummaryRow summaryRow); + } + + public void renderCell(ColumnConfig columnConfig) { + HTMLTableCellElement cellElement; + + cellElement = DominoElement.of(td()).css("dt-td-cell").element(); + + ColumnCssRuleMeta.get(columnConfig) + .ifPresent( + meta -> + meta.cssRules() + .forEach( + columnCssRule -> + DominoElement.of(cellElement).addCss(columnCssRule.getCssClass()))); + + SummaryRowCell rowCell = + new SummaryRowCell<>( + new SummaryCellRenderer.SummaryCellInfo<>(this, cellElement), columnConfig); + rowCell.updateCell(); + addCell(rowCell); + + columnConfig.applyScreenMedia(cellElement); + + if (columnConfig.isHidden()) { + DominoElement.of(cellElement).hide(); + } + element().appendChild(cellElement); + columnConfig.addShowHideListener(DefaultColumnShowHideListener.of(cellElement)); + } + + public interface SummaryRowRenderer { + void render(DataTable dataTable, SummaryRow summaryRow); + } + + private static class DefaultSummaryRowRenderer implements SummaryRowRenderer { + + @Override + public void render(DataTable dataTable, SummaryRow summaryRow) { + + List> columns = dataTable.getTableConfig().getColumns(); + for (int i = 0; i < columns.size(); i++) { + summaryRow.renderCell(columns.get(i)); + SummaryRowCell addedCell = summaryRow.rowCells.get(columns.get(i).getName()); + DominoElement cell = + DominoElement.of(addedCell.getCellInfo().getElement()); + if (cell.hasAttribute("colspan")) { + int colspan = Integer.parseInt(cell.getAttribute("colspan")); + if (colspan > 1) { + i += colspan - 1; + } + } + } + } + } +} diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/summary/SummaryRowCell.java b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/summary/SummaryRowCell.java new file mode 100644 index 000000000..ebdb5efbe --- /dev/null +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/summary/SummaryRowCell.java @@ -0,0 +1,94 @@ +/* + * 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.summary; + +import static java.util.Objects.nonNull; + +import elemental2.dom.HTMLTableCellElement; +import java.util.Optional; +import org.dominokit.domino.ui.datatable.CellRenderer; +import org.dominokit.domino.ui.datatable.ColumnConfig; +import org.dominokit.domino.ui.utils.DominoElement; +import org.dominokit.domino.ui.utils.TextNode; + +/** + * This class represent a single cell in a data table row, and it contains information about the + * cell row and column which it is part of + * + * @param the type of the data table records + */ +public class SummaryRowCell { + + private final ColumnConfig columnConfig; + private final SummaryCellRenderer.SummaryCellInfo cellInfo; + private SummaryCellRenderer defaultCellRenderer = cell -> TextNode.of(""); + + /** + * Creates and initialize an instance with the cell info and column info + * + * @param cellInfo the {@link CellRenderer.CellInfo} information about this cell + * @param columnConfig the {@link ColumnConfig} the column this cell is part of + */ + public SummaryRowCell( + SummaryCellRenderer.SummaryCellInfo cellInfo, ColumnConfig columnConfig) { + this.columnConfig = columnConfig; + this.cellInfo = cellInfo; + } + + /** @return the {@link ColumnConfig} the column this cell is part of */ + public ColumnConfig getColumnConfig() { + return columnConfig; + } + + /** + * This method will force update the cell which might result on clearing all it content and + * rerender them again with any updated data this is useful when for example changing a field + * value in the record instance, and we want to reflect the change to the cell that renders the + * field. + */ + public void updateCell() { + DominoElement cellElement = DominoElement.of(cellInfo.getElement()); + cellElement.clearElement(); + + if (nonNull(columnConfig.getMinWidth())) { + columnConfig.getHeadElement().style().setMinWidth(columnConfig.getMinWidth()); + } + + if (nonNull(columnConfig.getMaxWidth())) { + columnConfig.getHeadElement().style().setMaxWidth(columnConfig.getMaxWidth()); + } + + if (nonNull(columnConfig.getTextAlign())) { + cellElement.setTextAlign(columnConfig.getTextAlign()); + } + + if (nonNull(columnConfig.getHeaderTextAlign())) { + columnConfig.getHeadElement().style().setTextAlign(columnConfig.getHeaderTextAlign()); + } + + Optional> summaryMeta = SummaryMeta.get(columnConfig); + if (summaryMeta.isPresent()) { + cellElement.appendChild(summaryMeta.get().getCellRenderer().asElement(cellInfo)); + } else { + cellElement.appendChild(defaultCellRenderer.asElement(cellInfo)); + } + } + + /** @return the {@link CellRenderer.CellInfo} information about this cell */ + public SummaryCellRenderer.SummaryCellInfo getCellInfo() { + return cellInfo; + } +} diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/utils/DynamicStyleSheet.java b/domino-ui/src/main/java/org/dominokit/domino/ui/utils/DynamicStyleSheet.java new file mode 100644 index 000000000..da1fe7207 --- /dev/null +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/utils/DynamicStyleSheet.java @@ -0,0 +1,95 @@ +/* + * 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.utils; + +import static elemental2.dom.DomGlobal.document; + +import elemental2.dom.CSSRule; +import elemental2.dom.CSSStyleSheet; +import elemental2.dom.HTMLElement; +import elemental2.dom.HTMLStyleElement; + +public class DynamicStyleSheet> { + + private final String cssPrefix; + private final D target; + private final HTMLStyleElement styleElement; + private final CSSStyleSheet styleSheet; + + public DynamicStyleSheet(String cssPrefix, D target) { + this.cssPrefix = cssPrefix; + this.target = target; + + this.styleElement = (HTMLStyleElement) document.createElement("style"); + this.styleElement.type = "text/css"; + this.styleElement.id = target.getDominoId() + "styles"; + document.head.append(this.styleElement); + this.styleSheet = (CSSStyleSheet) this.styleElement.sheet; + + target.addCss(cssPrefix + target.getDominoId()); + String rule = "." + cssPrefix + target.getDominoId() + " {" + "}"; + this.styleSheet.insertRule(rule, 0); + + target.onDetached(mutationRecord -> document.head.removeChild(this.styleElement)); + } + + public DynamicCssRule insertRule(String cssClass) { + String ruleName = cssPrefix + cssClass; + + String selector = "." + cssPrefix + target.getDominoId() + " ." + ruleName; + + int index = styleSheet.insertRule(selector + "{}", styleSheet.cssRules.length); + CSSRule rule = styleSheet.cssRules.item(index); + + return new DynamicCssRule(selector, ruleName, rule); + } + + public void removeRule(CSSRule cssRule) { + styleSheet.deleteRule(styleSheet.cssRules.asList().indexOf(cssRule)); + } + + public HTMLStyleElement getStyleElement() { + return styleElement; + } + + public CSSStyleSheet getStyleSheet() { + return styleSheet; + } + + public static class DynamicCssRule { + private final String selector; + private final String className; + private final CSSRule cssRule; + + private DynamicCssRule(String selector, String className, CSSRule cssRule) { + this.selector = selector; + this.className = className; + this.cssRule = cssRule; + } + + public String getSelector() { + return selector; + } + + public String getClassName() { + return className; + } + + public CSSRule getCssRule() { + return cssRule; + } + } +} 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 003d44192..c25213a11 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 @@ -20,7 +20,7 @@ th { } .table { - border-bottom: 1px solid #eee; + /*border-bottom: 1px solid #eee;*/ margin-bottom: 0px; } @@ -80,7 +80,9 @@ th { .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{ +.table-bordered tbody tr td:last-child, +.table-bordered tfoot tr th:last-child, +.table-bordered tfoot tr td:last-child{ border-right: 1px solid #ddd; } @@ -506,14 +508,19 @@ table th[class*="col-"] { background-color: #ffffff; } -.table.dt-row-marker tbody tr, -.table.dt-row-marker thead tr { +.table.dt-row-marker tbody tr th:first-child, +.table.dt-row-marker tbody tr td:first-child, +.table.dt-row-marker thead tr th:first-child { border-left-width: 5px; border-left-style: solid; } +.table.dt-row-marker thead tr th:first-child { + border-left-color: transparent; +} -.table.dt-row-marker tbody tr.details-tr, -.table.dt-row-marker thead tr { +.table.dt-row-marker tbody tr.details-tr td:first-child, +.table.dt-row-marker tbody tr.details-tr th:first-child, +.table.dt-row-marker thead tr th:first-child { border-left-color: transparent; } @@ -624,29 +631,17 @@ table th[class*="col-"] { 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) { +.table-responsive .table-bordered tbody tr th:not(:last-child), +.table-responsive .table-bordered tfoot tr td:not(:last-child), +.table-responsive .table-bordered tfoot tr th:not(:last-child) { border-right: 1px solid #ddd; } .table-responsive .table { - border-collapse: collapse !important; + border-collapse: separate !important; } .dui-row-dnd-element { @@ -661,4 +656,19 @@ table th[class*="col-"] { text-overflow: ellipsis; overflow: hidden; white-space: nowrap; +} + +.table tfoot { + position: sticky; + bottom: 0; + background-color: #f9f9f9; + -webkit-box-shadow: 0px -1px 0px 0px rgba(221,221,221,1); + -moz-box-shadow: 0px -1px 0px 0px rgba(221,221,221,1); + box-shadow: 0px -1px 0px 0px rgba(221,221,221,1); +} + +.table:not([dui-data-v-scroll='0']) thead { + -webkit-box-shadow: 0px 1px 0px -1px rgba(221,221,221,1); + -moz-box-shadow: 0px 1px 0px -1px rgba(221,221,221,1); + box-shadow: 0px 1px 0px -1px rgba(221,221,221,1); } \ No newline at end of file