diff --git a/domino-ui-shared/src/main/java/org/dominokit/domino/ui/style/CssProperty.java b/domino-ui-shared/src/main/java/org/dominokit/domino/ui/style/CssProperty.java index a024fd939..97e6d8eaf 100644 --- a/domino-ui-shared/src/main/java/org/dominokit/domino/ui/style/CssProperty.java +++ b/domino-ui-shared/src/main/java/org/dominokit/domino/ui/style/CssProperty.java @@ -41,6 +41,72 @@ public static CssProperty of(String name, String value) { return new CssProperty(name, value); } + /** + * Creates a new {@link CssProperty} instance with the specified name and value. + * + * @param name The name of the CSS property. + * @param value The value of the CSS property. + * @return A new {@link CssProperty} instance. + */ + public static CssProperty of(String name, Number value) { + return new CssProperty(name, String.valueOf(value)); + } + + /** + * Creates a new {@link CssProperty} instance with the specified name and value. + * + * @param name The name of the CSS property. + * @param value The value of the CSS property. + * @return A new {@link CssProperty} instance. + */ + public static CssProperty of(String name, int value) { + return new CssProperty(name, String.valueOf(value)); + } + + /** + * Creates a new {@link CssProperty} instance with the specified name and value. + * + * @param name The name of the CSS property. + * @param value The value of the CSS property. + * @return A new {@link CssProperty} instance. + */ + public static CssProperty of(String name, double value) { + return new CssProperty(name, String.valueOf(value)); + } + + /** + * Creates a new {@link CssProperty} instance with the specified name and value. + * + * @param name The name of the CSS property. + * @param value The value of the CSS property. + * @return A new {@link CssProperty} instance. + */ + public static CssProperty of(String name, short value) { + return new CssProperty(name, String.valueOf(value)); + } + + /** + * Creates a new {@link CssProperty} instance with the specified name and value. + * + * @param name The name of the CSS property. + * @param value The value of the CSS property. + * @return A new {@link CssProperty} instance. + */ + public static CssProperty of(String name, float value) { + return new CssProperty(name, String.valueOf(value)); + } + + /** + * Creates a new {@link CssProperty} instance with the specified name and value. + * + * @param name The name of the CSS property. + * @param value The value of the CSS property. + * @return A new {@link CssProperty} instance. + */ + public static CssProperty of(String name, boolean value) { + return new CssProperty(name, String.valueOf(value)); + } + /** * Constructs a {@link CssProperty} with a specified name and value. * diff --git a/domino-ui-shared/src/main/java/org/dominokit/domino/ui/style/DominoStyle.java b/domino-ui-shared/src/main/java/org/dominokit/domino/ui/style/DominoStyle.java index d28195f82..532547739 100644 --- a/domino-ui-shared/src/main/java/org/dominokit/domino/ui/style/DominoStyle.java +++ b/domino-ui-shared/src/main/java/org/dominokit/domino/ui/style/DominoStyle.java @@ -28,6 +28,14 @@ */ public interface DominoStyle { + /** + * Sets a CSS property with the given name and string value. + * + * @param property {@link CssProperty} + * @return The updated style. + */ + R setCssProperty(CssProperty property); + /** * Sets a CSS property with the given name and string value. * diff --git a/domino-ui-shared/src/main/java/org/dominokit/domino/ui/style/Style.java b/domino-ui-shared/src/main/java/org/dominokit/domino/ui/style/Style.java index 1aa5f5480..f61dbd307 100644 --- a/domino-ui-shared/src/main/java/org/dominokit/domino/ui/style/Style.java +++ b/domino-ui-shared/src/main/java/org/dominokit/domino/ui/style/Style.java @@ -68,6 +68,12 @@ public static > Style of(T isElemen return new Style<>(isElement.element()); } + @Override + public Style setCssProperty(CssProperty property) { + style.setProperty(property.getName(), property.getValue()); + return this; + } + /** * Sets a CSS property with the specified name and value. * 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 fde819234..24d421a39 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 @@ -19,11 +19,7 @@ import static org.dominokit.domino.ui.utils.Domino.*; import elemental2.dom.HTMLDivElement; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; -import java.util.stream.Collectors; +import java.util.*; import org.dominokit.domino.ui.utils.ComponentMeta; import org.dominokit.domino.ui.utils.DominoCSSRule; import org.dominokit.domino.ui.utils.DynamicStyleSheet; @@ -107,11 +103,15 @@ public Optional getColumnCssRule(String key) { * @return A collection of {@link ColumnCssRule} representing all the CSS rules. */ public Collection cssRules() { - return cssRules.keySet().stream() - .map(this::getColumnCssRule) - .filter(Optional::isPresent) - .map(Optional::get) - .collect(Collectors.toList()); + List list = new ArrayList<>(); + for (String s : cssRules.keySet()) { + Optional columnCssRule = getColumnCssRule(s); + if (columnCssRule.isPresent()) { + ColumnCssRule cssRule = columnCssRule.get(); + list.add(cssRule); + } + } + return list; } /** diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/HeaderElementSupplier.java b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/HeaderElementSupplier.java index 35d6ac46f..e90080c90 100644 --- a/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/HeaderElementSupplier.java +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/datatable/HeaderElementSupplier.java @@ -16,8 +16,6 @@ package org.dominokit.domino.ui.datatable; -import static org.dominokit.domino.ui.utils.Domino.*; - import elemental2.dom.Node; /** A functional interface for supplying header elements in a data table. */ 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 283cbeba2..190137c79 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 @@ -21,9 +21,9 @@ import static org.dominokit.domino.ui.utils.ElementsFactory.elements; import elemental2.dom.DOMRect; +import elemental2.dom.HTMLElement; import java.util.*; import java.util.function.Consumer; -import java.util.stream.Collectors; import org.dominokit.domino.ui.datatable.plugins.DataTablePlugin; import org.dominokit.domino.ui.datatable.plugins.column.ResizeColumnMeta; import org.dominokit.domino.ui.elements.THeadElement; @@ -85,24 +85,24 @@ public class TableConfig .addCss(dui_datatable_utility_elements) .apply( div -> { - getPlugins().stream() - .map(plugin -> plugin.getUtilityElements(dataTable, cellInfo)) - .filter(Optional::isPresent) - .map(Optional::get) - .flatMap(Collection::stream) - .forEach( - node -> { - String order = - Optional.ofNullable( - elements.elementOf(node).getAttribute("order")) - .orElse("0"); - div.appendChild( - elements - .div() - .setCssProperty("order", Integer.parseInt(order)) - .addCss(dui_datatable_utility_element) - .appendChild(node)); - }); + List> pluginsList = getPlugins(); + for (DataTablePlugin plugin : pluginsList) { + Optional> optionalElements = + plugin.getUtilityElements(dataTable, cellInfo); + if (optionalElements.isPresent()) { + List nodes = optionalElements.get(); + for (HTMLElement node : nodes) { + String orderAttr = elements.elementOf(node).getAttribute("order"); + String order = (orderAttr != null) ? orderAttr : "0"; + div.appendChild( + elements + .div() + .setCssProperty("order", Integer.parseInt(order)) + .addCss(dui_datatable_utility_element) + .appendChild(node)); + } + } + } cellInfo .getColumnConfig() @@ -139,7 +139,16 @@ public class TableConfig public void drawHeaders(DataTable dataTable, THeadElement thead) { this.dataTable = dataTable; - int maxDepth = columns.stream().mapToInt(ColumnConfig::getColumnsDepth).max().orElse(0); + boolean seen = false; + int best = 0; + for (ColumnConfig column : columns) { + int columnsDepth = column.getColumnsDepth(); + if (!seen || columnsDepth > best) { + seen = true; + best = columnsDepth; + } + } + int maxDepth = seen ? best : 0; TableRowElement[] headers = new TableRowElement[maxDepth + 1]; for (int i = 0; i < headers.length; i++) { @@ -372,7 +381,14 @@ public void setRowAppender(RowAppender rowAppender) { * @return A sorted list of {@link DataTablePlugin}. */ public List> getPlugins() { - return plugins.stream().sorted().collect(Collectors.toList()); + List> sortedPlugins = new ArrayList<>(); + // Add all plugins using a simple loop + for (DataTablePlugin plugin : plugins) { + sortedPlugins.add(plugin); + } + // Sort the list using Collections.sort(), which sorts according to natural ordering + Collections.sort(sortedPlugins); + return sortedPlugins; } /** @@ -399,7 +415,15 @@ void onAfterHeaders(DataTable dataTable) { * @return A list of {@link ColumnConfig} representing the leaf columns. */ public List> getColumns() { - return columns.stream().flatMap(col -> col.leafColumns().stream()).collect(Collectors.toList()); + List> allColumns = new ArrayList<>(); + for (ColumnConfig col : columns) { + // Retrieve the leaf columns from the current column + List> leafColumns = col.leafColumns(); + for (ColumnConfig leaf : leafColumns) { + allColumns.add(leaf); + } + } + return allColumns; } /** @@ -408,9 +432,14 @@ public List> getColumns() { * @return A list of {@link ColumnConfig} representing all columns, flattened. */ public List> getFlattenColumns() { - return columns.stream() - .flatMap(col -> col.flattenColumns().stream()) - .collect(Collectors.toList()); + List> flattenColumns = new ArrayList<>(); + for (ColumnConfig col : columns) { + List> colFlattenColumns = col.flattenColumns(); + for (ColumnConfig flattened : colFlattenColumns) { + flattenColumns.add(flattened); + } + } + return flattenColumns; } /** @@ -419,7 +448,14 @@ public List> getFlattenColumns() { * @return A list of {@link ColumnConfig} representing all columns, flattened. */ public List> getLeafColumns() { - return columns.stream().flatMap(col -> col.leafColumns().stream()).collect(Collectors.toList()); + List> leafColumnsList = new ArrayList<>(); + for (ColumnConfig col : columns) { + List> childLeafColumns = col.leafColumns(); + for (ColumnConfig leaf : childLeafColumns) { + leafColumnsList.add(leaf); + } + } + return leafColumnsList; } /** @@ -437,7 +473,13 @@ public List> getColumnsGrouped() { * @return A list of {@link ColumnConfig} representing visible columns. */ public List> getVisibleColumns() { - return columns.stream().filter(column -> !column.isHidden()).collect(Collectors.toList()); + List> list = new ArrayList<>(); + for (ColumnConfig column : columns) { + if (!column.isHidden()) { + list.add(column); + } + } + return list; } /** @@ -448,10 +490,13 @@ public List> getVisibleColumns() { * @throws ColumnNofFoundException If no column is found with the specified name. */ public ColumnConfig getColumnByName(String name) { - Optional> first = - getFlattenColumns().stream() - .filter(columnConfig -> columnConfig.getName().equals(name)) - .findFirst(); + Optional> first = Optional.empty(); + for (ColumnConfig columnConfig : getFlattenColumns()) { + if (columnConfig.getName().equals(name)) { + first = Optional.of(columnConfig); + break; + } + } if (first.isPresent()) { return first.get(); } else { diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/events/CustomEvents.java b/domino-ui/src/main/java/org/dominokit/domino/ui/events/CustomEvents.java index 0d82d6238..ca1b03aca 100644 --- a/domino-ui/src/main/java/org/dominokit/domino/ui/events/CustomEvents.java +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/events/CustomEvents.java @@ -15,8 +15,6 @@ */ package org.dominokit.domino.ui.events; -import static org.dominokit.domino.ui.utils.Domino.*; - import elemental2.dom.CustomEvent; import elemental2.dom.CustomEventInit; import jsinterop.base.Js; diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/forms/AbstractFormElement.java b/domino-ui/src/main/java/org/dominokit/domino/ui/forms/AbstractFormElement.java index 7cce5ce5e..617c801ab 100644 --- a/domino-ui/src/main/java/org/dominokit/domino/ui/forms/AbstractFormElement.java +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/forms/AbstractFormElement.java @@ -78,15 +78,15 @@ public abstract class AbstractFormElement, V protected final LazyChild helperTextElement; protected Function errorElementSupplier; - protected Set> validators = new LinkedHashSet<>(); + private Set> validators; protected AutoValidator autoValidator; protected final List errors = new ArrayList<>(); protected String requiredErrorMessage; protected final FormsLabels labels = DominoUIConfig.CONFIG.getDominoUILabels(); protected final RequiredValidator requiredValidator; - protected Set> changeListeners = new LinkedHashSet<>(); - protected Set> clearListeners = new LinkedHashSet<>(); + private Set> changeListeners; + private Set> clearListeners; private boolean autoValidate = false; private boolean required = false; private boolean changeListenersPaused = false; @@ -434,6 +434,9 @@ public ValidationResult validate(T formElement) { */ @Override public Set> getValidators() { + if (isNull(validators)) { + this.validators = new LinkedHashSet<>(); + } return validators; } @@ -666,6 +669,9 @@ public T resumeChangeListeners() { */ @Override public Set> getChangeListeners() { + if (isNull(changeListeners)) { + this.changeListeners = new LinkedHashSet<>(); + } return changeListeners; } @@ -732,6 +738,9 @@ public T togglePauseClearListeners(boolean toggle) { */ @Override public Set> getClearListeners() { + if (isNull(clearListeners)) { + this.clearListeners = new LinkedHashSet<>(); + } return clearListeners; } diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/forms/RadioGroup.java b/domino-ui/src/main/java/org/dominokit/domino/ui/forms/RadioGroup.java index 9dea20cab..f503e5283 100644 --- a/domino-ui/src/main/java/org/dominokit/domino/ui/forms/RadioGroup.java +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/forms/RadioGroup.java @@ -213,7 +213,8 @@ private void selectFirst(boolean silent) { */ @Override public RadioGroup triggerChangeListeners(T oldValue, T newValue) { - changeListeners.forEach(changeListener -> changeListener.onValueChanged(oldValue, newValue)); + getChangeListeners() + .forEach(changeListener -> changeListener.onValueChanged(oldValue, newValue)); return this; } @@ -225,7 +226,7 @@ public RadioGroup triggerChangeListeners(T oldValue, T newValue) { */ @Override public RadioGroup triggerClearListeners(T oldValue) { - clearListeners.forEach(clearListener -> clearListener.onValueCleared(oldValue)); + getClearListeners().forEach(clearListener -> clearListener.onValueCleared(oldValue)); return this; } @@ -501,8 +502,8 @@ public AutoValidator createAutoValidator(ApplyFunction autoValidate) { */ void onSelectionChanged(T oldValue, Radio selectedRadio, boolean silent) { if (!silent) { - changeListeners.forEach( - listener -> listener.onValueChanged(oldValue, selectedRadio.getValue())); + getChangeListeners() + .forEach(listener -> listener.onValueChanged(oldValue, selectedRadio.getValue())); } } diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/menu/Menu.java b/domino-ui/src/main/java/org/dominokit/domino/ui/menu/Menu.java index 5b09775da..d84e96122 100644 --- a/domino-ui/src/main/java/org/dominokit/domino/ui/menu/Menu.java +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/menu/Menu.java @@ -67,19 +67,7 @@ import org.dominokit.domino.ui.search.SearchBox; import org.dominokit.domino.ui.style.BooleanCssClass; import org.dominokit.domino.ui.style.Elevation; -import org.dominokit.domino.ui.utils.AppendStrategy; -import org.dominokit.domino.ui.utils.BaseDominoElement; -import org.dominokit.domino.ui.utils.ChildHandler; -import org.dominokit.domino.ui.utils.DominoElement; -import org.dominokit.domino.ui.utils.DominoUIConfig; -import org.dominokit.domino.ui.utils.HasSelectionListeners; -import org.dominokit.domino.ui.utils.IsPopup; -import org.dominokit.domino.ui.utils.KeyboardNavigation; -import org.dominokit.domino.ui.utils.LazyChild; -import org.dominokit.domino.ui.utils.PopupsCloser; -import org.dominokit.domino.ui.utils.PrefixAddOn; -import org.dominokit.domino.ui.utils.Separator; -import org.dominokit.domino.ui.utils.SubheaderAddon; +import org.dominokit.domino.ui.utils.*; /** * Represents a UI Menu component that supports different configurations, items, and behaviors. @@ -144,7 +132,7 @@ public class Menu extends BaseDominoElement> private final DropDirection contextMenuDropDirection = new MouseBestFitDirection(); private final DropDirection smallScreenDropDirection = new MiddleOfScreenDropDirection(); private DropDirection effectiveDropDirection = dropDirection; - private Map targets = new HashMap<>(); + private Map targets; private MenuTarget lastTarget; private Element menuAppendTarget = document.body; private AppendStrategy appendStrategy = AppendStrategy.LAST; @@ -191,6 +179,13 @@ public class Menu extends BaseDominoElement> } } }; + + private final EventListener autoCloseListener = + evt -> { + if (isAutoClose()) { + remove(); + } + }; private final DivElement backArrowContainer; private boolean contextMenu = false; private boolean useSmallScreensDirection = true; @@ -220,7 +215,10 @@ public Menu() { menuElement = div().addCss(dui_menu); menuHeader = LazyChild.of(NavBar.create(), menuElement); menuSearchContainer = LazyChild.of(div().addCss(dui_menu_search), menuElement); - searchBox = LazyChild.of(SearchBox.create().addCss(dui_menu_search_box), menuSearchContainer); + searchBox = + LazyChild.of( + SupplyOnce.of(() -> SearchBox.create().addCss(dui_menu_search_box)), + menuSearchContainer); backArrowContainer = div().addCss(dui_order_first, dui_menu_back_icon); init(this); closeOnScrollListener = evt -> close(); @@ -469,6 +467,7 @@ public Menu() { nowAndWhenAttached( () -> { + document.addEventListener(PopupsCloser.DUI_AUTO_CLOSE, autoCloseListener); mediaQueryRecords.add( MediaQuery.addOnSmallAndDownListener( () -> { @@ -497,6 +496,7 @@ public Menu() { DomGlobal.document.body.removeEventListener("blur", lostFocusListener, true); document.removeEventListener("scroll", repositionListener, true); mediaQueryRecords.forEach(MediaQuery.MediaQueryListenerRecord::remove); + document.removeEventListener(PopupsCloser.DUI_AUTO_CLOSE, autoCloseListener); }); this.addEventListener(EventType.touchstart.getName(), Event::stopPropagation); @@ -1559,8 +1559,8 @@ public void focus() { * @return An optional containing the menu target, or empty if no target is set. */ public Optional getTarget() { - if (isNull(lastTarget) && targets.size() == 1) { - return targets.values().stream().findFirst(); + if (isNull(lastTarget) && targets().size() == 1) { + return targets().values().stream().findFirst(); } return Optional.ofNullable(lastTarget); } @@ -1593,7 +1593,7 @@ public Menu setTargetElement(Element targetElement) { * @return The current {@link Menu} instance. */ public Menu setTarget(MenuTarget menuTarget) { - this.targets + this.targets() .values() .forEach( target -> { @@ -1605,7 +1605,7 @@ public Menu setTarget(MenuTarget menuTarget) { target.getTargetElement().removeDetachObserver(menuTarget.getTargetDetachObserver()); target.getTargetElement().removeAttachObserver(menuTarget.getTargetAttachObserver()); }); - this.targets.clear(); + this.targets().clear(); return addTarget(menuTarget); } @@ -1617,25 +1617,25 @@ public Menu setTarget(MenuTarget menuTarget) { */ public Menu addTarget(MenuTarget menuTarget) { if (nonNull(menuTarget)) { - this.targets.put(menuTarget.getTargetElement().getDominoId(), menuTarget); + this.targets().put(menuTarget.getTargetElement().getDominoId(), menuTarget); menuTarget.setTargetDetachObserver( (e, mutationRecord) -> { if (Objects.equals(menuTarget, lastTarget)) { close(); } - this.targets.remove(menuTarget.getTargetElement().getDominoId()); + this.targets().remove(menuTarget.getTargetElement().getDominoId()); }); menuTarget.setTargetAttachObserver( (e, mutationRecord) -> { - this.targets.put(menuTarget.getTargetElement().getDominoId(), menuTarget); + this.targets().put(menuTarget.getTargetElement().getDominoId(), menuTarget); }); elementOf(menuTarget.getTargetElement()).onDetached(menuTarget.getTargetDetachObserver()); elementOf(menuTarget.getTargetElement()).onAttached(menuTarget.getTargetAttachObserver()); } - if (!this.targets.isEmpty()) { + if (!this.targets().isEmpty()) { applyTargetListeners(menuTarget); setDropDown(true); } else { @@ -1795,7 +1795,7 @@ public boolean isContextMenu() { public Menu setContextMenu(boolean contextMenu) { this.contextMenu = contextMenu; addCss(BooleanCssClass.of(dui_context_menu, contextMenu)); - targets.values().forEach(this::applyTargetListeners); + targets().values().forEach(this::applyTargetListeners); return this; } @@ -2038,6 +2038,13 @@ private boolean isCloseOnScroll() { && "true".equalsIgnoreCase(getAttribute("d-close-on-scroll")); } + private Map targets() { + if (isNull(this.targets)) { + this.targets = new HashMap<>(); + } + return this.targets; + } + /** * @return boolean true if the selection style should be preserved after the menu item loses the * selection focus, otherwise false. diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/menu/MenuTarget.java b/domino-ui/src/main/java/org/dominokit/domino/ui/menu/MenuTarget.java index 182e76ece..5b2d65311 100644 --- a/domino-ui/src/main/java/org/dominokit/domino/ui/menu/MenuTarget.java +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/menu/MenuTarget.java @@ -15,6 +15,7 @@ */ package org.dominokit.domino.ui.menu; +import static java.util.Objects.isNull; import static org.dominokit.domino.ui.utils.ElementsFactory.elements; import elemental2.dom.Element; @@ -42,7 +43,7 @@ public class MenuTarget implements HasMeta { private final Element targetElement; private ObserverCallback> targetDetachObserver; private ObserverCallback> targetAttachObserver; - private final Map metaObjects = new HashMap<>(); + private Map metaObjects; /** * Factory method to create an instance of {@link MenuTarget} using the given DOM {@link Element}. @@ -142,6 +143,9 @@ ObserverCallback> getTargetAttachObserver() { */ @Override public Map getMetaObjects() { + if (isNull(this.metaObjects)) { + this.metaObjects = new HashMap<>(); + } return metaObjects; } } diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/popover/BasePopover.java b/domino-ui/src/main/java/org/dominokit/domino/ui/popover/BasePopover.java index 8c083e974..78fa10bf6 100644 --- a/domino-ui/src/main/java/org/dominokit/domino/ui/popover/BasePopover.java +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/popover/BasePopover.java @@ -326,7 +326,9 @@ public void discard() { public T setPosition(DropDirection position) { this.popupPosition.cleanup(this.element()); this.popupPosition = position; - doPosition(); + if (isAttached()) { + doPosition(); + } return (T) this; } 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 6d4edbb15..33e22091d 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 @@ -53,13 +53,7 @@ import org.dominokit.domino.ui.menu.direction.DropDirection; import org.dominokit.domino.ui.popover.Popover; import org.dominokit.domino.ui.popover.Tooltip; -import org.dominokit.domino.ui.style.CssClass; -import org.dominokit.domino.ui.style.DominoStyle; -import org.dominokit.domino.ui.style.Elevation; -import org.dominokit.domino.ui.style.HasCssClass; -import org.dominokit.domino.ui.style.HasCssClasses; -import org.dominokit.domino.ui.style.Style; -import org.dominokit.domino.ui.style.WavesSupport; +import org.dominokit.domino.ui.style.*; import org.dominokit.domino.ui.themes.DominoThemeManager; import org.gwtproject.editor.client.Editor; import org.gwtproject.safehtml.shared.SafeHtml; @@ -3004,6 +2998,18 @@ public Tooltip getTooltip() { return tooltip; } + /** + * Sets a CSS property with a string value. + * + * @param property {@link CssProperty} the CSS property. + * @return The modified DOM element. + */ + @Override + public T setCssProperty(CssProperty property) { + style().setCssProperty(property); + return (T) this; + } + /** * Sets a CSS property with a string value. * diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/utils/LazyChild.java b/domino-ui/src/main/java/org/dominokit/domino/ui/utils/LazyChild.java index 637eabf44..1271c9586 100644 --- a/domino-ui/src/main/java/org/dominokit/domino/ui/utils/LazyChild.java +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/utils/LazyChild.java @@ -16,7 +16,6 @@ package org.dominokit.domino.ui.utils; -import static org.dominokit.domino.ui.utils.Domino.*; import static org.dominokit.domino.ui.utils.ElementsFactory.elements; import elemental2.dom.Element; @@ -33,7 +32,7 @@ */ public class LazyChild> extends BaseLazyInitializer> { - private T element; + private SupplyOnce supplyOnce; /** * Creates a new {@code LazyChild} instance with the specified child element and parent element. @@ -43,7 +42,7 @@ public class LazyChild> extends BaseLazyInitializer> LazyChild of(T element, IsElement parent) { - return new LazyChild<>(element, () -> parent); + return new LazyChild<>(SupplyOnce.of(() -> element), () -> parent); } /** @@ -55,7 +54,7 @@ public static > LazyChild of(T element, IsElement p * @return A new {@code LazyChild} instance. */ public static > LazyChild of(T element, Supplier> parent) { - return new LazyChild<>(element, parent); + return new LazyChild<>(SupplyOnce.of(() -> element), parent); } /** @@ -67,7 +66,7 @@ public static > LazyChild of(T element, Supplier> LazyChild of(T element, LazyChild parent) { - return new LazyChild<>(element, parent); + return new LazyChild<>(SupplyOnce.of(() -> element), parent); } /** @@ -81,7 +80,9 @@ public static > LazyChild of(T element, LazyChild p public static > LazyChild ofInsertFirst( T element, IsElement parent) { return new LazyChild<>( - element, () -> parent, (p, child) -> elements.elementOf(p).insertFirst(child.element())); + SupplyOnce.of(() -> element), + () -> parent, + (p, child) -> elements.elementOf(p).insertFirst(child.element())); } /** @@ -96,7 +97,9 @@ public static > LazyChild ofInsertFirst( public static > LazyChild ofInsertFirst( T element, Supplier> parent) { return new LazyChild<>( - element, parent, (p, child) -> elements.elementOf(p).insertFirst(child.element())); + SupplyOnce.of(() -> element), + parent, + (p, child) -> elements.elementOf(p).insertFirst(child.element())); } /** @@ -111,7 +114,9 @@ public static > LazyChild ofInsertFirst( public static > LazyChild ofInsertFirst( T element, LazyChild parent) { return new LazyChild<>( - element, parent, (p, child) -> elements.elementOf(p).insertFirst(child.element())); + SupplyOnce.of(() -> element), + parent, + (p, child) -> elements.elementOf(p).insertFirst(child.element())); } /** @@ -122,8 +127,10 @@ public static > LazyChild ofInsertFirst( * @param parent A supplier that provides the parent element to which the child will be added. */ public LazyChild(T element, Supplier> parent) { - this(element, parent, (p, child) -> elements.elementOf(p).appendChild(child)); - this.element = element; + this( + SupplyOnce.of(() -> element), + parent, + (p, child) -> elements.elementOf(p).appendChild(child)); } /** @@ -135,8 +142,7 @@ public LazyChild(T element, Supplier> parent) { * @param appendStrategy The strategy for adding the child element to the parent. */ public LazyChild(T element, Supplier> parent, AppendStrategy appendStrategy) { - super(() -> appendStrategy.onAppend(parent.get().element(), element)); - this.element = element; + this(SupplyOnce.of(() -> element), parent, appendStrategy); } /** @@ -147,7 +153,10 @@ public LazyChild(T element, Supplier> parent, AppendStrategy app * @param parent The parent {@code LazyChild} to which the child will be added. */ public LazyChild(T element, LazyChild parent) { - this(element, parent, (p, child) -> elements.elementOf(p).appendChild(child)); + this( + SupplyOnce.of(() -> element), + parent, + (p, child) -> elements.elementOf(p).appendChild(child)); } /** @@ -159,8 +168,272 @@ public LazyChild(T element, LazyChild parent) { * @param appendStrategy The strategy for adding the child element to the parent. */ public LazyChild(T element, LazyChild parent, AppendStrategy appendStrategy) { - super(() -> appendStrategy.onAppend(parent.get().element(), element)); - this.element = element; + this(SupplyOnce.of(() -> element), parent, appendStrategy); + } + + // ====================================== + + /** + * Creates a new {@code LazyChild} instance with the specified child element and parent element. + * + * @param supplier the function to create the element only once {@link SupplyOnce}. + * @param parent The parent element to which the child will be added. + * @return A new {@code LazyChild} instance. + */ + public static > LazyChild of( + Supplier supplier, IsElement parent) { + return new LazyChild<>(supplier, () -> parent); + } + + /** + * Creates a new {@code LazyChild} instance with the specified child element and a supplier for + * the parent element. + * + * @param supplier the function to create the element only once {@link SupplyOnce}. + * @param parent A supplier that provides the parent element to which the child will be added. + * @return A new {@code LazyChild} instance. + */ + public static > LazyChild of( + Supplier supplier, Supplier> parent) { + return new LazyChild<>(supplier, parent); + } + + /** + * Creates a new {@code LazyChild} instance with the specified child element and a parent {@code + * LazyChild}. + * + * @param supplier the function to create the element only once {@link SupplyOnce}. + * @param parent The parent {@code LazyChild} to which the child will be added. + * @return A new {@code LazyChild} instance. + */ + public static > LazyChild of( + Supplier supplier, LazyChild parent) { + return new LazyChild<>(supplier, parent); + } + + /** + * Creates a new {@code LazyChild} instance with the specified child element and parent element, + * inserting it as the first child. + * + * @param supplier the function to create the element only once {@link SupplyOnce}. + * @param parent The parent element to which the child will be inserted as the first child. + * @return A new {@code LazyChild} instance. + */ + public static > LazyChild ofInsertFirst( + Supplier supplier, IsElement parent) { + return new LazyChild<>( + supplier, () -> parent, (p, child) -> elements.elementOf(p).insertFirst(child.element())); + } + + /** + * Creates a new {@code LazyChild} instance with the specified child element and a supplier for + * the parent element, inserting it as the first child. + * + * @param supplier the function to create the element only once {@link SupplyOnce}. + * @param parent A supplier that provides the parent element to which the child will be inserted + * as the first child. + * @return A new {@code LazyChild} instance. + */ + public static > LazyChild ofInsertFirst( + Supplier supplier, Supplier> parent) { + return new LazyChild<>( + supplier, parent, (p, child) -> elements.elementOf(p).insertFirst(child.element())); + } + + /** + * Creates a new {@code LazyChild} instance with the specified child element and a parent {@code + * LazyChild}, inserting it as the first child. + * + * @param supplier the function to create the element only once {@link SupplyOnce}. + * @param parent The parent {@code LazyChild} to which the child will be inserted as the first + * child. + * @return A new {@code LazyChild} instance. + */ + public static > LazyChild ofInsertFirst( + Supplier supplier, LazyChild parent) { + return new LazyChild<>( + supplier, parent, (p, child) -> elements.elementOf(p).insertFirst(child.element())); + } + + /** + * Constructs a new {@code LazyChild} instance with the specified child element and parent element + * supplier. + * + * @param supplier the function to create the element only once {@link SupplyOnce}. + * @param parent A supplier that provides the parent element to which the child will be added. + */ + public LazyChild(Supplier supplier, Supplier> parent) { + this(supplier, parent, (p, child) -> elements.elementOf(p).appendChild(child)); + } + + /** + * Constructs a new {@code LazyChild} instance with the specified child element, parent element + * supplier, and append strategy. + * + * @param supplier the function to create the element only once {@link SupplyOnce}. + * @param parent A supplier that provides the parent element to which the child will be added. + * @param appendStrategy The strategy for adding the child element to the parent. + */ + public LazyChild( + Supplier supplier, Supplier> parent, AppendStrategy appendStrategy) { + this(SupplyOnce.of(supplier), parent, appendStrategy); + } + + /** + * Constructs a new {@code LazyChild} instance with the specified child element and a parent + * {@code LazyChild}. + * + * @param supplier the function to create the element only once {@link SupplyOnce}. + * @param parent The parent {@code LazyChild} to which the child will be added. + */ + public LazyChild(Supplier supplier, LazyChild parent) { + this(supplier, parent, (p, child) -> elements.elementOf(p).appendChild(child)); + } + + /** + * Constructs a new {@code LazyChild} instance with the specified child element, parent {@code + * LazyChild}, and append strategy. + * + * @param supplier the function to create the element only once {@link SupplyOnce}. + * @param parent The parent {@code LazyChild} to which the child will be added. + * @param appendStrategy The strategy for adding the child element to the parent. + */ + public LazyChild(Supplier supplier, LazyChild parent, AppendStrategy appendStrategy) { + this(SupplyOnce.of(supplier), parent, appendStrategy); + } + + // ====================================== + + /** + * Creates a new {@code LazyChild} instance with the specified child element and parent element. + * + * @param supplier the function to create the element only once {@link SupplyOnce}. + * @param parent The parent element to which the child will be added. + * @return A new {@code LazyChild} instance. + */ + public static > LazyChild of( + SupplyOnce supplier, IsElement parent) { + return new LazyChild<>(supplier, () -> parent); + } + + /** + * Creates a new {@code LazyChild} instance with the specified child element and a supplier for + * the parent element. + * + * @param supplier the function to create the element only once {@link SupplyOnce}. + * @param parent A supplier that provides the parent element to which the child will be added. + * @return A new {@code LazyChild} instance. + */ + public static > LazyChild of( + SupplyOnce supplier, Supplier> parent) { + return new LazyChild<>(supplier, parent); + } + + /** + * Creates a new {@code LazyChild} instance with the specified child element and a parent {@code + * LazyChild}. + * + * @param supplier the function to create the element only once {@link SupplyOnce}. + * @param parent The parent {@code LazyChild} to which the child will be added. + * @return A new {@code LazyChild} instance. + */ + public static > LazyChild of( + SupplyOnce supplier, LazyChild parent) { + return new LazyChild<>(supplier, parent); + } + + /** + * Creates a new {@code LazyChild} instance with the specified child element and parent element, + * inserting it as the first child. + * + * @param supplier the function to create the element only once {@link SupplyOnce}. + * @param parent The parent element to which the child will be inserted as the first child. + * @return A new {@code LazyChild} instance. + */ + public static > LazyChild ofInsertFirst( + SupplyOnce supplier, IsElement parent) { + return new LazyChild<>( + supplier, () -> parent, (p, child) -> elements.elementOf(p).insertFirst(child.element())); + } + + /** + * Creates a new {@code LazyChild} instance with the specified child element and a supplier for + * the parent element, inserting it as the first child. + * + * @param supplier the function to create the element only once {@link SupplyOnce}. + * @param parent A supplier that provides the parent element to which the child will be inserted + * as the first child. + * @return A new {@code LazyChild} instance. + */ + public static > LazyChild ofInsertFirst( + SupplyOnce supplier, Supplier> parent) { + return new LazyChild<>( + supplier, parent, (p, child) -> elements.elementOf(p).insertFirst(child.element())); + } + + /** + * Creates a new {@code LazyChild} instance with the specified child element and a parent {@code + * LazyChild}, inserting it as the first child. + * + * @param supplier the function to create the element only once {@link SupplyOnce}. + * @param parent The parent {@code LazyChild} to which the child will be inserted as the first + * child. + * @return A new {@code LazyChild} instance. + */ + public static > LazyChild ofInsertFirst( + SupplyOnce supplier, LazyChild parent) { + return new LazyChild<>( + supplier, parent, (p, child) -> elements.elementOf(p).insertFirst(child.element())); + } + + /** + * Constructs a new {@code LazyChild} instance with the specified child element and parent element + * supplier. + * + * @param supplier the function to create the element only once {@link SupplyOnce}. + * @param parent A supplier that provides the parent element to which the child will be added. + */ + public LazyChild(SupplyOnce supplier, Supplier> parent) { + this(supplier, parent, (p, child) -> elements.elementOf(p).appendChild(child)); + this.supplyOnce = supplier; + } + + /** + * Constructs a new {@code LazyChild} instance with the specified child element, parent element + * supplier, and append strategy. + * + * @param supplier the function to create the element only once {@link SupplyOnce}. + * @param parent A supplier that provides the parent element to which the child will be added. + * @param appendStrategy The strategy for adding the child element to the parent. + */ + public LazyChild( + SupplyOnce supplier, Supplier> parent, AppendStrategy appendStrategy) { + super(() -> appendStrategy.onAppend(parent.get().element(), supplier.get())); + this.supplyOnce = supplier; + } + + /** + * Constructs a new {@code LazyChild} instance with the specified child element and a parent + * {@code LazyChild}. + * + * @param supplier the function to create the element only once {@link SupplyOnce}. + * @param parent The parent {@code LazyChild} to which the child will be added. + */ + public LazyChild(SupplyOnce supplier, LazyChild parent) { + this(supplier, parent, (p, child) -> elements.elementOf(p).appendChild(child)); + } + + /** + * Constructs a new {@code LazyChild} instance with the specified child element, parent {@code + * LazyChild}, and append strategy. + * + * @param supplier the function to create the element only once {@link SupplyOnce}. + * @param parent The parent {@code LazyChild} to which the child will be added. + * @param appendStrategy The strategy for adding the child element to the parent. + */ + public LazyChild(SupplyOnce supplier, LazyChild parent, AppendStrategy appendStrategy) { + super(() -> appendStrategy.onAppend(parent.get().element(), supplier.get())); + this.supplyOnce = supplier; } /** @@ -171,7 +444,7 @@ public LazyChild(T element, LazyChild parent, AppendStrategy appendStrateg */ public T get() { apply(); - return element; + return supplyOnce.get(); } /** @@ -182,7 +455,7 @@ public T get() { */ public LazyChild remove() { if (isInitialized()) { - element.element().remove(); + supplyOnce.get().element().remove(); reset(); } return this; @@ -195,7 +468,7 @@ public LazyChild remove() { * @return The child element or {@code null} if not initialized. */ public T element() { - return element; + return supplyOnce.get(); } /** diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/utils/PopupsCloser.java b/domino-ui/src/main/java/org/dominokit/domino/ui/utils/PopupsCloser.java index eac01bb62..b9667beec 100644 --- a/domino-ui/src/main/java/org/dominokit/domino/ui/utils/PopupsCloser.java +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/utils/PopupsCloser.java @@ -17,12 +17,9 @@ package org.dominokit.domino.ui.utils; import static elemental2.dom.DomGlobal.document; -import static org.dominokit.domino.ui.utils.Domino.*; -import elemental2.dom.Element; -import elemental2.dom.HTMLElement; -import elemental2.dom.NodeList; -import jsinterop.base.Js; +import elemental2.dom.CustomEvent; +import org.dominokit.domino.ui.events.CustomEvents; import org.dominokit.domino.ui.events.EventType; /** @@ -35,6 +32,8 @@ public class PopupsCloser { /** The CSS class name used to mark elements as auto-closable. */ public static final String DOMINO_UI_AUTO_CLOSABLE = "domino-ui-auto-closable"; + public static final String DUI_AUTO_CLOSE = "dui-auto-close"; + private static boolean touchMoved; /** Initializes the PopupsCloser by adding event listeners for click and touch events. */ @@ -62,10 +61,7 @@ public static void close() { * @param selector The CSS class name used to select auto-closable elements. */ public static void close(String selector) { - NodeList elementsByName = document.body.querySelectorAll("[" + selector + "]"); - for (int i = 0; i < elementsByName.length; i++) { - HTMLElement element = Js.uncheckedCast(elementsByName.item(i)); - element.remove(); - } + CustomEvent closeEvent = CustomEvents.create(DUI_AUTO_CLOSE, selector); + document.dispatchEvent(closeEvent); } } diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/utils/SupplyOnce.java b/domino-ui/src/main/java/org/dominokit/domino/ui/utils/SupplyOnce.java new file mode 100644 index 000000000..f35d37543 --- /dev/null +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/utils/SupplyOnce.java @@ -0,0 +1,41 @@ +/* + * 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 java.util.Objects.isNull; +import static java.util.Objects.nonNull; + +import java.util.function.Supplier; + +public class SupplyOnce { + private T value; + private Supplier supplier; + + public static SupplyOnce of(Supplier supplier) { + return new SupplyOnce<>(supplier); + } + + public SupplyOnce(Supplier supplier) { + this.supplier = supplier; + } + + public T get() { + if (nonNull(supplier) && isNull(value)) { + this.value = supplier.get(); + } + return value; + } +}