mirror of
https://github.com/leptos-rs/leptos
synced 2024-11-10 06:44:17 +00:00
feat: use attr:
syntax rather than AdditionalAttributes
(#1728)
This commit is contained in:
parent
2c12256260
commit
73a85b4955
5 changed files with 65 additions and 74 deletions
|
@ -30,13 +30,13 @@ There’s 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"
|
||||
/>
|
||||
```
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue