001/* 002 * #%L 003 * GwtMaterial 004 * %% 005 * Copyright (C) 2015 - 2017 GwtMaterialDesign 006 * %% 007 * Licensed under the Apache License, Version 2.0 (the "License"); 008 * you may not use this file except in compliance with the License. 009 * You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 * #L% 019 */ 020package gwt.material.design.addins.client.popupmenu; 021 022import com.google.gwt.core.client.Scheduler; 023import com.google.gwt.dom.client.Element; 024import com.google.gwt.event.logical.shared.*; 025import com.google.gwt.event.shared.HandlerRegistration; 026import com.google.gwt.user.client.DOM; 027import gwt.material.design.addins.client.MaterialAddins; 028import gwt.material.design.addins.client.base.constants.AddinsCssName; 029import gwt.material.design.client.MaterialDesignBase; 030import gwt.material.design.client.base.JsLoader; 031import gwt.material.design.client.constants.CssName; 032import gwt.material.design.client.ui.html.UnorderedList; 033import gwt.material.design.jquery.client.api.JQueryElement; 034 035import static gwt.material.design.jquery.client.api.JQuery.$; 036 037/** 038 * Popup Menu. 039 * 040 * @author Mark Kevin 041 * @author Ben Dol 042 */ 043public class MaterialPopupMenu extends UnorderedList implements JsLoader, HasSelectionHandlers<Element>, HasOpenHandlers<MaterialPopupMenu>, 044 HasCloseHandlers<MaterialPopupMenu> { 045 046 static { 047 if (MaterialAddins.isDebug()) { 048 MaterialDesignBase.injectCss(MaterialPopupMenuDebugClientBundle.INSTANCE.menuCssDebug()); 049 } else { 050 MaterialDesignBase.injectCss(MaterialPopupMenuClientBundle.INSTANCE.menuCss()); 051 } 052 } 053 054 private int popupX; 055 private int popupY; 056 private String id; 057 private Object selected; 058 059 public MaterialPopupMenu() { 060 id = DOM.createUniqueId(); 061 setInitialClasses(AddinsCssName.POPUP_MENU, AddinsCssName.MENU_BAR, CssName.Z_DEPTH_3); 062 } 063 064 @Override 065 protected void onLoad() { 066 super.onLoad(); 067 068 load(); 069 } 070 071 @Override 072 public void load() { 073 $(this).attr("tabindex", "0"); 074 $(this).on("blur", e -> { 075 close(); 076 return true; 077 }); 078 079 $("*").on("scroll." + id, e -> { 080 close(); 081 return true; 082 }); 083 084 initializeSelectionEvent(); 085 086 close(); 087 } 088 089 @Override 090 protected void onUnload() { 091 super.onUnload(); 092 093 unload(); 094 } 095 096 @Override 097 public void unload() { 098 $(".popup-menu li").off("mouseleave"); 099 $(".popup-menu li").off("click"); 100 $(".popup-menu li").off("mouseover"); 101 $(this).off("." + id); 102 $("*").off("." + id); 103 } 104 105 @Override 106 public void reload() { 107 unload(); 108 load(); 109 } 110 111 112 private void initializeSelectionEvent() { 113 // Initialization of Selection event 114 $(".popup-menu li").on("click", e -> { 115 e.stopPropagation(); 116 SelectionEvent.fire(MaterialPopupMenu.this, $(e.getCurrentTarget()).asElement()); 117 $(this).hide(); 118 return true; 119 }); 120 121 // Check if the dropdown is not visible anymore into it's container either left / bottom side 122 $(".popup-menu li").on("mouseover", (e, param1) -> { 123 JQueryElement item = $(e.getCurrentTarget()).find("a"); 124 if (item.attr("data-activates") != null) { 125 JQueryElement dp = $("#" + item.attr("data-activates")); 126 127 double dpWidth = dp.width(); 128 double dpLeft = dp.offset().left; 129 double conWidth = body().width(); 130 131 double dpHeight = 200; 132 double dpTop = dp.offset().top; 133 double conHeight = body().height(); 134 135 if (dpWidth + dpLeft > conWidth) { 136 dp.addClass(AddinsCssName.EDGE_LEFT); 137 } 138 139 if (dpHeight + dpTop > conHeight) { 140 dp.addClass(AddinsCssName.EDGE_BOTTOM); 141 } 142 } 143 return true; 144 }); 145 146 $(".popup-menu li").on("mouseleave", (e, param) -> { 147 JQueryElement item = $(e.getCurrentTarget()).find("a"); 148 if (item.attr("data-activates") != null) { 149 JQueryElement dp = $("#" + item.attr("data-activates")); 150 dp.removeClass(AddinsCssName.EDGE_LEFT); 151 dp.removeClass(AddinsCssName.EDGE_BOTTOM); 152 } 153 return true; 154 }); 155 } 156 157 /** 158 * Set the popup position of the context menu 159 * 160 * @param popupX window x position 161 * @param popupY window y position 162 */ 163 public void setPopupPosition(int popupX, int popupY) { 164 // Will check if the popup is out of container 165 this.popupX = popupX; 166 this.popupY = popupY; 167 setLeft(popupX); 168 setTop(popupY); 169 } 170 171 @Override 172 public HandlerRegistration addSelectionHandler(SelectionHandler<Element> selectionHandler) { 173 return addHandler(selectionHandler, SelectionEvent.getType()); 174 } 175 176 public void open() { 177 setVisible(true); 178 Scheduler.get().scheduleDeferred(() -> setFocus(true)); 179 180 // Check if dropdown is out of the container (Left) 181 if ($(this).width() + $(this).offset().left > body().width()) { 182 setLeft(body().width() - $(this).width()); 183 } 184 OpenEvent.fire(this, this); 185 } 186 187 public void close() { 188 setVisible(false); 189 CloseEvent.fire(this, this); 190 } 191 192 public Object getSelected() { 193 return selected; 194 } 195 196 public void setSelected(Object selected) { 197 this.selected = selected; 198 } 199 200 @Override 201 public HandlerRegistration addCloseHandler(CloseHandler<MaterialPopupMenu> closeHandler) { 202 return addHandler(new CloseHandler<MaterialPopupMenu>() { 203 @Override 204 public void onClose(CloseEvent<MaterialPopupMenu> closeEvent) { 205 if (isEnabled()) { 206 closeHandler.onClose(closeEvent); 207 } 208 } 209 }, CloseEvent.getType()); 210 } 211 212 @Override 213 public HandlerRegistration addOpenHandler(OpenHandler<MaterialPopupMenu> openHandler) { 214 return addHandler(new OpenHandler<MaterialPopupMenu>() { 215 @Override 216 public void onOpen(OpenEvent<MaterialPopupMenu> openEvent) { 217 if (isEnabled()) { 218 openHandler.onOpen(openEvent); 219 } 220 } 221 }, OpenEvent.getType()); 222 } 223 224 public int getPopupX() { 225 return popupX; 226 } 227 228 public int getPopupY() { 229 return popupY; 230 } 231}