Skip to main content

euv_core/vdom/attribute/
struct.rs

1use crate::*;
2
3/// Represents a CSS style property.
4///
5/// A single key-value pair representing a CSS declaration.
6#[derive(Clone, Data, Debug, Default, Eq, New, PartialEq)]
7pub struct StyleProperty {
8    /// The CSS property name (e.g., "margin", "padding").
9    name: String,
10    /// The CSS property value.
11    value: String,
12}
13
14/// A collection of CSS style properties that can be converted to a style string.
15#[derive(Clone, Data, Debug, Eq, New, PartialEq)]
16pub struct Style {
17    /// The list of style properties.
18    properties: Vec<StyleProperty>,
19}
20
21/// Represents a single attribute on a virtual DOM node.
22///
23/// Combines an attribute name with its corresponding value.
24#[derive(Clone, CustomDebug, Data, New)]
25pub struct AttributeEntry {
26    /// The name of the attribute.
27    #[get(pub(crate))]
28    #[set(pub(crate))]
29    pub(crate) name: String,
30    /// The value of the attribute.
31    #[debug(skip)]
32    #[get(pub(crate))]
33    #[set(pub(crate))]
34    pub(crate) value: AttributeValue,
35}
36
37/// Represents a CSS pseudo-class or pseudo-element rule attached to a class.
38///
39/// Each rule has a selector suffix (e.g., ":hover", "::before", ":focus")
40/// and a style declaration string. When injected into the DOM, it produces
41/// a rule like `.class-name:hover { background: red; }`.
42#[derive(Clone, Data, Debug, Default, Eq, New, PartialEq)]
43pub struct PseudoRule {
44    /// The CSS pseudo selector suffix appended to the class name
45    /// (e.g., ":hover", ":focus", ":active", ":disabled", "::before", "::after",
46    /// ":first-child", ":last-child", ":nth-child(2n)", etc.).
47    selector: String,
48    /// The CSS style declarations for this pseudo rule
49    /// (e.g., "background: rgba(79, 70, 229, 0.04); color: #4f46e5;").
50    style: String,
51}
52
53/// Represents a CSS class with a name, its style declarations, and optional pseudo rules.
54///
55/// Created by the `class!` macro and used in `html!` via the `class:` attribute.
56/// When the renderer encounters a `CssClass`, it injects the styles into the
57/// DOM's `<style>` element on first use and applies the class name to the element.
58#[derive(Clone, Data, Debug, Default)]
59pub struct CssClass {
60    /// The CSS class name used in the DOM.
61    name: String,
62    /// The CSS style declarations (e.g., "max-width: 800px; margin: 0 auto;").
63    style: String,
64    /// The pseudo-class and pseudo-element rules for this class
65    /// (e.g., ":hover", ":focus", ":active", "::before", etc.).
66    pseudo_rules: Vec<PseudoRule>,
67    /// The media query rules for this class.
68    media_rules: Vec<MediaRule>,
69}
70
71/// Represents a CSS @media rule attached to a class.
72///
73/// Each media rule has a query string (e.g., "(max-width: 767px)")
74/// and a style declaration string. When injected into the DOM, it produces
75/// a rule like `@media (max-width: 767px) { .class-name { font-size: 14px; } }`.
76#[derive(Clone, Data, Debug, Default, Eq, New, PartialEq)]
77pub struct MediaRule {
78    /// The media query condition string (e.g., "(max-width: 767px)").
79    query: String,
80    /// The CSS style declarations inside this media rule
81    /// (e.g., "font-size: 14px; padding: 8px;").
82    style: String,
83}
84
85/// Adapts various event value types into an `AttributeValue` for event attributes.
86///
87/// The `html!` macro generates `EventAdapter::new(expr).into_attribute(event_name)`
88/// instead of inline trait dispatch boilerplate. This eliminates the per-attribute-site
89/// generation of `__EventWrapper`, `__IsClosure`, `__ClosurePicker`, `__ValuePicker`,
90/// `__FallbackHelper`, and `__dispatch` types, significantly reducing macro output size.
91///
92/// The adapter pattern handles three cases:
93/// - `FnMut(NativeEvent)` closure → `AttributeValue::Event` via `NativeEventHandler`
94/// - `NativeEventHandler` directly → `AttributeValue::Event` as-is
95/// - `Option<NativeEventHandler>` → `AttributeValue::Event` or `AttributeValue::Text`
96#[derive(Data, New)]
97pub struct EventAdapter<T> {
98    /// The wrapped value to be adapted into an attribute.
99    #[get(pub(crate))]
100    #[set(pub(crate))]
101    pub(crate) inner: T,
102}
103
104/// Adapts an arbitrary attribute value expression into an `AttributeValue`.
105///
106/// Handles the dispatch between event closures and reactive values without
107/// requiring the macro to generate inline trait hierarchies. The macro emits
108/// `AttrValueAdapter::new(expr).into_attribute_value()` instead of the
109/// `__IsClosure` / `__ClosurePicker` / `__ValuePicker` / `__FallbackHelper`
110/// / `__dispatch` boilerplate.
111///
112/// For event attributes (key starts with "on"), event closures are wrapped
113/// into `AttributeValue::Event`. For non-event attributes, values are
114/// converted via `IntoReactiveValue`.
115#[derive(Data, Debug, New)]
116pub struct AttrValueAdapter<T> {
117    /// The wrapped value to be adapted into an attribute.
118    #[get(pub(crate))]
119    #[set(pub(crate))]
120    pub(crate) inner: T,
121}
122
123/// A `Sync` wrapper for single-threaded global `HashSet` access.
124///
125/// SAFETY: This type is only safe to use in single-threaded contexts
126/// (e.g., WASM). It implements `Sync` to allow usage as a `static mut`
127/// variable, but concurrent access from multiple threads would be
128/// undefined behavior.
129#[derive(Data, Debug, New)]
130pub(crate) struct InjectedClassesCell(
131    /// Interior-mutable storage for the injected classes set.
132    #[get(pub(crate))]
133    #[set(pub(crate))]
134    pub(crate) UnsafeCell<Option<HashSet<String>>>,
135);