diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d030602af..4352c9b1df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,25 @@ +# [v3.1.8](https://github.com/finos/perspective/releases/tag/v3.1.8) + +_2 December 2024_ ([Full changelog](https://github.com/finos/perspective/compare/v3.1.7...v3.1.8)) + +Fixes + +- fix treemap rendering issue in firefox [#2813](https://github.com/finos/perspective/pull/2813) +- Allow for passthrough of event loop in psp webserver handlers [#2829](https://github.com/finos/perspective/pull/2829) +- Fix workspace dark theme, theme render bug [#2864](https://github.com/finos/perspective/pull/2864) + +# [v3.1.7](https://github.com/finos/perspective/releases/tag/v3.1.7) + +_30 November 2024_ ([Full changelog](https://github.com/finos/perspective/compare/v3.1.6...v3.1.7)) + +Fixes + +- Remove `workspace-layout-update` event debounce [#2862](https://github.com/finos/perspective/pull/2862) + +Misc + +- Add some information on JSON input formats [#2856](https://github.com/finos/perspective/pull/2856) + # [v3.1.6](https://github.com/finos/perspective/releases/tag/v3.1.6) _18 November 2024_ ([Full changelog](https://github.com/finos/perspective/compare/v3.1.5...v3.1.6)) diff --git a/Cargo.lock b/Cargo.lock index d292903a1b..ab3fdc80b7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1961,7 +1961,7 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "perspective" -version = "3.1.6" +version = "3.1.8" dependencies = [ "async-lock", "axum", @@ -2001,7 +2001,7 @@ dependencies = [ [[package]] name = "perspective-client" -version = "3.1.6" +version = "3.1.8" dependencies = [ "async-lock", "futures", @@ -2023,7 +2023,7 @@ dependencies = [ [[package]] name = "perspective-js" -version = "3.1.6" +version = "3.1.8" dependencies = [ "anyhow", "base64 0.13.1", @@ -2052,7 +2052,7 @@ dependencies = [ [[package]] name = "perspective-lint" -version = "3.1.6" +version = "3.1.8" dependencies = [ "glob", "yew-fmt", @@ -2072,7 +2072,7 @@ dependencies = [ [[package]] name = "perspective-python" -version = "3.1.6" +version = "3.1.8" dependencies = [ "async-lock", "cmake", @@ -2092,7 +2092,7 @@ dependencies = [ [[package]] name = "perspective-server" -version = "3.1.6" +version = "3.1.8" dependencies = [ "async-lock", "base64 0.22.1", @@ -2108,7 +2108,7 @@ dependencies = [ [[package]] name = "perspective-viewer" -version = "3.1.6" +version = "3.1.8" dependencies = [ "anyhow", "async-lock", diff --git a/cpp/perspective/package.json b/cpp/perspective/package.json index 8ed89e516e..05310e3c48 100644 --- a/cpp/perspective/package.json +++ b/cpp/perspective/package.json @@ -3,7 +3,7 @@ "private": true, "author": "The Perspective Authors", "license": "Apache-2.0", - "version": "3.1.6", + "version": "3.1.8", "main": "./dist/esm/perspective.cpp.js", "files": [ "dist/esm/**/*", @@ -13,4 +13,4 @@ "build": "node ../../tools/perspective-scripts/run_emsdk.mjs node ./build.js", "clean": "rimraf dist" } -} +} \ No newline at end of file diff --git a/docs/package.json b/docs/package.json index b000c643ae..deb6641c92 100644 --- a/docs/package.json +++ b/docs/package.json @@ -1,6 +1,6 @@ { "name": "@finos/perspective-docs", - "version": "3.1.6", + "version": "3.1.8", "private": true, "scripts": { "build": "node build.js && docusaurus build", @@ -54,4 +54,4 @@ "last 1 safari version" ] } -} +} \ No newline at end of file diff --git a/examples/blocks/package.json b/examples/blocks/package.json index bad510f2b7..aa098a9058 100644 --- a/examples/blocks/package.json +++ b/examples/blocks/package.json @@ -1,7 +1,7 @@ { "name": "blocks", "private": true, - "version": "3.1.6", + "version": "3.1.8", "description": "A collection of simple client-side Perspective examples for `http://bl.ocks.org`.", "scripts": { "start": "mkdirp dist && node --experimental-modules server.mjs", @@ -22,4 +22,4 @@ "devDependencies": { "esbuild": "^0.14.54" } -} +} \ No newline at end of file diff --git a/examples/blocks/src/superstore/index.html b/examples/blocks/src/superstore/index.html index 32b1127c8b..28c0bcbe37 100644 --- a/examples/blocks/src/superstore/index.html +++ b/examples/blocks/src/superstore/index.html @@ -6,17 +6,12 @@ diff --git a/examples/blocks/src/superstore/index.json b/examples/blocks/src/superstore/index.json new file mode 100644 index 0000000000..78e3391b0d --- /dev/null +++ b/examples/blocks/src/superstore/index.json @@ -0,0 +1,169 @@ +index.html: 48 { + "sizes": [ + 1 + ], + "detail": { + "main": { + "type": "split-area", + "orientation": "horizontal", + "children": [ + { + "type": "tab-area", + "widgets": [ + "PERSPECTIVE_GENERATED_ID_0", + "PERSPECTIVE_GENERATED_ID_1" + ], + "currentIndex": 0 + }, + { + "type": "tab-area", + "widgets": [ + "PERSPECTIVE_GENERATED_ID_3" + ], + "currentIndex": 0 + }, + { + "type": "tab-area", + "widgets": [ + "PERSPECTIVE_GENERATED_ID_2" + ], + "currentIndex": 0 + } + ], + "sizes": [ + 0.3765432098765432, + 0.24691358024691357, + 0.3765432098765432 + ] + } + }, + "viewers": { + "PERSPECTIVE_GENERATED_ID_0": { + "version": "3.1.8", + "plugin": "Datagrid", + "plugin_config": { + "columns": {}, + "edit_mode": "READ_ONLY", + "scroll_lock": false + }, + "columns_config": {}, + "title": "Sales Report 2", + "group_by": [ + "Region", + "State" + ], + "split_by": [ + "Category", + "Sub-Category" + ], + "columns": [ + "Sales", + "Profit" + ], + "filter": [], + "sort": [], + "expressions": {}, + "aggregates": {}, + "table": "superstore", + "settings": false + }, + "PERSPECTIVE_GENERATED_ID_1": { + "version": "3.1.8", + "plugin": "Datagrid", + "plugin_config": { + "columns": {}, + "edit_mode": "READ_ONLY", + "scroll_lock": false + }, + "columns_config": {}, + "title": null, + "group_by": [], + "split_by": [], + "columns": [ + "Row ID", + "Order ID", + "Ship Mode", + "Customer ID", + "Customer Name", + "Segment", + "Country", + "City", + "State", + "Postal Code", + "Region", + "Product ID", + "Category", + "Sub-Category", + "Product Name", + "Sales", + "Quantity", + "Discount", + "Profit", + "Order Date", + "Ship Date" + ], + "filter": [], + "sort": [], + "expressions": {}, + "aggregates": {}, + "table": "superstore", + "settings": false + }, + "PERSPECTIVE_GENERATED_ID_3": { + "version": "3.1.8", + "plugin": "Datagrid", + "plugin_config": { + "columns": {}, + "edit_mode": "READ_ONLY", + "scroll_lock": false + }, + "columns_config": {}, + "title": "Sales Report 2 (*)", + "group_by": [ + "Region", + "State" + ], + "split_by": [ + "Category", + "Sub-Category" + ], + "columns": [ + "Sales", + "Profit" + ], + "filter": [], + "sort": [], + "expressions": {}, + "aggregates": {}, + "table": "superstore", + "settings": false + }, + "PERSPECTIVE_GENERATED_ID_2": { + "version": "3.1.8", + "plugin": "Treemap", + "plugin_config": {}, + "columns_config": {}, + "title": "Sales Report (by State)", + "group_by": [ + "State" + ], + "split_by": [], + "columns": [ + "Sales", + "Profit", + null + ], + "filter": [], + "sort": [ + [ + "Profit", + "desc" + ] + ], + "expressions": {}, + "aggregates": {}, + "table": "superstore", + "settings": false + } + } +} \ No newline at end of file diff --git a/examples/esbuild-example/package.json b/examples/esbuild-example/package.json index ad8f18e1de..afa1cce194 100644 --- a/examples/esbuild-example/package.json +++ b/examples/esbuild-example/package.json @@ -1,7 +1,7 @@ { "name": "esbuild-example", "private": true, - "version": "3.1.6", + "version": "3.1.8", "description": "An esbuild example app built using `@finos/perspective-viewer`.", "scripts": { "build": "node build.js", @@ -22,4 +22,4 @@ "esbuild": "^0.14.54", "http-server": "^14.1.1" } -} +} \ No newline at end of file diff --git a/examples/esbuild-remote/package.json b/examples/esbuild-remote/package.json index c36cea0f4a..b6fb40ece0 100644 --- a/examples/esbuild-remote/package.json +++ b/examples/esbuild-remote/package.json @@ -1,7 +1,7 @@ { "name": "esbuild-remote", "private": true, - "version": "3.1.6", + "version": "3.1.8", "description": "An example of 2 Perspectives, one client and one server, streaming via Apache Arrow.", "scripts": { "start": "node build.js && node server/index.mjs" @@ -20,4 +20,4 @@ "@finos/perspective-esbuild-plugin": "workspace:^", "esbuild": "^0.14.54" } -} +} \ No newline at end of file diff --git a/examples/python-aiohttp/package.json b/examples/python-aiohttp/package.json index 84c2056504..17fa352330 100644 --- a/examples/python-aiohttp/package.json +++ b/examples/python-aiohttp/package.json @@ -1,7 +1,7 @@ { "name": "python-aiohttp", "private": true, - "version": "3.1.6", + "version": "3.1.8", "description": "An example of editing a `perspective-python` server from the browser.", "scripts": { "start": "PYTHONPATH=../../python/perspective python3 server.py" @@ -21,4 +21,4 @@ "npm-run-all": "^4.1.3", "rimraf": "^6" } -} +} \ No newline at end of file diff --git a/examples/python-starlette/package.json b/examples/python-starlette/package.json index daf4804274..ecdea23369 100644 --- a/examples/python-starlette/package.json +++ b/examples/python-starlette/package.json @@ -1,7 +1,7 @@ { "name": "python-starlette", "private": true, - "version": "3.1.6", + "version": "3.1.8", "description": "An example of editing a `perspective-python` server from the browser.", "scripts": { "start": "PYTHONPATH=../../python/perspective python3 server.py" @@ -21,4 +21,4 @@ "npm-run-all": "^4.1.3", "rimraf": "^6" } -} +} \ No newline at end of file diff --git a/examples/python-starlette/server.py b/examples/python-starlette/server.py index b1bc6f602b..f1d3baa0a0 100644 --- a/examples/python-starlette/server.py +++ b/examples/python-starlette/server.py @@ -18,7 +18,6 @@ import uvicorn from fastapi import FastAPI, WebSocket -from fastapi.middleware.cors import CORSMiddleware from starlette.responses import FileResponse from starlette.staticfiles import StaticFiles @@ -64,21 +63,12 @@ async def websocket_handler(websocket: WebSocket): ) await handler.run() - # static_html_files = StaticFiles(directory="../python-tornado", html=True) static_html_files = StaticFiles(directory="../python-tornado", html=True) app = FastAPI() app.add_api_websocket_route("/websocket", websocket_handler) app.get("/node_modules/{rest_of_path:path}")(static_node_modules_handler) app.mount("/", static_html_files) - - app.add_middleware( - CORSMiddleware, - allow_origins=["*"], - allow_credentials=True, - allow_methods=["*"], - allow_headers=["*"], - ) return app diff --git a/examples/python-tornado-streaming/package.json b/examples/python-tornado-streaming/package.json index d525a02e45..4e6c9dd7d6 100644 --- a/examples/python-tornado-streaming/package.json +++ b/examples/python-tornado-streaming/package.json @@ -1,7 +1,7 @@ { "name": "python-tornado-streaming", "private": true, - "version": "3.1.6", + "version": "3.1.8", "description": "An example of streaming a `perspective-python` server to the browser.", "scripts": { "start": "PYTHONPATH=../../python/perspective python3 server.py" @@ -21,4 +21,4 @@ "npm-run-all": "^4.1.3", "rimraf": "^6" } -} +} \ No newline at end of file diff --git a/examples/python-tornado/package.json b/examples/python-tornado/package.json index 97b1d38c8c..09465cafb5 100644 --- a/examples/python-tornado/package.json +++ b/examples/python-tornado/package.json @@ -1,7 +1,7 @@ { "name": "python-tornado", "private": true, - "version": "3.1.6", + "version": "3.1.8", "description": "An example of editing a `perspective-python` server from the browser.", "scripts": { "start": "PYTHONPATH=../../python/perspective python3 server.py" @@ -21,4 +21,4 @@ "npm-run-all": "^4.1.3", "rimraf": "^6" } -} +} \ No newline at end of file diff --git a/examples/react-example/package.json b/examples/react-example/package.json index 066f3fdc79..dd5373d555 100644 --- a/examples/react-example/package.json +++ b/examples/react-example/package.json @@ -1,7 +1,7 @@ { "name": "react-example", "private": true, - "version": "3.1.6", + "version": "3.1.8", "description": "An example app built using `@finos/perspective-viewer`.", "scripts": { "start": "webpack serve --open", @@ -24,4 +24,4 @@ "source-map-loader": "^0.2.4", "ts-loader": "^6.2.1" } -} +} \ No newline at end of file diff --git a/examples/rust-axum/Cargo.toml b/examples/rust-axum/Cargo.toml index de91ddd56a..08fc9cf9ee 100644 --- a/examples/rust-axum/Cargo.toml +++ b/examples/rust-axum/Cargo.toml @@ -17,7 +17,7 @@ edition = "2021" publish = false [dependencies] -perspective = { version = "3.1.6", path = "../../rust/perspective" } +perspective = { version = "3.1.8", path = "../../rust/perspective" } axum = { version = ">=0.7,<2", features = ["ws"] } futures = "0.3" tokio = { version = "1.0", features = ["full"] } diff --git a/examples/rust-axum/package.json b/examples/rust-axum/package.json index f9f112619d..9bb11d7d46 100644 --- a/examples/rust-axum/package.json +++ b/examples/rust-axum/package.json @@ -1,7 +1,7 @@ { "name": "rust-axum", "private": true, - "version": "3.1.6", + "version": "3.1.8", "description": "An example of a Rust/Axum virtual Perspective server", "scripts": { "start": "cargo run" @@ -10,4 +10,4 @@ "license": "Apache-2.0", "dependencies": {}, "devDependencies": {} -} +} \ No newline at end of file diff --git a/examples/webpack-example/package.json b/examples/webpack-example/package.json index 958a12fb97..78f682c54a 100644 --- a/examples/webpack-example/package.json +++ b/examples/webpack-example/package.json @@ -1,7 +1,7 @@ { "name": "webpack-example", "private": true, - "version": "3.1.6", + "version": "3.1.8", "description": "An example app built using `@finos/perspective-viewer`.", "scripts": { "webpack_build": "webpack", @@ -24,4 +24,4 @@ "webpack": "^5.14.0", "webpack-cli": "^4.7.0" } -} +} \ No newline at end of file diff --git a/examples/workspace/package.json b/examples/workspace/package.json index 6d9e5e4409..462ec5d443 100644 --- a/examples/workspace/package.json +++ b/examples/workspace/package.json @@ -1,7 +1,7 @@ { "name": "workspace", "private": true, - "version": "3.1.6", + "version": "3.1.8", "description": "An example app built using `@finos/perspective-workspace`.", "scripts": { "start": "webpack serve --open", @@ -22,4 +22,4 @@ "npm-run-all": "^4.1.3", "rimraf": "^6" } -} +} \ No newline at end of file diff --git a/package.json b/package.json index c65e0a3853..e61823b8b6 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "type": "git", "url": "https://github.com/finos/perspective" }, - "version": "3.1.6", + "version": "3.1.8", "changelog": { "labels": { "enhancement": "Added", @@ -107,4 +107,4 @@ "version": "node tools/perspective-scripts/version.mjs", "jlab_link": "pip3 install ./python/perspective --no-build-isolation" } -} +} \ No newline at end of file diff --git a/packages/perspective-cli/package.json b/packages/perspective-cli/package.json index c6465aec1d..7e4fbe2378 100644 --- a/packages/perspective-cli/package.json +++ b/packages/perspective-cli/package.json @@ -1,6 +1,6 @@ { "name": "@finos/perspective-cli", - "version": "3.1.6", + "version": "3.1.8", "description": "Perspective.js CLI", "main": "src/js/index.js", "publishConfig": { @@ -34,4 +34,4 @@ "commander": "^2.19.0", "puppeteer": "^23" } -} +} \ No newline at end of file diff --git a/packages/perspective-esbuild-plugin/package.json b/packages/perspective-esbuild-plugin/package.json index 98195e25c1..288a75e894 100644 --- a/packages/perspective-esbuild-plugin/package.json +++ b/packages/perspective-esbuild-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@finos/perspective-esbuild-plugin", - "version": "3.1.6", + "version": "3.1.8", "description": "esbuild plugin for Perspective", "author": "", "license": "Apache-2.0", @@ -20,4 +20,4 @@ "node-fetch": "^2.6.7", "tar": "^6.1.11" } -} +} \ No newline at end of file diff --git a/packages/perspective-jupyterlab/package.json b/packages/perspective-jupyterlab/package.json index 20926f1684..66a6293123 100644 --- a/packages/perspective-jupyterlab/package.json +++ b/packages/perspective-jupyterlab/package.json @@ -1,6 +1,6 @@ { "name": "@finos/perspective-jupyterlab", - "version": "3.1.6", + "version": "3.1.8", "description": "A Jupyterlab extension for the Perspective library, designed to be used with perspective-python.", "files": [ "dist/**/*", @@ -73,4 +73,4 @@ } } } -} +} \ No newline at end of file diff --git a/packages/perspective-viewer-d3fc/package.json b/packages/perspective-viewer-d3fc/package.json index 039b328153..36c84bc8d4 100644 --- a/packages/perspective-viewer-d3fc/package.json +++ b/packages/perspective-viewer-d3fc/package.json @@ -1,6 +1,6 @@ { "name": "@finos/perspective-viewer-d3fc", - "version": "3.1.6", + "version": "3.1.8", "description": "Perspective.js D3FC Plugin", "unpkg": "./dist/cdn/perspective-viewer-d3fc.js", "jsdelivr": "./dist/cdn/perspective-viewer-d3fc.js", @@ -63,4 +63,4 @@ "@finos/perspective-test": "workspace:^", "@prospective.co/procss": "^0.1.16" } -} +} \ No newline at end of file diff --git a/packages/perspective-viewer-d3fc/src/ts/series/treemap/treemapSeries.ts b/packages/perspective-viewer-d3fc/src/ts/series/treemap/treemapSeries.ts index 8715c0cb23..1358ac0513 100644 --- a/packages/perspective-viewer-d3fc/src/ts/series/treemap/treemapSeries.ts +++ b/packages/perspective-viewer-d3fc/src/ts/series/treemap/treemapSeries.ts @@ -70,10 +70,10 @@ export function treemapSeries() { const rects = nodesMerge .select("rect") .attr("class", (d) => `treerect ${nodeLevelHelper(maxDepth, d)}`) - .style("x", (d) => d.x0) - .style("y", (d) => d.y0) - .style("width", (d) => calcWidth(d)) - .style("height", (d) => calcHeight(d)); + .style("x", (d) => `${d.x0}px`) + .style("y", (d) => `${d.y0}px`) + .style("width", (d) => `${calcWidth(d)}px`) + .style("height", (d) => `${calcHeight(d)}px`); rects.style("fill", (d) => { if (nodeLevelHelper(maxDepth, d) === nodeLevel.leaf) { diff --git a/packages/perspective-viewer-datagrid/package.json b/packages/perspective-viewer-datagrid/package.json index 9153b46a18..b4002e1f1a 100644 --- a/packages/perspective-viewer-datagrid/package.json +++ b/packages/perspective-viewer-datagrid/package.json @@ -1,6 +1,6 @@ { "name": "@finos/perspective-viewer-datagrid", - "version": "3.1.6", + "version": "3.1.8", "description": "Perspective datagrid plugin based on `regular-table`", "unpkg": "dist/cdn/perspective-viewer-datagrid.js", "jsdelivr": "dist/cdn/perspective-viewer-datagrid.js", @@ -40,4 +40,4 @@ "@finos/perspective-esbuild-plugin": "workspace:^", "@finos/perspective-test": "workspace:^" } -} +} \ No newline at end of file diff --git a/packages/perspective-viewer-datagrid/src/less/row-hover.less b/packages/perspective-viewer-datagrid/src/less/row-hover.less index 616130f6a3..8d60e95475 100644 --- a/packages/perspective-viewer-datagrid/src/less/row-hover.less +++ b/packages/perspective-viewer-datagrid/src/less/row-hover.less @@ -20,15 +20,12 @@ regular-table { tr:hover:after { border-color: var(--rt-hover--border-color, #c5c9d080) !important; background-color: transparent; - box-shadow: 0px 1px 0px var(--rt-hover--border-color, #c5c9d080), - 0px 3px 0px rgba(0, 0, 0, 0.05), 0px 5px 0px rgba(0, 0, 0, 0.01); + box-shadow: 0px 1px 0px var(--rt-hover--border-color, #c5c9d080); &.psp-menu-open { box-shadow: inset -2px 0px 0px var(--icon--color), inset 2px 0px 0px var(--icon--color), - 0px 1px 0px var(--rt-hover--border-color, #c5c9d080), - 0px 3px 0px rgba(0, 0, 0, 0.05), - 0px 5px 0px rgba(0, 0, 0, 0.01); + 0px 1px 0px var(--rt-hover--border-color, #c5c9d080); } } @@ -37,8 +34,7 @@ regular-table { box-shadow: inset -2px 0px 0px var(--icon--color), inset 2px 0px 0px var(--icon--color), inset 0px -2px 0px var(--icon--color), - 0px 1px 0px var(--rt-hover--border-color, #c5c9d080), - 0px 3px 0px rgba(0, 0, 0, 0.05), 0px 5px 0px rgba(0, 0, 0, 0.01); + 0px 1px 0px var(--rt-hover--border-color, #c5c9d080); } tr:hover diff --git a/packages/perspective-viewer-openlayers/package.json b/packages/perspective-viewer-openlayers/package.json index 8eaa78348e..ff9eca02b8 100644 --- a/packages/perspective-viewer-openlayers/package.json +++ b/packages/perspective-viewer-openlayers/package.json @@ -1,6 +1,6 @@ { "name": "@finos/perspective-viewer-openlayers", - "version": "3.1.6", + "version": "3.1.8", "unpkg": "dist/cdn/perspective-viewer-openlayers.js", "jsdelivr": "dist/cdn/perspective-viewer-openlayers.js", "exports": { @@ -37,4 +37,4 @@ "@finos/perspective-esbuild-plugin": "workspace:^", "@prospective.co/procss": "^0.1.16" } -} +} \ No newline at end of file diff --git a/packages/perspective-webpack-plugin/package.json b/packages/perspective-webpack-plugin/package.json index 9fa13b861b..91dc4625ae 100644 --- a/packages/perspective-webpack-plugin/package.json +++ b/packages/perspective-webpack-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@finos/perspective-webpack-plugin", - "version": "3.1.6", + "version": "3.1.8", "description": "Perspective.js Webpack Plugin", "main": "index.js", "publishConfig": { @@ -29,4 +29,4 @@ "@finos/perspective-viewer": "workspace:^", "webpack": "^5.60.0" } -} +} \ No newline at end of file diff --git a/packages/perspective-workspace/build.js b/packages/perspective-workspace/build.js index ad7d4375a5..d0b09858d5 100644 --- a/packages/perspective-workspace/build.js +++ b/packages/perspective-workspace/build.js @@ -154,6 +154,16 @@ async function build_all() { } await Promise.all(BUILD.map(build)).catch(() => process.exit(1)); + + try { + await $`npx tsc --project ./tsconfig.json`.stdio( + "inherit", + "inherit", + "inherit" + ); + } catch (e) { + process.exit(1); + } } build_all(); diff --git a/packages/perspective-workspace/package.json b/packages/perspective-workspace/package.json index 0be97e9540..0502185241 100644 --- a/packages/perspective-workspace/package.json +++ b/packages/perspective-workspace/package.json @@ -1,6 +1,6 @@ { "name": "@finos/perspective-workspace", - "version": "3.1.6", + "version": "3.1.8", "description": "Perspective Workspace", "files": [ "dist/**/*", @@ -51,4 +51,4 @@ "@types/lodash": "^4.17.12", "zx": "^8.1.9" } -} +} \ No newline at end of file diff --git a/packages/perspective-workspace/src/less/menu.less b/packages/perspective-workspace/src/less/menu.less index f184081323..d1b2fbd038 100644 --- a/packages/perspective-workspace/src/less/menu.less +++ b/packages/perspective-workspace/src/less/menu.less @@ -12,11 +12,15 @@ @import "@lumino/widgets/style/menu.css"; +:host { + position: absolute; +} + .lm-Menu { font-size: 12px; padding: 8px; - background: white; - color: #666; + background-color: var(--plugin--background); + color: var(--icon--color); border: 1px solid var(--inactive--color); border-radius: 3px; max-width: 350px; @@ -71,12 +75,12 @@ padding: 0 8px; } -.lm-Menu-item[data-type="separator"] > div { +.lm-Menu-item[data-type="separator"]>div { padding: 0; height: 9px; } -.lm-Menu-item[data-type="separator"] > div::after { +.lm-Menu-item[data-type="separator"]>div::after { content: ""; display: block; position: relative; @@ -121,4 +125,4 @@ .lm-mod-drag-image.lm-TabBar-tab { display: none; -} +} \ No newline at end of file diff --git a/packages/perspective-workspace/src/less/viewer.less b/packages/perspective-workspace/src/less/viewer.less index 7aa9897dac..ae4fdf8113 100644 --- a/packages/perspective-workspace/src/less/viewer.less +++ b/packages/perspective-workspace/src/less/viewer.less @@ -64,7 +64,7 @@ } } -perspective-viewer { +::slotted(perspective-viewer) { flex: 1; position: relative; display: block; @@ -76,10 +76,11 @@ perspective-viewer { overflow: visible !important; } -.lm-mod-override-cursor { +:host-context(.lm-mod-override-cursor) { cursor: grabbing !important; } -.lm-mod-override-cursor perspective-viewer > * { - pointer-events: none; +:host-context(.lm-mod-override-cursor) ::slotted(perspective-viewer), +.context-menu ::slotted(perspective-viewer) { + --override-content-pointer-events: none; } diff --git a/packages/perspective-workspace/src/less/workspace.less b/packages/perspective-workspace/src/less/workspace.less index 6cf4f4d217..7b8c283ce9 100644 --- a/packages/perspective-workspace/src/less/workspace.less +++ b/packages/perspective-workspace/src/less/workspace.less @@ -21,6 +21,8 @@ @import "@lumino/widgets/style/tabbar.css"; @import "@lumino/widgets/style/tabpanel.css"; +@import "./injected.less"; + :host { @import "./tabbar.less"; @import "./dockpanel.less"; @@ -28,8 +30,8 @@ background-color: hsl(210deg 18% 90%); - width: 100%; - height: 100%; + // width: 100%; + // height: 100%; .workspace { width: 100%; diff --git a/packages/perspective-workspace/src/themes/pro-dark.less b/packages/perspective-workspace/src/themes/pro-dark.less index ce2b459b64..dbadcdbfe2 100644 --- a/packages/perspective-workspace/src/themes/pro-dark.less +++ b/packages/perspective-workspace/src/themes/pro-dark.less @@ -22,9 +22,6 @@ perspective-indicator[theme="Pro Dark"] { --theme-name: "Pro Dark"; } -.lm-Menu { - @include perspective-viewer-pro-dark--colors; -} perspective-workspace perspective-viewer { --status-bar--height: 38px; @@ -62,7 +59,7 @@ perspective-viewer[theme="Pro Dark"].workspace-master-widget { --plugin--background: @grey800; } -.lm-Menu { +perspective-workspace-menu { font-family: "ui-monospace", "SFMono-Regular", "SF Mono", "Menlo", "Consolas", "Liberation Mono", monospace; font-weight: 300; @@ -119,9 +116,7 @@ perspective-viewer[theme="Pro Dark"].workspace-master-widget { --warning--color: #242526; --warning--background: var(--icon--color); - --select-arrow--background-image: var( - --select-arrow-light--background-image - ); + --select-arrow--background-image: var(--select-arrow-light--background-image); // Syntax --code-editor-symbol--color: white; @@ -130,4 +125,4 @@ perspective-viewer[theme="Pro Dark"].workspace-master-widget { --code-editor-comment--color: rgb(204, 120, 48); --code-editor-column--color: #e18ee1; // --code-editor-unknown--color: rgb(204, 120, 48); -} +} \ No newline at end of file diff --git a/packages/perspective-workspace/src/themes/pro.less b/packages/perspective-workspace/src/themes/pro.less index 6c8296ac47..90cf22ec2e 100644 --- a/packages/perspective-workspace/src/themes/pro.less +++ b/packages/perspective-workspace/src/themes/pro.less @@ -24,11 +24,6 @@ perspective-workspace { background-color: #dadada; } -.lm-Menu { - @include perspective-viewer-pro--colors; - background-color: #ffffff; -} - perspective-workspace perspective-viewer[settings] { --modal-panel--margin: -4px 0 -4px 0; --status-bar--border-radius: 6px 0 0 0; @@ -46,7 +41,9 @@ perspective-workspace perspective-viewer { perspective-viewer[theme="Pro Light"].workspace-master-widget { background-color: #f2f4f6; --plugin--background: #f2f4f6; + regular-table { + td, th { border-color: #e0e4e9; @@ -84,9 +81,9 @@ perspective-viewer[theme="Pro Light"].workspace-master-widget { --workspace-tabbar-tab--border-width: 1px 1px 0px 1px; } -.lm-Menu { +perspective-workspace-menu { font-family: "ui-monospace", "SFMono-Regular", "SF Mono", "Menlo", "Consolas", "Liberation Mono", monospace; font-weight: 300; color: #161616; -} +} \ No newline at end of file diff --git a/packages/perspective-workspace/src/ts/workspace/commands.ts b/packages/perspective-workspace/src/ts/workspace/commands.ts index 09cc6ea761..1459012460 100644 --- a/packages/perspective-workspace/src/ts/workspace/commands.ts +++ b/packages/perspective-workspace/src/ts/workspace/commands.ts @@ -20,6 +20,7 @@ import type { } from "@finos/perspective-viewer"; import type { PerspectiveWorkspace } from "./workspace"; +import { WorkspaceMenu } from "./menu"; export const createCommands = ( workspace: PerspectiveWorkspace, @@ -43,7 +44,10 @@ export const createCommands = ( workspace.get_context_menu()?.init_overlay?.(); menu.addEventListener("blur", () => { const context_menu = workspace.get_context_menu()!; - const signal = context_menu.aboutToClose as Signal; + const signal = context_menu.aboutToClose as Signal< + WorkspaceMenu, + any + >; signal.emit({}); }); }, @@ -83,8 +87,8 @@ export const createCommands = ( menu.addEventListener("blur", () => { ( workspace.get_context_menu()?.aboutToClose as - | Signal - | undefined + | Signal + | undefined )?.emit({}); }); }, @@ -230,11 +234,11 @@ export const createCommands = ( workspace.toggleMasterDetail( workspace.getWidgetByName(args.widget_name as string)! ), - isVisible: () => true, - // iconClass: (args) => - // args.widget.parent === workspace.dockpanel - // ? "menu-master" - // : "menu-detail", + isVisible: (args) => { + return !!workspace.getWidgetByName(args.widget_name as string) + ?._is_pivoted; + }, + label: (args) => { return workspace.getWidgetByName(args.widget_name as string)! .parent === workspace.get_dock_panel() @@ -255,7 +259,7 @@ export const createCommands = ( commands.addCommand("workspace:help", { // iconClass: "menu-close", - execute: () => {}, + execute: () => { }, label: "Shift+Click for Browser Menu", isEnabled: () => false, // mnemonic: 0, diff --git a/packages/perspective-workspace/src/ts/workspace/menu.ts b/packages/perspective-workspace/src/ts/workspace/menu.ts index b7d1b27c4a..ba6b79af35 100644 --- a/packages/perspective-workspace/src/ts/workspace/menu.ts +++ b/packages/perspective-workspace/src/ts/workspace/menu.ts @@ -10,10 +10,61 @@ // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃ // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ +import { ElementExt } from "@lumino/domutils"; +import { MessageLoop } from "@lumino/messaging"; import { h } from "@lumino/virtualdom"; -import { Menu } from "@lumino/widgets"; +import { Menu, Widget } from "@lumino/widgets"; -export class MenuRenderer extends Menu.Renderer { +export class WorkspaceMenu extends Menu { + private _host: ShadowRoot; + init_overlay?: () => void; + + constructor(host: ShadowRoot, options: Menu.IOptions) { + options.renderer = new MenuRenderer(); + super(options); + this._host = host; + (this as any)._openChildMenu = this._overrideOpenChildMenu.bind(this); + } + + open(x: number, y: number, options?: Menu.IOpenOptions) { + options ||= {}; + options.host = this._host as any as HTMLElement; + super.open(x, y, options); + } + + // Override this lumino private method because it will otherwise always + // attach to `document.body`. + private _overrideOpenChildMenu(activateFirst = false) { + const self = this as any; + let item = this.activeItem; + if (!item || item.type !== "submenu" || !item.submenu) { + self._closeChildMenu(); + return; + } + + let submenu = item.submenu; + if (submenu === self._childMenu) { + return; + } + + Menu.saveWindowData(); + self._closeChildMenu(); + self._childMenu = submenu; + self._childIndex = self._activeIndex; + (submenu as any)._parentMenu = this; + MessageLoop.sendMessage(this, Widget.Msg.UpdateRequest); + let itemNode = this.contentNode.children[self._activeIndex]; + openSubmenu(submenu, itemNode as HTMLElement, self._host); + if (activateFirst) { + submenu.activeIndex = -1; + submenu.activateNextItem(); + } + + submenu.activate(); + } +} + +class MenuRenderer extends Menu.Renderer { formatLabel(data: Menu.IRenderData) { let { label, mnemonic } = data.item; if (mnemonic < 0 || mnemonic >= label.length) { @@ -67,3 +118,72 @@ export class MenuRenderer extends Menu.Renderer { ); } } + +// Prevent submenus from attaching outside the Shadow DOM. +// Forked from [Lumino](https://github.com/jupyterlab/lumino/blob/main/packages/widgets/src/menu.ts). +// [License](https://github.com/jupyterlab/lumino/blob/main/LICENSE) +export function openSubmenu( + submenu: Menu, + itemNode: HTMLElement, + host: HTMLElement +): void { + const windowData = getWindowData(); + let px = windowData.pageXOffset; + let py = windowData.pageYOffset; + let cw = windowData.clientWidth; + let ch = windowData.clientHeight; + const hostData = (host as any).host.getBoundingClientRect(); + let hx = hostData.x; + let hy = hostData.y; + MessageLoop.sendMessage(submenu, Widget.Msg.UpdateRequest); + let maxHeight = ch; + let node = submenu.node; + let style = node.style; + style.opacity = "0"; + style.maxHeight = `${maxHeight}px`; + Widget.attach(submenu, host); + let { width, height } = node.getBoundingClientRect(); + let box = ElementExt.boxSizing(submenu.node); + let itemRect = itemNode.getBoundingClientRect(); + let x = itemRect.right - SUBMENU_OVERLAP - hx; + if (x + width > px + cw + hx) { + x = itemRect.left + SUBMENU_OVERLAP - width; + } + + let y = itemRect.top - box.borderTop - box.paddingTop - hy; + if (y + height > py + ch + hy) { + y = itemRect.bottom + box.borderBottom + box.paddingBottom - height; + } + + style.transform = `translate(${Math.max(0, x)}px, ${Math.max(0, y)}px`; + style.opacity = "1"; +} + +export const SUBMENU_OVERLAP = 3; + +let transientWindowDataCache: IWindowData | null = null; +let transientCacheCounter: number = 0; + +function getWindowData(): IWindowData { + if (transientCacheCounter > 0) { + transientCacheCounter--; + return transientWindowDataCache!; + } + return _getWindowData(); +} + +function _getWindowData(): IWindowData { + return { + pageXOffset: window.pageXOffset, + pageYOffset: window.pageYOffset, + clientWidth: document.documentElement.clientWidth, + clientHeight: document.documentElement.clientHeight, + }; +} + +interface IWindowData { + pageXOffset: number; + pageYOffset: number; + clientWidth: number; + clientHeight: number; +} \ No newline at end of file diff --git a/packages/perspective-workspace/src/ts/workspace/tabbar.ts b/packages/perspective-workspace/src/ts/workspace/tabbar.ts index 363b78c962..f43b26bf77 100644 --- a/packages/perspective-workspace/src/ts/workspace/tabbar.ts +++ b/packages/perspective-workspace/src/ts/workspace/tabbar.ts @@ -1,3 +1,4 @@ + // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ // ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃ // ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃ diff --git a/packages/perspective-workspace/src/ts/workspace/widget.ts b/packages/perspective-workspace/src/ts/workspace/widget.ts index 0f9e12e3ae..59a615370f 100644 --- a/packages/perspective-workspace/src/ts/workspace/widget.ts +++ b/packages/perspective-workspace/src/ts/workspace/widget.ts @@ -25,6 +25,7 @@ export class PerspectiveViewerWidget extends Widget { viewer: psp_viewer.HTMLPerspectiveViewerElement; _title: string; _is_table_loaded: boolean; + _is_pivoted: boolean; _restore_config?: () => Promise; task?: Promise; @@ -33,6 +34,7 @@ export class PerspectiveViewerWidget extends Widget { this.viewer = viewer; this._title = ""; this._is_table_loaded = false; + this._is_pivoted = false; } get name(): string { @@ -65,16 +67,7 @@ export class PerspectiveViewerWidget extends Widget { this.viewer.setAttribute("table", table); } - // if (selectable) { - // this.viewer.setAttribute("selectable", selectable); - // } - - // if (editable) { - // this.viewer.setAttribute("editable", editable); - // } - const restore_config = () => this.viewer.restore({ ...viewerConfig }); - if (this._is_table_loaded) { return restore_config(); } else { @@ -88,14 +81,8 @@ export class PerspectiveViewerWidget extends Widget { table: this.viewer.getAttribute("table"), }; - if (this.viewer.hasAttribute("selectable")) { - config.selectable = this.viewer.getAttribute("selectable"); - } - - if (this.viewer.hasAttribute("editable")) { - config.editable = this.viewer.getAttribute("editable"); - } - + delete config["theme"]; + delete config["settings"]; return config; } diff --git a/packages/perspective-workspace/src/ts/workspace/workspace.ts b/packages/perspective-workspace/src/ts/workspace/workspace.ts index e2d256d01d..e37dfd45ed 100644 --- a/packages/perspective-workspace/src/ts/workspace/workspace.ts +++ b/packages/perspective-workspace/src/ts/workspace/workspace.ts @@ -158,13 +158,13 @@ export class PerspectiveWorkspace extends SplitPanel { let detail = is_settings ? this._minimizedLayoutSlots : PerspectiveDockPanel.mapWidgets( - (widget) => - // this.getWidgetByName(widget)!.viewer.getAttribute("slot") - (widget as PerspectiveViewerWidget).viewer.getAttribute( - "slot" - ), - this.dockpanel.saveLayout() - ); + (widget) => + // this.getWidgetByName(widget)!.viewer.getAttribute("slot") + (widget as PerspectiveViewerWidget).viewer.getAttribute( + "slot" + ), + this.dockpanel.saveLayout() + ); const layout = { sizes: [...this.relativeSizes()], diff --git a/packages/perspective-workspace/test/js/restore.spec.js b/packages/perspective-workspace/test/js/restore.spec.js index 125abe83eb..a5d92caf7d 100644 --- a/packages/perspective-workspace/test/js/restore.spec.js +++ b/packages/perspective-workspace/test/js/restore.spec.js @@ -84,6 +84,7 @@ function tests(context, compare) { }, config); await page.evaluate(async () => { + const workspace = document.getElementById("workspace"); await workspace.flush(); }); diff --git a/rust/bundle/main.rs b/rust/bundle/main.rs index c57749678a..770338d01f 100644 --- a/rust/bundle/main.rs +++ b/rust/bundle/main.rs @@ -67,6 +67,7 @@ fn bindgen(outdir: &Path, artifact: &str, is_release: bool) { Bindgen::new() .web(true) .unwrap() + .keep_debug(!is_release) .input_path(input) .typescript(true) .out_name(&format!("{}.wasm", artifact.replace('_', "-"))) diff --git a/rust/lint/Cargo.toml b/rust/lint/Cargo.toml index 2d138b8efd..36b4e76597 100644 --- a/rust/lint/Cargo.toml +++ b/rust/lint/Cargo.toml @@ -13,7 +13,7 @@ [package] name = "perspective-lint" description = "A CLI utility to lint rust code" -version = "3.1.6" +version = "3.1.8" edition = "2021" publish = false diff --git a/rust/perspective-client/Cargo.toml b/rust/perspective-client/Cargo.toml index e8a9cb32dd..f1b58ac6c2 100644 --- a/rust/perspective-client/Cargo.toml +++ b/rust/perspective-client/Cargo.toml @@ -12,7 +12,7 @@ [package] name = "perspective-client" -version = "3.1.6" +version = "3.1.8" authors = ["Andrew Stein "] edition = "2021" description = "A data visualization and analytics component, especially well-suited for large and/or streaming datasets." diff --git a/rust/perspective-client/docs/table.md b/rust/perspective-client/docs/table.md index 2217a0abe6..06f8cc5c15 100644 --- a/rust/perspective-client/docs/table.md +++ b/rust/perspective-client/docs/table.md @@ -279,3 +279,35 @@ table.replace(df)
`limit` cannot be used in conjunction with `index`.
+ +# JSON Input Data + +Perspective supports many kinds of input data, including two formats of JSON +data: row-oriented and column-oriented data. + +## Row Oriented JSON + +Row-oriented JSON is in the form of a list of objects. Each object in the list +corresponds to a row in the table. For example: + +```json +[ + { "a": 86, "b": false, "c": "words" }, + { "a": 0, "b": true, "c": "" }, + { "a": 12345, "b": false, "c": "here" } +] +``` + +## Column Oriented JSON + +Column-Oriented JSON comes in the form of an object of lists. Each key of the +object is a column name, and each element of the list is the corresponding value +in the row. + +```json +{ + "a": [86, 0, 12345], + "b": [false, true, false], + "c": ["words", "", "here"] +} +``` diff --git a/rust/perspective-client/package.json b/rust/perspective-client/package.json index 2e2d0268f3..de19b0e3eb 100644 --- a/rust/perspective-client/package.json +++ b/rust/perspective-client/package.json @@ -1,6 +1,6 @@ { "name": "@finos/perspective-client", - "version": "3.1.6", + "version": "3.1.8", "description": "", "private": true, "repository": { @@ -15,4 +15,4 @@ "devDependencies": { "rimraf": "^6" } -} +} \ No newline at end of file diff --git a/rust/perspective-js/Cargo.toml b/rust/perspective-js/Cargo.toml index 7bf31adf8e..97ca80b799 100644 --- a/rust/perspective-js/Cargo.toml +++ b/rust/perspective-js/Cargo.toml @@ -12,7 +12,7 @@ [package] name = "perspective-js" -version = "3.1.6" +version = "3.1.8" authors = ["Andrew Stein "] edition = "2021" description = "A data visualization and analytics component, especially well-suited for large and/or streaming datasets." @@ -47,7 +47,7 @@ anyhow = "1.0.66" wasm-bindgen-test = "0.3.13" [dependencies] -perspective-client = { path = "../perspective-client", version = "3.1.6" } +perspective-client = { path = "../perspective-client", version = "3.1.8" } base64 = "0.13.0" chrono = "0.4" extend = "1.1.2" diff --git a/rust/perspective-js/package.json b/rust/perspective-js/package.json index 414b2883db..b9fe5c4c6b 100644 --- a/rust/perspective-js/package.json +++ b/rust/perspective-js/package.json @@ -1,6 +1,6 @@ { "name": "@finos/perspective", - "version": "3.1.6", + "version": "3.1.8", "description": "", "repository": { "type": "git", @@ -60,4 +60,4 @@ "underscore": "^1.13.6", "zx": "8.1.9" } -} +} \ No newline at end of file diff --git a/rust/perspective-python/Cargo.toml b/rust/perspective-python/Cargo.toml index 66d6055a21..6b4307d0e9 100644 --- a/rust/perspective-python/Cargo.toml +++ b/rust/perspective-python/Cargo.toml @@ -12,7 +12,7 @@ [package] name = "perspective-python" -version = "3.1.6" +version = "3.1.8" edition = "2021" description = "A data visualization and analytics component, especially well-suited for large and/or streaming datasets." repository = "https://github.com/finos/perspective" @@ -61,8 +61,8 @@ python-config-rs = "0.1.2" # NOTE: when building from the git repo, these perspective-* dependencies are # overridden with path dependencies in .cargo/config.toml. This is done to # support the sdist, which doesn't include these packages. -perspective-client = { version = "3.1.6" } -perspective-server = { version = "3.1.6" } +perspective-client = { version = "3.1.8" } +perspective-server = { version = "3.1.8" } async-lock = "2.5.0" pollster = "0.3.0" extend = "1.1.2" diff --git a/rust/perspective-python/package.json b/rust/perspective-python/package.json index 9ebea4c1cb..a1de56a971 100644 --- a/rust/perspective-python/package.json +++ b/rust/perspective-python/package.json @@ -1,6 +1,6 @@ { "name": "@finos/perspective-python", - "version": "3.1.6", + "version": "3.1.8", "description": "", "private": true, "repository": { @@ -24,4 +24,4 @@ "dependencies": { "@finos/perspective-jupyterlab": "workspace:*" } -} +} \ No newline at end of file diff --git a/rust/perspective-python/perspective/__init__.py b/rust/perspective-python/perspective/__init__.py index fec5959597..f408157acd 100644 --- a/rust/perspective-python/perspective/__init__.py +++ b/rust/perspective-python/perspective/__init__.py @@ -10,7 +10,7 @@ # ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃ # ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ -__version__ = "3.1.6" +__version__ = "3.1.8" __all__ = [ "_jupyter_labextension_paths", "Server", diff --git a/rust/perspective-python/perspective/handlers/aiohttp.py b/rust/perspective-python/perspective/handlers/aiohttp.py index 421889720d..f4d459a217 100644 --- a/rust/perspective-python/perspective/handlers/aiohttp.py +++ b/rust/perspective-python/perspective/handlers/aiohttp.py @@ -37,11 +37,12 @@ class PerspectiveAIOHTTPHandler(object): def __init__(self, **kwargs): self.server = kwargs.pop("perspective_server", perspective.GLOBAL_SERVER) self._request = kwargs.pop("request") + self._loop = kwargs.pop("loop", asyncio.get_event_loop()) super().__init__(**kwargs) async def run(self) -> web.WebSocketResponse: def inner(msg): - asyncio.get_running_loop().create_task(self._ws.send_bytes(msg)) + self._loop.create_task(self._ws.send_bytes(msg)) self.session = self.server.new_session(inner) try: diff --git a/rust/perspective-python/perspective/handlers/starlette.py b/rust/perspective-python/perspective/handlers/starlette.py index 4a12fd0194..963ba1c3ef 100644 --- a/rust/perspective-python/perspective/handlers/starlette.py +++ b/rust/perspective-python/perspective/handlers/starlette.py @@ -34,11 +34,12 @@ class PerspectiveStarletteHandler(object): def __init__(self, **kwargs): self._server = kwargs.pop("perspective_server", perspective.GLOBAL_SERVER) self._websocket = kwargs.pop("websocket") + self._loop = kwargs.pop("loop", asyncio.get_event_loop()) super().__init__(**kwargs) async def run(self) -> None: def inner(msg): - asyncio.get_running_loop().create_task(self._websocket.send_bytes(msg)) + self._loop.create_task(self._websocket.send_bytes(msg)) self.session = self._server.new_session(inner) diff --git a/rust/perspective-python/perspective/handlers/tornado.py b/rust/perspective-python/perspective/handlers/tornado.py index 4abae77c73..a2adf35f5b 100644 --- a/rust/perspective-python/perspective/handlers/tornado.py +++ b/rust/perspective-python/perspective/handlers/tornado.py @@ -45,8 +45,9 @@ class PerspectiveTornadoHandler(WebSocketHandler): def check_origin(self, origin): return True - def initialize(self, perspective_server=perspective.GLOBAL_SERVER): + def initialize(self, perspective_server=perspective.GLOBAL_SERVER, loop=None): self.server = perspective_server + self.loop = loop or IOLoop.current() def open(self): def inner(msg): @@ -63,4 +64,4 @@ def on_message(self, msg: bytes): return self.session.handle_request(msg) - IOLoop.current().call_later(0, self.session.poll) + self.loop.call_later(0, self.session.poll) diff --git a/rust/perspective-python/pyproject.toml b/rust/perspective-python/pyproject.toml index a32edf9fa9..ec591f2148 100644 --- a/rust/perspective-python/pyproject.toml +++ b/rust/perspective-python/pyproject.toml @@ -44,7 +44,7 @@ starlette = ["starlette<1"] [tool.maturin] module-name = "perspective" -data = "perspective_python-3.1.6.data" +data = "perspective_python-3.1.8.data" features = ["pyo3/extension-module"] include = [ { path = "perspective/*libpsp.so", format = "wheel" }, diff --git a/rust/perspective-server/Cargo.toml b/rust/perspective-server/Cargo.toml index 1138589cea..15e8b5581b 100644 --- a/rust/perspective-server/Cargo.toml +++ b/rust/perspective-server/Cargo.toml @@ -12,7 +12,7 @@ [package] name = "perspective-server" -version = "3.1.6" +version = "3.1.8" authors = ["Andrew Stein "] edition = "2021" description = "A data visualization and analytics component, especially well-suited for large and/or streaming datasets." @@ -47,7 +47,7 @@ shlex = "1.3.0" [dependencies] link-cplusplus = "1.0.9" -perspective-client = { version = "3.1.6", path = "../perspective-client" } +perspective-client = { version = "3.1.8", path = "../perspective-client" } async-lock = "2.5.0" tracing = { version = ">=0.1.36" } futures = "0.3" diff --git a/rust/perspective-viewer/Cargo.toml b/rust/perspective-viewer/Cargo.toml index 01ff8c9233..5178bd4d73 100644 --- a/rust/perspective-viewer/Cargo.toml +++ b/rust/perspective-viewer/Cargo.toml @@ -12,7 +12,7 @@ [package] name = "perspective-viewer" -version = "3.1.6" +version = "3.1.8" authors = ["Andrew Stein "] edition = "2021" description = "A data visualization and analytics component, especially well-suited for large and/or streaming datasets." @@ -46,8 +46,8 @@ anyhow = "1.0.66" wasm-bindgen-test = "0.3.13" [dependencies] -perspective-client = { path = "../perspective-client", version = "3.1.6" } -perspective-js = { path = "../perspective-js", version = "3.1.6" } +perspective-client = { path = "../perspective-client", version = "3.1.8" } +perspective-js = { path = "../perspective-js", version = "3.1.8" } # Provides async `Mutex` for locked sections such as `render` async-lock = "2.5.0" @@ -83,7 +83,7 @@ js-sys = "0.3.64" nom = "7.1.1" # MessagePack serialization -rmp-serde = "1.1.1" +rmp-serde = "1.3.0" # Serialization for tokens and JS APIs serde = { version = "1.0", features = ["derive"] } diff --git a/rust/perspective-viewer/package.json b/rust/perspective-viewer/package.json index 24cd17b3ff..70b681d91f 100644 --- a/rust/perspective-viewer/package.json +++ b/rust/perspective-viewer/package.json @@ -1,6 +1,6 @@ { "name": "@finos/perspective-viewer", - "version": "3.1.6", + "version": "3.1.8", "description": "The `` Custom Element, frontend for Perspective.js", "repository": { "type": "git", @@ -47,4 +47,4 @@ "@prospective.co/procss": "0.1.16", "zx": "8.1.9" } -} +} \ No newline at end of file diff --git a/rust/perspective-viewer/src/less/viewer.less b/rust/perspective-viewer/src/less/viewer.less index 75db84eec1..80a61259ce 100644 --- a/rust/perspective-viewer/src/less/viewer.less +++ b/rust/perspective-viewer/src/less/viewer.less @@ -14,6 +14,10 @@ --settings-panel-z-index: 10; } +::slotted(*) { + pointer-events: var(--override-content-pointer-events); +} + :host .sidebar_close_button { position: absolute; top: 0; diff --git a/rust/perspective-viewer/src/rust/config/viewer_config.rs b/rust/perspective-viewer/src/rust/config/viewer_config.rs index 4991f4ca48..66b7b73402 100644 --- a/rust/perspective-viewer/src/rust/config/viewer_config.rs +++ b/rust/perspective-viewer/src/rust/config/viewer_config.rs @@ -120,14 +120,14 @@ impl ViewerConfig { match format { Some(ViewerConfigEncoding::String) => { let mut encoder = ZlibEncoder::new(Vec::new(), Compression::default()); - let bytes = rmp_serde::to_vec(&self.token())?; + let bytes = rmp_serde::to_vec_named(&self.token())?; encoder.write_all(&bytes)?; let encoded = encoder.finish()?; Ok(JsValue::from(base64::encode(encoded))) }, Some(ViewerConfigEncoding::ArrayBuffer) => { let mut encoder = ZlibEncoder::new(Vec::new(), Compression::default()); - let bytes = rmp_serde::to_vec(&self.token())?; + let bytes = rmp_serde::to_vec_named(&self.token())?; encoder.write_all(&bytes)?; let encoded = encoder.finish()?; let array = js_sys::Uint8Array::from(&encoded[..]); diff --git a/rust/perspective-viewer/src/rust/custom_elements/viewer.rs b/rust/perspective-viewer/src/rust/custom_elements/viewer.rs index df49f5ad5b..a117fef0b5 100644 --- a/rust/perspective-viewer/src/rust/custom_elements/viewer.rs +++ b/rust/perspective-viewer/src/rust/custom_elements/viewer.rs @@ -664,7 +664,7 @@ impl PerspectiveViewerElement { .collect(); let theme_name = presentation.get_selected_theme_name().await; - presentation.reset_available_themes(themes).await; + let mut changed = presentation.reset_available_themes(themes).await; let reset_theme = presentation .get_available_themes() .await? @@ -672,12 +672,14 @@ impl PerspectiveViewerElement { .find(|y| theme_name.as_ref() == Some(y)) .cloned(); - presentation.set_theme_name(reset_theme.as_deref()).await?; - if let Some(view) = session.get_view() { - renderer.restyle_all(&view).await - } else { - Ok(JsValue::UNDEFINED) + changed = presentation.set_theme_name(reset_theme.as_deref()).await? || changed; + if changed { + if let Some(view) = session.get_view() { + return renderer.restyle_all(&view).await; + } } + + Ok(JsValue::UNDEFINED) }) } diff --git a/rust/perspective-viewer/src/rust/presentation.rs b/rust/perspective-viewer/src/rust/presentation.rs index 07f7b4c78c..c1675d0985 100644 --- a/rust/perspective-viewer/src/rust/presentation.rs +++ b/rust/perspective-viewer/src/rust/presentation.rs @@ -11,7 +11,7 @@ // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ use std::cell::RefCell; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::ops::Deref; use std::rc::Rc; @@ -186,9 +186,20 @@ impl Presentation { /// Reset the state. `styleSheets` will be re-parsed next time /// `get_themes()` is called if the `themes` argument is `None`. - pub async fn reset_available_themes(&self, themes: Option>) { + /// + /// # Returns + /// A `bool` indicating whether the internal state changed. + pub async fn reset_available_themes(&self, themes: Option>) -> bool { + fn as_set(x: &Option>) -> HashSet<&'_ String> { + x.as_ref() + .map(|x| x.iter().collect::>()) + .unwrap_or_default() + } + let mut mutex = self.0.theme_data.lock().await; + let changed = as_set(&mutex.themes) != as_set(&themes); mutex.themes = themes; + changed } pub async fn get_selected_theme_config(&self) -> ApiResult<(Vec, Option)> { @@ -218,8 +229,17 @@ impl Presentation { } /// Set the theme by name, or `None` for the default theme. - pub async fn set_theme_name(&self, theme: Option<&str>) -> ApiResult<()> { - let (themes, _) = self.get_selected_theme_config().await?; + /// + /// # Returns + /// A `bool` indicating whether the internal state changed. + pub async fn set_theme_name(&self, theme: Option<&str>) -> ApiResult { + let (themes, selected) = self.get_selected_theme_config().await?; + if let Some(x) = selected { + if themes.get(x).map(|x| x.as_str()) == theme { + return Ok(false); + } + } + let index = if let Some(theme) = theme { self.set_theme_attribute(Some(theme))?; themes.iter().position(|x| x == theme) @@ -232,7 +252,7 @@ impl Presentation { }; self.theme_config_updated.emit((themes, index)); - Ok(()) + Ok(true) } /// Returns an owned copy of the curent column configuration map. diff --git a/rust/perspective-viewer/test/js/save_restore.spec.js b/rust/perspective-viewer/test/js/save_restore.spec.js index c51ceb44dd..5ea57413b7 100644 --- a/rust/perspective-viewer/test/js/save_restore.spec.js +++ b/rust/perspective-viewer/test/js/save_restore.spec.js @@ -142,4 +142,69 @@ test.describe("Save/Restore", async () => { "restore-restores-config-from-save.txt", ]); }); + + test("save/restore works in string format", async ({ page }) => { + const config = await page.evaluate(async () => { + const viewer = document.querySelector("perspective-viewer"); + await viewer.getTable(); + await viewer.restore({ + settings: true, + group_by: ["State"], + columns: ["Profit", "Sales"], + }); + return await viewer.save("string"); + }); + + const config3 = await page.evaluate(async (config) => { + const viewer = document.querySelector("perspective-viewer"); + await viewer.reset(); + await viewer.restore(config); + return await viewer.save(); + }, config); + + expect(config3).toEqual({ + ...DEFAULT_CONFIG, + columns: ["Profit", "Sales"], + plugin: "Debug", + group_by: ["State"], + settings: true, + theme: "Pro Light", + }); + + const contents = await get_contents(page); + await compareContentsToSnapshot(contents, [ + "save-restore-works-in-string-format.txt", + ]); + }); + + test("save/restore works in arraybuffer format", async ({ page }) => { + const config3 = await page.evaluate(async () => { + const viewer = document.querySelector("perspective-viewer"); + await viewer.getTable(); + await viewer.restore({ + settings: true, + group_by: ["State"], + columns: ["Profit", "Sales"], + }); + + const config = await viewer.save("arraybuffer"); + await viewer.reset(); + await viewer.restore(config); + return await viewer.save(); + }); + + expect(config3).toEqual({ + ...DEFAULT_CONFIG, + columns: ["Profit", "Sales"], + plugin: "Debug", + group_by: ["State"], + settings: true, + theme: "Pro Light", + }); + + const contents = await get_contents(page); + await compareContentsToSnapshot(contents, [ + "save-restore-works-in-arraybuffer-format.txt", + ]); + }); }); diff --git a/rust/perspective/Cargo.toml b/rust/perspective/Cargo.toml index 34f53ae339..387a86ddd7 100644 --- a/rust/perspective/Cargo.toml +++ b/rust/perspective/Cargo.toml @@ -12,7 +12,7 @@ [package] name = "perspective" -version = "3.1.6" +version = "3.1.8" authors = ["Andrew Stein "] edition = "2021" description = "A data visualization and analytics component, especially well-suited for large and/or streaming datasets." @@ -38,8 +38,8 @@ external-cpp = [ [dependencies] async-lock = "2.5.0" -perspective-client = { version = "3.1.6", path = "../perspective-client" } -perspective-server = { version = "3.1.6", path = "../perspective-server" } +perspective-client = { version = "3.1.8", path = "../perspective-client" } +perspective-server = { version = "3.1.8", path = "../perspective-server" } tracing = { version = ">=0.1.36" } axum = { version = ">=0.7,<2", features = ["ws"], optional = true } diff --git a/rust/perspective/package.json b/rust/perspective/package.json index 6ec4f04f01..322460a1b6 100644 --- a/rust/perspective/package.json +++ b/rust/perspective/package.json @@ -1,6 +1,6 @@ { "name": "@finos/perspective-rs", - "version": "3.1.6", + "version": "3.1.8", "description": "", "private": true, "repository": { @@ -14,4 +14,4 @@ "clean": "cargo clean", "test": "cargo test" } -} +} \ No newline at end of file diff --git a/tools/perspective-bench/basic_suite.mjs b/tools/perspective-bench/basic_suite.mjs index 7fc94a86f4..1f8e5cf2db 100644 --- a/tools/perspective-bench/basic_suite.mjs +++ b/tools/perspective-bench/basic_suite.mjs @@ -43,7 +43,7 @@ perspective_bench.suite( const { default: perspective } = await import("@finos/perspective"); client = await perspective.websocket(path); metadata = { - version: "3.1.6", + version: "3.1.8", version_idx, }; } else { diff --git a/tools/perspective-scripts/package.json b/tools/perspective-scripts/package.json index 77f9cfff2a..dfdfcf55ba 100644 --- a/tools/perspective-scripts/package.json +++ b/tools/perspective-scripts/package.json @@ -1,6 +1,6 @@ { "name": "@finos/perspective-scripts", - "version": "3.1.6", + "version": "3.1.8", "description": "Build scripts based on perspective", "private": true, "files": [ @@ -15,4 +15,4 @@ "devDependencies": { "zx": "^8.1.8" } -} +} \ No newline at end of file diff --git a/tools/perspective-test/package.json b/tools/perspective-test/package.json index b689bea916..55e3e3e1d3 100644 --- a/tools/perspective-test/package.json +++ b/tools/perspective-test/package.json @@ -1,6 +1,6 @@ { "name": "@finos/perspective-test", - "version": "3.1.6", + "version": "3.1.8", "description": "Test utility based on perspective", "private": true, "main": "src/js/index.ts", @@ -23,4 +23,4 @@ "glob": "^11", "xml-formatter": "2.4.0" } -} +} \ No newline at end of file diff --git a/tools/perspective-test/src/html/workspace-test.html b/tools/perspective-test/src/html/workspace-test.html index 3dda82c296..eabcbd27f4 100644 --- a/tools/perspective-test/src/html/workspace-test.html +++ b/tools/perspective-test/src/html/workspace-test.html @@ -23,11 +23,17 @@ padding: 0; overflow: hidden; } - #container { width: 100%; height: 100%; } + perspective-workspace { + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; + } perspective-workspace, perspective-viewer { font-family: "Roboto Mono" !important;