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

org.patternfly.component.menu.MenuToggleMenu Maven / Gradle / Ivy

There is a newer version: 0.2.11
Show newest version
/*
 *  Copyright 2023 Red Hat
 *
 *  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
 *
 *      https://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.patternfly.component.menu;

import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;

import org.jboss.elemento.Attachable;
import org.jboss.elemento.TypedBuilder;
import org.jboss.elemento.logger.Logger;
import org.patternfly.component.ComponentDelegate;
import org.patternfly.component.ComponentType;
import org.patternfly.component.Expandable;
import org.patternfly.core.Aria;
import org.patternfly.handler.ToggleHandler;
import org.patternfly.popper.Placement;
import org.patternfly.popper.Popper;
import org.patternfly.popper.PopperBuilder;
import org.patternfly.popper.TriggerAction;
import org.patternfly.style.Modifiers.Disabled;

import elemental2.dom.Event;
import elemental2.dom.HTMLElement;
import elemental2.dom.MutationRecord;

import static org.jboss.elemento.Elements.failSafeRemoveFromParent;
import static org.jboss.elemento.Elements.insertAfter;
import static org.jboss.elemento.Elements.setVisible;
import static org.patternfly.popper.Placement.auto;
import static org.patternfly.popper.Placement.bottomStart;

/**
 * Abstract base component for components that combine a {@link MenuToggle} and a {@link Menu}, such as {@link Dropdown},
 * {@link SingleSelect} or {@link MultiSelect}.
 * 

* The component delegates to the {@link MenuToggle} component. The {@link Menu} is managed by a {@link Popper} instance. */ abstract class MenuToggleMenu> extends ComponentDelegate implements Disabled, Expandable, Attachable { // ------------------------------------------------------ instance private static final Logger logger = Logger.getLogger(MenuToggleMenu.class.getName()); private static final int Z_INDEX = 9999; private final Set triggerActions; private final List> toggleHandler; private int zIndex; private boolean flip; private boolean disabled; private Placement placement; private Popper popper; final MenuToggle menuToggle; Menu menu; MenuToggleMenu(ComponentType componentType, MenuToggle menuToggle, TriggerAction triggerAction) { super(componentType); this.menuToggle = menuToggle; this.toggleHandler = new ArrayList<>(); this.triggerActions = EnumSet.of(triggerAction); this.flip = true; this.placement = bottomStart; this.zIndex = Z_INDEX; delegateTo(menuToggle.element()); Attachable.register(menuToggle.element(), this); } @Override public void attach(MutationRecord mutationRecord) { if (menuToggle != null && menu != null) { if (disabled) { menuToggle.disabled(true); } setVisible(menu, false); insertAfter(menu.element(), menuToggle.element()); popper = new PopperBuilder(componentType().componentName, menuToggle.element(), menu.element()) .zIndex(zIndex) .placement(placement) .addModifier(org.patternfly.popper.Modifiers.noOverflow(), org.patternfly.popper.Modifiers.hide(), org.patternfly.popper.Modifiers.flip(placement == auto || flip), org.patternfly.popper.Modifiers.widths(), org.patternfly.popper.Modifiers.placement(), org.patternfly.popper.Modifiers.eventListeners(false)) .registerHandler(menuToggle.toggleElement, triggerActions, event -> expand(), event -> collapse()) .build(); } else { logger.error("No toggle and/or menu defined for %s / %o", componentType().name(), element()); } } @Override public void detach(MutationRecord mutationRecord) { failSafeRemoveFromParent(menu); popper.cleanup(); } // ------------------------------------------------------ add public B addMenu(Menu menu) { return add(menu); } // override to ensure internal wiring public B add(Menu menu) { this.menu = menu; // Do *not* add the menu now, delay it to attach() return that(); } // ------------------------------------------------------ builder @Override public B disabled(boolean disabled) { if (menuToggle != null) { menuToggle.disabled(disabled); } else { // defer to attach() this.disabled = disabled; } return that(); } @Override public boolean isDisabled() { return menuToggle == null ? disabled : menuToggle.isDisabled(); } public B flip(boolean flip) { this.flip = flip; return that(); } public B placement(Placement placement) { if (placement == auto) { flip = true; } this.placement = placement; return that(); } public B stayOpen() { this.triggerActions.clear(); this.triggerActions.add(TriggerAction.stayOpen); return that(); } public B zIndex(int zIndex) { this.zIndex = zIndex; return that(); } // ------------------------------------------------------ aria public B ariaLabel(String label) { return aria(Aria.label, label); } // ------------------------------------------------------ events public B onToggle(ToggleHandler toggleHandler) { this.toggleHandler.add(toggleHandler); return that(); } // ------------------------------------------------------ api @Override public void collapse(boolean fireEvent) { popper.hide(() -> { Expandable.collapse(element(), element(), null); if (fireEvent) { toggleHandler.forEach(th -> th.onToggle(new Event(""), that(), false)); } }); } @Override public void expand(boolean fireEvent) { popper.show(() -> { Expandable.expand(element(), element(), null); if (fireEvent) { toggleHandler.forEach(th -> th.onToggle(new Event(""), that(), true)); } }); } public MenuToggle menuToggle() { return menuToggle; } public Menu menu() { return menu; } }