accordion_rs/
dioxus.rs

1use crate::common::{Align, Size};
2use dioxus::prelude::*;
3
4/// Properties for the Accordion component.
5#[derive(Props, PartialEq, Clone)]
6pub struct AccordionProps {
7    /// A signal that manages the expansion state of the accordion.
8    ///
9    /// This property determines whether the accordion is expanded (`true`) or collapsed (`false`).
10    /// It is required and is used to toggle the accordion's state.
11    pub expand: Signal<bool>,
12
13    /// The content to display when the accordion is expanded.
14    ///
15    /// This content will be visible only when the accordion is in an expanded state.
16    pub expanded: Element,
17
18    /// The content to display when the accordion is collapsed.
19    ///
20    /// This content will be visible only when the accordion is in a collapsed state.
21    pub collapsed: Element,
22
23    /// The child elements to display within the accordion container.
24    ///
25    /// Any additional content provided as children will be rendered inside the accordion body.
26    pub children: Element,
27
28    /// Size of the accordion.
29    ///
30    /// Defines the size of the accordion component. Typically used for layout adjustments.
31    /// Defaults to an `XXLarge` if not specified.
32    #[props(default)]
33    pub size: Size,
34
35    /// ARIA controls attribute for accessibility.
36    ///
37    /// Links the accordion container to another element, improving accessibility for screen readers.
38    /// Defaults to an empty string.
39    #[props(default = "")]
40    pub aria_controls: &'static str,
41
42    /// Custom inline styles for the accordion container.
43    ///
44    /// Specifies additional CSS styling for the accordion's container. Defaults to an empty string.
45    #[props(default = "")]
46    pub style: &'static str,
47
48    /// Custom inline styles for the expanded state.
49    ///
50    /// Defines additional styling for the accordion when it is expanded. Defaults to an empty string.
51    #[props(default = "")]
52    pub expanded_style: &'static str,
53
54    /// Custom inline styles for the collapsed state.
55    ///
56    /// Defines additional styling for the accordion when it is collapsed. Defaults to an empty string.
57    #[props(default = "")]
58    pub collapsed_style: &'static str,
59
60    /// Custom inline styles for the content section.
61    ///
62    /// Specifies additional styling for the content within the accordion. Defaults to an empty string.
63    #[props(default = "")]
64    pub content_style: &'static str,
65
66    /// Custom CSS class for the accordion container.
67    ///
68    /// Applies a custom class to the container element for styling purposes. Defaults to an empty string.
69    #[props(default = "")]
70    pub class: &'static str,
71
72    /// Custom CSS class for the expanded state.
73    ///
74    /// Specifies a class that is applied to the accordion when it is in an expanded state. Defaults to an empty string.
75    #[props(default = "")]
76    pub expanded_class: &'static str,
77
78    /// Custom CSS class for the collapsed state.
79    ///
80    /// Specifies a class that is applied to the accordion when it is in a collapsed state. Defaults to an empty string.
81    #[props(default = "")]
82    pub collapsed_class: &'static str,
83
84    /// Custom CSS class for the content container.
85    ///
86    /// Applies a class to the accordion's content container for styling purposes. Defaults to an empty string.
87    #[props(default = "")]
88    pub content_class: &'static str,
89
90    /// Indicates whether ARIA attributes should be included.
91    ///
92    /// If `true`, ARIA attributes such as `aria-expanded` and `aria-controls` will be added for accessibility.
93    /// Defaults to `true`.
94    #[props(default = true)]
95    pub aria_enabled: bool,
96
97    /// Duration of the expand/collapse animation in milliseconds.
98    ///
99    /// Specifies how long the transition animation should take. Defaults to `600` milliseconds.
100    #[props(default = 600)]
101    pub duration: u64,
102
103    /// Callback executed before the accordion expands.
104    ///
105    /// This callback is triggered just before the accordion transitions to an expanded state.
106    /// Defaults to a no-op.
107    #[props(default)]
108    pub will_open: Callback<()>,
109
110    /// Callback executed after the accordion has expanded.
111    ///
112    /// This callback is triggered once the accordion has completed its transition to an expanded state.
113    /// Defaults to a no-op.
114    #[props(default)]
115    pub did_open: Callback<()>,
116
117    /// Callback executed before the accordion collapses.
118    ///
119    /// This callback is triggered just before the accordion transitions to a collapsed state.
120    /// Defaults to a no-op.
121    #[props(default)]
122    pub will_close: Callback<()>,
123
124    /// Callback executed after the accordion has collapsed.
125    ///
126    /// This callback is triggered once the accordion has completed its transition to a collapsed state.
127    /// Defaults to a no-op.
128    #[props(default)]
129    pub did_close: Callback<()>,
130}
131
132/// Accordion Component
133///
134/// A Dioxus component for creating collapsible accordion sections. This component allows you
135/// to toggle between expanded and collapsed views with smooth animations, and supports customization
136/// through various properties like size, style, and callbacks.
137///
138/// # Properties
139/// The component uses the `AccordionProps` struct for configuration. Key properties include:
140///
141/// - **expand**: A `Signal<bool>` that controls the expansion state of the accordion. It is required to toggle between expanded and collapsed states.
142/// - **expanded**: The content that is displayed when the accordion is expanded (`Element`). Default: `""`.
143/// - **collapsed**: The content that is displayed when the accordion is collapsed (`Element`). Default: `""`.
144/// - **children**: The child elements to display inside the accordion when expanded (`Element`). Default: `""`.
145/// - **size**: Defines the size of the accordion (`Size`). Default: `Size::XXLarge`.
146/// - **aria_controls**: The ARIA controls attribute for accessibility (`&'static str`). Default: `""`.
147/// - **style**: Inline styles for the accordion container (`&'static str`). Default: `""`.
148/// - **expanded_style**: Inline styles for the expanded content (`&'static str`). Default: `""`.
149/// - **collapsed_style**: Inline styles for the collapsed content (`&'static str`). Default: `""`.
150/// - **content_style**: Inline styles for the content section (`&'static str`). Default: `""`.
151/// - **class**: Custom CSS class for the accordion container (`&'static str`). Default: `""`.
152/// - **expanded_class**: Custom CSS class for the expanded content (`&'static str`). Default: `""`.
153/// - **collapsed_class**: Custom CSS class for the collapsed content (`&'static str`). Default: `""`.
154/// - **content_class**: Custom CSS class for the content section (`&'static str`). Default: `""`.
155/// - **aria_enabled**: If `true`, ARIA attributes will be added to the HTML structure for better accessibility (`bool`). Default: `true`.
156/// - **duration**: Duration of the expand/collapse animation in milliseconds (`u64`). Default: `600`.
157/// - **will_open**: Callback invoked before the accordion expands (`Callback<()>`). Default: no-op.
158/// - **did_open**: Callback invoked after the accordion has expanded (`Callback<()>`). Default: no-op.
159/// - **will_close**: Callback invoked before the accordion collapses (`Callback<()>`). Default: no-op.
160/// - **did_close**: Callback invoked after the accordion has collapsed (`Callback<()>`). Default: no-op.
161///
162/// # Features
163/// - Smooth transitions between expanded and collapsed states with configurable duration.
164/// - Supports ARIA attributes for accessibility.
165/// - Customizable styles and classes for each section of the accordion.
166/// - Optional callbacks for pre- and post-expansion and collapse events.
167///
168/// # Examples
169///
170/// ## Basic Accordion
171/// ```rust
172/// use dioxus::prelude::*;
173/// use accordion_rs::dioxus::Accordion;
174/// use accordion_rs::Size;
175///
176/// fn App() -> Element {
177///     let expand_signal = use_signal(|| false);
178///
179///     rsx! {
180///         Accordion {
181///             expand: expand_signal.clone(),
182///             expanded: rsx! {p { "This is the expanded content" }},
183///             collapsed: rsx! {p { "This is the collapsed content" }},
184///             size: Size::Medium,
185///         }
186///     }
187/// }
188/// ```
189///
190/// ## Accordion with Custom Styles
191/// ```rust
192/// use dioxus::prelude::*;
193/// use accordion_rs::dioxus::Accordion;
194///
195/// fn App() -> Element {
196///     let expand_signal = use_signal(|| false);
197///
198///     rsx! {
199///         Accordion {
200///             expand: expand_signal.clone(),
201///             expanded: rsx! { p{ "Expanded content goes here" }},
202///             collapsed: rsx! { p{ "Collapsed content here" }},
203///             expanded_style: "background-color: lightblue;",
204///             collapsed_style: "background-color: lightgray;",
205///         }
206///     }
207/// }
208/// ```
209///
210/// ## Accordion with Callbacks
211/// ```rust
212/// use dioxus::prelude::*;
213/// use accordion_rs::dioxus::Accordion;
214///
215/// fn App() -> Element {
216///     let expand_signal = use_signal(|| false);
217///
218///     rsx! {
219///         Accordion {
220///             expand: expand_signal.clone(),
221///             expanded: rsx! { p{ "Expanded content" }},
222///             collapsed: rsx! { p{ "Collapsed content" }},
223///             will_open: |_| println!("Accordion will open!"),
224///             did_open: |_| println!("Accordion opened!"),
225///             will_close: |_| println!("Accordion will close!"),
226///             did_close: |_| println!("Accordion closed!"),
227///         }
228///     }
229/// }
230/// ```
231#[component]
232pub fn Accordion(mut props: AccordionProps) -> Element {
233    let toggle_expansion = {
234        move |_| {
235            if (props.expand)() {
236                props.will_close.call(());
237                props.expand.set(false);
238                props.did_close.call(());
239            } else {
240                props.will_open.call(());
241                props.expand.set(true);
242                props.did_open.call(());
243            }
244        }
245    };
246
247    rsx! {
248        div {
249            class: "{props.class}",
250            style: "{props.size.to_style()} {props.style}",
251            div {
252                class: {if (props.expand)() {
253                    props.expanded_class
254                } else {
255                    props.collapsed_class
256                }},
257                style: {format!(
258                    "cursor: pointer; transition: all {}ms; {}",
259                    props.duration,
260                    if (props.expand)() {
261                        props.expanded_style
262                    } else {
263                        props.collapsed_style
264                    }
265                )},
266                aria_expanded: if props.aria_enabled { Some((props.expand)().to_string()) } else { None },
267                aria_controls: if props.aria_enabled { Some(props.aria_controls) } else { None },
268                onclick: toggle_expansion,
269                if (props.expand)() {
270                    {props.expanded}
271                } else {
272                    {props.collapsed}
273                }
274            },
275            if (props.expand)() {
276                div {
277                    id: "{props.aria_controls}",
278                    class: "{props.content_class}",
279                    style: "overflow: hidden; transition: all {props.duration}ms; {props.content_style}",
280                    {props.children}
281                }
282            }
283        }
284    }
285}
286
287#[derive(Props, PartialEq, Clone)]
288pub struct ItemProps {
289    /// The child elements of the item.
290    ///
291    /// These elements will be rendered inside the item container.
292    pub children: Element,
293
294    /// The inline style for the item container.
295    ///
296    /// Defaults to an empty string.
297    #[props(default = "")]
298    pub style: &'static str,
299
300    /// The CSS class for the item container.
301    ///
302    /// Defaults to an empty string.
303    #[props(default = "")]
304    pub class: &'static str,
305
306    /// The alignment of the item content.
307    ///
308    /// Specifies how the content of the item should be aligned (e.g., `Align::Left`, `Align::Center`, etc.).
309    /// Defaults to `Align::Left`.
310    #[props(default)]
311    pub align: Align,
312
313    /// The title text for the item.
314    ///
315    /// This text typically appears as a header or label for the item.
316    /// Defaults to an empty string.
317    #[props(default = "")]
318    pub title: &'static str,
319
320    /// The icon associated with the item.
321    ///
322    /// This can be a URL to an image or an icon class name.
323    /// Defaults to an empty string.
324    #[props(default = "")]
325    pub icon: &'static str,
326}
327
328#[component]
329pub fn Item(props: ItemProps) -> Element {
330    rsx! {
331        li {
332            class: "{props.class}",
333            style: "{props.align.to_style()} {props.style}",
334            if !props.icon.is_empty() {
335                span { class: "mr-2", "{props.icon}" }
336            }
337            if !props.title.is_empty() {
338                strong { "{props.title}" }
339            }
340            {props.children}
341        }
342    }
343}
344
345#[derive(Props, PartialEq, Clone)]
346pub struct ButtonProps {
347    /// The child elements or text content of the button.
348    ///
349    /// This defines the content that will appear inside the button, such as text or other elements.
350    pub children: Element,
351
352    /// The inline style for the button.
353    ///
354    /// This allows for custom styling directly applied to the button element.
355    /// Defaults to an empty string.
356    #[props(default = "")]
357    pub style: &'static str,
358
359    /// The CSS class for the button.
360    ///
361    /// This applies a custom CSS class to the button for styling.
362    /// Defaults to an empty string.
363    #[props(default = "")]
364    pub class: &'static str,
365}
366
367#[component]
368pub fn Button(props: ButtonProps) -> Element {
369    rsx! {
370        button {
371            class: "{props.class}",
372            style: "{props.style}",
373            {props.children}
374        }
375    }
376}
377
378#[derive(Props, PartialEq, Clone)]
379pub struct ListProps {
380    /// The child elements of the list.
381    ///
382    /// These elements represent the individual list items or nested components inside the list container.
383    pub children: Element,
384
385    /// The inline style for the list container.
386    ///
387    /// You can provide custom CSS styles directly to the list container using this property.
388    /// Defaults to an empty string.
389    #[props(default = "")]
390    pub style: &'static str,
391
392    /// The CSS class for the list container.
393    ///
394    /// This class is used to apply custom styling to the list container via external CSS.
395    /// Defaults to an empty string.
396    #[props(default = "")]
397    pub class: &'static str,
398}
399
400#[component]
401pub fn List(props: ListProps) -> Element {
402    rsx! {
403        ul {
404            class: "{props.class}",
405            style: "{props.style}",
406            {props.children}
407        }
408    }
409}