button.rs 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. use std::collections::HashMap;
  2. use dioxus_html::input_data::keyboard_types::Key;
  3. use dioxus_native_core::{
  4. custom_element::CustomElement,
  5. node::OwnedAttributeDiscription,
  6. node_ref::AttributeMask,
  7. prelude::NodeType,
  8. real_dom::{ElementNodeMut, NodeImmutable, NodeMut, NodeTypeMut, RealDom},
  9. NodeId,
  10. };
  11. use shipyard::UniqueView;
  12. use crate::FormData;
  13. use super::{RinkWidget, WidgetContext};
  14. #[derive(Debug, Default)]
  15. pub(crate) struct Button {
  16. text_id: NodeId,
  17. value: String,
  18. }
  19. impl Button {
  20. fn width(el: &ElementNodeMut) -> String {
  21. if let Some(value) = el
  22. .get_attribute(&OwnedAttributeDiscription {
  23. name: "width".to_string(),
  24. namespace: None,
  25. })
  26. .and_then(|value| value.as_text())
  27. .map(|value| value.to_string())
  28. {
  29. value
  30. } else {
  31. "1px".to_string()
  32. }
  33. }
  34. fn height(el: &ElementNodeMut) -> String {
  35. if let Some(value) = el
  36. .get_attribute(&OwnedAttributeDiscription {
  37. name: "height".to_string(),
  38. namespace: None,
  39. })
  40. .and_then(|value| value.as_text())
  41. .map(|value| value.to_string())
  42. {
  43. value
  44. } else {
  45. "1px".to_string()
  46. }
  47. }
  48. fn update_size_attr(&mut self, el: &mut ElementNodeMut) {
  49. let width = Self::width(el);
  50. let height = Self::height(el);
  51. let single_char = width == "1px" || height == "1px";
  52. let border_style = if single_char { "none" } else { "solid" };
  53. el.set_attribute(
  54. OwnedAttributeDiscription {
  55. name: "border-style".to_string(),
  56. namespace: Some("style".to_string()),
  57. },
  58. border_style.to_string(),
  59. );
  60. }
  61. fn update_value_attr(&mut self, el: &ElementNodeMut) {
  62. if let Some(value) = el
  63. .get_attribute(&OwnedAttributeDiscription {
  64. name: "value".to_string(),
  65. namespace: None,
  66. })
  67. .and_then(|value| value.as_text())
  68. .map(|value| value.to_string())
  69. {
  70. self.value = value;
  71. }
  72. }
  73. fn write_value(&self, rdom: &mut RealDom) {
  74. if let Some(mut text) = rdom.get_mut(self.text_id) {
  75. let node_type = text.node_type_mut();
  76. let NodeTypeMut::Text(mut text) = node_type else { panic!("input must be an element") };
  77. *text.text_mut() = self.value.clone();
  78. }
  79. }
  80. fn switch(&mut self, ctx: &mut WidgetContext, node: NodeMut) {
  81. let data = FormData {
  82. value: self.value.to_string(),
  83. values: HashMap::new(),
  84. files: None,
  85. };
  86. ctx.send(crate::Event {
  87. id: node.id(),
  88. name: "input",
  89. data: crate::EventData::Form(data),
  90. bubbles: true,
  91. });
  92. }
  93. }
  94. impl CustomElement for Button {
  95. const NAME: &'static str = "input";
  96. fn roots(&self) -> Vec<NodeId> {
  97. vec![self.text_id]
  98. }
  99. fn create(mut root: dioxus_native_core::real_dom::NodeMut) -> Self {
  100. let node_type = root.node_type();
  101. let NodeType::Element(el) = &*node_type else { panic!("input must be an element") };
  102. let value = el
  103. .attributes
  104. .get(&OwnedAttributeDiscription {
  105. name: "value".to_string(),
  106. namespace: None,
  107. })
  108. .and_then(|value| value.as_text())
  109. .map(|value| value.to_string());
  110. drop(node_type);
  111. let rdom = root.real_dom_mut();
  112. let text = rdom.create_node(value.clone().unwrap_or_default());
  113. let text_id = text.id();
  114. root.add_event_listener("keydown");
  115. root.add_event_listener("click");
  116. Self {
  117. text_id,
  118. value: value.unwrap_or_default(),
  119. }
  120. }
  121. fn attributes_changed(
  122. &mut self,
  123. mut root: dioxus_native_core::real_dom::NodeMut,
  124. attributes: &dioxus_native_core::node_ref::AttributeMask,
  125. ) {
  126. match attributes {
  127. AttributeMask::All => {
  128. {
  129. let node_type = root.node_type_mut();
  130. let NodeTypeMut::Element(mut el) = node_type else { panic!("input must be an element") };
  131. self.update_value_attr(&el);
  132. self.update_size_attr(&mut el);
  133. }
  134. self.write_value(root.real_dom_mut());
  135. }
  136. AttributeMask::Some(attrs) => {
  137. {
  138. let node_type = root.node_type_mut();
  139. let NodeTypeMut::Element(mut el) = node_type else { panic!("input must be an element") };
  140. if attrs.contains("width") || attrs.contains("height") {
  141. self.update_size_attr(&mut el);
  142. }
  143. if attrs.contains("value") {
  144. self.update_value_attr(&el);
  145. }
  146. }
  147. if attrs.contains("value") {
  148. self.write_value(root.real_dom_mut());
  149. }
  150. }
  151. }
  152. }
  153. }
  154. impl RinkWidget for Button {
  155. fn handle_event(
  156. &mut self,
  157. event: &crate::Event,
  158. mut node: dioxus_native_core::real_dom::NodeMut,
  159. ) {
  160. let mut ctx: WidgetContext = {
  161. node.real_dom_mut()
  162. .raw_world_mut()
  163. .borrow::<UniqueView<WidgetContext>>()
  164. .expect("expected widget context")
  165. .clone()
  166. };
  167. match event.name {
  168. "click" => self.switch(&mut ctx, node),
  169. "keydown" => {
  170. if let crate::EventData::Keyboard(data) = &event.data {
  171. if !data.is_auto_repeating()
  172. && match data.key() {
  173. Key::Character(c) if c == " " => true,
  174. Key::Enter => true,
  175. _ => false,
  176. }
  177. {
  178. self.switch(&mut ctx, node);
  179. }
  180. }
  181. }
  182. _ => {}
  183. }
  184. }
  185. }