Skip to content

Commit

Permalink
fix #982 Add Multi Selection for Tree Component
Browse files Browse the repository at this point in the history
  • Loading branch information
vegegoku committed Dec 19, 2024
1 parent 05913e8 commit 8b0860b
Show file tree
Hide file tree
Showing 17 changed files with 2,101 additions and 1,920 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,19 @@

import static org.dominokit.domino.ui.collapsible.Collapsible.DUI_COLLAPSED;
import static org.dominokit.domino.ui.style.GenericCss.dui_transition_none;
import static org.dominokit.domino.ui.utils.Domino.*;
import static org.dominokit.domino.ui.utils.ElementsFactory.elements;

import elemental2.dom.AddEventListenerOptions;
import elemental2.dom.DomGlobal;
import elemental2.dom.Element;
import elemental2.dom.EventListener;
import org.dominokit.domino.ui.tree.TreeItem;
import org.dominokit.domino.ui.tree.TreeNode;
import org.dominokit.domino.ui.utils.DominoId;
import org.dominokit.domino.ui.utils.IsCollapsible;

/**
* An implementation of {@link org.dominokit.domino.ui.collapsible.CollapseStrategy} that is meant
* to be used with the {@link org.dominokit.domino.ui.tree.Tree} component
* An implementation of {@link CollapseStrategy} that is meant to be used with the {@link
* org.dominokit.domino.ui.tree.Tree} component
*/
public class TreeHeightCollapseStrategy implements CollapseStrategy, CollapsibleStyles {

Expand All @@ -43,42 +42,42 @@ public class TreeHeightCollapseStrategy implements CollapseStrategy, Collapsible
private final CollapsibleDuration transition;
private final String heightVar;
private CollapsibleHandlers handlers;
private final TreeItem<?> treeItem;
private final TreeNode<?, ?, ?> node;
private boolean expanding = false;
private boolean collapsing = false;

/**
* Constructor for TreeHeightCollapseStrategy.
*
* @param treeItem a {@link org.dominokit.domino.ui.tree.TreeItem} object
* @param node a {@link org.dominokit.domino.ui.tree.TreeItem} object
*/
public TreeHeightCollapseStrategy(TreeItem<?> treeItem) {
this(treeItem, CollapsibleDuration._300ms);
public TreeHeightCollapseStrategy(TreeNode<?, ?, ?> node) {
this(node, CollapsibleDuration._300ms);
}

/**
* Constructor for TreeHeightCollapseStrategy.
*
* @param treeItem a {@link org.dominokit.domino.ui.tree.TreeItem} object
* @param node a {@link org.dominokit.domino.ui.tree.TreeItem} object
* @param transition a {@link CollapsibleDuration} object
*/
public TreeHeightCollapseStrategy(TreeItem<?> treeItem, CollapsibleDuration transition) {
this.treeItem = treeItem;
public TreeHeightCollapseStrategy(TreeNode<?, ?, ?> node, CollapsibleDuration transition) {
this.node = node;
this.transition = transition;
this.heightVar = DominoId.unique(EXPAND_COLLAPSE_HEIGHT_VAR);
this.treeItem.setAttribute(DUI_EXPAND_COLLAPSE_VAR, this.heightVar);
this.treeItem.setCssProperty("height", "var(" + this.heightVar + ", auto)");
this.node.setAttribute(DUI_EXPAND_COLLAPSE_VAR, this.heightVar);
this.node.setCssProperty("height", "var(" + this.heightVar + ", auto)");
}

/** @dominokit-site-ignore {@inheritDoc} */
@Override
public void init(Element element, CollapsibleHandlers handlers) {
this.handlers = handlers;
this.treeItem.addCss(dui_height_collapsed_overflow).addCss(transition.getStyle());
this.treeItem.nowOrWhenAttached(
this.node.addCss(dui_height_collapsed_overflow).addCss(transition.getStyle());
this.node.nowOrWhenAttached(
() -> {
double height = treeItem.getBoundingClientRect().height;
treeItem.setAttribute(DUI_COLLAPSED_HEIGHT, height);
double height = node.getBoundingClientRect().height;
node.setAttribute(DUI_COLLAPSED_HEIGHT, height);
});

element.setAttribute(DUI_COLLAPSED, true);
Expand All @@ -94,74 +93,72 @@ public void cleanup(Element element) {
/** @dominokit-site-ignore {@inheritDoc} */
@Override
public void expand(Element element) {
treeItem.nowOrWhenAttached(
node.nowOrWhenAttached(
() -> {
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 height = node.getBoundingClientRect().height;
node.setAttribute(DUI_COLLAPSED_HEIGHT, height);
node.getSubTree().show();
node.setCssProperty(this.heightVar, height + "px");
double expandedHeight = getActualHeight();
treeItem.setAttribute(DUI_EXPANDED_HEIGHT, expandedHeight);
node.setAttribute(DUI_EXPANDED_HEIGHT, expandedHeight);
expandElement(element);
}
});
}

private double getActualHeight() {
double expandedHeight =
treeItem.childElements().stream()
node.childElements().stream()
.filter(IsCollapsible::isExpanded)
.mapToDouble(e -> e.getBoundingClientRect().height)
.sum();
return expandedHeight;
}

private void expandElement(Element element) {
if (dui_transition_none.isAppliedTo(treeItem)) {
treeItem.setCssProperty(this.heightVar, "auto");
treeItem.removeAttribute(DUI_COLLAPSED);
if (dui_transition_none.isAppliedTo(node)) {
node.setCssProperty(this.heightVar, "auto");
node.removeAttribute(DUI_COLLAPSED);
handlers.onExpandCompleted().run();
expanding = false;
} else {
EventListener stopListener =
evt -> {
resetParentHeight(treeItem);
treeItem.setCssProperty(this.heightVar, "auto");
resetParentHeight(node);
node.setCssProperty(this.heightVar, "auto");
handlers.onExpandCompleted().run();
expanding = false;
};

createAnimationEndListeners(stopListener);

String expandedHeight = treeItem.getAttribute(DUI_EXPANDED_HEIGHT);
treeItem.setCssProperty(this.heightVar, expandedHeight + "px");
treeItem.removeAttribute(DUI_COLLAPSED);
String expandedHeight = node.getAttribute(DUI_EXPANDED_HEIGHT);
node.setCssProperty(this.heightVar, expandedHeight + "px");
node.removeAttribute(DUI_COLLAPSED);
}
}

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);
node.element().addEventListener("webkitTransitionEnd", stopListener, addEventListenerOptions);
node.element().addEventListener("MSTransitionEnd", stopListener, addEventListenerOptions);
node.element().addEventListener("mozTransitionEnd", stopListener, addEventListenerOptions);
node.element().addEventListener("oanimationend", stopListener, addEventListenerOptions);
node.element().addEventListener("animationend", stopListener, addEventListenerOptions);
}

/** @dominokit-site-ignore {@inheritDoc} */
@Override
public void collapse(Element element) {
if (!expanding) {
collapsing = true;
treeItem.setCssProperty(this.heightVar, getActualHeight() + "px");
boolean disableAnimation = dui_transition_none.isAppliedTo(treeItem);
treeItem.apply(
node.setCssProperty(this.heightVar, getActualHeight() + "px");
boolean disableAnimation = dui_transition_none.isAppliedTo(node);
node.apply(
self -> {
if (self.isAttached()) {
this.handlers.onBeforeCollapse().run();
Expand All @@ -176,11 +173,11 @@ public void collapse(Element element) {
self.onAttached(
(e, mutationRecord) -> {
this.handlers.onBeforeCollapse().run();
treeItem.addCss(dui_transition_none);
node.addCss(dui_transition_none);
EventListener stopListener =
evt -> {
if (!disableAnimation) {
dui_transition_none.remove(treeItem);
dui_transition_none.remove(node);
}
handlers.onCollapseCompleted().run();
collapsing = false;
Expand All @@ -193,29 +190,28 @@ public void collapse(Element element) {
}
}

private void resetParentHeight(TreeItem<?> treeItem) {
treeItem
private void resetParentHeight(TreeNode<?, ?, ?> treeNode) {
treeNode
.getParent()
.ifPresent(
parent -> {
if (parent instanceof TreeItem) {
TreeItem<?> parentItem = (TreeItem<?>) parent;
if (parent instanceof TreeNode) {
TreeNode<?, ?, ?> parentItem = (TreeNode<?, ?, ?>) parent;
parentItem.removeCssProperty(parentItem.getAttribute(DUI_EXPAND_COLLAPSE_VAR));
parent.getParent().ifPresent(treeItem1 -> resetParentHeight(parentItem));
}
});
}

private void collapseElement(Element element) {
if (dui_transition_none.isAppliedTo(treeItem)) {
treeItem.setAttribute(DUI_COLLAPSED, "true");
treeItem.setCssProperty(this.heightVar, treeItem.getAttribute(DUI_COLLAPSED_HEIGHT) + "px");
if (dui_transition_none.isAppliedTo(node)) {
node.setAttribute(DUI_COLLAPSED, "true");
node.setCssProperty(this.heightVar, node.getAttribute(DUI_COLLAPSED_HEIGHT) + "px");
} else {
DomGlobal.requestAnimationFrame(
timestamp -> {
treeItem.setAttribute(DUI_COLLAPSED, "true");
treeItem.setCssProperty(
this.heightVar, treeItem.getAttribute(DUI_COLLAPSED_HEIGHT) + "px");
node.setAttribute(DUI_COLLAPSED, "true");
node.setCssProperty(this.heightVar, node.getAttribute(DUI_COLLAPSED_HEIGHT) + "px");
});
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import java.util.function.Supplier;
import org.dominokit.domino.ui.collapsible.CollapseStrategy;
import org.dominokit.domino.ui.collapsible.TreeHeightCollapseStrategy;
import org.dominokit.domino.ui.tree.TreeItem;
import org.dominokit.domino.ui.tree.TreeNode;

/**
* Implementations of this interface can be used to configure defaults for {@link
Expand All @@ -31,10 +31,10 @@ public interface TreeConfig extends ComponentConfig {
*
* <p>Defaults to : {@code TreeHeightCollapseStrategy}
*
* @param treeItem The TreeItem we are creating the strategy for.
* @param node The TreeItem we are creating the strategy for.
* @return a {@code Supplier<CollapseStrategy>}
*/
default Supplier<CollapseStrategy> getTreeDefaultCollapseStrategy(TreeItem<?> treeItem) {
return () -> new TreeHeightCollapseStrategy(treeItem);
default Supplier<CollapseStrategy> getTreeDefaultCollapseStrategy(TreeNode<?, ?, ?> node) {
return () -> new TreeHeightCollapseStrategy(node);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* 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.tree;

public interface HasActiveNode<V, N extends TreeNode<V, N, S>, S> {
void setActiveNode(N node);

void setActiveNode(N node, boolean silent);

N getActiveNode();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* Copyright © 2019 Dominokit
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dominokit.domino.ui.tree;

public interface HasTreeRoot<V> {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* 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.tree;

import java.util.Optional;

public interface IsParentNode<V, N extends TreeNode<V, N, S>, S> extends HasActiveNode<V, N, S> {
RootNode<V, N, S> getRootNode();

IsParentNode<V, N, S> expandNode();

IsParentNode<V, N, S> expandNode(boolean expandParent);

TreeItemFilter<N> getFilter();

Optional<IsParentNode<V, N, S>> getParent();

void removeNode(N node);

V getValue();

void setValue(V value);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* 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.tree;

import org.dominokit.domino.ui.icons.Icon;

/**
* An interface to provide custom icons for tree items.
*
* @param <N> The type of data associated with each tree item.
*/
public interface NodeIconSupplier<V, N extends TreeNode<V, N, S>, S> {
/**
* Creates an icon for the given tree item.
*
* @param item The tree item for which to create the icon.
* @return The created icon.
*/
Icon<?> createIcon(N item);
}
28 changes: 28 additions & 0 deletions domino-ui/src/main/java/org/dominokit/domino/ui/tree/RootNode.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* 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.tree;

public interface RootNode<V, N extends TreeNode<V, N, S>, S> extends HasActiveNode<V, N, S> {
boolean isAutoCollapse();

boolean isAutoExpandFound();

NodeIconSupplier<V, N, S> getIconSupplier();

default void onActiveNodeChanged(N node, S selection, boolean silent) {}

void onDeselectionChanged(N source, S selection);
}
Loading

0 comments on commit 8b0860b

Please sign in to comment.