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

org.bklab.flow.components.navigation.bar.AppBar Maven / Gradle / Ivy

There is a newer version: 22.0.1
Show newest version
/*
 * Copyright (c) 2008 - 2020. - Broderick Labs.
 * Author: Broderick Johansson
 * E-mail: [email protected]
 * Modify date:2020-06-24 15:16:28
 * _____________________________
 * Project name: fluent-vaadin-flow
 * Class name:org.bklab.flow.components.navigation.bar.AppBar
 * Copyright (c) 2008 - 2020. - Broderick Labs.
 */

package org.bklab.flow.components.navigation.bar;

import com.vaadin.flow.component.*;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.contextmenu.ContextMenu;
import com.vaadin.flow.component.dependency.CssImport;
import com.vaadin.flow.component.html.H1;
import com.vaadin.flow.component.html.Header;
import com.vaadin.flow.component.html.Image;
import com.vaadin.flow.component.html.Span;
import com.vaadin.flow.component.icon.Icon;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.orderedlayout.FlexComponent;
import com.vaadin.flow.component.tabs.Tab;
import com.vaadin.flow.component.tabs.Tabs;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.router.HasUrlParameter;
import com.vaadin.flow.shared.Registration;
import com.vaadin.flow.theme.lumo.Lumo;
import org.bklab.flow.components.navigation.tab.NaviTab;
import org.bklab.flow.components.navigation.tab.NaviTabs;
import org.bklab.flow.factory.ButtonFactory;
import org.bklab.flow.image.ImageBase;
import org.bklab.flow.layout.FlexBoxLayout;
import org.bklab.flow.util.lumo.LumoStyles;
import org.bklab.flow.util.lumo.UIUtils;
import org.bklab.flow.util.url.QueryParameterBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

@CssImport("./styles/components/app-bar.css")
public class AppBar extends Header {

	private final String CLASS_NAME = "app-bar";
	private static final Logger logger = LoggerFactory.getLogger(AppBar.class);

	private FlexBoxLayout container;

	private Button menuIcon;
	private Button contextIcon;

	private H1 title;
	private FlexBoxLayout leftActionItems;
	private FlexBoxLayout middleActionItems;
	private FlexBoxLayout actionItems;
	private Image avatar;

	private FlexBoxLayout tabContainer;
	private NaviTabs tabs;
	private ArrayList tabSelectionListeners;
	private Button addTab;
	private final List registrations = new ArrayList<>();
	private Span avatarName;
	private Button logout;

	private TextField search;
	private Registration searchRegistration;

	public AppBar(String title, NaviTab... tabs) {
		setClassName(CLASS_NAME);

		initMenuIcon();
		initContextIcon();
		initTitle(title);
		initSearch();
		initAvatar();
		initActionItems();
		initContainer();
		initTabs(tabs);
	}

	public AppBar(String title, Class defaultNavigate, NaviTab... tabs) {
		setClassName(CLASS_NAME);

		initMenuIcon();
		initContextIcon();
		initTitle(title);
		initSearch();
		initAvatar();
		initActionItems();
		initContainer();
		initTabs(defaultNavigate, tabs);
	}

	public AppBar lightTheme() {
		UIUtils.setTheme(Lumo.LIGHT, this);
		return this;
	}

	public AppBar darkTheme() {
		UIUtils.setTheme(Lumo.DARK, this);
		return this;
	}

	public void setNaviMode(NaviMode mode) {
		if (mode.equals(NaviMode.MENU)) {
			menuIcon.setVisible(true);
			contextIcon.setVisible(false);
		} else {
			menuIcon.setVisible(false);
			contextIcon.setVisible(true);
		}
	}

	private void initMenuIcon() {
		menuIcon = UIUtils.createTertiaryInlineButton(VaadinIcon.MENU);
		menuIcon.addClassName(CLASS_NAME + "__navi-icon");

		UIUtils.setAriaLabel("Menu", menuIcon);
		UIUtils.setLineHeight("1", menuIcon);
	}

	public AppBar ifMenuIconClick(ComponentEventListener> listener) {
		menuIcon.addClickListener(listener);
		return this;
	}

	private void initTitle(String title) {
		this.title = new H1(title);
		this.title.setClassName(CLASS_NAME + "__title");
	}

	private void initSearch() {
		search = new TextField();
		search.setPlaceholder("Search");
		search.setPrefixComponent(new Icon(VaadinIcon.SEARCH));
		search.setVisible(false);
	}

	private ContextMenu userIconMenu;

	private void initContextIcon() {
		contextIcon = UIUtils
				.createTertiaryInlineButton(VaadinIcon.ARROW_LEFT);
		contextIcon.addClassNames(CLASS_NAME + "__context-icon");
		contextIcon.setVisible(false);
		UIUtils.setAriaLabel("返回", contextIcon);
		UIUtils.setLineHeight("1", contextIcon);
	}

	public ContextMenu getUserIconMenu() {
		return userIconMenu;
	}

	private void initActionItems() {
		actionItems = new FlexBoxLayout();
		actionItems.addClassName(CLASS_NAME + "__action-items");
		actionItems.setVisible(false);
		leftActionItems = new FlexBoxLayout();
		leftActionItems.addClassName(CLASS_NAME + "__left-action-items");
		leftActionItems.setVisible(false);
		middleActionItems = new FlexBoxLayout();
		middleActionItems.addClassName(CLASS_NAME + "__middle-action-items");
	}

	private void initContainer() {
		container = new FlexBoxLayout(menuIcon, contextIcon, this.title,
				leftActionItems, middleActionItems, search, actionItems, avatarName, avatar, logout);
		container.addClassName(CLASS_NAME + "__container");
		container.setAlignItems(FlexComponent.Alignment.CENTER);
		container.setFlexGrow(1, search);
		add(container);
	}

	private void initTabs(NaviTab... tabs) {
		initTabs(null, tabs);
	}

	private void initTabs(Class defaultNavigate, NaviTab... tabs) {
		addTab = UIUtils.createSmallButton(VaadinIcon.PLUS);
		if (defaultNavigate != null) {
			addTab.addClickListener(e -> this.tabs.setSelectedTab(addClosableNaviTab("New Tab", defaultNavigate)));
		}
		addTab.setVisible(false);

		this.tabs = tabs.length > 0 ? new NaviTabs(tabs) : new NaviTabs();
		this.tabs.setClassName(CLASS_NAME + "__tabs");
		this.tabs.setVisible(false);
		for (NaviTab tab : tabs) {
			configureTab(tab);
		}

		this.tabSelectionListeners = new ArrayList<>();

		tabContainer = new FlexBoxLayout(this.tabs, addTab);
		tabContainer.addClassName(CLASS_NAME + "__tab-container");
		tabContainer.setAlignItems(FlexComponent.Alignment.CENTER);
		add(tabContainer);
	}

	public Button getMenuIcon() {
		return menuIcon;
	}

	/* === MENU ICON === */

	public Button getContextIcon() {
		return contextIcon;
	}

	/* === CONTEXT ICON === */

	public void setContextIcon(Icon icon) {
		contextIcon.setIcon(icon);
	}

	public Optional getTitle() {
		return Optional.ofNullable(this.title.getText());
	}

	/* === TITLE === */

	public void setTitle(String title) {
		this.title.setText(title);
	}

	public Component addActionItem(Component component) {
		actionItems.add(component);
		updateActionItemsVisibility();
		return component;
	}

	public Component addLeftActionItem(Component component) {
		leftActionItems.add(component);
		updateActionItemsVisibility();
		return component;
	}

	public Component addMiddleActionItem(Component component) {
		middleActionItems.add(component);
		updateActionItemsVisibility();
		return component;
	}

	/* === ACTION ITEMS === */

	public Button addActionItem(VaadinIcon icon) {
		Button button = UIUtils.createButton(icon, ButtonVariant.LUMO_SMALL,
				ButtonVariant.LUMO_TERTIARY);
		addActionItem(button);
		return button;
	}

	public Button addLeftActionItem(VaadinIcon icon) {
		Button button = UIUtils.createButton(icon, ButtonVariant.LUMO_SMALL,
				ButtonVariant.LUMO_TERTIARY);
		addLeftActionItem(button);
		return button;
	}

	public Button addMiddleActionItem(VaadinIcon icon) {
		Button button = UIUtils.createButton(icon, ButtonVariant.LUMO_SMALL,
				ButtonVariant.LUMO_TERTIARY);
		addMiddleActionItem(button);
		return button;
	}

	public void removeAllActionItems() {
		leftActionItems.removeAll();
		middleActionItems.removeAll();
		actionItems.removeAll();
		updateActionItemsVisibility();
	}

	public Image getAvatar() {
		return avatar;
	}

	/* === AVATAR == */

	public void centerTabs() {
		tabs.addClassName(LumoStyles.Margin.Horizontal.AUTO);
	}

	/* === TABS === */

	private void configureTab(Tab tab) {
		tab.addClassName(CLASS_NAME + "__tab");
		updateTabsVisibility();
	}

	public Tab addTab(String text) {
		Tab tab = tabs.addTab(text);
		configureTab(tab);
		return tab;
	}

	public Tab addTab(String text,
					  Class navigationTarget) {
		Tab tab = tabs.addTab(text, navigationTarget);
		configureTab(tab);
		return tab;
	}

	public Tab addClosableNaviTab(String text,
								  Class navigationTarget) {
		Tab tab = tabs.addClosableTab(text, navigationTarget);
		configureTab(tab);
		return tab;
	}

	public Tab getSelectedTab() {
		return tabs.getSelectedTab();
	}

	public void setSelectedTab(Tab selectedTab) {
		tabs.setSelectedTab(selectedTab);
	}

	public void updateSelectedTab(String text,
								  Class navigationTarget) {
		tabs.updateSelectedTab(text, navigationTarget);
	}

	public void navigateToSelectedTab() {
		tabs.navigateToSelectedTab();
	}

	public void addTabSelectionListener(
			ComponentEventListener listener) {
		Registration registration = tabs.addSelectedChangeListener(listener);
		tabSelectionListeners.add(registration);
	}

	public int getTabCount() {
		return tabs.getTabCount();
	}

	public void removeAllTabs() {
		tabSelectionListeners.forEach(Registration::remove);
		tabSelectionListeners.clear();
		tabs.removeAll();
		updateTabsVisibility();
	}

	public void setAddTabVisible(boolean visible) {
		addTab.setVisible(visible);
	}

	/* === ADD TAB BUTTON === */

	public void searchModeOn() {
		menuIcon.setVisible(false);
		title.setVisible(false);
		actionItems.setVisible(false);
		tabContainer.setVisible(false);

		contextIcon.setIcon(new Icon(VaadinIcon.ARROW_LEFT));
		contextIcon.setVisible(true);
		searchRegistration = contextIcon
				.addClickListener(e -> searchModeOff());

		search.setVisible(true);
		search.focus();
	}

	/* === SEARCH === */

	public void addSearchListener(HasValue.ValueChangeListener> listener) {
		search.addValueChangeListener(listener);
	}

	public void setSearchPlaceholder(String placeholder) {
		search.setPlaceholder(placeholder);
	}

	private void searchModeOff() {
		menuIcon.setVisible(true);
		title.setVisible(true);
		tabContainer.setVisible(true);

		updateActionItemsVisibility();
		updateTabsVisibility();

		contextIcon.setVisible(false);
		searchRegistration.remove();

		search.clear();
		search.setVisible(false);
	}

	private void initAvatar() {
		avatar = ImageBase.getImage("default-user-icon.svg");
		avatar.setClassName(CLASS_NAME + "__avatar");
		avatar.setAlt("用户菜单");


		avatarName = new Span();
		avatarName.addClassName(CLASS_NAME + "__avatar-name");

		logout = new ButtonFactory()
				.className(CLASS_NAME + "__logout")
				.icon(VaadinIcon.SIGN_OUT).visible(false)
				.lumoLarge().lumoTertiary().attributeTitle("退出登录").get();
		logout.getStyle().set("color", "var(--lumo-contrast-60pct)");

		this.userIconMenu = new ContextMenu(avatar);
		userIconMenu.setOpenOnClick(true);
	}

	public AppBar avatarName(String name) {
		avatarName.setText(name);
		return this;
	}

	public AppBar logout(ComponentEventListener> eventListener) {
		logout.addClickListener(eventListener);
		logout.setVisible(true);
		return this;
	}

	public Span getAvatarName() {
		return avatarName;
	}

	public Button getLogout() {
		return logout;
	}

	public void setContextIconAsBack() {
		contextIcon.setVisible(true);
		registrations.add(contextIcon.addClickListener(e -> UI.getCurrent().getPage().getHistory().back()));
	}

	public void setContextIconAsBack(Class navigationTarget) {
		contextIcon.setVisible(true);
		registrations.add(contextIcon.addClickListener(e -> UI.getCurrent().navigate(navigationTarget)));
	}

	public void setContextIconAsBackward(Component component, Class navigationTarget) {
		contextIcon.setVisible(true);
		Registration registration = contextIcon.addClickListener(e -> UI.getCurrent().navigate(navigationTarget));
		component.addDetachListener(e -> {
			reset();
			registration.remove();
		});
	}

	public void setContextIconAsBackward(Component component, Class navigationTarget, QueryParameterBuilder builder) {
		contextIcon.setVisible(true);
		Registration registration = contextIcon.addClickListener(e -> {
			UI current = UI.getCurrent();
			current.getRouter().getRegistry().getTargetUrl(navigationTarget).ifPresent(url -> current.navigate(url, builder.get()));
		});
		component.addDetachListener(e -> {
			reset();
			registration.remove();
		});
	}

	public void setContextIconAsBackward(Component component, String navigationTarget, QueryParameterBuilder builder) {
		contextIcon.setVisible(true);
		Registration registration = contextIcon.addClickListener(e -> UI.getCurrent().navigate(navigationTarget, builder.get()));
		component.addDetachListener(e -> {
			reset();
			registration.remove();
		});
	}

	public void setContextIconAsBackward(Component component, String navigationTarget) {
		contextIcon.setVisible(true);
		Registration registration = contextIcon.addClickListener(e -> UI.getCurrent().navigate(navigationTarget));
		component.addDetachListener(e -> {
			reset();
			registration.remove();
		});
	}

	public >
	void setContextIconAsBack(Class navigationTarget, T parameter) {
		contextIcon.setVisible(true);
		registrations.add(contextIcon.addClickListener(e -> UI.getCurrent().navigate(navigationTarget, parameter)));
	}

	public void reset() {
		title.setText("");
		setNaviMode(NaviMode.MENU);
		removeAllActionItems();
		removeAllTabs();
		registrations.forEach(Registration::remove);
	}

	/* === RESET === */

	private void updateActionItemsVisibility() {
		actionItems.setVisible(actionItems.getComponentCount() > 0);
		leftActionItems.setVisible(actionItems.getComponentCount() > 0);
	}

	/* === UPDATE VISIBILITY === */

	private void updateTabsVisibility() {
		tabs.setVisible(tabs.getComponentCount() > 0);
	}

	public FlexBoxLayout getContainer() {
		return container;
	}

	public enum NaviMode {
		MENU, CONTEXTUAL
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy