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