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