Skip to main content

orbital_base_components/flex/
base.rs

1use leptos::prelude::*;
2
3use crate::spacing::SpacingInset;
4
5use super::types::{FlexAlign, FlexGap, FlexJustify, FlexWrap};
6
7/// Headless flex container — layout structure only, no theme styling.
8#[component]
9pub fn BaseFlex(
10    #[prop(optional, into)] class: MaybeProp<String>,
11    #[prop(optional)] gap: FlexGap,
12    #[prop(optional)] vertical: bool,
13    #[prop(optional, into)] inline: Signal<bool>,
14    #[prop(optional, into)] align: MaybeProp<FlexAlign>,
15    #[prop(optional, into)] justify: MaybeProp<FlexJustify>,
16    #[prop(optional, default = FlexWrap::NoWrap)] wrap: FlexWrap,
17    #[prop(optional, default = false)] fill: bool,
18    #[prop(optional, default = false)] full_width: bool,
19    #[prop(optional, into)] padding: MaybeProp<SpacingInset>,
20    #[prop(optional, into)] margin: MaybeProp<SpacingInset>,
21    children: Children,
22) -> impl IntoView {
23    let layout = Memo::new(move |_| {
24        let mut s = String::new();
25        s.push_str(if inline.get() {
26            "display: inline-flex;"
27        } else {
28            "display: flex;"
29        });
30        s.push_str(if vertical {
31            "flex-direction: column;"
32        } else {
33            "flex-direction: row;"
34        });
35        s.push_str(&format!("flex-wrap: {};", wrap.as_str()));
36        if fill {
37            s.push_str("height: 100%;min-height: 0;");
38        }
39        if full_width {
40            s.push_str("width: 100%;");
41        }
42        let gap_css = match gap {
43            FlexGap::Small => "gap: 4px 8px;",
44            FlexGap::Medium => "gap: 8px 12px;",
45            FlexGap::Large => "gap: 12px 16px;",
46            FlexGap::Size(size) => {
47                s.push_str(&format!("gap: {size}px {size}px;"));
48                ""
49            }
50            FlexGap::WH(width, height) => {
51                s.push_str(&format!("gap: {width}px {height}px;"));
52                ""
53            }
54        };
55        if !gap_css.is_empty() {
56            s.push_str(gap_css);
57        }
58        if let Some(align) = align.get() {
59            s.push_str(&format!("align-items: {};", align.as_str()));
60        }
61        if let Some(justify) = justify.get() {
62            s.push_str(&format!("justify-content: {};", justify.as_str()));
63        }
64        if let Some(padding) = padding.get() {
65            s.push_str(&padding.padding_css());
66        }
67        if let Some(margin) = margin.get() {
68            s.push_str(&margin.margin_css());
69        }
70        s
71    });
72
73    view! {
74        <div
75            class=move || {
76                let extra = class.get().unwrap_or_default();
77                if extra.is_empty() {
78                    "orbital-flex".to_string()
79                } else {
80                    format!("orbital-flex {extra}")
81                }
82            }
83            style=move || layout.get()
84        >
85            {children()}
86        </div>
87    }
88}