fc_dioxus/components/
fab.rs1use crate::{
3 core::{
4 assets,
5 attrs::{availability_attributes, component_attributes},
6 },
7 prelude::*,
8 theme::use_stylesheets,
9};
10const BEM: Bem = Bem::new("fc-fab");
11const MENU: Bem = Bem::new("fc-fab-menu");
12const BTN: Bem = Bem::new("fc-btn");
13fc_modifier! {
14 pub enum FabTone for BEM {
16 Default => None,
18 Primary => "primary",
20 Surface => "surface",
22 }
23}
24fc_modifier! {
25 pub enum FabSize for BEM {
27 Md => None,
29 Sm => "sm",
31 Lg => "lg",
33 }
34}
35fc_modifier! { enum FabShape for BEM { Icon => None, Extended => "extended" } }
36fc_modifier! { enum FabElevation for BEM { Raised => None, Lowered => "lowered" } }
37fc_modifier! { enum FabPosition for BEM { Inline => None, Fixed => "fixed" } }
38
39#[component]
44pub fn Fab(
45 icon: Element,
46 label: AccessibleName,
47 #[props(default)] extended: bool,
48 #[props(default)] tone: FabTone,
49 #[props(default)] size: FabSize,
50 #[props(default)] lowered: bool,
51 #[props(default)]
53 fixed: bool,
54 #[props(default)] availability: Availability,
55 #[props(default)] busy: bool,
56 #[props(default)]
58 menu: Option<Id>,
59 #[props(default)] onclick: Option<EventHandler<MouseEvent>>,
60 #[props(default)] class: Option<String>,
61 #[props(extends = GlobalAttributes)] attributes: Vec<Attribute>,
62) -> Element {
63 let blocked = busy || availability != Availability::Enabled;
64 let classes = ClassList::new(BEM.block())
65 .with_modifier(BEM, tone)
66 .with_modifier(BEM, size)
67 .with_modifier(
68 BEM,
69 if extended {
70 FabShape::Extended
71 } else {
72 FabShape::Icon
73 },
74 )
75 .with_modifier(
76 BEM,
77 if lowered {
78 FabElevation::Lowered
79 } else {
80 FabElevation::Raised
81 },
82 )
83 .with_modifier(
84 BEM,
85 if fixed {
86 FabPosition::Fixed
87 } else {
88 FabPosition::Inline
89 },
90 )
91 .extend(class.as_deref());
92 let mut attributes = component_attributes(
93 attributes,
94 classes,
95 &[
96 "type",
97 "aria-label",
98 "aria-busy",
99 "disabled",
100 "aria-disabled",
101 ],
102 );
103 attributes.extend(availability_attributes(&availability));
104 if !extended {
105 attributes.push(Attribute::new("aria-label", label.to_string(), None, false));
106 }
107 if busy {
108 attributes.push(Attribute::new("aria-busy", "true", None, false));
109 }
110 if let Some(menu) = &menu {
111 attributes.push(Attribute::new(
112 "popovertarget",
113 menu.to_string(),
114 None,
115 false,
116 ));
117 }
118 rsx! {
119 if use_stylesheets() {
120 document::Stylesheet { href: assets::COMPONENTS_BUTTON_CSS }
121 document::Stylesheet { href: assets::COMPONENTS_FAB_CSS }
122 }
123 button { r#type: "button",
124 onclick: move |event: MouseEvent| {
125 if blocked { event.prevent_default(); event.stop_propagation(); }
126 else if let Some(onclick) = onclick { onclick.call(event); }
127 },
128 ..attributes,
129 if busy { span { class: BTN.element("spinner"), aria_hidden: "true" } }
130 else { span { class: BEM.element("icon"), aria_hidden: "true", {icon} } }
131 if extended { "{label}" }
132 }
133 }
134}
135
136#[component]
138pub fn FabMenu(
139 id: Id,
140 #[props(default)] class: Option<String>,
141 #[props(extends = GlobalAttributes)] attributes: Vec<Attribute>,
142 children: Element,
143) -> Element {
144 let mut attributes = component_attributes(
145 attributes,
146 ClassList::new(MENU.block()).extend(class.as_deref()),
147 &["id", "popover"],
148 );
149 attributes.push(Attribute::new("popover", "auto", None, false));
150 rsx! {
151 if use_stylesheets() { document::Stylesheet { href: assets::COMPONENTS_FAB_CSS } }
152 div { id: "{id}", ..attributes, {children} }
153 }
154}
155
156#[component]
158pub fn FabMenuItem(
159 #[props(default)] availability: Availability,
160 #[props(default)] onclick: Option<EventHandler<MouseEvent>>,
161 children: Element,
162) -> Element {
163 let onclick = availability.guard(onclick);
164 let state = availability_attributes(&availability);
165 rsx! {
166 button { class: MENU.element("item"), r#type: "button",
167 onclick: move |event: MouseEvent| if let Some(onclick) = onclick { onclick.call(event); } else { event.prevent_default(); },
168 ..state, {children}
169 }
170 }
171}