From 05913e81875985b2820e6ef68fc5914c9a48a497 Mon Sep 17 00:00:00 2001 From: "Ahmad K. Bawaneh" Date: Sun, 8 Dec 2024 19:57:20 +0300 Subject: [PATCH] fix #984 Clicking in Rapid Succession Causes Tree Component Interaction to Break --- .../domino/ui/collapsible/Collapsible.java | 20 ++-- .../TreeHeightCollapseStrategy.java | 102 +++++++++++------- 2 files changed, 76 insertions(+), 46 deletions(-) diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/collapsible/Collapsible.java b/domino-ui/src/main/java/org/dominokit/domino/ui/collapsible/Collapsible.java index e90b29a76..b63b1643f 100644 --- a/domino-ui/src/main/java/org/dominokit/domino/ui/collapsible/Collapsible.java +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/collapsible/Collapsible.java @@ -52,6 +52,7 @@ public class Collapsible implements IsElement, IsCollapsible expandHandlers = new ArrayList<>(); private List beforeExpandHandlers = new ArrayList<>(); private CollapseStrategy strategy = new DisplayCollapseStrategy(); + private boolean expandingCollapsing = false; /** * Creates a collapsible wrapping the element @@ -138,7 +139,8 @@ public Collapsible setForceCollapsed(boolean forceHidden) { */ @Override public Collapsible expand() { - if (!forceHidden && isCollapsed()) { + if (!forceHidden && isCollapsed() && !expandingCollapsing) { + expandingCollapsing = true; strategy.expand(element); element.setAttribute("aria-expanded", "true"); this.collapsed = false; @@ -153,7 +155,8 @@ public Collapsible expand() { */ @Override public Collapsible collapse() { - if (!forceHidden && !isCollapsed()) { + if (!forceHidden && !isCollapsed() && !expandingCollapsing) { + expandingCollapsing = true; strategy.collapse(element); element.setAttribute("aria-expanded", "false"); this.collapsed = true; @@ -162,6 +165,7 @@ public Collapsible collapse() { } private void onCollapseCompleted() { + expandingCollapsing = false; if (nonNull(collapseHandlers)) { collapseHandlers.forEach(CollapseHandler::apply); } @@ -174,6 +178,7 @@ private void onBeforeCollapse() { } private void onExpandCompleted() { + expandingCollapsing = false; if (nonNull(expandHandlers)) { expandHandlers.forEach(ExpandHandler::apply); } @@ -202,11 +207,14 @@ public boolean isCollapsed() { */ @Override public Collapsible toggleCollapse() { - if (isCollapsed()) { - expand(); - } else { - collapse(); + if (!expandingCollapsing) { + if (isCollapsed()) { + expand(); + } else { + collapse(); + } } + return this; } diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/collapsible/TreeHeightCollapseStrategy.java b/domino-ui/src/main/java/org/dominokit/domino/ui/collapsible/TreeHeightCollapseStrategy.java index d85925f08..e9ced3112 100644 --- a/domino-ui/src/main/java/org/dominokit/domino/ui/collapsible/TreeHeightCollapseStrategy.java +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/collapsible/TreeHeightCollapseStrategy.java @@ -44,6 +44,8 @@ public class TreeHeightCollapseStrategy implements CollapseStrategy, Collapsible private final String heightVar; private CollapsibleHandlers handlers; private final TreeItem treeItem; + private boolean expanding = false; + private boolean collapsing = false; /** * Constructor for TreeHeightCollapseStrategy. @@ -94,14 +96,17 @@ public void cleanup(Element element) { public void expand(Element element) { treeItem.nowOrWhenAttached( () -> { - this.handlers.onBeforeExpand().run(); - double height = treeItem.getBoundingClientRect().height; - treeItem.setAttribute(DUI_COLLAPSED_HEIGHT, height); - treeItem.getSubTree().show(); - treeItem.setCssProperty(this.heightVar, height + "px"); - double expandedHeight = getActualHeight(); - treeItem.setAttribute(DUI_EXPANDED_HEIGHT, expandedHeight); - expandElement(element); + if (!collapsing) { + this.expanding = true; + this.handlers.onBeforeExpand().run(); + double height = treeItem.getBoundingClientRect().height; + treeItem.setAttribute(DUI_COLLAPSED_HEIGHT, height); + treeItem.getSubTree().show(); + treeItem.setCssProperty(this.heightVar, height + "px"); + double expandedHeight = getActualHeight(); + treeItem.setAttribute(DUI_EXPANDED_HEIGHT, expandedHeight); + expandElement(element); + } }); } @@ -119,25 +124,17 @@ private void expandElement(Element element) { treeItem.setCssProperty(this.heightVar, "auto"); treeItem.removeAttribute(DUI_COLLAPSED); handlers.onExpandCompleted().run(); + expanding = false; } else { EventListener stopListener = evt -> { resetParentHeight(treeItem); treeItem.setCssProperty(this.heightVar, "auto"); handlers.onExpandCompleted().run(); + expanding = false; }; - AddEventListenerOptions addEventListenerOptions = AddEventListenerOptions.create(); - addEventListenerOptions.setOnce(true); - treeItem - .element() - .addEventListener("webkitTransitionEnd", stopListener, addEventListenerOptions); - treeItem.element().addEventListener("MSTransitionEnd", stopListener, addEventListenerOptions); - treeItem - .element() - .addEventListener("mozTransitionEnd", stopListener, addEventListenerOptions); - treeItem.element().addEventListener("oanimationend", stopListener, addEventListenerOptions); - treeItem.element().addEventListener("animationend", stopListener, addEventListenerOptions); + createAnimationEndListeners(stopListener); String expandedHeight = treeItem.getAttribute(DUI_EXPANDED_HEIGHT); treeItem.setCssProperty(this.heightVar, expandedHeight + "px"); @@ -145,30 +142,55 @@ private void expandElement(Element element) { } } + private void createAnimationEndListeners(EventListener stopListener) { + AddEventListenerOptions addEventListenerOptions = AddEventListenerOptions.create(); + addEventListenerOptions.setOnce(true); + treeItem + .element() + .addEventListener("webkitTransitionEnd", stopListener, addEventListenerOptions); + treeItem.element().addEventListener("MSTransitionEnd", stopListener, addEventListenerOptions); + treeItem.element().addEventListener("mozTransitionEnd", stopListener, addEventListenerOptions); + treeItem.element().addEventListener("oanimationend", stopListener, addEventListenerOptions); + treeItem.element().addEventListener("animationend", stopListener, addEventListenerOptions); + } + /** @dominokit-site-ignore {@inheritDoc} */ @Override public void collapse(Element element) { - treeItem.setCssProperty(this.heightVar, getActualHeight() + "px"); - boolean disableAnimation = dui_transition_none.isAppliedTo(treeItem); - treeItem.apply( - self -> { - if (self.isAttached()) { - this.handlers.onBeforeCollapse().run(); - collapseElement(element); - handlers.onCollapseCompleted().run(); - } else { - self.onAttached( - (e, mutationRecord) -> { - this.handlers.onBeforeCollapse().run(); - treeItem.addCss(dui_transition_none); - collapseElement(element); - if (!disableAnimation) { - dui_transition_none.remove(treeItem); - } - handlers.onCollapseCompleted().run(); - }); - } - }); + if (!expanding) { + collapsing = true; + treeItem.setCssProperty(this.heightVar, getActualHeight() + "px"); + boolean disableAnimation = dui_transition_none.isAppliedTo(treeItem); + treeItem.apply( + self -> { + if (self.isAttached()) { + this.handlers.onBeforeCollapse().run(); + EventListener stopListener = + evt -> { + handlers.onCollapseCompleted().run(); + collapsing = false; + }; + createAnimationEndListeners(stopListener); + collapseElement(element); + } else { + self.onAttached( + (e, mutationRecord) -> { + this.handlers.onBeforeCollapse().run(); + treeItem.addCss(dui_transition_none); + EventListener stopListener = + evt -> { + if (!disableAnimation) { + dui_transition_none.remove(treeItem); + } + handlers.onCollapseCompleted().run(); + collapsing = false; + }; + createAnimationEndListeners(stopListener); + collapseElement(element); + }); + } + }); + } } private void resetParentHeight(TreeItem treeItem) {