mirror of
https://github.com/yewstack/yew.git
synced 2025-12-08 21:26:25 +00:00
Allow setting JsValue as properties (#3458)
* Allow setting JsValue as properties * fix tests & CI * Rc::new * Workaround for Rust <1.72 * more Rc::new * ci green?
This commit is contained in:
parent
40ed8a205d
commit
d790e1beab
@ -218,27 +218,35 @@ impl ToTokens for HtmlElement {
|
||||
}
|
||||
});
|
||||
|
||||
fn apply_as(directive: Option<&PropDirective>) -> TokenStream {
|
||||
match directive {
|
||||
Some(PropDirective::ApplyAsProperty(token)) => {
|
||||
quote_spanned!(token.span()=> ::yew::virtual_dom::ApplyAttributeAs::Property)
|
||||
}
|
||||
None => quote!(::yew::virtual_dom::ApplyAttributeAs::Attribute),
|
||||
}
|
||||
}
|
||||
|
||||
/// Try to turn attribute list into a `::yew::virtual_dom::Attributes::Static`
|
||||
fn try_into_static(
|
||||
src: &[(LitStr, Value, Option<PropDirective>)],
|
||||
) -> Option<TokenStream> {
|
||||
if src
|
||||
.iter()
|
||||
.any(|(_, _, d)| matches!(d, Some(PropDirective::ApplyAsProperty(_))))
|
||||
{
|
||||
// don't try to make a static attribute list if there are any properties to
|
||||
// assign
|
||||
return None;
|
||||
}
|
||||
let mut kv = Vec::with_capacity(src.len());
|
||||
for (k, v, directive) in src.iter() {
|
||||
let v = match v {
|
||||
Value::Static(v) => quote! { #v },
|
||||
Value::Dynamic(_) => return None,
|
||||
};
|
||||
let apply_as = apply_as(directive.as_ref());
|
||||
kv.push(quote! { ( #k, #v, #apply_as ) });
|
||||
let v = match directive {
|
||||
Some(PropDirective::ApplyAsProperty(token)) => {
|
||||
quote_spanned!(token.span()=> ::yew::virtual_dom::AttributeOrProperty::Property(
|
||||
::std::convert::Into::into(#v)
|
||||
))
|
||||
}
|
||||
None => quote!(::yew::virtual_dom::AttributeOrProperty::Static(
|
||||
#v
|
||||
)),
|
||||
};
|
||||
kv.push(quote! { ( #k, #v) });
|
||||
}
|
||||
|
||||
Some(quote! { ::yew::virtual_dom::Attributes::Static(&[#(#kv),*]) })
|
||||
@ -251,9 +259,22 @@ impl ToTokens for HtmlElement {
|
||||
try_into_static(&attrs).unwrap_or_else(|| {
|
||||
let keys = attrs.iter().map(|(k, ..)| quote! { #k });
|
||||
let values = attrs.iter().map(|(_, v, directive)| {
|
||||
let apply_as = apply_as(directive.as_ref());
|
||||
let value = wrap_attr_value(v);
|
||||
quote! { ::std::option::Option::map(#value, |it| (it, #apply_as)) }
|
||||
let value = match directive {
|
||||
Some(PropDirective::ApplyAsProperty(token)) => {
|
||||
quote_spanned!(token.span()=> ::std::option::Option::Some(
|
||||
::yew::virtual_dom::AttributeOrProperty::Property(
|
||||
::std::convert::Into::into(#v)
|
||||
))
|
||||
)
|
||||
}
|
||||
None => {
|
||||
let value = wrap_attr_value(v);
|
||||
quote! {
|
||||
::std::option::Option::map(#value, ::yew::virtual_dom::AttributeOrProperty::Attribute)
|
||||
}
|
||||
},
|
||||
};
|
||||
quote! { #value }
|
||||
});
|
||||
quote! {
|
||||
::yew::virtual_dom::Attributes::Dynamic{
|
||||
|
||||
@ -9,7 +9,7 @@ use yew::AttrValue;
|
||||
use super::Apply;
|
||||
use crate::dom_bundle::BSubtree;
|
||||
use crate::virtual_dom::vtag::{InputFields, Value};
|
||||
use crate::virtual_dom::{ApplyAttributeAs, Attributes};
|
||||
use crate::virtual_dom::{AttributeOrProperty, Attributes};
|
||||
|
||||
impl<T: AccessValue> Apply for Value<T> {
|
||||
type Bundle = Self;
|
||||
@ -92,23 +92,23 @@ impl Attributes {
|
||||
#[cold]
|
||||
fn apply_diff_index_maps(
|
||||
el: &Element,
|
||||
new: &IndexMap<AttrValue, (AttrValue, ApplyAttributeAs)>,
|
||||
old: &IndexMap<AttrValue, (AttrValue, ApplyAttributeAs)>,
|
||||
new: &IndexMap<AttrValue, AttributeOrProperty>,
|
||||
old: &IndexMap<AttrValue, AttributeOrProperty>,
|
||||
) {
|
||||
for (key, value) in new.iter() {
|
||||
match old.get(key) {
|
||||
Some(old_value) => {
|
||||
if value != old_value {
|
||||
Self::set(el, key, value.0.as_ref(), value.1);
|
||||
Self::set(el, key, value);
|
||||
}
|
||||
}
|
||||
None => Self::set(el, key, value.0.as_ref(), value.1),
|
||||
None => Self::set(el, key, value),
|
||||
}
|
||||
}
|
||||
|
||||
for (key, (_, apply_as)) in old.iter() {
|
||||
for (key, value) in old.iter() {
|
||||
if !new.contains_key(key) {
|
||||
Self::remove(el, key, *apply_as);
|
||||
Self::remove(el, key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -117,26 +117,17 @@ impl Attributes {
|
||||
/// Works with any [Attributes] variants.
|
||||
#[cold]
|
||||
fn apply_diff_as_maps<'a>(el: &Element, new: &'a Self, old: &'a Self) {
|
||||
fn collect(src: &Attributes) -> HashMap<&str, (&str, ApplyAttributeAs)> {
|
||||
fn collect(src: &Attributes) -> HashMap<&str, &AttributeOrProperty> {
|
||||
use Attributes::*;
|
||||
|
||||
match src {
|
||||
Static(arr) => (*arr)
|
||||
.iter()
|
||||
.map(|(k, v, apply_as)| (*k, (*v, *apply_as)))
|
||||
.collect(),
|
||||
Static(arr) => (*arr).iter().map(|(k, v)| (*k, v)).collect(),
|
||||
Dynamic { keys, values } => keys
|
||||
.iter()
|
||||
.zip(values.iter())
|
||||
.filter_map(|(k, v)| {
|
||||
v.as_ref()
|
||||
.map(|(v, apply_as)| (*k, (v.as_ref(), *apply_as)))
|
||||
})
|
||||
.collect(),
|
||||
IndexMap(m) => m
|
||||
.iter()
|
||||
.map(|(k, (v, apply_as))| (k.as_ref(), (v.as_ref(), *apply_as)))
|
||||
.filter_map(|(k, v)| v.as_ref().map(|v| (*k, v)))
|
||||
.collect(),
|
||||
IndexMap(m) => m.iter().map(|(k, v)| (k.as_ref(), v)).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -149,37 +140,39 @@ impl Attributes {
|
||||
Some(old) => old != new,
|
||||
None => true,
|
||||
} {
|
||||
Self::set(el, k, new.0, new.1);
|
||||
Self::set(el, k, new);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove missing
|
||||
for (k, (_, apply_as)) in old.iter() {
|
||||
for (k, old_value) in old.iter() {
|
||||
if !new.contains_key(k) {
|
||||
Self::remove(el, k, *apply_as);
|
||||
Self::remove(el, k, old_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn set(el: &Element, key: &str, value: &str, apply_as: ApplyAttributeAs) {
|
||||
match apply_as {
|
||||
ApplyAttributeAs::Attribute => el
|
||||
fn set(el: &Element, key: &str, value: &AttributeOrProperty) {
|
||||
match value {
|
||||
AttributeOrProperty::Attribute(value) => el
|
||||
.set_attribute(intern(key), value)
|
||||
.expect("invalid attribute key"),
|
||||
ApplyAttributeAs::Property => {
|
||||
AttributeOrProperty::Static(value) => el
|
||||
.set_attribute(intern(key), value)
|
||||
.expect("invalid attribute key"),
|
||||
AttributeOrProperty::Property(value) => {
|
||||
let key = JsValue::from_str(key);
|
||||
let value = JsValue::from_str(value);
|
||||
js_sys::Reflect::set(el.as_ref(), &key, &value).expect("could not set property");
|
||||
js_sys::Reflect::set(el.as_ref(), &key, value).expect("could not set property");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn remove(el: &Element, key: &str, apply_as: ApplyAttributeAs) {
|
||||
match apply_as {
|
||||
ApplyAttributeAs::Attribute => el
|
||||
fn remove(el: &Element, key: &str, old_value: &AttributeOrProperty) {
|
||||
match old_value {
|
||||
AttributeOrProperty::Attribute(_) | AttributeOrProperty::Static(_) => el
|
||||
.remove_attribute(intern(key))
|
||||
.expect("could not remove attribute"),
|
||||
ApplyAttributeAs::Property => {
|
||||
AttributeOrProperty::Property(_) => {
|
||||
let key = JsValue::from_str(key);
|
||||
js_sys::Reflect::set(el.as_ref(), &key, &JsValue::UNDEFINED)
|
||||
.expect("could not remove property");
|
||||
@ -195,20 +188,20 @@ impl Apply for Attributes {
|
||||
fn apply(self, _root: &BSubtree, el: &Element) -> Self {
|
||||
match &self {
|
||||
Self::Static(arr) => {
|
||||
for (k, v, apply_as) in arr.iter() {
|
||||
Self::set(el, k, v, *apply_as);
|
||||
for (k, v) in arr.iter() {
|
||||
Self::set(el, k, v);
|
||||
}
|
||||
}
|
||||
Self::Dynamic { keys, values } => {
|
||||
for (k, v) in keys.iter().zip(values.iter()) {
|
||||
if let Some((v, apply_as)) = v {
|
||||
Self::set(el, k, v, *apply_as)
|
||||
if let Some(v) = v {
|
||||
Self::set(el, k, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
Self::IndexMap(m) => {
|
||||
for (k, (v, apply_as)) in m.iter() {
|
||||
Self::set(el, k, v, *apply_as)
|
||||
for (k, v) in m.iter() {
|
||||
Self::set(el, k, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -248,7 +241,7 @@ impl Apply for Attributes {
|
||||
}
|
||||
macro_rules! set {
|
||||
($new:expr) => {
|
||||
Self::set(el, key!(), $new.0.as_ref(), $new.1)
|
||||
Self::set(el, key!(), $new)
|
||||
};
|
||||
}
|
||||
|
||||
@ -260,7 +253,7 @@ impl Apply for Attributes {
|
||||
}
|
||||
(Some(new), None) => set!(new),
|
||||
(None, Some(old)) => {
|
||||
Self::remove(el, key!(), old.1);
|
||||
Self::remove(el, key!(), old);
|
||||
}
|
||||
(None, None) => (),
|
||||
}
|
||||
@ -282,6 +275,7 @@ impl Apply for Attributes {
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::rc::Rc;
|
||||
use std::time::Duration;
|
||||
|
||||
use gloo::utils::document;
|
||||
@ -303,10 +297,11 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn properties_are_set() {
|
||||
let attrs = Attributes::Static(&[
|
||||
("href", "https://example.com/", ApplyAttributeAs::Property),
|
||||
("alt", "somewhere", ApplyAttributeAs::Property),
|
||||
]);
|
||||
let attrs = indexmap::indexmap! {
|
||||
AttrValue::Static("href") => AttributeOrProperty::Property(JsValue::from_str("https://example.com/")),
|
||||
AttrValue::Static("alt") => AttributeOrProperty::Property(JsValue::from_str("somewhere")),
|
||||
};
|
||||
let attrs = Attributes::IndexMap(Rc::new(attrs));
|
||||
let (element, btree) = create_element();
|
||||
attrs.apply(&btree, &element);
|
||||
assert_eq!(
|
||||
@ -329,10 +324,11 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn respects_apply_as() {
|
||||
let attrs = Attributes::Static(&[
|
||||
("href", "https://example.com/", ApplyAttributeAs::Attribute),
|
||||
("alt", "somewhere", ApplyAttributeAs::Property),
|
||||
]);
|
||||
let attrs = indexmap::indexmap! {
|
||||
AttrValue::Static("href") => AttributeOrProperty::Attribute(AttrValue::from("https://example.com/")),
|
||||
AttrValue::Static("alt") => AttributeOrProperty::Property(JsValue::from_str("somewhere")),
|
||||
};
|
||||
let attrs = Attributes::IndexMap(Rc::new(attrs));
|
||||
let (element, btree) = create_element();
|
||||
attrs.apply(&btree, &element);
|
||||
assert_eq!(
|
||||
@ -352,7 +348,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn class_is_always_attrs() {
|
||||
let attrs = Attributes::Static(&[("class", "thing", ApplyAttributeAs::Attribute)]);
|
||||
let attrs = Attributes::Static(&[("class", AttributeOrProperty::Static("thing"))]);
|
||||
|
||||
let (element, btree) = create_element();
|
||||
attrs.apply(&btree, &element);
|
||||
@ -363,10 +359,10 @@ mod tests {
|
||||
async fn macro_syntax_works() {
|
||||
#[function_component]
|
||||
fn Comp() -> Html {
|
||||
html! { <a href="https://example.com/" ~alt="abc" /> }
|
||||
html! { <a href="https://example.com/" ~alt={"abc"} ~data-bool={JsValue::from_bool(true)} /> }
|
||||
}
|
||||
|
||||
let output = gloo::utils::document().get_element_by_id("output").unwrap();
|
||||
let output = document().get_element_by_id("output").unwrap();
|
||||
yew::Renderer::<Comp>::with_root(output.clone()).render();
|
||||
|
||||
gloo::timers::future::sleep(Duration::from_secs(1)).await;
|
||||
@ -384,5 +380,13 @@ mod tests {
|
||||
"abc",
|
||||
"property `alt` not set properly"
|
||||
);
|
||||
|
||||
assert!(
|
||||
Reflect::get(element.as_ref(), &JsValue::from_str("data-bool"))
|
||||
.expect("no alt")
|
||||
.as_bool()
|
||||
.expect("not a bool"),
|
||||
"property `alt` not set properly"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,6 +25,7 @@ use std::hint::unreachable_unchecked;
|
||||
use std::rc::Rc;
|
||||
|
||||
use indexmap::IndexMap;
|
||||
use wasm_bindgen::JsValue;
|
||||
|
||||
#[doc(inline)]
|
||||
pub use self::key::Key;
|
||||
@ -172,22 +173,29 @@ mod feat_ssr {
|
||||
}
|
||||
}
|
||||
|
||||
/// Defines if the [`Attributes`] is set as element's attribute or property
|
||||
/// Defines if the [`Attributes`] is set as element's attribute or property and its value.
|
||||
#[allow(missing_docs)]
|
||||
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
|
||||
pub enum ApplyAttributeAs {
|
||||
Attribute,
|
||||
Property,
|
||||
#[derive(PartialEq, Clone, Debug)]
|
||||
pub enum AttributeOrProperty {
|
||||
// This exists as a workaround to support Rust <1.72
|
||||
// Previous versions of Rust did not See
|
||||
// `AttributeOrProperty::Attribute(AttrValue::Static(_))` as `'static` that html! macro
|
||||
// used, and thus failed with "temporary value dropped while borrowed"
|
||||
//
|
||||
// See: https://github.com/yewstack/yew/pull/3458#discussion_r1350362215
|
||||
Static(&'static str),
|
||||
Attribute(AttrValue),
|
||||
Property(JsValue),
|
||||
}
|
||||
|
||||
/// A collection of attributes for an element
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
#[derive(PartialEq, Clone, Debug)]
|
||||
pub enum Attributes {
|
||||
/// Static list of attributes.
|
||||
///
|
||||
/// Allows optimizing comparison to a simple pointer equality check and reducing allocations,
|
||||
/// if the attributes do not change on a node.
|
||||
Static(&'static [(&'static str, &'static str, ApplyAttributeAs)]),
|
||||
Static(&'static [(&'static str, AttributeOrProperty)]),
|
||||
|
||||
/// Static list of attribute keys with possibility to exclude attributes and dynamic attribute
|
||||
/// values.
|
||||
@ -200,12 +208,12 @@ pub enum Attributes {
|
||||
|
||||
/// Attribute values. Matches [keys](Attributes::Dynamic::keys). Optional attributes are
|
||||
/// designated by setting [None].
|
||||
values: Box<[Option<(AttrValue, ApplyAttributeAs)>]>,
|
||||
values: Box<[Option<AttributeOrProperty>]>,
|
||||
},
|
||||
|
||||
/// IndexMap is used to provide runtime attribute deduplication in cases where the html! macro
|
||||
/// was not used to guarantee it.
|
||||
IndexMap(Rc<IndexMap<AttrValue, (AttrValue, ApplyAttributeAs)>>),
|
||||
IndexMap(Rc<IndexMap<AttrValue, AttributeOrProperty>>),
|
||||
}
|
||||
|
||||
impl Attributes {
|
||||
@ -216,21 +224,31 @@ impl Attributes {
|
||||
|
||||
/// Return iterator over attribute key-value pairs.
|
||||
/// This function is suboptimal and does not inline well. Avoid on hot paths.
|
||||
///
|
||||
/// This function only returns attributes
|
||||
pub fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = (&'a str, &'a str)> + 'a> {
|
||||
match self {
|
||||
Self::Static(arr) => Box::new(arr.iter().map(|(k, v, _)| (*k, *v as &'a str))),
|
||||
Self::Dynamic { keys, values } => Box::new(
|
||||
keys.iter()
|
||||
.zip(values.iter())
|
||||
.filter_map(|(k, v)| v.as_ref().map(|(v, _)| (*k, v.as_ref()))),
|
||||
),
|
||||
Self::IndexMap(m) => Box::new(m.iter().map(|(k, (v, _))| (k.as_ref(), v.as_ref()))),
|
||||
Self::Static(arr) => Box::new(arr.iter().filter_map(|(k, v)| match v {
|
||||
AttributeOrProperty::Attribute(v) => Some((*k, v.as_ref())),
|
||||
AttributeOrProperty::Property(_) => None,
|
||||
AttributeOrProperty::Static(v) => Some((*k, v)),
|
||||
})),
|
||||
Self::Dynamic { keys, values } => {
|
||||
Box::new(keys.iter().zip(values.iter()).filter_map(|(k, v)| match v {
|
||||
Some(AttributeOrProperty::Attribute(v)) => Some((*k, v.as_ref())),
|
||||
_ => None,
|
||||
}))
|
||||
}
|
||||
Self::IndexMap(m) => Box::new(m.iter().filter_map(|(k, v)| match v {
|
||||
AttributeOrProperty::Attribute(v) => Some((k.as_ref(), v.as_ref())),
|
||||
_ => None,
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a mutable reference to the underlying `IndexMap`.
|
||||
/// If the attributes are stored in the `Vec` variant, it will be converted.
|
||||
pub fn get_mut_index_map(&mut self) -> &mut IndexMap<AttrValue, (AttrValue, ApplyAttributeAs)> {
|
||||
pub fn get_mut_index_map(&mut self) -> &mut IndexMap<AttrValue, AttributeOrProperty> {
|
||||
macro_rules! unpack {
|
||||
() => {
|
||||
match self {
|
||||
@ -245,9 +263,7 @@ impl Attributes {
|
||||
Self::IndexMap(m) => Rc::make_mut(m),
|
||||
Self::Static(arr) => {
|
||||
*self = Self::IndexMap(Rc::new(
|
||||
arr.iter()
|
||||
.map(|(k, v, ty)| ((*k).into(), ((*v).into(), *ty)))
|
||||
.collect(),
|
||||
arr.iter().map(|(k, v)| ((*k).into(), v.clone())).collect(),
|
||||
));
|
||||
unpack!()
|
||||
}
|
||||
@ -269,7 +285,7 @@ impl From<IndexMap<AttrValue, AttrValue>> for Attributes {
|
||||
fn from(map: IndexMap<AttrValue, AttrValue>) -> Self {
|
||||
let v = map
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k, (v, ApplyAttributeAs::Attribute)))
|
||||
.map(|(k, v)| (k, AttributeOrProperty::Attribute(v)))
|
||||
.collect();
|
||||
Self::IndexMap(Rc::new(v))
|
||||
}
|
||||
@ -279,7 +295,17 @@ impl From<IndexMap<&'static str, AttrValue>> for Attributes {
|
||||
fn from(v: IndexMap<&'static str, AttrValue>) -> Self {
|
||||
let v = v
|
||||
.into_iter()
|
||||
.map(|(k, v)| (AttrValue::Static(k), (v, ApplyAttributeAs::Attribute)))
|
||||
.map(|(k, v)| (AttrValue::Static(k), (AttributeOrProperty::Attribute(v))))
|
||||
.collect();
|
||||
Self::IndexMap(Rc::new(v))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<IndexMap<&'static str, JsValue>> for Attributes {
|
||||
fn from(v: IndexMap<&'static str, JsValue>) -> Self {
|
||||
let v = v
|
||||
.into_iter()
|
||||
.map(|(k, v)| (AttrValue::Static(k), (AttributeOrProperty::Property(v))))
|
||||
.collect();
|
||||
Self::IndexMap(Rc::new(v))
|
||||
}
|
||||
|
||||
@ -6,9 +6,10 @@ use std::mem;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::rc::Rc;
|
||||
|
||||
use wasm_bindgen::JsValue;
|
||||
use web_sys::{HtmlInputElement as InputElement, HtmlTextAreaElement as TextAreaElement};
|
||||
|
||||
use super::{ApplyAttributeAs, AttrValue, Attributes, Key, Listener, Listeners, VNode};
|
||||
use super::{AttrValue, AttributeOrProperty, Attributes, Key, Listener, Listeners, VNode};
|
||||
use crate::html::{ImplicitClone, IntoPropValue, NodeRef};
|
||||
|
||||
/// SVG namespace string used for creating svg elements
|
||||
@ -386,17 +387,17 @@ impl VTag {
|
||||
pub fn add_attribute(&mut self, key: &'static str, value: impl Into<AttrValue>) {
|
||||
self.attributes.get_mut_index_map().insert(
|
||||
AttrValue::Static(key),
|
||||
(value.into(), ApplyAttributeAs::Attribute),
|
||||
AttributeOrProperty::Attribute(value.into()),
|
||||
);
|
||||
}
|
||||
|
||||
/// Set the given key as property on the element
|
||||
///
|
||||
/// [`js_sys::Reflect`] is used for setting properties.
|
||||
pub fn add_property(&mut self, key: &'static str, value: impl Into<AttrValue>) {
|
||||
pub fn add_property(&mut self, key: &'static str, value: impl Into<JsValue>) {
|
||||
self.attributes.get_mut_index_map().insert(
|
||||
AttrValue::Static(key),
|
||||
(value.into(), ApplyAttributeAs::Property),
|
||||
AttributeOrProperty::Property(value.into()),
|
||||
);
|
||||
}
|
||||
|
||||
@ -412,7 +413,7 @@ impl VTag {
|
||||
pub fn __macro_push_attr(&mut self, key: &'static str, value: impl IntoPropValue<AttrValue>) {
|
||||
self.attributes.get_mut_index_map().insert(
|
||||
AttrValue::from(key),
|
||||
(value.into_prop_value(), ApplyAttributeAs::Property),
|
||||
AttributeOrProperty::Attribute(value.into_prop_value()),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user