PL-8605 SideMenu responsive styles

This commit is contained in:
Yuriy Artamonov 2017-02-14 18:07:32 +04:00
parent e13b4c343e
commit 96c5ce3396
26 changed files with 622 additions and 110 deletions

View File

@ -16,6 +16,7 @@
package com.haulmont.cuba.gui.components.mainwindow;
import com.haulmont.cuba.gui.components.Button;
import com.haulmont.cuba.gui.components.Component;
import javax.annotation.Nullable;
@ -34,6 +35,28 @@ public interface SideMenu extends Component.BelongToFrame {
*/
void loadMenuConfig();
/**
* Bind show/hide side panel action to button.
*
* @param button button that should trigger show/hide of side panel
*/
void setSidePanelToggleButton(Button button);
/**
* @return side panel toggle button
*/
Button getSidePanelToggleButton();
/**
* Bind side panel for show/hide action.
*
* @param sidePanel side panel
*/
void setSidePanel(Component sidePanel);
/**
* @return side panel
*/
Component getSidePanel();
/**
* @return true if an item becomes selected by click
*/

View File

@ -37,28 +37,28 @@ import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.MissingResourceException;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* GenericUI class holding information about the main menu structure.
*
*/
@Component(MenuConfig.NAME)
public class MenuConfig {
private final Logger log = LoggerFactory.getLogger(MenuConfig.class);
public static final String NAME = "cuba_MenuConfig";
public static final String MENU_CONFIG_XML_PROP = "cuba.menuConfig";
private static Logger log = LoggerFactory.getLogger(MenuConfig.class);
protected List<MenuItem> rootItems = new ArrayList<>();
@Inject
protected Resources resources;
@Inject
protected Messages messages;
protected volatile boolean initialized;
protected ReadWriteLock lock = new ReentrantReadWriteLock();
@ -66,14 +66,17 @@ public class MenuConfig {
/**
* Localized menu item caption.
* @param id screen ID as defined in <code>screens.xml</code>
*
* @deprecated Use {@link MenuConfig#getItemCaption(String)}
*/
@Deprecated
public static String getMenuItemCaption(String id) {
try {
Messages messages = AppBeans.get(Messages.NAME);
return messages.getMainMessage("menu-config." + id);
} catch (MissingResourceException e) {
return id;
}
MenuConfig menuConfig = AppBeans.get(MenuConfig.NAME);
return menuConfig.getItemCaption(id);
}
public String getItemCaption(String id) {
return messages.getMainMessage("menu-config." + id);
}
protected void checkInitialized() {
@ -172,6 +175,7 @@ public class MenuConfig {
loadIcon(element, menuItem);
loadShortcut(menuItem, element);
loadStylename(element, menuItem);
loadExpanded(element, menuItem);
loadDescription(element, menuItem);
loadMenuItems(element, menuItem);
@ -210,6 +214,13 @@ public class MenuConfig {
}
}
protected void loadExpanded(Element element, MenuItem menuItem) {
String expanded = element.attributeValue("expanded");
if (StringUtils.isNotEmpty(expanded)) {
menuItem.setExpanded(Boolean.parseBoolean(expanded));
}
}
protected void loadDescription(Element element, MenuItem menuItem) {
String description = element.attributeValue("description");
if (StringUtils.isNotBlank(description)) {

View File

@ -17,7 +17,6 @@
package com.haulmont.cuba.gui.config;
import com.haulmont.bali.util.Dom4j;
import com.haulmont.cuba.gui.components.Component;
import com.haulmont.cuba.gui.components.KeyCombination;
import com.haulmont.cuba.security.entity.PermissionType;
import com.haulmont.cuba.security.global.UserSession;
@ -29,7 +28,6 @@ import java.util.List;
/**
* Main menu item descriptor
*
*/
public class MenuItem {
@ -42,6 +40,7 @@ public class MenuItem {
private String description;
private Element descriptor;
private boolean separator = false;
private boolean expanded = false;
private KeyCombination shortcut;
private boolean isMenu = false;
@ -145,4 +144,12 @@ public class MenuItem {
public void setIcon(String icon) {
this.icon = icon;
}
}
public boolean isExpanded() {
return expanded;
}
public void setExpanded(boolean expanded) {
this.expanded = expanded;
}
}

View File

@ -55,6 +55,8 @@ import java.util.concurrent.CopyOnWriteArrayList;
@Component("cuba_PermissionConfig")
public class PermissionConfig {
private final Logger log = LoggerFactory.getLogger(PermissionConfig.class);
@Inject
protected DynamicAttributes dynamicAttributes;
@ -106,7 +108,7 @@ public class PermissionConfig {
private void walkMenu(MenuItem info, Node<BasicPermissionTarget> node) {
String id = info.getId();
String caption = MenuConfig.getMenuItemCaption(id).replaceAll("<.+?>", "").replaceAll("&gt;", "");
String caption = menuConfig.getItemCaption(id).replaceAll("<.+?>", "").replaceAll("&gt;", "");
caption = StringEscapeUtils.unescapeHtml(caption);
if (info.getChildren() != null && !info.getChildren().isEmpty()) {
@ -150,12 +152,12 @@ public class PermissionConfig {
Session session = metadata.getSession();
List<MetaModel> modelList = new ArrayList<>(session.getModels());
Collections.sort(modelList, new MetadataObjectAlphabetComparator());
modelList.sort(new MetadataObjectAlphabetComparator());
for (MetaModel model : modelList) {
List<MetaClass> classList = new ArrayList<>(model.getClasses());
Collections.sort(classList, new MetadataObjectAlphabetComparator());
classList.sort(new MetadataObjectAlphabetComparator());
for (MetaClass metaClass : classList) {
String name = metaClass.getName();
@ -188,7 +190,7 @@ public class PermissionConfig {
DynamicAttributesUtils.encodeAttributeCode(dynamicAttribute.getCode()));
propertyList.add(metaPropertyPath.getMetaProperty());
}
Collections.sort(propertyList, new MetadataObjectAlphabetComparator());
propertyList.sort(new MetadataObjectAlphabetComparator());
for (MetaProperty metaProperty : propertyList) {
String metaPropertyName = metaProperty.getName();
@ -300,8 +302,6 @@ public class PermissionConfig {
private List<Item> items = new CopyOnWriteArrayList<>();
private Logger log = LoggerFactory.getLogger(PermissionConfig.class);
public PermissionConfig() {
this.clientType = AppConfig.getClientType();
}

View File

@ -79,6 +79,8 @@
<xs:extension base="baseMainElement">
<xs:attribute name="selectOnClick" type="xs:boolean"/>
<xs:attribute name="loadMenuConfig" type="xs:boolean"/>
<xs:attribute name="sidePanel" type="xs:string"/>
<xs:attribute name="sidePanelToggleButton" type="xs:string"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>

View File

@ -17,10 +17,10 @@
-->
<xs:schema targetNamespace="http://schemas.haulmont.com/cuba/menu.xsd"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns="http://schemas.haulmont.com/cuba/menu.xsd"
elementFormDefault="qualified"
attributeFormDefault="unqualified">
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns="http://schemas.haulmont.com/cuba/menu.xsd"
elementFormDefault="qualified"
attributeFormDefault="unqualified">
<xs:element name="menu-config" type="menuOrItem"/>
@ -41,12 +41,13 @@
<xs:element name="separator"/>
</xs:choice>
</xs:sequence>
<xs:attribute type="xs:string" name="id" use="optional"/>
<xs:attribute type="xs:string" name="insertBefore" use="optional"/>
<xs:attribute type="xs:string" name="insertAfter" use="optional"/>
<xs:attribute type="xs:string" name="description" use="optional"/>
<xs:attribute type="xs:string" name="stylename" use="optional"/>
<xs:attribute type="xs:string" name="icon" use="optional"/>
<xs:attribute type="xs:string" name="id"/>
<xs:attribute type="xs:string" name="insertBefore"/>
<xs:attribute type="xs:string" name="insertAfter"/>
<xs:attribute type="xs:string" name="description"/>
<xs:attribute type="xs:string" name="stylename"/>
<xs:attribute type="xs:string" name="icon"/>
<xs:attribute type="xs:boolean" name="expanded"/>
</xs:complexType>
<xs:complexType name="itemType">
@ -54,15 +55,15 @@
<xs:element type="paramType" name="param" minOccurs="0" maxOccurs="unbounded"/>
<xs:element type="permissionsType" name="permissions" minOccurs="0" maxOccurs="1"/>
</xs:sequence>
<xs:attribute type="xs:string" name="id" use="optional"/>
<xs:attribute type="openTypeType" name="openType" use="optional"/>
<xs:attribute type="xs:boolean" name="resizable" use="optional"/>
<xs:attribute type="xs:string" name="shortcut" use="optional"/>
<xs:attribute type="xs:string" name="description" use="optional"/>
<xs:attribute type="xs:string" name="stylename" use="optional"/>
<xs:attribute type="xs:string" name="icon" use="optional"/>
<xs:attribute type="xs:string" name="insertBefore" use="optional"/>
<xs:attribute type="xs:string" name="insertAfter" use="optional"/>
<xs:attribute type="xs:string" name="id"/>
<xs:attribute type="openTypeType" name="openType"/>
<xs:attribute type="xs:boolean" name="resizable"/>
<xs:attribute type="xs:string" name="shortcut"/>
<xs:attribute type="xs:string" name="description"/>
<xs:attribute type="xs:string" name="stylename"/>
<xs:attribute type="xs:string" name="icon"/>
<xs:attribute type="xs:string" name="insertBefore"/>
<xs:attribute type="xs:string" name="insertAfter"/>
</xs:complexType>
<xs:complexType name="paramType">

View File

@ -450,8 +450,6 @@
<xs:enumeration value="card"/>
<xs:enumeration value="well"/>
<xs:enumeration value="v-panel-caption"/>
<xs:enumeration value="c-sidemenu-title"/>
<xs:enumeration value="c-sidemenu-panel"/>
</xs:restriction>
</xs:simpleType>
</xs:union>
@ -468,8 +466,6 @@
<xs:enumeration value="well"/>
<xs:enumeration value="v-panel-caption"/>
<xs:enumeration value="dropzone-container"/>
<xs:enumeration value="c-sidemenu-title"/>
<xs:enumeration value="c-sidemenu-panel"/>
</xs:restriction>
</xs:simpleType>
</xs:union>
@ -486,8 +482,6 @@
<xs:enumeration value="well"/>
<xs:enumeration value="v-component-group"/>
<xs:enumeration value="v-panel-caption"/>
<xs:enumeration value="c-sidemenu-title"/>
<xs:enumeration value="c-sidemenu-panel"/>
</xs:restriction>
</xs:simpleType>
</xs:union>
@ -2524,6 +2518,7 @@
<xs:attributeGroup ref="hasSize"/>
<xs:attributeGroup ref="hasStyle"/>
<xs:attributeGroup ref="hasSpacingMargin"/>
<xs:attributeGroup ref="hasResponsive"/>
</xs:complexType>
</xs:element>

View File

@ -95,6 +95,7 @@ public class FrameComponentLoader extends ContainerLoader<Frame> {
loadVisible(resultComponent, element);
loadStyleName(resultComponent, element);
loadResponsive(resultComponent, element);
loadAlign(resultComponent, element);

View File

@ -160,6 +160,7 @@ public class FrameLoader<T extends Frame> extends ContainerLoader<T> {
loadWidth(resultComponent, layoutElement);
loadHeight(resultComponent, layoutElement);
loadStyleName(resultComponent, layoutElement);
loadResponsive(resultComponent, layoutElement);
ComponentLoaderContext parentContext = (ComponentLoaderContext) getContext();
setContext(innerContext);

View File

@ -16,6 +16,9 @@
package com.haulmont.cuba.gui.xml.layout.loaders;
import com.haulmont.cuba.gui.GuiDevelopmentException;
import com.haulmont.cuba.gui.components.Button;
import com.haulmont.cuba.gui.components.Component;
import com.haulmont.cuba.gui.components.mainwindow.SideMenu;
import org.apache.commons.lang.StringUtils;
import org.dom4j.Element;
@ -42,8 +45,10 @@ public class SideMenuLoader extends AbstractComponentLoader<SideMenu> {
loadSelectOnClick(resultComponent, element);
loadMenuConfigIfNeeded(resultComponent, element);
}
loadSidePanel(resultComponent, element);
loadSidePanelToggleButton(resultComponent, element);
}
protected void loadMenuConfigIfNeeded(SideMenu component, Element element) {
String loadMenuConfig = element.attributeValue("loadMenuConfig");
if (StringUtils.isEmpty(loadMenuConfig) || Boolean.parseBoolean(loadMenuConfig)) {
@ -57,4 +62,28 @@ public class SideMenuLoader extends AbstractComponentLoader<SideMenu> {
component.setSelectOnClick(Boolean.parseBoolean(selectOnClick));
}
}
protected void loadSidePanel(SideMenu component, Element element) {
String sidePanelId = element.attributeValue("sidePanel");
if (StringUtils.isNotEmpty(sidePanelId)) {
Component sidePanel = resultComponent.getFrame().getComponent(sidePanelId);
if (sidePanel == null) {
throw new GuiDevelopmentException("Unable to find sidePanel component for SideMenu",
context.getFullFrameId(), "sidePanel", sidePanelId);
}
component.setSidePanel(sidePanel);
}
}
protected void loadSidePanelToggleButton(SideMenu component, Element element) {
String toggleButtonId = element.attributeValue("sidePanelToggleButton");
if (StringUtils.isNotEmpty(toggleButtonId)) {
Component toggleButton = resultComponent.getFrame().getComponent(toggleButtonId);
if (!(toggleButton instanceof Button)) {
throw new GuiDevelopmentException("Unable to find sidePanelToggleButton for SideMenu",
context.getFullFrameId(), "sidePanelToggleButton", toggleButtonId);
}
component.setSidePanelToggleButton((Button) toggleButton);
}
}
}

View File

@ -74,6 +74,7 @@ public class WindowLoader extends FrameLoader<Window> {
loadWidth(resultComponent, layoutElement);
loadHeight(resultComponent, layoutElement);
loadStyleName(resultComponent, layoutElement);
loadResponsive(resultComponent, layoutElement);
loadVisible(resultComponent, layoutElement);
loadTimers(factory, resultComponent, element);

View File

@ -295,4 +295,16 @@ public interface WebConfig extends Config {
@Property("cuba.web.widgetSet")
@Default("com.haulmont.cuba.web.toolkit.ui.WidgetSet")
String getWidgetSet();
@Property("cuba.web.useDeviceWidthForViewport")
@DefaultBoolean(false)
boolean getUseDeviceWidthForViewport();
@Property("cuba.web.customDeviceWidthForViewport")
@DefaultInt(-1)
int getCustomDeviceWidthForViewport();
@Property("cuba.web.pageInitialScale")
@DefaultString("0.8")
String getPageInitialScale();
}

View File

@ -870,16 +870,20 @@ public class WebWindow implements Window, Component.Wrapper,
@Override
public boolean isResponsive() {
if (component instanceof AbstractComponent) {
return ((AbstractComponent) component).isResponsive();
ComponentContainer container = getContainer();
if (container instanceof AbstractComponent) {
return ((AbstractComponent) container).isResponsive();
}
return false;
}
@Override
public void setResponsive(boolean responsive) {
if (component instanceof AbstractComponent) {
((AbstractComponent) component).setResponsive(responsive);
ComponentContainer container = getContainer();
if (container instanceof AbstractComponent) {
((AbstractComponent) container).setResponsive(responsive);
}
}

View File

@ -90,12 +90,20 @@ public abstract class WebAbstractComponent<T extends com.vaadin.ui.AbstractCompo
@Override
public boolean isResponsive() {
return component.isResponsive();
com.vaadin.ui.Component composition = getComposition();
if (composition instanceof AbstractComponent) {
return ((AbstractComponent) composition).isResponsive();
}
return false;
}
@Override
public void setResponsive(boolean responsive) {
component.setResponsive(responsive);
com.vaadin.ui.Component composition = getComposition();
if (composition instanceof AbstractComponent) {
((AbstractComponent) composition).setResponsive(true);
}
}
public void assignAutoDebugId() {

View File

@ -16,10 +16,15 @@
package com.haulmont.cuba.web.gui.components.mainwindow;
import com.haulmont.cuba.core.global.AppBeans;
import com.haulmont.cuba.gui.components.AbstractAction;
import com.haulmont.cuba.gui.components.Button;
import com.haulmont.cuba.gui.components.Component;
import com.haulmont.cuba.gui.components.mainwindow.SideMenu;
import com.haulmont.cuba.web.gui.components.WebAbstractComponent;
import com.haulmont.cuba.web.gui.components.WebComponentsHelper;
import com.haulmont.cuba.web.sys.SideMenuBuilder;
import com.haulmont.cuba.web.theme.HaloTheme;
import com.haulmont.cuba.web.toolkit.ui.CubaSideMenu;
import javax.annotation.Nullable;
@ -35,14 +40,73 @@ public class WebSideMenu extends WebAbstractComponent<CubaSideMenu> implements S
protected Map<String, MenuItem> allItemsIds = new HashMap<>();
protected Button toggleButton;
protected Component sidePanel;
public WebSideMenu() {
component = new CubaSideMenu();
component.setBeforeMenuItemTriggeredHandler(menuItem -> {
if (sidePanel != null) {
sidePanel.removeStyleName(HaloTheme.SIDEMENU_PANEL_OPEN);
}
});
}
@Override
public void loadMenuConfig() {
SideMenuBuilder menuBuilder = new SideMenuBuilder(this);
menuBuilder.build();
SideMenuBuilder menuBuilder = AppBeans.getPrototype(SideMenuBuilder.NAME);
menuBuilder.build(this);
}
@Override
public void setSidePanelToggleButton(Button toggleButton) {
if (this.toggleButton != null) {
toggleButton.setAction(null);
}
if (toggleButton != null) {
AbstractAction toggleAction = new AbstractAction("toggleSideMenu") {
@Override
public void actionPerform(Component component) {
toggleSidePanel();
}
};
toggleAction.setCaption(toggleButton.getCaption());
toggleAction.setIcon(toggleButton.getIcon());
toggleAction.setDescription(toggleButton.getDescription());
toggleAction.setEnabled(toggleButton.isEnabled());
toggleAction.setVisible(toggleButton.isVisible());
toggleButton.setAction(toggleAction);
}
this.toggleButton = toggleButton;
}
protected void toggleSidePanel() {
if (sidePanel != null) {
if (sidePanel.getStyleName().contains(HaloTheme.SIDEMENU_PANEL_OPEN)) {
sidePanel.removeStyleName(HaloTheme.SIDEMENU_PANEL_OPEN);
} else {
sidePanel.addStyleName(HaloTheme.SIDEMENU_PANEL_OPEN);
}
}
}
@Override
public Button getSidePanelToggleButton() {
return toggleButton;
}
@Override
public void setSidePanel(Component sidePanel) {
this.sidePanel = sidePanel;
}
@Override
public Component getSidePanel() {
return sidePanel;
}
@Override

View File

@ -79,10 +79,13 @@ public class WebUserIndicator extends WebAbstractComponent<CubaCssLayout> implem
User user = uss.getUserSession().getUser();
AppUI ui = AppUI.getCurrent();
String substitutedUserCaption = getSubstitutedUserCaption(user);
if (substitutions.isEmpty()) {
userComboBox = null;
userNameLabel = new Label(getSubstitutedUserCaption(user));
userNameLabel = new Label(substitutedUserCaption);
userNameLabel.setStyleName("c-user-select-label");
userNameLabel.setSizeUndefined();
@ -91,6 +94,7 @@ public class WebUserIndicator extends WebAbstractComponent<CubaCssLayout> implem
}
component.addComponent(userNameLabel);
component.setDescription(substitutedUserCaption);
} else {
userNameLabel = null;
@ -106,7 +110,7 @@ public class WebUserIndicator extends WebAbstractComponent<CubaCssLayout> implem
userComboBox.setStyleName("c-user-select-combobox");
userComboBox.addItem(user);
userComboBox.setItemCaption(user, getSubstitutedUserCaption(user));
userComboBox.setItemCaption(user, substitutedUserCaption);
for (UserSubstitution substitution : substitutions) {
User substitutedUser = substitution.getSubstitutedUser();
@ -119,6 +123,7 @@ public class WebUserIndicator extends WebAbstractComponent<CubaCssLayout> implem
userComboBox.addValueChangeListener(new SubstitutedUserChangeListener(userComboBox));
component.addComponent(userComboBox);
component.setDescription(null);
}
adjustWidth();

View File

@ -20,6 +20,7 @@
app.initErrorCaption = Unexpected error
app.initErrorMessage = Please contact system administrator
app.initRetry = Retry
app.menu = Menu
cuba.poweredBy = powered by <a href="http://cuba-platform.com" target="_blank"><span class="c-cuba-word">CUBA</span>.platform</a>

View File

@ -20,6 +20,7 @@
app.initErrorCaption = Непредвиденная ошибка
app.initErrorMessage = Пожалуйста свяжитесь с администратором системы
app.initRetry = Попробовать ещё раз
app.menu = Меню
cuba.poweredBy = разработано на <a href="http://cuba-platform.ru" target="_blank"><span class="c-cuba-word">CUBA</span>.platform</a>

View File

@ -17,13 +17,15 @@
package com.haulmont.cuba.web.sys;
import com.haulmont.cuba.web.WebConfig;
import com.vaadin.server.BootstrapFragmentResponse;
import com.vaadin.server.BootstrapListener;
import com.vaadin.server.BootstrapPageResponse;
import org.jsoup.nodes.Element;
import org.springframework.stereotype.Component;
import javax.inject.Inject;
/**
* Event listener notified when the bootstrap HTML is about to be generated and
* send to the client. The bootstrap HTML is first constructed as an in-memory
@ -35,6 +37,9 @@ public class CubaBootstrapListener implements BootstrapListener {
public static final String NAME = "cuba_BootstrapListener";
@Inject
protected WebConfig webConfig;
@Override
public void modifyBootstrapFragment(BootstrapFragmentResponse response) {
}
@ -44,6 +49,15 @@ public class CubaBootstrapListener implements BootstrapListener {
Element head = response.getDocument().getElementsByTag("head").get(0);
includeScript("VAADIN/resources/jquery/jquery-1.12.4.min.js", response, head);
int customDeviceWidthForViewport = webConfig.getCustomDeviceWidthForViewport();
if (customDeviceWidthForViewport > 0) {
includeMetaViewport("width=" + customDeviceWidthForViewport +
", initial-scale=" + webConfig.getPageInitialScale(), response, head);
} else if (webConfig.getUseDeviceWidthForViewport()) {
includeMetaViewport("width=device-width" +
", initial-scale=" + webConfig.getPageInitialScale(), response, head);
}
}
protected void includeScript(String src, BootstrapPageResponse response, Element head) {
@ -51,4 +65,11 @@ public class CubaBootstrapListener implements BootstrapListener {
script.attr("src", src);
head.appendChild(script);
}
protected void includeMetaViewport(String content, BootstrapPageResponse response, Element head) {
Element meta = response.getDocument().createElement("meta");
meta.attr("name", "viewport");
meta.attr("content", content);
head.appendChild(meta);
}
}

View File

@ -17,9 +17,7 @@
package com.haulmont.cuba.web.sys;
import com.google.common.base.Strings;
import com.haulmont.cuba.core.global.AppBeans;
import com.haulmont.cuba.core.global.DevelopmentException;
import com.haulmont.cuba.core.global.UserSessionSource;
import com.haulmont.cuba.gui.ComponentsHelper;
import com.haulmont.cuba.gui.NoSuchScreenException;
import com.haulmont.cuba.gui.TestIdManager;
@ -33,7 +31,11 @@ import com.vaadin.event.ShortcutListener;
import com.vaadin.ui.AbstractComponent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import javax.inject.Inject;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
@ -43,27 +45,28 @@ import static com.haulmont.cuba.gui.components.KeyCombination.getShortcutModifie
/**
* Side menu builder.
*/
@Component(SideMenuBuilder.NAME)
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class SideMenuBuilder {
public static final String NAME = "cuba_SideMenuBuilder";
private final Logger log = LoggerFactory.getLogger(SideMenuBuilder.class);
@Inject
protected UserSession session;
protected SideMenu menu;
@Inject
protected MenuConfig menuConfig;
protected Window window;
@Inject
protected WindowConfig windowConfig;
protected MenuConfig menuConfig = AppBeans.get(MenuConfig.NAME);
protected UserSessionSource uss = AppBeans.get(UserSessionSource.NAME);
// call MenuBuilder after attaching menubar to UI
public SideMenuBuilder(SideMenu menu) {
this.session = uss.getUserSession();
this.menu = menu;
this.window = ComponentsHelper.getWindowImplementation(menu);
public SideMenuBuilder() {
}
public void build() {
public void build(SideMenu menu) {
Window window = ComponentsHelper.getWindowImplementation(menu);
if (window == null) {
throw new IllegalStateException("SideMenu is not belong to Window");
}
@ -71,17 +74,19 @@ public class SideMenuBuilder {
List<MenuItem> rootItems = menuConfig.getRootItems();
for (MenuItem menuItem : rootItems) {
if (menuItem.isPermitted(session)) {
createMenuBarItem(menu, menuItem);
createMenuBarItem(window, menu, menuItem);
}
}
removeExtraSeparators(menu);
}
protected void removeExtraSeparators(SideMenu menuBar) {
for (SideMenu.MenuItem item : new ArrayList<>(menuBar.getMenuItems())) {
List<SideMenu.MenuItem> menuItems = menuBar.getMenuItems();
for (SideMenu.MenuItem item : menuItems.toArray(new SideMenu.MenuItem[menuItems.size()])) {
removeExtraSeparators(item);
if (isMenuItemEmpty(item))
if (isMenuItemEmpty(item)) {
menuBar.removeMenuItem(item);
}
}
}
@ -106,55 +111,52 @@ public class SideMenuBuilder {
} while (!done);
}
protected void createMenuBarItem(SideMenu menuBar, MenuItem item) {
protected void createMenuBarItem(Window webWindow, SideMenu menu, MenuItem item) {
if (item.isPermitted(session)) {
SideMenu.MenuItem menuItem = menuBar.createMenuItem(item.getId(),
MenuConfig.getMenuItemCaption(item.getId()), null, createMenuBarCommand(item));
SideMenu.MenuItem menuItem = menu.createMenuItem(item.getId(),
menuConfig.getItemCaption(item.getId()), null, createMenuBarCommand(item));
createSubMenu(menuItem, item, session);
assignTestId(menuItem, item);
createSubMenu(webWindow, menu, menuItem, item, session);
assignTestId(menu, menuItem, item);
assignStyleName(menuItem, item);
assignIcon(menuItem, item);
assignDescription(menuItem, item);
assignShortcut(menuItem, item);
assignExpanded(menuItem, item);
assignShortcut(webWindow, menuItem, item);
if (!isMenuItemEmpty(menuItem)) {
menuBar.addMenuItem(menuItem);
menu.addMenuItem(menuItem);
}
}
}
protected void createSubMenu(SideMenu.MenuItem vItem, MenuItem parentItem, UserSession session) {
if (parentItem.isPermitted(session) && !parentItem.getChildren().isEmpty()) {
protected void createSubMenu(Window webWindow, SideMenu menu, SideMenu.MenuItem vItem,
MenuItem parentItem, UserSession session) {
if (parentItem.isPermitted(session)) {
for (MenuItem child : parentItem.getChildren()) {
if (child.isSeparator()) {
continue;
}
if (child.getChildren().isEmpty()) {
if (child.isPermitted(session)) {
SideMenu.MenuItem menuItem = menu.createMenuItem(child.getId(),
MenuConfig.getMenuItemCaption(child.getId()), null, createMenuBarCommand(child));
if (child.isPermitted(session)) {
SideMenu.MenuItem menuItem = menu.createMenuItem(child.getId(),
menuConfig.getItemCaption(child.getId()));
assignTestId(menuItem, child);
assignDescription(menuItem, child);
assignIcon(menuItem, child);
assignStyleName(menuItem, child);
assignShortcut(menuItem, child);
assignTestId(menu, menuItem, child);
assignDescription(menuItem, child);
assignIcon(menuItem, child);
assignStyleName(menuItem, child);
if (child.getChildren().isEmpty()) {
menuItem.setCommand(createMenuBarCommand(child));
assignShortcut(webWindow, menuItem, child);
vItem.addChildItem(menuItem);
}
} else {
if (child.isPermitted(session)) {
SideMenu.MenuItem menuItem = menu.createMenuItem(child.getId());
menuItem.setCaption(MenuConfig.getMenuItemCaption(child.getId()));
} else {
createSubMenu(webWindow, menu, menuItem, child, session);
createSubMenu(menuItem, child, session);
assignTestId(menuItem, child);
assignDescription(menuItem, child);
assignIcon(menuItem, child);
assignStyleName(menuItem, child);
assignShortcut(menuItem, child);
assignExpanded(menuItem, child);
if (!isMenuItemEmpty(menuItem)) {
vItem.addChildItem(menuItem);
@ -165,12 +167,15 @@ public class SideMenuBuilder {
}
}
protected void assignExpanded(SideMenu.MenuItem menuItem, MenuItem item) {
menuItem.setExpanded(item.isExpanded());
}
protected Consumer<SideMenu.MenuItem> createMenuBarCommand(final MenuItem item) {
if (!item.getChildren().isEmpty() || item.isMenu()) //check item is menu
return null;
WindowInfo windowInfo = null;
final WindowConfig windowConfig = AppBeans.get(WindowConfig.NAME);
try {
windowInfo = windowConfig.getWindowInfo(item.getId());
} catch (NoSuchScreenException e) {
@ -202,7 +207,7 @@ public class SideMenuBuilder {
return !menuItem.hasChildren() && menuItem.getCommand() == null;
}
protected void assignTestId(SideMenu.MenuItem menuItem, MenuItem conf) {
protected void assignTestId(SideMenu menu, SideMenu.MenuItem menuItem, MenuItem conf) {
if (menu.getId() != null && !conf.isSeparator()) {
TestIdManager testIdManager = AppUI.getCurrent().getTestIdManager();
@ -232,11 +237,12 @@ public class SideMenuBuilder {
}
}
protected void assignShortcut(SideMenu.MenuItem menuItem, MenuItem item) {
protected void assignShortcut(Window webWindow, SideMenu.MenuItem menuItem, MenuItem item) {
KeyCombination itemShortcut = item.getShortcut();
if (itemShortcut != null) {
ShortcutListener shortcut = new SideMenuShortcutListener(menuItem, item);
AbstractComponent windowImpl = window.unwrap(AbstractComponent.class);
AbstractComponent windowImpl = webWindow.unwrap(AbstractComponent.class);
windowImpl.addShortcutListener(shortcut);
if (Strings.isNullOrEmpty(menuItem.getBadgeText())) {

View File

@ -363,4 +363,45 @@ public class HaloTheme {
* description.
*/
public static final String FILEUPLOADFIELD_DROPZONE_DESCRIPTION = "dropzone-description";
/**
* Style for layout tag in mainwindow with responsive {@link com.haulmont.cuba.gui.components.mainwindow.SideMenu}.
*/
public static final String SIDEMENU_LAYOUT_RESPONSIVE = "c-sidemenu-responsive";
/**
* Style for horizontal layout that contains {@link #SIDEMENU_PANEL} and {@link com.haulmont.cuba.gui.components.mainwindow.AppWorkArea}
* in responsive mainwindow with {@link com.haulmont.cuba.gui.components.mainwindow.SideMenu}.
*/
public static final String SIDEMENU_LAYOUT = "c-sidemenu-layout";
/**
* Style for side panel in responsive mainwindow with {@link com.haulmont.cuba.gui.components.mainwindow.SideMenu}.
*/
public static final String SIDEMENU_PANEL = "c-sidemenu-panel";
/**
* Style for opened side panel in responsive mainwindow with {@link com.haulmont.cuba.gui.components.mainwindow.SideMenu}.
*/
public static final String SIDEMENU_PANEL_OPEN = "c-sidemenu-open";
/**
* Style for opened side panel content in responsive mainwindow with {@link com.haulmont.cuba.gui.components.mainwindow.SideMenu}.
*/
public static final String SIDEMENU_WRAP = "c-sidemenu-wrap";
/**
* Style for side panel toggle button in responsive mainwindow with {@link com.haulmont.cuba.gui.components.mainwindow.SideMenu}.
*/
public static final String SIDEMENU_TOGGLE_BUTTON = "c-sidemenu-toggle";
/**
* Style for mobile buttons in top panel in responsive mainwindow with {@link com.haulmont.cuba.gui.components.mainwindow.SideMenu}.
*/
public static final String SIDEMENU_MOBILE_BUTTONS = "c-sidemenu-mobile-buttons";
/**
* Style for top header in responsive mainwindow with {@link com.haulmont.cuba.gui.components.mainwindow.SideMenu}.
*/
public static final String SIDEMENU_TITLE = "c-sidemenu-title";
}

View File

@ -16,6 +16,7 @@
package com.haulmont.cuba.web.toolkit.ui;
import com.haulmont.cuba.web.theme.HaloTheme;
import com.haulmont.cuba.web.toolkit.ui.client.verticalmenu.CubaSideMenuClientRpc;
import com.haulmont.cuba.web.toolkit.ui.client.verticalmenu.CubaSideMenuServerRpc;
import com.haulmont.cuba.web.toolkit.ui.client.verticalmenu.CubaSideMenuState;
@ -64,6 +65,8 @@ public class CubaSideMenu extends AbstractComponent implements Component.Focusab
protected PropertyChangeListener itemsPropertyChangeListener = this::menuItemPropertyChanged;
protected Consumer<MenuItem> beforeMenuItemTriggeredHandler = null;
public CubaSideMenu() {
CubaSideMenuServerRpc menuRpc = (CubaSideMenuServerRpc) itemId -> {
MenuItem menuItem = menuItemIdMapper.get(itemId);
@ -71,14 +74,26 @@ public class CubaSideMenu extends AbstractComponent implements Component.Focusab
if (isSelectOnClick()) {
this.selectedItem = menuItem;
}
if (beforeMenuItemTriggeredHandler != null) {
beforeMenuItemTriggeredHandler.accept(menuItem);
}
if (menuItem.getCommand() != null) {
menuItem.getCommand().accept(new MenuItemTriggeredEvent(this, menuItem));
}
removeStyleName(HaloTheme.SIDEMENU_PANEL_OPEN);
}
};
registerRpc(menuRpc);
}
public Consumer<MenuItem> getBeforeMenuItemTriggeredHandler() {
return beforeMenuItemTriggeredHandler;
}
public void setBeforeMenuItemTriggeredHandler(Consumer<MenuItem> beforeMenuItemTriggeredHandler) {
this.beforeMenuItemTriggeredHandler = beforeMenuItemTriggeredHandler;
}
@Override
protected CubaSideMenuState getState() {
return (CubaSideMenuState) super.getState();

View File

@ -203,6 +203,7 @@ cuba.web.icons.upload.png = UPLOAD
cuba.web.icons.unlock.png = UNLOCK
cuba.web.icons.download.png = DOWNLOAD
cuba.web.icons.question-white.png = QUESTION_CIRCLE
cuba.web.icons.mobile-menu.png = LIST
cuba.web.components.pickerfield.images.clear-btn.png = TIMES
cuba.web.components.pickerfield.images.clear-btn-readonly.png = TIMES

View File

@ -15,6 +15,124 @@
*
*/
@mixin side-menu-large-icons-style($primary-stylename: c-sidemenu) {
$bg: $valo-menu-background-color;
background-color: $bg;
.#{$primary-stylename}-title {
.v-label {
line-height: 1.2;
font-size: round($v-font-size--h2 * 0.75);
font-weight: bold;
}
.v-label-undef-w {
white-space: normal;
}
}
.c-userindicator {
padding-left: $v-layout-margin-left/2;
padding-right: $v-layout-margin-right/2;
.v-label {
font-size: round($v-font-size * 0.8);
}
}
.c-main-buttons {
.v-icon {
font-size: round($v-font-size * 1.5);
}
img.v-icon {
height: round($v-font-size * 1.5);
}
}
& > * {
max-width: $v-unit-size * 4;
}
.#{$primary-stylename}-item-action {
display: block;
font-size: round($v-font-size * 1.6);
line-height: 1;
padding: round($v-unit-size/3);
text-align: center;
border-top: valo-border($color: $bg, $strength: 0.2, $border: first-number($v-border) solid v-tone);
&:first-child {
border-top: none;
}
.#{$primary-stylename}-item-caption {
display: block;
width: auto;
margin: .3em 0 0;
padding: 0;
font-size: round($v-font-size * 0.8);
line-height: 1.3;
}
.v-icon {
margin: 0;
}
span.v-icon {
opacity: 0.8;
}
}
.#{$primary-stylename}-item-header {
margin: round($v-unit-size/4) 0 0;
padding: round($v-unit-size/5) round($v-unit-size/1.5) round($v-unit-size/5) round($v-unit-size/4);
border: none;
text-overflow: ellipsis;
overflow: hidden;
background: darken($bg, 6%);
box-shadow: valo-bevel-and-shadow($shadow: $v-shadow);
.#{$primary-stylename}-item-wrap {
line-height: 1.2;
}
.#{$primary-stylename}-item-caption {
font-size: round($v-font-size * 0.9);
}
.#{$primary-stylename}-item-badge {
right: round($v-unit-size/4);
}
+ .#{$primary-stylename}-item {
border-top: none;
}
}
.#{$primary-stylename}-item-action.#{$primary-stylename}-item-selected {
background: if(is-dark-color($bg), darken($bg, 3%), lighten($bg, 5%));
.v-icon {
opacity: 1;
}
.#{$primary-stylename}-item-badge {
border-color: darken($bg, 3%);
}
}
.#{$primary-stylename}-item-action .#{$primary-stylename}-item-badge {
padding-left: round($v-unit-size/9);
padding-right: round($v-unit-size/9);
top: round($v-unit-size/5);
right: round($v-unit-size/5);
border: 2px solid $bg;
}
}
$cuba-sidemenu-top-offset: floor(($v-unit-size - $v-unit-size * 0.8) / 2);
$cuba-sidemenu-top-height: $v-unit-size + 2 * $cuba-sidemenu-top-offset;
@mixin halo-cuba-sidemenu($primary-stylename: c-sidemenu) {
.#{$primary-stylename}-user {
background: transparent;
@ -70,8 +188,19 @@
}
}
.#{$primary-stylename}-wrap {
font-size: 0;
}
.#{$primary-stylename}-title {
@include valo-menu-title-style;
@include valo-gradient($color: $v-selection-color);
$font-color: valo-font-color($v-selection-color, 1);
color: $font-color;
text-shadow: valo-text-shadow($font-color: $font-color, $background-color: $v-selection-color);
padding: round($v-unit-size/3) round($v-unit-size/2);
border-bottom: valo-border($color: $v-selection-color);
@include box-shadow(valo-bevel-and-shadow($shadow: $v-shadow));
text-align: center;
width: 100%;
@ -83,8 +212,14 @@
.#{$primary-stylename} {
outline: 0;
$bg: $valo-menu-background-color;
@include valo-menu-style;
@include linear-gradient(to left, (darken($bg, valo-gradient-opacity() / 2) 0%, $bg round($v-unit-size/4)), $fallback: $bg);
color: valo-font-color($bg, 0.5);
font-size: round($v-font-size * 0.9);
line-height: round($v-unit-size * 0.8);
border-right: valo-border($color: $bg);
white-space: nowrap;
font-size: $v-font-size;
}
@ -112,12 +247,15 @@
}
.#{$primary-stylename}-item-header {
@include valo-menu-subtitle-style;
color: valo-font-color($valo-menu-background-color, 0.33);
margin: round($v-unit-size/5) 0 round($v-unit-size/5) round($v-unit-size/2);
border-bottom: valo-border($color: $valo-menu-background-color, $strength: 0.5, $border: first-number($v-border) solid v-tone);
margin-left: 0;
.#{$primary-stylename}-item-badge {
background: transparent;
color: mix(valo-font-color($valo-menu-background-color), $v-selection-color);
}
}
@ -175,4 +313,128 @@
@include valo-badge-style($states: active, $active-color: $active-color);
}
}
}
.#{$primary-stylename}-toggle,
.#{$primary-stylename}-mobile-buttons {
display: none;
position: fixed;
z-index: 200;
}
.#{$primary-stylename}-toggle {
top: $cuba-sidemenu-top-offset;
left: $cuba-sidemenu-top-offset;
min-width: 0;
}
.#{$primary-stylename}-mobile-buttons {
top: $cuba-sidemenu-top-offset;
right: 0;
.v-button {
color: valo-font-color($v-selection-color, 1);
}
}
.#{$primary-stylename}-responsive {
.#{$primary-stylename}-panel {
padding-bottom: $v-unit-size;
overflow: auto;
height: 100%;
}
.c-app-icon {
margin: $v-layout-spacing-vertical 0;
}
.c-userindicator > .v-label,
.c-app-icon,
.c-welcome-text,
.c-user-timezone-label,
.c-main-buttons {
text-align: center;
}
.#{$primary-stylename}-panel > .#{$primary-stylename}-title {
display: block;
}
.#{$primary-stylename}-wrap {
& > .#{$primary-stylename},
& > .c-app-icon,
& > .c-userindicator,
& > .c-user-timezone-label,
& > .c-fts-field-wrap,
& > .c-main-buttons {
display: block;
}
}
@include width-range($min: 801px, $max: 1100px) {
.#{$primary-stylename}-panel {
@include side-menu-large-icons-style;
}
}
@include width-range($max: 800px) {
padding-top: $cuba-sidemenu-top-height;
.#{$primary-stylename}-panel {
overflow: visible;
width: 0;
border-right: 0;
}
.#{$primary-stylename}-toggle,
.#{$primary-stylename}-mobile-buttons {
display: inline-block;
}
.c-main-buttons {
display: none;
}
.#{$primary-stylename}-title {
position: fixed;
z-index: 100;
top: 0;
left: 0;
right: 0;
height: $cuba-sidemenu-top-height !important;
padding-top: 0;
padding-bottom: 0;
-webkit-backface-visibility: hidden;
.v-label {
line-height: $v-unit-size;
}
}
.#{$primary-stylename}-wrap {
@include valo-menu-style;
position: fixed;
z-index: 9000;
top: $cuba-sidemenu-top-height;
bottom: 0;
height: auto;
max-width: 100%;
overflow: auto;
padding: round($v-unit-size / 2) 0;
@include transform(translatex(-100%));
@include transition(all 300ms);
}
.#{$primary-stylename}-panel.#{$primary-stylename}-open .#{$primary-stylename}-wrap {
@include transform(translatex(0%));
}
}
@include width-range($max: 500px) {
.#{$primary-stylename}-toggle .v-button-caption {
display: none;
}
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 504 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 504 B