1use std::cell::Cell;
7use std::rc::Rc;
8
9use ravenrs::prelude::*;
10
11const LOCAL_ROWS: usize = 100_000;
12const SERVER_ROWS: usize = 5_000_000;
13const REGIONS: [&str; 5] = ["Norte", "Nordeste", "Centro-Oeste", "Sudeste", "Sul"];
14const PLANS: [&str; 4] = ["Grátis", "Básico", "Pro", "Empresa"];
15const OWNERS: [&str; 6] = ["Ana", "Bruno", "Carla", "Diego", "Elisa", "Fábio"];
16
17fn row(n: usize) -> Vec<Value> {
19 vec![
20 Value::Number(n as f64),
21 Value::Text(format!("Cliente {n:07}")),
22 Value::Text(REGIONS[n * 7 % REGIONS.len()].to_owned()),
23 Value::Text(PLANS[n * 13 % PLANS.len()].to_owned()),
24 Value::Text(OWNERS[n * 3 % OWNERS.len()].to_owned()),
25 Value::Number((n * 37 % 500) as f64 + 1.0),
26 Value::Number(((n * 97 % 90_000) as f64 + 1_000.0) / 10.0),
27 ]
28}
29
30fn columns() -> Vec<GridColumn> {
31 vec![
32 GridColumn::number("Id").width(90.0).groupable(false),
33 GridColumn::text("Cliente").width(190.0).fill(),
34 GridColumn::text("Região").width(140.0),
35 GridColumn::text("Plano").width(120.0),
36 GridColumn::text("Responsável").width(140.0),
37 GridColumn::number("Licenças").width(110.0).summary(Summary::Sum),
38 GridColumn::currency("Mensalidade", "R$ ", 2).width(150.0).summary(Summary::Sum),
39 ]
40}
41
42#[derive(Clone, Default)]
44struct Asked {
45 questions: Rc<Cell<usize>>,
46 rows_sent: Rc<Cell<usize>>,
47}
48
49fn server(asked: &Asked, with_total: bool) -> RemoteRows {
50 let asked = asked.clone();
51 RemoteRows::new(move |request, answer| {
52 asked.questions.set(asked.questions.get() + 1);
53 let end = (request.offset + request.limit).min(SERVER_ROWS);
54 asked.rows_sent.set(asked.rows_sent.get() + (end - request.offset));
55 let page = GridPage::new((request.offset..end).map(row).collect(), end < SERVER_ROWS);
56 answer.send(if with_total { page.total(SERVER_ROWS) } else { page });
57 })
58}
59
60struct Poc {
61 data: GridData,
62 view: State<usize>,
63 scenarios: State<Vec<GridScenario>>,
64 asked: Asked,
65 infinite: RemoteRows,
66 paged: RemoteRows,
67}
68
69const CHUNK: usize = 100;
70const KEPT_CHUNKS: usize = 8;
71
72impl Poc {
73 fn new() -> Self {
74 let asked = Asked::default();
75 Self {
76 data: GridData::new((0..LOCAL_ROWS).map(row).collect()),
77 view: State::new(0),
78 scenarios: State::new(Vec::new()),
79 infinite: server(&asked, false).chunk_size(CHUNK).max_chunks(KEPT_CHUNKS),
80 paged: server(&asked, true).chunk_size(CHUNK).max_chunks(KEPT_CHUNKS),
81 asked,
82 }
83 }
84
85 fn counters(&self, held: usize, kind: &str) -> impl View + use<> {
86 let line = format!(
87 "{kind} · o servidor recebeu {} pedidos e mandou {} linhas · no máximo {} de {} linhas ficaram em memória",
88 self.asked.questions.get(),
89 self.asked.rows_sent.get(),
90 held,
91 SERVER_ROWS,
92 );
93 Text::new(line).style(TextStyle::Callout).foreground(Semantic::InkMuted)
94 }
95}
96
97impl View for Poc {
98 fn build(&self, cx: &mut BuildCx<'_>) -> NodeId {
99 let view = self.view.get();
100 let body: AnyView = match view {
101 0 => Box::new(
102 DataGrid::new(self.data.clone(), columns())
103 .group_panel(true)
104 .selection_mode(SelectionMode::Multiple)
105 .scenarios(self.scenarios.binding())
106 .group_by(2)
107 .on_export(|_| {}),
108 ),
109 1 => Box::new(
110 DataGrid::new(self.data.clone(), columns())
111 .group_panel(true)
112 .scenarios(self.scenarios.binding())
113 .page_size(50)
114 .on_export(|_| {}),
115 ),
116 2 => Box::new(DataGrid::remote(self.infinite.clone(), columns())),
117 _ => Box::new(DataGrid::remote(self.paged.clone(), columns()).page_size(50)),
118 };
119 let hint: AnyView = match view {
120 0 => Box::new(
121 Text::new(
122 "Arraste um cabeçalho para o espaço acima para agrupar; arraste os grupos para trocar a ordem, ou para fora para desagrupar. Botão direito em um cabeçalho abre o menu. Clique em um total no rodapé para trocar a soma ou escrever uma fórmula, como Sum([Mensalidade]) / Sum([Licenças]). Em Cenário, grave o arranjo com as fórmulas.",
123 )
124 .style(TextStyle::Callout)
125 .foreground(Semantic::InkMuted),
126 ),
127 1 => Box::new(
128 Text::new("As mesmas 100 mil linhas em páginas: escolha quantas linhas por página no rodapé.")
129 .style(TextStyle::Callout)
130 .foreground(Semantic::InkMuted),
131 ),
132 2 => Box::new(self.counters(self.infinite.peak_rows(), "Rolagem infinita, sem total: nunca há COUNT(*)")),
133 _ => Box::new(self.counters(self.paged.peak_rows(), "Páginas: só a página aberta é pedida")),
134 };
135 cx.child(
136 &VStack::new((
137 Text::new("Grade de dados").style(TextStyle::Title).font_weight(Weight::SEMIBOLD),
138 SegmentedControl::new(
139 self.view.binding(),
140 [
141 "100 mil linhas: agrupar",
142 "Em páginas",
143 "Servidor: rolagem infinita",
144 "Servidor: páginas",
145 ],
146 )
147 .label("Demonstração"),
148 hint,
149 Keyed::new(view as u64, body).grow(1.0),
150 ))
151 .spacing(14.0)
152 .padding(24.0)
153 .align(Align::Stretch)
154 .grow(1.0),
155 )
156 }
157}
158
159fn main() -> Result<(), ravenrs::native::PlatformError> {
160 tracing_subscriber::fmt()
161 .with_env_filter(
162 tracing_subscriber::EnvFilter::try_from_default_env()
163 .unwrap_or_else(|_| "info,wgpu=warn,naga=warn".into()),
164 )
165 .init();
166 let options = ravenrs::native::WindowOptions::new("RavenRS: grade de dados").size(1360.0, 860.0);
167 let app = Localized::new(Labels::pt_br(), Poc::new());
168 ravenrs::native::run(app, options)
169}