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