@perspective-dev/viewer 5.3.1 → 5.5.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 (291) hide show
  1. package/dist/cdn/perspective-viewer.js +6 -6
  2. package/dist/cdn/perspective-viewer.js.map +2 -2
  3. package/dist/css/blueprint.css +1 -1
  4. package/dist/css/botanical.css +1 -1
  5. package/dist/css/dracula.css +1 -1
  6. package/dist/css/eggplant.css +1 -1
  7. package/dist/css/gruvbox-dark.css +1 -1
  8. package/dist/css/gruvbox.css +1 -1
  9. package/dist/css/icons.css +1 -1
  10. package/dist/css/intl/ar.css +1 -0
  11. package/dist/css/intl/de.css +1 -1
  12. package/dist/css/intl/es.css +1 -1
  13. package/dist/css/intl/fr.css +1 -1
  14. package/dist/css/intl/ja.css +1 -1
  15. package/dist/css/intl/pt.css +1 -1
  16. package/dist/css/intl/zh.css +1 -1
  17. package/dist/css/intl.css +1 -1
  18. package/dist/css/ledger.css +1 -1
  19. package/dist/css/monokai.css +1 -1
  20. package/dist/css/nord.css +1 -1
  21. package/dist/css/phosphor.css +1 -1
  22. package/dist/css/pro-dark.css +1 -1
  23. package/dist/css/pro.css +1 -1
  24. package/dist/css/solarized-dark.css +1 -1
  25. package/dist/css/solarized.css +1 -1
  26. package/dist/css/themes.css +1 -1
  27. package/dist/css/vaporwave.css +1 -1
  28. package/dist/css/velvet.css +1 -1
  29. package/dist/docs/perspective-docs.json +1 -1
  30. package/dist/esm/extensions.d.ts +0 -5
  31. package/dist/esm/perspective-viewer.d.ts +2 -0
  32. package/dist/esm/perspective-viewer.inline.js +8 -8
  33. package/dist/esm/perspective-viewer.inline.js.map +2 -2
  34. package/dist/esm/perspective-viewer.js +5 -5
  35. package/dist/esm/perspective-viewer.js.map +2 -2
  36. package/dist/esm/plugin.d.ts +9 -0
  37. package/dist/esm/ts-rs/AddPanelOptions.d.ts +9 -0
  38. package/dist/esm/ts-rs/GetViewMode.d.ts +4 -0
  39. package/dist/esm/ts-rs/GetViewOptions.d.ts +8 -0
  40. package/dist/esm/ts-rs/RestoreOptions.d.ts +6 -0
  41. package/dist/esm/ts-rs/RestoreWorkspaceOptions.d.ts +9 -0
  42. package/dist/esm/ts-rs/ViewOnUpdateResp.d.ts +1 -1
  43. package/dist/esm/ts-rs/WorkspaceConfigUpdate.d.ts +30 -22
  44. package/dist/wasm/perspective-viewer.d.ts +137 -104
  45. package/dist/wasm/perspective-viewer.js +337 -269
  46. package/dist/wasm/perspective-viewer.wasm +0 -0
  47. package/dist/wasm/perspective-viewer.wasm.d.ts +20 -30
  48. package/package.json +7 -7
  49. package/src/css/_context_menu.css +1 -1
  50. package/src/css/_dropdown_menu.css +1 -1
  51. package/src/css/_viewer_bundle.css +7 -8
  52. package/src/css/column-dropdown.css +0 -3
  53. package/src/css/column-selector.css +18 -7
  54. package/src/css/column-settings-panel.css +0 -225
  55. package/src/css/column-style.css +175 -0
  56. package/src/css/config-selector.css +19 -11
  57. package/src/css/filter-dropdown.css +5 -5
  58. package/src/css/form/debug.css +37 -4
  59. package/src/css/function-dropdown.css +5 -5
  60. package/src/css/panel-tab.css +23 -0
  61. package/src/css/plugin-selector.css +5 -5
  62. package/src/css/render-warning.css +1 -1
  63. package/src/css/status-bar.css +63 -21
  64. package/src/css/type-icon.css +1 -3
  65. package/src/css/{containers → ui/containers}/context-menu.css +22 -16
  66. package/src/css/ui/containers/dropdown-menu-items.css +66 -0
  67. package/src/css/{containers → ui/containers}/dropdown-menu.css +8 -53
  68. package/src/css/{containers → ui/containers}/tabs.css +2 -1
  69. package/src/css/viewer.css +32 -26
  70. package/src/rust/agent/tools.rs +80 -7
  71. package/src/rust/components/chat_panel.rs +1 -1
  72. package/src/rust/components/{form/code_editor.rs → code_editor.rs} +4 -2
  73. package/src/rust/components/column_dropdown.rs +2 -2
  74. package/src/rust/components/column_selector/active_column.rs +24 -9
  75. package/src/rust/components/column_selector/aggregate_selector.rs +1 -1
  76. package/src/rust/components/column_selector/config_selector.rs +2 -2
  77. package/src/rust/components/column_selector/filter_column.rs +8 -5
  78. package/src/rust/components/column_selector/inactive_column.rs +4 -2
  79. package/src/rust/components/column_selector/pivot_column.rs +4 -3
  80. package/src/rust/components/column_selector/sort_column.rs +7 -4
  81. package/src/rust/components/column_selector.rs +14 -3
  82. package/src/rust/components/column_settings_sidebar/style_tab/agg_depth_selector.rs +1 -1
  83. package/src/rust/components/column_settings_sidebar/style_tab/alignment_field.rs +90 -0
  84. package/src/rust/components/column_settings_sidebar/style_tab/font_field.rs +267 -0
  85. package/src/rust/components/column_settings_sidebar/style_tab/primitive_field.rs +16 -18
  86. package/src/rust/components/column_settings_sidebar/style_tab/symbol/symbol_selector.rs +1 -1
  87. package/src/rust/components/column_settings_sidebar/style_tab.rs +70 -12
  88. package/src/rust/components/column_settings_sidebar.rs +3 -4
  89. package/src/rust/components/copy_dropdown.rs +1 -1
  90. package/src/rust/components/datetime_column_style/custom.rs +1 -3
  91. package/src/rust/components/datetime_column_style/simple.rs +1 -2
  92. package/src/rust/components/datetime_column_style.rs +1 -2
  93. package/src/rust/components/{form/debug.rs → debug_panel.rs} +112 -55
  94. package/src/rust/components/{containers/dragdrop_list.rs → dragdrop_list.rs} +2 -10
  95. package/src/rust/components/export_dropdown.rs +1 -1
  96. package/src/rust/components/expression_editor.rs +1 -1
  97. package/src/rust/components/filter_dropdown.rs +2 -2
  98. package/src/rust/components/function_dropdown.rs +2 -2
  99. package/src/rust/components/main_panel/frame_theme.rs +3 -59
  100. package/src/rust/components/main_panel/msg.rs +4 -0
  101. package/src/rust/components/main_panel/reconcile.rs +9 -4
  102. package/src/rust/components/main_panel/render.rs +62 -11
  103. package/src/rust/components/main_panel/update.rs +39 -10
  104. package/src/rust/components/main_panel.rs +40 -2
  105. package/src/rust/components/mod.rs +5 -8
  106. package/src/rust/components/new_panel_menu.rs +97 -0
  107. package/src/rust/components/number_series_style.rs +2 -3
  108. package/src/rust/components/panel_menu.rs +61 -87
  109. package/src/rust/components/panel_tab.rs +100 -43
  110. package/src/rust/components/plugin_tab.rs +58 -4
  111. package/src/rust/components/render_warning.rs +2 -2
  112. package/src/rust/components/{status_bar_counter.rs → rows_counter.rs} +10 -10
  113. package/src/rust/components/settings_panel.rs +8 -2
  114. package/src/rust/components/status_bar.rs +113 -19
  115. package/src/rust/components/status_indicator.rs +9 -14
  116. package/src/rust/components/{style/mod.rs → style.rs} +32 -15
  117. package/src/rust/components/style_controls/number_string_format/digits_section.rs +3 -5
  118. package/src/rust/components/style_controls/number_string_format/misc_section.rs +1 -1
  119. package/src/rust/components/style_controls/number_string_format/style_section.rs +1 -1
  120. package/src/rust/components/viewer/msg.rs +43 -30
  121. package/src/rust/components/viewer/panels.rs +53 -19
  122. package/src/rust/components/viewer/render.rs +16 -13
  123. package/src/rust/components/viewer/settings.rs +137 -68
  124. package/src/rust/components/viewer/snapshots.rs +5 -10
  125. package/src/rust/components/viewer/wiring.rs +11 -22
  126. package/src/rust/components/viewer.rs +15 -10
  127. package/src/rust/components/window_editor.rs +8 -9
  128. package/src/rust/config/column_config_schema.rs +312 -33
  129. package/src/rust/config/datetime_column_style.rs +0 -11
  130. package/src/rust/config/mod.rs +0 -4
  131. package/src/rust/config/options.rs +50 -0
  132. package/src/rust/config/workspace_config.rs +70 -24
  133. package/src/rust/custom_elements/debug_plugin.rs +1 -10
  134. package/src/rust/custom_elements/mod.rs +0 -3
  135. package/src/rust/custom_elements/viewer.rs +194 -115
  136. package/src/rust/js/plugin.rs +3 -87
  137. package/src/rust/lib.rs +19 -18
  138. package/src/rust/presentation/column_locator.rs +3 -7
  139. package/src/rust/presentation.rs +68 -38
  140. package/src/rust/queries/hosted_tables.rs +38 -0
  141. package/src/rust/queries/mod.rs +2 -0
  142. package/src/rust/queries/palette_set.rs +1 -1
  143. package/src/rust/queries/plugin_column_styles.rs +6 -5
  144. package/src/rust/renderer/activate.rs +1 -3
  145. package/src/rust/renderer/dispatch.rs +83 -22
  146. package/src/rust/renderer/limits.rs +24 -5
  147. package/src/rust/renderer/plugin_config.rs +201 -41
  148. package/src/rust/renderer/plugin_store.rs +2 -1
  149. package/src/rust/renderer/props.rs +7 -1
  150. package/src/rust/renderer.rs +23 -27
  151. package/src/rust/session/props.rs +8 -3
  152. package/src/rust/session/view_subscription.rs +19 -6
  153. package/src/rust/session.rs +189 -150
  154. package/src/rust/tasks/create_panel.rs +5 -2
  155. package/src/rust/tasks/dismiss_render_warning.rs +1 -1
  156. package/src/rust/tasks/eject.rs +1 -0
  157. package/src/rust/tasks/pipeline.rs +18 -67
  158. package/src/rust/tasks/presize_panels.rs +24 -1
  159. package/src/rust/tasks/restore_and_render.rs +0 -17
  160. package/src/rust/tasks/restore_panel.rs +129 -30
  161. package/src/rust/tasks/send_column_config.rs +4 -1
  162. package/src/rust/tasks/send_plugin_config.rs +1 -1
  163. package/src/rust/tasks/set_edit_mode.rs +1 -1
  164. package/src/rust/tasks/sync_update_panels.rs +147 -67
  165. package/src/rust/tasks/table_lifecycle.rs +7 -1
  166. package/src/rust/{components → ui}/containers/control_group.rs +25 -3
  167. package/src/rust/{components → ui}/containers/dropdown_menu.rs +5 -2
  168. package/src/rust/{components → ui}/containers/mod.rs +0 -4
  169. package/src/rust/{components → ui}/containers/scroll_panel.rs +0 -22
  170. package/src/rust/{components → ui}/containers/scroll_panel_item.rs +0 -13
  171. package/src/rust/{components → ui}/containers/select.rs +0 -6
  172. package/src/rust/{components → ui}/containers/sidebar.rs +5 -32
  173. package/src/rust/{components → ui}/containers/split_panel.rs +8 -80
  174. package/src/rust/{components → ui}/containers/tab_list.rs +15 -9
  175. package/src/rust/{components → ui}/context_menu.rs +8 -13
  176. package/src/rust/{presentation/drag_helpers.rs → ui/dragdrop.rs} +26 -31
  177. package/src/rust/{components → ui}/font_loader.rs +0 -26
  178. package/src/rust/{components → ui}/form/color_selector.rs +4 -2
  179. package/src/rust/{config/datetime_column_style/color_mode.rs → ui/form/intl_label.rs} +42 -42
  180. package/src/rust/{components → ui}/form/mirrored_textarea.rs +0 -53
  181. package/src/rust/{components → ui}/form/mod.rs +1 -10
  182. package/src/rust/{components → ui}/form/multi_stop_gradient_selector.rs +4 -5
  183. package/src/rust/{components → ui}/form/named_value_picker.rs +1 -6
  184. package/src/rust/{components → ui}/form/number_field.rs +1 -1
  185. package/src/rust/{components → ui}/form/number_range_field.rs +1 -1
  186. package/src/rust/{components → ui}/form/optional_field.rs +3 -1
  187. package/src/rust/{components → ui}/form/palette_selector.rs +4 -5
  188. package/src/rust/{components → ui}/form/select_enum_field.rs +2 -2
  189. package/src/rust/{components → ui}/form/select_value_field.rs +2 -2
  190. package/src/rust/ui/mod.rs +56 -0
  191. package/src/rust/ui/modal.rs +61 -0
  192. package/src/rust/{components → ui}/portal.rs +175 -79
  193. package/src/rust/{components/style/style_provider.rs → ui/style.rs} +4 -34
  194. package/src/rust/utils/completion.rs +0 -6
  195. package/src/rust/{config/css_scale.rs → utils/css.rs} +38 -35
  196. package/src/rust/utils/css_vars.rs +13 -0
  197. package/src/rust/utils/local_fonts.rs +395 -0
  198. package/src/rust/utils/mod.rs +4 -0
  199. package/src/rust/workspace.rs +47 -0
  200. package/src/svg/boolean-type.svg +3 -2
  201. package/src/svg/checkbox-checked-icon.svg +4 -7
  202. package/src/svg/checkbox-hover.svg +3 -3
  203. package/src/svg/checkbox-off.svg +2 -3
  204. package/src/svg/checkbox-on.svg +3 -3
  205. package/src/svg/checkbox-unchecked-icon.svg +3 -3
  206. package/src/svg/datagrid-select-column.svg +8 -15
  207. package/src/svg/datagrid-select-region.svg +8 -15
  208. package/src/svg/datagrid-select-row-tree.svg +7 -12
  209. package/src/svg/datagrid-select-row.svg +8 -13
  210. package/src/svg/date-type.svg +4 -23
  211. package/src/svg/drawer-tab-hover.svg +4 -6
  212. package/src/svg/drawer-tab-invert-hover.svg +5 -5
  213. package/src/svg/drawer-tab-invert.svg +3 -5
  214. package/src/svg/drawer-tab.svg +5 -5
  215. package/src/svg/mega-menu-icons-candlestick.svg +5 -37
  216. package/src/svg/mega-menu-icons-ohlc.svg +4 -39
  217. package/src/svg/mega-menu-icons-sunburst.svg +4 -38
  218. package/src/svg/mega-menu-icons-treemap.svg +3 -43
  219. package/src/svg/mega-menu-icons-y-scatter.svg +12 -72
  220. package/src/svg/new-panel-icon.svg +4 -0
  221. package/src/svg/number-type.svg +3 -3
  222. package/src/svg/radio-hover.svg +3 -3
  223. package/src/svg/radio-off.svg +3 -3
  224. package/src/svg/radio-on.svg +3 -3
  225. package/src/svg/status_ok.svg +3 -26
  226. package/src/svg/status_pending.svg +3 -0
  227. package/src/svg/string-type.svg +4 -8
  228. package/src/svg/theme-icon.svg +5 -5
  229. package/src/themes/blueprint.css +0 -57
  230. package/src/themes/botanical.css +0 -36
  231. package/src/themes/defaults.css +1 -24
  232. package/src/themes/dracula.css +0 -58
  233. package/src/themes/eggplant.css +0 -58
  234. package/src/themes/gruvbox-dark.css +0 -65
  235. package/src/themes/gruvbox.css +1 -39
  236. package/src/themes/icons.css +2 -8
  237. package/src/themes/intl/ar.css +198 -0
  238. package/src/themes/intl/de.css +63 -40
  239. package/src/themes/intl/es.css +58 -35
  240. package/src/themes/intl/fr.css +57 -34
  241. package/src/themes/intl/ja.css +59 -36
  242. package/src/themes/intl/pt.css +54 -31
  243. package/src/themes/intl/zh.css +65 -42
  244. package/src/themes/intl.css +34 -8
  245. package/src/themes/ledger.css +1 -59
  246. package/src/themes/monokai.css +0 -51
  247. package/src/themes/nord.css +0 -55
  248. package/src/themes/phosphor.css +0 -48
  249. package/src/themes/pro-dark.css +0 -37
  250. package/src/themes/pro.css +3 -23
  251. package/src/themes/solarized-dark.css +0 -31
  252. package/src/themes/solarized.css +1 -24
  253. package/src/themes/vaporwave.css +0 -44
  254. package/src/themes/velvet.css +0 -60
  255. package/src/ts/extensions.ts +0 -13
  256. package/src/ts/perspective-viewer.ts +2 -0
  257. package/src/ts/plugin.ts +9 -0
  258. package/src/ts/ts-rs/AddPanelOptions.ts +10 -0
  259. package/src/ts/ts-rs/{DatetimeColorMode.ts → GetViewMode.ts} +4 -1
  260. package/src/ts/ts-rs/GetViewOptions.ts +7 -0
  261. package/src/ts/ts-rs/RestoreOptions.ts +7 -1
  262. package/src/ts/ts-rs/RestoreWorkspaceOptions.ts +10 -0
  263. package/src/ts/ts-rs/ViewOnUpdateResp.ts +1 -1
  264. package/src/ts/ts-rs/WorkspaceConfigUpdate.ts +31 -20
  265. package/dist/esm/ts-rs/DatetimeColorMode.d.ts +0 -1
  266. package/dist/esm/ts-rs/FormatMode.d.ts +0 -1
  267. package/dist/esm/ts-rs/StringColorMode.d.ts +0 -1
  268. package/src/css/plugin-settings-panel.css +0 -171
  269. package/src/rust/components/modal.rs +0 -177
  270. package/src/rust/components/string_column_style.rs +0 -121
  271. package/src/rust/config/string_column_style.rs +0 -135
  272. package/src/rust/custom_elements/copy_dropdown.rs +0 -219
  273. package/src/rust/custom_elements/export_dropdown.rs +0 -216
  274. package/src/rust/custom_elements/modal.rs +0 -299
  275. package/src/ts/ts-rs/FormatMode.ts +0 -3
  276. package/src/ts/ts-rs/StringColorMode.ts +0 -3
  277. /package/dist/wasm/snippets/{perspective-viewer-87bd21b215d0631a → perspective-viewer-ea71a3990cad35d7}/inline0.js +0 -0
  278. /package/dist/wasm/snippets/{perspective-viewer-87bd21b215d0631a → perspective-viewer-ea71a3990cad35d7}/inline1.js +0 -0
  279. /package/dist/wasm/snippets/{perspective-viewer-87bd21b215d0631a → perspective-viewer-ea71a3990cad35d7}/inline2.js +0 -0
  280. /package/dist/wasm/snippets/{perspective-viewer-87bd21b215d0631a → perspective-viewer-ea71a3990cad35d7}/inline3.js +0 -0
  281. /package/dist/wasm/snippets/{perspective-viewer-87bd21b215d0631a → perspective-viewer-ea71a3990cad35d7}/inline4.js +0 -0
  282. /package/dist/wasm/snippets/{perspective-viewer-87bd21b215d0631a → perspective-viewer-ea71a3990cad35d7}/inline5.js +0 -0
  283. /package/src/css/{containers → ui/containers}/scroll-panel.css +0 -0
  284. /package/src/css/{containers → ui/containers}/split-panel.css +0 -0
  285. /package/src/css/{dom → ui/dom}/checkbox.css +0 -0
  286. /package/src/css/{dom → ui/dom}/scrollbar.css +0 -0
  287. /package/src/css/{dom → ui/dom}/select.css +0 -0
  288. /package/src/css/{form → ui/form}/mirrored-textarea.css +0 -0
  289. /package/src/rust/components/{form → code_editor}/highlight.rs +0 -0
  290. /package/src/rust/{components → ui}/containers/sidebar_close_button.rs +0 -0
  291. /package/src/rust/{components → ui}/form/number_input.rs +0 -0
@@ -19,14 +19,14 @@ use wasm_bindgen::JsCast;
19
19
  use web_sys::*;
20
20
  use yew::prelude::*;
21
21
 
22
- use crate::components::containers::dragdrop_list::*;
23
- use crate::components::containers::select::*;
22
+ use crate::components::dragdrop_list::*;
24
23
  use crate::components::filter_dropdown::FilterDropDownElement;
25
24
  use crate::components::type_icon::TypeIcon;
26
25
  use crate::presentation::Presentation;
27
26
  use crate::renderer::*;
28
27
  use crate::session::*;
29
28
  use crate::tasks::apply_and_render;
29
+ use crate::ui::{Select, SelectItem};
30
30
  use crate::utils::*;
31
31
 
32
32
  #[derive(Clone, Properties)]
@@ -240,9 +240,12 @@ impl Component for FilterColumn {
240
240
  let event_name = ctx.props().filter.column().to_owned();
241
241
  let presentation = ctx.props().presentation.clone();
242
242
  move |event: DragEvent| {
243
- presentation.set_drag_image(&event).unwrap();
244
- presentation
245
- .notify_drag_start(event_name.to_string(), DragEffect::Move(DragTarget::Filter))
243
+ if presentation.set_drag_image(&event) {
244
+ presentation.notify_drag_start(
245
+ event_name.to_string(),
246
+ DragEffect::Move(DragTarget::Filter),
247
+ )
248
+ }
246
249
  }
247
250
  });
248
251
 
@@ -141,8 +141,10 @@ impl Component for InactiveColumn {
141
141
  let event_name = ctx.props().name.to_owned();
142
142
  let presentation = ctx.props().presentation.clone();
143
143
  move |event: DragEvent| {
144
- presentation.set_drag_image(&event).unwrap();
145
- presentation.notify_drag_start(event_name.to_string(), DragEffect::Copy);
144
+ if presentation.set_drag_image(&event) {
145
+ presentation.notify_drag_start(event_name.to_string(), DragEffect::Copy);
146
+ }
147
+
146
148
  MouseLeave(true)
147
149
  }
148
150
  });
@@ -14,7 +14,7 @@ use perspective_client::config::ColumnType;
14
14
  use web_sys::*;
15
15
  use yew::prelude::*;
16
16
 
17
- use crate::components::containers::dragdrop_list::*;
17
+ use crate::components::dragdrop_list::*;
18
18
  use crate::components::type_icon::TypeIcon;
19
19
  use crate::presentation::Presentation;
20
20
  use crate::session::*;
@@ -73,8 +73,9 @@ impl Component for PivotColumn {
73
73
  let presentation = ctx.props().presentation.clone();
74
74
  let action = ctx.props().action;
75
75
  move |event: DragEvent| {
76
- presentation.set_drag_image(&event).unwrap();
77
- presentation.notify_drag_start(event_name.to_string(), DragEffect::Move(action))
76
+ if presentation.set_drag_image(&event) {
77
+ presentation.notify_drag_start(event_name.to_string(), DragEffect::Move(action))
78
+ }
78
79
  }
79
80
  });
80
81
 
@@ -14,7 +14,7 @@ use perspective_client::config::*;
14
14
  use web_sys::*;
15
15
  use yew::prelude::*;
16
16
 
17
- use crate::components::containers::dragdrop_list::*;
17
+ use crate::components::dragdrop_list::*;
18
18
  use crate::components::type_icon::TypeIcon;
19
19
  use crate::presentation::Presentation;
20
20
  use crate::renderer::*;
@@ -104,9 +104,12 @@ impl Component for SortColumn {
104
104
  let event_name = ctx.props().sort.0.to_owned();
105
105
  let presentation = ctx.props().presentation.clone();
106
106
  move |event: DragEvent| {
107
- presentation.set_drag_image(&event).unwrap();
108
- presentation
109
- .notify_drag_start(event_name.to_string(), DragEffect::Move(DragTarget::Sort))
107
+ if presentation.set_drag_image(&event) {
108
+ presentation.notify_drag_start(
109
+ event_name.to_string(),
110
+ DragEffect::Move(DragTarget::Sort),
111
+ )
112
+ }
110
113
  }
111
114
  });
112
115
 
@@ -38,10 +38,7 @@ use self::active_column::*;
38
38
  use self::add_expression_button::AddExpressionButton;
39
39
  use self::config_selector::ConfigSelector;
40
40
  use self::inactive_column::*;
41
- use super::containers::scroll_panel::*;
42
- use super::containers::split_panel::{Orientation, SplitPanel};
43
41
  use crate::components::column_dropdown::{ColumnDropDownElement, ColumnDropDownPortal};
44
- use crate::components::containers::scroll_panel_item::ScrollPanelItem;
45
42
  use crate::config::PluginStaticConfig;
46
43
  use crate::presentation::{ColumnLocator, ColumnSettingsTarget, DragDropContainer, Presentation};
47
44
  use crate::queries::{
@@ -51,6 +48,7 @@ use crate::renderer::*;
51
48
  use crate::session::drag_drop_update::*;
52
49
  use crate::session::*;
53
50
  use crate::tasks::apply_and_render;
51
+ use crate::ui::{Orientation, ScrollPanel, ScrollPanelItem, SplitPanel};
54
52
  use crate::utils::*;
55
53
 
56
54
  #[derive(Properties)]
@@ -69,6 +67,10 @@ pub struct ColumnSelectorProps {
69
67
  /// prop on `SettingsPanelProps`.
70
68
  pub plugin_static_config: Rc<PluginStaticConfig>,
71
69
  pub view_config: PtrEqRc<ViewConfig>,
70
+
71
+ /// Snapshot of the active plugin's `columns_config` bucket, whose
72
+ /// non-empty entries mark their active columns as modified.
73
+ pub columns_config: PtrEqRc<ColumnConfigMap>,
72
74
  pub drag_column: Option<String>,
73
75
 
74
76
  /// Cloned session metadata snapshot — threaded from `SessionProps`
@@ -112,6 +114,7 @@ impl PartialEq for ColumnSelectorProps {
112
114
  && self.named_column_count == rhs.named_column_count
113
115
  && self.plugin_static_config == rhs.plugin_static_config
114
116
  && self.view_config == rhs.view_config
117
+ && self.columns_config == rhs.columns_config
115
118
  && self.drag_column == rhs.drag_column
116
119
  && self.metadata == rhs.metadata
117
120
  && self.selected_theme == rhs.selected_theme
@@ -333,6 +336,7 @@ impl Component for ColumnSelector {
333
336
  prop_config.clone()
334
337
  };
335
338
 
339
+ let is_last_column = config.columns.len() == 1;
336
340
  let is_aggregated = config.is_aggregated();
337
341
  let columns_iter = ColumnsIteratorSet::new(&config, metadata, renderer, presentation);
338
342
  let onselect = ctx.link().callback(|()| Redraw);
@@ -456,6 +460,11 @@ impl Component for ColumnSelector {
456
460
  name.get_name().is_some() && renderer.can_render_column_styles();
457
461
 
458
462
  let show_edit_btn = is_expression || is_window || can_render_styles;
463
+ let is_modified = name
464
+ .get_name()
465
+ .and_then(|n| ctx.props().columns_config.get(n))
466
+ .is_some_and(|entry| !entry.is_empty());
467
+
459
468
  let on_open_expr_panel = &ctx.props().on_open_expr_panel;
460
469
  html_nested! {
461
470
  <ScrollPanelItem {key} {size_hint}>
@@ -466,8 +475,10 @@ impl Component for ColumnSelector {
466
475
  {is_editing}
467
476
  {is_expression}
468
477
  {is_window}
478
+ {is_modified}
469
479
  {show_edit_btn}
470
480
  {col_type}
481
+ {is_last_column}
471
482
  view_config={config.clone()}
472
483
  metadata={metadata.clone()}
473
484
  {name}
@@ -13,8 +13,8 @@
13
13
  use perspective_client::clone;
14
14
  use yew::{Callback, Html, Properties, function_component, html};
15
15
 
16
- use crate::components::form::number_field::NumberField;
17
16
  use crate::config::ColumnConfigFieldUpdate;
17
+ use crate::ui::NumberField;
18
18
 
19
19
  // ░░░█▀█░█▀▄░█▀█░█▀█░█▀▀░█▀▄░▀█▀░▀█▀░█▀▀░█▀▀░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
20
20
  // ░░░█▀▀░█▀▄░█░█░█▀▀░█▀▀░█▀▄░░█░░░█░░█▀▀░▀▀█░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
@@ -0,0 +1,90 @@
1
+ // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
2
+ // ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
3
+ // ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
4
+ // ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
5
+ // ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
6
+ // ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
7
+ // ┃ Copyright (c) 2017, the Perspective Authors. ┃
8
+ // ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
9
+ // ┃ This file is part of the Perspective library, distributed under the terms ┃
10
+ // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11
+ // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
+
13
+ use serde_json::Value;
14
+ use web_sys::MouseEvent;
15
+ use yew::prelude::*;
16
+
17
+ use super::primitive_field::emit;
18
+ use crate::config::{Alignment, ColumnConfigFieldUpdate};
19
+ use crate::ui::OptionalField;
20
+
21
+ #[derive(Properties, PartialEq)]
22
+ pub struct AlignmentFieldProps {
23
+ pub field_key: String,
24
+ pub default: Option<Alignment>,
25
+ pub corners: bool,
26
+ pub current: Option<Alignment>,
27
+ pub on_change: Callback<ColumnConfigFieldUpdate>,
28
+ }
29
+
30
+ /// The `ControlSpec::Alignment` widget: a 3×3 grid of cells whose dot sits
31
+ /// where the cell's token anchors.
32
+ #[function_component]
33
+ pub fn AlignmentField(props: &AlignmentFieldProps) -> Html {
34
+ let selected = props.current.or(props.default);
35
+ let checked = props.current.is_some() && props.current != props.default;
36
+ let on_reset = use_callback(
37
+ (props.field_key.clone(), props.on_change.clone()),
38
+ |_: MouseEvent, (key, on_change)| emit(on_change, key, None),
39
+ );
40
+
41
+ let cells = Alignment::ALL.into_iter().map(|align| {
42
+ let onclick = {
43
+ let key = props.field_key.clone();
44
+ let default = props.default;
45
+ let on_change = props.on_change.clone();
46
+ Callback::from(move |_: MouseEvent| {
47
+ if Some(align) == default {
48
+ emit(&on_change, &key, None);
49
+ } else {
50
+ emit(
51
+ &on_change,
52
+ &key,
53
+ Some(Value::String(align.as_str().to_owned())),
54
+ );
55
+ }
56
+ })
57
+ };
58
+
59
+ let is_selected = selected == Some(align);
60
+ html! {
61
+ <button
62
+ type="button"
63
+ role="radio"
64
+ class={classes!(
65
+ "alignment-cell",
66
+ (is_selected && props.current.is_none()).then_some("is-default")
67
+ )}
68
+ data-align={align.as_str()}
69
+ aria-checked={is_selected.to_string()}
70
+ aria-label={align.humanized()}
71
+ disabled={props.corners && !align.is_corner()}
72
+ {onclick}
73
+ />
74
+ }
75
+ });
76
+
77
+ html! {
78
+ <div class="row">
79
+ <OptionalField label={props.field_key.clone()} on_check={on_reset} {checked}>
80
+ <div
81
+ class="alignment-grid"
82
+ role="radiogroup"
83
+ id={format!("{}-alignment", props.field_key)}
84
+ >
85
+ { for cells }
86
+ </div>
87
+ </OptionalField>
88
+ </div>
89
+ }
90
+ }
@@ -0,0 +1,267 @@
1
+ // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
2
+ // ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
3
+ // ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
4
+ // ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
5
+ // ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
6
+ // ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
7
+ // ┃ Copyright (c) 2017, the Perspective Authors. ┃
8
+ // ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
9
+ // ┃ This file is part of the Perspective library, distributed under the terms ┃
10
+ // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11
+ // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
+
13
+ use std::rc::Rc;
14
+
15
+ use serde_json::Value;
16
+ use web_sys::{HtmlInputElement, InputEvent, MouseEvent};
17
+ use yew::prelude::*;
18
+
19
+ use super::primitive_field::emit;
20
+ use crate::config::{ColumnConfigFieldUpdate, FontSize, FontToggle};
21
+ use crate::ui::{OptionalField, Select, SelectItem};
22
+ use crate::utils::{
23
+ LocalFontsAccess, font_family_options, local_fonts_access, on_local_fonts_changed,
24
+ probe_local_fonts_access, read_intl_string, request_local_fonts_access,
25
+ };
26
+
27
+ #[derive(Properties, PartialEq)]
28
+ pub struct FontFieldProps {
29
+ pub field_key: String,
30
+ pub default: String,
31
+ pub current: Option<String>,
32
+
33
+ /// The size input's key, default and bounds, when the spec declares
34
+ /// one, and the stored value for that key.
35
+ #[prop_or_default]
36
+ pub size: Option<FontSize>,
37
+
38
+ #[prop_or_default]
39
+ pub size_current: Option<f64>,
40
+
41
+ /// The bold toggle's key and default, when the spec declares one, and
42
+ /// the stored value for that key.
43
+ #[prop_or_default]
44
+ pub bold: Option<FontToggle>,
45
+
46
+ #[prop_or_default]
47
+ pub bold_current: Option<bool>,
48
+
49
+ #[prop_or_default]
50
+ pub italic: Option<FontToggle>,
51
+
52
+ #[prop_or_default]
53
+ pub italic_current: Option<bool>,
54
+ pub on_change: Callback<ColumnConfigFieldUpdate>,
55
+ }
56
+
57
+ /// The `ControlSpec::Font` widget: a family `<select>`, the optional size
58
+ /// input and bold / italic toggles, and a "Local Fonts" action while the
59
+ /// `local-fonts` permission is promptable.
60
+ #[function_component]
61
+ pub fn FontField(props: &FontFieldProps) -> Html {
62
+ let force_update = use_force_update();
63
+ use_effect_with((), move |_| {
64
+ let sub = on_local_fonts_changed(Callback::from(move |()| force_update.force_update()));
65
+ probe_local_fonts_access();
66
+ move || drop(sub)
67
+ });
68
+
69
+ let selected = props
70
+ .current
71
+ .clone()
72
+ .unwrap_or_else(|| props.default.clone());
73
+
74
+ let toggle_state = |toggle: &Option<FontToggle>, current: Option<bool>| {
75
+ toggle
76
+ .as_ref()
77
+ .map(|t| (t.clone(), current.unwrap_or(t.default)))
78
+ };
79
+
80
+ let bold = toggle_state(&props.bold, props.bold_current);
81
+ let italic = toggle_state(&props.italic, props.italic_current);
82
+ let family_checked = selected != props.default;
83
+ let style_checked = props
84
+ .size
85
+ .as_ref()
86
+ .is_some_and(|s| props.size_current.is_some_and(|v| v != s.default))
87
+ || bold.as_ref().is_some_and(|(t, on)| *on != t.default)
88
+ || italic.as_ref().is_some_and(|(t, on)| *on != t.default);
89
+
90
+ let values: Rc<Vec<SelectItem<String>>> = Rc::new(
91
+ font_family_options(&selected)
92
+ .into_iter()
93
+ .map(SelectItem::Option)
94
+ .collect(),
95
+ );
96
+
97
+ let on_select = use_callback(
98
+ (
99
+ props.field_key.clone(),
100
+ props.default.clone(),
101
+ props.on_change.clone(),
102
+ ),
103
+ |value: String, (key, default, on_change)| {
104
+ if value == *default {
105
+ emit(on_change, key, None);
106
+ } else {
107
+ emit(on_change, key, Some(Value::String(value)));
108
+ }
109
+ },
110
+ );
111
+
112
+ let on_reset_style = use_callback(
113
+ (
114
+ props.size.clone(),
115
+ props.bold.clone(),
116
+ props.italic.clone(),
117
+ props.field_key.clone(),
118
+ props.on_change.clone(),
119
+ ),
120
+ |_: MouseEvent, (size, bold, italic, key, on_change)| {
121
+ let keys = [
122
+ size.as_ref().map(|s| s.key.clone()),
123
+ bold.as_ref().map(|t| t.key.clone()),
124
+ italic.as_ref().map(|t| t.key.clone()),
125
+ Some(key.clone()),
126
+ ]
127
+ .into_iter()
128
+ .flatten()
129
+ .collect();
130
+
131
+ on_change.emit(ColumnConfigFieldUpdate {
132
+ keys,
133
+ value: serde_json::Map::new(),
134
+ });
135
+ },
136
+ );
137
+
138
+ let size_input = props.size.as_ref().map(|size| {
139
+ let on_input = {
140
+ let on_change = props.on_change.clone();
141
+ let size = size.clone();
142
+ Callback::from(move |event: InputEvent| {
143
+ let value = event
144
+ .target_unchecked_into::<HtmlInputElement>()
145
+ .value_as_number();
146
+ let value = (value.is_finite() && value != size.default)
147
+ .then(|| serde_json::Number::from_f64(value).map(Value::Number))
148
+ .flatten();
149
+
150
+ emit(&on_change, &size.key, value);
151
+ })
152
+ };
153
+
154
+ html! {
155
+ <input
156
+ type="number"
157
+ class="parameter font-size"
158
+ id={format!("{}-input", size.key)}
159
+ min={size.min.map(|v| v.to_string())}
160
+ max={size.max.map(|v| v.to_string())}
161
+ step={size.step.map(|v| v.to_string())}
162
+ value={props.size_current.unwrap_or(size.default).to_string()}
163
+ oninput={on_input}
164
+ />
165
+ }
166
+ });
167
+
168
+ let toggle_button = |class: &'static str, state: &Option<(FontToggle, bool)>| {
169
+ let Some((toggle, on)) = state else {
170
+ return html! {};
171
+ };
172
+
173
+ let next = !*on;
174
+ let on_toggle = {
175
+ let on_change = props.on_change.clone();
176
+ let toggle = toggle.clone();
177
+ Callback::from(move |_: MouseEvent| {
178
+ let value = (next != toggle.default).then_some(Value::Bool(next));
179
+ emit(&on_change, &toggle.key, value);
180
+ })
181
+ };
182
+
183
+ html! {
184
+ <span
185
+ class={classes!("font-style-toggle", class)}
186
+ id={format!("{}-toggle", toggle.key)}
187
+ role="button"
188
+ aria-pressed={on.to_string()}
189
+ onclick={on_toggle}
190
+ />
191
+ }
192
+ };
193
+
194
+ let access = local_fonts_access();
195
+ let action_ref = use_node_ref();
196
+ use_effect_with((action_ref.clone(), access), |(action_ref, access)| {
197
+ let Some(elem) = action_ref.cast::<web_sys::Element>() else {
198
+ return;
199
+ };
200
+
201
+ let (slug, fallback) = match access {
202
+ Some(LocalFontsAccess::Denied) => (
203
+ "local-fonts-blocked-title",
204
+ "Access to local fonts is blocked for this site. Allow it in the browser's site \
205
+ settings to list them here.",
206
+ ),
207
+ _ => (
208
+ "request-local-fonts-title",
209
+ "Allow access to the fonts installed on this device",
210
+ ),
211
+ };
212
+
213
+ let title = read_intl_string(&elem, slug).unwrap_or_else(|| fallback.to_owned());
214
+ let _ = elem.set_attribute("title", &title);
215
+ });
216
+
217
+ let local_fonts = match access {
218
+ Some(LocalFontsAccess::Prompt) => {
219
+ let on_request = Callback::from(|_: MouseEvent| request_local_fonts_access());
220
+ html! {
221
+ <div class="local-fonts-controls">
222
+ <span
223
+ ref={action_ref}
224
+ class="local-fonts-request"
225
+ id="request-local-fonts"
226
+ onclick={on_request}
227
+ />
228
+ </div>
229
+ }
230
+ },
231
+ Some(LocalFontsAccess::Denied) => html! {
232
+ <div class="local-fonts-controls">
233
+ <span
234
+ ref={action_ref}
235
+ class="local-fonts-request blocked"
236
+ id="request-local-fonts"
237
+ />
238
+ </div>
239
+ },
240
+ _ => html! {},
241
+ };
242
+
243
+ html! {
244
+ <>
245
+ <div class="row">
246
+ { local_fonts }
247
+ <OptionalField
248
+ label={props.field_key.clone()}
249
+ on_check={on_reset_style}
250
+ checked={family_checked || style_checked}
251
+ >
252
+ <div class="font-group">
253
+ <Select<String>
254
+ wrapper_class="font-group-family"
255
+ {values}
256
+ {selected}
257
+ {on_select}
258
+ />
259
+ { size_input }
260
+ { toggle_button("bold", &bold) }
261
+ { toggle_button("italic", &italic) }
262
+ </div>
263
+ </OptionalField>
264
+ </div>
265
+ </>
266
+ }
267
+ }
@@ -26,20 +26,17 @@ use wasm_bindgen::JsCast;
26
26
  use web_sys::{HtmlInputElement, MouseEvent};
27
27
  use yew::{Callback, Html, Properties, function_component, html, use_callback};
28
28
 
29
- use crate::components::containers::select::{Select, SelectItem};
30
- use crate::components::form::color_selector::ColorSelector;
31
- use crate::components::form::multi_stop_gradient_selector::MultiStopGradientSelector;
32
- use crate::components::form::named_value_picker::NamedValuePicker;
33
- use crate::components::form::number_field::NumberField;
34
- use crate::components::form::optional_field::OptionalField;
35
- use crate::components::form::palette_selector::PaletteSelector;
36
- use crate::config::{
37
- ColumnConfigFieldUpdate, CssColor, CssGradient, CssKind, CssPalette, EnumVariant,
38
- GradientStopSpec, NamedValue, canonicalize_css_color, discrete_pair, gradient_to_css,
39
- palette_name_for,
29
+ use crate::config::{ColumnConfigFieldUpdate, EnumVariant, discrete_pair};
30
+ use crate::ui::{
31
+ ColorSelector, MultiStopGradientSelector, NamedValuePicker, NumberField, OptionalField,
32
+ PaletteSelector, Select, SelectItem,
33
+ };
34
+ use crate::utils::{
35
+ CssColor, CssGradient, CssKind, CssPalette, GradientStopSpec, NamedValue,
36
+ canonicalize_css_color, gradient_to_css, palette_name_for,
40
37
  };
41
38
 
42
- fn emit(on_change: &Callback<ColumnConfigFieldUpdate>, key: &str, value: Option<Value>) {
39
+ pub(super) fn emit(on_change: &Callback<ColumnConfigFieldUpdate>, key: &str, value: Option<Value>) {
43
40
  let mut map = serde_json::Map::new();
44
41
  if let Some(v) = value {
45
42
  map.insert(key.to_owned(), v);
@@ -150,9 +147,10 @@ pub fn BoolField(props: &BoolFieldProps) -> Html {
150
147
  checked={current}
151
148
  {oninput}
152
149
  />
153
- <label for={format!("{}-checkbox", props.field_key)} class="bool-field-desc">
154
- { if current { "Enabled" } else { "Disabled" } }
155
- </label>
150
+ <label
151
+ for={format!("{}-checkbox", props.field_key)}
152
+ class={if current { "bool-field-desc enabled" } else { "bool-field-desc disabled" }}
153
+ />
156
154
  </div>
157
155
  </OptionalField>
158
156
  </div>
@@ -346,7 +344,7 @@ pub fn PaletteField(props: &PaletteFieldProps) -> Html {
346
344
  on_change={on_change_palette}
347
345
  {on_reset}
348
346
  {is_modified}
349
- title={Some(format!("{}-label", props.field_key))}
347
+ label={Some(props.field_key.clone())}
350
348
  />
351
349
  </div>
352
350
  }
@@ -459,7 +457,7 @@ pub fn GradientStopsField(props: &GradientStopsFieldProps) -> Html {
459
457
  on_change={on_change_stops}
460
458
  {on_reset}
461
459
  {is_modified}
462
- title={Some(format!("{}-label", props.field_key))}
460
+ label={Some(props.field_key.clone())}
463
461
  />
464
462
  </div>
465
463
  }
@@ -555,7 +553,7 @@ pub fn ColorField(props: &ColorFieldProps) -> Html {
555
553
  {on_color}
556
554
  {on_reset}
557
555
  {is_modified}
558
- title={Some(format!("{}-label", props.field_key))}
556
+ label={Some(props.field_key.clone())}
559
557
  />
560
558
  </div>
561
559
  }
@@ -13,7 +13,7 @@
13
13
  use itertools::Itertools;
14
14
  use yew::{Callback, Html, Properties, function_component, html};
15
15
 
16
- use crate::components::containers::select::{Select, SelectItem};
16
+ use crate::ui::{Select, SelectItem};
17
17
  use crate::utils::PtrEqRc;
18
18
 
19
19
  #[derive(Properties, PartialEq)]