leptix_password_toggle/
password_toggle.rs1use 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}