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

org.dominokit.domino.ui.popover.BasePopover Maven / Gradle / Ivy

There is a newer version: 2.0.3
Show newest version
/*
 * 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.popover;

import static java.util.Objects.isNull;
import static org.dominokit.domino.ui.collapsible.Collapsible.DUI_COLLAPSED;
import static org.dominokit.domino.ui.utils.Domino.*;

import elemental2.dom.*;
import java.util.Objects;
import java.util.function.Supplier;
import jsinterop.base.Js;
import org.dominokit.domino.ui.config.HasComponentConfig;
import org.dominokit.domino.ui.config.ZIndexConfig;
import org.dominokit.domino.ui.elements.DivElement;
import org.dominokit.domino.ui.events.EventType;
import org.dominokit.domino.ui.menu.direction.DropDirection;
import org.dominokit.domino.ui.utils.*;

/**
 * The base class for creating popovers in the Domino UI framework. Popovers are small overlays that
 * can be displayed next to an HTML element and often contain additional information or actions.
 * This class provides the core functionality for creating and managing popovers.
 *
 * 

Usage Example: * *

 * // Create a new popover attached to an HTML element
 * Element targetElement = document.getElementById("popover-target");
 * BasePopover<MyPopover> popover = new MyPopover(targetElement);
 *
 * // Customize the popover
 * popover.withHeader(header -> header.setTextContent("Popover Header"));
 * popover.withBody(body -> body.setTextContent("Popover Content"));
 *
 * // Show the popover
 * popover.expand();
 * 
* * @param The type of the popover class that extends {@code BasePopover}. * @see BaseDominoElement */ public abstract class BasePopover> extends BaseDominoElement implements IsPopup, PopoverStyles, FollowOnScroll.ScrollFollower, HasComponentConfig { protected final Element targetElement; protected final EventListener closeAllListener; private DivElement root; private DivElement wrapper; private DivElement arrow; private DivElement header; private DivElement body; private DropDirection popupPosition = DropDirection.BEST_MIDDLE_UP_DOWN; private boolean closeOthers = true; protected final EventListener closeListener; private final FollowOnScroll followOnScroll; private Supplier openCondition = () -> true; private EventListener lostFocusListener; private boolean closeOnBlur = DominoUIConfig.CONFIG.isClosePopupOnBlur(); /** * Constructs a new BasePopover associated with the provided target element. BasePopover is the * base class for creating popovers in the Domino UI framework. * * @param target The HTML element to which the popover will be attached. */ public BasePopover(Element target) { this.targetElement = target; root = div() .addCss(dui_popover) .setAttribute("dui-position-x-offset", "9") .appendChild( wrapper = div() .addCss(dui_popover_wrapper) .appendChild(arrow = div().addCss(dui_popover_arrow)) .appendChild(header = div().addCss(dui_popover_header)) .appendChild(body = div().addCss(dui_popover_body))); init((T) this); followOnScroll = new FollowOnScroll(targetElement, this); closeListener = getCloseListener(); root.addEventListener( EventType.click.getName(), evt -> { evt.preventDefault(); evt.stopPropagation(); }); elementOf(targetElement) .onDetached( mutationRecord -> { close(); DomGlobal.document.body.removeEventListener("blur", lostFocusListener, true); }); onDetached( mutationRecord -> { body().removeEventListener(EventType.keydown.getName(), closeListener); DomGlobal.document.body.removeEventListener("blur", lostFocusListener, true); }); addCollapseListener(this::doClose); closeAllListener = evt -> { CustomEvent event = Js.uncheckedCast(evt); if (!Objects.equals(event.detail, getDominoId())) { close(); } }; setAttribute(DUI_COLLAPSED, "true"); lostFocusListener = evt -> { if (isCloseOnBlur()) { DomGlobal.setTimeout( p0 -> { Element e = DomGlobal.document.activeElement; if (!(targetElement.contains(e) || e.equals(targetElement) || this.element().contains(e) || e.equals(this.element()))) { close(); } }, 0); } }; } /** * Gets the EventListener responsible for closing the popover when certain events occur. * *

Subclasses should override this method to provide a custom EventListener that defines the * popover's close behavior. * * @return The EventListener for closing the popover. */ protected abstract EventListener getCloseListener(); /** * Expands the popover, displaying it on the screen. * * @return The current instance of the popover. */ @Override public T expand() { if (isEnabled() && (isNull(openCondition.get()) || openCondition.get())) { if (closeOthers) { closeOthers(getDominoId()); } doOpen(); getConfig().getZindexManager().onPopupOpen(this); triggerOpenListeners((T) this); } return (T) this; } /** * Opens the popover, making it visible on the screen. * * @return The instance of the popover after opening. */ public T open() { return expand(); } /** * Internal method to perform the opening of the popover. It appends the popover's root element to * the body, positions the popover, and starts monitoring for scroll events (if applicable). */ protected void doOpen() { body().appendChild(root.element()); super.expand(); doPosition(); if (!isCloseOnScroll()) { followOnScroll.start(); } DomGlobal.document.body.addEventListener("blur", lostFocusListener, true); } /** Positions the popover element based on the specified or default drop direction. */ private void doPosition() { doPosition(this.popupPosition); } /** * Positions the popover element based on the provided drop direction. * * @param position The drop direction to position the popover. */ protected void doPosition(DropDirection position) { popupPosition.position(root.element(), targetElement); } /** * Closes the popover, hiding it from view. * * @return The instance of the popover after closing. */ public T close() { collapse(); return (T) this; } /** * Closes other popovers that are currently open, except for the one with the specified source ID. * * @param sourceId The unique identifier of the popover to exclude from closing others. * @return The instance of the popover after closing others. */ protected abstract T closeOthers(String sourceId); /** * Internal method to perform the closing of the popover. It stops monitoring scroll events, * removes the popover element, and triggers collapse listeners. */ protected void doClose() { followOnScroll.stop(); element().remove(); body().removeEventListener(EventType.keydown.getName(), closeListener); getConfig().getZindexManager().onPopupClose(this); triggerCloseListeners((T) this); } /** * {@inheritDoc} Returns whether the popover is modal. Popovers are not modal by default. * * @return {@code false} since popovers are not modal by default. */ @Override public boolean isModal() { return false; } /** * {@inheritDoc} Returns whether the popover should automatically close. Popovers are set to close * automatically by default. * * @return {@code true} since popovers are set to close automatically by default. */ @Override public boolean isAutoClose() { return true; } /** * Discards the popover by closing it. This method is equivalent to calling the {@link #close()} * method. It is provided for convenience to explicitly indicate the intention to discard the * popover. */ public void discard() { close(); } /** * Sets the position of the popover relative to its target element. * * @param position The desired position of the popover. * @return The current instance of the popover. */ public T setPosition(DropDirection position) { this.popupPosition.cleanup(this.element()); this.popupPosition = position; return (T) this; } /** * Specifies whether the popover should automatically close when other popovers are opened. * * @param closeOthers {@code true} to close other popovers, {@code false} to keep them open. * @return The current instance of the popover. */ public T setCloseOthers(boolean closeOthers) { this.closeOthers = closeOthers; return (T) this; } /** * Gets the element representing the arrow of the popover. * * @return The arrow element of the popover. */ public DivElement getArrowElement() { return arrow; } /** * Sets the content of the popover's arrow element. * * @param handler A function to customize the arrow element. * @return The current instance of the popover. */ public T withArrow(ChildHandler handler) { handler.apply((T) this, arrow); return (T) this; } /** * Gets the element representing the header of the popover. * * @return The header element of the popover. */ public DivElement getHeaderElement() { return header; } /** * Sets the content of the popover's header element. * * @param handler A function to customize the header element. * @return The current instance of the popover. */ public T withHeader(ChildHandler handler) { handler.apply((T) this, header); return (T) this; } /** * Gets the body element of the popover. * * @return The body element of the popover. */ public DivElement getBody() { return body; } /** * Sets the content of the popover's body element. * * @param handler A function to customize the body element. * @return The current instance of the popover. */ public T withBody(ChildHandler handler) { handler.apply((T) this, body); return (T) this; } /** * Gets the HTML element to which the popover's content should be appended. * * @return The HTML element to which the popover's content should be appended. */ @Override public HTMLElement getAppendTarget() { return body.element(); } /** * Sets whether the popover should be closed automatically when the user scrolls the page. * * @param closeOnScroll {@code true} to enable automatic closing on page scroll, {@code false} * otherwise. * @return The updated instance of the popover. */ public T closeOnScroll(boolean closeOnScroll) { setAttribute("d-close-on-scroll", closeOnScroll); return (T) this; } /** * Sets a condition supplier that determines whether the popover should be opened. The popover * will only open if the condition provided by the supplier evaluates to {@code true}. * * @param openCondition The supplier that provides the open condition. * @return The updated instance of the popover. */ public T setOpenCondition(Supplier openCondition) { this.openCondition = openCondition; return (T) this; } /** * Checks if the popover should be closed when the user clicks outside of it (loses focus). * * @return {@code true} if the popover should be closed on blur, {@code false} otherwise. */ public boolean isCloseOnBlur() { return closeOnBlur; } /** * Specifies whether the popover should automatically close when the user clicks outside of it. * * @param closeOnBlur {@code true} to close on blur, {@code false} to keep it open. * @return The current instance of the popover. */ public T setCloseOnBlur(boolean closeOnBlur) { this.closeOnBlur = closeOnBlur; return (T) this; } /** * Gets the DOM element representing the popover. * * @return The DOM element of the popover. */ @Override public HTMLDivElement element() { return root.element(); } /** * Checks if the popover should automatically close when the user scrolls the page. * * @return {@code true} if the popover should close on page scroll, {@code false} otherwise. */ public boolean isCloseOnScroll() { return hasAttribute("d-close-on-scroll") && getAttribute("d-close-on-scroll").equalsIgnoreCase("true"); } /** * Checks if the follower (popover) is currently open. * * @return {@code true} if the follower is open, {@code false} otherwise. */ @Override public boolean isFollowerOpen() { return isAttached(); } /** Positions the follower (popover) relative to its target element. */ @Override public void positionFollower() { doPosition(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy