1
- use wasm_bindgen:: JsValue ;
1
+ use std:: rc:: Rc ;
2
+
3
+ use wasm_bindgen:: closure:: Closure ;
4
+ use wasm_bindgen:: { JsCast , JsValue } ;
2
5
3
6
mod js {
4
7
use wasm_bindgen:: prelude:: * ;
@@ -12,26 +15,113 @@ mod js {
12
15
}
13
16
}
14
17
18
+ #[ derive( Clone , Debug ) ]
19
+ pub struct Event {
20
+ pub raw_event : js_sys:: Object ,
21
+ pub to : web_sys:: HtmlElement ,
22
+ pub from : web_sys:: HtmlElement ,
23
+ pub item : web_sys:: HtmlElement ,
24
+ pub clone : web_sys:: HtmlElement ,
25
+ pub old_index : Option < usize > ,
26
+ pub new_index : Option < usize > ,
27
+ pub old_draggable_index : Option < usize > ,
28
+ pub new_draggable_index : Option < usize > ,
29
+ // TODO: pullMode
30
+ }
31
+
32
+ impl Event {
33
+ fn from_raw_event ( raw_event : js_sys:: Object ) -> Event {
34
+ macro_rules! get {
35
+ ( $field: expr) => {
36
+ js_sys:: Reflect :: get( & raw_event, & JsValue :: from_str( $field) )
37
+ . expect( "failed retrieving field from raw event" )
38
+ . dyn_into( )
39
+ . expect( "failed casting field of raw event to proper type" )
40
+ } ;
41
+ }
42
+ macro_rules! get_optint {
43
+ ( $field: expr) => {
44
+ js_sys:: Reflect :: get( & raw_event, & JsValue :: from_str( $field) )
45
+ . ok( )
46
+ . map( |evt| {
47
+ let float = evt. as_f64( )
48
+ . expect( "failed casting field of raw event to proper type" ) ;
49
+ let int = float as usize ;
50
+ assert!( ( int as f64 - float) . abs( ) < 0.1 , "received index that is not an integer: {}" , float) ;
51
+ int
52
+ } )
53
+ } ;
54
+ }
55
+ Event {
56
+ to : get ! ( "to" ) ,
57
+ from : get ! ( "from" ) ,
58
+ item : get ! ( "item" ) ,
59
+ clone : get ! ( "clone" ) ,
60
+ old_index : get_optint ! ( "oldIndex" ) ,
61
+ new_index : get_optint ! ( "newIndex" ) ,
62
+ old_draggable_index : get_optint ! ( "oldDraggableIndex" ) ,
63
+ new_draggable_index : get_optint ! ( "newDraggableIndex" ) ,
64
+ raw_event,
65
+ }
66
+ }
67
+ }
68
+
69
+ #[ repr( usize ) ]
70
+ enum CallbackId {
71
+ Choose ,
72
+ Unchoose ,
73
+ Start ,
74
+ End ,
75
+ Add ,
76
+ Update ,
77
+ Sort ,
78
+ Remove ,
79
+ Filter ,
80
+ Clone ,
81
+ Change ,
82
+ _Total,
83
+ }
84
+
15
85
/// See https://github.com/SortableJS/Sortable for more documentation about available options
16
- pub struct Options ( js_sys:: Object ) ;
86
+ pub struct Options {
87
+ options : js_sys:: Object ,
88
+ callbacks : [ Option < Rc < Closure < dyn FnMut ( js_sys:: Object ) > > > ; CallbackId :: _Total as usize ] ,
89
+ }
17
90
18
91
macro_rules! option {
19
92
( $setter: ident, $jsname: expr, $typ: ty, $builder: ident ) => {
20
- pub fn $setter( & self , value: $typ) -> & Options {
93
+ pub fn $setter( & mut self , value: $typ) -> & mut Options {
21
94
let res = js_sys:: Reflect :: set(
22
- & self . 0 ,
95
+ & self . options ,
23
96
& JsValue :: from_str( $jsname) ,
24
- & JsValue :: $builder( value)
25
- ) . expect( "setting property on object failed" ) ;
97
+ & JsValue :: $builder( value) ,
98
+ )
99
+ . expect( "setting property on object failed" ) ;
26
100
assert!( res, "failed setting property on object" ) ;
27
101
self
28
102
}
29
- }
103
+ } ;
104
+ }
105
+
106
+ macro_rules! callback {
107
+ ( $setter: ident, $jsname: expr, $id: ident ) => {
108
+ pub fn $setter( & mut self , mut cb: impl ' static + FnMut ( Event ) ) -> & Options {
109
+ let cb = Closure :: new( move |e: js_sys:: Object | cb( Event :: from_raw_event( e) ) ) ;
110
+ let res = js_sys:: Reflect :: set( & self . options, & JsValue :: from_str( $jsname) , cb. as_ref( ) )
111
+ . expect( "setting callback on object failed" ) ;
112
+ assert!( res, "failed setting callback on object" ) ;
113
+ self . callbacks[ CallbackId :: $id as usize ] = Some ( Rc :: new( cb) ) ;
114
+ self
115
+ }
116
+ } ;
30
117
}
31
118
32
119
impl Options {
33
120
pub fn new ( ) -> Options {
34
- Options ( js_sys:: Object :: new ( ) )
121
+ Options {
122
+ options : js_sys:: Object :: new ( ) ,
123
+ callbacks : std:: array:: from_fn ( |_| None ) ,
124
+ }
35
125
}
36
126
37
127
option ! ( group, "group" , & str , from_str) ;
@@ -56,7 +146,12 @@ impl Options {
56
146
57
147
option ! ( swap_threshold, "swapThreshold" , f64 , from_f64) ;
58
148
option ! ( invert_swap, "invertSwap" , bool , from_bool) ;
59
- option ! ( inverted_swap_threshold, "invertedSwapThreshold" , f64 , from_f64) ;
149
+ option ! (
150
+ inverted_swap_threshold,
151
+ "invertedSwapThreshold" ,
152
+ f64 ,
153
+ from_f64
154
+ ) ;
60
155
option ! ( direction, "direction" , & str , from_str) ;
61
156
62
157
option ! ( force_fallback, "forceFallback" , bool , from_bool) ;
@@ -67,7 +162,24 @@ impl Options {
67
162
68
163
option ! ( dragover_bubble, "dragoverBubble" , bool , from_bool) ;
69
164
option ! ( remove_clone_on_hide, "removeCloneOnHide" , bool , from_bool) ;
70
- option ! ( empty_insert_threshold, "emptyInsertThreshold" , f64 , from_f64) ;
165
+ option ! (
166
+ empty_insert_threshold,
167
+ "emptyInsertThreshold" ,
168
+ f64 ,
169
+ from_f64
170
+ ) ;
171
+
172
+ callback ! ( on_choose, "onChoose" , Choose ) ;
173
+ callback ! ( on_unchoose, "onUnchoose" , Unchoose ) ;
174
+ callback ! ( on_start, "onStart" , Start ) ;
175
+ callback ! ( on_end, "onEnd" , End ) ;
176
+ callback ! ( on_add, "onAdd" , Add ) ;
177
+ callback ! ( on_update, "onUpdate" , Update ) ;
178
+ callback ! ( on_sort, "onSort" , Sort ) ;
179
+ callback ! ( on_remove, "onRemove" , Remove ) ;
180
+ callback ! ( on_filter, "onFilter" , Filter ) ;
181
+ callback ! ( on_clone, "onClone" , Clone ) ;
182
+ callback ! ( on_change, "onChange" , Change ) ;
71
183
72
184
// TODO: all the callbacks
73
185
@@ -76,10 +188,21 @@ impl Options {
76
188
/// Note that you can set options on this object through `js_sys::Reflect`.
77
189
/// This allows setting options that are not planned for by `sortable-js-rs`.
78
190
pub fn options ( & self ) -> & js_sys:: Object {
79
- & self . 0
191
+ & self . options
80
192
}
81
193
82
- pub fn apply ( & self , elt : & web_sys:: Element ) {
83
- js:: Sortable :: new ( elt, & self . 0 ) ;
194
+ pub fn apply ( & self , elt : & web_sys:: Element ) -> Sortable {
195
+ js:: Sortable :: new ( elt, & self . options ) ;
196
+ Sortable {
197
+ _callbacks : self . callbacks . clone ( ) ,
198
+ }
84
199
}
85
200
}
201
+
202
+ /// Data related to the Sortable instance
203
+ ///
204
+ /// It must be kept alive on the rust sideas long as the instance can call callbacks, as otherwise the link between the js-side callback and the rust-side callback would be lost.
205
+ pub struct Sortable {
206
+ /// Keep the callbacks alive
207
+ _callbacks : [ Option < Rc < Closure < dyn FnMut ( js_sys:: Object ) > > > ; CallbackId :: _Total as usize ] ,
208
+ }
0 commit comments