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.Document;
024import com.google.gwt.dom.client.Element;
025import com.google.gwt.user.client.ui.Widget;
026import com.google.web.bindery.event.shared.HandlerRegistration;
027import gwt.material.design.client.base.HasSelectables;
028import gwt.material.design.client.base.MaterialWidget;
029import gwt.material.design.client.constants.CollapsibleType;
030import gwt.material.design.client.events.ClearActiveEvent;
031import gwt.material.design.client.events.ClearActiveEvent.ClearActiveHandler;
032
033//@formatter:off
034
035/**
036 * Collapsibles are accordion elements that expand when clicked on.
037 * They allow you to hide content that is not immediately relevant
038 * to the user.
039 *
040 * <h3>UiBinder Usage:</h3>
041 *
042 * <pre>
043 *{@code
044 * // Accordion
045 * <m:MaterialCollapsible type="ACCORDION" grid="s12 m6 l8">
046 *   <!-- ITEM 1 -->
047 *   <m:MaterialCollapsibleItem>
048 *     <m:MaterialCollapsibleHeader>
049 *       <m:MaterialLink text="First" iconType="POLYMER" iconPosition="LEFT" textColor="black"/>
050 *     </m:MaterialCollapsibleHeader>
051 *     <m:MaterialCollapsibleBody>
052 *       <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."/>
053 *     </m:MaterialCollapsibleBody>
054 *   </m:MaterialCollapsibleItem>
055 * </m:MaterialCollapsible>
056 *
057 * // Expandable
058 * <m:MaterialCollapsible type="EXPANDABLE" grid="s12 m6 l8">
059 *   <!-- ITEM 1 -->
060 *   <m:MaterialCollapsibleItem>
061 *     <m:MaterialCollapsibleHeader>
062 *       <m:MaterialLink text="First" iconType="POLYMER" iconPosition="LEFT" textColor="black"/>
063 *     </m:MaterialCollapsibleHeader>
064 *     <m:MaterialCollapsibleBody>
065 *       <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."/>
066 *     </m:MaterialCollapsibleBody>
067 *   </m:MaterialCollapsibleItem>
068 * </m:MaterialCollapsible>
069 *
070 * // Popout
071 * <m:MaterialCollapsible type="POPOUT" grid="s12 m6 l8">
072 *   <!-- ITEM 1 -->
073 *     <m:MaterialCollapsibleItem>
074 *     <m:MaterialCollapsibleHeader>
075 *       <m:MaterialLink text="First" iconType="POLYMER" iconPosition="LEFT" textColor="black"/>
076 *     </m:MaterialCollapsibleHeader>
077 *     <m:MaterialCollapsibleBody>
078 *       <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."/>
079 *     </m:MaterialCollapsibleBody>
080 *   </m:MaterialCollapsibleItem>
081 * </m:MaterialCollapsible>
082 * }
083 * </pre>
084 *
085 * @author kevzlou7979
086 * @author Ben Dol
087 * @see <a href="http://gwt-material-demo.herokuapp.com/#collapsibles">Material Collapsibles</a>
088 */
089//@formatter:on
090public class MaterialCollapsible extends MaterialWidget implements HasSelectables {
091
092    protected interface HasCollapsibleParent {
093        void setParent(MaterialCollapsible parent);
094    }
095
096    private boolean initialized;
097    private boolean accordion = true;
098
099    private int activeIndex = -1;
100    private Widget activeWidget;
101
102    /**
103     * Creates an empty collapsible
104     */
105    public MaterialCollapsible() {
106        super(Document.get().createULElement(), "collapsible");
107
108        enableFeature(Feature.ONLOAD_ADD_QUEUE, true);
109    }
110
111    /**
112     *  Creates a list and adds the given widgets.
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        // Setup the expansion type
127        if (isAccordion()) {
128            getElement().setAttribute("data-collapsible", "accordion");
129        } else {
130            getElement().setAttribute("data-collapsible", "expandable");
131        }
132
133        // Activate preset activation index
134        if(activeIndex != -1 && activeWidget == null) {
135            setActive(activeIndex);
136        }
137
138        // Initialize collapsible after all elements
139        // are attached and marked as active, etc.
140        initCollapsible(getElement(), accordion);
141    }
142
143    @Override
144    public void add(final Widget child) {
145        if(child instanceof MaterialCollapsibleItem) {
146            ((MaterialCollapsibleItem) child).setParent(this);
147        }
148        super.add(child);
149    }
150
151    @Override
152    public boolean remove(Widget w) {
153        if(w instanceof MaterialCollapsibleItem) {
154            ((MaterialCollapsibleItem) w).setParent(null);
155        }
156        w.removeStyleName("active");
157
158        return super.remove(w);
159    }
160
161    /**
162     * Initialize the collapsible material component.
163     */
164    protected void initCollapsible() {
165        initCollapsible(getElement(), isAccordion());
166    }
167
168    /**
169     * Initialize the collapsible material component.
170     */
171    protected native void initCollapsible(final Element e, boolean accordion) /*-{
172        $wnd.jQuery(document).ready(function() {
173            $wnd.jQuery(e).collapsible({
174                accordion: accordion
175            });
176        });
177        this.@gwt.material.design.client.ui.MaterialCollapsible::initialized = true;
178    }-*/;
179
180    public void setType(CollapsibleType type) {
181        switch (type) {
182            case POPOUT:
183                addStyleName(type.getCssName());
184                break;
185            default:
186                break;
187        }
188    }
189
190    /**
191     * Configure if you want this collapsible container to
192     * accordion its child elements or use expandable.
193     */
194    public void setAccordion(boolean accordion) {
195        this.accordion = accordion;
196
197        if(initialized) {
198            // Since we have attached already reinitialize collapsible.
199            initCollapsible();
200        }
201    }
202
203    public boolean isAccordion() {
204        return accordion;
205    }
206
207    /**
208     * Providing the one-based index of the
209     * {@link MaterialCollapsibleItem} to mark as active.
210     */
211    public void setActive(int index) {
212        clearActive();
213        activeIndex = index;
214        if(isAttached()) {
215            if(index <= getWidgetCount()) {
216                activeWidget = getWidget(index - 1);
217                if (activeWidget != null && activeWidget instanceof MaterialCollapsibleItem) {
218                    ((MaterialCollapsibleItem) activeWidget).setActive(true);
219                    if(initialized) {
220                        initCollapsible();
221                    }
222                }
223            }
224        }
225    }
226
227    public HandlerRegistration addClearActiveHandler(final ClearActiveHandler handler) {
228        return addHandler(new ClearActiveHandler() {
229            @Override
230            public void onClearActive(ClearActiveEvent event) {
231                if(isEnabled()){
232                    handler.onClearActive(event);
233                }
234            }
235        }, ClearActiveEvent.TYPE);
236    }
237
238    @Override
239    public void clearActive() {
240        clearActiveClass(this);
241        ClearActiveEvent.fire(this);
242    }
243}