perspective_viewer/components/
number_column_style.rs1use yew::prelude::*;
14use yew::*;
15
16use super::form::color_range_selector::*;
17use super::form::number_field::NumberFieldProps;
18use super::modal::*;
19use super::style::LocalStyle;
20use crate::components::form::number_field::NumberField;
21use crate::components::form::select_field::SelectEnumField;
22use crate::config::*;
23use crate::session::Session;
24use crate::utils::WeakScope;
25use crate::*;
26
27#[derive(PartialEq, Eq, Copy, Clone, Debug)]
28pub enum Side {
29 Fg,
30 Bg,
31}
32
33use Side::*;
34
35#[derive(Debug)]
36pub enum NumberColumnStyleMsg {
37 PosColorChanged(Side, String),
38 NegColorChanged(Side, String),
39 NumberForeModeChanged(NumberForegroundMode),
40 NumberBackModeChanged(NumberBackgroundMode),
41 GradientChanged(Side, Option<f64>),
42 DefaultGradientChanged(f64),
43}
44
45#[derive(Properties)]
51pub struct NumberColumnStyleProps {
52 #[cfg_attr(test, prop_or_default)]
53 pub config: Option<NumberColumnStyleConfig>,
54
55 #[cfg_attr(test, prop_or_default)]
56 pub default_config: NumberColumnStyleDefaultConfig,
57
58 #[prop_or_default]
59 pub on_change: Callback<ColumnConfigValueUpdate>,
60
61 #[prop_or_default]
62 pub weak_link: WeakScope<NumberColumnStyle>,
63
64 #[prop_or_default]
65 pub session: Option<Session>,
66
67 #[prop_or_default]
68 pub column_name: Option<String>,
69}
70
71impl ModalLink<NumberColumnStyle> for NumberColumnStyleProps {
72 fn weak_link(&self) -> &'_ WeakScope<NumberColumnStyle> {
73 &self.weak_link
74 }
75}
76
77impl PartialEq for NumberColumnStyleProps {
78 fn eq(&self, _other: &Self) -> bool {
79 false
80 }
81}
82
83impl NumberColumnStyleProps {
84 fn set_default_gradient(&self, ctx: &Context<NumberColumnStyle>) {
85 if let Some(session) = self.session.clone()
86 && let Some(column_name) = self.column_name.clone()
87 {
88 ctx.link().send_future(async move {
89 let view = session.get_view().unwrap();
90 let min_max = view.get_min_max(column_name).await.unwrap();
91 let abs_max = max!(
92 min_max.0.parse::<f64>().unwrap().abs(),
93 min_max.1.parse::<f64>().unwrap().abs()
94 );
95 let gradient = (abs_max * 100.).round() / 100.;
96 NumberColumnStyleMsg::DefaultGradientChanged(gradient)
97 });
98 }
99 }
100}
101
102pub struct NumberColumnStyle {
104 config: NumberColumnStyleConfig,
105 default_config: NumberColumnStyleDefaultConfig,
106 fg_mode: NumberForegroundMode,
107 bg_mode: NumberBackgroundMode,
108 pos_fg_color: String,
109 neg_fg_color: String,
110 pos_bg_color: String,
111 neg_bg_color: String,
112 fg_gradient: Option<f64>,
113 bg_gradient: Option<f64>,
114}
115
116impl Component for NumberColumnStyle {
117 type Message = NumberColumnStyleMsg;
118 type Properties = NumberColumnStyleProps;
119
120 fn create(ctx: &Context<Self>) -> Self {
121 ctx.set_modal_link();
122 ctx.props().set_default_gradient(ctx);
123 Self::reset(
124 &ctx.props().config.clone().unwrap_or_default(),
125 &ctx.props().default_config.clone(),
126 )
127 }
128
129 fn changed(&mut self, ctx: &Context<Self>, _old: &Self::Properties) -> bool {
130 let mut new = Self::reset(
131 &ctx.props().config.clone().unwrap_or_default(),
132 &ctx.props().default_config.clone(),
133 );
134 ctx.props().set_default_gradient(ctx);
135 std::mem::swap(self, &mut new);
136 true
137 }
138
139 fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
140 match msg {
141 NumberColumnStyleMsg::PosColorChanged(side, val) => {
197 if side == Fg {
198 self.pos_fg_color = val;
199 self.config.pos_fg_color = Some(self.pos_fg_color.to_owned());
200 } else {
201 self.pos_bg_color = val;
202 self.config.pos_bg_color = Some(self.pos_bg_color.to_owned());
203 }
204
205 self.dispatch_config(ctx);
206 true
207 },
208 NumberColumnStyleMsg::NegColorChanged(side, val) => {
209 if side == Fg {
210 self.neg_fg_color = val;
211 self.config.neg_fg_color = Some(self.neg_fg_color.to_owned());
212 } else {
213 self.neg_bg_color = val;
214 self.config.neg_bg_color = Some(self.neg_bg_color.to_owned());
215 }
216
217 self.dispatch_config(ctx);
218 true
219 },
220 NumberColumnStyleMsg::NumberForeModeChanged(val) => {
221 self.fg_mode = val;
222 self.config.number_fg_mode = val;
223 if self.fg_mode.needs_gradient() {
224 self.config.fg_gradient = Some(self.fg_gradient.unwrap());
225 } else {
226 self.config.fg_gradient = None;
227 }
228
229 self.dispatch_config(ctx);
230 true
231 },
232 NumberColumnStyleMsg::NumberBackModeChanged(val) => {
233 self.bg_mode = val;
234 self.config.number_bg_mode = val;
235 if self.bg_mode.needs_gradient() {
236 self.config.bg_gradient = Some(self.bg_gradient.unwrap());
237 } else {
238 self.config.bg_gradient = None;
239 }
240
241 self.dispatch_config(ctx);
242 true
243 },
244 NumberColumnStyleMsg::GradientChanged(side, gradient) => {
245 match (side, gradient) {
246 (Fg, Some(x)) => {
247 self.fg_gradient = Some(x);
248 self.config.fg_gradient = Some(x);
249 },
250 (Fg, None) => {
251 self.fg_gradient = Some(self.default_config.fg_gradient);
252 self.config.fg_gradient = None;
253 },
254 (Bg, Some(x)) => {
255 self.bg_gradient = Some(x);
256 self.config.bg_gradient = Some(x);
257 },
258 (Bg, None) => {
259 self.bg_gradient = Some(self.default_config.bg_gradient);
260 self.config.bg_gradient = None;
261 },
262 };
263
264 self.dispatch_config(ctx);
265 false
266 },
267 NumberColumnStyleMsg::DefaultGradientChanged(gradient) => {
268 self.fg_gradient.get_or_insert(gradient);
269 self.bg_gradient.get_or_insert(gradient);
270 self.default_config.fg_gradient = gradient;
271 self.default_config.bg_gradient = gradient;
272 true
273 },
274 }
275 }
276
277 fn view(&self, ctx: &Context<Self>) -> Html {
278 let fg_mode_changed = ctx.link().callback(|x: Option<_>| {
279 NumberColumnStyleMsg::NumberForeModeChanged(x.unwrap_or_default())
280 });
281
282 let bg_mode_changed = ctx.link().callback(|x: Option<_>| {
283 NumberColumnStyleMsg::NumberBackModeChanged(x.unwrap_or_default())
284 });
285
286 let fg_controls = match self.fg_mode {
287 NumberForegroundMode::Disabled => html! {},
288 NumberForegroundMode::Color => html! {
289 <div class="row">
290 <ColorRangeSelector ..self.color_props("fg-color", Fg, false, ctx) />
291 </div>
292 },
293 NumberForegroundMode::Bar => html! {
294 <>
295 <div class="row">
296 <ColorRangeSelector ..self.color_props("bar-color", Fg, false, ctx) />
297 </div>
298 <NumberField ..self.max_value_props(Fg, ctx) />
299 </>
300 },
301 };
302
303 let bg_controls = match self.bg_mode {
304 NumberBackgroundMode::Disabled => html! {},
305 NumberBackgroundMode::Color => html! {
306 <div class="row">
307 <ColorRangeSelector ..self.color_props("bg-color", Bg,false, ctx) />
308 </div>
309 },
310 NumberBackgroundMode::Gradient => html! {
311 <>
312 <div class="row">
313 <ColorRangeSelector ..self.color_props("gradient-color", Bg, true, ctx) />
314 </div>
315 <NumberField ..self.max_value_props(Bg, ctx) />
316 </>
317 },
318 NumberBackgroundMode::Pulse => html! {
319 <div class="row">
320 <ColorRangeSelector ..self.color_props("pulse-color", Bg, true, ctx) />
321 </div>
322 },
323 };
324
325 html! {
326 <>
327 <LocalStyle href={css!("column-style")} />
328 <div id="column-style-container" class="number-column-style-container">
329 <SelectEnumField<NumberForegroundMode>
330 label="foreground"
331 on_change={fg_mode_changed}
332 current_value={self.fg_mode}
333 />
334 { fg_controls }
335 <SelectEnumField<NumberBackgroundMode>
336 label="background"
337 on_change={bg_mode_changed}
338 current_value={self.bg_mode}
339 />
340 { bg_controls }
341 </div>
342 </>
343 }
344 }
345}
346
347impl NumberColumnStyle {
348 fn dispatch_config(&self, ctx: &Context<Self>) {
350 let mut config = self.config.clone();
351 match &self.config {
352 NumberColumnStyleConfig {
353 pos_fg_color: Some(pos_color),
354 neg_fg_color: Some(neg_color),
355 ..
356 } if *pos_color == self.default_config.pos_fg_color
357 && *neg_color == self.default_config.neg_fg_color =>
358 {
359 config.pos_fg_color = None;
360 config.neg_fg_color = None;
361 },
362 _ => {},
363 };
364
365 match &self.config {
366 NumberColumnStyleConfig {
367 pos_bg_color: Some(pos_color),
368 neg_bg_color: Some(neg_color),
369 ..
370 } if *pos_color == self.default_config.pos_bg_color
371 && *neg_color == self.default_config.neg_bg_color =>
372 {
373 config.pos_bg_color = None;
374 config.neg_bg_color = None;
375 },
376 _ => {},
377 };
378
379 let update = Some(config).filter(|config| config != &NumberColumnStyleConfig::default());
380
381 ctx.props()
382 .on_change
383 .emit(ColumnConfigValueUpdate::DatagridNumberStyle(update));
384 }
385
386 fn color_props(
387 &self,
388 id: &str,
389 side: Side,
390 is_gradient: bool,
391 ctx: &Context<Self>,
392 ) -> ColorRangeProps {
393 let on_pos_color = ctx
394 .link()
395 .callback(move |x| NumberColumnStyleMsg::PosColorChanged(side, x));
396 let on_neg_color = ctx
397 .link()
398 .callback(move |x| NumberColumnStyleMsg::NegColorChanged(side, x));
399
400 props!(ColorRangeProps {
401 id: id.to_string(),
402 is_gradient,
403 pos_color: if side == Fg {
404 &self.pos_fg_color
405 } else {
406 &self.pos_bg_color
407 }
408 .to_owned(),
409 neg_color: if side == Fg {
410 &self.neg_fg_color
411 } else {
412 &self.neg_bg_color
413 }
414 .to_owned(),
415 on_pos_color,
416 on_neg_color
417 })
418 }
419
420 fn max_value_props(&self, side: Side, ctx: &Context<Self>) -> NumberFieldProps {
421 let on_change = ctx
422 .link()
423 .callback(move |x| NumberColumnStyleMsg::GradientChanged(side, x));
424
425 let value = if side == Fg {
426 self.fg_gradient.unwrap_or_default()
427 } else {
428 self.bg_gradient.unwrap_or_default()
429 };
430
431 props!(NumberFieldProps {
432 default: value,
433 current_value: value,
434 label: "max-value",
435 on_change
436 })
437 }
438
439 fn reset(
440 config: &NumberColumnStyleConfig,
441 default_config: &NumberColumnStyleDefaultConfig,
442 ) -> Self {
443 let mut config = config.clone();
444 let fg_gradient = config.fg_gradient;
445 let bg_gradient = config.bg_gradient;
446
447 let pos_fg_color = config
448 .pos_fg_color
449 .as_ref()
450 .unwrap_or(&default_config.pos_fg_color)
451 .to_owned();
452
453 let neg_fg_color = config
454 .neg_fg_color
455 .as_ref()
456 .unwrap_or(&default_config.neg_fg_color)
457 .to_owned();
458
459 let pos_bg_color = config
460 .pos_bg_color
461 .as_ref()
462 .unwrap_or(&default_config.pos_bg_color)
463 .to_owned();
464
465 let neg_bg_color = config
466 .neg_bg_color
467 .as_ref()
468 .unwrap_or(&default_config.neg_bg_color)
469 .to_owned();
470
471 config.pos_fg_color = Some(pos_fg_color.to_owned());
472 config.neg_fg_color = Some(neg_fg_color.to_owned());
473 let fg_mode = config.number_fg_mode;
474 config.pos_bg_color = Some(pos_bg_color.to_owned());
475 config.neg_bg_color = Some(neg_bg_color.to_owned());
476 let bg_mode = config.number_bg_mode;
477 Self {
478 config,
479 default_config: default_config.clone(),
480 fg_mode,
481 bg_mode,
482 pos_fg_color,
483 neg_fg_color,
484 pos_bg_color,
485 neg_bg_color,
486 fg_gradient,
487 bg_gradient,
488 }
489 }
490}