org.dominokit.domino.ui.tree.Tree Maven / Gradle / Ivy
/*
* 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 static java.util.Objects.isNull;
import static java.util.Objects.nonNull;
import static org.dominokit.domino.ui.tree.TreeStyles.*;
import static org.jboss.elemento.Elements.*;
import elemental2.dom.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.dominokit.domino.ui.collapsible.CollapseStrategy;
import org.dominokit.domino.ui.icons.Icon;
import org.dominokit.domino.ui.icons.Icons;
import org.dominokit.domino.ui.search.Search;
import org.dominokit.domino.ui.style.ColorScheme;
import org.dominokit.domino.ui.style.Styles;
import org.dominokit.domino.ui.style.Unit;
import org.dominokit.domino.ui.utils.BaseDominoElement;
import org.dominokit.domino.ui.utils.DominoElement;
import org.dominokit.domino.ui.utils.ParentTreeItem;
import org.jboss.elemento.IsElement;
/**
* A component provides a tree representation of elements
*
* Customize the component can be done by overwriting classes provided by {@link TreeStyles}
*
*
For example:
*
*
* Tree hardwareTree =
* Tree.create("HARDWARE")
* .setToggleTarget(ToggleTarget.ICON)
* .addItemClickListener((treeItem) -> DomGlobal.console.info(treeItem.getValue()))
* .appendChild(
* TreeItem.create("Computer", Icons.ALL.laptop_mdi())
* .addClickListener((evt) -> Notification.create("Computer").show()))
* .appendChild(
* TreeItem.create("Headset", Icons.ALL.headset_mdi())
* .addClickListener((evt) -> Notification.create("Headset").show()))
* .appendChild(
* TreeItem.create("Keyboard", Icons.ALL.keyboard_mdi())
* .addClickListener((evt) -> Notification.create("Keyboard").show()))
* .appendChild(
* TreeItem.create("Mouse", Icons.ALL.mouse_mdi())
* .addClickListener((evt) -> Notification.create("Mouse").show()))
* .addSeparator()
* .appendChild(
* TreeItem.create("Laptop", Icons.ALL.laptop_mdi())
* .addClickListener((evt) -> Notification.create("Laptop").show()))
* .appendChild(
* TreeItem.create("Smart phone", Icons.ALL.cellphone_mdi())
* .addClickListener((evt) -> Notification.create("Smart phone").show()))
* .appendChild(
* TreeItem.create("Tablet", Icons.ALL.tablet_mdi())
* .addClickListener((evt) -> Notification.create("Tablet").show()))
* .appendChild(
* TreeItem.create("Speaker", Icons.ALL.speaker_mdi())
* .addClickListener((evt) -> Notification.create("Speaker").show()));
*
*
* @param the type of the object
* @see BaseDominoElement
* @see ParentTreeItem
*/
public class Tree extends BaseDominoElement>
implements ParentTreeItem>, IsElement {
private final HTMLElement title = DominoElement.of(span()).css(TITLE).element();
private ToggleTarget toggleTarget = ToggleTarget.ANY;
private TreeItemFilter> filter =
(treeItem, searchToken) ->
treeItem.getTitle().toLowerCase().contains(searchToken.toLowerCase());
private final HTMLLIElement header =
DominoElement.of(li()).css(HEADER).css(MENU_HEADER).add(title).element();
private final HTMLUListElement root = DominoElement.of(ul()).add(header).css(LIST).element();
private final HTMLDivElement menu =
DominoElement.of(div()).style("overflow-x: hidden").css(MENU).add(root).element();
private TreeItem activeTreeItem;
private boolean autoCollapse = true;
private final List> subItems = new ArrayList<>();
private boolean autoExpandFound;
private ColorScheme colorScheme;
private Search search;
private Icon searchIcon;
private Icon collapseAllIcon;
private Icon expandAllIcon;
private int levelPadding = 15;
private T value;
private final List> itemsClickListeners = new ArrayList<>();
private CollapseStrategy collapseStrategy;
public Tree() {
this("");
}
public Tree(String treeTitle) {
init(this);
if (isNull(treeTitle) || treeTitle.trim().isEmpty()) {
DominoElement.of(header).hide();
}
title.textContent = treeTitle;
}
public Tree(String treeTitle, T value) {
this(treeTitle);
this.value = value;
}
public Tree(T value) {
this("");
this.value = value;
}
/**
* @param title the title of the tree
* @return new instance
*/
public static Tree create(String title) {
return new Tree<>(title);
}
/** @return new instance without title */
public static Tree create() {
Tree tree = new Tree<>();
DominoElement.of(tree.header).hide();
return tree;
}
/**
* @param title the title of the tree
* @param value the default selected value
* @param the type of the object
* @return new instance
*/
public static Tree create(String title, T value) {
return new Tree<>(title, value);
}
/**
* @param value the default selected value
* @param the type of the object
* @return new instance
*/
public static Tree create(T value) {
return new Tree<>(value);
}
/**
* Adds a new tree item
*
* @param treeItem a new {@link TreeItem}
* @return same instance
*/
public Tree appendChild(TreeItem treeItem) {
root.appendChild(treeItem.element());
treeItem.setParent(this);
treeItem.setLevel(1);
treeItem.setLevelPadding(levelPadding);
treeItem.setToggleTarget(this.toggleTarget);
if (nonNull(collapseStrategy)) {
treeItem.setCollapseStrategy(collapseStrategy);
}
this.subItems.add(treeItem);
return this;
}
/**
* Adds a new separator
*
* @return same instance
*/
public Tree addSeparator() {
root.appendChild(DominoElement.of(li()).css("gap").css("separator").add(a()).element());
return this;
}
/**
* Adds spaces between items
*
* @return same instance
*/
public Tree addGap() {
root.appendChild(DominoElement.of(li()).css("gap").add(a()).element());
return this;
}
/**
* Sets what is the target for toggling an item
*
* @param toggleTarget the {@link ToggleTarget}
* @return same instance
*/
public Tree setToggleTarget(ToggleTarget toggleTarget) {
if (nonNull(toggleTarget)) {
subItems.forEach(item -> item.setToggleTarget(toggleTarget));
this.toggleTarget = toggleTarget;
}
return this;
}
/**
* Sets level padding for item
*
* @param levelPadding string with padding for item
* @return same instance
*/
public Tree setLevelPadding(int levelPadding) {
this.levelPadding = levelPadding;
subItems.forEach(item -> item.setLevelPadding(levelPadding));
return this;
}
/**
* Sets the color scheme for the tree
*
* @param colorScheme the {@link ColorScheme}
* @return same instance
*/
public Tree setColorScheme(ColorScheme colorScheme) {
if (nonNull(this.colorScheme)) {
removeCss(colorScheme.color().getBackground());
DominoElement.of(header).removeCss(this.colorScheme.darker_3().getBackground());
}
this.colorScheme = colorScheme;
addCss(colorScheme.color().getBackground());
DominoElement.of(header).addCss(this.colorScheme.darker_3().getBackground());
return this;
}
/** {@inheritDoc} */
@Override
public TreeItem getActiveItem() {
return activeTreeItem;
}
/** {@inheritDoc} */
@Override
public void setActiveItem(TreeItem activeItem) {
setActiveItem(activeItem, false);
}
/** {@inheritDoc} */
@Override
public void setActiveItem(TreeItem activeItem, boolean silent) {
if (nonNull(this.activeTreeItem) && !this.activeTreeItem.equals(activeItem)) {
this.activeTreeItem.deactivate();
}
this.activeTreeItem = activeItem;
this.activeTreeItem.activate();
if (!silent) {
onTreeItemClicked(activeItem);
}
}
/** @return the header element */
public DominoElement getHeader() {
return DominoElement.of(header);
}
/** @return the root element */
public DominoElement getRoot() {
return DominoElement.of(root);
}
/** @return the title element */
public DominoElement getTitle() {
return DominoElement.of(title);
}
/**
* Sets the height of the tree to be automatic based on the content
*
* @return same instance
*/
public Tree autoHeight() {
root.style.height = CSSProperties.HeightUnionType.of("calc(100vh - 83px)");
element().style.height = CSSProperties.HeightUnionType.of("calc(100vh - 70px)");
return this;
}
/**
* Sets the height of the tree to be automatic based on the content with an offset
*
* @param offset the offset value
* @return same instance
*/
public Tree autoHeight(int offset) {
root.style.height = CSSProperties.HeightUnionType.of("calc(100vh - " + offset + 13 + "px)");
element().style.height = CSSProperties.HeightUnionType.of("calc(100vh - " + offset + "px)");
return this;
}
/**
* Enables the search
*
* @return same instance
*/
public Tree enableSearch() {
search =
Search.create(true)
.styler(style -> style.setHeight(Unit.px.of(40)))
.onSearch(Tree.this::filter)
.onClose(this::clearFilter);
searchIcon =
Icons.ALL
.search()
.setMarginBottom("0px")
.setMarginTop("0px")
.addCss(Styles.pull_right)
.setCssProperty("cursor", "pointer");
this.header.appendChild(search.element());
this.header.appendChild(searchIcon.element());
searchIcon.element().addEventListener("click", evt -> search.open());
return this;
}
/**
* Adds the ability to expand/collapse all items
*
* @return same instance
*/
public Tree enableFolding() {
collapseAllIcon =
Icons.ALL
.fullscreen_exit()
.setMarginBottom("0px")
.setMarginTop("0px")
.addCss(Styles.pull_right)
.setCssProperty("cursor", "pointer");
collapseAllIcon.element().addEventListener("click", evt -> collapseAll());
expandAllIcon =
Icons.ALL
.fullscreen()
.setMarginBottom("0px")
.setMarginTop("0px")
.addCss(Styles.pull_right)
.setCssProperty("cursor", "pointer");
expandAllIcon.element().addEventListener("click", evt -> expandAll());
header.appendChild(expandAllIcon.element());
header.appendChild(collapseAllIcon.element());
return this;
}
/** Expand all items */
public void expandAll() {
getSubItems().forEach(TreeItem::expandAll);
}
/** Collapse all items */
public void collapseAll() {
getSubItems().forEach(TreeItem::collapseAll);
}
/** Deactivate all items */
public void deactivateAll() {
getSubItems().forEach(TreeItem::deactivate);
}
/**
* Expand the items found by the search automatically
*
* @return same instance
*/
public Tree autoExpandFound() {
this.autoExpandFound = true;
return this;
}
/** {@inheritDoc} */
@Override
public boolean isAutoExpandFound() {
return autoExpandFound;
}
/**
* Sets if the items found by the search should be expanded automatically
*
* @param autoExpandFound true to expand automatically, false otherwise
*/
public void setAutoExpandFound(boolean autoExpandFound) {
this.autoExpandFound = autoExpandFound;
}
/** Clears all the filters */
public void clearFilter() {
subItems.forEach(TreeItem::clearFilter);
}
/**
* Filter based on the search query
*
* @param searchToken the query
*/
public void filter(String searchToken) {
subItems.forEach(treeItem -> treeItem.filter(searchToken));
}
/** {@inheritDoc} */
@Override
public Tree getTreeRoot() {
return this;
}
/**
* Sets if item should be collapsed automatically when it is deactivated
*
* @param autoCollapse true to collapse automatically, false otherwise
* @return same instance
*/
public Tree setAutoCollapse(boolean autoCollapse) {
this.autoCollapse = autoCollapse;
return this;
}
/**
* Sets the title of the tree
*
* @param title the title text
* @return same instance
*/
public Tree setTitle(String title) {
getTitle().setTextContent(title);
if (getHeader().isCollapsed()) {
getHeader().show();
}
return this;
}
/** @return true if deactivated items should be collapsed automatically */
public boolean isAutoCollapse() {
return autoCollapse;
}
/** {@inheritDoc} */
@Override
public List> getSubItems() {
return new ArrayList<>(subItems);
}
/** {@inheritDoc} */
@Override
public Tree expand(boolean expandParent) {
return this;
}
/** {@inheritDoc} */
@Override
public Tree expand() {
return this;
}
/** {@inheritDoc} */
@Override
public Optional> getParent() {
return Optional.empty();
}
/** {@inheritDoc} */
@Override
public void activate() {}
/** {@inheritDoc} */
@Override
public void activate(boolean activateParent) {}
/** @return the search element */
public Search getSearch() {
return search;
}
/** @return the search icon */
public Icon getSearchIcon() {
return searchIcon;
}
/** @return the collapse all icon */
public Icon getCollapseAllIcon() {
return collapseAllIcon;
}
/** @return the expand all icon */
public Icon getExpandAllIcon() {
return expandAllIcon;
}
/** @return the current value */
public T getValue() {
return value;
}
/**
* Sets the value
*
* @param value the new value
*/
public void setValue(T value) {
this.value = value;
}
/**
* Adds a click listener to be called when item is clicked
*
* @param itemClickListener a {@link ItemClickListener}
* @return same instance
*/
public Tree addItemClickListener(ItemClickListener itemClickListener) {
this.itemsClickListeners.add(itemClickListener);
return this;
}
/**
* Removes a click listener
*
* @param itemClickListener a {@link ItemClickListener} to be removed
* @return same instance
*/
public Tree removeItemClickListener(ItemClickListener itemClickListener) {
this.itemsClickListeners.remove(itemClickListener);
return this;
}
void onTreeItemClicked(TreeItem treeItem) {
this.itemsClickListeners.forEach(
itemClickListener -> itemClickListener.onTreeItemClicked(treeItem));
}
/** @return the list of the items in the current active path */
public List> getActivePath() {
List> activeItems = new ArrayList<>();
TreeItem activeItem = getActiveItem();
while (nonNull(activeItem)) {
activeItems.add(activeItem);
activeItem = activeItem.getActiveItem();
}
return activeItems;
}
/** @return the list of values in the current active path */
public List getActivePathValues() {
List activeValues = new ArrayList<>();
TreeItem activeItem = getActiveItem();
while (nonNull(activeItem)) {
activeValues.add(activeItem.getValue());
activeItem = activeItem.getActiveItem();
}
return activeValues;
}
/** {@inheritDoc} */
@Override
public void removeItem(TreeItem item) {
subItems.remove(item);
item.remove();
}
/**
* Sets the filter that will be used when searching items, the default filter searches using the
* title of the items
*
* @param filter a {@link TreeItemFilter}
* @return same instance
*/
public Tree setFilter(TreeItemFilter> filter) {
this.filter = filter;
return this;
}
/** {@inheritDoc} */
@Override
public TreeItemFilter> getFilter() {
return this.filter;
}
public Tree setCollapseStrategy(CollapseStrategy collapseStrategy) {
getSubItems().forEach(tTreeItem -> setCollapseStrategy(collapseStrategy));
this.collapseStrategy = collapseStrategy;
return this;
}
public CollapseStrategy getCollapseStrategy() {
return collapseStrategy;
}
/** {@inheritDoc} */
@Override
public HTMLDivElement element() {
return menu;
}
/**
* A listener to be called when clicking on item
*
* @param the type of the object
*/
public interface ItemClickListener {
void onTreeItemClicked(TreeItem treeItem);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy