shadow_design/
dropdown.rs1use crate::icon::{Icon, IconKind};
2use dioxus::prelude::*;
3
4#[derive(Clone, Copy, PartialEq, Debug, Default)]
5pub enum DropdownPlacement {
6 #[default]
7 BottomRight,
8 BottomLeft,
9 TopRight,
10 TopLeft,
11}
12
13impl DropdownPlacement {
14 pub fn scss_class(&self) -> &'static str {
15 match self {
16 DropdownPlacement::BottomRight => "sd-dropdown-menu--bottom-right",
17 DropdownPlacement::BottomLeft => "sd-dropdown-menu--bottom-left",
18 DropdownPlacement::TopRight => "sd-dropdown-menu--top-right",
19 DropdownPlacement::TopLeft => "sd-dropdown-menu--top-left",
20 }
21 }
22}
23
24#[component]
25pub fn Dropdown(
26 is_open: Signal<bool>,
27 trigger: Element,
28 #[props(default)] placement: DropdownPlacement,
29 #[props(default)] glow: Option<bool>,
30 #[props(default)] class: String,
31 children: Element,
32) -> Element {
33 let open = *is_open.read();
34
35 #[cfg(feature = "theme")]
36 let theme = crate::theme::use_theme();
37 #[cfg(feature = "theme")]
38 let glow_class = match glow {
39 Some(true) => "sd-dropdown-menu--glow",
40 Some(false) => "",
41 None => {
42 if theme().glow_mode != crate::theme::GlowMode::Off {
43 "sd-dropdown-menu--glow"
44 } else {
45 ""
46 }
47 }
48 };
49
50 #[cfg(not(feature = "theme"))]
51 let glow_class = if glow == Some(true) {
52 "sd-dropdown-menu--glow"
53 } else {
54 ""
55 };
56
57 let open_container_class = if open { "sd-dropdown-container--open" } else { "" };
58 let placement_class = placement.scss_class();
59
60 #[cfg(feature = "animation")]
61 let anim_class = crate::animation::Animations::DROPDOWN_ENTER;
62 #[cfg(not(feature = "animation"))]
63 let anim_class = "animate-dropdown-enter";
64
65 rsx! {
66 div { class: "sd-dropdown-container {open_container_class} {class}",
67 div {
68 onclick: move |_| {
69 let next = !*is_open.read();
70 *is_open.write() = next;
71 },
72 {trigger}
73 }
74 if open {
75 div {
76 class: "sd-dropdown-backdrop",
77 onclick: move |e| {
78 e.stop_propagation();
79 *is_open.write() = false;
80 },
81 }
82 div {
83 class: "sd-dropdown-menu {placement_class} {glow_class} {anim_class}",
84 onclick: move |e| e.stop_propagation(),
85 {children}
86 }
87 }
88 }
89 }
90}
91
92#[component]
93pub fn DropdownItem(
94 #[props(default)] icon: Option<IconKind>,
95 #[props(default)] danger: bool,
96 #[props(default)] class: String,
97 onclick: Option<EventHandler<MouseEvent>>,
98 children: Element,
99) -> Element {
100 let danger_class = if danger { "sd-dropdown-item--danger" } else { "" };
101
102 rsx! {
103 button {
104 r#type: "button",
105 class: "sd-dropdown-item {danger_class} {class}",
106 onclick: move |e| {
107 if let Some(h) = onclick.as_ref() {
108 h.call(e);
109 }
110 },
111 if let Some(i) = icon {
112 Icon { kind: i, size: 14 }
113 }
114 {children}
115 }
116 }
117}
118
119#[component]
120pub fn DropdownDivider() -> Element {
121 rsx! {
122 div { class: "sd-dropdown-divider" }
123 }
124}