biji_ui/components/accordion/
item.rs1use std::time::Duration;
2
3use leptos::{
4 context::Provider,
5 ev::{click, focus, keydown},
6 prelude::*,
7};
8use leptos_use::use_event_listener;
9
10use crate::{
11 cn,
12 custom_animated_show::CustomAnimatedShow,
13 items::{Focus, ManageFocus, NavigateItems, Toggle},
14};
15
16use super::context::{ItemContext, RootContext};
17
18#[component]
19pub fn Item(
20 #[prop(default = false)] disabled: bool,
21 #[prop(into, optional)] class: String,
22 children: Children,
23) -> impl IntoView {
24 let root_ctx = expect_context::<RootContext>();
25
26 let index = root_ctx.next_index();
27
28 let item_ctx = ItemContext {
29 index,
30 disabled,
31 allow_loop: root_ctx.allow_loop,
32 ..Default::default()
33 };
34
35 root_ctx.upsert_item(index, item_ctx);
36
37 on_cleanup(move || {
38 root_ctx.remove_item(index);
39 });
40
41 view! {
42 <Provider value={item_ctx}>
43 <div
44 class={class}
45 data-index={item_ctx.index}
46 data-state={move || item_ctx.data_state()}
47 data-disabled={item_ctx.disabled}
48 data-highlighted={move || root_ctx.item_in_focus(item_ctx.index)}
49 >
50 {children()}
51 </div>
52 </Provider>
53 }
54}
55
56#[component]
57pub fn ItemToggle(#[prop(into, optional)] class: String, children: Children) -> impl IntoView {
58 let root_ctx = expect_context::<RootContext>();
59 let item_ctx = expect_context::<ItemContext>();
60
61 let trigger_ref = item_ctx.trigger_ref;
62 view! {
63 <ItemToggleEvents>
64 <button
65 node_ref={trigger_ref}
66 class={class}
67 data-index={item_ctx.index}
68 data-state={move || item_ctx.data_state()}
69 data-disabled={item_ctx.disabled}
70 data-highlighted={move || root_ctx.item_in_focus(item_ctx.index)}
71 >
72 {children()}
73 </button>
74 </ItemToggleEvents>
75 }
76}
77
78#[component]
79pub fn ItemToggleEvents(children: Children) -> impl IntoView {
80 let root_ctx = expect_context::<RootContext>();
81 let item_ctx = expect_context::<ItemContext>();
82
83 let _ = use_event_listener(item_ctx.trigger_ref, click, move |_| {
84 item_ctx.toggle();
85 });
86
87 let _ = use_event_listener(item_ctx.trigger_ref, keydown, move |evt| {
88 let key = evt.key();
89 match key.as_str() {
90 "ArrowDown" => {
91 if let Some(item) = root_ctx.navigate_next_item() {
92 evt.prevent_default();
93 item.focus();
94 }
95 }
96 "ArrowUp" => {
97 if let Some(item) = root_ctx.navigate_previous_item() {
98 evt.prevent_default();
99 item.focus();
100 }
101 }
102 _ => {}
103 };
104 });
105
106 let _ = use_event_listener(item_ctx.trigger_ref, focus, move |_| {
107 root_ctx.set_focus(Some(item_ctx.index));
108 });
109
110 children()
111}
112
113#[component]
114pub fn ItemContent(
115 children: ChildrenFn,
116 #[prop(into, optional)]
118 class: String,
119 #[prop(into, optional)]
121 show_class: String,
122 #[prop(into, optional)]
124 hide_class: String,
125 #[prop(default = Duration::from_millis(200))]
127 hide_delay: Duration,
128) -> impl IntoView {
129 let ctx = expect_context::<ItemContext>();
130
131 view! {
133 <CustomAnimatedShow
134 when={ctx.open}
135 show_class={cn!(class, show_class)}
136 hide_class={cn!(class, hide_class)}
137 hide_delay={hide_delay}
138 >
139 {children()}
140 </CustomAnimatedShow>
141 }
142}