use std::collections::HashMap; use dioxus_html::input_data::keyboard_types::Key; use dioxus_native_core::{ custom_element::CustomElement, node::OwnedAttributeDiscription, node_ref::AttributeMask, prelude::NodeType, real_dom::{ElementNodeMut, NodeImmutable, NodeMut, NodeTypeMut, RealDom}, NodeId, }; use shipyard::UniqueView; use crate::FormData; use super::{RinkWidget, WidgetContext}; #[derive(Debug, Default)] pub(crate) struct Button { text_id: NodeId, value: String, } impl Button { fn width(el: &ElementNodeMut) -> String { if let Some(value) = el .get_attribute(&OwnedAttributeDiscription { name: "width".to_string(), namespace: None, }) .and_then(|value| value.as_text()) .map(|value| value.to_string()) { value } else { "1px".to_string() } } fn height(el: &ElementNodeMut) -> String { if let Some(value) = el .get_attribute(&OwnedAttributeDiscription { name: "height".to_string(), namespace: None, }) .and_then(|value| value.as_text()) .map(|value| value.to_string()) { value } else { "1px".to_string() } } fn update_size_attr(&mut self, el: &mut ElementNodeMut) { let width = Self::width(el); let height = Self::height(el); let single_char = width == "1px" || height == "1px"; let border_style = if single_char { "none" } else { "solid" }; el.set_attribute( OwnedAttributeDiscription { name: "border-style".to_string(), namespace: Some("style".to_string()), }, border_style.to_string(), ); } fn update_value_attr(&mut self, el: &ElementNodeMut) { if let Some(value) = el .get_attribute(&OwnedAttributeDiscription { name: "value".to_string(), namespace: None, }) .and_then(|value| value.as_text()) .map(|value| value.to_string()) { self.value = value; } } fn write_value(&self, rdom: &mut RealDom) { if let Some(mut text) = rdom.get_mut(self.text_id) { let node_type = text.node_type_mut(); let NodeTypeMut::Text(mut text) = node_type else { panic!("input must be an element") }; *text.text_mut() = self.value.clone(); } } fn switch(&mut self, ctx: &mut WidgetContext, node: NodeMut) { let data = FormData { value: self.value.to_string(), values: HashMap::new(), files: None, }; ctx.send(crate::Event { id: node.id(), name: "input", data: crate::EventData::Form(data), bubbles: true, }); } } impl CustomElement for Button { const NAME: &'static str = "input"; fn roots(&self) -> Vec { vec![self.text_id] } fn create(mut root: dioxus_native_core::real_dom::NodeMut) -> Self { let node_type = root.node_type(); let NodeType::Element(el) = &*node_type else { panic!("input must be an element") }; let value = el .attributes .get(&OwnedAttributeDiscription { name: "value".to_string(), namespace: None, }) .and_then(|value| value.as_text()) .map(|value| value.to_string()); drop(node_type); let rdom = root.real_dom_mut(); let text = rdom.create_node(value.clone().unwrap_or_default()); let text_id = text.id(); root.add_event_listener("keydown"); root.add_event_listener("click"); Self { text_id, value: value.unwrap_or_default(), } } fn attributes_changed( &mut self, mut root: dioxus_native_core::real_dom::NodeMut, attributes: &dioxus_native_core::node_ref::AttributeMask, ) { match attributes { AttributeMask::All => { { let node_type = root.node_type_mut(); let NodeTypeMut::Element(mut el) = node_type else { panic!("input must be an element") }; self.update_value_attr(&el); self.update_size_attr(&mut el); } self.write_value(root.real_dom_mut()); } AttributeMask::Some(attrs) => { { let node_type = root.node_type_mut(); let NodeTypeMut::Element(mut el) = node_type else { panic!("input must be an element") }; if attrs.contains("width") || attrs.contains("height") { self.update_size_attr(&mut el); } if attrs.contains("value") { self.update_value_attr(&el); } } if attrs.contains("value") { self.write_value(root.real_dom_mut()); } } } } } impl RinkWidget for Button { fn handle_event( &mut self, event: &crate::Event, mut node: dioxus_native_core::real_dom::NodeMut, ) { let mut ctx: WidgetContext = { node.real_dom_mut() .raw_world_mut() .borrow::>() .expect("expected widget context") .clone() }; match event.name { "click" => self.switch(&mut ctx, node), "keydown" => { if let crate::EventData::Keyboard(data) = &event.data { if !data.is_auto_repeating() && match data.key() { Key::Character(c) if c == " " => true, Key::Enter => true, _ => false, } { self.switch(&mut ctx, node); } } } _ => {} } } }