perspective_viewer/components/style_controls/
number_string_format.rs1mod 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}