Skip to main content

fc_dioxus/components/
divider.rs

1//! A thematic break or a vertical separator, matching divider.css.
2
3use crate::{
4    core::{
5        assets,
6        class::{Bem, ClassList},
7        state::Orientation,
8    },
9    fc_modifier,
10    theme::use_stylesheets,
11};
12use dioxus::core::AttributeValue;
13use dioxus::prelude::*;
14
15const BEM: Bem = Bem::new("fc-divider");
16
17fc_modifier! {
18    /// The inset of a divider within its containing surface.
19    pub enum DividerInset for BEM {
20        /// Span the full available size.
21        None => None,
22        /// Inset from inline-start to align with list text.
23        Inset => "inset",
24        /// Inset from both inline edges.
25        Middle => "middle",
26    }
27}
28
29fc_modifier! {
30    /// The CSS orientation vocabulary, separate from semantic orientation.
31    enum DividerAxis for BEM {
32        /// The default thematic break.
33        Horizontal => None,
34        /// An inline separator in a row.
35        Vertical => "vertical",
36    }
37}
38
39/// Props for a divider. Consumer attributes cannot replace separator semantics.
40#[derive(Props, Clone, PartialEq)]
41pub struct DividerProps {
42    /// Horizontal thematic break or vertical separator.
43    #[props(default)]
44    pub orientation: Orientation,
45    /// Full, start-inset, or middle-inset.
46    #[props(default)]
47    pub inset: DividerInset,
48    /// Additional consumer classes, appended after upstream classes.
49    #[props(default)]
50    pub class: Option<String>,
51    /// Additional global attributes, applied before component semantics.
52    #[props(extends = GlobalAttributes)]
53    pub attributes: Vec<Attribute>,
54}
55
56/// A horizontal `<hr>` or an unfocusable vertical `role="separator"`.
57#[component]
58pub fn Divider(props: DividerProps) -> Element {
59    let axis = if props.orientation == Orientation::Vertical {
60        DividerAxis::Vertical
61    } else {
62        DividerAxis::Horizontal
63    };
64    let mut classes = ClassList::new(BEM.block())
65        .with_modifier(BEM, props.inset)
66        .with_modifier(BEM, axis);
67    for attribute in &props.attributes {
68        if attribute.name == "class" {
69            if let AttributeValue::Text(value) = &attribute.value {
70                classes = classes.with(value);
71            }
72        }
73    }
74    let classes = classes.extend(props.class.as_deref()).to_string();
75    let mut attributes: Vec<_> = props
76        .attributes
77        .into_iter()
78        .filter(|attr| {
79            !matches!(
80                attr.name,
81                "class" | "role" | "aria-orientation" | "tabindex"
82            )
83        })
84        .collect();
85    attributes.push(Attribute::new("class", classes, None, false));
86    if props.orientation == Orientation::Vertical {
87        attributes.push(Attribute::new("role", "separator", None, false));
88        attributes.push(Attribute::new("aria-orientation", "vertical", None, false));
89    }
90    rsx! {
91        if use_stylesheets() { document::Stylesheet { href: assets::COMPONENTS_DIVIDER_CSS } }
92        if props.orientation == Orientation::Vertical {
93            div { ..attributes }
94        } else {
95            hr { ..attributes }
96        }
97    }
98}