Created
May 12, 2023 15:05
-
-
Save MartinKavik/409c455322f61632c1ae6f48fab5c715 to your computer and use it in GitHub Desktop.
MoonZoon Sortable wrapper v1
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::{cell::Cell, iter, rc::Rc}; | |
use zoon::*; | |
#[static_ref] | |
fn load_assets_once() -> &'static Mutable<bool> { | |
Task::start(async { | |
load_script(public_url("sortable/sortable_1.15.0.min.js")).await; | |
load_assets_once().set(true); | |
}); | |
Mutable::default() | |
} | |
// ------ Sortable ------ | |
type ElementId = String; | |
pub struct Sortable { | |
raw_el: RawHtmlEl<web_sys::HtmlElement>, | |
controller: Mutable<Option<js_bridge::SortableController>>, | |
} | |
impl Sortable { | |
pub fn new(el: impl Element) -> Self { | |
load_assets_once(); | |
let controller = Mutable::new(None::<js_bridge::SortableController>); | |
let controller_creator = Rc::new(Cell::new(None)); | |
let raw_element = el.into_raw_element(); | |
let mut raw_el = match raw_element { | |
RawElement::El(raw_el) => raw_el, | |
_ => panic!("Sortable supports only HTML Elements"), | |
}; | |
raw_el = raw_el | |
.after_insert( | |
clone!((controller, controller_creator) move |html_element| { | |
controller_creator.set(Some(Task::start_droppable(async move { | |
load_assets_once().signal().wait_for(true).await; | |
controller.set(Some(js_bridge::SortableController::new(&html_element))); | |
}))); | |
}), | |
) | |
.after_remove(clone!((controller) move |_| { | |
drop(controller); | |
drop(controller_creator); | |
})); | |
Self { raw_el, controller } | |
} | |
pub fn on_start(mut self, on_start: impl Fn(ElementId) + 'static) -> Self { | |
let callback = move |element_id: JsValue| { | |
on_start(element_id.as_string().unwrap_throw()); | |
}; | |
let closure = Closure::new(callback); | |
let on_start_setter = Task::start_droppable(self.controller.signal_cloned().for_each_sync( | |
move |controller| { | |
if let Some(controller) = controller { | |
controller.on_start(&closure); | |
} | |
}, | |
)); | |
self.raw_el = self.raw_el.after_remove(move |_| drop(on_start_setter)); | |
self | |
} | |
pub fn on_end(mut self, on_end: impl Fn(ElementId, usize) + 'static) -> Self { | |
let callback = move |element_id: JsValue, new_element_index: usize| { | |
on_end(element_id.as_string().unwrap_throw(), new_element_index); | |
}; | |
let closure = Closure::new(callback); | |
let on_end_setter = Task::start_droppable(self.controller.signal_cloned().for_each_sync( | |
move |controller| { | |
if let Some(controller) = controller { | |
controller.on_end(&closure); | |
} | |
}, | |
)); | |
self.raw_el = self.raw_el.after_remove(move |_| drop(on_end_setter)); | |
self | |
} | |
} | |
impl Element for Sortable { | |
fn into_raw_element(self) -> RawElement { | |
RawElement::El(self.raw_el) | |
} | |
} | |
impl IntoIterator for Sortable { | |
type Item = Self; | |
type IntoIter = iter::Once<Self>; | |
#[inline] | |
fn into_iter(self) -> Self::IntoIter { | |
iter::once(self) | |
} | |
} | |
// ------ ------ | |
// JS Bridge | |
// ------ ------ | |
mod js_bridge { | |
use super::*; | |
// https://rustwasm.github.io/wasm-bindgen/reference/js-snippets.html | |
#[wasm_bindgen(module = "/js/sortable/sortable_controller.js")] | |
extern "C" { | |
#[derive(Clone)] | |
pub type SortableController; | |
#[wasm_bindgen(constructor)] | |
pub fn new(element: &JsValue) -> SortableController; | |
#[wasm_bindgen(method)] | |
pub fn on_start(this: &SortableController, on_start: &Closure<dyn Fn(JsValue)>); | |
#[wasm_bindgen(method)] | |
pub fn on_end(this: &SortableController, on_end: &Closure<dyn Fn(JsValue, usize)>); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export class SortableController { | |
constructor(element) { | |
this.sortable = new Sortable(element, { | |
animation: 150, | |
filter: ".sortable-ignore", | |
// Undesirable white background and slow with native DnD on Chrome | |
// https://github.com/SortableJS/Sortable/issues/2082 | |
// and `forceFallback: true` resolves it. | |
forceFallback: true, | |
fallbackTolerance: 3, | |
}); | |
} | |
on_start(on_start) { | |
this.sortable.option("onStart", event => { | |
on_start(event.item.id); | |
}); | |
} | |
on_end(on_end) { | |
this.sortable.option("onEnd", event => { | |
on_end(event.item.id, event.newDraggableIndex); | |
}); | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment