kozan_core/html/html_element.rs
1//! `HtmlElement` — shared behavior for ALL HTML elements.
2//!
3//! Like Chrome's `HTMLElement` (500+ lines of shared logic).
4//! Every HTML element (Div, Button, Input, etc.) implements this trait.
5//! SVG/MathML elements would NOT implement it.
6//!
7//! All methods have **default implementations** — concrete elements
8//! get them for free. Override only what differs (like Chrome's virtual methods).
9//!
10//! # Chrome equivalence
11//!
12//! | Chrome method | Kozan trait method |
13//! |---------------------------|-------------------------|
14//! | `hidden()` | `hidden()` |
15//! | `title()` | `title()` |
16//! | `lang()` | `lang()` |
17//! | `dir()` | `dir()` |
18//! | `tabIndex` | `tab_index()` |
19//! | `click()` | `click()` |
20//! | `focus()` | `focus()` |
21//! | `blur()` | `blur()` |
22//! | `draggable` | `draggable()` |
23//! | `spellcheck` | `spellcheck()` |
24//! | `CollectStyleForPresAttr` | `presentation_style()` |
25
26use crate::dom::traits::Element;
27
28/// Shared behavior for all HTML elements.
29///
30/// Every method has a default implementation that reads/writes attributes
31/// through the `Element` trait. Concrete elements inherit all behavior
32/// and override only what they need.
33///
34/// `HtmlDivElement` overrides: `presentation_style()` (for legacy `align`).
35/// `HtmlButtonElement` overrides: nothing (adds props via `ButtonData` instead).
36pub trait HtmlElement: Element {
37 // ---- Global HTML attributes (default impls read/write from attributes) ----
38
39 /// The `hidden` attribute. Elements with `hidden` should not be rendered.
40 fn hidden(&self) -> bool {
41 self.attribute("hidden").is_some()
42 }
43
44 fn set_hidden(&self, hidden: bool) {
45 if hidden {
46 self.set_attribute("hidden", "");
47 } else {
48 self.remove_attribute("hidden");
49 }
50 }
51
52 /// The `title` attribute (tooltip text).
53 fn title(&self) -> String {
54 self.attribute("title").unwrap_or_default()
55 }
56
57 fn set_title(&self, title: impl Into<String>) {
58 self.set_attribute("title", title);
59 }
60
61 /// The `lang` attribute (language code).
62 fn lang(&self) -> String {
63 self.attribute("lang").unwrap_or_default()
64 }
65
66 fn set_lang(&self, lang: impl Into<String>) {
67 self.set_attribute("lang", lang);
68 }
69
70 /// The `dir` attribute (text direction: "ltr", "rtl", "auto").
71 fn dir(&self) -> String {
72 self.attribute("dir").unwrap_or_default()
73 }
74
75 fn set_dir(&self, dir: impl Into<String>) {
76 self.set_attribute("dir", dir);
77 }
78
79 /// The `tabindex` attribute. Controls focus order.
80 /// Returns the element's default if not explicitly set.
81 fn tab_index(&self) -> i32 {
82 self.attribute("tabindex")
83 .and_then(|v| v.parse().ok())
84 .unwrap_or(if Self::IS_FOCUSABLE { 0 } else { -1 })
85 }
86
87 fn set_tab_index(&self, index: i32) {
88 self.set_attribute("tabindex", index.to_string());
89 }
90
91 /// The `draggable` attribute.
92 fn draggable(&self) -> bool {
93 self.attribute("draggable").is_some_and(|v| v == "true")
94 }
95
96 fn set_draggable(&self, draggable: bool) {
97 self.set_attribute("draggable", if draggable { "true" } else { "false" });
98 }
99
100 /// The `spellcheck` attribute.
101 fn spellcheck(&self) -> bool {
102 self.attribute("spellcheck").is_none_or(|v| v != "false")
103 }
104
105 fn set_spellcheck(&self, spellcheck: bool) {
106 self.set_attribute("spellcheck", if spellcheck { "true" } else { "false" });
107 }
108
109 // ---- Behavioral hooks (override in concrete elements) ----
110
111 /// Map presentation attributes to a `PropertyDeclarationBlock`.
112 ///
113 /// Like Chrome's `CollectStyleForPresentationAttribute`. Override in elements
114 /// that expose legacy presentation attributes (e.g. `align`, `width`, `color`).
115 fn collect_presentation_styles(&self) {
116 // Default: no presentation attributes to map.
117 }
118
119 /// Called when an attribute changes.
120 ///
121 /// Like Chrome's `HTMLElement::ParseAttribute`.
122 /// Default: no-op. Override to react to attribute changes.
123 fn attribute_changed(&self, _name: &str, _old: Option<&str>, _new: Option<&str>) {
124 // Default: no special handling.
125 }
126
127 // ---- Actions ----
128
129 /// Programmatically click this element.
130 fn click(&self) {
131 // Future: dispatch ClickEvent through the event system.
132 }
133
134 /// Focus this element.
135 fn focus(&self) {
136 // Future: focus management through the document.
137 }
138
139 /// Blur (unfocus) this element.
140 fn blur(&self) {
141 // Future: focus management through the document.
142 }
143}