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