use crate::{FieldInjection, FieldValidationState, Rule};
use leptos::{html, prelude::*};
use std::ops::Deref;
use thaw_utils::{class_list, mount_style, Model};
#[component]
pub fn Switch(
#[prop(optional, into)] class: MaybeProp<String>,
#[prop(optional, into)] id: MaybeProp<String>,
#[prop(optional, into)]
name: MaybeProp<String>,
#[prop(optional, into)]
value: MaybeProp<String>,
#[prop(optional, into)]
rules: Vec<SwitchRule>,
#[prop(optional, into)]
checked: Model<bool>,
#[prop(optional, into)]
label: MaybeProp<String>,
) -> impl IntoView {
mount_style("switch", include_str!("./switch.css"));
let (id, name) = FieldInjection::use_id_and_name(id, name);
let validate = Rule::validate(rules, checked, name);
let id = Signal::derive(move || id.get().unwrap_or_else(|| uuid::Uuid::new_v4().to_string()));
let input_ref = NodeRef::<html::Input>::new();
let on_change = move |_| {
let input = input_ref.get_untracked().unwrap();
let did_update = checked.try_maybe_update(|checked| {
if *checked != input.checked() {
*checked = input.checked();
(true, true)
} else {
(false, false)
}
});
if did_update.unwrap_or_default() {
validate.run(Some(SwitchRuleTrigger::Change));
}
};
view! {
<div class=class_list!["thaw-switch", class]>
<input
class="thaw-switch__input"
role="switch"
type="checkbox"
id=id
name=name
value=move || value.get()
checked=checked.get_untracked()
prop:checked=move || { checked.get() }
node_ref=input_ref
on:change=on_change
/>
<div aria-hidden="true" class="thaw-switch__indicator thaw-switch__button">
<svg
fill="currentColor"
aria-hidden="true"
width="1em"
height="1em"
viewBox="0 0 20 20"
>
<path d="M10 2a8 8 0 1 0 0 16 8 8 0 0 0 0-16Z" fill="currentColor"></path>
</svg>
</div>
{move || {
if let Some(label) = label.get() {
view! {
<label class="thaw-switch__label" for=id>
{label}
</label>
}
.into()
} else {
None
}
}}
</div>
}
}
#[derive(Debug, Default, PartialEq, Clone, Copy)]
pub enum SwitchRuleTrigger {
#[default]
Change,
}
pub struct SwitchRule(Rule<bool, SwitchRuleTrigger>);
impl SwitchRule {
pub fn validator(
f: impl Fn(&bool, Signal<Option<String>>) -> Result<(), FieldValidationState>
+ Send
+ Sync
+ 'static,
) -> Self {
Self(Rule::validator(f))
}
pub fn with_trigger(self, trigger: SwitchRuleTrigger) -> Self {
Self(Rule::with_trigger(self.0, trigger))
}
}
impl Deref for SwitchRule {
type Target = Rule<bool, SwitchRuleTrigger>;
fn deref(&self) -> &Self::Target {
&self.0
}
}