001package gwt.material.design.client.ui; 002 003/* 004 * #%L 005 * GwtMaterial 006 * %% 007 * Copyright (C) 2015 GwtMaterialDesign 008 * %% 009 * Licensed under the Apache License, Version 2.0 (the "License"); 010 * you may not use this file except in compliance with the License. 011 * You may obtain a copy of the License at 012 * 013 * http://www.apache.org/licenses/LICENSE-2.0 014 * 015 * Unless required by applicable law or agreed to in writing, software 016 * distributed under the License is distributed on an "AS IS" BASIS, 017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 018 * See the License for the specific language governing permissions and 019 * limitations under the License. 020 * #L% 021 */ 022 023import com.google.gwt.dom.client.Element; 024import com.google.gwt.dom.client.Style; 025import com.google.gwt.event.dom.client.ClickEvent; 026import com.google.gwt.event.dom.client.ClickHandler; 027import com.google.gwt.event.logical.shared.HasSelectionHandlers; 028import com.google.gwt.event.logical.shared.SelectionEvent; 029import com.google.gwt.event.logical.shared.SelectionHandler; 030import com.google.gwt.event.shared.HandlerRegistration; 031import com.google.gwt.uibinder.client.UiConstructor; 032import com.google.gwt.user.client.DOM; 033import com.google.gwt.user.client.ui.UIObject; 034import com.google.gwt.user.client.ui.Widget; 035import gwt.material.design.client.base.HasWaves; 036import gwt.material.design.client.base.helper.DOMHelper; 037import gwt.material.design.client.constants.Alignment; 038import gwt.material.design.client.ui.html.ListItem; 039import gwt.material.design.client.ui.html.UnorderedList; 040 041import java.util.ArrayList; 042import java.util.List; 043 044//@formatter:off 045 046/** 047 * You can add dropdown easily by specifying it's item 048 * content and add a UiHandler on it to implement any event. 049 * 050 * <h3>UiBinder Usage:</h3> 051 * <pre> 052 *{@code 053 *<m:MaterialDropDown> 054 * <m:MaterialLink text="First" /> 055 * <m:MaterialLink text="Second" /> 056 * <m:MaterialLink text="Third" /> 057 * </m:MaterialDropDown> 058 * } 059 * </pre> 060 * @author kevzlou7979 061 * @author Ben Dol 062 * @see <a href="http://gwt-material-demo.herokuapp.com/#dropdowns">Material DropDowns</a> 063 */ 064//@formatter:on 065public class MaterialDropDown extends UnorderedList implements HasSelectionHandlers<Widget> { 066 067 private String activator; 068 private Element activatorElem; 069 070 // Options 071 private int inDuration = 300; 072 private int outDuration = 225; 073 private boolean constrainWidth = true; 074 private boolean hover = false; 075 private boolean belowOrigin = false; 076 private int gutter = 0; 077 private String alignment = Alignment.LEFT.getCssName(); 078 private List<Widget> children = new ArrayList<>(); 079 080 public MaterialDropDown() { 081 setInitialClasses("dropdown-content"); 082 setId(DOM.createUniqueId()); 083 } 084 085 /** 086 * Material Dropdown - adds a list item selection when button, link, icon button pressed. 087 * @param activator - data-activates attribute name of your dropdown activator. 088 */ 089 @UiConstructor 090 public MaterialDropDown(String activator) { 091 this(); 092 this.activator = activator; 093 094 getElement().setId(this.activator); 095 } 096 097 public MaterialDropDown(Element activatorElem) { 098 this(); 099 activatorElem.setAttribute("data-activates", getId()); 100 this.activatorElem = activatorElem; 101 } 102 103 public MaterialDropDown(UIObject activator) { 104 this(activator.getElement()); 105 } 106 107 @Override 108 protected void onLoad() { 109 super.onLoad(); 110 111 initialize(); 112 } 113 114 /** 115 * The duration of the transition enter in milliseconds. Default: 300 116 */ 117 public void setInDuration(int durationMillis) { 118 this.inDuration = durationMillis; 119 } 120 121 public int getInDuration() { 122 return inDuration; 123 } 124 125 /** 126 * The duration of the transition out in milliseconds. Default: 225 127 */ 128 public void setOutDuration(int durationMillis) { 129 this.outDuration = durationMillis; 130 } 131 132 public int getOutDuration() { 133 return outDuration; 134 } 135 136 /** 137 * If true, constrainWidth to the size of the dropdown activator. Default: true 138 */ 139 public void setConstrainWidth(boolean constrainWidth) { 140 this.constrainWidth = constrainWidth; 141 } 142 143 public boolean isConstrainWidth() { 144 return constrainWidth; 145 } 146 147 /** 148 * If true, the dropdown will open on hover. Default: false 149 */ 150 public void setHover(boolean hover) { 151 this.hover = hover; 152 } 153 154 public boolean isHover() { 155 return hover; 156 } 157 158 /** 159 * This defines the spacing from the aligned edge. Default: 0 160 */ 161 public void setGutter(int gutter) { 162 this.gutter = gutter; 163 } 164 165 public int getGutter() { 166 return gutter; 167 } 168 169 /** 170 * If true, the dropdown will show below the activator. Default: false 171 */ 172 public void setBelowOrigin(boolean belowOrigin) { 173 this.belowOrigin = belowOrigin; 174 } 175 176 public boolean isBelowOrigin() { 177 return belowOrigin; 178 } 179 180 /** 181 * Defines the edge the menu is aligned to. Default: 'left' 182 */ 183 public void setAlignment(Alignment alignment) { 184 this.alignment = alignment.getCssName(); 185 } 186 187 public Alignment getAlignment() { 188 return Alignment.fromStyleName(alignment); 189 } 190 191 /** 192 * Get the unique activator set by material widget e.g links, icons, buttons to trigger the dropdown. 193 */ 194 public String getActivator() { 195 return activator; 196 } 197 198 /** 199 * Set the unique activator of each dropdown component and it must be unique 200 */ 201 public void setActivator(String activator) { 202 this.activator = activator; 203 setId(activator); 204 } 205 206 @Override 207 public void add(final Widget child) { 208 String tagName = child.getElement().getTagName(); 209 if(child instanceof ListItem || tagName.toLowerCase().startsWith("li")) { 210 child.getElement().getStyle().setDisplay(Style.Display.BLOCK); 211 add(child, (Element) getElement()); 212 } else { 213 ListItem li = new ListItem(child); 214 children.add(child); 215 child.addDomHandler(new ClickHandler() { 216 @Override 217 public void onClick(ClickEvent event) { 218 SelectionEvent.fire(MaterialDropDown.this, child); 219 } 220 }, ClickEvent.getType()); 221 222 // Checks if there are sub dropdown components 223 if(child instanceof MaterialLink) { 224 MaterialLink link = (MaterialLink) child; 225 for(int i = 0; i < link.getWidgetCount(); i++) { 226 if(link.getWidget(i) instanceof MaterialDropDown) { 227 link.addClickHandler(new ClickHandler() { 228 @Override 229 public void onClick(ClickEvent event) { 230 event.stopPropagation(); 231 } 232 }); 233 link.stopTouchStartEvent(); 234 } 235 } 236 } 237 238 if(child instanceof HasWaves) { 239 li.setWaves(((HasWaves) child).getWaves()); 240 ((HasWaves) child).setWaves(null); 241 } 242 li.getElement().getStyle().setDisplay(Style.Display.BLOCK); 243 add(li, (Element) getElement()); 244 } 245 } 246 247 protected void initialize() { 248 if(activatorElem == null) { 249 activatorElem = DOMHelper.getElementByAttribute("data-activates", activator); 250 if (activatorElem == null) { 251 throw new IllegalStateException("There is no activator element with id: '" + activator 252 + "' in the DOM, cannot instantiate MaterialDropDown without a data-activates."); 253 } 254 } 255 initialize(activatorElem); 256 } 257 258 /** 259 * Must be called after changing any options. 260 */ 261 public void reinitialize() { 262 remove(activatorElem); 263 initialize(activatorElem); 264 } 265 266 /** 267 * Initialize the dropdown components. 268 */ 269 protected native void initialize(Element activator)/*-{ 270 var that = this; 271 $wnd.jQuery(document).ready(function() { 272 $wnd.jQuery(activator).dropdown({ 273 inDuration: that.@gwt.material.design.client.ui.MaterialDropDown::inDuration, 274 outDuration: that.@gwt.material.design.client.ui.MaterialDropDown::outDuration, 275 constrain_width: that.@gwt.material.design.client.ui.MaterialDropDown::constrainWidth, // Does not change width of dropdown to that of the activator 276 hover: that.@gwt.material.design.client.ui.MaterialDropDown::hover, // Activate on hover 277 gutter: that.@gwt.material.design.client.ui.MaterialDropDown::gutter, // Spacing from edge 278 belowOrigin: that.@gwt.material.design.client.ui.MaterialDropDown::belowOrigin, // Displays dropdown below the button 279 alignment: that.@gwt.material.design.client.ui.MaterialDropDown::alignment // Displays dropdown with edge aligned to the left of button 280 }); 281 }); 282 }-*/; 283 284 protected native void remove(Element activator)/*-{ 285 $wnd.jQuery(activator).dropdown("remove"); 286 }-*/; 287 288 @Override 289 public HandlerRegistration addSelectionHandler(final SelectionHandler<Widget> handler) { 290 return addHandler(new SelectionHandler<Widget>() { 291 @Override 292 public void onSelection(SelectionEvent<Widget> event) { 293 if(isEnabled()){ 294 handler.onSelection(event); 295 } 296 } 297 }, SelectionEvent.getType()); 298 } 299 300 public List<Widget> getItems() { 301 return children; 302 } 303}