Skip to main content

leptix_password_toggle/
password_toggle.rs

1use leptix_core::primitive::{Primitive, VoidPrimitive};
2use leptos::{context::Provider, html, prelude::*};
3use leptos_node_ref::AnyNodeRef;
4
5#[derive(Clone, Debug)]
6struct PasswordToggleContextValue {
7    visible: RwSignal<bool>,
8}
9
10#[component]
11pub fn PasswordToggleField(
12    #[prop(into, optional)] default_visible: MaybeProp<bool>,
13    #[prop(into, optional)] as_child: MaybeProp<bool>,
14    #[prop(into, optional)] node_ref: AnyNodeRef,
15    children: TypedChildrenFn<impl IntoView + 'static>,
16) -> impl IntoView {
17    let children = StoredValue::new(children.into_inner());
18    let visible = RwSignal::new(default_visible.get().unwrap_or(false));
19    let ctx = PasswordToggleContextValue { visible };
20
21    view! {
22        <Provider value=ctx>
23            <Primitive element=html::div as_child=as_child node_ref=node_ref
24                attr:data-state=move || if visible.get() { "visible" } else { "hidden" }
25            >
26                {children.with_value(|c| c())}
27            </Primitive>
28        </Provider>
29    }
30}
31
32#[component]
33pub fn PasswordToggleFieldInput(
34    #[prop(into, optional)] as_child: MaybeProp<bool>,
35    #[prop(into, optional)] node_ref: AnyNodeRef,
36) -> impl IntoView {
37    let ctx = expect_context::<PasswordToggleContextValue>();
38
39    view! {
40        <VoidPrimitive element=html::input as_child=as_child node_ref=node_ref
41            attr:r#type=move || if ctx.visible.get() { "text" } else { "password" }
42            attr:data-state=move || if ctx.visible.get() { "visible" } else { "hidden" }
43        >
44            {""}
45        </VoidPrimitive>
46    }
47}
48
49#[component]
50pub fn PasswordToggleFieldToggle(
51    #[prop(into, optional)] as_child: MaybeProp<bool>,
52    #[prop(into, optional)] node_ref: AnyNodeRef,
53    children: TypedChildrenFn<impl IntoView + 'static>,
54) -> impl IntoView {
55    let children = StoredValue::new(children.into_inner());
56    let ctx = expect_context::<PasswordToggleContextValue>();
57
58    view! {
59        <Primitive element=html::button as_child=as_child node_ref=node_ref
60            attr:r#type="button"
61            attr:aria-label=move || if ctx.visible.get() { "Hide password" } else { "Show password" }
62            attr:aria-pressed=move || ctx.visible.get().to_string()
63            attr:data-state=move || if ctx.visible.get() { "visible" } else { "hidden" }
64            on:click=move |_| ctx.visible.set(!ctx.visible.get())
65        >
66            {children.with_value(|c| c())}
67        </Primitive>
68    }
69}
70
71#[component]
72pub fn PasswordToggleFieldIcon(
73    #[prop(into, optional)] as_child: MaybeProp<bool>,
74    #[prop(into, optional)] node_ref: AnyNodeRef,
75    #[prop(optional)] children: Option<ChildrenFn>,
76) -> impl IntoView {
77    let children = StoredValue::new(children);
78    let ctx = expect_context::<PasswordToggleContextValue>();
79
80    view! {
81        <Primitive element=html::span as_child=as_child node_ref=node_ref
82            attr:aria-hidden="true"
83            attr:data-state=move || if ctx.visible.get() { "visible" } else { "hidden" }
84        >
85            {children.with_value(|c| c.as_ref().map(|c| c()))}
86        </Primitive>
87    }
88}
89
90#[component]
91pub fn PasswordToggleFieldSlot(
92    #[prop(into, optional)] as_child: MaybeProp<bool>,
93    #[prop(into, optional)] node_ref: AnyNodeRef,
94    children: TypedChildrenFn<impl IntoView + 'static>,
95) -> impl IntoView {
96    let children = StoredValue::new(children.into_inner());
97
98    view! {
99        <Primitive element=html::div as_child=as_child node_ref=node_ref>
100            {children.with_value(|c| c())}
101        </Primitive>
102    }
103}