All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.dominokit.domino.ui.splitpanel.BaseSplitPanel 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.splitpanel;

import static org.dominokit.domino.ui.utils.Domino.*;

import elemental2.dom.HTMLDivElement;
import java.util.LinkedList;
import java.util.List;
import org.dominokit.domino.ui.elements.DivElement;
import org.dominokit.domino.ui.utils.BaseDominoElement;
import org.dominokit.domino.ui.utils.ChildHandler;

/**
 * Represents the base class for split panels which allow a UI to be divided into multiple panels
 * with resize capabilities.
 *
 * 

Usage example: * *

 * BaseSplitPanel splitPanel = new YourConcreteSplitPanel();
 * splitPanel.appendChild(new YourSplitPanelType());
 * // ... additional configurations
 * 
* * @param The specific type of the split panel * @param The specific type of the splitter * @see BaseDominoElement */ abstract class BaseSplitPanel, S extends BaseSplitter> extends BaseDominoElement implements HasSize, HasSplitPanels, SplitStyles { private final DivElement element; private final List panels = new LinkedList<>(); private final List splitters = new LinkedList<>(); private double firstSize = 0; private double secondSize = 0; /** Creates a new base split panel. */ public BaseSplitPanel() { element = div().addCss(dui_split_layout); init((T) this); element.onAttached(mutationRecord -> updatePanelsSize()); } /** * Updates the sizes of the panels based on the current configuration. It calculates the required * size for each panel taking into account the splitter size share and sets the appropriate size. */ private void updatePanelsSize() { double mainPanelSize = getSize(); String splitterPanelShare = getSplittersSizeShare(); for (SplitPanel panel : panels) { double panelSize = getPanelSize(panel); double sizePercent = (panelSize / mainPanelSize) * 100; setPanelSize(panel, "calc(" + sizePercent + "% - " + splitterPanelShare + ")"); } } /** * Calculates and returns the size share for the splitters based on the number of panels. The * calculation uses the CSS variable for the splitter size to determine how much space the * splitters occupy. * * @return the calculated size share in CSS format. */ public String getSplittersSizeShare() { int n = panels.size(); return "(var(--dui-split-layout-splitter-size)*" + (n - 1) + "/" + n + ")"; } /** * {@inheritDoc} * *

This implementation captures the starting sizes of the two provided panels which will be * resized. * * @param first The first panel involved in the resizing. * @param second The second panel involved in the resizing. */ @Override public void onResizeStart(SplitPanel first, SplitPanel second) { this.firstSize = Math.round(getPanelSize(first)); this.secondSize = Math.round(getPanelSize(second)); } /** * {@inheritDoc} * *

This implementation performs the resizing of the two provided panels based on the specified * size difference. The resizing is subject to constraints such as minimum and maximum sizes or * percentages. * * @param first The first panel to be resized. * @param second The second panel to be resized. * @param sizeDiff The size difference used for resizing. */ @Override public void resizePanels(SplitPanel first, SplitPanel second, double sizeDiff) { double maxSize = getSize(); double current1stSize = getPanelSize(first); double new1stSize = this.firstSize + sizeDiff; double new1stPercent = (new1stSize / maxSize) * 100; double new2ndSize = this.secondSize - sizeDiff; double new2ndPercent = (new2ndSize / maxSize) * 100; boolean right = new1stSize > current1stSize; boolean left = !right; if (right && ((new2ndSize < second.getMinSize() || new2ndPercent < second.getMinPercent())) || right && ((new1stSize > first.getMaxSize()) || (new1stPercent > first.getMaxPercent()))) { return; } if (left && ((new1stSize < first.getMinSize() || new1stPercent < first.getMinPercent())) || left && ((new2ndSize > second.getMaxSize()) || (new2ndPercent > second.getMaxPercent()))) { return; } setPanelSize(first, (new1stSize) + "px"); setPanelSize(second, (new2ndSize) + "px"); double panelsTotalSize = panels.stream().mapToDouble(this::getPanelSize).sum(); double splittersSize = splitters.stream().mapToDouble(BaseSplitter::getSize).sum(); double totalElementsSize = panelsTotalSize + splittersSize; double diff = maxSize - totalElementsSize; setPanelSize(second, (new2ndSize + diff) + "px"); } /** * Retrieves the size of the provided panel. The actual implementation will be provided by * subclasses. * * @param panel The panel whose size is to be retrieved. * @return the size of the panel. */ protected abstract double getPanelSize(SplitPanel panel); /** * Sets the size of the provided panel. The actual implementation will be provided by subclasses. * * @param panel The panel whose size is to be set. * @param size The new size to be set, in CSS format. */ protected abstract void setPanelSize(SplitPanel panel, String size); /** * Appends the given {@link SplitPanel} to the current split panel layout. * * @param panel the panel to append * @return the current instance of {@link T} */ public T appendChild(SplitPanel panel) { panels.add(panel); if (panels.size() > 1) { S splitter = createSplitter(panels.get(panels.size() - 2), panel, this); splitters.add(splitter); element.appendChild(splitter); element.appendChild(panel); SplitPanel secondLast = panels.get(panels.size() - 1); secondLast.setLast(false); panel.setLast(true); } else { panel.setFirst(true); element.appendChild(panel); } return (T) this; } /** * Creates and returns a splitter element used between two panels. This method is intended to be * implemented by subclasses to provide a concrete representation and behavior for the splitter * based on the context in which it will be used. * *

Example Usage: * *

   * @Override
   * protected MySplitter createSplitter(SplitPanel first, SplitPanel second, HasSplitPanels mainPanel) {
   *     return new MySplitter(first, second, mainPanel);
   * }
   * 
* * @param first The first panel that the splitter will be between. * @param second The second panel that the splitter will be between. * @param mainPanel The main panel in which both panels and the splitter reside. * @return the created splitter element. */ protected abstract S createSplitter( SplitPanel first, SplitPanel second, HasSplitPanels mainPanel); /** * Allows customization of the splitters contained within this split panel. * * @param handler the handler to manipulate the splitters * @return the current instance of {@link T} */ public T withSplitters(ChildHandler> handler) { handler.apply((T) this, splitters); return (T) this; } /** {@inheritDoc} */ @Override public HTMLDivElement element() { return element.element(); } }