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.client.ui; 021 022import com.google.gwt.core.client.GWT; 023import com.google.gwt.dom.client.Document; 024import com.google.gwt.dom.client.Element; 025import com.google.gwt.event.shared.HandlerRegistration; 026import com.google.gwt.user.client.ui.Widget; 027import gwt.material.design.client.base.*; 028import gwt.material.design.client.base.mixin.CssTypeMixin; 029import gwt.material.design.client.constants.CollapsibleType; 030import gwt.material.design.client.constants.CssName; 031import gwt.material.design.client.events.ClearActiveEvent; 032import gwt.material.design.client.events.ClearActiveEvent.ClearActiveHandler; 033 034import static gwt.material.design.client.js.JsMaterialElement.$; 035 036//@formatter:off 037 038/** 039 * Collapsible are accordion elements that expand when clicked on. 040 * They allow you to hide content that is not immediately relevant 041 * to the user. 042 * <p> 043 * <h3>UiBinder Usage:</h3> 044 * <p> 045 * <pre> 046 * {@code 047 * // Accordion 048 * <m:MaterialCollapsible accordion="true" grid="s12 m6 l8"> 049 * <!-- ITEM 1 --> 050 * <m:MaterialCollapsibleItem> 051 * <m:MaterialCollapsibleHeader> 052 * <m:MaterialLink text="First" iconType="POLYMER" iconPosition="LEFT" textColor="BLACK"/> 053 * </m:MaterialCollapsibleHeader> 054 * <m:MaterialCollapsibleBody> 055 * <m:MaterialLabel text="Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."/> 056 * </m:MaterialCollapsibleBody> 057 * </m:MaterialCollapsibleItem> 058 * </m:MaterialCollapsible> 059 * 060 * // Expandable 061 * <m:MaterialCollapsible accordion="false" grid="s12 m6 l8"> 062 * <!-- ITEM 1 --> 063 * <m:MaterialCollapsibleItem> 064 * <m:MaterialCollapsibleHeader> 065 * <m:MaterialLink text="First" iconType="POLYMER" iconPosition="LEFT" textColor="BLACK"/> 066 * </m:MaterialCollapsibleHeader> 067 * <m:MaterialCollapsibleBody> 068 * <m:MaterialLabel text="Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."/> 069 * </m:MaterialCollapsibleBody> 070 * </m:MaterialCollapsibleItem> 071 * </m:MaterialCollapsible> 072 * 073 * // Popout 074 * <m:MaterialCollapsible type="POPOUT" grid="s12 m6 l8"> 075 * <!-- ITEM 1 --> 076 * <m:MaterialCollapsibleItem> 077 * <m:MaterialCollapsibleHeader> 078 * <m:MaterialLink text="First" iconType="POLYMER" iconPosition="LEFT" textColor="BLACK"/> 079 * </m:MaterialCollapsibleHeader> 080 * <m:MaterialCollapsibleBody> 081 * <m:MaterialLabel text="Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."/> 082 * </m:MaterialCollapsibleBody> 083 * </m:MaterialCollapsibleItem> 084 * </m:MaterialCollapsible> 085 * } 086 * </pre> 087 * 088 * @author kevzlou7979 089 * @author Ben Dol 090 * @see <a href="http://gwtmaterialdesign.github.io/gwt-material-demo/#collapsible">Material Collapsibles</a> 091 * @see <a href="https://material.io/guidelines/components/expansion-panels.html#expansion-panels-behavior">Material Design Specification</a> 092 */ 093//@formatter:on 094public class MaterialCollapsible extends MaterialWidget implements JsLoader, HasType<CollapsibleType>, HasActiveParent, HasNoSideNavSelection, HasClearActiveHandler { 095 096 protected interface HasCollapsibleParent { 097 void setParent(MaterialCollapsible parent); 098 } 099 100 private boolean accordion = true; 101 private int activeIndex = -1; 102 private Widget activeWidget; 103 104 private CssTypeMixin<CollapsibleType, MaterialCollapsible> typeMixin; 105 106 public MaterialCollapsible() { 107 super(Document.get().createULElement(), CssName.COLLAPSIBLE); 108 109 // Items need to be added after the widget has loaded to avoid 110 // premature configuration issues. 111 enableFeature(Feature.ONLOAD_ADD_QUEUE, true); 112 } 113 114 public MaterialCollapsible(final MaterialCollapsibleItem... widgets) { 115 this(); 116 117 for (final MaterialCollapsibleItem item : widgets) { 118 add(item); 119 } 120 } 121 122 @Override 123 protected void onLoad() { 124 super.onLoad(); 125 126 if (activeIndex != -1 && activeWidget == null) { 127 setActive(activeIndex); 128 } 129 130 load(); 131 } 132 133 @Override 134 public void load() { 135 collapsible(getElement()); 136 } 137 138 @Override 139 public void unload() { 140 } 141 142 @Override 143 public void reload() { 144 unload(); 145 load(); 146 } 147 148 @Override 149 public void add(final Widget child) { 150 if (child instanceof MaterialCollapsibleItem) { 151 ((MaterialCollapsibleItem) child).setParent(this); 152 } 153 super.add(child); 154 } 155 156 @Override 157 public boolean remove(Widget w) { 158 if (w instanceof MaterialCollapsibleItem) { 159 ((MaterialCollapsibleItem) w).setParent(null); 160 } 161 w.removeStyleName(CssName.ACTIVE); 162 163 return super.remove(w); 164 } 165 166 @Override 167 public void clearActive() { 168 clearActiveClass(this); 169 ClearActiveEvent.fire(this); 170 } 171 172 /** 173 * Open the given collapsible item. 174 * 175 * @param index the one-based collapsible item index. 176 */ 177 public void open(int index) { 178 setActive(index, true); 179 } 180 181 /** 182 * Close the given collapsible item. 183 * 184 * @param index the one-based collapsible item index. 185 */ 186 public void close(int index) { 187 setActive(index, false); 188 } 189 190 /** 191 * Close all the collapsible items. 192 */ 193 public void closeAll() { 194 clearActive(); 195 reload(); 196 } 197 198 @Override 199 public void setEnabled(boolean enabled) { 200 getEnabledMixin().setEnabled(this, enabled); 201 } 202 203 @Override 204 public void setType(CollapsibleType type) { 205 getTypeMixin().setType(type); 206 } 207 208 @Override 209 public CollapsibleType getType() { 210 return getTypeMixin().getType(); 211 } 212 213 protected void collapsible(final Element e) { 214 $(e).collapsible(isAccordion()); 215 } 216 217 /** 218 * Configure if you want this collapsible container to 219 * accordion its child elements or use expandable. 220 */ 221 public void setAccordion(boolean accordion) { 222 getElement().setAttribute("data-collapsible", accordion ? CssName.ACCORDION : CssName.EXPANDABLE); 223 reload(); 224 } 225 226 /** 227 * Is the collapsible an 'accordion' type. 228 */ 229 public boolean isAccordion() { 230 return getElement().getAttribute("data-collapsible").equals(CssName.ACCORDION); 231 } 232 233 @Override 234 public void setActive(int index) { 235 clearActive(); 236 setActive(index, true); 237 } 238 239 @Override 240 public void setActive(int index, boolean active) { 241 activeIndex = index; 242 if (isAttached()) { 243 if (index <= getWidgetCount()) { 244 if (index != 0) { 245 activeWidget = getWidget(index - 1); 246 if (activeWidget != null && activeWidget instanceof MaterialCollapsibleItem) { 247 ((MaterialCollapsibleItem) activeWidget).setActive(active); 248 reload(); 249 } 250 } else { 251 GWT.log("The active index must be a one-base index to mark as active.", new IndexOutOfBoundsException()); 252 } 253 } 254 } 255 } 256 257 @Override 258 public Widget getActive() { 259 try { 260 return activeWidget; 261 } catch (IndexOutOfBoundsException ex) { 262 return null; 263 } 264 } 265 266 @Override 267 public HandlerRegistration addClearActiveHandler(final ClearActiveHandler handler) { 268 return addHandler(handler, ClearActiveEvent.TYPE); 269 } 270 271 protected CssTypeMixin<CollapsibleType, MaterialCollapsible> getTypeMixin() { 272 if (typeMixin == null) { 273 typeMixin = new CssTypeMixin<>(this); 274 } 275 return typeMixin; 276 } 277}