perspective_viewer/components/
datetime_column_style.rs1mod custom;
14mod simple;
15
16use std::rc::Rc;
17use std::sync::LazyLock;
18
19use derivative::Derivative;
20use perspective_js::json;
21use perspective_js::utils::global::navigator;
22use wasm_bindgen::prelude::*;
23use yew::prelude::*;
24use yew::*;
25
26use super::form::color_selector::*;
27use super::modal::{ModalLink, SetModalLink};
28use super::style::LocalStyle;
29use crate::components::datetime_column_style::custom::DatetimeStyleCustom;
30use crate::components::datetime_column_style::simple::DatetimeStyleSimple;
31use crate::components::form::select_enum_field::SelectEnumField;
32use crate::components::form::select_value_field::SelectValueField;
33use crate::config::*;
34use crate::css;
35use crate::utils::WeakScope;
36
37#[derive(Properties, Derivative)]
38#[derivative(Debug)]
39pub struct DatetimeColumnStyleProps {
40 pub enable_time_config: bool,
41 pub config: Option<DatetimeColumnStyleConfig>,
42 pub default_config: DatetimeColumnStyleDefaultConfig,
43
44 #[prop_or_default]
45 pub on_change: Callback<ColumnConfigValueUpdate>,
46
47 #[prop_or_default]
48 #[derivative(Debug = "ignore")]
49 weak_link: WeakScope<DatetimeColumnStyle>,
50}
51
52impl ModalLink<DatetimeColumnStyle> for DatetimeColumnStyleProps {
53 fn weak_link(&self) -> &'_ WeakScope<DatetimeColumnStyle> {
54 &self.weak_link
55 }
56}
57
58impl PartialEq for DatetimeColumnStyleProps {
59 fn eq(&self, other: &Self) -> bool {
60 self.enable_time_config == other.enable_time_config
61 && self.config == other.config
62 && self.default_config == other.default_config
63 }
64}
65
66pub enum DatetimeColumnStyleMsg {
67 SimpleDatetimeStyleConfigChanged(SimpleDatetimeStyleConfig),
68 CustomDatetimeStyleConfigChanged(CustomDatetimeStyleConfig),
69 TimezoneChanged(Option<String>),
70 ColorModeChanged(Option<DatetimeColorMode>),
71 ColorChanged(String),
72 ColorReset,
73}
74
75#[derive(Debug)]
77pub struct DatetimeColumnStyle {
78 config: DatetimeColumnStyleConfig,
79 default_config: DatetimeColumnStyleDefaultConfig,
80}
81
82impl Component for DatetimeColumnStyle {
83 type Message = DatetimeColumnStyleMsg;
84 type Properties = DatetimeColumnStyleProps;
85
86 fn create(ctx: &Context<Self>) -> Self {
87 ctx.set_modal_link();
88 Self {
89 config: ctx.props().config.clone().unwrap_or_default(),
90 default_config: ctx.props().default_config.clone(),
91 }
92 }
93
94 fn changed(&mut self, ctx: &Context<Self>, old: &Self::Properties) -> bool {
96 let mut rerender = false;
97 let mut new_config = ctx.props().config.clone().unwrap_or_default();
98 if self.config != new_config {
99 std::mem::swap(&mut self.config, &mut new_config);
100 rerender = true;
101 }
102 if old.enable_time_config != ctx.props().enable_time_config {
103 rerender = true;
104 }
105 rerender
106 }
107
108 fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
110 match msg {
111 DatetimeColumnStyleMsg::TimezoneChanged(val) => {
112 if Some(&*USER_TIMEZONE) != val.as_ref() {
113 *self.config.date_format.time_zone_mut() = val;
114 } else {
115 *self.config.date_format.time_zone_mut() = None;
116 }
117
118 self.dispatch_config(ctx);
119 true
120 },
121 DatetimeColumnStyleMsg::ColorModeChanged(mode) => {
122 self.config.datetime_color_mode = mode.unwrap_or_default();
123 self.dispatch_config(ctx);
124 true
125 },
126 DatetimeColumnStyleMsg::ColorChanged(color) => {
127 self.config.color = Some(color);
128 self.dispatch_config(ctx);
129 true
130 },
131
132 DatetimeColumnStyleMsg::SimpleDatetimeStyleConfigChanged(simple) => {
133 self.config.date_format = DatetimeFormatType::Simple(simple);
134 self.dispatch_config(ctx);
135 true
136 },
137 DatetimeColumnStyleMsg::CustomDatetimeStyleConfigChanged(custom) => {
138 self.config.date_format = DatetimeFormatType::Custom(custom);
139 self.dispatch_config(ctx);
140 true
141 },
142 DatetimeColumnStyleMsg::ColorReset => {
143 self.config.color = Some(self.default_config.color.clone());
144 self.dispatch_config(ctx);
145 true
146 },
147 }
148 }
149
150 fn view(&self, ctx: &Context<Self>) -> Html {
151 let selected_color_mode = self.config.datetime_color_mode;
152 let color_mode_changed = ctx
153 .link()
154 .callback(DatetimeColumnStyleMsg::ColorModeChanged);
155
156 let color_controls = match selected_color_mode {
157 DatetimeColorMode::None => html! {},
158 DatetimeColorMode::Foreground => {
159 self.color_select_row(ctx, &DatetimeColorMode::Foreground, "foreground-label")
160 },
161 DatetimeColorMode::Background => {
162 self.color_select_row(ctx, &DatetimeColorMode::Background, "background-label")
163 },
164 };
165
166 html! {
167 <>
168 <LocalStyle href={css!("column-style")} />
169 <div id="column-style-container" class="datetime-column-style-container">
170 <SelectEnumField<DatetimeColorMode>
171 label="color"
172 on_change={color_mode_changed}
173 current_value={selected_color_mode}
174 />
175 { color_controls }
176 if ctx.props().enable_time_config {
177 <SelectValueField<String>
178 label="timezone"
179 values={ALL_TIMEZONES.with(|x| (*x).clone())}
180 default_value={(*USER_TIMEZONE).clone()}
181 on_change={ctx.link().callback(DatetimeColumnStyleMsg::TimezoneChanged)}
182 current_value={self.config.date_format.time_zone().as_ref().unwrap_or(&*USER_TIMEZONE).clone()}
183 />
184 }
185 if let DatetimeFormatType::Simple(config) = &self.config.date_format {
186 if ctx.props().enable_time_config {
187 <div class="row">
188 <button
189 id="datetime_format"
190 data-title="Simple"
191 data-title-hover="Switch to Custom"
192 onclick={ctx.link().callback(|_| DatetimeColumnStyleMsg::CustomDatetimeStyleConfigChanged(CustomDatetimeStyleConfig::default()))}
193 />
194 </div>
195 }
196 <DatetimeStyleSimple
197 enable_time_config={ctx.props().enable_time_config}
198 on_change={ctx.link().callback(DatetimeColumnStyleMsg::SimpleDatetimeStyleConfigChanged)}
199 config={config.clone()}
200 />
201 } else if let DatetimeFormatType::Custom(config) = &self.config.date_format {
202 if ctx.props().enable_time_config {
203 <div class="row">
204 <button
205 id="datetime_format"
206 data-title="Custom"
207 data-title-hover="Switch to Simple"
208 onclick={ctx.link().callback(|_| DatetimeColumnStyleMsg::SimpleDatetimeStyleConfigChanged(SimpleDatetimeStyleConfig::default()))}
209 />
210 </div>
211 }
212 <DatetimeStyleCustom
213 enable_time_config={ctx.props().enable_time_config}
214 on_change={ctx.link().callback(DatetimeColumnStyleMsg::CustomDatetimeStyleConfigChanged)}
215 config={config.clone()}
216 />
217 }
218 </div>
219 </>
220 }
221 }
222}
223
224#[wasm_bindgen]
225extern "C" {
226 #[wasm_bindgen(js_name = supportedValuesOf, js_namespace = Intl)]
227 pub fn supported_values_of(s: &JsValue) -> js_sys::Array;
228}
229
230thread_local! {
231 static ALL_TIMEZONES: LazyLock<Rc<Vec<String>>> = LazyLock::new(|| {
232 Rc::new(
233 supported_values_of(&JsValue::from("timeZone"))
234 .iter()
235 .map(|x| x.as_string().unwrap())
236 .collect(),
237 )
238 });
239}
240
241static USER_TIMEZONE: LazyLock<String> = LazyLock::new(|| {
242 js_sys::Reflect::get(
243 &js_sys::Intl::DateTimeFormat::new(&navigator().languages(), &json!({})).resolved_options(),
244 &JsValue::from("timeZone"),
245 )
246 .unwrap()
247 .as_string()
248 .unwrap()
249});
250
251impl DatetimeColumnStyle {
252 fn dispatch_config(&self, ctx: &Context<Self>) {
254 let update =
255 Some(self.config.clone()).filter(|x| x != &DatetimeColumnStyleConfig::default());
256 ctx.props()
257 .on_change
258 .emit(ColumnConfigValueUpdate::DatagridDatetimeStyle(update));
259 }
260
261 fn color_select_row(&self, ctx: &Context<Self>, mode: &DatetimeColorMode, title: &str) -> Html {
264 let on_color = ctx.link().callback(DatetimeColumnStyleMsg::ColorChanged);
265 let color = self
266 .config
267 .color
268 .clone()
269 .unwrap_or_else(|| self.default_config.color.to_owned());
270
271 let color_props = props!(ColorProps {
272 title: title.to_owned(),
273 on_color,
274 is_modified: color != self.default_config.color,
275 color,
276 on_reset: ctx.link().callback(|_| DatetimeColumnStyleMsg::ColorReset)
277 });
278
279 if &self.config.datetime_color_mode == mode {
280 html! { <div class="row"><ColorSelector ..color_props /></div> }
281 } else {
282 html! {}
283 }
284 }
285}