119 lines
3.3 KiB
Rust
119 lines
3.3 KiB
Rust
use gloo_console::error;
|
|
use serde::{Deserialize, Serialize};
|
|
use stylist::css;
|
|
use thiserror::Error;
|
|
use visions_types::CardId;
|
|
use yew::prelude::*;
|
|
|
|
use crate::{clone, design};
|
|
|
|
#[derive(Debug, Error)]
|
|
pub enum DragError {
|
|
#[error("No data transfer object found in the drag event")]
|
|
NoData,
|
|
|
|
#[error("Data in the drag event could not be deserialized: {0}")]
|
|
MalformedData(String),
|
|
|
|
#[error("Data cannot be serialized to json")]
|
|
Unserializable,
|
|
|
|
#[error("Data transfer failed")]
|
|
DataTransferFailed,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
|
pub enum DraggableItem {
|
|
Card(CardId),
|
|
}
|
|
|
|
#[derive(Properties, PartialEq)]
|
|
pub struct DragSourceProps {
|
|
pub item: DraggableItem,
|
|
pub children: Html,
|
|
}
|
|
|
|
#[function_component]
|
|
pub fn DragSource(DragSourceProps { item, children }: &DragSourceProps) -> Html {
|
|
html! {
|
|
<div draggable="true" ondragstart={
|
|
Callback::from(clone!(item,
|
|
move |e: DragEvent| on_drag_item(e, item.clone())
|
|
))
|
|
}>
|
|
{children.clone()}
|
|
</div>
|
|
}
|
|
}
|
|
|
|
fn on_drag_item(e: DragEvent, item: DraggableItem) {
|
|
let result: Result<(), DragError> =
|
|
e.data_transfer()
|
|
.ok_or(DragError::NoData)
|
|
.and_then(|data_transfer| {
|
|
serde_json::to_string(&item)
|
|
.map_err(|_| DragError::Unserializable)
|
|
.and_then(|js| {
|
|
data_transfer
|
|
.set_data("application/json", &js)
|
|
.map_err(|_| DragError::DataTransferFailed)
|
|
})
|
|
});
|
|
|
|
if let Err(error) = result {
|
|
error!(format!("Drag start failed: {:?}", error));
|
|
}
|
|
}
|
|
|
|
#[derive(Properties, PartialEq)]
|
|
pub struct DropTargetProps {
|
|
pub on_drop: Callback<DraggableItem>,
|
|
pub children: Html,
|
|
}
|
|
|
|
#[function_component]
|
|
pub fn DropTarget(DropTargetProps { on_drop, children }: &DropTargetProps) -> Html {
|
|
let styles = css!(
|
|
border: ${*design::BORDER_HOVER_SHALLOW};
|
|
);
|
|
|
|
let current_styles = use_state(|| css!());
|
|
|
|
html! {
|
|
<div
|
|
ondragover={Callback::from(clone!((styles, current_styles), move |e: DragEvent| {
|
|
e.prevent_default();
|
|
current_styles.set(styles.clone());
|
|
}))}
|
|
ondragleave={Callback::from(clone!(current_styles, move |_: DragEvent| {
|
|
current_styles.set(css!());
|
|
}))}
|
|
ondrop={Callback::from(clone!((current_styles, on_drop), move |e: DragEvent| {
|
|
current_styles.set(css!());
|
|
on_drop_item(e, on_drop.clone())
|
|
})
|
|
)}
|
|
class={(*current_styles).clone()}
|
|
>
|
|
{children.clone()}
|
|
</div>
|
|
}
|
|
}
|
|
|
|
fn on_drop_item(e: DragEvent, callback: Callback<DraggableItem>) {
|
|
let result: Result<DraggableItem, DragError> = e
|
|
.data_transfer()
|
|
.ok_or(DragError::NoData)
|
|
.and_then(|transfer| {
|
|
transfer
|
|
.get_data("application/json")
|
|
.map_err(|_| DragError::NoData)
|
|
})
|
|
.and_then(|json| serde_json::from_str(&json).map_err(|_| DragError::MalformedData(json)));
|
|
|
|
match result {
|
|
Ok(item) => callback.emit(item),
|
|
Err(error) => error!(format!("drop error: {:?}", error)),
|
|
}
|
|
}
|