Skip to main content

perspective_viewer/components/style_controls/
number_string_format.rs

1// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
2// ┃ ██████ ██████ ██████       █      █      █      █      █ █▄  ▀███ █       ┃
3// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█  ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄  ▀█ █ ▀▀▀▀▀ ┃
4// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄   █ ▄▄▄▄▄ ┃
5// ┃ █      ██████ █  ▀█▄       █ ██████      █      ███▌▐███ ███████▄ █       ┃
6// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
7// ┃ Copyright (c) 2017, the Perspective Authors.                              ┃
8// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
9// ┃ This file is part of the Perspective library, distributed under the terms ┃
10// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
13mod digits_section;
14mod misc_section;
15mod style_section;
16mod types;
17
18use perspective_client::config::ColumnType;
19pub use types::*;
20use yew::{Callback, Component, Properties, html};
21
22use crate::config::*;
23
24#[derive(Properties, PartialEq, Clone)]
25pub struct CustomNumberFormatProps {
26    pub restored_config: CustomNumberFormatConfig,
27    pub on_change: Callback<ColumnConfigFieldUpdate>,
28    pub view_type: ColumnType,
29    pub column_name: String,
30    #[prop_or_default]
31    pub keys: Vec<String>,
32}
33
34pub enum CustomNumberFormatMsg {
35    StyleChanged(Option<NumberStyle>),
36    NotationChanged(Option<NotationName>),
37    CurrencyCode(Option<CurrencyCode>),
38    CurrencyDisplay(Option<CurrencyDisplay>),
39    CompactDisplay(Option<CompactDisplay>),
40    CurrencySign(Option<CurrencySign>),
41    UseGrouping(Option<UseGrouping>),
42    SignDisplay(Option<SignDisplay>),
43    Unit(Option<Unit>),
44    UnitDisplay(Option<UnitDisplay>),
45    MinimumIntegerDigits(Option<f64>),
46    SigChange(Option<(f64, f64)>),
47    FracChange(Option<(f64, f64)>),
48    RoundingIncrement(RoundingIncrement),
49    TrailingZero(Option<TrailingZeroDisplay>),
50    RoundingMode(Option<RoundingMode>),
51    RoundingPriority(Option<RoundingPriority>),
52}
53
54#[derive(Default)]
55pub struct CustomNumberFormat {
56    config: CustomNumberFormatConfig,
57    style: NumberStyle,
58    notation: Option<NotationName>,
59}
60
61impl Component for CustomNumberFormat {
62    type Message = CustomNumberFormatMsg;
63    type Properties = CustomNumberFormatProps;
64
65    fn create(ctx: &yew::prelude::Context<Self>) -> Self {
66        Self::initialize(ctx)
67    }
68
69    fn changed(
70        &mut self,
71        ctx: &yew::prelude::Context<Self>,
72        _old_props: &Self::Properties,
73    ) -> bool {
74        *self = Self::initialize(ctx);
75        true
76    }
77
78    fn update(&mut self, ctx: &yew::prelude::Context<Self>, msg: Self::Message) -> bool {
79        match msg {
80            CustomNumberFormatMsg::StyleChanged(style) => {
81                let style = style.unwrap_or_default();
82                let new_style = match style {
83                    NumberStyle::Decimal => NumberFormatStyle::Decimal,
84                    NumberStyle::Percent => NumberFormatStyle::Percent,
85                    NumberStyle::Currency => {
86                        NumberFormatStyle::Currency(CurrencyNumberFormatStyle::default())
87                    },
88                    NumberStyle::Unit => NumberFormatStyle::Unit(UnitNumberFormatStyle {
89                        unit: Unit::default(),
90                        unit_display: None,
91                    }),
92                };
93                self.config._style = Some(new_style);
94                self.style = style;
95            },
96            CustomNumberFormatMsg::NotationChanged(notation) => {
97                self.notation = notation;
98                let new_notation = notation.map(|notation| match notation {
99                    NotationName::Standard => Notation::Standard,
100                    NotationName::Scientific => Notation::Scientific,
101                    NotationName::Engineering => Notation::Engineering,
102                    NotationName::Compact => Notation::Compact(CompactDisplay::default()),
103                });
104                self.config._notation = new_notation;
105            },
106            CustomNumberFormatMsg::CurrencyCode(val) => {
107                if let Some(NumberFormatStyle::Currency(currency)) = &mut self.config._style {
108                    currency.currency = val.unwrap_or_default();
109                }
110            },
111            CustomNumberFormatMsg::CurrencyDisplay(val) => {
112                if let Some(NumberFormatStyle::Currency(currency)) = &mut self.config._style {
113                    currency.currency_display = val;
114                }
115            },
116            CustomNumberFormatMsg::CurrencySign(val) => {
117                if let Some(NumberFormatStyle::Currency(currency)) = &mut self.config._style {
118                    currency.currency_sign = val;
119                }
120            },
121            CustomNumberFormatMsg::CompactDisplay(val) => {
122                if let Some(Notation::Compact(old)) = &mut self.config._notation {
123                    if let Some(val) = val {
124                        *old = val;
125                    }
126                } else {
127                    tracing::error!("Unreachable change in compact display!");
128                }
129            },
130            CustomNumberFormatMsg::UseGrouping(val) => {
131                self.config.use_grouping = val;
132            },
133            CustomNumberFormatMsg::SignDisplay(val) => {
134                self.config.sign_display = val;
135            },
136            CustomNumberFormatMsg::Unit(val) => {
137                if let Some(NumberFormatStyle::Unit(style)) = &mut self.config._style {
138                    style.unit = val.unwrap_or_default();
139                }
140            },
141            CustomNumberFormatMsg::UnitDisplay(val) => {
142                if let Some(NumberFormatStyle::Unit(style)) = &mut self.config._style {
143                    style.unit_display = val;
144                }
145            },
146            CustomNumberFormatMsg::MinimumIntegerDigits(val) => {
147                self.config.minimum_integer_digits = val;
148            },
149            CustomNumberFormatMsg::FracChange(val) => {
150                self.config.rounding_increment = None;
151                self.config.maximum_fraction_digits = val.map(|(_, val)| {
152                    let min = self.config.minimum_fraction_digits.unwrap_or(2.);
153                    val.max(min)
154                });
155
156                self.config.minimum_fraction_digits = val.map(|(val, _)| {
157                    let max = self.config.maximum_fraction_digits.unwrap_or(2.);
158                    val.min(max)
159                });
160            },
161            CustomNumberFormatMsg::SigChange(val) => {
162                self.config.maximum_significant_digits = val.map(|(_, val)| {
163                    let min = self.config.minimum_significant_digits.unwrap_or(1.);
164                    val.max(min)
165                });
166
167                self.config.minimum_significant_digits = val.map(|(val, _)| {
168                    let max = self.config.maximum_significant_digits.unwrap_or(21.);
169                    val.min(max)
170                });
171            },
172            CustomNumberFormatMsg::RoundingIncrement(val) => {
173                if let RoundingIncrement::Custom(val) = val {
174                    self.config.rounding_priority = None;
175                    self.config.rounding_increment = Some(val);
176                    self.config.maximum_fraction_digits = Some(0.);
177                    self.config.minimum_fraction_digits = Some(0.);
178                } else {
179                    self.config.rounding_increment = None;
180                }
181            },
182            CustomNumberFormatMsg::TrailingZero(val) => {
183                self.config.trailing_zero_display = val;
184            },
185            CustomNumberFormatMsg::RoundingMode(val) => {
186                self.config.rounding_mode = val;
187            },
188            CustomNumberFormatMsg::RoundingPriority(val) => {
189                self.config.rounding_increment = None;
190                self.config.rounding_priority = val;
191            },
192        };
193
194        let is_float = ctx.props().view_type == ColumnType::Float;
195        let filtered_config = self.config.clone().filter_default(is_float);
196        let mut value = serde_json::Map::new();
197        if filtered_config != CustomNumberFormatConfig::default() {
198            value.insert(
199                "number_format".to_owned(),
200                serde_json::to_value(&filtered_config).unwrap_or(serde_json::Value::Null),
201            );
202        }
203        ctx.props().on_change.emit(ColumnConfigFieldUpdate {
204            keys: ctx.props().keys.clone(),
205            value,
206        });
207        true
208    }
209
210    fn view(&self, ctx: &yew::prelude::Context<Self>) -> yew::prelude::Html {
211        html! {
212            <>{ self.style_section(ctx) }{ self.digits_section(ctx) }{ self.misc_section(ctx) }</>
213        }
214    }
215}
216
217impl CustomNumberFormat {
218    fn initialize(ctx: &yew::prelude::Context<Self>) -> Self {
219        let config = ctx.props().restored_config.clone();
220        Self {
221            style: config
222                ._style
223                .as_ref()
224                .map(|style| match style {
225                    NumberFormatStyle::Decimal => NumberStyle::Decimal,
226                    NumberFormatStyle::Currency(_) => NumberStyle::Currency,
227                    NumberFormatStyle::Percent => NumberStyle::Percent,
228                    NumberFormatStyle::Unit(_) => NumberStyle::Unit,
229                })
230                .unwrap_or_default(),
231            config,
232            notation: None,
233        }
234    }
235}