Skip to main content

dioxus_core/view/
attribute.rs

1//! Typed attribute views: static and dynamic attribute builders plus the
2//! traits that append attribute values onto a builder target.
3
4use std::marker::PhantomData;
5
6use crate::{Attribute, DynamicValues, HasAttributes, IntoAttributeValue};
7use dioxus_core_template::TemplateRawTree;
8
9use super::{ElementBuilder, View, ViewTemplate};
10
11/// Static metadata for a generated attribute builder method.
12pub trait AttributeDescriptor {
13    /// Attribute name.
14    const NAME: &'static str;
15
16    /// Attribute namespace.
17    const NAMESPACE: Option<&'static str> = None;
18
19    /// Whether this dynamic attribute should always be written.
20    const VOLATILE: bool = false;
21}
22
23/// A static attribute view.
24pub struct StaticAttributeBuilder<Descriptor, Value = Descriptor>(PhantomData<(Descriptor, Value)>);
25
26/// Create a static attribute view for an attribute marker.
27#[inline]
28pub const fn static_attribute<A: AttributeDescriptor + StaticAttributeValue>()
29-> StaticAttributeBuilder<A> {
30    StaticAttributeBuilder(PhantomData)
31}
32
33impl<Descriptor, Value> ViewTemplate for StaticAttributeBuilder<Descriptor, Value>
34where
35    Descriptor: AttributeDescriptor,
36    Value: StaticAttributeValue,
37{
38    const TEMPLATE_TREE: &'static TemplateRawTree = &TemplateRawTree::StaticAttr {
39        name: Descriptor::NAME,
40        value: Value::VALUE,
41        namespace: Descriptor::NAMESPACE,
42    };
43}
44
45impl<Descriptor, Value> View for StaticAttributeBuilder<Descriptor, Value>
46where
47    Descriptor: AttributeDescriptor,
48    Value: StaticAttributeValue,
49{
50}
51
52/// A marker for one static attribute value.
53pub trait StaticAttributeValue {
54    /// Attribute value.
55    const VALUE: &'static str;
56}
57
58/// A static attribute value that can be passed to typed attribute methods.
59pub struct StaticAttributeValueBuilder<V>(#[doc(hidden)] pub PhantomData<V>);
60
61/// Marker for static attribute values passed to typed attribute methods.
62#[doc(hidden)]
63pub struct StaticAttributeValueBuilderMarker;
64
65/// A value that can be appended by a generated attribute builder method.
66pub trait IntoAttributeBuilderValue<Target, Descriptor, Marker>
67where
68    Target: AttributeBuilderTarget,
69    Descriptor: AttributeDescriptor,
70{
71    /// The target returned after appending this attribute value.
72    type Output;
73
74    /// Append this value to the target.
75    fn append_to(self, target: Target) -> Self::Output;
76}
77
78/// A dynamic attribute slot.
79pub struct DynamicAttributesBuilder {
80    attrs: Box<[Attribute]>,
81}
82
83/// Create a dynamic attribute slot from an already boxed attribute list.
84#[inline]
85#[doc(hidden)]
86pub fn dynamic_attributes_builder(attrs: Box<[Attribute]>) -> DynamicAttributesBuilder {
87    DynamicAttributesBuilder { attrs }
88}
89
90impl ViewTemplate for DynamicAttributesBuilder {
91    const TEMPLATE_TREE: &'static TemplateRawTree = &TemplateRawTree::DynamicAttr;
92}
93
94impl View for DynamicAttributesBuilder {
95    fn push(self, dynamic: &mut DynamicValues) {
96        dynamic.push_attrs(self.attrs);
97    }
98}
99
100/// A builder target that can accept one attribute.
101pub trait AttributeBuilderTarget: Sized {
102    /// The target returned after adding the attribute.
103    type Output;
104
105    /// Append one fully constructed attribute.
106    fn append_attribute(self, attr: Attribute) -> Self::Output;
107}
108
109impl<Target> AttributeBuilderTarget for Target
110where
111    Target: HasAttributes,
112{
113    type Output = Self;
114
115    #[inline]
116    fn append_attribute(self, attr: Attribute) -> Self::Output {
117        self.push_attribute(attr.name, attr.namespace, attr.value, attr.volatile)
118    }
119}
120
121impl<Tag, Attributes, Children> AttributeBuilderTarget
122    for ElementBuilder<Tag, Attributes, Children>
123{
124    type Output = ElementBuilder<Tag, (Attributes, DynamicAttributesBuilder), Children>;
125
126    #[inline]
127    fn append_attribute(self, attr: Attribute) -> Self::Output {
128        self.attribute(dynamic_attributes_builder(Box::new([attr])))
129    }
130}
131
132impl AttributeBuilderTarget for Vec<Attribute> {
133    type Output = Self;
134
135    #[inline]
136    fn append_attribute(mut self, attr: Attribute) -> Self::Output {
137        self.push(attr);
138        self
139    }
140}
141
142impl<Target, Descriptor, Marker, Value> IntoAttributeBuilderValue<Target, Descriptor, Marker>
143    for Value
144where
145    Target: AttributeBuilderTarget,
146    Descriptor: AttributeDescriptor,
147    Value: IntoAttributeValue<Marker>,
148{
149    type Output = <Target as AttributeBuilderTarget>::Output;
150
151    #[inline]
152    fn append_to(self, target: Target) -> Self::Output {
153        AttributeBuilderTarget::append_attribute(
154            target,
155            Attribute::new(
156                Descriptor::NAME,
157                self,
158                Descriptor::NAMESPACE,
159                Descriptor::VOLATILE,
160            ),
161        )
162    }
163}
164
165impl<Tag, Attributes, Children, Descriptor, Value>
166    IntoAttributeBuilderValue<
167        ElementBuilder<Tag, Attributes, Children>,
168        Descriptor,
169        StaticAttributeValueBuilderMarker,
170    > for StaticAttributeValueBuilder<Value>
171where
172    Descriptor: AttributeDescriptor,
173    Value: StaticAttributeValue,
174{
175    type Output =
176        ElementBuilder<Tag, (Attributes, StaticAttributeBuilder<Descriptor, Value>), Children>;
177
178    #[inline]
179    fn append_to(self, target: ElementBuilder<Tag, Attributes, Children>) -> Self::Output {
180        target.attribute(StaticAttributeBuilder(PhantomData))
181    }
182}