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