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.addins.client.dnd;
021
022import com.google.gwt.dom.client.Element;
023import com.google.gwt.user.client.ui.UIObject;
024import gwt.material.design.addins.client.MaterialAddins;
025import gwt.material.design.addins.client.dnd.constants.DragEvents;
026import gwt.material.design.addins.client.dnd.constants.DropEvents;
027import gwt.material.design.addins.client.dnd.js.JsDnd;
028import gwt.material.design.addins.client.dnd.js.JsDragOptions;
029import gwt.material.design.addins.client.dnd.js.JsDropOptions;
030import gwt.material.design.client.MaterialDesignBase;
031import gwt.material.design.client.base.MaterialWidget;
032import gwt.material.design.client.events.*;
033import gwt.material.design.jquery.client.api.Event;
034import gwt.material.design.jquery.client.api.JQuery;
035
036//@formatter:off
037
038/**
039 * Drag and drop feature on Material Design specs are great UX guide to
040 * provide a delightful motion on dragging and dropping gestures.
041 * <p>
042 * <h3>Java Usage</h3>
043 * <pre>
044 * {@code
045 *
046 * MaterialDnd dnd = new MaterialDnd();
047 * // Set the draggable object
048 * dnd.setDraggable(widget);
049 *
050 * // Set the ignored widget when dragging the element
051 * dnd.setIgnoreFrom(toolbar);
052 *
053 * }
054 * </pre>
055 *
056 * @author kevzlou7979
057 * @see <a href="http://gwtmaterialdesign.github.io/gwt-material-demo/#dnd">Drag and Drop</a>
058 * @see <a href="https://github.com/taye/interact.js">InteractJs 1.2.6</a>
059 */
060//@formatter:on
061public class MaterialDnd {
062
063    static {
064        if (MaterialAddins.isDebug()) {
065            MaterialDesignBase.injectDebugJs(MaterialDndDebugClientBundle.INSTANCE.dndDebugJs());
066        } else {
067            MaterialDesignBase.injectJs(MaterialDndClientBundle.INSTANCE.dndJs());
068        }
069    }
070
071    private JsDnd jsDnd;
072    private final MaterialWidget target;
073    private Element[] ignoreFrom;
074    private JsDropOptions dropOptions;
075    private JsDragOptions dragOptions;
076
077    protected MaterialDnd(MaterialWidget target) {
078        this.target = target;
079    }
080
081    protected MaterialDnd draggable() {
082        if (jsDnd == null) {
083            jsDnd = JsDnd.interact(target.getElement());
084
085            // Events
086            jsDnd.off(DragEvents.DRAG_MOVE).on(DragEvents.DRAG_MOVE, event -> {
087                move(event);
088                DragMoveEvent.fire(this.target);
089                return true;
090            });
091            jsDnd.off(DragEvents.DRAG_START).on(DragEvents.DRAG_START, event -> {
092                DragStartEvent.fire(this.target);
093                return true;
094            });
095            jsDnd.off(DragEvents.DRAG_END).on(DragEvents.DRAG_END, event -> {
096                DragEndEvent.fire(this.target);
097                return true;
098            });
099        }
100        jsDnd.draggable(dragOptions);
101        return this;
102    }
103
104    public MaterialDnd draggable(JsDragOptions options) {
105        dragOptions = options;
106        if (target.isAttached()) {
107            draggable();
108        } else {
109            target.registerHandler(target.addAttachHandler(event -> draggable(), true));
110        }
111        return this;
112    }
113
114    public static MaterialDnd draggable(MaterialWidget target) {
115        return draggable(target, JsDragOptions.create());
116    }
117
118    public static MaterialDnd draggable(MaterialWidget target, JsDragOptions options) {
119        return new MaterialDnd(target).draggable(options);
120    }
121
122    protected MaterialDnd dropzone() {
123        if (jsDnd == null) {
124            jsDnd = JsDnd.interact(target.getElement());
125
126            // Events
127            jsDnd.off(DropEvents.DROP_ACTIVATE).on(DropEvents.DROP_ACTIVATE, event -> {
128                DropActivateEvent.fire(this.target);
129                return true;
130            });
131            jsDnd.off(DragEvents.DRAG_ENTER).on(DragEvents.DRAG_ENTER, event -> {
132                DragEnterEvent.fire(this.target, event.getRelatedTarget());
133                return true;
134            });
135            jsDnd.off(DragEvents.DRAG_LEAVE).on(DragEvents.DRAG_LEAVE, event -> {
136                DragLeaveEvent.fire(this.target, event.getRelatedTarget());
137                return true;
138            });
139            jsDnd.off(DropEvents.DROP).on(DropEvents.DROP, event -> {
140                DropEvent.fire(this.target, event.getRelatedTarget());
141                return true;
142            });
143            jsDnd.off(DropEvents.DROP_DEACTIVATE).on(DropEvents.DROP_DEACTIVATE, event -> {
144                DropDeactivateEvent.fire(this.target);
145                return true;
146            });
147        }
148
149        jsDnd.dropzone(dropOptions);
150        return this;
151    }
152
153    public MaterialDnd dropzone(JsDropOptions options) {
154        dropOptions = options;
155        if (target.isAttached()) {
156            dropzone();
157        } else {
158            target.registerHandler(target.addAttachHandler(event -> dropzone(), true));
159        }
160        return this;
161    }
162
163    public static MaterialDnd dropzone(MaterialWidget target) {
164        return dropzone(target, JsDropOptions.create());
165    }
166
167    public static MaterialDnd dropzone(MaterialWidget target, JsDropOptions options) {
168        return new MaterialDnd(target).dropzone(options);
169    }
170
171    public void ignoreFrom(UIObject uiObject) {
172        ignoreFrom(uiObject.getElement());
173    }
174
175    public void ignoreFrom(Element... elements) {
176        this.ignoreFrom = elements;
177        if (target.isAttached()) {
178            for (Element element : ignoreFrom) {
179                JsDnd.interact(target.getElement()).ignoreFrom(element);
180            }
181        } else {
182            target.registerHandler(target.addAttachHandler(event -> {
183                for (Element element : ignoreFrom) {
184                    JsDnd.interact(target.getElement()).ignoreFrom(element);
185                }
186            }, true));
187        }
188    }
189
190    public void ignoreFrom(String selector) {
191        this.ignoreFrom = new Element[]{JQuery.$(selector).asElement()};
192        if (target.isAttached()) {
193            JsDnd.interact(target.getElement()).ignoreFrom(selector);
194        } else {
195            target.registerHandler(target.addAttachHandler(event -> JsDnd.interact(target.getElement()).ignoreFrom(selector), true));
196        }
197    }
198
199    public MaterialWidget getTarget() {
200        return target;
201    }
202
203    public Element[] getIgnoreFrom() {
204        return ignoreFrom;
205    }
206
207    public JsDropOptions getDropOptions() {
208        return dropOptions;
209    }
210
211    public JsDragOptions getDragOptions() {
212        return dragOptions;
213    }
214
215    public static native void move(Event event) /*-{
216        var target = event.target,
217            x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx,
218            y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy;
219
220        target.style.webkitTransform =
221            target.style.transform =
222                'translate(' + x + 'px, ' + y + 'px)';
223
224        target.setAttribute('data-x', x);
225        target.setAttribute('data-y', y);
226    }-*/;
227}