@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.
- package/LICENSE.md +193 -0
- package/README.md +4 -0
- package/dist/cdn/perspective-viewer.js +3 -0
- package/dist/cdn/perspective-viewer.js.map +7 -0
- package/dist/css/dracula.css +1 -0
- package/dist/css/gruvbox-dark.css +1 -0
- package/dist/css/gruvbox.css +1 -0
- package/dist/css/icons.css +1 -0
- package/dist/css/intl/de.css +1 -0
- package/dist/css/intl/es.css +1 -0
- package/dist/css/intl/fr.css +1 -0
- package/dist/css/intl/ja.css +1 -0
- package/dist/css/intl/pt.css +1 -0
- package/dist/css/intl/zh.css +1 -0
- package/dist/css/intl.css +1 -0
- package/dist/css/monokai.css +1 -0
- package/dist/css/pro-dark.css +1 -0
- package/dist/css/pro.css +1 -0
- package/dist/css/solarized-dark.css +1 -0
- package/dist/css/solarized.css +1 -0
- package/dist/css/themes.css +1 -0
- package/dist/css/vaporwave.css +1 -0
- package/dist/css/variables.css +0 -0
- package/dist/esm/bootstrap.d.ts +1 -0
- package/dist/esm/extensions.d.ts +70 -0
- package/dist/esm/perspective-viewer.d.ts +39 -0
- package/dist/esm/perspective-viewer.inline.js +3 -0
- package/dist/esm/perspective-viewer.inline.js.map +7 -0
- package/dist/esm/perspective-viewer.js +3 -0
- package/dist/esm/perspective-viewer.js.map +7 -0
- package/dist/esm/plugin.d.ts +195 -0
- package/dist/esm/ts-rs/Aggregate.d.ts +1 -0
- package/dist/esm/ts-rs/ColumnConfigValues.d.ts +31 -0
- package/dist/esm/ts-rs/CustomDatetimeFormat.d.ts +1 -0
- package/dist/esm/ts-rs/CustomDatetimeStyleConfig.d.ts +15 -0
- package/dist/esm/ts-rs/CustomNumberFormatConfig.d.ts +18 -0
- package/dist/esm/ts-rs/DatetimeColorMode.d.ts +1 -0
- package/dist/esm/ts-rs/DatetimeFormatType.d.ts +6 -0
- package/dist/esm/ts-rs/Expressions.d.ts +3 -0
- package/dist/esm/ts-rs/Filter.d.ts +2 -0
- package/dist/esm/ts-rs/FilterReducer.d.ts +1 -0
- package/dist/esm/ts-rs/FilterTerm.d.ts +2 -0
- package/dist/esm/ts-rs/FormatMode.d.ts +1 -0
- package/dist/esm/ts-rs/FormatUnit.d.ts +1 -0
- package/dist/esm/ts-rs/NumberBackgroundMode.d.ts +1 -0
- package/dist/esm/ts-rs/NumberForegroundMode.d.ts +1 -0
- package/dist/esm/ts-rs/OptionalUpdate.d.ts +1 -0
- package/dist/esm/ts-rs/PluginConfig.d.ts +2 -0
- package/dist/esm/ts-rs/RoundingMode.d.ts +1 -0
- package/dist/esm/ts-rs/RoundingPriority.d.ts +1 -0
- package/dist/esm/ts-rs/Scalar.d.ts +5 -0
- package/dist/esm/ts-rs/SignDisplay.d.ts +1 -0
- package/dist/esm/ts-rs/SimpleDatetimeFormat.d.ts +1 -0
- package/dist/esm/ts-rs/SimpleDatetimeStyleConfig.d.ts +6 -0
- package/dist/esm/ts-rs/Sort.d.ts +2 -0
- package/dist/esm/ts-rs/SortDir.d.ts +1 -0
- package/dist/esm/ts-rs/StringColorMode.d.ts +1 -0
- package/dist/esm/ts-rs/TrailingZeroDisplay.d.ts +1 -0
- package/dist/esm/ts-rs/UseGrouping.d.ts +1 -0
- package/dist/esm/ts-rs/ViewerConfigUpdate.d.ts +102 -0
- package/dist/esm/ts-rs/serde_json/JsonValue.d.ts +3 -0
- package/dist/wasm/perspective-viewer.d.ts +1291 -0
- package/dist/wasm/perspective-viewer.js +3883 -0
- package/dist/wasm/perspective-viewer.wasm +0 -0
- package/dist/wasm/perspective-viewer.wasm.d.ts +142 -0
- package/dist/wasm/snippets/perspective-viewer-c69283f6f62a5f14/inline0.js +1 -0
- package/dist/wasm/snippets/perspective-viewer-c69283f6f62a5f14/inline1.js +1 -0
- package/dist/wasm/snippets/perspective-viewer-c69283f6f62a5f14/inline2.js +1 -0
- package/dist/wasm/snippets/perspective-viewer-c69283f6f62a5f14/inline3.js +44 -0
- package/dist/wasm/snippets/perspective-viewer-c69283f6f62a5f14/inline4.js +1 -0
- package/package.json +55 -0
- package/src/less/aggregate-selector.less +72 -0
- package/src/less/column-dropdown.less +95 -0
- package/src/less/column-selector.less +551 -0
- package/src/less/column-settings-panel.less +255 -0
- package/src/less/column-style.less +319 -0
- package/src/less/column-symbol-attributes.less +73 -0
- package/src/less/config-selector.less +363 -0
- package/src/less/containers/dropdown-menu.less +94 -0
- package/src/less/containers/pairs-list.less +46 -0
- package/src/less/containers/radio-list.less +29 -0
- package/src/less/containers/scroll-panel.less +27 -0
- package/src/less/containers/split-panel.less +80 -0
- package/src/less/containers/tabs.less +90 -0
- package/src/less/dom/checkbox.less +100 -0
- package/src/less/dom/scrollbar.less +35 -0
- package/src/less/dom/select.less +63 -0
- package/src/less/empty-column.less +72 -0
- package/src/less/expression-editor.less +152 -0
- package/src/less/filter-dropdown.less +53 -0
- package/src/less/filter-item.less +126 -0
- package/src/less/form/code-editor.less +137 -0
- package/src/less/form/debug.less +88 -0
- package/src/less/function-dropdown.less +45 -0
- package/src/less/plugin-selector.less +173 -0
- package/src/less/render-warning.less +81 -0
- package/src/less/status-bar.less +551 -0
- package/src/less/type-icon.less +68 -0
- package/src/less/viewer.less +368 -0
- package/src/rust/components/column_dropdown.rs +162 -0
- package/src/rust/components/column_selector/active_column.rs +462 -0
- package/src/rust/components/column_selector/add_expression_button.rs +59 -0
- package/src/rust/components/column_selector/aggregate_selector.rs +186 -0
- package/src/rust/components/column_selector/config_selector.rs +674 -0
- package/src/rust/components/column_selector/empty_column.rs +134 -0
- package/src/rust/components/column_selector/expression_toolbar.rs +45 -0
- package/src/rust/components/column_selector/filter_column.rs +530 -0
- package/src/rust/components/column_selector/inactive_column.rs +221 -0
- package/src/rust/components/column_selector/invalid_column.rs +37 -0
- package/src/rust/components/column_selector/pivot_column.rs +93 -0
- package/src/rust/components/column_selector/sort_column.rs +135 -0
- package/src/rust/components/column_selector.rs +426 -0
- package/src/rust/components/column_settings_sidebar/attributes_tab.rs +34 -0
- package/src/rust/components/column_settings_sidebar/mod.rs +17 -0
- package/src/rust/components/column_settings_sidebar/save_settings.rs +68 -0
- package/src/rust/components/column_settings_sidebar/sidebar.rs +396 -0
- package/src/rust/components/column_settings_sidebar/style_tab/column_style.rs +177 -0
- package/src/rust/components/column_settings_sidebar/style_tab/stub.rs +34 -0
- package/src/rust/components/column_settings_sidebar/style_tab/symbol/row_selector.rs +101 -0
- package/src/rust/components/column_settings_sidebar/style_tab/symbol/symbol_config.rs +24 -0
- package/src/rust/components/column_settings_sidebar/style_tab/symbol/symbol_pairs.rs +185 -0
- package/src/rust/components/column_settings_sidebar/style_tab/symbol/symbol_selector.rs +55 -0
- package/src/rust/components/column_settings_sidebar/style_tab/symbol.rs +131 -0
- package/src/rust/components/column_settings_sidebar/style_tab.rs +231 -0
- package/src/rust/components/containers/dragdrop_list.rs +325 -0
- package/src/rust/components/containers/dropdown_menu.rs +104 -0
- package/src/rust/components/containers/kvpair.rs +47 -0
- package/src/rust/components/containers/mod.rs +28 -0
- package/src/rust/components/containers/scroll_panel.rs +311 -0
- package/src/rust/components/containers/scroll_panel_item.rs +111 -0
- package/src/rust/components/containers/select.rs +269 -0
- package/src/rust/components/containers/sidebar.rs +89 -0
- package/src/rust/components/containers/split_panel.rs +483 -0
- package/src/rust/components/containers/tab_list.rs +104 -0
- package/src/rust/components/containers/tests/mod.rs +11 -0
- package/src/rust/components/containers/tests/split_panel.rs +91 -0
- package/src/rust/components/containers/trap_door_panel.rs +50 -0
- package/src/rust/components/copy_dropdown.rs +136 -0
- package/src/rust/components/datetime_column_style/custom.rs +222 -0
- package/src/rust/components/datetime_column_style/simple.rs +122 -0
- package/src/rust/components/datetime_column_style.rs +284 -0
- package/src/rust/components/editable_header.rs +183 -0
- package/src/rust/components/empty_row.rs +169 -0
- package/src/rust/components/error_message.rs +56 -0
- package/src/rust/components/export_dropdown.rs +153 -0
- package/src/rust/components/expression_editor.rs +153 -0
- package/src/rust/components/filter_dropdown.rs +144 -0
- package/src/rust/components/font_loader.rs +254 -0
- package/src/rust/components/form/code_editor.rs +286 -0
- package/src/rust/components/form/color_range_selector.rs +115 -0
- package/src/rust/components/form/color_selector.rs +54 -0
- package/src/rust/components/form/debug.rs +253 -0
- package/src/rust/components/form/highlight.rs +50 -0
- package/src/rust/components/form/mod.rs +29 -0
- package/src/rust/components/form/number_field.rs +72 -0
- package/src/rust/components/form/number_input.rs +45 -0
- package/src/rust/components/form/number_range_field.rs +77 -0
- package/src/rust/components/form/optional_field.rs +53 -0
- package/src/rust/components/form/select_field.rs +104 -0
- package/src/rust/components/function_dropdown.rs +151 -0
- package/src/rust/components/mod.rs +45 -0
- package/src/rust/components/modal.rs +171 -0
- package/src/rust/components/number_column_style.rs +522 -0
- package/src/rust/components/plugin_selector.rs +196 -0
- package/src/rust/components/render_warning.rs +178 -0
- package/src/rust/components/status_bar.rs +315 -0
- package/src/rust/components/status_bar_counter.rs +229 -0
- package/src/rust/components/status_indicator.rs +164 -0
- package/src/rust/components/string_column_style.rs +186 -0
- package/src/rust/components/style/local_style.rs +35 -0
- package/src/rust/components/style/mod.rs +52 -0
- package/src/rust/components/style/style_cache.rs +132 -0
- package/src/rust/components/style/style_provider.rs +65 -0
- package/src/rust/components/style_controls/number_string_format/digits_section.rs +140 -0
- package/src/rust/components/style_controls/number_string_format/misc_section.rs +56 -0
- package/src/rust/components/style_controls/number_string_format/style_section.rs +69 -0
- package/src/rust/components/style_controls/number_string_format/types.rs +31 -0
- package/src/rust/components/style_controls/number_string_format.rs +259 -0
- package/src/rust/components/style_controls.rs +15 -0
- package/src/rust/components/type_icon.rs +53 -0
- package/src/rust/components/viewer.rs +668 -0
- package/src/rust/config/columns_config.rs +132 -0
- package/src/rust/config/datetime_column_style/color_mode.rs +62 -0
- package/src/rust/config/datetime_column_style/custom.rs +121 -0
- package/src/rust/config/datetime_column_style/custom_format.rs +90 -0
- package/src/rust/config/datetime_column_style/simple.rs +55 -0
- package/src/rust/config/datetime_column_style/simple_format.rs +86 -0
- package/src/rust/config/datetime_column_style.rs +104 -0
- package/src/rust/config/mod.rs +30 -0
- package/src/rust/config/number_column_style.rs +138 -0
- package/src/rust/config/number_string_format/enums.rs +298 -0
- package/src/rust/config/number_string_format.rs +269 -0
- package/src/rust/config/string_column_style.rs +140 -0
- package/src/rust/config/view_config.rs +24 -0
- package/src/rust/config/viewer_config.rs +337 -0
- package/src/rust/custom_elements/column_dropdown.rs +123 -0
- package/src/rust/custom_elements/copy_dropdown.rs +112 -0
- package/src/rust/custom_elements/debug_plugin.rs +114 -0
- package/src/rust/custom_elements/export_dropdown.rs +112 -0
- package/src/rust/custom_elements/filter_dropdown.rs +179 -0
- package/src/rust/custom_elements/function_dropdown.rs +115 -0
- package/src/rust/custom_elements/mod.rs +24 -0
- package/src/rust/custom_elements/modal.rs +395 -0
- package/src/rust/custom_elements/viewer.rs +880 -0
- package/src/rust/custom_events.rs +214 -0
- package/src/rust/dragdrop.rs +362 -0
- package/src/rust/exprtk/cursor.rs +81 -0
- package/src/rust/exprtk/mod.rs +17 -0
- package/src/rust/exprtk/tokenize/comment.rs +21 -0
- package/src/rust/exprtk/tokenize/number.rs +44 -0
- package/src/rust/exprtk/tokenize/string.rs +98 -0
- package/src/rust/exprtk/tokenize/symbol.rs +26 -0
- package/src/rust/exprtk/tokenize.rs +190 -0
- package/src/rust/js/clipboard.rs +77 -0
- package/src/rust/js/clipboard_item.rs +21 -0
- package/src/rust/js/intersection_observer.rs +32 -0
- package/src/rust/js/mimetype.rs +42 -0
- package/src/rust/js/mod.rs +29 -0
- package/src/rust/js/plugin.rs +167 -0
- package/src/rust/js/resize_observer.rs +37 -0
- package/src/rust/js/testing.rs +149 -0
- package/src/rust/lib.rs +116 -0
- package/src/rust/model/columns_iter_set.rs +340 -0
- package/src/rust/model/copy_export.rs +157 -0
- package/src/rust/model/create_col.rs +59 -0
- package/src/rust/model/edit_expression.rs +95 -0
- package/src/rust/model/export_app.rs +58 -0
- package/src/rust/model/export_method.rs +151 -0
- package/src/rust/model/get_viewer_config.rs +85 -0
- package/src/rust/model/intersection_observer.rs +81 -0
- package/src/rust/model/is_invalid_drop.rs +36 -0
- package/src/rust/model/mod.rs +93 -0
- package/src/rust/model/plugin_column_styles.rs +106 -0
- package/src/rust/model/resize_observer.rs +98 -0
- package/src/rust/model/restore_and_render.rs +113 -0
- package/src/rust/model/structural.rs +73 -0
- package/src/rust/model/update_and_render.rs +74 -0
- package/src/rust/presentation.rs +370 -0
- package/src/rust/renderer/activate.rs +49 -0
- package/src/rust/renderer/limits.rs +200 -0
- package/src/rust/renderer/plugin_store.rs +45 -0
- package/src/rust/renderer/registry.rs +135 -0
- package/src/rust/renderer/render_timer.rs +185 -0
- package/src/rust/renderer.rs +463 -0
- package/src/rust/session/column_defaults_update.rs +141 -0
- package/src/rust/session/drag_drop_update.rs +179 -0
- package/src/rust/session/metadata.rs +308 -0
- package/src/rust/session/replace_expression_update.rs +131 -0
- package/src/rust/session/view_subscription.rs +189 -0
- package/src/rust/session.rs +794 -0
- package/src/rust/utils/browser/blob.rs +49 -0
- package/src/rust/utils/browser/download.rs +26 -0
- package/src/rust/utils/browser/mod.rs +24 -0
- package/src/rust/utils/browser/request_animation_frame.rs +76 -0
- package/src/rust/utils/browser/selection.rs +79 -0
- package/src/rust/utils/browser/tests/debounce.rs +114 -0
- package/src/rust/utils/browser/tests/mod.rs +13 -0
- package/src/rust/utils/custom_element.rs +85 -0
- package/src/rust/utils/datetime.rs +49 -0
- package/src/rust/utils/debounce.rs +54 -0
- package/src/rust/utils/hooks/mod.rs +15 -0
- package/src/rust/utils/hooks/use_async_callback.rs +53 -0
- package/src/rust/utils/mod.rs +114 -0
- package/src/rust/utils/number_format.rs +48 -0
- package/src/rust/utils/pubsub.rs +222 -0
- package/src/rust/utils/scope.rs +54 -0
- package/src/rust/utils/tee.rs +88 -0
- package/src/rust/utils/tests/mod.rs +14 -0
- package/src/rust/utils/tests/pubsub.rs +95 -0
- package/src/rust/utils/tests/request_animation_frame.rs +42 -0
- package/src/rust/utils/wasm_abi.rs +61 -0
- package/src/rust/utils/weak_scope.rs +39 -0
- package/src/svg/align-scroll-icon.svg +7 -0
- package/src/svg/bg-pattern.png +0 -0
- package/src/svg/boolean-type.svg +3 -0
- package/src/svg/checkbox-checked-icon.svg +7 -0
- package/src/svg/checkbox-hover.svg +4 -0
- package/src/svg/checkbox-off.svg +4 -0
- package/src/svg/checkbox-on.svg +4 -0
- package/src/svg/checkbox-unchecked-icon.svg +3 -0
- package/src/svg/close-icon.svg +6 -0
- package/src/svg/column-settings-icon.svg +6 -0
- package/src/svg/datagrid-select-column.svg +15 -0
- package/src/svg/datagrid-select-region.svg +15 -0
- package/src/svg/datagrid-select-row.svg +13 -0
- package/src/svg/datagrid.svg +13 -0
- package/src/svg/date-type.svg +23 -0
- package/src/svg/downloading.gif +0 -0
- package/src/svg/drag-handle.svg +10 -0
- package/src/svg/drawer-bg-pattern-hidpi.png +0 -0
- package/src/svg/drawer-tab-hover.svg +8 -0
- package/src/svg/drawer-tab-invert-hover.svg +9 -0
- package/src/svg/drawer-tab-invert.svg +8 -0
- package/src/svg/drawer-tab.svg +7 -0
- package/src/svg/dropdown-selector-light.svg +3 -0
- package/src/svg/dropdown-selector.svg +3 -0
- package/src/svg/duplicate-icon.svg +4 -0
- package/src/svg/editable-icon.svg +5 -0
- package/src/svg/export-icon.svg +4 -0
- package/src/svg/expression.svg +10 -0
- package/src/svg/free-scroll-icon.svg +5 -0
- package/src/svg/inactive-mouseover.svg +4 -0
- package/src/svg/mega-menu-icons-candlestick.svg +38 -0
- package/src/svg/mega-menu-icons-datagrid.svg +38 -0
- package/src/svg/mega-menu-icons-heatmap.svg +60 -0
- package/src/svg/mega-menu-icons-map-scatter.svg +43 -0
- package/src/svg/mega-menu-icons-ohlc.svg +40 -0
- package/src/svg/mega-menu-icons-sunburst.svg +39 -0
- package/src/svg/mega-menu-icons-treemap.svg +44 -0
- package/src/svg/mega-menu-icons-x-bar.svg +36 -0
- package/src/svg/mega-menu-icons-x-y-line.svg +38 -0
- package/src/svg/mega-menu-icons-x-y-scatter.svg +72 -0
- package/src/svg/mega-menu-icons-y-area.svg +73 -0
- package/src/svg/mega-menu-icons-y-bar.svg +36 -0
- package/src/svg/mega-menu-icons-y-line.svg +36 -0
- package/src/svg/mega-menu-icons-y-scatter.svg +72 -0
- package/src/svg/number-type.svg +4 -0
- package/src/svg/radio-hover.svg +4 -0
- package/src/svg/radio-off.svg +3 -0
- package/src/svg/radio-on.svg +4 -0
- package/src/svg/read-only-icon.svg +6 -0
- package/src/svg/revert-icon.svg +4 -0
- package/src/svg/sort-abs-asc-icon.svg +3 -0
- package/src/svg/sort-abs-col-asc-icon.svg +3 -0
- package/src/svg/sort-abs-col-desc-icon.svg +3 -0
- package/src/svg/sort-abs-desc-icon.svg +3 -0
- package/src/svg/sort-asc-icon.svg +4 -0
- package/src/svg/sort-col-asc-icon.svg +4 -0
- package/src/svg/sort-col-desc-icon.svg +4 -0
- package/src/svg/sort-desc-icon.svg +4 -0
- package/src/svg/sort-none-icon.svg +3 -0
- package/src/svg/status_error.svg +70 -0
- package/src/svg/status_ok.svg +26 -0
- package/src/svg/string-type.svg +8 -0
- package/src/svg/theme-icon.svg +5 -0
- package/src/svg/updating.gif +0 -0
- package/src/themes/dracula.less +101 -0
- package/src/themes/gruvbox-dark.less +116 -0
- package/src/themes/gruvbox.less +152 -0
- package/src/themes/icons.less +130 -0
- package/src/themes/intl/de.less +102 -0
- package/src/themes/intl/es.less +102 -0
- package/src/themes/intl/fr.less +102 -0
- package/src/themes/intl/ja.less +102 -0
- package/src/themes/intl/pt.less +102 -0
- package/src/themes/intl/zh.less +102 -0
- package/src/themes/intl.less +102 -0
- package/src/themes/monokai.less +107 -0
- package/src/themes/pro-dark.less +147 -0
- package/src/themes/pro.less +186 -0
- package/src/themes/solarized-dark.less +78 -0
- package/src/themes/solarized.less +102 -0
- package/src/themes/themes.less +21 -0
- package/src/themes/vaporwave.less +145 -0
- package/src/themes/variables.less +24 -0
- package/src/ts/bootstrap.ts +26 -0
- package/src/ts/extensions.ts +124 -0
- package/src/ts/perspective-viewer.cdn.ts +19 -0
- package/src/ts/perspective-viewer.inline.ts +23 -0
- package/src/ts/perspective-viewer.ts +59 -0
- package/src/ts/plugin.ts +279 -0
- package/src/ts/ts-rs/Aggregate.ts +3 -0
- package/src/ts/ts-rs/ColumnConfigValues.ts +14 -0
- package/src/ts/ts-rs/CustomDatetimeFormat.ts +3 -0
- package/src/ts/ts-rs/CustomDatetimeStyleConfig.ts +5 -0
- package/src/ts/ts-rs/CustomNumberFormatConfig.ts +8 -0
- package/src/ts/ts-rs/DatetimeColorMode.ts +3 -0
- package/src/ts/ts-rs/DatetimeFormatType.ts +8 -0
- package/src/ts/ts-rs/Expressions.ts +3 -0
- package/src/ts/ts-rs/Filter.ts +4 -0
- package/src/ts/ts-rs/FilterReducer.ts +3 -0
- package/src/ts/ts-rs/FilterTerm.ts +4 -0
- package/src/ts/ts-rs/FormatMode.ts +3 -0
- package/src/ts/ts-rs/FormatUnit.ts +3 -0
- package/src/ts/ts-rs/NumberBackgroundMode.ts +3 -0
- package/src/ts/ts-rs/NumberForegroundMode.ts +3 -0
- package/src/ts/ts-rs/OnUpdateData.ts +8 -0
- package/src/ts/ts-rs/OptionalUpdate.ts +3 -0
- package/src/ts/ts-rs/PluginConfig.ts +4 -0
- package/src/ts/ts-rs/RoundingMode.ts +3 -0
- package/src/ts/ts-rs/RoundingPriority.ts +3 -0
- package/src/ts/ts-rs/Scalar.ts +7 -0
- package/src/ts/ts-rs/SignDisplay.ts +3 -0
- package/src/ts/ts-rs/SimpleDatetimeFormat.ts +3 -0
- package/src/ts/ts-rs/SimpleDatetimeStyleConfig.ts +4 -0
- package/src/ts/ts-rs/Sort.ts +4 -0
- package/src/ts/ts-rs/SortDir.ts +3 -0
- package/src/ts/ts-rs/StringColorMode.ts +3 -0
- package/src/ts/ts-rs/TrailingZeroDisplay.ts +3 -0
- package/src/ts/ts-rs/UseGrouping.ts +3 -0
- package/src/ts/ts-rs/ViewOnUpdateResp.ts +3 -0
- package/src/ts/ts-rs/ViewerConfigUpdate.ts +90 -0
- package/src/ts/ts-rs/serde_json/JsonValue.ts +3 -0
- package/tsconfig.json +15 -0
|
@@ -0,0 +1,370 @@
|
|
|
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::RefCell;
|
|
14
|
+
use std::collections::{HashMap, HashSet};
|
|
15
|
+
use std::ops::Deref;
|
|
16
|
+
use std::rc::Rc;
|
|
17
|
+
|
|
18
|
+
use async_lock::Mutex;
|
|
19
|
+
use perspective_js::utils::{ApiFuture, ApiResult, ToApiError, global};
|
|
20
|
+
use wasm_bindgen::prelude::*;
|
|
21
|
+
use web_sys::*;
|
|
22
|
+
use yew::html::ImplicitClone;
|
|
23
|
+
|
|
24
|
+
use crate::components::column_settings_sidebar::ColumnSettingsTab;
|
|
25
|
+
use crate::components::viewer::ColumnLocator;
|
|
26
|
+
use crate::config::{ColumnConfigUpdate, ColumnConfigValueUpdate, ColumnConfigValues};
|
|
27
|
+
use crate::utils::*;
|
|
28
|
+
|
|
29
|
+
/// The available themes as detected in the browser environment or set
|
|
30
|
+
/// explicitly when CORS prevents detection. Detection is expensive and
|
|
31
|
+
/// typically must be performed only once, when `document.styleSheets` is
|
|
32
|
+
/// up-to-date.
|
|
33
|
+
#[derive(Clone)]
|
|
34
|
+
pub struct Presentation(Rc<PresentationHandle>);
|
|
35
|
+
|
|
36
|
+
impl PartialEq for Presentation {
|
|
37
|
+
fn eq(&self, other: &Self) -> bool {
|
|
38
|
+
Rc::ptr_eq(&self.0, &other.0)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
impl Deref for Presentation {
|
|
43
|
+
type Target = PresentationHandle;
|
|
44
|
+
|
|
45
|
+
fn deref(&self) -> &Self::Target {
|
|
46
|
+
&self.0
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
impl ImplicitClone for Presentation {}
|
|
51
|
+
|
|
52
|
+
#[derive(Clone, Default, Debug, PartialEq)]
|
|
53
|
+
pub struct OpenColumnSettings {
|
|
54
|
+
pub locator: Option<ColumnLocator>,
|
|
55
|
+
pub tab: Option<ColumnSettingsTab>,
|
|
56
|
+
}
|
|
57
|
+
impl OpenColumnSettings {
|
|
58
|
+
pub fn name(&self) -> Option<String> {
|
|
59
|
+
self.locator
|
|
60
|
+
.as_ref()
|
|
61
|
+
.and_then(|l| l.name())
|
|
62
|
+
.map(|s| s.to_owned())
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
pub type ColumnConfigMap = HashMap<String, ColumnConfigValues>;
|
|
67
|
+
|
|
68
|
+
pub struct PresentationHandle {
|
|
69
|
+
viewer_elem: HtmlElement,
|
|
70
|
+
theme_data: Mutex<ThemeData>,
|
|
71
|
+
name: RefCell<Option<String>>,
|
|
72
|
+
is_settings_open: RefCell<bool>,
|
|
73
|
+
open_column_settings: RefCell<OpenColumnSettings>,
|
|
74
|
+
is_workspace: RefCell<Option<bool>>,
|
|
75
|
+
columns_config: RefCell<ColumnConfigMap>,
|
|
76
|
+
pub settings_open_changed: PubSub<bool>,
|
|
77
|
+
pub column_settings_open_changed: PubSub<(bool, Option<String>)>,
|
|
78
|
+
pub column_settings_updated: PubSub<JsValue>,
|
|
79
|
+
pub theme_config_updated: PubSub<(Rc<Vec<String>>, Option<usize>)>,
|
|
80
|
+
pub title_changed: PubSub<Option<String>>,
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
#[derive(Default)]
|
|
84
|
+
pub struct ThemeData {
|
|
85
|
+
themes: Option<Vec<String>>,
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
impl Presentation {
|
|
89
|
+
pub fn new(elem: &HtmlElement) -> Self {
|
|
90
|
+
let theme = Self(Rc::new(PresentationHandle {
|
|
91
|
+
viewer_elem: elem.clone(),
|
|
92
|
+
name: Default::default(),
|
|
93
|
+
theme_data: Default::default(),
|
|
94
|
+
settings_open_changed: Default::default(),
|
|
95
|
+
column_settings_open_changed: Default::default(),
|
|
96
|
+
column_settings_updated: Default::default(),
|
|
97
|
+
columns_config: Default::default(),
|
|
98
|
+
is_settings_open: Default::default(),
|
|
99
|
+
is_workspace: Default::default(),
|
|
100
|
+
open_column_settings: Default::default(),
|
|
101
|
+
theme_config_updated: PubSub::default(),
|
|
102
|
+
title_changed: PubSub::default(),
|
|
103
|
+
}));
|
|
104
|
+
|
|
105
|
+
ApiFuture::spawn(theme.clone().init());
|
|
106
|
+
theme
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
pub fn get_title(&self) -> Option<String> {
|
|
110
|
+
self.name.borrow().clone()
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
pub fn set_title(&self, title: Option<String>) {
|
|
114
|
+
self.name.borrow_mut().clone_from(&title);
|
|
115
|
+
self.title_changed.emit(title);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
pub fn get_is_workspace(&self) -> bool {
|
|
119
|
+
if self.is_workspace.borrow().is_none() {
|
|
120
|
+
let is_workspace = self
|
|
121
|
+
.viewer_elem
|
|
122
|
+
.parent_element()
|
|
123
|
+
.map(|x| x.tag_name() == "PERSPECTIVE-WORKSPACE")
|
|
124
|
+
.unwrap_or_default();
|
|
125
|
+
|
|
126
|
+
*self.is_workspace.borrow_mut() = Some(is_workspace);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
self.is_workspace.borrow().unwrap()
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
pub fn set_settings_attribute(&self, opt: bool) {
|
|
133
|
+
self.viewer_elem
|
|
134
|
+
.toggle_attribute_with_force("settings", opt)
|
|
135
|
+
.unwrap();
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
pub fn is_settings_open(&self) -> bool {
|
|
139
|
+
*self.is_settings_open.borrow()
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
pub fn set_settings_open(&self, open: Option<bool>) -> ApiResult<bool> {
|
|
143
|
+
let open_state = open.unwrap_or_else(|| !*self.is_settings_open.borrow());
|
|
144
|
+
if *self.is_settings_open.borrow() != open_state {
|
|
145
|
+
*self.is_settings_open.borrow_mut() = open_state;
|
|
146
|
+
self.set_settings_attribute(open_state);
|
|
147
|
+
self.settings_open_changed.emit(open_state);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
Ok(open_state)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/// Sets the currently opened column settings. Emits an internal event on
|
|
154
|
+
/// change. Passing None is a shorthand for setting all fields to
|
|
155
|
+
/// None.
|
|
156
|
+
pub fn set_open_column_settings(&self, settings: Option<OpenColumnSettings>) {
|
|
157
|
+
let settings = settings.unwrap_or_default();
|
|
158
|
+
if *(self.open_column_settings.borrow()) != settings {
|
|
159
|
+
settings.clone_into(&mut *self.open_column_settings.borrow_mut());
|
|
160
|
+
self.column_settings_open_changed
|
|
161
|
+
.emit((true, settings.name()));
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/// Gets a clone of the current OpenColumnSettings.
|
|
166
|
+
pub fn get_open_column_settings(&self) -> OpenColumnSettings {
|
|
167
|
+
self.open_column_settings.borrow().deref().clone()
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
async fn init(self) -> ApiResult<()> {
|
|
171
|
+
self.set_theme_attribute(self.get_selected_theme_name().await.as_deref())
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/// Get the available theme names from the browser environment by parsing
|
|
175
|
+
/// readable stylesheets. This method is memoized - the state can be
|
|
176
|
+
/// flushed by calling `reset()`.
|
|
177
|
+
pub async fn get_available_themes(&self) -> ApiResult<Rc<Vec<String>>> {
|
|
178
|
+
let mut data = self.0.theme_data.lock().await;
|
|
179
|
+
if data.themes.is_none() {
|
|
180
|
+
await_dom_loaded().await?;
|
|
181
|
+
let themes = get_theme_names(&self.0.viewer_elem)?;
|
|
182
|
+
data.themes = Some(themes);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
Ok(data.themes.clone().unwrap().into())
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/// Reset the state. `styleSheets` will be re-parsed next time
|
|
189
|
+
/// `get_themes()` is called if the `themes` argument is `None`.
|
|
190
|
+
///
|
|
191
|
+
/// # Returns
|
|
192
|
+
/// A `bool` indicating whether the internal state changed.
|
|
193
|
+
pub async fn reset_available_themes(&self, themes: Option<Vec<String>>) -> bool {
|
|
194
|
+
fn as_set(x: &Option<Vec<String>>) -> HashSet<&'_ String> {
|
|
195
|
+
x.as_ref()
|
|
196
|
+
.map(|x| x.iter().collect::<HashSet<_>>())
|
|
197
|
+
.unwrap_or_default()
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
let mut mutex = self.0.theme_data.lock().await;
|
|
201
|
+
let changed = as_set(&mutex.themes) != as_set(&themes);
|
|
202
|
+
mutex.themes = themes;
|
|
203
|
+
changed
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
pub async fn get_selected_theme_config(&self) -> ApiResult<(Rc<Vec<String>>, Option<usize>)> {
|
|
207
|
+
let themes = self.get_available_themes().await?;
|
|
208
|
+
let name = self.0.viewer_elem.get_attribute("theme");
|
|
209
|
+
let index = name
|
|
210
|
+
.and_then(|x| themes.iter().position(|y| y == &x))
|
|
211
|
+
.or(if !themes.is_empty() { Some(0) } else { None });
|
|
212
|
+
|
|
213
|
+
Ok((themes, index))
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/// Returns the currently applied theme, or the default theme if no theme
|
|
217
|
+
/// has been set and themes are detected in the `document`, or `None` if
|
|
218
|
+
/// no themes are available.
|
|
219
|
+
pub async fn get_selected_theme_name(&self) -> Option<String> {
|
|
220
|
+
let (themes, index) = self.get_selected_theme_config().await.ok()?;
|
|
221
|
+
index.and_then(|x| themes.get(x).cloned())
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
fn set_theme_attribute(&self, theme: Option<&str>) -> ApiResult<()> {
|
|
225
|
+
if let Some(theme) = theme {
|
|
226
|
+
Ok(self.0.viewer_elem.set_attribute("theme", theme)?)
|
|
227
|
+
} else {
|
|
228
|
+
Ok(self.0.viewer_elem.remove_attribute("theme")?)
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
pub async fn reset_theme(&self) -> ApiResult<()> {
|
|
233
|
+
let themes = self.get_available_themes().await?;
|
|
234
|
+
let default_theme = themes.first().map(|x| x.as_str());
|
|
235
|
+
self.set_theme_name(default_theme).await?;
|
|
236
|
+
Ok(())
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/// Set the theme by name, or `None` for the default theme.
|
|
240
|
+
///
|
|
241
|
+
/// # Returns
|
|
242
|
+
/// A `bool` indicating whether the internal state changed.
|
|
243
|
+
pub async fn set_theme_name(&self, theme: Option<&str>) -> ApiResult<bool> {
|
|
244
|
+
let (themes, selected) = self.get_selected_theme_config().await?;
|
|
245
|
+
if let Some(x) = selected
|
|
246
|
+
&& themes.get(x).map(|x| x.as_str()) == theme
|
|
247
|
+
{
|
|
248
|
+
return Ok(false);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
let index = if let Some(theme) = theme {
|
|
252
|
+
self.set_theme_attribute(Some(theme))?;
|
|
253
|
+
themes.iter().position(|x| x == theme)
|
|
254
|
+
} else if !themes.is_empty() {
|
|
255
|
+
self.set_theme_attribute(themes.first().map(|x| x.as_str()))?;
|
|
256
|
+
Some(0)
|
|
257
|
+
} else {
|
|
258
|
+
self.set_theme_attribute(None)?;
|
|
259
|
+
None
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
self.theme_config_updated.emit((themes, index));
|
|
263
|
+
Ok(true)
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/// Returns an owned copy of the curent column configuration map.
|
|
267
|
+
pub fn all_columns_configs(&self) -> ColumnConfigMap {
|
|
268
|
+
self.columns_config.borrow().clone()
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
pub fn reset_columns_configs(&self) {
|
|
272
|
+
*self.columns_config.borrow_mut() = ColumnConfigMap::new();
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/// Gets a clone of the ColumnConfig for the given column name.
|
|
276
|
+
pub fn get_columns_config(&self, column_name: &str) -> Option<ColumnConfigValues> {
|
|
277
|
+
self.columns_config.borrow().get(column_name).cloned()
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/// Updates the entire column config struct. (like from a restore() call)
|
|
281
|
+
pub fn update_columns_configs(&self, update: ColumnConfigUpdate) {
|
|
282
|
+
match update {
|
|
283
|
+
crate::config::OptionalUpdate::SetDefault => {
|
|
284
|
+
let mut config = self.columns_config.borrow_mut();
|
|
285
|
+
*config = HashMap::default()
|
|
286
|
+
},
|
|
287
|
+
crate::config::OptionalUpdate::Missing => {},
|
|
288
|
+
crate::config::OptionalUpdate::Update(update) => {
|
|
289
|
+
for (col_name, new_config) in update.into_iter() {
|
|
290
|
+
self.columns_config
|
|
291
|
+
.borrow_mut()
|
|
292
|
+
.insert(col_name, new_config);
|
|
293
|
+
}
|
|
294
|
+
},
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
pub fn update_columns_config_value(
|
|
299
|
+
&self,
|
|
300
|
+
column_name: String,
|
|
301
|
+
update: ColumnConfigValueUpdate,
|
|
302
|
+
) {
|
|
303
|
+
let mut config = self.columns_config.borrow_mut();
|
|
304
|
+
let value = config.remove(&column_name).unwrap_or_default();
|
|
305
|
+
let update = value.update(update);
|
|
306
|
+
if !update.is_empty() {
|
|
307
|
+
config.insert(column_name, update);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
macro_rules! iter_index {
|
|
313
|
+
($x:expr) => {
|
|
314
|
+
(0..$x.length()).map(|x| $x.item(x))
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
fn fill_rule_theme_names(
|
|
319
|
+
themes: &mut Vec<String>,
|
|
320
|
+
rule: &Option<CssRule>,
|
|
321
|
+
elem: &HtmlElement,
|
|
322
|
+
) -> ApiResult<()> {
|
|
323
|
+
if let Some(rule) = rule.as_ref().into_apierror()?.dyn_ref::<CssStyleRule>() {
|
|
324
|
+
let txt = rule.selector_text();
|
|
325
|
+
if elem.matches(&txt)? {
|
|
326
|
+
let style = rule.style();
|
|
327
|
+
let x = (0..style.length()).map(|x| style.item(x));
|
|
328
|
+
for property in x {
|
|
329
|
+
if property == "--theme-name" {
|
|
330
|
+
let name = style.get_property_value("--theme-name")?;
|
|
331
|
+
let trimmed = name.trim();
|
|
332
|
+
themes.push(trimmed[1..trimmed.len() - 1].to_owned());
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
Ok(())
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
fn fill_sheet_theme_names(
|
|
342
|
+
themes: &mut Vec<String>,
|
|
343
|
+
sheet: &Option<StyleSheet>,
|
|
344
|
+
elem: &HtmlElement,
|
|
345
|
+
) -> ApiResult<()> {
|
|
346
|
+
let sheet = sheet
|
|
347
|
+
.as_ref()
|
|
348
|
+
.into_apierror()?
|
|
349
|
+
.unchecked_ref::<CssStyleSheet>();
|
|
350
|
+
|
|
351
|
+
if let Ok(rules) = sheet.css_rules() {
|
|
352
|
+
for rule in iter_index!(&rules) {
|
|
353
|
+
fill_rule_theme_names(themes, &rule, elem).unwrap_or_default();
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
Ok(())
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/// Search the document's `styleSheets` for rules which apply to `elem` and
|
|
361
|
+
/// provide the `--theme-name` CSS custom property.
|
|
362
|
+
fn get_theme_names(elem: &HtmlElement) -> Result<Vec<String>, JsValue> {
|
|
363
|
+
let sheets = global::document().style_sheets();
|
|
364
|
+
let mut themes: Vec<String> = vec![];
|
|
365
|
+
for sheet in iter_index!(sheets) {
|
|
366
|
+
fill_sheet_theme_names(&mut themes, &sheet, elem)?;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
Ok(themes)
|
|
370
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
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::future::Future;
|
|
14
|
+
|
|
15
|
+
use perspective_js::utils::ApiResult;
|
|
16
|
+
use wasm_bindgen::JsCast;
|
|
17
|
+
use web_sys::*;
|
|
18
|
+
|
|
19
|
+
use crate::js::plugin::JsPerspectiveViewerPlugin;
|
|
20
|
+
|
|
21
|
+
/// Given an async `task` which draws `plugin`, activates the plugin in stages
|
|
22
|
+
/// to prevent screen shearing. First, and plugin is appended ot the DOM with
|
|
23
|
+
/// opacity 0, then rendered. After rendering, the previous plugin is removed
|
|
24
|
+
/// and the opacity is set back to 1.
|
|
25
|
+
///
|
|
26
|
+
/// # Arguments
|
|
27
|
+
/// - `viewer` the root `<perspective-viewer>` element.
|
|
28
|
+
/// - `plugin` the plugin custom element.
|
|
29
|
+
/// - `task` an async task which renders the plugin.
|
|
30
|
+
pub async fn activate_plugin<T>(
|
|
31
|
+
viewer: &HtmlElement,
|
|
32
|
+
plugin: &JsPerspectiveViewerPlugin,
|
|
33
|
+
task: impl Future<Output = ApiResult<T>>,
|
|
34
|
+
) -> ApiResult<T> {
|
|
35
|
+
let html_plugin = plugin.unchecked_ref::<HtmlElement>();
|
|
36
|
+
if html_plugin.parent_node().is_none() {
|
|
37
|
+
html_plugin.style().set_property("opacity", "0")?;
|
|
38
|
+
viewer.append_child(html_plugin)?;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
let result = task.await;
|
|
42
|
+
let first_child = viewer.children().item(0).unwrap();
|
|
43
|
+
if first_child != *plugin.unchecked_ref::<Element>() {
|
|
44
|
+
viewer.remove_child(&first_child)?;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
html_plugin.style().set_property("opacity", "1")?;
|
|
48
|
+
result
|
|
49
|
+
}
|
|
@@ -0,0 +1,200 @@
|
|
|
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 perspective_js::utils::ApiError;
|
|
14
|
+
|
|
15
|
+
use crate::js::plugin::*;
|
|
16
|
+
|
|
17
|
+
pub async fn get_row_and_col_limits(
|
|
18
|
+
view: &perspective_client::View,
|
|
19
|
+
plugin_metadata: &ViewConfigRequirements,
|
|
20
|
+
) -> Result<(usize, usize, Option<usize>, Option<usize>), ApiError> {
|
|
21
|
+
let dimensions = view.dimensions().await?;
|
|
22
|
+
let num_cols = dimensions.num_view_columns as usize;
|
|
23
|
+
let num_rows = dimensions.num_view_rows as usize;
|
|
24
|
+
match (plugin_metadata.max_columns, plugin_metadata.render_warning) {
|
|
25
|
+
(Some(_), false) => Ok((num_cols, num_rows, None, None)),
|
|
26
|
+
(max_columns, _) => {
|
|
27
|
+
let schema = view.schema().await?;
|
|
28
|
+
let keys = schema.keys();
|
|
29
|
+
let num_schema_columns = std::cmp::max(1, keys.len() as usize);
|
|
30
|
+
let max_cols = max_columns.and_then(|max_columns| {
|
|
31
|
+
let column_group_diff = max_columns % num_schema_columns;
|
|
32
|
+
let column_limit = max_columns + column_group_diff;
|
|
33
|
+
if column_limit < num_cols {
|
|
34
|
+
Some(column_limit)
|
|
35
|
+
} else {
|
|
36
|
+
None
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
let max_rows = plugin_metadata.max_cells.map(|max_cells| {
|
|
41
|
+
match max_cols {
|
|
42
|
+
Some(max_cols) => max_cells as f64 / max_cols as f64,
|
|
43
|
+
None => max_cells as f64 / num_cols as f64,
|
|
44
|
+
}
|
|
45
|
+
.ceil() as usize
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
Ok((num_cols, num_rows, max_cols, max_rows))
|
|
49
|
+
},
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// #[cfg(test)]
|
|
54
|
+
// mod tests {
|
|
55
|
+
// use super::*;
|
|
56
|
+
|
|
57
|
+
// fn closure_helper<T>(x: T) -> Closure<dyn Fn(JsValue) -> js_sys::Promise>
|
|
58
|
+
// where
|
|
59
|
+
// T: Into<JsValue>,
|
|
60
|
+
// {
|
|
61
|
+
// let val = x.into();
|
|
62
|
+
// Closure::new(move |_: JsValue| {
|
|
63
|
+
// clone!(val);
|
|
64
|
+
// future_to_promise(async move { Ok(val.clone()) })
|
|
65
|
+
// })
|
|
66
|
+
// }
|
|
67
|
+
|
|
68
|
+
// #[wasm_bindgen_test]
|
|
69
|
+
// pub async fn test_emtpy_schema_no_columns() {
|
|
70
|
+
// let closure = closure_helper(0);
|
|
71
|
+
// let closure2 = closure_helper(json!({}));
|
|
72
|
+
// let view = json!({
|
|
73
|
+
// "num_columns": closure.as_ref().unchecked_ref::<JsValue>(),
|
|
74
|
+
// "num_rows": closure.as_ref().unchecked_ref::<JsValue>(),
|
|
75
|
+
// "schema": closure2.as_ref().unchecked_ref::<JsValue>()
|
|
76
|
+
// })
|
|
77
|
+
// .unchecked_into::<JsPerspectiveView>();
|
|
78
|
+
|
|
79
|
+
// let reqs = ViewConfigRequirements {
|
|
80
|
+
// render_warning: true,
|
|
81
|
+
// ..ViewConfigRequirements::default()
|
|
82
|
+
// };
|
|
83
|
+
// let (_, _, max_cols, max_rows) = get_row_and_col_limits(&view,
|
|
84
|
+
// &reqs).await.unwrap(); assert_eq!(max_cols, None);
|
|
85
|
+
// assert_eq!(max_rows, None);
|
|
86
|
+
// }
|
|
87
|
+
|
|
88
|
+
// #[wasm_bindgen_test]
|
|
89
|
+
// pub async fn test_columns_do_not_exceed_max_columns() {
|
|
90
|
+
// let closure = closure_helper(1);
|
|
91
|
+
// let closure2 = closure_helper(0);
|
|
92
|
+
// let closure3 = closure_helper(json!({}));
|
|
93
|
+
// let view = json!({
|
|
94
|
+
// "num_columns": closure.as_ref().unchecked_ref::<JsValue>(),
|
|
95
|
+
// "num_rows": closure2.as_ref().unchecked_ref::<JsValue>(),
|
|
96
|
+
// "schema": closure3.as_ref().unchecked_ref::<JsValue>()
|
|
97
|
+
// })
|
|
98
|
+
// .unchecked_into::<JsPerspectiveView>();
|
|
99
|
+
|
|
100
|
+
// let reqs = ViewConfigRequirements {
|
|
101
|
+
// max_columns: Some(2),
|
|
102
|
+
// render_warning: true,
|
|
103
|
+
// ..ViewConfigRequirements::default()
|
|
104
|
+
// };
|
|
105
|
+
|
|
106
|
+
// let (_, _, max_cols, max_rows) = get_row_and_col_limits(&view,
|
|
107
|
+
// &reqs).await.unwrap(); assert_eq!(max_cols, None);
|
|
108
|
+
// assert_eq!(max_rows, None);
|
|
109
|
+
// }
|
|
110
|
+
|
|
111
|
+
// #[wasm_bindgen_test]
|
|
112
|
+
// pub async fn test_columns_exceed_max_columns() {
|
|
113
|
+
// let closure = closure_helper(2);
|
|
114
|
+
// let closure2 = closure_helper(0);
|
|
115
|
+
// let closure3 = closure_helper(json!({}));
|
|
116
|
+
// let view = json!({
|
|
117
|
+
// "num_columns": closure.as_ref().unchecked_ref::<JsValue>(),
|
|
118
|
+
// "num_rows": closure2.as_ref().unchecked_ref::<JsValue>(),
|
|
119
|
+
// "schema": closure3.as_ref().unchecked_ref::<JsValue>()
|
|
120
|
+
// })
|
|
121
|
+
// .unchecked_into::<JsPerspectiveView>();
|
|
122
|
+
|
|
123
|
+
// let reqs = ViewConfigRequirements {
|
|
124
|
+
// max_columns: Some(1),
|
|
125
|
+
// render_warning: true,
|
|
126
|
+
// ..ViewConfigRequirements::default()
|
|
127
|
+
// };
|
|
128
|
+
// let (_, _, max_cols, max_rows) = get_row_and_col_limits(&view,
|
|
129
|
+
// &reqs).await.unwrap(); assert_eq!(max_cols, Some(1));
|
|
130
|
+
// assert_eq!(max_rows, None);
|
|
131
|
+
// }
|
|
132
|
+
|
|
133
|
+
// #[wasm_bindgen_test]
|
|
134
|
+
// pub async fn test_when_schema_columns_are_present() {
|
|
135
|
+
// let closure = closure_helper(100);
|
|
136
|
+
// let closure2 = closure_helper(0);
|
|
137
|
+
// let closure3 = closure_helper(json!({"x": "string", "y": "string"}));
|
|
138
|
+
// let view = json!({
|
|
139
|
+
// "num_columns": closure.as_ref().unchecked_ref::<JsValue>(),
|
|
140
|
+
// "num_rows": closure2.as_ref().unchecked_ref::<JsValue>(),
|
|
141
|
+
// "schema": closure3.as_ref().unchecked_ref::<JsValue>()
|
|
142
|
+
// })
|
|
143
|
+
// .unchecked_into::<JsPerspectiveView>();
|
|
144
|
+
|
|
145
|
+
// let reqs = ViewConfigRequirements {
|
|
146
|
+
// max_columns: Some(3),
|
|
147
|
+
// render_warning: true,
|
|
148
|
+
// ..ViewConfigRequirements::default()
|
|
149
|
+
// };
|
|
150
|
+
// let (_, _, max_cols, max_rows) = get_row_and_col_limits(&view,
|
|
151
|
+
// &reqs).await.unwrap(); assert_eq!(max_cols, Some(4));
|
|
152
|
+
// assert_eq!(max_rows, None);
|
|
153
|
+
// }
|
|
154
|
+
|
|
155
|
+
// #[wasm_bindgen_test]
|
|
156
|
+
// pub async fn test_when_max_cells_exits() {
|
|
157
|
+
// let closure = closure_helper(1);
|
|
158
|
+
// let closure2 = closure_helper(0);
|
|
159
|
+
// let closure3 = closure_helper(json!({}));
|
|
160
|
+
// let view = json!({
|
|
161
|
+
// "num_columns": closure.as_ref().unchecked_ref::<JsValue>(),
|
|
162
|
+
// "num_rows": closure2.as_ref().unchecked_ref::<JsValue>(),
|
|
163
|
+
// "schema": closure3.as_ref().unchecked_ref::<JsValue>()
|
|
164
|
+
// })
|
|
165
|
+
// .unchecked_into::<JsPerspectiveView>();
|
|
166
|
+
|
|
167
|
+
// let reqs = ViewConfigRequirements {
|
|
168
|
+
// max_cells: Some(2),
|
|
169
|
+
// render_warning: true,
|
|
170
|
+
// ..ViewConfigRequirements::default()
|
|
171
|
+
// };
|
|
172
|
+
// let (_, _, max_cols, max_rows) = get_row_and_col_limits(&view,
|
|
173
|
+
// &reqs).await.unwrap(); assert_eq!(max_cols, None);
|
|
174
|
+
// assert_eq!(max_rows, Some(2));
|
|
175
|
+
// }
|
|
176
|
+
|
|
177
|
+
// #[wasm_bindgen_test]
|
|
178
|
+
// pub async fn test_when_columns_exceed_max_columns_and_max_cells_exists()
|
|
179
|
+
// { let closure = closure_helper(4);
|
|
180
|
+
// let closure2 = closure_helper(0);
|
|
181
|
+
// let closure3 = closure_helper(json!({}));
|
|
182
|
+
// let view = json!({
|
|
183
|
+
// "num_columns": closure.as_ref().unchecked_ref::<JsValue>(),
|
|
184
|
+
// "num_rows": closure2.as_ref().unchecked_ref::<JsValue>(),
|
|
185
|
+
// "schema": closure3.as_ref().unchecked_ref::<JsValue>()
|
|
186
|
+
// })
|
|
187
|
+
// .unchecked_into::<JsPerspectiveView>();
|
|
188
|
+
|
|
189
|
+
// let reqs = ViewConfigRequirements {
|
|
190
|
+
// max_columns: Some(2),
|
|
191
|
+
// max_cells: Some(10),
|
|
192
|
+
// render_warning: true,
|
|
193
|
+
// ..ViewConfigRequirements::default()
|
|
194
|
+
// };
|
|
195
|
+
|
|
196
|
+
// let (_, _, max_cols, max_rows) = get_row_and_col_limits(&view,
|
|
197
|
+
// &reqs).await.unwrap(); assert_eq!(max_cols, Some(2));
|
|
198
|
+
// assert_eq!(max_rows, Some(5));
|
|
199
|
+
// }
|
|
200
|
+
// }
|
|
@@ -0,0 +1,45 @@
|
|
|
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::collections::HashMap;
|
|
14
|
+
|
|
15
|
+
use super::registry::*;
|
|
16
|
+
use crate::js::plugin::*;
|
|
17
|
+
|
|
18
|
+
#[derive(Default)]
|
|
19
|
+
pub struct PluginStore {
|
|
20
|
+
plugins: Option<Vec<JsPerspectiveViewerPlugin>>,
|
|
21
|
+
plugin_records: Option<HashMap<String, Vec<String>>>,
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
impl PluginStore {
|
|
25
|
+
fn init_lazy(&mut self) {
|
|
26
|
+
self.plugins = Some(PLUGIN_REGISTRY.create_plugins());
|
|
27
|
+
self.plugin_records = Some(PLUGIN_REGISTRY.available_plugin_names_by_category());
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
pub fn plugins(&mut self) -> &Vec<JsPerspectiveViewerPlugin> {
|
|
31
|
+
if self.plugins.is_none() {
|
|
32
|
+
self.init_lazy();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
self.plugins.as_ref().unwrap()
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
pub fn plugin_records(&mut self) -> &HashMap<String, Vec<String>> {
|
|
39
|
+
if self.plugins.is_none() {
|
|
40
|
+
self.init_lazy();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
self.plugin_records.as_ref().unwrap()
|
|
44
|
+
}
|
|
45
|
+
}
|