001package org.gwtbootstrap3.client.ui;
002
003/*
004 * #%L
005 * GwtBootstrap3
006 * %%
007 * Copyright (C) 2013 GwtBootstrap3
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 org.gwtbootstrap3.client.shared.event.ModalHiddenEvent;
024import org.gwtbootstrap3.client.shared.event.ModalHiddenHandler;
025import org.gwtbootstrap3.client.shared.event.ModalHideEvent;
026import org.gwtbootstrap3.client.shared.event.ModalHideHandler;
027import org.gwtbootstrap3.client.shared.event.ModalShowEvent;
028import org.gwtbootstrap3.client.shared.event.ModalShowHandler;
029import org.gwtbootstrap3.client.shared.event.ModalShownEvent;
030import org.gwtbootstrap3.client.shared.event.ModalShownHandler;
031import org.gwtbootstrap3.client.ui.base.helper.StyleHelper;
032import org.gwtbootstrap3.client.ui.base.modal.ModalContent;
033import org.gwtbootstrap3.client.ui.base.modal.ModalDialog;
034import org.gwtbootstrap3.client.ui.constants.Attributes;
035import org.gwtbootstrap3.client.ui.constants.ModalBackdrop;
036import org.gwtbootstrap3.client.ui.constants.Styles;
037import org.gwtbootstrap3.client.ui.html.Div;
038
039import com.google.gwt.dom.client.Element;
040import com.google.gwt.user.client.Event;
041import com.google.gwt.user.client.ui.RootPanel;
042import com.google.gwt.user.client.ui.Widget;
043import com.google.web.bindery.event.shared.HandlerRegistration;
044
045/**
046 * Modal dialog.
047 * <p/>
048 * <h3>UiBinder example</h3>
049 * <p/>
050 * <pre>
051 * {@code
052 *     <b:Modal title="Important information" b:id="modal1">
053 *         <b:ModalBody>
054 *             <g:HTML>Lorem ipsum...</g:HTML>
055 *         </b:ModalBody>
056 *         <b:ModalFooter>
057 *             <b:Button type="PRIMARY">Do something</b:Button>
058 *             <b:Button type="DANGER" dismiss="MODAL">Close</b:Button>
059 *         </b:ModalFooter>
060 *     </b:Modal>
061 *     <b:Button target="#modal1" toggle="MODAL">Show modal</b:Button>
062 * }
063 * </pre>
064 * <p/>
065 * It's also possible to specify a custom modal header:
066 * <p/>
067 * <pre>
068 * {@code
069 *     <b:Modal>
070 *         <b:ModalHeader>
071 *             <g:HTML>
072 *                 <h4>Custom header</h4>
073 *             </g:HTML>
074 *         </b:ModalHeader>
075 *         ...
076 *     </b:Modal>
077 * }
078 * </pre>
079 *
080 * @author Sven Jacobs
081 * @author Joshua Godi
082 * @see ModalHeader
083 * @see ModalBody
084 * @see ModalFooter
085 * @see org.gwtbootstrap3.client.shared.event.ModalShowEvent
086 * @see org.gwtbootstrap3.client.shared.event.ModalShownEvent
087 * @see org.gwtbootstrap3.client.shared.event.ModalHideEvent
088 * @see org.gwtbootstrap3.client.shared.event.ModalHiddenEvent
089 */
090public class Modal extends Div implements IsClosable {
091
092    private final static String TOGGLE = "toggle";
093    private final static String HIDE = "hide";
094    private final static String SHOW = "show";
095
096    private final ModalContent content = new ModalContent();
097    private final ModalDialog dialog = new ModalDialog();
098    private ModalHeader header = new ModalHeader();
099
100    private HandlerRegistration removeOnHideHandlerReg = null;
101
102    private boolean hideOtherModals = false;
103
104    public Modal() {
105        setStyleName(Styles.MODAL);
106        
107        // Set the z-index to match bootstrap's .modal
108        getElement().getStyle().setZIndex(1050);
109
110        content.add(header);
111        dialog.add(content);
112
113        add(dialog);
114    }
115
116    @Override
117    public void setWidth(final String width) {
118        dialog.setWidth(width);
119    }
120
121    public void setSize(ModalSize size) {
122        StyleHelper.addUniqueEnumStyleName(dialog, ModalSize.class, size);
123    }
124
125    @Override
126    protected void onLoad() {
127        super.onLoad();
128        bindJavaScriptEvents(getElement());
129    }
130
131    @Override
132    protected void onUnload() {
133        super.onUnload();
134        unbindAllHandlers(getElement());
135    }
136
137    @Override
138    public void add(final Widget w) {
139        // User can supply own ModalHeader
140        if (w instanceof ModalHeader) {
141            header.removeFromParent();
142            header = (ModalHeader) w;
143        }
144
145        if (w instanceof ModalComponent) {
146            content.add(w);
147        } else {
148            super.add(w);
149        }
150    }
151
152    @Override
153    public void setTitle(final String title) {
154        header.setTitle(title);
155    }
156
157    @Override
158    public void setClosable(final boolean closable) {
159        header.setClosable(closable);
160    }
161
162    @Override
163    public boolean isClosable() {
164        return header.isClosable();
165    }
166
167    /**
168     * If set to true, when the modal is shown it will force hide all other modals
169     *
170     * @param hideOtherModals - true to force hide other modals, false to keep them shown
171     */
172    public void setHideOtherModals(final boolean hideOtherModals) {
173        this.hideOtherModals = hideOtherModals;
174    }
175
176    /**
177     * If set to true, will remove the modal from the DOM completely and unbind any events to the modal
178     *
179     * @param removeOnHide - true to remove modal and unbind events on hide, false to keep it in the DOM
180     */
181    public void setRemoveOnHide(final boolean removeOnHide) {
182        if (removeOnHideHandlerReg != null) {
183            removeOnHideHandlerReg.removeHandler();
184            removeOnHideHandlerReg = null;
185        }
186        if (removeOnHide) {
187            removeOnHideHandlerReg = addHiddenHandler(new ModalHiddenHandler() {
188                @Override
189                public void onHidden(final ModalHiddenEvent evt) {
190                    // Do logical detach
191                    removeFromParent();
192                }
193            });
194        }
195    }
196
197    /**
198     * If set Modal will fade in/out.
199     *
200     * @param fade If {@code true} modal will fade in/out
201     */
202    public void setFade(final boolean fade) {
203        if (fade) {
204            addStyleName(Styles.FADE);
205        } else {
206            removeStyleName(Styles.FADE);
207        }
208    }
209
210    /**
211     * Sets backdrop of modal.
212     *
213     * @param backdrop Backdrop of modal
214     * @see org.gwtbootstrap3.client.ui.constants.ModalBackdrop
215     */
216    public void setDataBackdrop(final ModalBackdrop backdrop) {
217        if (backdrop != null) {
218            getElement().setAttribute(Attributes.DATA_BACKDROP, backdrop.getBackdrop());
219        } else {
220            getElement().removeAttribute(Attributes.DATA_BACKDROP);
221        }
222    }
223
224    public void setDataKeyboard(final boolean keyboard) {
225        getElement().setAttribute(Attributes.DATA_KEYBOARD, Boolean.toString(keyboard));
226
227        // tabindex must be set to -1 for ESC key to work
228        if (keyboard) {
229            getElement().setAttribute(Attributes.TABINDEX, "-1");
230        }
231    }
232
233    public void toggle() {
234        modal(getElement(), TOGGLE);
235    }
236
237    public void show() {
238        checkIsAttached();
239        modal(getElement(), SHOW);
240    }
241
242    public void hide() {
243        modal(getElement(), HIDE);
244    }
245
246    public HandlerRegistration addShowHandler(final ModalShowHandler modalShowHandler) {
247        return addHandler(modalShowHandler, ModalShowEvent.getType());
248    }
249
250    public HandlerRegistration addShownHandler(final ModalShownHandler modalShownHandler) {
251        return addHandler(modalShownHandler, ModalShownEvent.getType());
252    }
253
254    public HandlerRegistration addHideHandler(final ModalHideHandler modalHideHandler) {
255        return addHandler(modalHideHandler, ModalHideEvent.getType());
256    }
257
258    public HandlerRegistration addHiddenHandler(final ModalHiddenHandler modalHiddenHandler) {
259        return addHandler(modalHiddenHandler, ModalHiddenEvent.getType());
260    }
261
262    /**
263     * Can be override by subclasses to handle Modal's "show" event however it's
264     * recommended to add an event handler to the modal.
265     *
266     * @param evt Event
267     * @see org.gwtbootstrap3.client.shared.event.ModalShowEvent
268     */
269    protected void onShow(final Event evt) {
270        if (hideOtherModals) {
271            hideOtherModals();
272        }
273        fireEvent(new ModalShowEvent(this, evt));
274    }
275
276    /**
277     * Can be override by subclasses to handle Modal's "shown" event however
278     * it's recommended to add an event handler to the modal.
279     *
280     * @param evt Event
281     * @see org.gwtbootstrap3.client.shared.event.ModalShownEvent
282     */
283    protected void onShown(final Event evt) {
284        fireEvent(new ModalShownEvent(this, evt));
285    }
286
287    /**
288     * Can be override by subclasses to handle Modal's "hide" event however it's
289     * recommended to add an event handler to the modal.
290     *
291     * @param evt Event
292     * @see org.gwtbootstrap3.client.shared.event.ModalHideEvent
293     */
294    protected void onHide(final Event evt) {
295        fireEvent(new ModalHideEvent(this, evt));
296    }
297
298    /**
299     * Can be override by subclasses to handle Modal's "hidden" event however
300     * it's recommended to add an event handler to the modal.
301     *
302     * @param evt Event
303     * @see org.gwtbootstrap3.client.shared.event.ModalHiddenEvent
304     */
305    protected void onHidden(final Event evt) {
306        fireEvent(new ModalHiddenEvent(this, evt));
307    }
308
309    private void checkIsAttached() {
310        if (!this.isAttached()) {
311            RootPanel.get().add(this);
312        }
313    }
314
315    private native void bindJavaScriptEvents(final Element e) /*-{
316        var target = this;
317        var $modal = $wnd.jQuery(e);
318
319        $modal.on('show.bs.modal', function (evt) {
320            target.@org.gwtbootstrap3.client.ui.Modal::onShow(Lcom/google/gwt/user/client/Event;)(evt);
321        });
322
323        $modal.on('shown.bs.modal', function (evt) {
324            target.@org.gwtbootstrap3.client.ui.Modal::onShown(Lcom/google/gwt/user/client/Event;)(evt);
325        });
326
327        $modal.on('hide.bs.modal', function (evt) {
328            target.@org.gwtbootstrap3.client.ui.Modal::onHide(Lcom/google/gwt/user/client/Event;)(evt);
329        });
330
331        $modal.on('hidden.bs.modal', function (evt) {
332            target.@org.gwtbootstrap3.client.ui.Modal::onHidden(Lcom/google/gwt/user/client/Event;)(evt);
333        });
334    }-*/;
335
336    private native void modal(final Element e, final String arg) /*-{
337        $wnd.jQuery(e).modal(arg);
338    }-*/;
339
340    // Will iterate over all the modals, if they are visible it will hide them
341    private native void hideOtherModals() /*-{
342        $wnd.jQuery('.modal.in').modal('hide');
343    }-*/;
344
345    // Unbinds all the handlers
346    private native void unbindAllHandlers(final Element e) /*-{
347        var $e = $wnd.jQuery(e);
348        $e.off('show.bs.modal');
349        $e.off('shown.bs.modal');
350        $e.off('hide.bs.modal');
351        $e.off('hidden.bs.modal');
352    }-*/;
353}