use crate::ouia;
use crate::prelude::OuiaComponentType;
use crate::utils::OuiaSafe;
use crate::{core::OptionalHtml, hooks::id::use_prop_id, utils::Ouia};
use web_sys::HtmlInputElement;
use yew::html::IntoPropValue;
use yew::prelude::*;
const OUIA: Ouia = ouia!("Checkbox");
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum CheckboxState {
Checked,
#[default]
Unchecked,
Indeterminate,
}
impl From<bool> for CheckboxState {
fn from(b: bool) -> Self {
match b {
true => Self::Checked,
false => Self::Unchecked,
}
}
}
impl From<CheckboxState> for bool {
fn from(value: CheckboxState) -> Self {
match value {
CheckboxState::Checked => true,
CheckboxState::Indeterminate | CheckboxState::Unchecked => false,
}
}
}
impl From<CheckboxState> for Option<bool> {
fn from(value: CheckboxState) -> Self {
match value {
CheckboxState::Checked => Some(true),
CheckboxState::Unchecked => Some(false),
CheckboxState::Indeterminate => None,
}
}
}
impl IntoPropValue<CheckboxState> for bool {
fn into_prop_value(self) -> CheckboxState {
self.into()
}
}
#[derive(Debug, Clone, PartialEq, Properties)]
pub struct CheckboxProperties {
#[prop_or_default]
pub id: Option<String>,
#[prop_or_default]
pub name: Option<AttrValue>,
#[prop_or_default]
pub class: Classes,
#[prop_or_default]
pub input_class: Classes,
#[prop_or_default]
pub valid: bool,
#[prop_or_default]
pub disabled: bool,
#[prop_or_default]
pub required: bool,
#[prop_or_default]
pub checked: CheckboxState,
#[prop_or_default]
pub onchange: Callback<CheckboxState>,
#[prop_or_default]
pub label: OptionalHtml,
#[prop_or_default]
pub aria_label: AttrValue,
#[prop_or_default]
pub description: OptionalHtml,
#[prop_or_default]
pub body: Option<Html>,
#[prop_or(String::from("div"))]
pub component: String,
#[prop_or_default]
pub ouia_id: Option<String>,
#[prop_or(OUIA.component_type())]
pub ouia_type: OuiaComponentType,
#[prop_or(OuiaSafe::TRUE)]
pub ouia_safe: OuiaSafe,
}
#[function_component(Checkbox)]
pub fn checkbox(props: &CheckboxProperties) -> Html {
let ouia_id = use_memo(props.ouia_id.clone(), |id| {
id.clone().unwrap_or(OUIA.generated_id())
});
let id = use_prop_id(props.id.clone());
let mut outer_class = classes!["pf-v5-c-check", props.class.clone()];
if props.label.is_none() {
outer_class.push("pf-m-standalone");
}
let node_ref = use_node_ref();
{
let node_ref = node_ref.clone();
let checked = props.checked;
use_effect(move || {
if let Some(elem) = node_ref.cast::<HtmlInputElement>() {
elem.set_indeterminate(checked == CheckboxState::Indeterminate)
}
});
}
let onchange = use_callback(
(props.onchange.clone(), node_ref.clone()),
|_: Event, (onchange, node_ref)| {
let checked = node_ref
.cast::<HtmlInputElement>()
.map(|input| input.checked().into())
.unwrap_or_default();
onchange.emit(checked);
},
);
let label = if let Some(label) = &props.label.0 {
let mut class = classes!["pf-v5-c-check__label"];
if props.disabled {
class.push("pf-m-disabled");
}
html! (
<label {class} for={(*id).clone()}>
{label.clone()}
if props.required {
<span class="pf-v5-c-check__label-required" aria-hidden="true">{"*"}</span>
}
</label>
)
} else {
html!()
};
html! (
<@{props.component.clone()} class={outer_class}>
<input
class={classes!["pf-v5-c-check__input", props.input_class.clone()]}
type="checkbox"
{onchange}
aria-invalid={props.valid.to_string()}
aria-label={props.aria_label.clone()}
disabled={props.disabled}
required={props.required}
id={(*id).clone()}
ref={node_ref.clone()}
checked={props.checked != CheckboxState::Unchecked}
data-ouia-component-id={(*ouia_id).clone()}
data-ouia-component-type={props.ouia_type}
data-ouia-safe={props.ouia_safe}
/>
{label}
if let Some(description) = &props.description.0 {
<span class="pf-v5-c-check__description">{description.clone()}</span>
}
if let Some(body) = &props.body {
<span class="pf-v5-c-check__body">{body.clone()}</span>
}
</@>
)
}