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

com.vaadin.flow.component.applayout.AppLayout Maven / Gradle / Ivy

There is a newer version: 24.6.0
Show newest version
/*
 * Copyright 2000-2024 Vaadin Ltd.
 *
 * 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 com.vaadin.flow.component.applayout;

import java.io.Serializable;
import java.util.Locale;
import java.util.Objects;

import com.vaadin.flow.component.AttachEvent;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.HasElement;
import com.vaadin.flow.component.HasStyle;
import com.vaadin.flow.component.PropertyDescriptor;
import com.vaadin.flow.component.PropertyDescriptors;
import com.vaadin.flow.component.Synchronize;
import com.vaadin.flow.component.Tag;
import com.vaadin.flow.component.UI;
import com.vaadin.flow.component.dependency.JsModule;
import com.vaadin.flow.component.dependency.NpmPackage;
import com.vaadin.flow.function.SerializableConsumer;
import com.vaadin.flow.internal.JsonSerializer;
import com.vaadin.flow.router.RouterLayout;

import elemental.json.JsonObject;
import elemental.json.JsonType;

/**
 * App Layout is a component for building common application layouts.
 * 

* The layout consists of three sections: a horizontal navigation bar (navbar), * a collapsible navigation drawer (drawer) and a content area. An application’s * main navigation blocks should be positioned in the navbar and/or drawer while * views are rendered in the content area. *

* App Layout is responsive and adjusts automatically to fit desktop, tablet, * and mobile screen sizes. * * @author Vaadin Ltd */ @Tag("vaadin-app-layout") @NpmPackage(value = "@vaadin/polymer-legacy-adapter", version = "23.5.7") @JsModule("@vaadin/polymer-legacy-adapter/style-modules.js") @NpmPackage(value = "@vaadin/app-layout", version = "23.5.7") @NpmPackage(value = "@vaadin/vaadin-app-layout", version = "23.5.7") @JsModule("@vaadin/app-layout/src/vaadin-app-layout.js") public class AppLayout extends Component implements RouterLayout, HasStyle { private static final PropertyDescriptor primarySectionProperty = PropertyDescriptors .propertyWithDefault("primarySection", Section.NAVBAR.toWebcomponentValue()); private static final PropertyDescriptor overlayProperty = PropertyDescriptors .propertyWithDefault("overlay", false); private Component content; private AppLayoutI18n i18n; /** * Gets the internationalization object previously set for this component. *

* Note: updating the i18n object that is returned from this method will not * update the the component, unless it is set again using * {@link AppLayout#setI18n(AppLayoutI18n)} * * @return the i18n object. It will be null, if the i18n * properties are not set. */ public AppLayoutI18n getI18n() { return i18n; } /** * Sets the internationalization properties for this component. * * @param i18n * the internationalized properties, not null */ public void setI18n(AppLayoutI18n i18n) { Objects.requireNonNull(i18n, "The I18N properties object should not be null"); this.i18n = i18n; runBeforeClientResponse(ui -> { if (i18n == this.i18n) { setI18nWithJS(); } }); } private void setI18nWithJS() { JsonObject i18nJson = (JsonObject) JsonSerializer.toJson(this.i18n); // Remove properties with null values to prevent errors in web // component removeNullValuesFromJsonObject(i18nJson); // Assign new I18N object to WC, by merging the existing // WC I18N, and the values from the new AppLayoutI18n instance, // into an empty object getElement().executeJs("this.i18n = Object.assign({}, this.i18n, $0);", i18nJson); } @Override protected void onAttach(AttachEvent attachEvent) { super.onAttach(attachEvent); // Element state is not persisted across attach/detach if (this.i18n != null) { setI18nWithJS(); } } private void removeNullValuesFromJsonObject(JsonObject jsonObject) { for (String key : jsonObject.keys()) { if (jsonObject.get(key).getType() == JsonType.NULL) { jsonObject.remove(key); } } } private void runBeforeClientResponse(SerializableConsumer command) { getElement().getNode().runWhenAttached(ui -> ui .beforeClientResponse(this, context -> command.accept(ui))); } /** * @see #setPrimarySection(Section) * @return value for the primarySection property. Default is * {@link Section#NAVBAR}. */ @Synchronize("primary-section-changed") public Section getPrimarySection() { return Section.fromWebcomponentValue(primarySectionProperty.get(this)); } /** * Defines whether navbar or drawer will come first visually. * *

    *
  • If {@link Section#NAVBAR}, the navbar takes the full available width * and moves the drawer down. This is the default.
  • *
  • If {@link Section#DRAWER} is set, then the drawer will move the * navbar, taking the full available height.
  • *
* * @param primarySection * new value for the primarySection property. Not {@code null}. * @throws NullPointerException * if primarySection is {@code null}. */ public void setPrimarySection(Section primarySection) { Objects.requireNonNull(primarySection, "primary section must not be null"); primarySectionProperty.set(this, primarySection.toWebcomponentValue()); } /** * Whether the drawer is opened (visible) or not. Its default value depends * on the viewport: *
    *
  • {@code true} for desktop size views
  • *
  • {@code false} for mobile size views
  • *
* * @return {@code true} if the drawer is opened (visible). {@code false} * otherwise. */ @Synchronize("drawer-opened-changed") public boolean isDrawerOpened() { return getElement().getProperty("drawerOpened", true); } /** * Server-side API for showing and hiding the drawer. * * @param drawerOpened * new value for the drawerOpened property. * @see #isDrawerOpened * @see DrawerToggle for a component that allows the user to open and close * the drawer. */ public void setDrawerOpened(boolean drawerOpened) { getElement().setProperty("drawerOpened", drawerOpened); } /** * Note: This property is controlled via CSS and can not be * changed directly. * * @return {@code true} if drawer is an overlay on top of the content. * {@code false} otherwise. */ @Synchronize("overlay-changed") public boolean isOverlay() { return overlayProperty.get(this); } /** * @return the displayed content */ public Component getContent() { return content; } /** * Sets the displayed content. * * @param content * {@link Component} to display in the content area */ public void setContent(Component content) { removeContent(); if (content != null) { this.content = content; content.getElement().removeAttribute("slot"); add(content); } } /** * Adds the components to the drawer slot of this AppLayout. * * @param components * Components to add to the drawer slot. * @throws NullPointerException * if any of the components is null or if the components array * is null. */ public void addToDrawer(Component... components) { addToSlot("drawer", components); } /** * Adds the components to the navbar slot of this AppLayout. * * @param components * Components to add to the navbar slot. * @throws NullPointerException * if any of the components is null or if the components array * is null. */ public void addToNavbar(Component... components) { final boolean touchOptimized = false; addToNavbar(touchOptimized, components); } /** * Adds the components to the navbar slot of this AppLayout. * * @param touchOptimized * if true, the components will be moved to the bottom navbar * area on mobile devices. * @param components * Components to add to the navbar slot. * @throws NullPointerException * if any of the components is null or if the components array * is null. */ public void addToNavbar(boolean touchOptimized, Component... components) { final String slot = "navbar" + (touchOptimized ? " touch-optimized" : ""); addToSlot(slot, components); } /** * Removes the child components from the parent. Components can be in any * slot or be the main content. * * @param components * Components to remove. */ public void remove(Component... components) { for (Component component : components) { if (this.content != null && this.content.equals(component)) { this.content = null; } remove(component); } } /** * {@inheritDoc} * * @throws IllegalArgumentException * if content is not a {@link Component} */ @Override public void showRouterLayoutContent(HasElement content) { Component target = null; if (content != null) { target = content.getElement().getComponent() .orElseThrow(() -> new IllegalArgumentException( "AppLayout content must be a Component")); } setContent(target); afterNavigation(); } /** * Called after a navigation event. The default behaviour is to close the * drawer on mobile devices after a navigation event. */ protected void afterNavigation() { // Close drawer after navigation on mobile devices. if (isOverlay()) { setDrawerOpened(false); } } private void addToSlot(String slot, Component... components) { for (Component component : components) { setSlot(component, slot); add(component); } } private void add(Component component) { getElement().appendChild(component.getElement()); } private static void setSlot(Component component, String slot) { component.getElement().setAttribute("slot", slot); } /** * Removes the displayed content. */ private void removeContent() { remove(this.content); this.content = null; } private void remove(Component component) { if (component != null) { component.getElement().removeFromParent(); } } /** * Sections in the component that can be used as primary. * * @see #setPrimarySection(Section) */ public enum Section { NAVBAR, DRAWER; public String toWebcomponentValue() { return this.name().toLowerCase(Locale.ENGLISH); } public static Section fromWebcomponentValue(String webcomponentValue) { return webcomponentValue != null ? valueOf(webcomponentValue.toUpperCase()) : null; } } /** * The internationalization properties for {@link AppLayout} */ public static class AppLayoutI18n implements Serializable { private String drawer; /** * Gets the text for the `aria-label` attribute on the drawer. * * @return the drawer aria-label */ public String getDrawer() { return drawer; } /** * Sets the text for the `aria-label` attribute on the drawer. The * attribute is set when the drawer is in the overlay mode and announced * once the drawer is opened. * * @param drawer * the drawer aria-label * @return this instance for method chaining */ public AppLayoutI18n setDrawer(String drawer) { this.drawer = drawer; return this; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy