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

org.patternfly.component.page.Page 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.page;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;

import org.jboss.elemento.Attachable;
import org.jboss.elemento.Callback;
import org.jboss.elemento.ResizeObserverCleanup;
import org.jboss.elemento.Scheduler;
import org.patternfly.component.BaseComponent;
import org.patternfly.component.ComponentType;
import org.patternfly.component.skiptocontent.SkipToContent;
import org.patternfly.core.ObservableValue;
import org.patternfly.handler.ResizeHandler;
import org.patternfly.style.Breakpoint;
import org.patternfly.style.Classes;
import org.patternfly.style.Rect;

import elemental2.dom.HTMLDivElement;
import elemental2.dom.MutationRecord;

import static org.jboss.elemento.Elements.div;
import static org.jboss.elemento.Elements.failSafeRemoveFromParent;
import static org.jboss.elemento.Elements.insertAfter;
import static org.jboss.elemento.Elements.insertBefore;
import static org.jboss.elemento.Elements.insertFirst;
import static org.jboss.elemento.Elements.resizeObserver;
import static org.patternfly.core.ObservableValue.ov;
import static org.patternfly.style.Classes.component;
import static org.patternfly.style.Classes.modifier;
import static org.patternfly.style.Classes.page;

/**
 * The page component is used to define the basic layout of a page with either vertical or horizontal navigation.
 * 

* {@snippet class = PageDemo region = page} * * @see https://www.patternfly.org/components/page */ public class Page extends BaseComponent implements Attachable { // ------------------------------------------------------ factory private static Page instance; /** Create or returns the page singleton. */ public static Page page() { return page(false); } public static Page page(boolean newInstance) { if (newInstance) { return new Page(); } else { if (instance == null) { instance = new Page(); } return instance; } } // ------------------------------------------------------ instance private final ObservableValue rect; private final List> resizeHandler; private SkipToContent skipToContent; private Masthead masthead; private PageSidebar sidebar; private PageMain main; private ResizeObserverCleanup cleanup; private Function breakpointFn; private Function verticalBreakpointFn; protected Page() { super(ComponentType.Page, div().css(component(page)).element()); this.rect = ov(new Rect()).subscribe(this::onChangedRect); this.resizeHandler = new ArrayList<>(); Attachable.register(this, this); } @Override public void attach(MutationRecord mutationRecord) { Callback resizeCallback = Scheduler.debounce(250, this::onResize); cleanup = resizeObserver(element(), () -> { resizeHandler.forEach(rh -> rh.onResize(this)); resizeCallback.call(); }); onResize(); } @Override public void detach(MutationRecord mutationRecord) { if (cleanup != null) { cleanup.cleanup(); } } // ------------------------------------------------------ add /** Adds the {@link SkipToContent} component as first element and removes the previous one (if any). */ public Page addSkipToContent(SkipToContent skipToContent) { return add(skipToContent); } /** Adds the {@link SkipToContent} component as first element and removes the previous one (if any). */ public Page add(SkipToContent skipToContent) { failSafeRemoveFromParent(this.skipToContent); this.skipToContent = skipToContent; insertFirst(element(), this.skipToContent); return this; } /** Adds the {@link Masthead} component and removes the previous one (if any). */ public Page addMasthead(Masthead masthead) { return add(masthead); } /** Adds the {@link Masthead} component and removes the previous one (if any). */ public Page add(Masthead masthead) { failSafeRemoveFromParent(this.masthead); this.masthead = masthead; if (skipToContent != null) { insertAfter(this.masthead, skipToContent.element()); } else { insertFirst(element(), this.masthead); } return this; } /** Adds the {@link PageSidebar} component and removes the previous one (if any). */ public Page addSidebar(PageSidebar sidebar) { return add(sidebar); } /** Adds the {@link PageSidebar} component and removes the previous one (if any). */ public Page add(PageSidebar sidebar) { failSafeRemoveFromParent(this.sidebar); this.sidebar = sidebar; if (main != null) { insertBefore(this.sidebar, main.element()); } else { add(sidebar.element()); } return this; } /** Adds the {@link PageMain} component and removes the previous one (if any). */ public Page addMain(PageMain main) { return add(main); } /** Adds the {@link PageMain} component and removes the previous one (if any). */ public Page add(PageMain main) { failSafeRemoveFromParent(this.main); this.main = main; return add(main.element()); } // ------------------------------------------------------ builder /** * The page resize observer uses the breakpoints returned from this function when adding the * {@code pf-m-breakpoint-[default|sm|md|lg|xl|2xl]} class. You can override the default function * {@link Breakpoint#breakpoint(int)} to return breakpoints at different sizes than the default. */ public Page breakpoint(Function breakpointFn) { this.breakpointFn = breakpointFn; return this; } /** * The page resize observer uses the breakpoints returned from this function when adding the * {@code pf-m-height-breakpoint-[default|sm|md|lg|xl|2xl]} class. You can override the default function * {@link Breakpoint#verticalBreakpoint(int)} to return breakpoints at different sizes than the default. */ public Page verticalBreakpoint(Function verticalBreakpointFn) { this.verticalBreakpointFn = verticalBreakpointFn; return this; } @Override public Page that() { return this; } // ------------------------------------------------------ events public Page onResize(ResizeHandler resizeHandler) { this.resizeHandler.add(resizeHandler); return this; } // ------------------------------------------------------ api /** * Returns the current {@link Masthead} or {@code null} if no masthead has been defined yet. */ public Masthead masthead() { return masthead; } /** * Returns the current {@link PageSidebar} or {@code null} if no sidebar has been defined yet. */ public PageSidebar sidebar() { return sidebar; } /** * Returns the current {@link PageMain} or {@code null} if no main has been defined yet. */ @SuppressWarnings("ConfusingMainMethod") public PageMain main() { return main; } // ------------------------------------------------------ internal boolean underXl() { return element().clientWidth < Breakpoint.xl.widthValue; } private void onResize() { int width = element().clientWidth; int height = element().clientHeight; rect.set(new Rect(width, height)); // triggers onChangedRect() } private void onChangedRect(Rect current, Rect previous) { // if it's already present, it won't be added again css(modifier(Classes.resizeObserver)); Breakpoint previousBreakpoint = breakpointFn != null ? breakpointFn.apply(previous.width) : Breakpoint.breakpoint(previous.width); Breakpoint currentBreakpoint = breakpointFn != null ? breakpointFn.apply(current.width) : Breakpoint.breakpoint(current.width); if (previousBreakpoint != currentBreakpoint) { classList().remove(modifier("breakpoint-" + previousBreakpoint.value)); classList().add(modifier("breakpoint-" + currentBreakpoint.value)); } Breakpoint previousVerticalBreakpoint = breakpointFn != null ? verticalBreakpointFn.apply(previous.height) : Breakpoint.verticalBreakpoint(previous.height); Breakpoint currentVerticalBreakpoint = breakpointFn != null ? verticalBreakpointFn.apply(current.height) : Breakpoint.verticalBreakpoint(current.height); if (previousVerticalBreakpoint != currentVerticalBreakpoint) { classList().remove(modifier("height-breakpoint-" + previousVerticalBreakpoint.value)); classList().add(modifier("height-breakpoint-" + currentVerticalBreakpoint.value)); } // resize (sub) components if (masthead != null) { masthead.onPageResize(current, previous); } if (sidebar != null) { if (underXl()) { sidebar.collapse(); } else { sidebar.expand(); } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy