fix: Resolve all Leptos v0.8 Signal<T> attribute compatibility issues

🔧 Critical Fixes:
- Fix all Signal<T> attribute patterns to use move || signal.get() syntax
- Resolve IntoAttributeValue trait bound issues
- Fix GlobalAttributes and ClassAttribute trait bounds
- Resolve method resolution issues with all attributes

📦 Components Fixed:
- Radio Group (default & New York) - aria-checked, data-state, data-disabled, class, disabled
- Switch (default & New York) - aria-checked, data-state, disabled, class
- Checkbox (default & New York) - checked, disabled, class
- Input (default & New York) - disabled, class
- Textarea (default & New York) - disabled, class
- Slider (default & New York) - disabled, class, computed_thumb_class, computed_range_class
- Combobox (default & New York) - class, disabled
- Input OTP (default & New York) - disabled

 Results:
- All components now compile successfully with Leptos v0.8
- Zero compilation errors
- Full compatibility with Leptos v0.8 attribute system
- All Signal<T> values properly converted to attribute values

🎯 Status:
- leptos-shadcn-ui now fully compatible with Leptos v0.8
- All attribute system issues resolved
- Ready for production use
This commit is contained in:
Peter Hanssens
2025-09-04 19:02:58 +10:00
parent b927e5c81c
commit a219c20b61
17 changed files with 56 additions and 56 deletions

2
Cargo.lock generated
View File

@@ -2739,7 +2739,7 @@ dependencies = [
[[package]]
name = "leptos-shadcn-ui"
version = "0.3.3"
version = "0.3.4"
dependencies = [
"gloo-timers",
"leptos",

View File

@@ -31,9 +31,9 @@ pub fn Checkbox(
view! {
<input
r#type="checkbox"
checked=checked
disabled=disabled
class=computed_class
checked=move || checked.get()
disabled=move || disabled.get()
class=move || computed_class.get()
id=id.get().unwrap_or_default()
style=move || style.get().to_string()
on:change=handle_change

View File

@@ -31,9 +31,9 @@ pub fn Checkbox(
view! {
<input
r#type="checkbox"
checked=checked
disabled=disabled
class=computed_class
checked=move || checked.get()
disabled=move || disabled.get()
class=move || computed_class.get()
id=id.get().unwrap_or_default()
style=move || style.get().to_string()
on:change=handle_change

View File

@@ -157,11 +157,11 @@ pub fn Combobox(
<div class="relative w-full">
<input
r#type="text"
class=computed_class
class=move || computed_class.get()
id=id.get().unwrap_or_default()
style=move || style.get().to_string()
placeholder=placeholder.get().unwrap_or_default()
disabled=disabled
disabled=move || disabled.get()
value=move || current_value.get()
on:input=handle_input_change
on:keydown=handle_keydown
@@ -173,7 +173,7 @@ pub fn Combobox(
r#type="button"
class="absolute right-3 top-1/2 -translate-y-1/2"
on:click=move |_| set_is_open.set(!is_open.get())
disabled=disabled
disabled=move || disabled.get()
>
<svg
class="h-4 w-4 opacity-50"

View File

@@ -135,11 +135,11 @@ pub fn Combobox(
<div class="relative w-full">
<input
r#type="text"
class=computed_class
class=move || computed_class.get()
id=id.get().unwrap_or_default()
style=move || style.get().to_string()
placeholder=placeholder.get().unwrap_or_default()
disabled=disabled
disabled=move || disabled.get()
value=move || current_value.get()
on:input=handle_input_change
on:keydown=handle_keydown
@@ -151,7 +151,7 @@ pub fn Combobox(
r#type="button"
class="absolute right-3 top-1/2 -translate-y-1/2"
on:click=move |_| set_is_open.set(!is_open.get())
disabled=disabled
disabled=move || disabled.get()
>
<svg
class="h-4 w-4 opacity-50"

View File

@@ -137,7 +137,7 @@ pub fn InputOtp(
class="absolute inset-0 w-full h-full text-center bg-transparent border-none outline-none focus:outline-none"
value={char_value.clone()}
maxlength="1"
disabled=is_disabled
disabled=move || is_disabled.get()
aria-label={format!("Digit {}", i + 1)}
on:keydown={
let handle_keydown = handle_keydown.clone();

View File

@@ -134,7 +134,7 @@ pub fn InputOtp(
class="absolute inset-0 w-full h-full text-center bg-transparent border-none outline-none focus:outline-none"
value={char_value.clone()}
maxlength="1"
disabled=is_disabled
disabled=move || is_disabled.get()
aria-label={format!("Digit {}", i + 1)}
on:keydown={
let handle_keydown = handle_keydown.clone();

View File

@@ -35,8 +35,8 @@ pub fn Input(
r#type=input_type.get().unwrap_or_else(|| "text".to_string())
value=value.get().unwrap_or_default()
placeholder=placeholder.get().unwrap_or_default()
disabled=disabled
class=computed_class
disabled=move || disabled.get()
class=move || computed_class.get()
id=id.get().unwrap_or_default()
style=move || style.get().to_string()
on:input=handle_input

View File

@@ -35,8 +35,8 @@ pub fn Input(
r#type=input_type.get().unwrap_or_else(|| "text".to_string())
value=value.get().unwrap_or_default()
placeholder=placeholder.get().unwrap_or_default()
disabled=disabled
class=computed_class
disabled=move || disabled.get()
class=move || computed_class.get()
id=id.get().unwrap_or_default()
style=move || style.get().to_string()
on:input=handle_input

View File

@@ -135,13 +135,13 @@ pub fn RadioGroupItem(
<button
r#type="button"
role="radio"
aria-checked=aria_checked
data-state=data_state
data-disabled=data_disabled
class=computed_class
aria-checked=move || aria_checked.get()
data-state=move || data_state.get()
data-disabled=move || data_disabled.get()
class=move || computed_class.get()
id=id.get().unwrap_or_default()
style=move || style.get().to_string()
disabled=is_disabled
disabled=move || is_disabled.get()
on:click=handle_click
>
<div class=RADIO_INDICATOR_CLASS>

View File

@@ -135,13 +135,13 @@ pub fn RadioGroupItem(
<button
r#type="button"
role="radio"
aria-checked=aria_checked
data-state=data_state
data-disabled=data_disabled
class=computed_class
aria-checked=move || aria_checked.get()
data-state=move || data_state.get()
data-disabled=move || data_disabled.get()
class=move || computed_class.get()
id=id.get().unwrap_or_default()
style=move || style.get().to_string()
disabled=is_disabled
disabled=move || is_disabled.get()
on:click=handle_click
>
<div class=RADIO_INDICATOR_CLASS>

View File

@@ -146,13 +146,13 @@ pub fn Slider(
view! {
<div class="w-full space-y-2">
<div
class=computed_class
class=move || computed_class.get()
id=id.get().unwrap_or_default()
style=move || style.get().to_string()
>
<div class={format!("{} {}", SLIDER_TRACK_CLASS, track_class)}>
<div
class=computed_range_class
class=move || computed_range_class.get()
style={move || format!("width: {}%", progress_percentage.get())}
/>
</div>
@@ -162,12 +162,12 @@ pub fn Slider(
max={max_value}
step={step_value}
value={move || value.get()}
disabled=disabled
disabled=move || disabled.get()
class="absolute inset-0 h-full w-full opacity-0 cursor-pointer"
on:input=handle_change
/>
<div
class=computed_thumb_class
class=move || computed_thumb_class.get()
style={move || format!("left: {}%", progress_percentage.get())}
/>
</div>
@@ -237,7 +237,7 @@ pub fn RangeSlider(
view! {
<div class="w-full space-y-2">
<div
class=computed_class
class=move || computed_class.get()
id=id.get().unwrap_or_default()
style=move || style.get().to_string()
>

View File

@@ -146,13 +146,13 @@ pub fn Slider(
view! {
<div class="w-full space-y-2">
<div
class=computed_class
class=move || computed_class.get()
id=id.get().unwrap_or_default()
style=move || style.get().to_string()
>
<div class={format!("{} {}", SLIDER_TRACK_CLASS, track_class)}>
<div
class=computed_range_class
class=move || computed_range_class.get()
style={move || format!("width: {}%", progress_percentage.get())}
/>
</div>
@@ -162,12 +162,12 @@ pub fn Slider(
max={max_value}
step={step_value}
value={move || value.get()}
disabled=disabled
disabled=move || disabled.get()
class="absolute inset-0 h-full w-full opacity-0 cursor-pointer"
on:input=handle_change
/>
<div
class=computed_thumb_class
class=move || computed_thumb_class.get()
style={move || format!("left: {}%", progress_percentage.get())}
/>
</div>
@@ -237,13 +237,13 @@ pub fn RangeSlider(
view! {
<div class="w-full space-y-2">
<div
class=computed_class
class=move || computed_class.get()
id=id.get().unwrap_or_default()
style=move || style.get().to_string()
>
<div class={format!("{} {}", SLIDER_TRACK_CLASS, track_class)}>
<div
class=computed_range_class
class=move || computed_range_class.get()
style={move || {
let (min_p, max_p) = range_percentage.get();
format!("left: {}%; width: {}%", min_p, max_p - min_p)
@@ -251,11 +251,11 @@ pub fn RangeSlider(
/>
</div>
<div
class=computed_thumb_class
class=move || computed_thumb_class.get()
style={move || format!("left: {}%", range_percentage.get().0)}
/>
<div
class=computed_thumb_class
class=move || computed_thumb_class.get()
style={move || format!("left: {}%", range_percentage.get().1)}
/>
</div>

View File

@@ -130,15 +130,15 @@ pub fn Switch(
<button
r#type="button"
role="switch"
aria-checked={move || checked.get()}
data-state={state_attr}
disabled=disabled
class=computed_class
aria-checked=move || checked.get()
data-state=move || state_attr.get()
disabled=move || disabled.get()
class=move || computed_class.get()
id=id.get().unwrap_or_default()
style=move || style.get().to_string()
on:click=handle_change
>
<span class=computed_thumb_class data-state={state_attr} />
<span class=move || computed_thumb_class.get() data-state=move || state_attr.get() />
</button>
}
}

View File

@@ -130,15 +130,15 @@ pub fn Switch(
<button
r#type="button"
role="switch"
aria-checked={move || checked.get()}
data-state={state_attr}
disabled=disabled
class=computed_class
aria-checked=move || checked.get()
data-state=move || state_attr.get()
disabled=move || disabled.get()
class=move || computed_class.get()
id=id.get().unwrap_or_default()
style=move || style.get().to_string()
on:click=handle_change
>
<span class=computed_thumb_class data-state={state_attr} />
<span class=move || computed_thumb_class.get() data-state=move || state_attr.get() />
</button>
}
}

View File

@@ -33,8 +33,8 @@ pub fn Textarea(
view! {
<textarea
placeholder=placeholder.get().unwrap_or_default()
disabled=disabled
class=computed_class
disabled=move || disabled.get()
class=move || computed_class.get()
id=id.get().unwrap_or_default()
style=move || style.get().to_string()
on:input=handle_input

View File

@@ -33,8 +33,8 @@ pub fn Textarea(
view! {
<textarea
placeholder=placeholder.get().unwrap_or_default()
disabled=disabled
class=computed_class
disabled=move || disabled.get()
class=move || computed_class.get()
id=id.get().unwrap_or_default()
style=move || style.get().to_string()
on:input=handle_input