com.vaadin.flow.component.tabs.Tabs Maven / Gradle / Ivy
/*
* Copyright 2000-2017 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.tabs;
import java.io.Serializable;
import java.util.Locale;
import java.util.Objects;
import com.vaadin.flow.component.ClientCallable;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.ComponentEvent;
import com.vaadin.flow.component.ComponentEventListener;
import com.vaadin.flow.component.DomEvent;
import com.vaadin.flow.component.HasOrderedComponents;
import com.vaadin.flow.component.HasSize;
import com.vaadin.flow.component.Synchronize;
import com.vaadin.flow.shared.Registration;
/**
* Server-side component for the {@code vaadin-tabs} element.
*
* @author Vaadin Ltd.
*/
public class Tabs extends GeneratedVaadinTabs
implements HasOrderedComponents, HasSize {
private static final String SELECTED = "selected";
private transient Tab selectedTab;
/**
* The valid orientations of {@link Tabs} instances.
*/
public enum Orientation {
HORIZONTAL, VERTICAL
}
/**
* Constructs an empty new object with {@link Orientation#HORIZONTAL
* HORIZONTAL} orientation.
*/
public Tabs() {
getElement().addPropertyChangeListener(SELECTED,
event -> updateSelectedTab());
}
/**
* Constructs a new object enclosing the given tabs, with
* {@link Orientation#HORIZONTAL HORIZONTAL} orientation.
*
* @param tabs
* the tabs to enclose
*/
public Tabs(Tab... tabs) {
this();
add(tabs);
}
/**
* Adds the given tabs to the component.
*
* @param tabs
* the tabs to enclose
*/
public void add(Tab... tabs) {
HasOrderedComponents.super.add(tabs);
}
/**
* An event to mark that the selected tab has changed.
*/
@DomEvent("selected-changed")
public static class SelectedChangeEvent extends ComponentEvent {
public SelectedChangeEvent(Tabs source, boolean fromClient) {
super(source, fromClient);
}
}
/**
* Adds a listener for {@link SelectedChangeEvent}.
*
* NOTE: This method adds a listener for {@link SelectedChangeEvent}
* which is a {@link DomEvent}. It means in particular that the listener
* will be notified ONLY when event is fired from the
* client-side. Even though the server-side may be the originator of the
* event (via selection methods) the even will be fired only after the
* client-side round-trip.
*
* It means that you should not expect the event fired immediately when you
* call e.g. {@link #setSelectedTab(Tab)} on the server-side. If you want to
* get an event immediately on the server side then you should add a
* property change listener for {@literal "selected"} property:
*
*
*
* Tabs tab =...;
* tabs.getElement().addPropertyChangeListener("selected", event->{});
*
*
*
* @param listener
* the listener to add, not null
* @return a handle that can be used for removing the listener
*/
public Registration addSelectedChangeListener(
ComponentEventListener listener) {
return addListener(SelectedChangeEvent.class, listener);
}
/**
* Gets the zero-based index of the currently selected tab.
*
* @return the zero-based index of the selected tab
*/
@Synchronize(property = SELECTED, value = "selected-changed")
public int getSelectedIndex() {
return getElement().getProperty(SELECTED, 0);
}
/**
* Selects a tab based on its zero-based index.
*
* @param selectedIndex
* the zero-based index of the selected tab
*/
public void setSelectedIndex(int selectedIndex) {
getElement().setProperty(SELECTED, selectedIndex);
}
/**
* Gets the currently selected tab.
*
* @return the selected tab
*/
public Tab getSelectedTab() {
int selectedIndex = getSelectedIndex();
Component selectedComponent = getComponentAt(selectedIndex);
if (!(selectedComponent instanceof Tab)) {
throw new IllegalStateException(
"Illegal component inside Tabs: " + selectedComponent);
}
return (Tab) selectedComponent;
}
/**
* Selects the given tab.
*
* @param selectedTab
* the tab to select
* @throws IllegalArgumentException
* if {@code selectedTab} is not a child of this component
*/
public void setSelectedTab(Tab selectedTab) {
int selectedIndex = indexOf(selectedTab);
if (selectedIndex < 0) {
throw new IllegalArgumentException(
"Tab to select must be a child: " + selectedTab);
}
getElement().setProperty(SELECTED, selectedIndex);
}
/**
* Gets the orientation of this tab sheet.
*
* @return the orientation
*/
public Orientation getOrientation() {
String orientation = getElement().getProperty("orientation");
if (orientation != null) {
return Orientation.valueOf(orientation.toUpperCase(Locale.ROOT));
}
return Orientation.HORIZONTAL;
}
/**
* Sets the orientation of this tab sheet.
*
* @param orientation
* the orientation
*/
public void setOrientation(Orientation orientation) {
getElement().setProperty("orientation",
orientation.name().toLowerCase());
}
/**
* Sets the flex grow property of all enclosed tabs. The flex grow property
* specifies what amount of the available space inside the layout the
* component should take up, proportionally to the other components.
*
* For example, if all components have a flex grow property value set to 1,
* the remaining space in the layout will be distributed equally to all
* components inside the layout. If you set a flex grow property of one
* component to 2, that component will take twice the available space as the
* other components, and so on.
*
* Setting to flex grow property value 0 disables the expansion of the
* component. Negative values are not allowed.
*
* @param flexGrow
* the proportion of the available space the enclosed tabs should
* take up
* @throws IllegalArgumentException
* if {@code flexGrow} is negative
*/
public void setFlexGrowForEnclosedTabs(double flexGrow) {
if (flexGrow < 0) {
throw new IllegalArgumentException(
"Flex grow property must not be negative");
}
getChildren().forEach(tab -> ((Tab) tab).setFlexGrow(flexGrow));
}
@ClientCallable
private void itemsChanged() {
Tab currentlySelected = getSelectedTab();
if (!Objects.equals(currentlySelected, selectedTab)) {
selectedTab = currentlySelected;
}
}
private void updateSelectedTab() {
Tab selected = getSelectedTab();
if (selected.isEnabled()) {
selectedTab = selected;
getChildren().filter(Tab.class::isInstance).map(Tab.class::cast)
.forEach(tab -> tab.setSelected(false));
selectedTab.setSelected(true);
} else {
updateEnabled(selected);
setSelectedTab(selectedTab);
}
}
private void updateEnabled(Tab tab) {
boolean enabled = tab.isEnabled();
Serializable rawValue = tab.getElement().getPropertyRaw("disabled");
if (rawValue instanceof Boolean) {
// convert the boolean value to a String to force update the
// property value. Otherwise since the provided value is the same as
// the current one the update don't do anything.
tab.getElement().setProperty("disabled",
enabled ? null : Boolean.TRUE.toString());
} else {
tab.setEnabled(enabled);
}
}
}