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.dom.client.Document; 023import com.google.gwt.dom.client.Style; 024import com.google.gwt.event.dom.client.ClickEvent; 025import com.google.gwt.user.client.Window; 026import com.google.gwt.user.client.ui.HasWidgets; 027import com.google.gwt.user.client.ui.Widget; 028import gwt.material.design.client.base.MaterialWidget; 029import gwt.material.design.client.constants.CssName; 030import gwt.material.design.client.ui.MaterialCollapsible.HasCollapsibleParent; 031import gwt.material.design.client.ui.html.ListItem; 032import gwt.material.design.client.ui.html.UnorderedList; 033 034//@formatter:off 035 036/** 037 * CollapsibleItem element to define the body 038 * 039 * @author kevzlou7979 040 * @author Ben Dol 041 * @see <a href="http://gwtmaterialdesign.github.io/gwt-material-demo/#collapsible">Material Collapsibles</a> 042 * @see <a href="https://material.io/guidelines/components/expansion-panels.html#expansion-panels-behavior">Material Design Specification</a> 043 */ 044//@formatter:on 045public class MaterialCollapsibleBody extends MaterialWidget implements HasCollapsibleParent { 046 047 private MaterialCollapsible parent; 048 049 /** 050 * Creates empty collapsible body. 051 */ 052 public MaterialCollapsibleBody() { 053 super(Document.get().createDivElement(), CssName.COLLAPSIBLE_BODY); 054 } 055 056 /** 057 * Add other components into collapsible body. 058 */ 059 public MaterialCollapsibleBody(final Widget... widgets) { 060 this(); 061 for (Widget w : widgets) { 062 add(w); 063 } 064 } 065 066 @Override 067 public void setParent(MaterialCollapsible parent) { 068 this.parent = parent; 069 070 for (Widget child : this) { 071 checkActiveState(child); 072 073 if (child instanceof HasCollapsibleParent) { 074 ((HasCollapsibleParent) child).setParent(parent); 075 } 076 } 077 } 078 079 @Override 080 public void add(final Widget child) { 081 if (child instanceof UnorderedList) { 082 for (Widget w : (UnorderedList) child) { 083 if (w instanceof ListItem) { 084 w.getElement().getStyle().setDisplay(Style.Display.BLOCK); 085 086 provideActiveClickHandler(w); 087 } 088 } 089 } else if (child instanceof ListItem) { 090 child.getElement().getStyle().setDisplay(Style.Display.BLOCK); 091 092 provideActiveClickHandler(child); 093 } 094 super.add(child); 095 } 096 097 @Override 098 public boolean remove(Widget w) { 099 if (w instanceof HasCollapsibleParent) { 100 ((HasCollapsibleParent) w).setParent(null); 101 } 102 return super.remove(w); 103 } 104 105 public void makeActive(Widget child) { 106 parent.clearActive(); 107 108 // mark the collapsible item as active 109 MaterialCollapsibleItem item = findCollapsibleItemParent(child); 110 if (item != null) { 111 item.expand(); 112 } 113 114 child.addStyleName(CssName.ACTIVE); 115 } 116 117 protected void provideActiveClickHandler(final Widget child) { 118 // Active click handler 119 registerHandler(child.addDomHandler(event -> makeActive(child), ClickEvent.getType())); 120 } 121 122 /** 123 * Checks if this child holds the current active state. 124 * If the child is or contains the active state it is applied. 125 */ 126 protected void checkActiveState(Widget child) { 127 // Check if this widget has a valid href 128 String href = child.getElement().getAttribute("href"); 129 String url = Window.Location.getHref(); 130 int pos = url.indexOf("#"); 131 String location = pos >= 0 ? url.substring(pos, url.length()) : ""; 132 133 if (!href.isEmpty() && location.startsWith(href)) { 134 ListItem li = findListItemParent(child); 135 if (li != null) { 136 makeActive(li); 137 } 138 } else if (child instanceof HasWidgets) { 139 // Recursive check 140 for (Widget w : (HasWidgets) child) { 141 checkActiveState(w); 142 } 143 } 144 } 145 146 protected MaterialCollapsibleItem findCollapsibleItemParent(Widget widget) { 147 if (widget != null) { 148 if (widget instanceof MaterialCollapsibleItem) { 149 return (MaterialCollapsibleItem) widget; 150 } else { 151 return findCollapsibleItemParent(widget.getParent()); 152 } 153 } 154 return null; 155 } 156 157 protected ListItem findListItemParent(Widget widget) { 158 if (widget != null) { 159 if (widget instanceof ListItem) { 160 return (ListItem) widget; 161 } else { 162 return findListItemParent(widget.getParent()); 163 } 164 } 165 return null; 166 } 167}