material_dioxus/
list.rs

1mod list_item;
2pub use list_item::*;
3
4mod check_list_item;
5pub use check_list_item::*;
6
7mod radio_list_item;
8pub use radio_list_item::*;
9
10mod separator;
11pub use separator::*;
12
13mod list_index;
14pub use list_index::ListIndex;
15
16mod selected_detail;
17pub use selected_detail::{IndexDiff, SelectedDetail};
18
19mod action_detail;
20pub use action_detail::ActionDetail;
21
22mod request_selected;
23pub use request_selected::{RequestSelectedDetail, RequestSelectedSource};
24
25mod graphic_type;
26pub use graphic_type::GraphicType;
27
28use dioxus::prelude::*;
29use gloo::events::EventListener;
30use wasm_bindgen::prelude::*;
31use web_sys::Node;
32
33use crate::{event_into_details, StaticCallback};
34
35#[wasm_bindgen(module = "/build/mwc-list.js")]
36extern "C" {
37    #[derive(Debug)]
38    #[wasm_bindgen(extends = Node)]
39    type List;
40
41    #[wasm_bindgen(getter, static_method_of = List)]
42    fn _dummy_loader() -> JsValue;
43
44    #[wasm_bindgen(method, getter)]
45    fn index(this: &List) -> JsValue;
46
47    #[wasm_bindgen(method)]
48    fn toggle(this: &List, index: usize, force: bool);
49
50    #[wasm_bindgen(method, js_name = getFocusedItemIndex)]
51    fn get_focused_item_index(this: &List) -> usize;
52
53    #[wasm_bindgen(method, js_name = focusItemAtIndex)]
54    fn focus_item_at_index(this: &List, index: usize);
55}
56
57loader_hack!(List);
58
59/// Props for [`MatList`]
60///
61/// MWC Documentation:
62///
63/// - [Properties](https://github.com/material-components/material-components-web-components/tree/v0.27.0/packages/list#mwc-list-1)
64/// - [Events](https://github.com/material-components/material-components-web-components/tree/v0.27.0/packages/list#mwc-list-2)
65#[derive(Props)]
66pub struct ListProps<'a> {
67    #[props(default)]
68    pub activatable: bool,
69    #[props(default)]
70    pub root_tabbable: bool,
71    #[props(default)]
72    pub multi: bool,
73    #[props(default)]
74    pub wrap_focus: bool,
75    #[props(into)]
76    pub item_roles: Option<String>,
77    #[props(into)]
78    pub inner_role: Option<String>,
79    #[props(default)]
80    pub noninteractive: bool,
81    /// Binds to `action` event on `mwc-list`
82    #[props(into)]
83    // the name cannot start with `on` or dioxus will expect an `EventHandler` which aren't static
84    // and thus cannot be used here
85    pub _onaction: Option<StaticCallback<ListIndex>>,
86    /// Binds to `selected` event `mwc-list`
87    #[props(into)]
88    pub _onselected: Option<StaticCallback<SelectedDetail>>,
89    // TODO: make methods callable
90    // /// [`WeakComponentLink`] for `MatList` which provides the following methods
91    // /// - ```toggle(&self, index: usize, force: bool)```
92    // /// - ```get_focused_item_index(&self) -> usize```
93    // /// - ```focus_item_at_index(&self, index: usize)```
94    // ///
95    // /// See [`WeakComponentLink`] documentation for more information
96    // #[props(default)]
97    // pub list_link: WeakComponentLink<MatList>,
98    pub children: Element<'a>,
99
100    #[props(into, default)]
101    pub style: String,
102    #[props(into, default)]
103    pub class: String,
104    #[props(into)]
105    pub slot: Option<String>,
106}
107
108fn render<'a>(cx: Scope<'a, ListProps<'a>>) -> Element<'a> {
109    let id = crate::use_id(cx, "list");
110    let selected_listener = cx.use_hook(|| None);
111    let action_listener = cx.use_hook(|| None);
112    if let Some(elem) = crate::get_elem_by_id(id) {
113        let target = elem.clone();
114        let list = JsValue::from(elem).dyn_into::<List>().unwrap();
115        if let Some(listener) = cx.props._onselected.clone() {
116            *selected_listener = Some(EventListener::new(&target, "selected", move |event| {
117                let val = SelectedDetail::from(event_into_details(event));
118                listener.call(val)
119            }));
120        }
121        if let Some(listener) = cx.props._onaction.clone() {
122            *action_listener = Some(EventListener::new(&target, "action", move |_| {
123                let val: JsValue = list.index();
124                let index = ListIndex::from(val);
125                listener.call(index)
126            }));
127        }
128    }
129
130    render! {
131        mwc-list {
132            id: id,
133
134            activatable: bool_attr!(cx.props.activatable),
135            rootTabbable: bool_attr!(cx.props.root_tabbable),
136            multi: bool_attr!(cx.props.multi),
137            wrapFocus: bool_attr!(cx.props.wrap_focus),
138            itemRoles: optional_string_attr!(cx.props.item_roles),
139            innerRole: optional_string_attr!(cx.props.inner_role),
140            noninteractive: bool_attr!(cx.props.noninteractive),
141
142            style: string_attr!(cx.props.style),
143            class: string_attr!(cx.props.class),
144            slot: optional_string_attr!(cx.props.slot),
145
146            &cx.props.children
147        }
148    }
149}
150
151component!('a, MatList, ListProps, render, List, "list");