@perspective-dev/viewer 4.0.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 (394) hide show
  1. package/LICENSE.md +193 -0
  2. package/README.md +4 -0
  3. package/dist/cdn/perspective-viewer.js +3 -0
  4. package/dist/cdn/perspective-viewer.js.map +7 -0
  5. package/dist/css/dracula.css +1 -0
  6. package/dist/css/gruvbox-dark.css +1 -0
  7. package/dist/css/gruvbox.css +1 -0
  8. package/dist/css/icons.css +1 -0
  9. package/dist/css/intl/de.css +1 -0
  10. package/dist/css/intl/es.css +1 -0
  11. package/dist/css/intl/fr.css +1 -0
  12. package/dist/css/intl/ja.css +1 -0
  13. package/dist/css/intl/pt.css +1 -0
  14. package/dist/css/intl/zh.css +1 -0
  15. package/dist/css/intl.css +1 -0
  16. package/dist/css/monokai.css +1 -0
  17. package/dist/css/pro-dark.css +1 -0
  18. package/dist/css/pro.css +1 -0
  19. package/dist/css/solarized-dark.css +1 -0
  20. package/dist/css/solarized.css +1 -0
  21. package/dist/css/themes.css +1 -0
  22. package/dist/css/vaporwave.css +1 -0
  23. package/dist/css/variables.css +0 -0
  24. package/dist/esm/bootstrap.d.ts +1 -0
  25. package/dist/esm/extensions.d.ts +70 -0
  26. package/dist/esm/perspective-viewer.d.ts +39 -0
  27. package/dist/esm/perspective-viewer.inline.js +3 -0
  28. package/dist/esm/perspective-viewer.inline.js.map +7 -0
  29. package/dist/esm/perspective-viewer.js +3 -0
  30. package/dist/esm/perspective-viewer.js.map +7 -0
  31. package/dist/esm/plugin.d.ts +195 -0
  32. package/dist/esm/ts-rs/Aggregate.d.ts +1 -0
  33. package/dist/esm/ts-rs/ColumnConfigValues.d.ts +31 -0
  34. package/dist/esm/ts-rs/CustomDatetimeFormat.d.ts +1 -0
  35. package/dist/esm/ts-rs/CustomDatetimeStyleConfig.d.ts +15 -0
  36. package/dist/esm/ts-rs/CustomNumberFormatConfig.d.ts +18 -0
  37. package/dist/esm/ts-rs/DatetimeColorMode.d.ts +1 -0
  38. package/dist/esm/ts-rs/DatetimeFormatType.d.ts +6 -0
  39. package/dist/esm/ts-rs/Expressions.d.ts +3 -0
  40. package/dist/esm/ts-rs/Filter.d.ts +2 -0
  41. package/dist/esm/ts-rs/FilterReducer.d.ts +1 -0
  42. package/dist/esm/ts-rs/FilterTerm.d.ts +2 -0
  43. package/dist/esm/ts-rs/FormatMode.d.ts +1 -0
  44. package/dist/esm/ts-rs/FormatUnit.d.ts +1 -0
  45. package/dist/esm/ts-rs/NumberBackgroundMode.d.ts +1 -0
  46. package/dist/esm/ts-rs/NumberForegroundMode.d.ts +1 -0
  47. package/dist/esm/ts-rs/OptionalUpdate.d.ts +1 -0
  48. package/dist/esm/ts-rs/PluginConfig.d.ts +2 -0
  49. package/dist/esm/ts-rs/RoundingMode.d.ts +1 -0
  50. package/dist/esm/ts-rs/RoundingPriority.d.ts +1 -0
  51. package/dist/esm/ts-rs/Scalar.d.ts +5 -0
  52. package/dist/esm/ts-rs/SignDisplay.d.ts +1 -0
  53. package/dist/esm/ts-rs/SimpleDatetimeFormat.d.ts +1 -0
  54. package/dist/esm/ts-rs/SimpleDatetimeStyleConfig.d.ts +6 -0
  55. package/dist/esm/ts-rs/Sort.d.ts +2 -0
  56. package/dist/esm/ts-rs/SortDir.d.ts +1 -0
  57. package/dist/esm/ts-rs/StringColorMode.d.ts +1 -0
  58. package/dist/esm/ts-rs/TrailingZeroDisplay.d.ts +1 -0
  59. package/dist/esm/ts-rs/UseGrouping.d.ts +1 -0
  60. package/dist/esm/ts-rs/ViewerConfigUpdate.d.ts +102 -0
  61. package/dist/esm/ts-rs/serde_json/JsonValue.d.ts +3 -0
  62. package/dist/wasm/perspective-viewer.d.ts +1291 -0
  63. package/dist/wasm/perspective-viewer.js +3883 -0
  64. package/dist/wasm/perspective-viewer.wasm +0 -0
  65. package/dist/wasm/perspective-viewer.wasm.d.ts +142 -0
  66. package/dist/wasm/snippets/perspective-viewer-c69283f6f62a5f14/inline0.js +1 -0
  67. package/dist/wasm/snippets/perspective-viewer-c69283f6f62a5f14/inline1.js +1 -0
  68. package/dist/wasm/snippets/perspective-viewer-c69283f6f62a5f14/inline2.js +1 -0
  69. package/dist/wasm/snippets/perspective-viewer-c69283f6f62a5f14/inline3.js +44 -0
  70. package/dist/wasm/snippets/perspective-viewer-c69283f6f62a5f14/inline4.js +1 -0
  71. package/package.json +55 -0
  72. package/src/less/aggregate-selector.less +72 -0
  73. package/src/less/column-dropdown.less +95 -0
  74. package/src/less/column-selector.less +551 -0
  75. package/src/less/column-settings-panel.less +255 -0
  76. package/src/less/column-style.less +319 -0
  77. package/src/less/column-symbol-attributes.less +73 -0
  78. package/src/less/config-selector.less +363 -0
  79. package/src/less/containers/dropdown-menu.less +94 -0
  80. package/src/less/containers/pairs-list.less +46 -0
  81. package/src/less/containers/radio-list.less +29 -0
  82. package/src/less/containers/scroll-panel.less +27 -0
  83. package/src/less/containers/split-panel.less +80 -0
  84. package/src/less/containers/tabs.less +90 -0
  85. package/src/less/dom/checkbox.less +100 -0
  86. package/src/less/dom/scrollbar.less +35 -0
  87. package/src/less/dom/select.less +63 -0
  88. package/src/less/empty-column.less +72 -0
  89. package/src/less/expression-editor.less +152 -0
  90. package/src/less/filter-dropdown.less +53 -0
  91. package/src/less/filter-item.less +126 -0
  92. package/src/less/form/code-editor.less +137 -0
  93. package/src/less/form/debug.less +88 -0
  94. package/src/less/function-dropdown.less +45 -0
  95. package/src/less/plugin-selector.less +173 -0
  96. package/src/less/render-warning.less +81 -0
  97. package/src/less/status-bar.less +551 -0
  98. package/src/less/type-icon.less +68 -0
  99. package/src/less/viewer.less +368 -0
  100. package/src/rust/components/column_dropdown.rs +162 -0
  101. package/src/rust/components/column_selector/active_column.rs +462 -0
  102. package/src/rust/components/column_selector/add_expression_button.rs +59 -0
  103. package/src/rust/components/column_selector/aggregate_selector.rs +186 -0
  104. package/src/rust/components/column_selector/config_selector.rs +674 -0
  105. package/src/rust/components/column_selector/empty_column.rs +134 -0
  106. package/src/rust/components/column_selector/expression_toolbar.rs +45 -0
  107. package/src/rust/components/column_selector/filter_column.rs +530 -0
  108. package/src/rust/components/column_selector/inactive_column.rs +221 -0
  109. package/src/rust/components/column_selector/invalid_column.rs +37 -0
  110. package/src/rust/components/column_selector/pivot_column.rs +93 -0
  111. package/src/rust/components/column_selector/sort_column.rs +135 -0
  112. package/src/rust/components/column_selector.rs +426 -0
  113. package/src/rust/components/column_settings_sidebar/attributes_tab.rs +34 -0
  114. package/src/rust/components/column_settings_sidebar/mod.rs +17 -0
  115. package/src/rust/components/column_settings_sidebar/save_settings.rs +68 -0
  116. package/src/rust/components/column_settings_sidebar/sidebar.rs +396 -0
  117. package/src/rust/components/column_settings_sidebar/style_tab/column_style.rs +177 -0
  118. package/src/rust/components/column_settings_sidebar/style_tab/stub.rs +34 -0
  119. package/src/rust/components/column_settings_sidebar/style_tab/symbol/row_selector.rs +101 -0
  120. package/src/rust/components/column_settings_sidebar/style_tab/symbol/symbol_config.rs +24 -0
  121. package/src/rust/components/column_settings_sidebar/style_tab/symbol/symbol_pairs.rs +185 -0
  122. package/src/rust/components/column_settings_sidebar/style_tab/symbol/symbol_selector.rs +55 -0
  123. package/src/rust/components/column_settings_sidebar/style_tab/symbol.rs +131 -0
  124. package/src/rust/components/column_settings_sidebar/style_tab.rs +231 -0
  125. package/src/rust/components/containers/dragdrop_list.rs +325 -0
  126. package/src/rust/components/containers/dropdown_menu.rs +104 -0
  127. package/src/rust/components/containers/kvpair.rs +47 -0
  128. package/src/rust/components/containers/mod.rs +28 -0
  129. package/src/rust/components/containers/scroll_panel.rs +311 -0
  130. package/src/rust/components/containers/scroll_panel_item.rs +111 -0
  131. package/src/rust/components/containers/select.rs +269 -0
  132. package/src/rust/components/containers/sidebar.rs +89 -0
  133. package/src/rust/components/containers/split_panel.rs +483 -0
  134. package/src/rust/components/containers/tab_list.rs +104 -0
  135. package/src/rust/components/containers/tests/mod.rs +11 -0
  136. package/src/rust/components/containers/tests/split_panel.rs +91 -0
  137. package/src/rust/components/containers/trap_door_panel.rs +50 -0
  138. package/src/rust/components/copy_dropdown.rs +136 -0
  139. package/src/rust/components/datetime_column_style/custom.rs +222 -0
  140. package/src/rust/components/datetime_column_style/simple.rs +122 -0
  141. package/src/rust/components/datetime_column_style.rs +284 -0
  142. package/src/rust/components/editable_header.rs +183 -0
  143. package/src/rust/components/empty_row.rs +169 -0
  144. package/src/rust/components/error_message.rs +56 -0
  145. package/src/rust/components/export_dropdown.rs +153 -0
  146. package/src/rust/components/expression_editor.rs +153 -0
  147. package/src/rust/components/filter_dropdown.rs +144 -0
  148. package/src/rust/components/font_loader.rs +254 -0
  149. package/src/rust/components/form/code_editor.rs +286 -0
  150. package/src/rust/components/form/color_range_selector.rs +115 -0
  151. package/src/rust/components/form/color_selector.rs +54 -0
  152. package/src/rust/components/form/debug.rs +253 -0
  153. package/src/rust/components/form/highlight.rs +50 -0
  154. package/src/rust/components/form/mod.rs +29 -0
  155. package/src/rust/components/form/number_field.rs +72 -0
  156. package/src/rust/components/form/number_input.rs +45 -0
  157. package/src/rust/components/form/number_range_field.rs +77 -0
  158. package/src/rust/components/form/optional_field.rs +53 -0
  159. package/src/rust/components/form/select_field.rs +104 -0
  160. package/src/rust/components/function_dropdown.rs +151 -0
  161. package/src/rust/components/mod.rs +45 -0
  162. package/src/rust/components/modal.rs +171 -0
  163. package/src/rust/components/number_column_style.rs +522 -0
  164. package/src/rust/components/plugin_selector.rs +196 -0
  165. package/src/rust/components/render_warning.rs +178 -0
  166. package/src/rust/components/status_bar.rs +315 -0
  167. package/src/rust/components/status_bar_counter.rs +229 -0
  168. package/src/rust/components/status_indicator.rs +164 -0
  169. package/src/rust/components/string_column_style.rs +186 -0
  170. package/src/rust/components/style/local_style.rs +35 -0
  171. package/src/rust/components/style/mod.rs +52 -0
  172. package/src/rust/components/style/style_cache.rs +132 -0
  173. package/src/rust/components/style/style_provider.rs +65 -0
  174. package/src/rust/components/style_controls/number_string_format/digits_section.rs +140 -0
  175. package/src/rust/components/style_controls/number_string_format/misc_section.rs +56 -0
  176. package/src/rust/components/style_controls/number_string_format/style_section.rs +69 -0
  177. package/src/rust/components/style_controls/number_string_format/types.rs +31 -0
  178. package/src/rust/components/style_controls/number_string_format.rs +259 -0
  179. package/src/rust/components/style_controls.rs +15 -0
  180. package/src/rust/components/type_icon.rs +53 -0
  181. package/src/rust/components/viewer.rs +668 -0
  182. package/src/rust/config/columns_config.rs +132 -0
  183. package/src/rust/config/datetime_column_style/color_mode.rs +62 -0
  184. package/src/rust/config/datetime_column_style/custom.rs +121 -0
  185. package/src/rust/config/datetime_column_style/custom_format.rs +90 -0
  186. package/src/rust/config/datetime_column_style/simple.rs +55 -0
  187. package/src/rust/config/datetime_column_style/simple_format.rs +86 -0
  188. package/src/rust/config/datetime_column_style.rs +104 -0
  189. package/src/rust/config/mod.rs +30 -0
  190. package/src/rust/config/number_column_style.rs +138 -0
  191. package/src/rust/config/number_string_format/enums.rs +298 -0
  192. package/src/rust/config/number_string_format.rs +269 -0
  193. package/src/rust/config/string_column_style.rs +140 -0
  194. package/src/rust/config/view_config.rs +24 -0
  195. package/src/rust/config/viewer_config.rs +337 -0
  196. package/src/rust/custom_elements/column_dropdown.rs +123 -0
  197. package/src/rust/custom_elements/copy_dropdown.rs +112 -0
  198. package/src/rust/custom_elements/debug_plugin.rs +114 -0
  199. package/src/rust/custom_elements/export_dropdown.rs +112 -0
  200. package/src/rust/custom_elements/filter_dropdown.rs +179 -0
  201. package/src/rust/custom_elements/function_dropdown.rs +115 -0
  202. package/src/rust/custom_elements/mod.rs +24 -0
  203. package/src/rust/custom_elements/modal.rs +395 -0
  204. package/src/rust/custom_elements/viewer.rs +880 -0
  205. package/src/rust/custom_events.rs +214 -0
  206. package/src/rust/dragdrop.rs +362 -0
  207. package/src/rust/exprtk/cursor.rs +81 -0
  208. package/src/rust/exprtk/mod.rs +17 -0
  209. package/src/rust/exprtk/tokenize/comment.rs +21 -0
  210. package/src/rust/exprtk/tokenize/number.rs +44 -0
  211. package/src/rust/exprtk/tokenize/string.rs +98 -0
  212. package/src/rust/exprtk/tokenize/symbol.rs +26 -0
  213. package/src/rust/exprtk/tokenize.rs +190 -0
  214. package/src/rust/js/clipboard.rs +77 -0
  215. package/src/rust/js/clipboard_item.rs +21 -0
  216. package/src/rust/js/intersection_observer.rs +32 -0
  217. package/src/rust/js/mimetype.rs +42 -0
  218. package/src/rust/js/mod.rs +29 -0
  219. package/src/rust/js/plugin.rs +167 -0
  220. package/src/rust/js/resize_observer.rs +37 -0
  221. package/src/rust/js/testing.rs +149 -0
  222. package/src/rust/lib.rs +116 -0
  223. package/src/rust/model/columns_iter_set.rs +340 -0
  224. package/src/rust/model/copy_export.rs +157 -0
  225. package/src/rust/model/create_col.rs +59 -0
  226. package/src/rust/model/edit_expression.rs +95 -0
  227. package/src/rust/model/export_app.rs +58 -0
  228. package/src/rust/model/export_method.rs +151 -0
  229. package/src/rust/model/get_viewer_config.rs +85 -0
  230. package/src/rust/model/intersection_observer.rs +81 -0
  231. package/src/rust/model/is_invalid_drop.rs +36 -0
  232. package/src/rust/model/mod.rs +93 -0
  233. package/src/rust/model/plugin_column_styles.rs +106 -0
  234. package/src/rust/model/resize_observer.rs +98 -0
  235. package/src/rust/model/restore_and_render.rs +113 -0
  236. package/src/rust/model/structural.rs +73 -0
  237. package/src/rust/model/update_and_render.rs +74 -0
  238. package/src/rust/presentation.rs +370 -0
  239. package/src/rust/renderer/activate.rs +49 -0
  240. package/src/rust/renderer/limits.rs +200 -0
  241. package/src/rust/renderer/plugin_store.rs +45 -0
  242. package/src/rust/renderer/registry.rs +135 -0
  243. package/src/rust/renderer/render_timer.rs +185 -0
  244. package/src/rust/renderer.rs +463 -0
  245. package/src/rust/session/column_defaults_update.rs +141 -0
  246. package/src/rust/session/drag_drop_update.rs +179 -0
  247. package/src/rust/session/metadata.rs +308 -0
  248. package/src/rust/session/replace_expression_update.rs +131 -0
  249. package/src/rust/session/view_subscription.rs +189 -0
  250. package/src/rust/session.rs +794 -0
  251. package/src/rust/utils/browser/blob.rs +49 -0
  252. package/src/rust/utils/browser/download.rs +26 -0
  253. package/src/rust/utils/browser/mod.rs +24 -0
  254. package/src/rust/utils/browser/request_animation_frame.rs +76 -0
  255. package/src/rust/utils/browser/selection.rs +79 -0
  256. package/src/rust/utils/browser/tests/debounce.rs +114 -0
  257. package/src/rust/utils/browser/tests/mod.rs +13 -0
  258. package/src/rust/utils/custom_element.rs +85 -0
  259. package/src/rust/utils/datetime.rs +49 -0
  260. package/src/rust/utils/debounce.rs +54 -0
  261. package/src/rust/utils/hooks/mod.rs +15 -0
  262. package/src/rust/utils/hooks/use_async_callback.rs +53 -0
  263. package/src/rust/utils/mod.rs +114 -0
  264. package/src/rust/utils/number_format.rs +48 -0
  265. package/src/rust/utils/pubsub.rs +222 -0
  266. package/src/rust/utils/scope.rs +54 -0
  267. package/src/rust/utils/tee.rs +88 -0
  268. package/src/rust/utils/tests/mod.rs +14 -0
  269. package/src/rust/utils/tests/pubsub.rs +95 -0
  270. package/src/rust/utils/tests/request_animation_frame.rs +42 -0
  271. package/src/rust/utils/wasm_abi.rs +61 -0
  272. package/src/rust/utils/weak_scope.rs +39 -0
  273. package/src/svg/align-scroll-icon.svg +7 -0
  274. package/src/svg/bg-pattern.png +0 -0
  275. package/src/svg/boolean-type.svg +3 -0
  276. package/src/svg/checkbox-checked-icon.svg +7 -0
  277. package/src/svg/checkbox-hover.svg +4 -0
  278. package/src/svg/checkbox-off.svg +4 -0
  279. package/src/svg/checkbox-on.svg +4 -0
  280. package/src/svg/checkbox-unchecked-icon.svg +3 -0
  281. package/src/svg/close-icon.svg +6 -0
  282. package/src/svg/column-settings-icon.svg +6 -0
  283. package/src/svg/datagrid-select-column.svg +15 -0
  284. package/src/svg/datagrid-select-region.svg +15 -0
  285. package/src/svg/datagrid-select-row.svg +13 -0
  286. package/src/svg/datagrid.svg +13 -0
  287. package/src/svg/date-type.svg +23 -0
  288. package/src/svg/downloading.gif +0 -0
  289. package/src/svg/drag-handle.svg +10 -0
  290. package/src/svg/drawer-bg-pattern-hidpi.png +0 -0
  291. package/src/svg/drawer-tab-hover.svg +8 -0
  292. package/src/svg/drawer-tab-invert-hover.svg +9 -0
  293. package/src/svg/drawer-tab-invert.svg +8 -0
  294. package/src/svg/drawer-tab.svg +7 -0
  295. package/src/svg/dropdown-selector-light.svg +3 -0
  296. package/src/svg/dropdown-selector.svg +3 -0
  297. package/src/svg/duplicate-icon.svg +4 -0
  298. package/src/svg/editable-icon.svg +5 -0
  299. package/src/svg/export-icon.svg +4 -0
  300. package/src/svg/expression.svg +10 -0
  301. package/src/svg/free-scroll-icon.svg +5 -0
  302. package/src/svg/inactive-mouseover.svg +4 -0
  303. package/src/svg/mega-menu-icons-candlestick.svg +38 -0
  304. package/src/svg/mega-menu-icons-datagrid.svg +38 -0
  305. package/src/svg/mega-menu-icons-heatmap.svg +60 -0
  306. package/src/svg/mega-menu-icons-map-scatter.svg +43 -0
  307. package/src/svg/mega-menu-icons-ohlc.svg +40 -0
  308. package/src/svg/mega-menu-icons-sunburst.svg +39 -0
  309. package/src/svg/mega-menu-icons-treemap.svg +44 -0
  310. package/src/svg/mega-menu-icons-x-bar.svg +36 -0
  311. package/src/svg/mega-menu-icons-x-y-line.svg +38 -0
  312. package/src/svg/mega-menu-icons-x-y-scatter.svg +72 -0
  313. package/src/svg/mega-menu-icons-y-area.svg +73 -0
  314. package/src/svg/mega-menu-icons-y-bar.svg +36 -0
  315. package/src/svg/mega-menu-icons-y-line.svg +36 -0
  316. package/src/svg/mega-menu-icons-y-scatter.svg +72 -0
  317. package/src/svg/number-type.svg +4 -0
  318. package/src/svg/radio-hover.svg +4 -0
  319. package/src/svg/radio-off.svg +3 -0
  320. package/src/svg/radio-on.svg +4 -0
  321. package/src/svg/read-only-icon.svg +6 -0
  322. package/src/svg/revert-icon.svg +4 -0
  323. package/src/svg/sort-abs-asc-icon.svg +3 -0
  324. package/src/svg/sort-abs-col-asc-icon.svg +3 -0
  325. package/src/svg/sort-abs-col-desc-icon.svg +3 -0
  326. package/src/svg/sort-abs-desc-icon.svg +3 -0
  327. package/src/svg/sort-asc-icon.svg +4 -0
  328. package/src/svg/sort-col-asc-icon.svg +4 -0
  329. package/src/svg/sort-col-desc-icon.svg +4 -0
  330. package/src/svg/sort-desc-icon.svg +4 -0
  331. package/src/svg/sort-none-icon.svg +3 -0
  332. package/src/svg/status_error.svg +70 -0
  333. package/src/svg/status_ok.svg +26 -0
  334. package/src/svg/string-type.svg +8 -0
  335. package/src/svg/theme-icon.svg +5 -0
  336. package/src/svg/updating.gif +0 -0
  337. package/src/themes/dracula.less +101 -0
  338. package/src/themes/gruvbox-dark.less +116 -0
  339. package/src/themes/gruvbox.less +152 -0
  340. package/src/themes/icons.less +130 -0
  341. package/src/themes/intl/de.less +102 -0
  342. package/src/themes/intl/es.less +102 -0
  343. package/src/themes/intl/fr.less +102 -0
  344. package/src/themes/intl/ja.less +102 -0
  345. package/src/themes/intl/pt.less +102 -0
  346. package/src/themes/intl/zh.less +102 -0
  347. package/src/themes/intl.less +102 -0
  348. package/src/themes/monokai.less +107 -0
  349. package/src/themes/pro-dark.less +147 -0
  350. package/src/themes/pro.less +186 -0
  351. package/src/themes/solarized-dark.less +78 -0
  352. package/src/themes/solarized.less +102 -0
  353. package/src/themes/themes.less +21 -0
  354. package/src/themes/vaporwave.less +145 -0
  355. package/src/themes/variables.less +24 -0
  356. package/src/ts/bootstrap.ts +26 -0
  357. package/src/ts/extensions.ts +124 -0
  358. package/src/ts/perspective-viewer.cdn.ts +19 -0
  359. package/src/ts/perspective-viewer.inline.ts +23 -0
  360. package/src/ts/perspective-viewer.ts +59 -0
  361. package/src/ts/plugin.ts +279 -0
  362. package/src/ts/ts-rs/Aggregate.ts +3 -0
  363. package/src/ts/ts-rs/ColumnConfigValues.ts +14 -0
  364. package/src/ts/ts-rs/CustomDatetimeFormat.ts +3 -0
  365. package/src/ts/ts-rs/CustomDatetimeStyleConfig.ts +5 -0
  366. package/src/ts/ts-rs/CustomNumberFormatConfig.ts +8 -0
  367. package/src/ts/ts-rs/DatetimeColorMode.ts +3 -0
  368. package/src/ts/ts-rs/DatetimeFormatType.ts +8 -0
  369. package/src/ts/ts-rs/Expressions.ts +3 -0
  370. package/src/ts/ts-rs/Filter.ts +4 -0
  371. package/src/ts/ts-rs/FilterReducer.ts +3 -0
  372. package/src/ts/ts-rs/FilterTerm.ts +4 -0
  373. package/src/ts/ts-rs/FormatMode.ts +3 -0
  374. package/src/ts/ts-rs/FormatUnit.ts +3 -0
  375. package/src/ts/ts-rs/NumberBackgroundMode.ts +3 -0
  376. package/src/ts/ts-rs/NumberForegroundMode.ts +3 -0
  377. package/src/ts/ts-rs/OnUpdateData.ts +8 -0
  378. package/src/ts/ts-rs/OptionalUpdate.ts +3 -0
  379. package/src/ts/ts-rs/PluginConfig.ts +4 -0
  380. package/src/ts/ts-rs/RoundingMode.ts +3 -0
  381. package/src/ts/ts-rs/RoundingPriority.ts +3 -0
  382. package/src/ts/ts-rs/Scalar.ts +7 -0
  383. package/src/ts/ts-rs/SignDisplay.ts +3 -0
  384. package/src/ts/ts-rs/SimpleDatetimeFormat.ts +3 -0
  385. package/src/ts/ts-rs/SimpleDatetimeStyleConfig.ts +4 -0
  386. package/src/ts/ts-rs/Sort.ts +4 -0
  387. package/src/ts/ts-rs/SortDir.ts +3 -0
  388. package/src/ts/ts-rs/StringColorMode.ts +3 -0
  389. package/src/ts/ts-rs/TrailingZeroDisplay.ts +3 -0
  390. package/src/ts/ts-rs/UseGrouping.ts +3 -0
  391. package/src/ts/ts-rs/ViewOnUpdateResp.ts +3 -0
  392. package/src/ts/ts-rs/ViewerConfigUpdate.ts +90 -0
  393. package/src/ts/ts-rs/serde_json/JsonValue.ts +3 -0
  394. package/tsconfig.json +15 -0
@@ -0,0 +1,254 @@
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::cell::{Cell, Ref, RefCell};
14
+ use std::future::Future;
15
+ use std::iter::{Iterator, repeat_with};
16
+ use std::rc::Rc;
17
+
18
+ use futures::future::{join_all, select_all};
19
+ use perspective_js::utils::{global, *};
20
+ use wasm_bindgen::JsCast;
21
+ use wasm_bindgen::prelude::*;
22
+ use wasm_bindgen_futures::JsFuture;
23
+ use yew::prelude::*;
24
+
25
+ use crate::utils::*;
26
+
27
+ const FONT_DOWNLOAD_TIMEOUT_MS: i32 = 1000;
28
+
29
+ /// This test string is injected into the DOM with the target `font-family`
30
+ /// applied. It is important for this string to contain the correct unicode
31
+ /// range, as otherwise the browser may download the latin-only variant of the
32
+ /// font which will later be invalidated.
33
+ const FONT_TEST_SAMPLE: &str = "ABCDΔ";
34
+
35
+ /// `state` is private to force construction of props with the `::new()` static
36
+ /// method, which initializes the async `load_fonts_task()` method.
37
+ #[derive(Clone, Properties)]
38
+ pub struct FontLoaderProps {
39
+ state: Rc<FontLoaderState>,
40
+ }
41
+
42
+ impl PartialEq for FontLoaderProps {
43
+ fn eq(&self, _rhs: &Self) -> bool {
44
+ false
45
+ }
46
+ }
47
+
48
+ /// The `FontLoader` component ensures that fonts are loaded before they are
49
+ /// visible.
50
+ pub struct FontLoader {}
51
+
52
+ impl Component for FontLoader {
53
+ type Message = ();
54
+ type Properties = FontLoaderProps;
55
+
56
+ fn create(_ctx: &Context<Self>) -> Self {
57
+ Self {}
58
+ }
59
+
60
+ fn update(&mut self, _ctx: &Context<Self>, _msg: ()) -> bool {
61
+ false
62
+ }
63
+
64
+ fn view(&self, ctx: &Context<Self>) -> yew::virtual_dom::VNode {
65
+ if matches!(ctx.props().get_status(), FontLoaderStatus::Finished) {
66
+ html! {}
67
+ } else {
68
+ let inner = ctx
69
+ .props()
70
+ .get_fonts()
71
+ .iter()
72
+ .map(font_test_html)
73
+ .collect::<Html>();
74
+
75
+ html! { <><style>{ ":host{opacity:0!important;}" }</style>{ inner }</> }
76
+ }
77
+ }
78
+ }
79
+
80
+ /// The possible font loading state, which proceeds from top to bottom once per
81
+ /// `<perspective-viewer>` element.
82
+ #[derive(Clone, Copy)]
83
+ pub enum FontLoaderStatus {
84
+ Uninitialized,
85
+ Loading,
86
+ Finished,
87
+ }
88
+
89
+ type PromiseSet = Vec<ApiFuture<JsValue>>;
90
+
91
+ pub struct FontLoaderState {
92
+ status: Cell<FontLoaderStatus>,
93
+ elem: web_sys::HtmlElement,
94
+ on_update: Callback<()>,
95
+ fonts: RefCell<Vec<(String, String)>>,
96
+ }
97
+
98
+ impl FontLoaderProps {
99
+ pub fn new(elem: &web_sys::HtmlElement, on_update: Callback<()>) -> Self {
100
+ let inner = FontLoaderState {
101
+ status: Cell::new(FontLoaderStatus::Uninitialized),
102
+ elem: elem.clone(),
103
+ on_update,
104
+ fonts: RefCell::new(vec![]),
105
+ };
106
+
107
+ let state = yew::props!(Self {
108
+ state: Rc::new(inner)
109
+ });
110
+
111
+ ApiFuture::spawn(state.clone().load_fonts_task_safe());
112
+ state
113
+ }
114
+
115
+ pub fn get_status(&self) -> FontLoaderStatus {
116
+ self.state.status.get()
117
+ }
118
+
119
+ fn get_fonts(&self) -> Ref<Vec<(String, String)>> {
120
+ self.state.fonts.borrow()
121
+ }
122
+
123
+ /// We only want errors in this task to warn, since they are not necessarily
124
+ /// error conditions and mainly of interest to developers.
125
+ async fn load_fonts_task_safe(self) -> ApiResult<JsValue> {
126
+ if let Err(msg) = self.load_fonts_task().await {
127
+ web_sys::console::warn_1(&msg.into());
128
+ };
129
+
130
+ Ok(JsValue::UNDEFINED)
131
+ }
132
+
133
+ /// Awaits loading of a required set of font/weight pairs, given an element
134
+ /// with a CSS variable of the format:
135
+ /// ```css
136
+ /// perspective-viewer {
137
+ /// --preload-fonts: "Roboto Mono:200;Open Sans:300,400";
138
+ /// }
139
+ /// ```
140
+ async fn load_fonts_task(self) -> ApiResult<JsValue> {
141
+ await_dom_loaded().await?;
142
+ let txt = global::window()
143
+ .get_computed_style(&self.state.elem)?
144
+ .unwrap()
145
+ .get_property_value("--preload-fonts")?;
146
+
147
+ let mut block_promises: PromiseSet = vec![];
148
+ let preload_fonts = parse_fonts(&txt);
149
+ self.state.fonts.borrow_mut().clone_from(&preload_fonts);
150
+ self.state.status.set(FontLoaderStatus::Loading);
151
+ self.state.on_update.emit(());
152
+
153
+ for (family, weight) in preload_fonts.iter() {
154
+ let task = timeout_font_task(family, weight);
155
+ let mut block_fonts: PromiseSet = vec![ApiFuture::new(task)];
156
+
157
+ for entry in font_iter(global::document().fonts().values()) {
158
+ let font_face = js_sys::Reflect::get(&entry, js_intern::js_intern!("value"))?
159
+ .dyn_into::<web_sys::FontFace>()?;
160
+
161
+ // Safari always has to be "different".
162
+ if family == &font_face.family().replace('"', "")
163
+ && (weight == &font_face.weight()
164
+ || (font_face.weight() == "normal" && weight == "400"))
165
+ {
166
+ block_fonts.push(ApiFuture::new(async move {
167
+ Ok(JsFuture::from(font_face.loaded()?).await?)
168
+ }));
169
+ }
170
+ }
171
+
172
+ let fut = async { select_all(block_fonts.into_iter()).await.0 };
173
+ block_promises.push(ApiFuture::new(fut))
174
+ }
175
+
176
+ if block_promises.len() != preload_fonts.len() {
177
+ web_sys::console::warn_1(&format!("Missing preload fonts {:?}", &preload_fonts).into());
178
+ }
179
+
180
+ let res = join_all(block_promises)
181
+ .await
182
+ .into_iter()
183
+ .collect::<ApiResult<Vec<JsValue>>>()
184
+ .map(|_| JsValue::UNDEFINED);
185
+
186
+ self.state.status.set(FontLoaderStatus::Finished);
187
+ self.state.on_update.emit(());
188
+ res
189
+ }
190
+ }
191
+
192
+ // An async task which times out. Can be used to timeout an optional async task
193
+ // by combinging with `Promise::any`.
194
+ fn timeout_font_task(
195
+ family: &str,
196
+ weight: &str,
197
+ ) -> impl Future<Output = ApiResult<JsValue>> + use<> {
198
+ let timeout_msg = format!("Timeout awaiting font \"{family}:{weight}\"");
199
+ async {
200
+ set_timeout(FONT_DOWNLOAD_TIMEOUT_MS).await?;
201
+ Err(timeout_msg.into())
202
+ }
203
+ }
204
+
205
+ /// Generates a `<span>` for a specific font family and weight with `opacity:0`,
206
+ /// since not all of the fonts may be shown and when e.g. the settings panel is
207
+ /// closed, and this will defer font loading.
208
+ fn font_test_html((family, weight): &(String, String)) -> Html {
209
+ let style = format!("opacity:0;font-family:\"{family}\";font-weight:{weight}");
210
+
211
+ html! { <span {style}>{ FONT_TEST_SAMPLE }</span> }
212
+ }
213
+
214
+ fn parse_font(txt: &str) -> Option<Vec<(String, String)>> {
215
+ match *txt.trim().split(':').collect::<Vec<_>>().as_slice() {
216
+ [family, weights] => Some(
217
+ weights
218
+ .split(',')
219
+ .map(|weight| (family.to_owned(), weight.to_owned()))
220
+ .collect::<Vec<_>>(),
221
+ ),
222
+ _ => None,
223
+ }
224
+ }
225
+
226
+ /// Parse a `--preload-fonts` value into (Family, Weight) tuples.
227
+ fn parse_fonts(txt: &str) -> Vec<(String, String)> {
228
+ let trim = txt.trim();
229
+ let trim = if trim.len() > 2 {
230
+ &trim[1..trim.len() - 1]
231
+ } else {
232
+ trim
233
+ };
234
+
235
+ trim.split(';')
236
+ .filter_map(parse_font)
237
+ .flatten()
238
+ .collect::<Vec<_>>()
239
+ }
240
+
241
+ /// wasm_bindgen doesn't fully implement `FontFaceIterator`, but this is
242
+ /// basically how it would be implemented if it was.
243
+ fn font_iter(
244
+ iter: web_sys::FontFaceSetIterator,
245
+ ) -> impl Iterator<Item = web_sys::FontFaceSetIteratorResult> {
246
+ repeat_with(move || iter.next())
247
+ .filter_map(|x| x.ok())
248
+ .take_while(|entry| {
249
+ !js_sys::Reflect::get(entry, js_intern::js_intern!("done"))
250
+ .unwrap()
251
+ .as_bool()
252
+ .unwrap()
253
+ })
254
+ }
@@ -0,0 +1,286 @@
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 perspective_client::ExprValidationError;
16
+ use wasm_bindgen::JsCast;
17
+ use web_sys::*;
18
+ use yew::prelude::*;
19
+
20
+ use crate::components::form::highlight::highlight;
21
+ use crate::components::style::LocalStyle;
22
+ use crate::custom_elements::FunctionDropDownElement;
23
+ use crate::exprtk::{Cursor, tokenize};
24
+ use crate::utils::*;
25
+ use crate::*;
26
+
27
+ #[derive(Properties, PartialEq)]
28
+ pub struct CodeEditorProps {
29
+ pub expr: Rc<String>,
30
+ pub oninput: Callback<Rc<String>>,
31
+ pub onsave: Callback<()>,
32
+ pub disabled: bool,
33
+
34
+ #[prop_or_default]
35
+ pub autofocus: bool,
36
+
37
+ #[prop_or_default]
38
+ pub wordwrap: bool,
39
+
40
+ #[prop_or_default]
41
+ pub autosuggest: bool,
42
+
43
+ #[prop_or_default]
44
+ pub select_all: Subscriber<()>,
45
+
46
+ #[prop_or_default]
47
+ pub error: Option<ExprValidationError>,
48
+ }
49
+
50
+ /// Capture the input (for re-parse) and caret position whenever the input
51
+ /// text changes.
52
+ fn on_input_callback(
53
+ event: InputEvent,
54
+ // position: &UseStateSetter<u32>,
55
+ oninput: &Callback<Rc<String>>,
56
+ ) {
57
+ let elem = event
58
+ .target()
59
+ .unwrap()
60
+ .unchecked_into::<web_sys::HtmlTextAreaElement>();
61
+
62
+ oninput.emit(elem.value().into());
63
+ // position.set(elem.get_caret_position().unwrap_or_default());
64
+ }
65
+
66
+ /// Overide for special non-character commands e.g. shift+enter
67
+ fn on_keydown(event: KeyboardEvent, deps: &(UseStateSetter<u32>, Callback<()>)) {
68
+ let elem = event
69
+ .target()
70
+ .unwrap()
71
+ .unchecked_into::<web_sys::HtmlTextAreaElement>();
72
+
73
+ deps.0.set(elem.get_caret_position().unwrap_or_default());
74
+ if event.shift_key() && event.key_code() == 13 {
75
+ event.prevent_default();
76
+ deps.1.emit(())
77
+ }
78
+
79
+ // handle the tab key press
80
+ if event.key() == "Tab" {
81
+ event.prevent_default();
82
+
83
+ let caret_pos = elem.selection_start().unwrap().unwrap_or_default() as usize;
84
+
85
+ let mut initial_text = elem.value();
86
+
87
+ initial_text.insert(caret_pos, '\t');
88
+
89
+ elem.set_value(&initial_text);
90
+
91
+ let input_event = web_sys::InputEvent::new("input").unwrap();
92
+ let _ = elem.dispatch_event(&input_event).unwrap();
93
+
94
+ // place caret after inserted tab
95
+ let new_caret_pos = (caret_pos + 1) as u32;
96
+ let _ = elem.set_selection_range(new_caret_pos, new_caret_pos);
97
+
98
+ elem.focus().unwrap();
99
+ }
100
+ }
101
+
102
+ /// Scrolling callback
103
+ fn on_scroll(scroll: &UseStateSetter<(i32, i32)>, editable: &NodeRef) {
104
+ let div = editable.cast::<HtmlElement>().unwrap();
105
+ scroll.set((div.scroll_top(), div.scroll_left()));
106
+ }
107
+
108
+ /// Scrolling sync
109
+ fn scroll_sync(scroll: &UseStateHandle<(i32, i32)>, div: &NodeRef, lineno: &NodeRef) {
110
+ if let Some(div) = div.cast::<HtmlElement>() {
111
+ div.set_scroll_top(scroll.0);
112
+ div.set_scroll_left(scroll.1);
113
+ }
114
+
115
+ if let Some(div) = lineno.cast::<HtmlElement>() {
116
+ div.set_scroll_top(scroll.0);
117
+ }
118
+ }
119
+
120
+ /// Autocomplete
121
+ /// TODO this should use a portal
122
+ fn autocomplete(
123
+ filter_dropdown: &Rc<FunctionDropDownElement>,
124
+ token: &Option<String>,
125
+ target: &NodeRef,
126
+ ) {
127
+ if let Some(x) = token {
128
+ let elem = target.cast::<HtmlElement>().unwrap();
129
+ if elem.is_connected() {
130
+ filter_dropdown
131
+ .autocomplete(x.clone(), elem, Callback::from(|_| ()))
132
+ .unwrap();
133
+ } else {
134
+ filter_dropdown.hide().unwrap();
135
+ }
136
+ } else {
137
+ filter_dropdown.hide().unwrap();
138
+ }
139
+ }
140
+
141
+ /// A syntax-highlighted text editor component.
142
+ #[function_component(CodeEditor)]
143
+ pub fn code_editor(props: &CodeEditorProps) -> Html {
144
+ let select_all = use_state_eq(|| false);
145
+ let caret_position = use_state_eq(|| 0_u32);
146
+ let scroll_offset = use_state_eq(|| (0, 0));
147
+ let textarea_ref = use_node_ref().tee::<5>();
148
+ let content_ref = use_node_ref().tee::<3>();
149
+ let lineno_ref = use_node_ref().tee::<2>();
150
+ let filter_dropdown = use_memo((), |_| FunctionDropDownElement::default());
151
+ let mut cursor = Cursor::new(&props.error);
152
+ let terms = tokenize(&props.expr)
153
+ .into_iter()
154
+ .map(|token| highlight(&mut cursor, token, *caret_position))
155
+ .collect::<Html>();
156
+
157
+ let onkeydown = use_callback((caret_position.setter(), props.onsave.clone()), on_keydown);
158
+ let oninput = use_callback(props.oninput.clone(), |event, deps| {
159
+ on_input_callback(event, deps)
160
+ });
161
+
162
+ let onscroll = use_callback((scroll_offset.setter(), textarea_ref.0), |_, deps| {
163
+ on_scroll(&deps.0, &deps.1)
164
+ });
165
+
166
+ let autofocus = props.autofocus;
167
+ use_effect_with((props.expr.clone(), textarea_ref.1), {
168
+ move |(expr, textarea_ref)| {
169
+ let elem = textarea_ref.cast::<web_sys::HtmlTextAreaElement>().unwrap();
170
+ if autofocus {
171
+ elem.focus().unwrap();
172
+ }
173
+
174
+ if **expr != elem.value() {
175
+ elem.set_value(&format!("{expr}"));
176
+ elem.set_scroll_top(0);
177
+ elem.set_scroll_left(0);
178
+ elem.set_caret_position(0).unwrap();
179
+ }
180
+ }
181
+ });
182
+
183
+ // select_all.set(props.select_all);
184
+ use_effect_with((select_all.setter(), props.select_all.clone()), {
185
+ move |(select_all, props_select_all)| {
186
+ clone!(select_all);
187
+ let sub = props_select_all.add_listener(move |()| select_all.set(true));
188
+ move || drop(sub)
189
+ }
190
+ });
191
+
192
+ // select_all.set(props.select_all);
193
+ use_effect_with((select_all, textarea_ref.2), {
194
+ move |(select_all, textarea_ref)| {
195
+ let elem = textarea_ref.cast::<web_sys::HtmlTextAreaElement>().unwrap();
196
+ if **select_all {
197
+ elem.focus().unwrap();
198
+ elem.select_all().unwrap();
199
+ }
200
+
201
+ select_all.set(false);
202
+ }
203
+ });
204
+
205
+ // ????
206
+ let autofocus = props.autofocus;
207
+ use_effect_with((props.error.clone(), textarea_ref.3), {
208
+ move |(_, textarea_ref)| {
209
+ if autofocus {
210
+ let elem = textarea_ref.cast::<web_sys::HtmlTextAreaElement>().unwrap();
211
+ elem.focus().unwrap();
212
+ }
213
+ }
214
+ });
215
+
216
+ // Sync scrolling between textarea and formatted HTML
217
+ use_effect_with((scroll_offset, content_ref.0, lineno_ref.0), |deps| {
218
+ scroll_sync(&deps.0, &deps.1, &deps.2)
219
+ });
220
+
221
+ // Blur if this element is not in the tree
222
+ use_effect_with(filter_dropdown.clone(), |filter_dropdown| {
223
+ clone!(filter_dropdown);
224
+ move || filter_dropdown.hide().unwrap()
225
+ });
226
+
227
+ // Show autocomplete
228
+ use_effect_with(
229
+ (
230
+ props.autosuggest,
231
+ filter_dropdown,
232
+ cursor.auto.clone(),
233
+ cursor.noderef.clone(),
234
+ ),
235
+ |deps| {
236
+ if deps.0 {
237
+ autocomplete(&deps.1, &deps.2, &deps.3)
238
+ }
239
+ },
240
+ );
241
+
242
+ let error_line = props.error.as_ref().map(|x| x.line);
243
+ let line_numbers = cursor
244
+ .map_rows(|x| html!(
245
+ <span
246
+ class={if Some(x) == error_line {"line_number error_highlight"} else {"line_number"}}
247
+ >
248
+ { x + 1 }
249
+ </span>
250
+ ))
251
+ .collect::<Html>();
252
+
253
+ let class = if props.wordwrap { "wordwrap" } else { "" };
254
+ clone!(props.disabled);
255
+ html! {
256
+ <>
257
+ <LocalStyle href={css!("form/code-editor")} />
258
+ <div id="editor" {class}>
259
+ <div id="line_numbers" ref={lineno_ref.1}>{ line_numbers }</div>
260
+ <div id="editor-inner" {class}>
261
+ <textarea
262
+ {disabled}
263
+ id="textarea_editable"
264
+ ref={textarea_ref.4}
265
+ spellcheck="false"
266
+ {oninput}
267
+ {onscroll}
268
+ {onkeydown}
269
+ />
270
+ <div id="editor-height-sizer" />
271
+ <pre id="content" ref={content_ref.1}>
272
+ { terms }
273
+ { {
274
+ // A linebreak which pushs a textarea into scroll overflow
275
+ // may not necessarily do so in the `<pre>`, because there is
276
+ // no cursor when the last line has no content, so add
277
+ // some space here to make sure overlfow is in sync
278
+ // with the text area.
279
+ " "
280
+ } }
281
+ </pre>
282
+ </div>
283
+ </div>
284
+ </>
285
+ }
286
+ }
@@ -0,0 +1,115 @@
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 wasm_bindgen::JsCast;
14
+ use web_sys::*;
15
+ use yew::prelude::*;
16
+
17
+ #[derive(Properties, PartialEq, Clone)]
18
+ pub struct ColorRangeProps {
19
+ pub id: String,
20
+ pub pos_color: String,
21
+ pub neg_color: String,
22
+ pub is_gradient: bool,
23
+ pub on_pos_color: Callback<String>,
24
+ pub on_neg_color: Callback<String>,
25
+ pub on_reset: Callback<()>,
26
+ pub is_modified: bool,
27
+ }
28
+
29
+ fn infer_fg(color: &str) -> &'static str {
30
+ let r = i32::from_str_radix(&color[1..3], 16).unwrap_or(255) as f64;
31
+ let g = i32::from_str_radix(&color[3..5], 16).unwrap_or(0) as f64;
32
+ let b = i32::from_str_radix(&color[5..7], 16).unwrap_or(0) as f64;
33
+ if (r * r * 0.299 + g * g * 0.587 + b * b * 0.114).sqrt() > 130.0 {
34
+ "--sign--color:#161616"
35
+ } else {
36
+ "--sign--color:#FFFFFF"
37
+ }
38
+ }
39
+
40
+ #[function_component(ColorRangeSelector)]
41
+ pub fn color_chooser_component(props: &ColorRangeProps) -> Html {
42
+ let on_pos_color = use_callback(
43
+ props.on_pos_color.clone(),
44
+ |event: InputEvent, on_pos_color| {
45
+ let color = event
46
+ .target()
47
+ .unwrap()
48
+ .unchecked_into::<HtmlInputElement>()
49
+ .value();
50
+ on_pos_color.emit(color);
51
+ },
52
+ );
53
+
54
+ let on_neg_color = use_callback(
55
+ props.on_neg_color.clone(),
56
+ |event: InputEvent, on_neg_color| {
57
+ let color = event
58
+ .target()
59
+ .unwrap()
60
+ .unchecked_into::<HtmlInputElement>()
61
+ .value();
62
+ on_neg_color.emit(color);
63
+ },
64
+ );
65
+
66
+ let fg_pos = infer_fg(&props.pos_color);
67
+ let fg_neg = infer_fg(&props.neg_color);
68
+ let style = if props.is_gradient {
69
+ format!(
70
+ "background:linear-gradient(to right, {} 0%, transparent 50%, {} 100%)",
71
+ props.pos_color, props.neg_color
72
+ )
73
+ } else {
74
+ format!(
75
+ "background:linear-gradient(to right, {} 0%, {} 50%, {} 50%, {} 100%)",
76
+ props.pos_color, props.pos_color, props.neg_color, props.neg_color
77
+ )
78
+ };
79
+
80
+ let on_reset = use_callback(props.clone(), |_: MouseEvent, deps| deps.on_reset.emit(()));
81
+
82
+ html! {
83
+ <>
84
+ <label id="color-range-label" />
85
+ <div class="color-gradient-container">
86
+ <div style={fg_pos} class="color-selector">
87
+ <input
88
+ id={format!("{}-pos", props.id)}
89
+ class="parameter pos-color-param"
90
+ type="color"
91
+ value={props.pos_color.to_owned()}
92
+ data-value={props.pos_color.to_owned()}
93
+ oninput={on_pos_color}
94
+ />
95
+ <label for={format!("{}-pos", props.id)} class="color-label">{ "+" }</label>
96
+ </div>
97
+ <div class="color-thermometer" {style} />
98
+ <div style={fg_neg} class="color-selector">
99
+ <input
100
+ id={format!("{}-neg", props.id)}
101
+ class="parameter neg-color-param"
102
+ type="color"
103
+ value={props.neg_color.to_owned()}
104
+ data-value={props.neg_color.to_owned()}
105
+ oninput={on_neg_color}
106
+ />
107
+ <label for={format!("{}-neg", props.id)} class="color-label">{ "-" }</label>
108
+ </div>
109
+ if props.is_modified { <span class="reset-default-style" onclick={on_reset} /> } else {
110
+ <span class="reset-default-style-disabled" />
111
+ }
112
+ </div>
113
+ </>
114
+ }
115
+ }