use perspective_client::config::Scalar;
use yew::prelude::*;
use yew::*;
use super::form::color_range_selector::*;
use super::form::number_field::NumberFieldProps;
use super::modal::*;
use super::style::LocalStyle;
use crate::components::form::number_field::NumberField;
use crate::components::form::select_enum_field::SelectEnumField;
use crate::config::*;
use crate::session::Session;
use crate::utils::*;
use crate::*;
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub enum Side {
Fg,
Bg,
}
use Side::*;
#[derive(Properties)]
pub struct NumberColumnStyleProps {
#[cfg_attr(test, prop_or_default)]
pub config: Option<NumberColumnStyleConfig>,
#[cfg_attr(test, prop_or_default)]
pub default_config: NumberColumnStyleDefaultConfig,
#[prop_or_default]
pub on_change: Callback<ColumnConfigValueUpdate>,
#[prop_or_default]
pub weak_link: WeakScope<NumberColumnStyle>,
#[prop_or_default]
pub column_name: Option<String>,
pub session: Session,
}
impl ModalLink<NumberColumnStyle> for NumberColumnStyleProps {
fn weak_link(&self) -> &'_ WeakScope<NumberColumnStyle> {
&self.weak_link
}
}
impl PartialEq for NumberColumnStyleProps {
fn eq(&self, other: &Self) -> bool {
self.config == other.config
&& self.default_config == other.default_config
&& self.column_name == other.column_name
}
}
fn scalar_to_f64(scalar: &Scalar) -> f64 {
match scalar {
Scalar::Float(x) => *x,
Scalar::String(x) => x.parse::<f64>().unwrap_or_default(),
Scalar::Bool(x) => {
if *x {
1.0
} else {
0.0
}
},
Scalar::Null => 0.0,
}
}
fn set_default_gradient(session: &Session, ctx: &Context<NumberColumnStyle>) {
if let Some(column_name) = ctx.props().column_name.clone() {
let session = session.clone();
ctx.link().send_future(async move {
let view = session.get_view().unwrap();
let min_max = view.get_min_max(column_name).await.unwrap();
let abs_max = scalar_to_f64(&min_max.0)
.abs()
.max(scalar_to_f64(&min_max.1).abs());
let gradient = (abs_max * 100.).round() / 100.;
NumberColumnStyleMsg::DefaultGradientChanged(gradient)
});
}
}
#[derive(Debug)]
pub enum NumberColumnStyleMsg {
PosColorChanged(Side, String),
NegColorChanged(Side, String),
NumberForeModeChanged(NumberForegroundMode),
NumberBackModeChanged(NumberBackgroundMode),
GradientChanged(Side, Option<f64>),
DefaultGradientChanged(f64),
}
pub struct NumberColumnStyle {
config: NumberColumnStyleConfig,
default_config: NumberColumnStyleDefaultConfig,
fg_mode: NumberForegroundMode,
bg_mode: NumberBackgroundMode,
pos_fg_color: String,
neg_fg_color: String,
pos_bg_color: String,
neg_bg_color: String,
fg_gradient: Option<f64>,
bg_gradient: Option<f64>,
}
impl Component for NumberColumnStyle {
type Message = NumberColumnStyleMsg;
type Properties = NumberColumnStyleProps;
fn create(ctx: &Context<Self>) -> Self {
ctx.set_modal_link();
set_default_gradient(&ctx.props().session, ctx);
Self::reset(
&ctx.props().config.clone().unwrap_or_default(),
&ctx.props().default_config.clone(),
)
}
fn changed(&mut self, ctx: &Context<Self>, _old: &Self::Properties) -> bool {
let mut new = Self::reset(
&ctx.props().config.clone().unwrap_or_default(),
&ctx.props().default_config.clone(),
);
set_default_gradient(&ctx.props().session, ctx);
std::mem::swap(self, &mut new);
true
}
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
match msg {
NumberColumnStyleMsg::PosColorChanged(side, val) => {
if side == Fg {
self.pos_fg_color = val;
self.config.pos_fg_color = Some(self.pos_fg_color.to_owned());
} else {
self.pos_bg_color = val;
self.config.pos_bg_color = Some(self.pos_bg_color.to_owned());
}
self.dispatch_config(ctx);
true
},
NumberColumnStyleMsg::NegColorChanged(side, val) => {
if side == Fg {
self.neg_fg_color = val;
self.config.neg_fg_color = Some(self.neg_fg_color.to_owned());
} else {
self.neg_bg_color = val;
self.config.neg_bg_color = Some(self.neg_bg_color.to_owned());
}
self.dispatch_config(ctx);
true
},
NumberColumnStyleMsg::NumberForeModeChanged(val) => {
self.fg_mode = val;
self.config.number_fg_mode = val;
if self.fg_mode.needs_gradient() {
self.config.fg_gradient = Some(self.fg_gradient.unwrap());
} else {
self.config.fg_gradient = None;
}
self.dispatch_config(ctx);
true
},
NumberColumnStyleMsg::NumberBackModeChanged(val) => {
self.bg_mode = val;
self.config.number_bg_mode = val;
if self.bg_mode.needs_gradient() {
self.config.bg_gradient = Some(self.bg_gradient.unwrap());
} else {
self.config.bg_gradient = None;
}
self.dispatch_config(ctx);
true
},
NumberColumnStyleMsg::GradientChanged(side, gradient) => {
match (side, gradient) {
(Fg, Some(x)) => {
self.fg_gradient = Some(x);
self.config.fg_gradient = Some(x);
},
(Fg, None) => {
self.fg_gradient = Some(self.default_config.fg_gradient);
self.config.fg_gradient = None;
},
(Bg, Some(x)) => {
self.bg_gradient = Some(x);
self.config.bg_gradient = Some(x);
},
(Bg, None) => {
self.bg_gradient = Some(self.default_config.bg_gradient);
self.config.bg_gradient = None;
},
};
self.dispatch_config(ctx);
false
},
NumberColumnStyleMsg::DefaultGradientChanged(gradient) => {
self.fg_gradient.get_or_insert(gradient);
self.bg_gradient.get_or_insert(gradient);
self.default_config.fg_gradient = gradient;
self.default_config.bg_gradient = gradient;
true
},
}
}
fn view(&self, ctx: &Context<Self>) -> Html {
let fg_mode_changed = ctx.link().callback(|x: Option<_>| {
NumberColumnStyleMsg::NumberForeModeChanged(x.unwrap_or_default())
});
let bg_mode_changed = ctx.link().callback(|x: Option<_>| {
NumberColumnStyleMsg::NumberBackModeChanged(x.unwrap_or_default())
});
let fg_controls = match self.fg_mode {
NumberForegroundMode::Disabled => html! {},
NumberForegroundMode::Color => html! {
<div class="row">
<ColorRangeSelector ..self.color_props("fg-color", Fg, false, ctx) />
</div>
},
NumberForegroundMode::Bar => html! {
<>
<div class="row">
<ColorRangeSelector ..self.color_props("bar-color", Fg, false, ctx) />
</div>
<NumberField ..self.max_value_props(Fg, ctx) />
</>
},
NumberForegroundMode::LabelBar => html! {
<>
<div class="row">
<ColorRangeSelector ..self.color_props("label-bar-color", Fg, false, ctx) />
</div>
<NumberField ..self.max_value_props(Fg, ctx) />
</>
},
};
let bg_controls = match self.bg_mode {
NumberBackgroundMode::Disabled => html! {},
NumberBackgroundMode::Color => html! {
<div class="row">
<ColorRangeSelector ..self.color_props("bg-color", Bg,false, ctx) />
</div>
},
NumberBackgroundMode::Gradient => html! {
<>
<div class="row">
<ColorRangeSelector ..self.color_props("gradient-color", Bg, true, ctx) />
</div>
<NumberField ..self.max_value_props(Bg, ctx) />
</>
},
NumberBackgroundMode::Pulse => html! {
<div class="row">
<ColorRangeSelector ..self.color_props("pulse-color", Bg, true, ctx) />
</div>
},
};
html! {
<>
<LocalStyle href={css!("column-style")} />
<div id="column-style-container" class="number-column-style-container">
<SelectEnumField<NumberForegroundMode>
label="foreground"
on_change={fg_mode_changed}
current_value={self.fg_mode}
/>
{ fg_controls }
<SelectEnumField<NumberBackgroundMode>
label="background"
on_change={bg_mode_changed}
current_value={self.bg_mode}
/>
{ bg_controls }
</div>
</>
}
}
}
impl NumberColumnStyle {
fn dispatch_config(&self, ctx: &Context<Self>) {
let mut config = self.config.clone();
match &self.config {
NumberColumnStyleConfig {
pos_fg_color: Some(pos_color),
neg_fg_color: Some(neg_color),
..
} if *pos_color == self.default_config.pos_fg_color
&& *neg_color == self.default_config.neg_fg_color =>
{
config.pos_fg_color = None;
config.neg_fg_color = None;
},
_ => {},
};
match &self.config {
NumberColumnStyleConfig {
pos_bg_color: Some(pos_color),
neg_bg_color: Some(neg_color),
..
} if *pos_color == self.default_config.pos_bg_color
&& *neg_color == self.default_config.neg_bg_color =>
{
config.pos_bg_color = None;
config.neg_bg_color = None;
},
_ => {},
};
let update = Some(config).filter(|config| config != &NumberColumnStyleConfig::default());
ctx.props()
.on_change
.emit(ColumnConfigValueUpdate::DatagridNumberStyle(update));
}
fn color_props(
&self,
id: &str,
side: Side,
is_gradient: bool,
ctx: &Context<Self>,
) -> ColorRangeProps {
let on_pos_color = ctx
.link()
.callback(move |x| NumberColumnStyleMsg::PosColorChanged(side, x));
let on_neg_color = ctx
.link()
.callback(move |x| NumberColumnStyleMsg::NegColorChanged(side, x));
let default_config = self.default_config.clone();
props!(ColorRangeProps {
id: id.to_string(),
is_gradient,
pos_color: if side == Fg {
&self.pos_fg_color
} else {
&self.pos_bg_color
}
.to_owned(),
neg_color: if side == Fg {
&self.neg_fg_color
} else {
&self.neg_bg_color
}
.to_owned(),
on_pos_color,
on_neg_color,
on_reset: ctx.link().batch_callback(move |_| if side == Fg {
vec![
NumberColumnStyleMsg::PosColorChanged(
side,
default_config.pos_fg_color.clone(),
),
NumberColumnStyleMsg::NegColorChanged(
side,
default_config.neg_fg_color.clone(),
),
]
} else {
vec![
NumberColumnStyleMsg::PosColorChanged(
side,
default_config.pos_bg_color.clone(),
),
NumberColumnStyleMsg::NegColorChanged(
side,
default_config.neg_bg_color.clone(),
),
]
}),
is_modified: if side == Fg {
self.pos_fg_color != self.default_config.pos_fg_color
|| self.neg_fg_color != self.default_config.neg_fg_color
} else {
self.pos_bg_color != self.default_config.pos_bg_color
|| self.neg_bg_color != self.default_config.neg_bg_color
},
})
}
fn max_value_props(&self, side: Side, ctx: &Context<Self>) -> NumberFieldProps {
let on_change = ctx
.link()
.callback(move |x| NumberColumnStyleMsg::GradientChanged(side, x));
let value = if side == Fg {
self.fg_gradient.unwrap_or_default()
} else {
self.bg_gradient.unwrap_or_default()
};
props!(NumberFieldProps {
default: value,
current_value: value,
label: "max-value",
on_change
})
}
fn reset(
config: &NumberColumnStyleConfig,
default_config: &NumberColumnStyleDefaultConfig,
) -> Self {
let mut config = config.clone();
let fg_gradient = config.fg_gradient;
let bg_gradient = config.bg_gradient;
let pos_fg_color = config
.pos_fg_color
.as_ref()
.unwrap_or(&default_config.pos_fg_color)
.to_owned();
let neg_fg_color = config
.neg_fg_color
.as_ref()
.unwrap_or(&default_config.neg_fg_color)
.to_owned();
let pos_bg_color = config
.pos_bg_color
.as_ref()
.unwrap_or(&default_config.pos_bg_color)
.to_owned();
let neg_bg_color = config
.neg_bg_color
.as_ref()
.unwrap_or(&default_config.neg_bg_color)
.to_owned();
config.pos_fg_color = Some(pos_fg_color.to_owned());
config.neg_fg_color = Some(neg_fg_color.to_owned());
let fg_mode = config.number_fg_mode;
config.pos_bg_color = Some(pos_bg_color.to_owned());
config.neg_bg_color = Some(neg_bg_color.to_owned());
let bg_mode = config.number_bg_mode;
Self {
config,
default_config: default_config.clone(),
fg_mode,
bg_mode,
pos_fg_color,
neg_fg_color,
pos_bg_color,
neg_bg_color,
fg_gradient,
bg_gradient,
}
}
}