use std::marker::PhantomData;
use leptodon_proc_macros::generate_docs;
use leptos::context::Provider;
use leptos::prelude::{ClassAttribute, ElementChild, Get, IntoAny, MaybeProp, RwSignal, Show};
use leptos::{IntoView, component, prelude::Children, view};
#[derive(Clone, Copy)]
pub struct FormInputContext<E: Clone + Send + Sync + std::fmt::Display + 'static> {
pub required: bool,
pub label: MaybeProp<String>,
pub feedback: RwSignal<Option<E>>,
}
#[generate_docs]
#[component]
pub fn FormInput<E>(
#[prop(into)] label: String,
required: bool,
children: Children,
#[prop(default = PhantomData)] _phantom: PhantomData<E>,
) -> impl IntoView
where
E: Clone + Send + Sync + std::fmt::Display + 'static,
{
view! {
<MaybeLabelledFormInput<E> label required children />
}
}
#[component]
pub(crate) fn MaybeLabelledFormInput<E>(
#[prop(optional, into)] label: MaybeProp<String>,
required: bool,
children: Children,
#[prop(default = PhantomData)] _phantom: PhantomData<E>,
) -> impl IntoView
where
E: Clone + Send + Sync + std::fmt::Display + 'static,
{
let feedback: RwSignal<Option<E>> = RwSignal::new(None);
let form_ctx = FormInputContext {
required,
label,
feedback,
};
view! {
<div class="mb-2 flex flex-col">
<Label label required>
<Provider<_, _> value=form_ctx>
{children()}
</Provider<_, _>>
</Label>
<Show
when=move || feedback.get().is_some()
fallback=|| ()
>
<span class="text-red-500">{
move || feedback.get().unwrap().to_string()
}</span>
</Show>
</div>
}
}
#[component]
pub fn Label(
#[prop(optional, into)] label: MaybeProp<String>,
required: bool,
children: Children,
) -> impl IntoView {
if let Some(label) = label.get() {
view! {
<label class="block">
<div class="text-sm font-semibold text-heading text-gray-900 dark:text-gray-100">
<RequiredStar required/>
{label}
</div>
{children()}
</label>
}
.into_any()
} else {
view! { {children()} }.into_any()
}
}
#[component]
pub fn PostfixLabelStyle(required: bool, children: Children) -> impl IntoView {
view! {
<span class="ms-2 text-sm font-medium text-gray-900 dark:text-gray-100"><RequiredStar required=required/>{children()}</span>
}
}
#[component]
pub fn RequiredStar(required: bool) -> impl IntoView {
view! {
<Show
when=move || required
fallback=|| ()
>
<span class="text-red-500">*</span>
</Show>
}
}