vizia_core/views/
collapsible.rs1use crate::{icons::ICON_CHEVRON_DOWN, prelude::*};
2
3pub enum CollapsibleEvent {
5 ToggleOpen,
6}
7
8pub struct Collapsible {
26 is_open: Signal<bool>,
27 on_toggle: Option<Box<dyn Fn(&mut EventContext, bool)>>,
28}
29
30impl Collapsible {
31 pub fn new(
33 cx: &mut Context,
34 header: impl Fn(&mut Context),
35 content: impl Fn(&mut Context),
36 ) -> Handle<Self> {
37 let is_open = Signal::new(false);
38
39 Self { is_open, on_toggle: None }
40 .build(cx, |cx| {
41 let entity = cx.current();
42 HStack::new(cx, |cx| {
44 header(cx);
45 Svg::new(cx, ICON_CHEVRON_DOWN)
46 .class("expand-icon")
47 .on_press(|cx| cx.emit(CollapsibleEvent::ToggleOpen));
48 })
49 .navigable(true)
50 .role(Role::Button)
51 .expanded(is_open)
52 .class("header")
53 .controls(format!("{}", entity))
54 .on_press(|cx| cx.emit(CollapsibleEvent::ToggleOpen));
55
56 VStack::new(cx, |cx| {
58 content(cx);
59 })
60 .id(format!("{}", entity))
61 .class("content");
62 })
63 .toggle_class("open", is_open)
64 }
65}
66
67impl View for Collapsible {
68 fn element(&self) -> Option<&'static str> {
69 Some("collapsible")
70 }
71
72 fn event(&mut self, cx: &mut EventContext, event: &mut Event) {
73 event.map(|collapsible_event, _| match collapsible_event {
74 CollapsibleEvent::ToggleOpen => {
75 self.is_open.set(!self.is_open.get());
76
77 if let Some(callback) = &self.on_toggle {
78 (callback)(cx, self.is_open.get());
79 }
80 }
81 });
82 }
83}
84
85impl Handle<'_, Collapsible> {
86 pub fn open(self, open: impl Res<bool> + 'static) -> Self {
88 let open = open.to_signal(self.cx);
89 self.bind(open, move |handle| {
90 let open = open.get();
91 handle.modify(|collapsible| {
92 collapsible.is_open.set_if_changed(open);
93 });
94 })
95 }
96
97 pub fn on_toggle(self, callback: impl Fn(&mut EventContext, bool) + 'static) -> Self {
99 self.modify(|collapsible| collapsible.on_toggle = Some(Box::new(callback)))
100 }
101}