feat: use `attr:` syntax rather than `AdditionalAttributes` (#1728)

This commit is contained in:
Greg Johnston 2023-09-15 18:36:54 -04:00 committed by GitHub
parent 2c12256260
commit 73a85b4955
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 65 additions and 74 deletions

View File

@ -30,13 +30,13 @@ Theres a very simple way to determine whether you should use a capital-S `<Sc
There are even a couple elements designed to make semantic HTML and styling easier. [`<Html/>`](https://docs.rs/leptos_meta/latest/leptos_meta/fn.Html.html) lets you set the `lang` and `dir` on your `<html>` tag from your application code. `<Html/>` and [`<Body/>`](https://docs.rs/leptos_meta/latest/leptos_meta/fn.Html.html) both have `class` props that let you set their respective `class` attributes, which is sometimes needed by CSS frameworks for styling.
`<Body/>` and `<Html/>` both also have `attributes` props which can be used to set any number of additional attributes on them via the [`AdditionalAttributes`](https://docs.rs/leptos/latest/leptos/struct.AdditionalAttributes.html) type:
`<Body/>` and `<Html/>` both also have `attributes` props which can be used to set any number of additional attributes on them via the `attr:` syntax:
```rust
<Html
lang="he"
dir="rtl"
attributes=AdditionalAttributes::from(vec![("data-theme", "dark")])
attr:data-theme="dark"
/>
```

View File

@ -1,3 +1,5 @@
#![allow(deprecated)]
use crate::TextProp;
use std::rc::Rc;
@ -5,6 +7,9 @@ use std::rc::Rc;
/// each of which may or may not be reactive.
#[derive(Clone)]
#[repr(transparent)]
#[deprecated = "Most uses of `AdditionalAttributes` can be replaced with `#[prop(attrs)]` \
and the `attr:` syntax. If you have a use case that still requires `AdditionalAttributes`, please \
open a GitHub issue here and share it: https://github.com/leptos-rs/leptos"]
pub struct AdditionalAttributes(pub(crate) Rc<[(String, TextProp)]>);
impl<I, T, U> From<I> for AdditionalAttributes

View File

@ -1,6 +1,8 @@
use cfg_if::cfg_if;
use leptos::*;
#[cfg(feature = "ssr")]
use std::collections::HashMap;
#[cfg(feature = "ssr")]
use std::{cell::RefCell, rc::Rc};
/// Contains the current metadata for the document's `<body>`.
@ -9,7 +11,7 @@ pub struct BodyContext {
#[cfg(feature = "ssr")]
class: Rc<RefCell<Option<TextProp>>>,
#[cfg(feature = "ssr")]
attributes: Rc<RefCell<Option<MaybeSignal<AdditionalAttributes>>>>,
attributes: Rc<RefCell<HashMap<&'static str, Attribute>>>,
}
impl BodyContext {
@ -22,20 +24,23 @@ impl BodyContext {
leptos::leptos_dom::ssr::escape_attr(&val.get())
)
});
let attributes = self.attributes.borrow().as_ref().map(|val| {
val.with(|val| {
val.into_iter()
.map(|(n, v)| {
let attributes = self.attributes.borrow();
let attributes = (!attributes.is_empty()).then(|| {
attributes
.iter()
.filter_map(|(n, v)| {
v.as_nameless_value_string().map(|v| {
format!(
"{}=\"{}\"",
n,
leptos::leptos_dom::ssr::escape_attr(&v.get())
leptos::leptos_dom::ssr::escape_attr(&v)
)
})
.collect::<Vec<_>>()
.join(" ")
})
})
.collect::<Vec<_>>()
.join(" ")
});
let mut val = [class, attributes]
.into_iter()
.flatten()
@ -77,7 +82,7 @@ impl std::fmt::Debug for BodyContext {
///
/// view! {
/// <main>
/// <Body class=body_class/>
/// <Body class=body_class attr:class="foo"/>
/// </main>
/// }
/// }
@ -88,11 +93,13 @@ pub fn Body(
#[prop(optional, into)]
class: Option<TextProp>,
/// Arbitrary attributes to add to the `<html>`
#[prop(optional, into)]
attributes: Option<MaybeSignal<AdditionalAttributes>>,
#[prop(attrs)]
attributes: Vec<(&'static str, Attribute)>,
) -> impl IntoView {
cfg_if! {
if #[cfg(any(feature = "csr", feature = "hydrate"))] {
if #[cfg(all(target_arch = "wasm32", any(feature = "csr", feature = "hydrate")))] {
use wasm_bindgen::JsCast;
let el = document().body().expect("there to be a <body> element");
if let Some(class) = class {
@ -105,24 +112,15 @@ pub fn Body(
});
}
if let Some(attributes) = attributes {
let attributes = attributes.get();
for (attr_name, attr_value) in attributes.into_iter() {
let el = el.clone();
let attr_name = attr_name.to_owned();
let attr_value = attr_value.to_owned();
create_render_effect(move |_|{
let value = attr_value.get();
_ = el.set_attribute(&attr_name, &value);
});
}
for (name, value) in attributes {
leptos::leptos_dom::attribute_helper(el.unchecked_ref(), name.into(), value);
}
} else if #[cfg(feature = "ssr")] {
let meta = crate::use_head();
*meta.body.class.borrow_mut() = class;
*meta.body.attributes.borrow_mut() = attributes;
meta.body.attributes.borrow_mut().extend(attributes);
} else {
_ = class;
_ = class;
_ = attributes;
#[cfg(debug_assertions)]

View File

@ -1,6 +1,8 @@
use cfg_if::cfg_if;
use leptos::*;
#[cfg(feature = "ssr")]
use std::collections::HashMap;
#[cfg(feature = "ssr")]
use std::{cell::RefCell, rc::Rc};
/// Contains the current metadata for the document's `<html>`.
@ -13,7 +15,7 @@ pub struct HtmlContext {
#[cfg(feature = "ssr")]
class: Rc<RefCell<Option<TextProp>>>,
#[cfg(feature = "ssr")]
attributes: Rc<RefCell<Option<MaybeSignal<AdditionalAttributes>>>>,
attributes: Rc<RefCell<HashMap<&'static str, Attribute>>>,
}
impl HtmlContext {
@ -38,19 +40,21 @@ impl HtmlContext {
leptos::leptos_dom::ssr::escape_attr(&val.get())
)
});
let attributes = self.attributes.borrow().as_ref().map(|val| {
val.with(|val| {
val.into_iter()
.map(|(n, v)| {
let attributes = self.attributes.borrow();
let attributes = (!attributes.is_empty()).then(|| {
attributes
.iter()
.filter_map(|(n, v)| {
v.as_nameless_value_string().map(|v| {
format!(
"{}=\"{}\"",
n,
leptos::leptos_dom::ssr::escape_attr(&v.get())
leptos::leptos_dom::ssr::escape_attr(&v)
)
})
.collect::<Vec<_>>()
.join(" ")
})
})
.collect::<Vec<_>>()
.join(" ")
});
let mut val = [lang, dir, class, attributes]
.into_iter()
@ -88,8 +92,8 @@ impl std::fmt::Debug for HtmlContext {
/// <Html
/// lang="he"
/// dir="rtl"
/// // arbitrary additional attributes can be passed via `attributes`
/// attributes=AdditionalAttributes::from(vec![("data-theme", "dark")])
/// // arbitrary additional attributes can be passed via `attr:`
/// attr:data-theme="dark"
/// />
/// </main>
/// }
@ -107,11 +111,13 @@ pub fn Html(
#[prop(optional, into)]
class: Option<TextProp>,
/// Arbitrary attributes to add to the `<html>`
#[prop(optional, into)]
attributes: Option<MaybeSignal<AdditionalAttributes>>,
#[prop(attrs)]
attributes: Vec<(&'static str, Attribute)>,
) -> impl IntoView {
cfg_if! {
if #[cfg(any(feature = "csr", feature = "hydrate"))] {
if #[cfg(all(target_arch = "wasm32", any(feature = "csr", feature = "hydrate")))] {
use wasm_bindgen::JsCast;
let el = document().document_element().expect("there to be a <html> element");
if let Some(lang) = lang {
@ -138,24 +144,15 @@ pub fn Html(
});
}
if let Some(attributes) = attributes {
let attributes = attributes.get();
for (attr_name, attr_value) in attributes.into_iter() {
let el = el.clone();
let attr_name = attr_name.to_owned();
let attr_value = attr_value.to_owned();
create_render_effect(move |_|{
let value = attr_value.get();
_ = el.set_attribute(&attr_name, &value);
});
}
for (name, value) in attributes {
leptos::leptos_dom::attribute_helper(el.unchecked_ref(), name.into(), value);
}
} else if #[cfg(feature = "ssr")] {
let meta = crate::use_head();
*meta.html.lang.borrow_mut() = lang;
*meta.html.dir.borrow_mut() = dir;
*meta.html.class.borrow_mut() = class;
*meta.html.attributes.borrow_mut() = attributes;
meta.html.attributes.borrow_mut().extend(attributes);
} else {
_ = lang;
_ = dir;

View File

@ -56,9 +56,10 @@ pub fn Form<A>(
/// Sets whether the page should be scrolled to the top when the form is submitted.
#[prop(optional)]
noscroll: bool,
/// Arbitrary attributes to add to the `<form>`
#[prop(optional, into)]
attributes: Option<MaybeSignal<AdditionalAttributes>>,
/// Arbitrary attributes to add to the `<form>`. Attributes can be added with the
/// `attr:` syntax in the `view` macro.
#[prop(attrs)]
attributes: Vec<(&'static str, Attribute)>,
/// Component children; should include the HTML of the form elements.
children: Children,
) -> impl IntoView
@ -78,7 +79,7 @@ where
children: Children,
node_ref: Option<NodeRef<html::Form>>,
noscroll: bool,
attributes: Option<MaybeSignal<AdditionalAttributes>>,
attributes: Vec<(&'static str, Attribute)>,
) -> HtmlElement<html::Form> {
let action_version = version;
let on_submit = {
@ -276,13 +277,8 @@ where
if let Some(node_ref) = node_ref {
form = form.node_ref(node_ref)
};
if let Some(attributes) = attributes {
let attributes = attributes.get();
for (attr_name, attr_value) in attributes.into_iter() {
let attr_name = attr_name.to_owned();
let attr_value = attr_value.to_owned();
form = form.attr(attr_name, move || attr_value.get());
}
for (attr_name, attr_value) in attributes {
form = form.attr(attr_name, attr_value);
}
form
}
@ -352,7 +348,7 @@ pub fn ActionForm<I, O>(
noscroll: bool,
/// Arbitrary attributes to add to the `<form>`
#[prop(optional, into)]
attributes: Option<MaybeSignal<AdditionalAttributes>>,
attributes: Vec<(&'static str, Attribute)>,
/// Component children; should include the HTML of the form elements.
children: Children,
) -> impl IntoView
@ -539,7 +535,7 @@ pub fn MultiActionForm<I, O>(
node_ref: Option<NodeRef<html::Form>>,
/// Arbitrary attributes to add to the `<form>`
#[prop(optional, into)]
attributes: Option<MaybeSignal<AdditionalAttributes>>,
attributes: Vec<(&'static str, Attribute)>,
/// Component children; should include the HTML of the form elements.
children: Children,
) -> impl IntoView
@ -591,13 +587,8 @@ where
if let Some(node_ref) = node_ref {
form = form.node_ref(node_ref)
};
if let Some(attributes) = attributes {
let attributes = attributes.get();
for (attr_name, attr_value) in attributes.into_iter() {
let attr_name = attr_name.to_owned();
let attr_value = attr_value.to_owned();
form = form.attr(attr_name, move || attr_value.get());
}
for (attr_name, attr_value) in attributes {
form = form.attr(attr_name, attr_value);
}
form
}