Skip to main content

orbital_core_components/stack/
stack.rs

1use leptos::prelude::*;
2use orbital_base_components::BaseStack;
3use orbital_macros::component_doc;
4
5use super::types::StackConfig;
6
7/// One-dimensional layout with a consistent gap between every child.
8///
9/// Convenience wrapper over [`Flex`](crate::Flex) with even-gap, full-width defaults. Equivalent to `Flex(vertical=true, full_width=true)` with optional direction override. Set `config.horizontal=true` for a row direction.
10///
11/// Default presets: column direction, `FlexGap::Medium`, `full_width=true`, no justify override. Reach for [`Space`](crate::Space) to push children to opposite edges. Reach for [`Flex`](crate::Flex) when you need wrap, inline, fill, or inset padding.
12///
13/// # When to use
14///
15/// - Vertical form sections, settings blocks, and button rows - Even gaps between every child on one axis - Opinionated defaults over raw flex props
16///
17/// # Usage
18///
19/// 1. Pass `config=StackConfig::vertical(FlexGap::Medium)` or customize fields on `StackConfig`. 2. Set `config.horizontal=true` for row direction. 3. Tune `config.align` / `config.justify` for distribution.
20///
21/// # Best Practices
22///
23/// ## Do's
24///
25/// * Use `Stack` for simple vertical sections and button groups with even gaps * Reach for [`Space`](crate::Space) for opposite-edge distribution (space-between) * Reach for [`Flex`](crate::Flex) when you need wrap, inline, fill, or padding props
26///
27/// ## Don'ts
28///
29/// * Do not use `Stack` for two-dimensional page grids — prefer [`Grid`](crate::Grid)
30///
31/// # Examples
32///
33/// ## Horizontal cluster
34/// Default row direction spaces inline items with medium gap.
35/// <!-- preview -->
36/// ```rust
37/// use crate::{DemoBox, Stack, StackConfig, FlexGap};
38/// view! {
39///     <div data-testid="stack-preview">
40///         <Stack config=StackConfig::horizontal(FlexGap::Medium)>
41///             <DemoBox data_testid="stack-item-1">"One"</DemoBox>
42///             <DemoBox data_testid="stack-item-2">"Two"</DemoBox>
43///         </Stack>
44///     </div>
45/// }
46/// ```
47///
48/// ## Vertical stack
49/// Column direction stacks children with explicit medium gap.
50/// <!-- preview -->
51/// ```rust
52/// use crate::{DemoBox, Stack, StackConfig, FlexGap};
53/// view! {
54///     <div data-testid="stack-vertical">
55///         <Stack config=StackConfig::vertical(FlexGap::Medium)>
56///             <DemoBox data_testid="stack-v-1">"First"</DemoBox>
57///             <DemoBox data_testid="stack-v-2">"Second"</DemoBox>
58///         </Stack>
59///     </div>
60/// }
61/// ```
62///
63/// ## Gap preset matrix
64/// Compare small and large gap presets side by side.
65/// <!-- preview -->
66/// ```rust
67/// use crate::{DemoBox, Stack, StackConfig, FlexGap};
68/// view! {
69///     <div data-testid="stack-gap-matrix" style="width: 100%; max-width: 560px;">
70///         <Stack config=StackConfig { gap: FlexGap::Small, horizontal: true, ..Default::default() }>
71///             <DemoBox data_testid="stack-gap-small">"Small"</DemoBox>
72///             <DemoBox>"gap"</DemoBox>
73///         </Stack>
74///         <Stack config=StackConfig { gap: FlexGap::Large, horizontal: true, ..Default::default() }>
75///             <DemoBox data_testid="stack-gap-large">"Large"</DemoBox>
76///             <DemoBox>"gap"</DemoBox>
77///         </Stack>
78///     </div>
79/// }
80/// ```
81///
82/// ## Align center
83/// Cross-axis centering with mixed-height children in a bounded frame.
84/// <!-- preview -->
85/// ```rust
86/// use crate::{DemoBox, Stack, StackConfig, FlexGap, FlexAlign};
87/// view! {
88///     <div data-testid="stack-align" style="width: 100%; max-width: 560px; height: 120px; border: 1px solid var(--orb-color-border-default); padding: 8px;">
89///         <Stack config=StackConfig { gap: FlexGap::Medium, horizontal: true, align: Some(FlexAlign::Center), ..Default::default() }>
90///             <DemoBox height="32px">"Short"</DemoBox>
91///             <DemoBox height="72px" data_testid="stack-align-tall">"Tall"</DemoBox>
92///         </Stack>
93///     </div>
94/// }
95/// ```
96///
97/// ## Justify space-between
98/// Main-axis distribution pushes items to opposite edges.
99/// <!-- preview -->
100/// ```rust
101/// use crate::{DemoBox, Stack, StackConfig, FlexGap, FlexJustify};
102/// view! {
103///     <div data-testid="stack-justify" style="width: 100%; max-width: 560px;">
104///         <Stack config=StackConfig { gap: FlexGap::Medium, horizontal: true, justify: Some(FlexJustify::SpaceBetween), ..Default::default() }>
105///             <DemoBox data_testid="stack-justify-start">"Start"</DemoBox>
106///             <DemoBox data_testid="stack-justify-end">"End"</DemoBox>
107///         </Stack>
108///     </div>
109/// }
110/// ```
111///
112/// ## Mixed child types
113/// Buttons, text, and badges share the same stack gap rhythm.
114/// <!-- preview -->
115/// ```rust
116/// use crate::{Stack, StackConfig, FlexGap, Button, ButtonAppearance, Badge};
117/// view! {
118///     <div data-testid="stack-mixed">
119///         <Stack config=StackConfig::horizontal(FlexGap::Medium)>
120///             <Button appearance=ButtonAppearance::Primary>"Action"</Button>
121///             <span>"Status"</span>
122///             <Badge>"New"</Badge>
123///         </Stack>
124///     </div>
125/// }
126/// ```
127#[component_doc(
128    category = "Layout",
129    preview_slug = "stack",
130    preview_label = "Stack",
131    preview_icon = icondata::AiColumnHeightOutlined,
132)]
133#[component]
134pub fn Stack(
135    /// Gap, direction, and alignment settings for the stack.
136    #[prop(default = StackConfig::default())]
137    config: StackConfig,
138    /// Extra CSS class names merged onto the flex stack container.
139    #[prop(optional, into)]
140    class: MaybeProp<String>,
141    /// Flex item children laid out with even gaps between each sibling.
142    children: Children,
143) -> impl IntoView {
144    view! {
145        <BaseStack
146            class=class
147            gap=config.gap
148            horizontal=config.horizontal
149            align=MaybeProp::from(config.align)
150            justify=MaybeProp::from(config.justify)
151        >
152            {children()}
153        </BaseStack>
154    }
155}