panes/preset/
dashboard.rs1use std::sync::Arc;
2
3use taffy::prelude::fr;
4
5use crate::builder::LayoutBuilder;
6use crate::error::{ConstraintError, PaneError, TreeError};
7use crate::layout::Layout;
8
9pub struct Dashboard {
11 cards: Arc<[(Arc<str>, usize)]>,
12 columns: usize,
13 gap: f32,
14}
15
16impl Dashboard {
17 pub(crate) fn new(cards: impl IntoIterator<Item = (impl Into<Arc<str>>, usize)>) -> Self {
18 Self {
19 cards: cards
20 .into_iter()
21 .map(|(k, span)| (k.into(), span))
22 .collect(),
23 columns: 4,
24 gap: 0.0,
25 }
26 }
27
28 pub fn columns(mut self, columns: usize) -> Self {
30 self.columns = columns;
31 self
32 }
33
34 pub fn gap(mut self, gap: f32) -> Self {
36 self.gap = gap;
37 self
38 }
39
40 pub fn build(&self) -> Result<Layout, PaneError> {
42 match self.cards.is_empty() {
43 true => {
44 return Err(PaneError::InvalidTree(TreeError::DashboardNoCards));
45 }
46 _ => {}
47 }
48 match self.columns {
49 0 => {
50 return Err(PaneError::InvalidTree(TreeError::DashboardNoColumns));
51 }
52 _ => {}
53 }
54
55 let mut b = LayoutBuilder::new();
56 let grid_style = self.grid_root_style();
57
58 b.row(|r| {
59 r.taffy_node(grid_style, |grid| add_cards(grid, &self.cards));
60 })?;
61
62 b.build()
63 }
64
65 fn grid_root_style(&self) -> taffy::Style {
66 let gap_len = taffy::LengthPercentage::length(self.gap);
67 taffy::Style {
68 display: taffy::Display::Grid,
69 size: taffy::Size {
70 width: taffy::Dimension::percent(1.0),
71 height: taffy::Dimension::percent(1.0),
72 },
73 grid_template_columns: vec![fr(1.0); self.columns],
74 grid_auto_rows: vec![fr(1.0)],
75 gap: taffy::Size {
76 width: gap_len,
77 height: gap_len,
78 },
79 ..Default::default()
80 }
81 }
82}
83
84fn card_style(span: usize) -> Result<taffy::Style, PaneError> {
85 let span_u16 = u16::try_from(span)
86 .map_err(|_| PaneError::InvalidConstraint(ConstraintError::GridSpanOverflow(span)))?;
87 Ok(taffy::Style {
88 grid_column: taffy::Line {
89 start: taffy::GridPlacement::Auto,
90 end: taffy::GridPlacement::Span(span_u16),
91 },
92 ..Default::default()
93 })
94}
95
96fn add_cards(ctx: &mut crate::ContainerCtx, cards: &[(Arc<str>, usize)]) {
97 for (kind, span) in cards {
98 match card_style(*span) {
99 Ok(style) => {
100 ctx.taffy_node(style, |inner| {
101 inner.panel(Arc::clone(kind));
102 });
103 }
104 Err(e) => {
105 ctx.set_error(e);
106 return;
107 }
108 }
109 }
110}
111
112impl Dashboard {
113 pub fn into_runtime(self) -> Result<crate::runtime::LayoutRuntime, PaneError> {
115 let spans: Arc<[usize]> = self.cards.iter().map(|(_, s)| *s).collect();
116 let kinds: Vec<Arc<str>> = self.cards.iter().map(|(k, _)| Arc::clone(k)).collect();
117 let strategy = crate::strategy::StrategyKind::Dashboard {
118 columns: self.columns,
119 gap: self.gap,
120 spans,
121 };
122 crate::runtime::LayoutRuntime::from_strategy(strategy, &kinds)
123 }
124}
125
126super::impl_preset!(Dashboard);