ravenrs_app_ui/
dashboard.rs1use std::collections::BTreeMap;
4
5use ravenrs_app::{FrameContext, ViewInfo, ViewKind};
6use ravenrs_reactive::State;
7use ravenrs_widgets::{AnyView, BuildCx, Keyed, NodeId, Text, Tile, TileLayout, TileSpot, View, ViewExt};
8
9use crate::{ListView, Session};
10
11const ITEM_COLUMNS: u16 = 2;
13const ITEM_ROWS: u16 = 2;
14
15#[derive(Debug)]
18#[cfg_attr(feature = "live", ravenrs_live::expose(part = "fields"))]
19pub struct DashboardView {
20 session: Session,
21 view: ViewInfo,
22}
23
24#[cfg_attr(feature = "live", ravenrs_live::expose)]
25impl DashboardView {
26 pub fn new(session: Session, view: ViewInfo) -> Self {
28 Self { session, view }
29 }
30}
31
32impl View for DashboardView {
33 fn build(&self, cx: &mut BuildCx<'_>) -> NodeId {
34 self.session.revision();
36 let view = self.session.dashboard_shown(&self.view.id);
37 let key = view.id.bytes().fold(0_u64, |h, b| (h ^ u64::from(b)).wrapping_mul(0x100_0000_01b3));
38 cx.child(&Keyed::new(key, DashboardItems { session: self.session.clone(), view }))
39 }
40}
41
42struct DashboardItems {
44 session: Session,
45 view: ViewInfo,
46}
47
48impl View for DashboardItems {
49 fn build(&self, cx: &mut BuildCx<'_>) -> NodeId {
50 let model = self.session.model();
51 let Some(items) = model.at(&["Views", &self.view.id, "Items"]) else {
52 return cx.child(&Text::new(self.view.id.clone()));
53 };
54 let order = items.in_order();
55 let frames: State<BTreeMap<String, Session>> = cx.use_state(BTreeMap::new);
56 let spots: State<Vec<TileSpot>> = cx.use_state(|| {
57 order.iter().map(|item| TileSpot::new(item.id.clone(), ITEM_COLUMNS, ITEM_ROWS)).collect()
58 });
59 let alone = order.len() == 1;
60 let mut tiles = TileLayout::new(spots.binding());
61 for item in order {
62 let caption = item.text("Caption").unwrap_or(&item.id).to_owned();
63 let content: AnyView = if let Some(name) = item.text("Control") {
64 match self.session.control(name) {
65 Some(control) => control(),
66 None => Box::new(Text::new(name.to_owned())),
67 }
68 } else if let Some(info) = item.text("View").and_then(|id| ViewInfo::from_model(&model, id).ok())
69 {
70 let kept = frames.peek().get(&item.id).cloned();
71 let session = kept.unwrap_or_else(|| {
72 let session = self.session.child(FrameContext::Nested);
73 session.frame().set_view(Some(info.clone()));
74 frames.update(|all| {
75 all.insert(item.id.clone(), session.clone());
76 });
77 session
78 });
79 match info.kind {
80 ViewKind::List => {
81 let opener = self.session.clone();
82 let entity = info.entity.clone().unwrap_or_default();
83 Box::new(
84 ListView::new(session.clone(), session.frame(), info)
85 .plain_toolbar()
86 .on_open(std::rc::Rc::new(move |key: &ravenrs_app::Value| {
87 opener.open_detail(&entity, key);
88 }))
89 .grid_shape(|grid| grid.toolbar(false).filter_row(false))
90 .stretch()
91 .grow(1.0),
92 )
93 }
94 _ => Box::new(Text::new(info.id)),
95 }
96 } else {
97 Box::new(Text::new(item.id.clone()))
98 };
99 if alone {
101 return cx.child(&content);
102 }
103 tiles = tiles.tile(Tile::new(item.id.clone(), caption, content));
104 }
105 cx.child(&tiles)
106 }
107}