Skip to content

Commit d2d6308

Browse files
authored
Merge pull request #16 from blorbb/leptos-0.7
Leptos 0.7 support
2 parents 65df573 + d822c07 commit d2d6308

File tree

25 files changed

+522
-609
lines changed

25 files changed

+522
-609
lines changed

Cargo.toml

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "leptos-mview"
3-
version = "0.3.2"
3+
version = "0.4.0"
44
edition = "2021"
55
license = "MIT OR Apache-2.0"
66
keywords = ["macro", "leptos", "view"]
@@ -11,11 +11,16 @@ readme = "README.md"
1111
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
1212

1313
[dependencies]
14-
leptos-mview-macro = { path = "leptos-mview-macro", version = "0.3.2" }
14+
leptos-mview-macro = { path = "leptos-mview-macro", version = "0.4.0" }
1515

1616
[dev-dependencies]
1717
trybuild = "1"
18-
leptos = { version = "0.6.12", features = ["nightly", "csr"] }
18+
# needs to use ssr for some view-to-HTML features to work.
19+
leptos = { version = "0.7.0", features = ["ssr", "nightly"] }
20+
leptos-mview = { path = ".", features = ["nightly"] }
21+
22+
[features]
23+
nightly = ["leptos-mview-macro/nightly"]
1924

2025
[workspace]
2126
members = ["leptos-mview-core", "leptos-mview-macro"]

README.md

Lines changed: 13 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@ An alternative `view!` macro for [Leptos](https://github.com/leptos-rs/leptos/tr
1111
A little preview of the syntax:
1212

1313
```rust
14-
use leptos::*;
14+
use leptos::prelude::*;
1515
use leptos_mview::mview;
1616

1717
#[component]
1818
fn MyComponent() -> impl IntoView {
19-
let (value, set_value) = create_signal(String::new());
19+
let (value, set_value) = signal(String::new());
2020
let red_input = move || value().len() % 2 == 0;
2121

2222
mview! {
@@ -37,7 +37,7 @@ fn MyComponent() -> impl IntoView {
3737
fallback=[mview! { "..." }]
3838
{
3939
Await
40-
future=[fetch_from_db(value())]
40+
future={fetch_from_db(value())}
4141
blocking
4242
|db_info| {
4343
p { "Things found: " strong { {*db_info} } "!" }
@@ -54,12 +54,12 @@ async fn fetch_from_db(data: String) -> usize { data.len() }
5454
<summary> Explanation of the example: </summary>
5555

5656
```rust
57-
use leptos::*;
57+
use leptos::prelude::*;
5858
use leptos_mview::mview;
5959

6060
#[component]
6161
fn MyComponent() -> impl IntoView {
62-
let (value, set_value) = create_signal(String::new());
62+
let (value, set_value) = signal(String::new());
6363
let red_input = move || value().len() % 2 == 0;
6464

6565
mview! {
@@ -85,7 +85,7 @@ fn MyComponent() -> impl IntoView {
8585
fallback=[mview! { "..." }] // `{move || mview! { "..." }}`
8686
{ // I recommend placing children like this when attributes are multi-line
8787
Await
88-
future=[fetch_from_db(value())]
88+
future={fetch_from_db(value())}
8989
blocking // expanded to `blocking=true`
9090
// children take arguments with a 'closure'
9191
// this is very different to `let:db_info` in Leptos!
@@ -110,10 +110,6 @@ async fn fetch_from_db(data: String) -> usize { data.len() }
110110

111111
The `view!` macros in Leptos is often the largest part of a component, and can get extremely long when writing complex components. This macro aims to be as **concise** as possible, trying to **minimise unnecessary punctuation/words** and **shorten common patterns**.
112112

113-
## Performance note
114-
115-
Currently, the macro expands to the [builder syntax](https://github.com/leptos-rs/leptos/blob/main/docs/book/src/view/builder.md) (ish), but it has some [performance downsides](https://github.com/leptos-rs/leptos/issues/1492#issuecomment-1664675672) in SSR mode. This is expected to be fixed with the new renderer in Leptos `0.7`, so I'm not going to make this implementation.
116-
117113
## Compatibility
118114

119115
This macro will be compatible with the latest stable release of Leptos. The macro references Leptos items using `::leptos::...`, no items are re-exported from this crate. Therefore, this crate will likely work with any Leptos version if no view-related items are changed.
@@ -125,6 +121,9 @@ The below are the versions with which I have tested it to be working. It is like
125121
| `0.1` | `0.5` |
126122
| `0.2` | `0.5`, `0.6` |
127123
| `0.3` | `0.6` |
124+
| `0.4` | `0.7` |
125+
126+
This crate also has a feature `"nightly"` that enables better proc-macro diagnostics (simply enables the nightly feature in proc-macro-error2. Necessary while [this pr](https://github.com/GnomedDev/proc-macro-error-2/pull/5) is not yet merged).
128127

129128
## Syntax details
130129

@@ -182,7 +181,7 @@ The name of the parameter in the component function must be the same as the slot
182181

183182
Using the slots defined by the [`SlotIf` example linked](https://github.com/leptos-rs/leptos/blob/main/examples/slots/src/lib.rs):
184183
```rust
185-
use leptos::*;
184+
use leptos::prelude::*;
186185
use leptos_mview::mview;
187186

188187
#[component]
@@ -219,7 +218,7 @@ There are (currently) 3 main types of values you can pass in:
219218
input
220219
class="main"
221220
checked=true
222-
madeup=3
221+
data-index=3
223222
type={input_type}
224223
on:input={move |_| handle_input(1)};
225224
}
@@ -343,60 +342,6 @@ mview! {
343342

344343
Note that the `use:` directive automatically calls `.into()` on its argument, consistent with behaviour from Leptos.
345344

346-
#### Special Attributes
347-
348-
There are a few special attributes you can put on your component to emulate some features only available on HTML elements.
349-
350-
If a component has a `class` attribute, the classes using the selector syntax `.some-class` and dynamic classes `class:thing={signal}` can be passed in!
351-
352-
```rust
353-
#[component]
354-
// the `class` parameter should have these attributes and type to work properly
355-
fn TakesClasses(#[prop(optional, into)] class: TextProp) -> impl IntoView {
356-
mview! {
357-
// "my-component" will always be present, extra classes passed in will also be added
358-
div.my-component class=[class.get()] { "..." }
359-
}
360-
}
361-
362-
// <div class="my-component extra-class">
363-
mview! {
364-
TakesClasses.extra-class;
365-
};
366-
```
367-
368-
It is suggested to only pass in static classes (i.e. with selectors or just a plain `class="..."`), as using dynamic classes needs to construct a new string every time any of the signals change; dynamic classes are supported if you want them though.
369-
370-
```rust
371-
let signal = RwSignal::new(true);
372-
// <div class="my-component always-has-this special">
373-
mview! {
374-
TakesClasses.always-has-this class:special={signal};
375-
}
376-
signal.set(false);
377-
// becomes <div class="my-component always-has-this">
378-
```
379-
380-
There is one small difference from the `class:` syntax on HTML elements: the value passed in must be an `Fn() -> bool`, it cannot just be a `bool`.
381-
382-
This is also supported with an `id` attribute to forward `#my-id`, though not reactively.
383-
```rust
384-
#[component]
385-
// the `id` parameter should have these attributes and type to work properly
386-
fn TakesIds(#[prop(optional)] id: &'static str) -> impl IntoView {
387-
mview! {
388-
div {id} { "..." }
389-
}
390-
}
391-
392-
// <div id="my-unique-id">
393-
mview! {
394-
TakesIds #my-unique-id;
395-
};
396-
```
397-
398-
This is also supported on slots by having a `class` and `id` field with the same attributes and types as the components above.
399-
400345
### Children
401346

402347
You may have noticed that the `let:data` prop was missing from the previous section on directive attributes!
@@ -405,7 +350,7 @@ This is replaced with a closure right before the children block. This way, you c
405350
```rust
406351
mview! {
407352
Await
408-
future=[async { 3 }]
353+
future={async { 3 }}
409354
|monkeys| {
410355
p { {*monkeys} " little monkeys, jumping on the bed." }
411356
}
@@ -425,7 +370,7 @@ mview! {
425370

426371
Summary from the previous section on values in case you missed it: children can be literal strings (not bools or numbers!), blocks with Rust code inside (`{*monkeys}`), or the closure shorthand `[number() + 1]`.
427372

428-
Children with closures are also supported on slots, add a field `children: Callback<T, View>` to use it (`T` is whatever type you want).
373+
Children with closures are also supported on slots.
429374

430375
## Extra details
431376

@@ -454,7 +399,6 @@ If an attribute shorthand has hyphens:
454399

455400
Note the behaviour from Leptos: setting an HTML attribute to true adds the attribute with no value associated.
456401
```rust
457-
use leptos::view;
458402
view! { <input type="checkbox" checked=true data-smth=true not-here=false /> }
459403
```
460404
Becomes `<input type="checkbox" checked data-smth />`, NOT `checked="true"` or `data-smth="true"` or `not-here="false"`.

leptos-mview-core/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "leptos-mview-core"
3-
version = "0.3.2"
3+
version = "0.4.0"
44
edition = "2021"
55
license = "MIT OR Apache-2.0"
66
description = "Main implementation of leptos-mview"
@@ -13,4 +13,4 @@ readme = "README.md"
1313
syn = { version = "2", features = [] }
1414
quote = "1"
1515
proc-macro2 = "1"
16-
proc-macro-error = { version = "1", default-features = false }
16+
proc-macro-error2 = "2"

leptos-mview-core/src/ast/attribute/kv.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use proc_macro2::Span;
2-
use syn::{parse::Parse, parse_quote, Token};
2+
use syn::{parse::Parse, Token};
33

44
use crate::{
55
ast::{BracedKebabIdent, KebabIdent, Value},
@@ -52,8 +52,7 @@ impl Parse for KvAttr {
5252
let value = Value::parse_or_emit_err(input, eq.span);
5353
(ident, value)
5454
} else {
55-
// don't span the attribute name to the `true` or it becomes bool-colored
56-
let value = Value::Lit(parse_quote!(true));
55+
let value = Value::new_true();
5756
(ident, value)
5857
}
5958
};

leptos-mview-core/src/ast/children.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use proc_macro2::Span;
2-
use proc_macro_error::emit_error;
3-
use quote::ToTokens;
2+
use proc_macro_error2::emit_error;
3+
use quote::{quote, ToTokens};
44
use syn::{
55
ext::IdentExt,
66
parse::{Parse, ParseStream},
@@ -25,10 +25,13 @@ pub enum NodeChild {
2525

2626
impl ToTokens for NodeChild {
2727
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
28-
match self {
29-
Self::Value(v) => tokens.extend(v.into_token_stream()),
30-
Self::Element(e) => tokens.extend(e.into_token_stream()),
31-
}
28+
let child_tokens = match self {
29+
Self::Value(v) => v.into_token_stream(),
30+
Self::Element(e) => e.into_token_stream(),
31+
};
32+
tokens.extend(quote! {
33+
::leptos::prelude::IntoRender::into_render(#child_tokens)
34+
});
3235
}
3336
}
3437

leptos-mview-core/src/ast/element.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use proc_macro2::{TokenStream, TokenTree};
2-
use proc_macro_error::emit_error;
2+
use proc_macro_error2::emit_error;
33
use quote::{ToTokens, TokenStreamExt};
44
use syn::{
55
parse::{Parse, ParseStream},

leptos-mview-core/src/ast/ident.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use proc_macro2::{Span, TokenStream};
2-
use proc_macro_error::emit_error;
2+
use proc_macro_error2::emit_error;
33
use quote::{quote, ToTokens};
44
use syn::{
55
ext::IdentExt,
@@ -206,6 +206,13 @@ impl KebabIdentOrStr {
206206
}
207207
}
208208
}
209+
210+
pub fn to_unspanned_string(&self) -> String {
211+
match self {
212+
Self::KebabIdent(kebab_ident) => kebab_ident.repr().to_string(),
213+
Self::Str(lit_str) => lit_str.value(),
214+
}
215+
}
209216
}
210217

211218
impl Parse for KebabIdentOrStr {

leptos-mview-core/src/ast/tag.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,24 @@ impl Tag {
3939
/// Returns the [`Span`] of the tag identifier.
4040
///
4141
/// Component generics are not included in this span.
42-
///
43-
/// Use the [`Tag::ident`] function if the identifier itself is required.
4442
pub fn span(&self) -> Span {
4543
match self {
4644
Self::Html(ident) | Self::Svg(ident) | Self::Math(ident) => ident.span(),
4745
Self::WebComponent(ident) => ident.span(),
4846
Self::Component(path) => path.span(),
4947
}
5048
}
49+
50+
/// Returns the [`TagKind`] of this tag.
51+
pub fn kind(&self) -> TagKind {
52+
match self {
53+
Tag::Html(_) => TagKind::Html,
54+
Tag::Component(_) => TagKind::Component,
55+
Tag::Svg(_) => TagKind::Svg,
56+
Tag::Math(_) => TagKind::Math,
57+
Tag::WebComponent(_) => TagKind::WebComponent,
58+
}
59+
}
5160
}
5261

5362
impl Parse for Tag {

leptos-mview-core/src/ast/value.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
use proc_macro2::{Span, TokenStream};
2-
use proc_macro_error::{emit_error, Diagnostic};
2+
use proc_macro_error2::{emit_error, Diagnostic};
33
use quote::{quote, quote_spanned, ToTokens};
44
use syn::{
55
ext::IdentExt,
66
parse::{Parse, ParseStream},
7+
parse_quote,
78
spanned::Spanned,
89
};
910

@@ -130,7 +131,7 @@ impl Value {
130131
// incomplete typing; place a MissingValueAfterEq and continue
131132
let error = Diagnostic::spanned(
132133
span,
133-
proc_macro_error::Level::Error,
134+
proc_macro_error2::Level::Error,
134135
"expected value after =".to_string(),
135136
);
136137
// if the token after the `=` is an ident, perhaps the user forgot to wrap in
@@ -148,6 +149,9 @@ impl Value {
148149
}
149150
}
150151
}
152+
153+
/// Constructs self as a literal `true` with no span.
154+
pub fn new_true() -> Self { Self::Lit(parse_quote!(true)) }
151155
}
152156

153157
#[cfg(test)]

leptos-mview-core/src/error_ext.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
//!
44
//! A simplified version of the extension traits have been added here.
55
6-
use proc_macro_error::{abort, emit_error};
6+
use proc_macro_error2::{abort, emit_error};
77

88
pub trait ResultExt {
99
type Ok;

0 commit comments

Comments
 (0)