use dioxus::prelude::*;
use crate::core::DropEffect;
#[derive(Debug, Clone, PartialEq)]
pub enum OutboundContent {
Text(String),
Url {
url: String,
title: Option<String>,
},
Html {
html: String,
fallback_text: String,
},
Custom(Vec<(String, String)>),
}
impl OutboundContent {
pub fn url(url: impl Into<String>, title: Option<&str>) -> Self {
Self::Url {
url: url.into(),
title: title.map(str::to_string),
}
}
pub fn entries(&self) -> Vec<(String, String)> {
match self {
OutboundContent::Text(t) => vec![("text/plain".into(), t.clone())],
OutboundContent::Url { url, title } => {
let mut out = vec![
("text/uri-list".into(), url.clone()),
("text/plain".into(), url.clone()),
];
if let Some(title) = title {
out.push((
"text/html".into(),
format!(r#"<a href="{url}">{title}</a>"#),
));
}
out
}
OutboundContent::Html {
html,
fallback_text,
} => vec![
("text/html".into(), html.clone()),
("text/plain".into(), fallback_text.clone()),
],
OutboundContent::Custom(pairs) => pairs.clone(),
}
}
}
#[component]
pub fn ExternalDragSource(
content: OutboundContent,
#[props(default = DropEffect::Copy)]
effect: DropEffect,
#[props(default)]
disabled: bool,
#[props(extends = div, extends = GlobalAttributes)] attributes: Vec<Attribute>,
children: Element,
) -> Element {
rsx! {
div {
draggable: !disabled,
ondragstart: move |evt: DragEvent| {
if disabled {
return;
}
evt.stop_propagation();
let dt = evt.data_transfer();
for (format, data) in content.entries() {
let _ = dt.set_data(&format, &data);
}
dt.set_effect_allowed(effect.as_str());
},
..attributes,
{children}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn url_content_covers_all_formats() {
let c = OutboundContent::url("https://example.com", Some("Example"));
let e = c.entries();
assert_eq!(e[0].0, "text/uri-list");
assert_eq!(e[1], ("text/plain".into(), "https://example.com".into()));
assert!(e[2].1.contains(r#"href="https://example.com""#));
assert_eq!(OutboundContent::url("https://x.y", None).entries().len(), 2);
}
}