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