@perspective-dev/viewer 4.3.0 → 4.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (227) hide show
  1. package/dist/cdn/perspective-viewer.js +2 -2
  2. package/dist/cdn/perspective-viewer.js.map +4 -4
  3. package/dist/css/botanical.css +1 -1
  4. package/dist/css/dracula.css +1 -1
  5. package/dist/css/gruvbox-dark.css +1 -1
  6. package/dist/css/gruvbox.css +1 -1
  7. package/dist/css/icons.css +1 -1
  8. package/dist/css/intl/de.css +1 -1
  9. package/dist/css/intl/es.css +1 -1
  10. package/dist/css/intl/fr.css +1 -1
  11. package/dist/css/intl/ja.css +1 -1
  12. package/dist/css/intl/pt.css +1 -1
  13. package/dist/css/intl/zh.css +1 -1
  14. package/dist/css/intl.css +1 -1
  15. package/dist/css/monokai.css +1 -1
  16. package/dist/css/pro-dark.css +1 -1
  17. package/dist/css/pro.css +1 -1
  18. package/dist/css/solarized-dark.css +1 -1
  19. package/dist/css/solarized.css +1 -1
  20. package/dist/css/themes.css +1 -1
  21. package/dist/css/vaporwave.css +1 -1
  22. package/dist/esm/perspective-viewer.inline.js +2 -2
  23. package/dist/esm/perspective-viewer.inline.js.map +4 -4
  24. package/dist/esm/perspective-viewer.js +2 -2
  25. package/dist/esm/perspective-viewer.js.map +4 -4
  26. package/dist/wasm/perspective-viewer.d.ts +57 -53
  27. package/dist/wasm/perspective-viewer.js +190 -165
  28. package/dist/wasm/perspective-viewer.wasm +0 -0
  29. package/dist/wasm/perspective-viewer.wasm.d.ts +17 -18
  30. package/package.json +7 -5
  31. package/src/{less/aggregate-selector.less → css/aggregate-selector.css} +23 -20
  32. package/src/css/column-dropdown.css +109 -0
  33. package/src/{less/column-selector.less → css/column-selector.css} +160 -158
  34. package/src/{less/column-settings-panel.less → css/column-settings-panel.css} +69 -59
  35. package/src/{less/column-style.less → css/column-style.css} +52 -66
  36. package/src/{less/column-symbol-attributes.less → css/column-symbol-attributes.css} +15 -14
  37. package/src/{less/config-selector.less → css/config-selector.css} +151 -135
  38. package/src/{less/containers/dropdown-menu.less → css/containers/dropdown-menu.css} +20 -19
  39. package/src/{less/containers/pairs-list.less → css/containers/pairs-list.css} +13 -12
  40. package/src/{themes/variables.less → css/containers/scroll-panel.css} +25 -22
  41. package/src/{less/containers/split-panel.less → css/containers/split-panel.css} +15 -14
  42. package/src/{less/containers/tabs.less → css/containers/tabs.css} +17 -19
  43. package/src/css/dom/checkbox.css +102 -0
  44. package/src/css/dom/scrollbar.css +35 -0
  45. package/src/{less/dom/select.less → css/dom/select.css} +17 -18
  46. package/src/{less/empty-column.less → css/empty-column.css} +19 -18
  47. package/src/{less/expression-editor.less → css/expression-editor.css} +19 -18
  48. package/src/{less/filter-dropdown.less → css/filter-dropdown.css} +12 -11
  49. package/src/{less/filter-item.less → css/filter-item.css} +16 -15
  50. package/src/{less/form/code-editor.less → css/form/code-editor.css} +26 -30
  51. package/src/{less/form/debug.less → css/form/debug.css} +19 -18
  52. package/src/{less/function-dropdown.less → css/function-dropdown.css} +12 -11
  53. package/src/css/plugin-selector.css +261 -0
  54. package/src/{less/render-warning.less → css/render-warning.css} +18 -17
  55. package/src/{less/status-bar.less → css/status-bar.css} +156 -144
  56. package/src/css/type-icon.css +116 -0
  57. package/src/{less/viewer.less → css/viewer.css} +112 -146
  58. package/src/rust/components/column_dropdown.rs +229 -119
  59. package/src/rust/components/column_selector/active_column.rs +81 -62
  60. package/src/rust/components/column_selector/add_expression_button.rs +1 -0
  61. package/src/rust/components/column_selector/aggregate_selector.rs +25 -15
  62. package/src/rust/components/column_selector/config_selector.rs +315 -199
  63. package/src/rust/components/column_selector/empty_column.rs +2 -2
  64. package/src/rust/components/column_selector/expr_edit_button.rs +8 -2
  65. package/src/rust/components/column_selector/filter_column.rs +37 -26
  66. package/src/rust/components/column_selector/inactive_column.rs +41 -29
  67. package/src/rust/components/column_selector/invalid_column.rs +7 -18
  68. package/src/rust/components/column_selector/pivot_column.rs +11 -5
  69. package/src/rust/components/column_selector/sort_column.rs +23 -13
  70. package/src/rust/components/column_selector.rs +163 -84
  71. package/src/rust/components/column_settings_sidebar/style_tab/symbol/row_selector.rs +1 -1
  72. package/src/rust/components/column_settings_sidebar/style_tab/symbol/symbol_pairs.rs +3 -2
  73. package/src/rust/components/column_settings_sidebar/style_tab/symbol/symbol_pairs_item.rs +3 -2
  74. package/src/rust/components/column_settings_sidebar/style_tab/symbol/symbol_selector.rs +2 -3
  75. package/src/rust/components/column_settings_sidebar/style_tab/symbol.rs +7 -1
  76. package/src/rust/components/column_settings_sidebar/style_tab.rs +153 -112
  77. package/src/rust/components/column_settings_sidebar.rs +91 -53
  78. package/src/rust/components/containers/dragdrop_list.rs +2 -1
  79. package/src/rust/components/containers/sidebar_close_button.rs +1 -1
  80. package/src/rust/components/containers/split_panel.rs +1 -0
  81. package/src/rust/components/containers/tab_list.rs +1 -1
  82. package/src/rust/components/copy_dropdown.rs +7 -28
  83. package/src/rust/components/datetime_column_style/custom.rs +2 -2
  84. package/src/rust/components/datetime_column_style/simple.rs +2 -2
  85. package/src/rust/components/datetime_column_style.rs +4 -2
  86. package/src/rust/components/editable_header.rs +7 -4
  87. package/src/rust/components/empty_row.rs +1 -1
  88. package/src/rust/components/export_dropdown.rs +4 -30
  89. package/src/rust/components/expression_editor.rs +19 -10
  90. package/src/rust/components/filter_dropdown.rs +246 -102
  91. package/src/rust/components/font_loader.rs +11 -28
  92. package/src/rust/components/form/code_editor.rs +17 -2
  93. package/src/rust/components/form/color_range_selector.rs +19 -6
  94. package/src/rust/components/form/debug.rs +30 -13
  95. package/src/rust/components/function_dropdown.rs +186 -113
  96. package/src/rust/components/main_panel.rs +71 -89
  97. package/src/rust/components/mod.rs +1 -1
  98. package/src/rust/components/modal.rs +7 -1
  99. package/src/rust/components/number_column_style.rs +22 -7
  100. package/src/rust/components/plugin_selector.rs +34 -102
  101. package/src/rust/components/portal.rs +274 -0
  102. package/src/rust/components/render_warning.rs +72 -123
  103. package/src/rust/components/settings_panel.rs +115 -11
  104. package/src/rust/components/status_bar.rs +222 -98
  105. package/src/rust/components/status_bar_counter.rs +8 -20
  106. package/src/rust/components/status_indicator.rs +64 -114
  107. package/src/rust/components/string_column_style.rs +2 -2
  108. package/src/rust/components/style/style_cache.rs +5 -1
  109. package/src/rust/components/viewer.rs +391 -39
  110. package/src/rust/custom_elements/copy_dropdown.rs +102 -21
  111. package/src/rust/custom_elements/export_dropdown.rs +102 -20
  112. package/src/rust/custom_elements/mod.rs +0 -7
  113. package/src/rust/custom_elements/modal.rs +7 -103
  114. package/src/rust/custom_elements/viewer.rs +99 -35
  115. package/src/rust/custom_events.rs +23 -2
  116. package/src/rust/dragdrop.rs +149 -10
  117. package/src/{less/containers/scroll-panel.less → rust/engines.rs} +15 -13
  118. package/src/rust/js/plugin.rs +1 -1
  119. package/src/rust/lib.rs +5 -4
  120. package/src/rust/presentation/props.rs +39 -0
  121. package/src/rust/presentation/sheets.rs +3 -3
  122. package/src/rust/presentation.rs +44 -8
  123. package/src/rust/renderer/limits.rs +32 -3
  124. package/src/{less/dom/scrollbar.less → rust/renderer/props.rs} +18 -19
  125. package/src/rust/renderer.rs +83 -9
  126. package/src/rust/session/column_defaults_update.rs +1 -1
  127. package/src/rust/session/metadata.rs +23 -2
  128. package/src/rust/session/props.rs +178 -0
  129. package/src/rust/session.rs +124 -117
  130. package/src/rust/tasks/column_locator.rs +133 -0
  131. package/src/rust/{model → tasks}/columns_iter_set.rs +14 -23
  132. package/src/rust/{model → tasks}/edit_expression.rs +34 -10
  133. package/src/rust/{model → tasks}/eject.rs +2 -2
  134. package/src/rust/{model → tasks}/get_viewer_config.rs +0 -11
  135. package/src/rust/{model → tasks}/intersection_observer.rs +19 -3
  136. package/src/{less/containers/radio-list.less → rust/tasks/is_invalid_drop.rs} +21 -14
  137. package/src/rust/tasks/mod.rs +52 -0
  138. package/src/rust/{model → tasks}/plugin_column_styles.rs +69 -46
  139. package/src/rust/{model → tasks}/resize_observer.rs +39 -6
  140. package/src/rust/{model → tasks}/send_plugin_config.rs +1 -1
  141. package/src/rust/tasks/structural.rs +53 -0
  142. package/src/rust/utils/mod.rs +4 -0
  143. package/src/rust/utils/modal_position.rs +110 -0
  144. package/src/rust/utils/ptr_eq_rc.rs +74 -0
  145. package/src/rust/utils/pubsub.rs +11 -1
  146. package/src/svg/bg-pattern.png +0 -0
  147. package/src/svg/close-icon.svg +1 -1
  148. package/src/svg/expression.svg +1 -1
  149. package/src/svg/mega-menu-icons-candlestick.svg +1 -1
  150. package/src/svg/mega-menu-icons-datagrid.svg +1 -2
  151. package/src/svg/mega-menu-icons-heatmap.svg +1 -1
  152. package/src/svg/mega-menu-icons-map-scatter.svg +1 -1
  153. package/src/svg/mega-menu-icons-ohlc.svg +1 -1
  154. package/src/svg/mega-menu-icons-sunburst.svg +1 -1
  155. package/src/svg/mega-menu-icons-treemap.svg +1 -1
  156. package/src/svg/mega-menu-icons-x-bar.svg +1 -1
  157. package/src/svg/mega-menu-icons-x-y-line.svg +1 -1
  158. package/src/svg/mega-menu-icons-x-y-scatter.svg +1 -1
  159. package/src/svg/mega-menu-icons-y-area.svg +1 -1
  160. package/src/svg/mega-menu-icons-y-bar.svg +1 -1
  161. package/src/svg/mega-menu-icons-y-line.svg +1 -1
  162. package/src/svg/mega-menu-icons-y-scatter.svg +1 -1
  163. package/src/svg/radio-hover.svg +1 -1
  164. package/src/svg/radio-off.svg +1 -1
  165. package/src/svg/radio-on.svg +1 -1
  166. package/src/themes/botanical.css +157 -0
  167. package/src/themes/defaults.css +139 -0
  168. package/src/themes/dracula.css +233 -0
  169. package/src/themes/gruvbox-dark.css +255 -0
  170. package/src/themes/gruvbox.css +134 -0
  171. package/src/themes/icons.css +124 -0
  172. package/src/themes/intl/de.css +102 -0
  173. package/src/themes/intl/es.css +102 -0
  174. package/src/themes/intl/fr.css +102 -0
  175. package/src/themes/intl/ja.css +102 -0
  176. package/src/themes/intl/pt.css +102 -0
  177. package/src/themes/intl/zh.css +102 -0
  178. package/src/themes/intl.css +102 -0
  179. package/src/themes/monokai.css +233 -0
  180. package/src/themes/pro-dark.css +158 -0
  181. package/src/themes/{themes.less → pro.css} +17 -21
  182. package/src/themes/solarized-dark.css +135 -0
  183. package/src/themes/solarized.css +95 -0
  184. package/src/themes/themes.css +22 -0
  185. package/src/themes/vaporwave.css +256 -0
  186. package/dist/css/variables.css +0 -0
  187. package/src/less/column-dropdown.less +0 -95
  188. package/src/less/dom/checkbox.less +0 -100
  189. package/src/less/plugin-selector.less +0 -183
  190. package/src/less/type-icon.less +0 -68
  191. package/src/rust/components/error_message.rs +0 -56
  192. package/src/rust/custom_elements/column_dropdown.rs +0 -123
  193. package/src/rust/custom_elements/filter_dropdown.rs +0 -179
  194. package/src/rust/custom_elements/function_dropdown.rs +0 -115
  195. package/src/rust/model/column_locator.rs +0 -82
  196. package/src/rust/model/is_invalid_drop.rs +0 -36
  197. package/src/rust/model/mod.rs +0 -100
  198. package/src/rust/model/reset_all.rs +0 -38
  199. package/src/rust/model/structural.rs +0 -244
  200. package/src/themes/botanical.less +0 -142
  201. package/src/themes/dracula.less +0 -101
  202. package/src/themes/gruvbox-dark.less +0 -116
  203. package/src/themes/gruvbox.less +0 -152
  204. package/src/themes/icons.less +0 -130
  205. package/src/themes/intl/de.less +0 -102
  206. package/src/themes/intl/es.less +0 -102
  207. package/src/themes/intl/fr.less +0 -102
  208. package/src/themes/intl/ja.less +0 -102
  209. package/src/themes/intl/pt.less +0 -102
  210. package/src/themes/intl/zh.less +0 -102
  211. package/src/themes/intl.less +0 -102
  212. package/src/themes/monokai.less +0 -107
  213. package/src/themes/pro-dark.less +0 -147
  214. package/src/themes/pro.less +0 -186
  215. package/src/themes/solarized-dark.less +0 -78
  216. package/src/themes/solarized.less +0 -102
  217. package/src/themes/vaporwave.less +0 -145
  218. /package/dist/wasm/snippets/{perspective-viewer-d729f682ba5c19df → perspective-viewer-68fef752754ffbc6}/inline0.js +0 -0
  219. /package/dist/wasm/snippets/{perspective-viewer-d729f682ba5c19df → perspective-viewer-68fef752754ffbc6}/inline1.js +0 -0
  220. /package/dist/wasm/snippets/{perspective-viewer-d729f682ba5c19df → perspective-viewer-68fef752754ffbc6}/inline2.js +0 -0
  221. /package/dist/wasm/snippets/{perspective-viewer-d729f682ba5c19df → perspective-viewer-68fef752754ffbc6}/inline3.js +0 -0
  222. /package/dist/wasm/snippets/{perspective-viewer-d729f682ba5c19df → perspective-viewer-68fef752754ffbc6}/inline4.js +0 -0
  223. /package/src/rust/{model → tasks}/copy_export.rs +0 -0
  224. /package/src/rust/{model → tasks}/export_app.rs +0 -0
  225. /package/src/rust/{model → tasks}/export_method.rs +0 -0
  226. /package/src/rust/{model → tasks}/restore_and_render.rs +0 -0
  227. /package/src/rust/{model → tasks}/update_and_render.rs +0 -0
@@ -26,18 +26,30 @@ use crate::components::number_column_style::NumberColumnStyle;
26
26
  use crate::components::string_column_style::StringColumnStyle;
27
27
  use crate::components::style_controls::CustomNumberFormat;
28
28
  use crate::custom_events::CustomEvents;
29
- use crate::model::*;
30
29
  use crate::presentation::Presentation;
31
30
  use crate::renderer::Renderer;
32
31
  use crate::session::Session;
33
- use crate::*;
32
+ use crate::tasks::{
33
+ HasCustomEvents, HasPresentation, HasRenderer, HasSession, SendPluginConfig,
34
+ get_column_style_control_options,
35
+ };
36
+ use crate::utils::PtrEqRc;
34
37
 
35
- #[derive(Clone, PartialEq, Properties, PerspectiveProperties!)]
38
+ #[derive(Clone, PartialEq, Properties)]
36
39
  pub struct StyleTabProps {
37
40
  pub ty: Option<ColumnType>,
38
41
  pub column_name: String,
39
42
  pub group_by_depth: u32,
40
43
 
44
+ /// View config snapshot — threaded from parent.
45
+ pub view_config: PtrEqRc<perspective_client::config::ViewConfig>,
46
+
47
+ /// Session metadata snapshot — threaded from parent.
48
+ pub metadata: PtrEqRc<crate::session::SessionMetadata>,
49
+
50
+ /// Selected theme name, threaded for PortalModal consumers.
51
+ pub selected_theme: Option<String>,
52
+
41
53
  // State
42
54
  pub custom_events: CustomEvents,
43
55
  pub presentation: Presentation,
@@ -45,123 +57,152 @@ pub struct StyleTabProps {
45
57
  pub session: Session,
46
58
  }
47
59
 
60
+ impl HasCustomEvents for StyleTabProps {
61
+ fn custom_events(&self) -> &CustomEvents {
62
+ &self.custom_events
63
+ }
64
+ }
65
+
66
+ impl HasPresentation for StyleTabProps {
67
+ fn presentation(&self) -> &Presentation {
68
+ &self.presentation
69
+ }
70
+ }
71
+
72
+ impl HasRenderer for StyleTabProps {
73
+ fn renderer(&self) -> &Renderer {
74
+ &self.renderer
75
+ }
76
+ }
77
+
78
+ impl HasSession for StyleTabProps {
79
+ fn session(&self) -> &Session {
80
+ &self.session
81
+ }
82
+ }
83
+
48
84
  #[function_component]
49
85
  pub fn StyleTab(props: &StyleTabProps) -> Html {
50
- let config = props.presentation().get_columns_config(&props.column_name);
86
+ let config = props.presentation.get_columns_config(&props.column_name);
51
87
  let on_change = yew::use_callback(
52
- (props.clone_state(), props.column_name.clone()),
88
+ (props.clone(), props.column_name.clone()),
53
89
  |config, (state, column_name)| {
54
90
  state.send_plugin_config(column_name, config);
55
91
  },
56
92
  );
57
93
 
58
- let components = props
59
- .get_column_style_control_options(&props.column_name)
60
- .map(|opts| {
61
- let mut components = vec![];
62
- if !props.session().get_view_config().group_by.is_empty() {
63
- let aggregate_depth = config.as_ref().map(|x| x.aggregate_depth as f64);
64
- components.push(("Aggregate Depth", html! {
65
- <AggregateDepthSelector
66
- group_by_depth={props.group_by_depth}
67
- on_change={on_change.clone()}
68
- column_name={props.column_name.to_owned()}
69
- value={aggregate_depth.unwrap_or_default() as u32}
70
- />
71
- }));
72
- }
73
-
74
- if let Some(default_config) = opts.datagrid_number_style {
75
- let config = config
76
- .as_ref()
77
- .map(|config| config.datagrid_number_style.clone());
78
-
79
- components.push(("Number Styles", html! {
80
- <NumberColumnStyle
81
- column_name={props.column_name.clone()}
82
- {config}
83
- {default_config}
84
- on_change={on_change.clone()}
85
- session={props.session()}
86
- />
87
- }));
88
- }
89
- if let Some(default_config) = opts.datagrid_string_style {
90
- let config = config
91
- .as_ref()
92
- .map(|config| config.datagrid_string_style.clone());
93
-
94
- components.push(("String Styles", html! {
95
- <StringColumnStyle {config} {default_config} on_change={on_change.clone()} />
96
- }));
97
- }
98
-
99
- if let Some(default_config) = opts.datagrid_datetime_style {
100
- let config = config
101
- .as_ref()
102
- .map(|config| config.datagrid_datetime_style.clone());
103
-
104
- let enable_time_config = props.ty.unwrap() == ColumnType::Datetime;
105
- components.push(("Datetime Styles", html! {
106
- <DatetimeColumnStyle
107
- {enable_time_config}
108
- {config}
109
- {default_config}
110
- on_change={on_change.clone()}
111
- />
112
- }))
113
- }
114
-
115
- if let Some(default_config) = opts.symbols {
116
- let restored_config = config
117
- .as_ref()
118
- .map(|config| config.symbols.clone())
119
- .unwrap_or_default();
120
-
121
- components.push(("Symbols", html! {
122
- <SymbolStyle
123
- {default_config}
124
- {restored_config}
125
- on_change={on_change.clone()}
126
- column_name={props.column_name.clone()}
127
- session={props.session().clone()}
128
- />
129
- }))
130
- }
131
-
132
- if opts.number_string_format.unwrap_or_default() {
133
- let restored_config = config
134
- .as_ref()
135
- .and_then(|config| config.number_format.clone())
136
- .unwrap_or_default();
137
-
138
- components.push(("Number Formatting", html! {
139
- <CustomNumberFormat
140
- {restored_config}
141
- on_change={on_change.clone()}
142
- view_type={props.ty.unwrap()}
143
- column_name={props.column_name.clone()}
144
- />
145
- }));
146
- }
147
-
148
- components
149
- .into_iter()
150
- .map(|(_title, component)| {
151
- html! {
152
- <fieldset class="style-control">
153
- // <legend >{ title }</legend>
154
- { component }
155
- </fieldset>
156
- }
157
- })
158
- .collect_vec()
159
- })
160
- .unwrap_or_else(|error| {
161
- vec![html! {
162
- <Stub message="Could not render column styles" error={Some(format!("{error:?}"))} />
163
- }]
164
- });
94
+ let components = get_column_style_control_options(
95
+ &props.renderer,
96
+ &props.view_config,
97
+ &props.metadata,
98
+ &props.column_name,
99
+ )
100
+ .map(|opts| {
101
+ let mut components = vec![];
102
+ if !props.view_config.group_by.is_empty() {
103
+ let aggregate_depth = config.as_ref().map(|x| x.aggregate_depth as f64);
104
+ components.push(("Aggregate Depth", html! {
105
+ <AggregateDepthSelector
106
+ group_by_depth={props.group_by_depth}
107
+ on_change={on_change.clone()}
108
+ column_name={props.column_name.to_owned()}
109
+ value={aggregate_depth.unwrap_or_default() as u32}
110
+ />
111
+ }));
112
+ }
113
+
114
+ if let Some(default_config) = opts.datagrid_number_style {
115
+ let config = config
116
+ .as_ref()
117
+ .map(|config| config.datagrid_number_style.clone());
118
+
119
+ components.push(("Number Styles", html! {
120
+ <NumberColumnStyle
121
+ column_name={props.column_name.clone()}
122
+ {config}
123
+ {default_config}
124
+ on_change={on_change.clone()}
125
+ session={props.session.clone()}
126
+ />
127
+ }));
128
+ }
129
+ if let Some(default_config) = opts.datagrid_string_style {
130
+ let config = config
131
+ .as_ref()
132
+ .map(|config| config.datagrid_string_style.clone());
133
+
134
+ components.push(("String Styles", html! {
135
+ <StringColumnStyle {config} {default_config} on_change={on_change.clone()} />
136
+ }));
137
+ }
138
+
139
+ if let Some(default_config) = opts.datagrid_datetime_style {
140
+ let config = config
141
+ .as_ref()
142
+ .map(|config| config.datagrid_datetime_style.clone());
143
+
144
+ let enable_time_config = props.ty.unwrap() == ColumnType::Datetime;
145
+ components.push(("Datetime Styles", html! {
146
+ <DatetimeColumnStyle
147
+ {enable_time_config}
148
+ {config}
149
+ {default_config}
150
+ on_change={on_change.clone()}
151
+ />
152
+ }))
153
+ }
154
+
155
+ if let Some(default_config) = opts.symbols {
156
+ let restored_config = config
157
+ .as_ref()
158
+ .map(|config| config.symbols.clone())
159
+ .unwrap_or_default();
160
+
161
+ components.push(("Symbols", html! {
162
+ <SymbolStyle
163
+ {default_config}
164
+ {restored_config}
165
+ on_change={on_change.clone()}
166
+ column_name={props.column_name.clone()}
167
+ selected_theme={props.selected_theme.clone()}
168
+ session={props.session.clone()}
169
+ />
170
+ }))
171
+ }
172
+
173
+ if opts.number_string_format.unwrap_or_default() {
174
+ let restored_config = config
175
+ .as_ref()
176
+ .and_then(|config| config.number_format.clone())
177
+ .unwrap_or_default();
178
+
179
+ components.push(("Number Formatting", html! {
180
+ <CustomNumberFormat
181
+ {restored_config}
182
+ on_change={on_change.clone()}
183
+ view_type={props.ty.unwrap()}
184
+ column_name={props.column_name.clone()}
185
+ />
186
+ }));
187
+ }
188
+
189
+ components
190
+ .into_iter()
191
+ .map(|(_title, component)| {
192
+ html! {
193
+ <fieldset class="style-control">
194
+ // <legend >{ title }</legend>
195
+ { component }
196
+ </fieldset>
197
+ }
198
+ })
199
+ .collect_vec()
200
+ })
201
+ .unwrap_or_else(|error| {
202
+ vec![html! {
203
+ <Stub message="Could not render column styles" error={Some(format!("{error:?}"))} />
204
+ }]
205
+ });
165
206
 
166
207
  html! {
167
208
  <div id="style-tab">
@@ -18,7 +18,7 @@ use std::rc::Rc;
18
18
 
19
19
  use derivative::Derivative;
20
20
  use itertools::Itertools;
21
- use perspective_client::config::{ColumnType, Expression};
21
+ use perspective_client::config::{ColumnType, Expression, ViewConfig};
22
22
  use perspective_client::utils::PerspectiveResultExt;
23
23
  use yew::{Callback, Component, Html, Properties, html, props};
24
24
 
@@ -34,14 +34,17 @@ use crate::components::expression_editor::ExpressionEditorProps;
34
34
  use crate::components::style::LocalStyle;
35
35
  use crate::components::type_icon::TypeIconType;
36
36
  use crate::custom_events::CustomEvents;
37
- use crate::model::*;
38
37
  use crate::presentation::{ColumnLocator, ColumnSettingsTab, Presentation};
39
38
  use crate::renderer::Renderer;
40
- use crate::session::Session;
41
- use crate::utils::*;
39
+ use crate::session::{Session, SessionMetadataRc};
40
+ use crate::tasks::{
41
+ EditExpression, HasCustomEvents, HasPresentation, HasRenderer, HasSession,
42
+ can_render_column_styles, locator_name_or_default, locator_view_type,
43
+ };
44
+ use crate::utils::PtrEqRc;
42
45
  use crate::*;
43
46
 
44
- #[derive(Clone, Derivative, Properties, PerspectiveProperties!)]
47
+ #[derive(Clone, Derivative, Properties)]
45
48
  #[derivative(Debug)]
46
49
  pub struct ColumnSettingsPanelProps {
47
50
  pub selected_column: ColumnLocator,
@@ -50,6 +53,20 @@ pub struct ColumnSettingsPanelProps {
50
53
  pub width_override: Option<i32>,
51
54
  pub on_select_tab: Callback<ColumnSettingsTab>,
52
55
 
56
+ /// Active plugin name threaded as a value prop so that plugin changes
57
+ /// trigger re-initialization via `changed()` rather than a PubSub
58
+ /// `render_limits_changed` subscription.
59
+ pub plugin_name: Option<String>,
60
+
61
+ /// Session metadata snapshot — threaded from `SessionProps`.
62
+ pub metadata: SessionMetadataRc,
63
+
64
+ /// View config snapshot — threaded from `SessionProps`.
65
+ pub view_config: PtrEqRc<ViewConfig>,
66
+
67
+ /// Selected theme name, threaded for PortalModal consumers.
68
+ pub selected_theme: Option<String>,
69
+
53
70
  // State
54
71
  #[derivative(Debug = "ignore")]
55
72
  pub custom_events: CustomEvents,
@@ -66,7 +83,36 @@ pub struct ColumnSettingsPanelProps {
66
83
 
67
84
  impl PartialEq for ColumnSettingsPanelProps {
68
85
  fn eq(&self, other: &Self) -> bool {
69
- self.selected_column == other.selected_column && self.selected_tab == other.selected_tab
86
+ self.selected_column == other.selected_column
87
+ && self.selected_tab == other.selected_tab
88
+ && self.plugin_name == other.plugin_name
89
+ && self.metadata == other.metadata
90
+ && self.view_config == other.view_config
91
+ && self.selected_theme == other.selected_theme
92
+ }
93
+ }
94
+
95
+ impl HasCustomEvents for ColumnSettingsPanelProps {
96
+ fn custom_events(&self) -> &CustomEvents {
97
+ &self.custom_events
98
+ }
99
+ }
100
+
101
+ impl HasPresentation for ColumnSettingsPanelProps {
102
+ fn presentation(&self) -> &Presentation {
103
+ &self.presentation
104
+ }
105
+ }
106
+
107
+ impl HasRenderer for ColumnSettingsPanelProps {
108
+ fn renderer(&self) -> &Renderer {
109
+ &self.renderer
110
+ }
111
+ }
112
+
113
+ impl HasSession for ColumnSettingsPanelProps {
114
+ fn session(&self) -> &Session {
115
+ &self.session
70
116
  }
71
117
  }
72
118
 
@@ -80,7 +126,6 @@ pub enum ColumnSettingsPanelMsg {
80
126
  OnSaveAttributes(()),
81
127
  OnResetAttributes(()),
82
128
  OnDelete(()),
83
- SessionUpdated(bool),
84
129
  }
85
130
 
86
131
  #[derive(Derivative)]
@@ -102,9 +147,6 @@ pub struct ColumnSettingsPanel {
102
147
  save_count: u8,
103
148
  save_enabled: bool,
104
149
  tabs: Vec<ColumnSettingsTab>,
105
-
106
- #[derivative(Debug = "ignore")]
107
- _session_sub: Option<Subscription>,
108
150
  }
109
151
 
110
152
  impl Component for ColumnSettingsPanel {
@@ -112,18 +154,7 @@ impl Component for ColumnSettingsPanel {
112
154
  type Properties = ColumnSettingsPanelProps;
113
155
 
114
156
  fn create(ctx: &yew::prelude::Context<Self>) -> Self {
115
- let session_cb = ctx
116
- .link()
117
- .callback(|(is_update, _)| ColumnSettingsPanelMsg::SessionUpdated(is_update));
118
-
119
- let session_sub = ctx
120
- .props()
121
- .renderer
122
- .render_limits_changed
123
- .add_listener(session_cb);
124
-
125
157
  let mut this = Self {
126
- _session_sub: Some(session_sub),
127
158
  initial_expr_value: Rc::default(),
128
159
  expr_value: Rc::default(),
129
160
  expr_valid: false,
@@ -233,14 +264,6 @@ impl Component for ColumnSettingsPanel {
233
264
  ctx.props().on_close.emit(());
234
265
  true
235
266
  },
236
- ColumnSettingsPanelMsg::SessionUpdated(is_update) => {
237
- if !is_update {
238
- self.initialize(ctx);
239
- true
240
- } else {
241
- false
242
- }
243
- },
244
267
  }
245
268
  }
246
269
 
@@ -264,6 +287,7 @@ impl Component for ColumnSettingsPanel {
264
287
  ColumnSettingsPanelMsg::SetHeaderValid(valid),
265
288
  ]
266
289
  }),
290
+ metadata: ctx.props().metadata.clone(),
267
291
  session: &ctx.props().session
268
292
  });
269
293
 
@@ -274,13 +298,28 @@ impl Component for ColumnSettingsPanel {
274
298
  alias: ctx.props().selected_column.name().cloned(),
275
299
  disabled: !ctx.props().selected_column.is_expr(),
276
300
  reset_count: self.reset_count,
301
+ metadata: ctx.props().metadata.clone(),
302
+ selected_theme: ctx.props().selected_theme.clone(),
277
303
  session: &ctx.props().session
278
304
  });
279
305
 
280
306
  let disable_delete = ctx
281
307
  .props()
282
- .session
283
- .is_locator_active(&ctx.props().selected_column);
308
+ .selected_column
309
+ .name()
310
+ .map(|name| {
311
+ let config = &ctx.props().view_config;
312
+ config.columns.iter().any(|maybe_col| {
313
+ maybe_col
314
+ .as_ref()
315
+ .map(|col| col == name)
316
+ .unwrap_or_default()
317
+ }) || config.group_by.iter().any(|col| col == name)
318
+ || config.split_by.iter().any(|col| col == name)
319
+ || config.filter.iter().any(|col| col.column() == name)
320
+ || config.sort.iter().any(|col| &col.0 == name)
321
+ })
322
+ .unwrap_or_default();
284
323
 
285
324
  let save_section = SaveSettingsProps {
286
325
  save_enabled: self.save_enabled,
@@ -302,15 +341,18 @@ impl Component for ColumnSettingsPanel {
302
341
  save_section,
303
342
  };
304
343
 
305
- let style_tab = props!(StyleTabProps {
344
+ let style_tab = StyleTabProps {
306
345
  ty: self.maybe_ty,
307
346
  column_name: self.column_name.clone(),
308
- group_by_depth: ctx.props().session.get_view_config().group_by.len() as u32,
309
- custom_events: ctx.props().custom_events(),
310
- presentation: ctx.props().presentation(),
311
- renderer: ctx.props().renderer(),
312
- session: ctx.props().session()
313
- });
347
+ group_by_depth: ctx.props().view_config.group_by.len() as u32,
348
+ view_config: ctx.props().view_config.clone(),
349
+ metadata: ctx.props().metadata.clone(),
350
+ selected_theme: ctx.props().selected_theme.clone(),
351
+ custom_events: ctx.props().custom_events.clone(),
352
+ presentation: ctx.props().presentation.clone(),
353
+ renderer: ctx.props().renderer.clone(),
354
+ session: ctx.props().session.clone(),
355
+ };
314
356
 
315
357
  let tab_children = self.tabs.iter().map(|tab| match tab {
316
358
  ColumnSettingsTab::Attributes => html! { <AttributesTab ..attrs_tab.clone() /> },
@@ -356,15 +398,12 @@ impl ColumnSettingsPanel {
356
398
  }
357
399
 
358
400
  fn initialize(&mut self, ctx: &yew::prelude::Context<Self>) {
359
- let column_name = ctx
360
- .props()
361
- .session
362
- .locator_name_or_default(&ctx.props().selected_column);
401
+ let column_name =
402
+ locator_name_or_default(&ctx.props().metadata, &ctx.props().selected_column);
363
403
 
364
404
  let initial_expr_value = ctx
365
405
  .props()
366
- .session
367
- .metadata()
406
+ .metadata
368
407
  .get_expression_by_alias(&column_name)
369
408
  .unwrap_or_default();
370
409
 
@@ -372,19 +411,19 @@ impl ColumnSettingsPanel {
372
411
  let initial_header_value =
373
412
  (*initial_expr_value != column_name).then_some(column_name.clone());
374
413
 
375
- let maybe_ty = ctx
376
- .props()
377
- .session()
378
- .locator_view_type(&ctx.props().selected_column);
414
+ let maybe_ty = locator_view_type(&ctx.props().metadata, &ctx.props().selected_column);
379
415
 
380
416
  let tabs = {
381
417
  let mut tabs = vec![];
382
418
  let is_new_expr = ctx.props().selected_column.is_new_expr();
383
419
  let show_styles = !is_new_expr
384
- && ctx
385
- .props()
386
- .can_render_column_styles(&column_name)
387
- .unwrap_or_default();
420
+ && can_render_column_styles(
421
+ &ctx.props().renderer,
422
+ &ctx.props().view_config,
423
+ &ctx.props().metadata,
424
+ &column_name,
425
+ )
426
+ .unwrap_or_default();
388
427
 
389
428
  if !is_new_expr && show_styles {
390
429
  tabs.push(ColumnSettingsTab::Style);
@@ -414,7 +453,6 @@ impl ColumnSettingsPanel {
414
453
  on_input,
415
454
  on_save,
416
455
  on_validate,
417
- _session_sub: self._session_sub.take(),
418
456
  ..*self
419
457
  }
420
458
  }
@@ -19,9 +19,9 @@ use web_sys::*;
19
19
  use yew::html::Scope;
20
20
  use yew::prelude::*;
21
21
 
22
+ use crate::components::column_dropdown::ColumnDropDownElement;
22
23
  use crate::components::column_selector::{EmptyColumn, InPlaceColumn, InvalidColumn};
23
24
  use crate::components::type_icon::TypeIcon;
24
- use crate::custom_elements::ColumnDropDownElement;
25
25
  use crate::dragdrop::*;
26
26
  use crate::utils::DragTarget;
27
27
 
@@ -309,6 +309,7 @@ where
309
309
  if ctx.props().disabled && ctx.props().is_dragover.is_none() {
310
310
  <div class="pivot-column">
311
311
  <div class="pivot-column-border pivot-column-total">
312
+ <span class="drag-handle icon" />
312
313
  <TypeIcon ty={ColumnType::Integer} />
313
314
  <span class="column_name">{ "TOTAL" }</span>
314
315
  </div>
@@ -24,7 +24,7 @@ pub fn SidebarCloseButton(p: &SidebarCloseButtonProps) -> Html {
24
24
  let id = &p.id;
25
25
  html! {
26
26
  <div {onclick} {id} class="sidebar_close_button">
27
- <div class="sidebar_close_button_inner" />
27
+ <div class="sidebar_close_button_inner"><span class="icon" /></div>
28
28
  </div>
29
29
  }
30
30
  }
@@ -412,6 +412,7 @@ impl ResizingState {
412
412
  Orientation::Vertical => event.client_y(),
413
413
  })
414
414
  });
415
+
415
416
  move |x| cb.emit(x)
416
417
  });
417
418
 
@@ -95,7 +95,7 @@ impl<T: ColumnTab> Component for TabList<T> {
95
95
  <div class="tab-border" />
96
96
  </span>
97
97
  </div>
98
- <div id="format-tab" class="tab-content">
98
+ <div id="format-tab" class="tab-content scrollable">
99
99
  { ctx.props().children.iter().nth(self.selected_idx) }
100
100
  </div>
101
101
  </>
@@ -15,47 +15,25 @@ use std::rc::Rc;
15
15
  use yew::prelude::*;
16
16
 
17
17
  use super::containers::dropdown_menu::*;
18
- use super::modal::*;
19
- use super::style::StyleProvider;
20
- use crate::model::*;
21
18
  use crate::renderer::*;
22
- use crate::utils::*;
19
+ use crate::tasks::*;
23
20
 
24
21
  type CopyDropDownMenuItem = DropDownMenuItem<ExportFile>;
25
22
 
26
23
  #[derive(Properties, PartialEq)]
27
24
  pub struct CopyDropDownMenuProps {
28
25
  pub callback: Callback<ExportFile>,
29
- pub root: web_sys::HtmlElement,
30
26
  pub renderer: Renderer,
31
-
32
- #[prop_or_default]
33
- weak_link: WeakScope<CopyDropDownMenu>,
34
- }
35
-
36
- impl ModalLink<CopyDropDownMenu> for CopyDropDownMenuProps {
37
- fn weak_link(&self) -> &'_ WeakScope<CopyDropDownMenu> {
38
- &self.weak_link
39
- }
40
27
  }
41
28
 
42
- pub struct CopyDropDownMenu {
43
- _sub: Subscription,
44
- }
29
+ pub struct CopyDropDownMenu {}
45
30
 
46
31
  impl Component for CopyDropDownMenu {
47
32
  type Message = ();
48
33
  type Properties = CopyDropDownMenuProps;
49
34
 
50
- fn create(ctx: &Context<Self>) -> Self {
51
- ctx.set_modal_link();
52
- let _sub = ctx
53
- .props()
54
- .renderer
55
- .plugin_changed
56
- .add_listener(ctx.link().callback(|_| ()));
57
-
58
- Self { _sub }
35
+ fn create(_ctx: &Context<Self>) -> Self {
36
+ Self {}
59
37
  }
60
38
 
61
39
  fn update(&mut self, _ctx: &Context<Self>, _msg: Self::Message) -> bool {
@@ -67,12 +45,13 @@ impl Component for CopyDropDownMenu {
67
45
  let is_chart = plugin.name().as_str() != "Datagrid";
68
46
  let has_selection = ctx.props().renderer.get_selection().is_some();
69
47
  html! {
70
- <StyleProvider root={ctx.props().root.clone()}>
48
+ <>
49
+ <div id="test" />
71
50
  <DropDownMenu<ExportFile>
72
51
  values={Rc::new(get_menu_items(is_chart, has_selection))}
73
52
  callback={&ctx.props().callback}
74
53
  />
75
- </StyleProvider>
54
+ </>
76
55
  }
77
56
  }
78
57
  }