1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
use crate::closure;
#[cfg(any(feature = "js-sys", test))]
use js_sys::Array;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
#[wasm_bindgen]
extern "C" {
/// Interface provided by Dragula to interact with active drag-and-drop system
///
/// ### Example:
#[cfg_attr(feature = "js-sys", doc = "```no_run")]
#[cfg_attr(not(feature = "js-sys"), doc = "```no_compile")]
/// use dragula::*;
///
/// let doc = web_sys::window().unwrap().document().unwrap();
/// let element_1 = doc.get_element_by_id("drag-container-1").unwrap();
/// let element_2 = doc.get_element_by_id("drag-container-2").unwrap();
///
/// let mut drake = dragula(&[element_1]);
///
/// drake.add_container(element_2);
///
/// ```
#[wasm_bindgen(js_name = Drake)]
pub type Drake;
/// This property will be `true` whenever an element is being dragged.
#[wasm_bindgen(method, getter)]
pub fn dragging(this: &Drake) -> bool;
/// Gracefully end the drag event as if using **the last position marked by
/// the preview shadow** as the drop target. The proper `cancel` or `drop`
/// event will be fired, depending on whether the item was dropped back
/// where it was originally lifted from _(which is essentially a no-op
/// that's treated as a `cancel` event)_.
#[wasm_bindgen(method)]
pub fn end(this: &Drake);
/// If an element managed by `Drake` is currently being dragged, this method
/// will gracefully remove it from the DOM.
#[wasm_bindgen(method)]
pub fn remove(this: &Drake);
/// Removes all drag and drop events used by `dragula` to manage drag and
/// drop between the `containers`. If `destroy` is called while an element
/// is being dragged, the drag will be effectively cancelled.
#[wasm_bindgen(method)]
pub fn destroy(this: &Drake);
#[wasm_bindgen(method)]
fn on(this: &Drake, event_type: &str, listener: JsValue);
/// If an element managed by `Drake` is currently being dragged, this method
/// will gracefully cancel the drag action.
///
/// Note that **a _"cancellation"_ will result in a `cancel` event** only in
/// the following scenarios.
///
/// - `revert_on_spill` is `true`
/// - Drop target _(as previewed by the feedback shadow)_ is the source
/// container **and** the item is dropped in the same position where it
/// was originally dragged from
#[wasm_bindgen(method)]
pub fn cancel(this: &Drake);
/// If an element managed by `Drake` is currently being dragged, this method
/// will gracefully cancel the drag action. If `true` is passed to this
/// function, it will effectively produce the same result as if
/// `revert_on_spill` is true.
///
/// - `revert_on_spill` is `true`
/// - Drop target _(as previewed by the feedback shadow)_ is the source
/// container **and** the item is dropped in the same position where it
/// was originally dragged from
#[wasm_bindgen(method, js_name = cancel)]
pub fn cancel_with_revert(this: &Drake, revert: bool);
#[wasm_bindgen(method, getter = containers)]
fn containers_getter_impl(this: &Drake) -> JsValue;
#[wasm_bindgen(method, setter = containers)]
fn containers_setter_impl(this: &Drake, val: Box<[JsValue]>);
#[wasm_bindgen(method, js_name = start)]
fn start_impl(this: &Drake, item: JsValue);
#[wasm_bindgen(method, js_name = canMove)]
fn can_move_impl(this: &Drake, item: JsValue) -> bool;
}
impl Drake {
/// Gets the active containers currently allowing dragging
///
/// Requires that feature `js-sys` be turned on (it is on by default)
#[cfg(any(feature = "js-sys", test))]
pub fn containers(&self) -> Vec<JsValue> {
let containers = self.containers_getter_impl();
let containers = Array::from(&containers);
containers.to_vec()
}
/// Sets the list of active containers for dragging. This overrides the
/// list that is currently there.
pub fn set_containers<T>(&mut self, objs: &[T])
where
T: JsCast + Clone,
{
let obj_array =
objs.iter().cloned().map(|o| JsValue::from(&o)).collect();
self.containers_setter_impl(obj_array);
}
/// Adds to the list of active containers for dragging
///
/// Requires that feature `js-sys` be turned on (it is on by default)
#[cfg(feature = "js-sys")]
pub fn add_container<T>(&mut self, obj: T)
where
T: JsCast,
{
let mut containers = self.containers();
let container_to_add = JsValue::from(&obj);
containers.push(container_to_add);
self.set_containers(&containers);
}
/// Enter drag mode **without a shadow**. This function is most useful when
/// providing complementary keyboard shortcuts to an existing drag and drop
/// solution. Even though a shadow won't be created at first, the user will
/// get one as soon as they click on `item` and start dragging it around.
/// Note that if they click and drag something else, `end` will be called
/// before picking up the new item.
pub fn start<T>(&mut self, item: &T)
where
T: JsCast,
{
let item = JsValue::from(item);
self.start_impl(item);
}
/// Returns whether the `Drake` instance can accept drags for a DOM element
/// `item`. This function returns `true` when all the conditions outlined
/// below are met, and `false` otherwise.
///
/// - `item` is a child of one of the specified containers for `Drake`
/// - `item` passes the pertinent [`invalid`](crate::Options::invalid) checks
/// - `item` passes a `moves` check
pub fn can_move<T>(&self, item: &T) -> bool
where
T: JsCast,
{
let item = JsValue::from(item);
self.can_move_impl(item)
}
/// Sets callback for `drag` event.
/// Callback will be passed arguments `(el, source)`
/// The `drag` event implies that
/// `el` was lifted from `source`.
pub fn on_drag<F: 'static>(&mut self, listener: F)
where
F: FnMut(JsValue, JsValue),
{
const EVENT_NAME: &str = "drag";
let listener = closure::to_js_2(listener);
self.on(EVENT_NAME, listener);
}
/// Sets callback for `dragend` event.
/// Callback will be passed argument `(el)`
/// The `dragend` event implies that
/// dragging event for `el` ended with either `cancel`, `remove`, or `drop`.
pub fn on_dragend<F: 'static>(&mut self, listener: F)
where
F: FnMut(JsValue),
{
const EVENT_NAME: &str = "dragend";
let listener = closure::to_js_1(listener);
self.on(EVENT_NAME, listener);
}
/// Sets callback for `drop` event.
/// Callback will be passed arguments `(el, target, source, sibling)`
/// The `drop` event implies that
/// `el` was dropped into `target` before a `sibling` element, and
/// originally came from `source`.
pub fn on_drop<F: 'static>(&mut self, listener: F)
where
F: FnMut(JsValue, JsValue, JsValue, JsValue),
{
const EVENT_NAME: &str = "drop";
let listener = closure::to_js_4(listener);
self.on(EVENT_NAME, listener);
}
/// Sets callback for `cancel` event.
/// Callback will be passed argument `(el, container, source)`
/// The `cancel` event implies that
/// `el` was being dragged but it got nowhere and went back into
/// `container`, its last stable parent; `el` originally came from `source`.
pub fn on_cancel<F: 'static>(&mut self, listener: F)
where
F: FnMut(JsValue, JsValue, JsValue),
{
const EVENT_NAME: &str = "cancel";
let listener = closure::to_js_3(listener);
self.on(EVENT_NAME, listener);
}
/// Sets callback for `remove` event.
/// Callback will be passed argument `(el, container, source)`
/// The `remove` event implies that
/// `el` was being dragged but it got nowhere and it was removed from the
/// DOM. Its last stable parent was `container`, and originally came from
/// `source`.
pub fn on_remove<F: 'static>(&mut self, listener: F)
where
F: FnMut(JsValue, JsValue, JsValue),
{
const EVENT_NAME: &str = "remove";
let listener = closure::to_js_3(listener);
self.on(EVENT_NAME, listener);
}
/// Sets callback for `shadow` event.
/// Callback will be passed argument `(el, container, source)`
/// The `shadow` event implies that
/// `el`, _the visual aid shadow_, was moved into `container`. May trigger
/// many times as the position of `el` changes, even within the same
/// `container`; `el` originally came from `source`.
pub fn on_shadow<F: 'static>(&mut self, listener: F)
where
F: FnMut(JsValue, JsValue, JsValue),
{
const EVENT_NAME: &str = "shadow";
let listener = closure::to_js_3(listener);
self.on(EVENT_NAME, listener);
}
/// Sets callback for `over` event.
/// Callback will be passed argument `(el, container, source)`
/// The `over` event implies that
/// `el` is over `container`, and originally came from `source`.
pub fn on_over<F: 'static>(&mut self, listener: F)
where
F: FnMut(JsValue, JsValue, JsValue),
{
const EVENT_NAME: &str = "over";
let listener = closure::to_js_3(listener);
self.on(EVENT_NAME, listener);
}
/// Sets callback for `out` event.
/// Callback will be passed argument `(el, container, source)`
/// The `out` event implies that
/// `el` was dragged out of `container` or dropped, and originally came from
/// `source`.
pub fn on_out<F: 'static>(&mut self, listener: F)
where
F: FnMut(JsValue, JsValue, JsValue),
{
const EVENT_NAME: &str = "out";
let listener = closure::to_js_3(listener);
self.on(EVENT_NAME, listener);
}
/// Sets callback for `cloned` event.
/// Callback will be passed argument `(clone, original, type)`
/// The `cloned` event implies that
/// DOM element `original` was cloned as `clone`, of `type` _(`'mirror'` or
/// `'copy'`)_. Fired for mirror images and when `copy: true`.
pub fn on_cloned<F: 'static>(&mut self, listener: F)
where
F: FnMut(JsValue, JsValue, JsValue),
{
const EVENT_NAME: &str = "cloned";
let listener = closure::to_js_3(listener);
self.on(EVENT_NAME, listener);
}
}
#[cfg(test)]
pub mod test;