impulse_thaw/combobox/
combobox_option.rs

1use super::listbox::ListboxInjection;
2use crate::ComboboxInjection;
3use leptos::prelude::*;
4use thaw_components::{Fallback, If, OptionComp, Then};
5use thaw_utils::class_list;
6
7#[component]
8pub fn ComboboxOption(
9    #[prop(optional, into)] class: MaybeProp<String>,
10    /// Sets an option to the disabled state. Disabled options cannot be selected,
11    /// but are still keyboard navigable.
12    #[prop(optional, into)]
13    disabled: Signal<bool>,
14    /// Defines a unique identifier for the option. Defaults to `text` if not provided.
15    #[prop(optional, into)]
16    value: Option<String>,
17    /// An optional override the string value of the Option's display text,
18    /// defaulting to the Option's child content.
19    #[prop(into)]
20    text: String,
21    #[prop(optional)] children: Option<Children>,
22) -> impl IntoView {
23    let combobox = ComboboxInjection::expect_context();
24    let listbox = ListboxInjection::expect_context();
25    let value = StoredValue::new(value.unwrap_or_else(|| text.clone()));
26    let text = StoredValue::new(text);
27    let is_selected = Memo::new(move |_| value.with_value(|value| combobox.is_selected(&value)));
28    let id = uuid::Uuid::new_v4().to_string();
29
30    let on_click = move |_| {
31        if disabled.get_untracked() {
32            return;
33        }
34        text.with_value(|text| {
35            value.with_value(|value| {
36                combobox.select_option(value, text);
37            });
38        });
39    };
40
41    {
42        combobox.insert_option(id.clone(), (value.get_value(), text.get_value(), disabled));
43        let id = id.clone();
44        listbox.trigger();
45        on_cleanup(move || {
46            combobox.remove_option(&id);
47            listbox.trigger();
48        });
49    }
50
51    view! {
52        <div
53            role="option"
54            aria-disabled=move || if disabled.get() { "true" } else { "" }
55            aria-selected=move || is_selected.get().to_string()
56            id=id
57            class=class_list![
58                "thaw-combobox-option",
59                ("thaw-combobox-option--selected", move || is_selected.get()),
60                ("thaw-combobox-option--disabled", move || disabled.get()),
61                class
62            ]
63            on:click=on_click
64        >
65            {if combobox.multiselect {
66                view! {
67                    <span aria-hidden="true" class="thaw-combobox-option__check-icon--multiselect">
68                        <If cond=is_selected>
69                            <Then slot>
70                                <svg
71                                    fill="currentColor"
72                                    aria-hidden="true"
73                                    width="12"
74                                    height="12"
75                                    viewBox="0 0 12 12"
76                                >
77                                    <path
78                                        d="M9.76 3.2c.3.29.32.76.04 1.06l-4.25 4.5a.75.75 0 0 1-1.08.02L2.22 6.53a.75.75 0 0 1 1.06-1.06l1.7 1.7L8.7 3.24a.75.75 0 0 1 1.06-.04Z"
79                                        fill="currentColor"
80                                    ></path>
81                                </svg>
82                            </Then>
83                        </If>
84                    </span>
85                }
86                    .into_any()
87            } else {
88                view! {
89                    <span aria-hidden="true" class="thaw-combobox-option__check-icon">
90                        <svg
91                            fill="currentColor"
92                            aria-hidden="true"
93                            width="1em"
94                            height="1em"
95                            viewBox="0 0 20 20"
96                        >
97                            <path
98                                d="M7.03 13.9 3.56 10a.75.75 0 0 0-1.12 1l4 4.5c.29.32.79.34 1.09.03l10.5-10.5a.75.75 0 0 0-1.06-1.06l-9.94 9.94Z"
99                                fill="currentColor"
100                            ></path>
101                        </svg>
102                    </span>
103                }
104                    .into_any()
105            }}
106            <OptionComp value=children let:children>
107                <Fallback slot>{text.get_value()}</Fallback>
108                {children()}
109            </OptionComp>
110        </div>
111    }
112}