diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e4e672f468..74817086f6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1186,14 +1186,13 @@ jobs: runs-on: ${{ matrix.os }} needs: [initialize] env: - # NOTA BENE: When upgrading past 0.24.1, remove workaround below for https://github.com/pyodide/pyodide/issues/4216 - PYODIDE_VERSION: 0.24.1 + PYODIDE_VERSION: 0.25.0 # PYTHON_VERSION and EMSCRIPTEN_VERSION are determined by PYODIDE_VERSION. # The appropriate versions can be found in the Pyodide repodata.json # "info" field, or in Makefile.envs: - # https://github.com/pyodide/pyodide/blob/main/Makefile.envs#L2 + # https://github.com/pyodide/pyodide/blob/0.25.0/Makefile.envs#L2 PYTHON_VERSION: 3.11.3 - EMSCRIPTEN_VERSION: 3.1.45 + EMSCRIPTEN_VERSION: 3.1.46 steps: - name: Checkout perspective uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 @@ -1221,16 +1220,6 @@ jobs: - name: Install pyodide-build run: pip install pyodide-build==$PYODIDE_VERSION "pydantic<2" - # NOTA BENE: When removing this step, also remove the vendored pyodide file - - name: Provision workaround for bug in Pyodide 0.24.1 - run: | - PYTHON_HOME="$(python -c 'import sys; print(sys.prefix)')" - TOOLCHAIN_DIR="$PYTHON_HOME/lib/python3.11/site-packages/pyodide_build/tools/cmake/Modules/Platform" - echo "$PYTHON_HOME" - echo "$TOOLCHAIN_DIR" - mkdir -p "$TOOLCHAIN_DIR" - cp vendor/pyodide/Emscripten.cmake "$TOOLCHAIN_DIR" - - name: Build # Without --exports=pyinit, pyodide-build tries to export all symbols from every .o, # which causes the em++ linker command line invocation to be so long diff --git a/CHANGELOG.md b/CHANGELOG.md index eaa6637215..e161aee09f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,24 @@ +# [v2.9.0](https://github.com/finos/perspective/releases/tag/v2.9.0) + +_12 March 2024_ ([Full changelog](https://github.com/finos/perspective/compare/v2.8.1...v2.9.0)) + +**Breaking** + +- Formatting for `float` and `integer` columns via `Intl.NumberFormat` [#2563](https://github.com/finos/perspective/pull/2563) + +Features + +- Localization Support [#2565](https://github.com/finos/perspective/pull/2565) + +Fixes + +- Fix get_hosted_table_names in Python client [#2551](https://github.com/finos/perspective/pull/2551) +- Fix Candlestick & OHLC charts [#2562](https://github.com/finos/perspective/pull/2562) + +Misc + +- Update Pyodide to version 0.25.0 [#2547](https://github.com/finos/perspective/pull/2547) + # [v2.8.1](https://github.com/finos/perspective/releases/tag/v2.8.1) _26 February 2024_ ([Full changelog](https://github.com/finos/perspective/compare/v2.8.0...v2.8.1)) @@ -8,6 +29,7 @@ Fixes Misc +- Update to LLVM 17 & fix Python/Windows build [#2546](https://github.com/finos/perspective/pull/2546) - Add `clang-format`, `clang-tidy` and `clangd` support for C++ development [#2541](https://github.com/finos/perspective/pull/2541) - Add Docs Tests [#2502](https://github.com/finos/perspective/pull/2502) - Add workspaceFolder to cargo_target_dir [#2537](https://github.com/finos/perspective/pull/2537) diff --git a/cpp/perspective/package.json b/cpp/perspective/package.json index b97b33c36d..a5e1b9cc25 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": "2.8.1", + "version": "2.9.0", "main": "./dist/esm/perspective.cpp.js", "files": [ "dist/esm/**/*", diff --git a/docs/package.json b/docs/package.json index ed89a996e8..80254bc865 100644 --- a/docs/package.json +++ b/docs/package.json @@ -1,6 +1,6 @@ { "name": "@finos/perspective-docs", - "version": "2.8.1", + "version": "2.9.0", "private": true, "scripts": { "docusaurus": "docusaurus", @@ -16,13 +16,13 @@ "dependencies": { "@docusaurus/core": "2.2.0", "@docusaurus/preset-classic": "2.2.0", - "@finos/perspective": "^2.8.1", - "@finos/perspective-viewer": "^2.8.1", - "@finos/perspective-viewer-d3fc": "^2.8.1", - "@finos/perspective-viewer-datagrid": "^2.8.1", - "@finos/perspective-webpack-plugin": "^2.8.1", + "@finos/perspective": "^2.9.0", + "@finos/perspective-viewer": "^2.9.0", + "@finos/perspective-viewer-d3fc": "^2.9.0", + "@finos/perspective-viewer-datagrid": "^2.9.0", + "@finos/perspective-webpack-plugin": "^2.9.0", "@mdx-js/react": "^1.6.22", - "blocks": "^2.8.1", + "blocks": "^2.9.0", "clsx": "^1.1.1", "prism-react-renderer": "^1.3.3", "react": "^17.0.2", diff --git a/examples/blocks/package.json b/examples/blocks/package.json index daba118f92..5b4ef72493 100644 --- a/examples/blocks/package.json +++ b/examples/blocks/package.json @@ -1,7 +1,7 @@ { "name": "blocks", "private": true, - "version": "2.8.1", + "version": "2.9.0", "description": "A collection of simple client-side Perspective examples for `http://bl.ocks.org`.", "scripts": { "start": "mkdirp dist && node server.mjs" @@ -10,12 +10,12 @@ "keywords": [], "license": "Apache-2.0", "dependencies": { - "@finos/perspective": "^2.8.1", - "@finos/perspective-viewer": "^2.8.1", - "@finos/perspective-viewer-d3fc": "^2.8.1", - "@finos/perspective-viewer-datagrid": "^2.8.1", - "@finos/perspective-viewer-openlayers": "^2.8.1", - "@finos/perspective-workspace": "^2.8.1", + "@finos/perspective": "^2.9.0", + "@finos/perspective-viewer": "^2.9.0", + "@finos/perspective-viewer-d3fc": "^2.9.0", + "@finos/perspective-viewer-datagrid": "^2.9.0", + "@finos/perspective-viewer-openlayers": "^2.9.0", + "@finos/perspective-workspace": "^2.9.0", "superstore-arrow": "3.0.0" }, "devDependencies": { diff --git a/examples/blocks/src/streaming/streaming.js b/examples/blocks/src/streaming/streaming.js index e259a32d42..65661c5431 100644 --- a/examples/blocks/src/streaming/streaming.js +++ b/examples/blocks/src/streaming/streaming.js @@ -75,15 +75,15 @@ window.addEventListener("DOMContentLoaded", async function () { elem.restore({ plugin: "Datagrid", plugin_config: { - columns: { - "(+)chg": { fg_gradient: 7.93, number_fg_mode: "bar" }, - "(-)chg": { fg_gradient: 8.07, number_fg_mode: "bar" }, - chg: { bg_gradient: 9.97, number_bg_mode: "gradient" }, - }, editable: false, scroll_lock: true, }, settings: true, + columns_config: { + "(+)chg": { fg_gradient: 7.93, number_fg_mode: "bar" }, + "(-)chg": { fg_gradient: 8.07, number_fg_mode: "bar" }, + chg: { bg_gradient: 9.97, number_bg_mode: "gradient" }, + }, theme: "Pro Light", group_by: ["name"], split_by: ["client"], diff --git a/examples/blocks/src/webcam/layouts.json b/examples/blocks/src/webcam/layouts.json index 4abd2da152..302c07c5c9 100644 --- a/examples/blocks/src/webcam/layouts.json +++ b/examples/blocks/src/webcam/layouts.json @@ -1,17 +1,16 @@ [ { "plugin": "Datagrid", + "columns_config": { + "color": { + "bg_gradient": 251.04, + "neg_bg_color": "#ffa38f", + "number_bg_mode": "gradient", + "number_fg_mode": "disabled", + "pos_bg_color": "#346ead" + } + }, "plugin_config": { - "columns": { - "color": { - "fixed": 0, - "bg_gradient": 251.04, - "neg_bg_color": "#ffa38f", - "number_bg_mode": "gradient", - "number_fg_mode": "disabled", - "pos_bg_color": "#346ead" - } - }, "editable": false, "scroll_lock": false }, @@ -77,16 +76,16 @@ }, { "plugin": "Datagrid", + "columns_config": { + "color": { + "bg_gradient": 2463.68, + "neg_bg_color": "#ffa38f", + "number_bg_mode": "gradient", + "number_fg_mode": "disabled", + "pos_bg_color": "#307bb0" + } + }, "plugin_config": { - "columns": { - "color": { - "bg_gradient": 2463.68, - "neg_bg_color": "#ffa38f", - "number_bg_mode": "gradient", - "number_fg_mode": "disabled", - "pos_bg_color": "#307bb0" - } - }, "editable": false, "scroll_lock": false }, @@ -138,7 +137,6 @@ { "plugin": "Datagrid", "plugin_config": { - "columns": {}, "editable": false, "scroll_lock": false }, diff --git a/examples/blocks/src/webcam/webcam.js b/examples/blocks/src/webcam/webcam.js index 7aaf48743f..cbb7e4f94b 100644 --- a/examples/blocks/src/webcam/webcam.js +++ b/examples/blocks/src/webcam/webcam.js @@ -69,11 +69,16 @@ window.addEventListener("DOMContentLoaded", async function () { const viewer = document.querySelector("perspective-viewer"); viewer.load(table); await viewer.restore({ settings, ...layouts[0] }); - const regular_table = document.querySelector("regular-table"); + const regular_table = document + .querySelector("perspective-viewer-datagrid") + .shadowRoot.querySelector("regular-table"); + regular_table.scrollTop = regular_table.scrollHeight / 2 - regular_table.clientHeight / 2; + regular_table.scrollLeft = regular_table.scrollWidth / 2 - regular_table.clientWidth / 2; + for (const layout of layouts) { const option = document.createElement("option"); option.value = layout.title; diff --git a/examples/esbuild-example/package.json b/examples/esbuild-example/package.json index 0470422276..a28e334c9a 100644 --- a/examples/esbuild-example/package.json +++ b/examples/esbuild-example/package.json @@ -1,7 +1,7 @@ { "name": "esbuild-example", "private": true, - "version": "2.8.1", + "version": "2.9.0", "description": "An esbuild example app built using `@finos/perspective-viewer`.", "scripts": { "build": "node build.js", @@ -10,15 +10,15 @@ "keywords": [], "license": "Apache-2.0", "dependencies": { - "@finos/perspective": "^2.8.1", - "@finos/perspective-viewer": "^2.8.1", - "@finos/perspective-viewer-d3fc": "^2.8.1", - "@finos/perspective-viewer-datagrid": "^2.8.1", - "@finos/perspective-viewer-openlayers": "^2.8.1", + "@finos/perspective": "^2.9.0", + "@finos/perspective-viewer": "^2.9.0", + "@finos/perspective-viewer-d3fc": "^2.9.0", + "@finos/perspective-viewer-datagrid": "^2.9.0", + "@finos/perspective-viewer-openlayers": "^2.9.0", "superstore-arrow": "^3.0.0" }, "devDependencies": { - "@finos/perspective-esbuild-plugin": "^2.8.1", + "@finos/perspective-esbuild-plugin": "^2.9.0", "esbuild": "^0.14.54", "http-server": "^14.1.1" } diff --git a/examples/esbuild-remote/package.json b/examples/esbuild-remote/package.json index 5f9f74c58f..8705463fa9 100644 --- a/examples/esbuild-remote/package.json +++ b/examples/esbuild-remote/package.json @@ -1,7 +1,7 @@ { "name": "esbuild-remote", "private": true, - "version": "2.8.1", + "version": "2.9.0", "description": "An example of 2 Perspectives, one client and one server, streaming via Apache Arrow.", "scripts": { "start": "node build.js && node server/index.mjs" @@ -9,15 +9,15 @@ "keywords": [], "license": "Apache-2.0", "dependencies": { - "@finos/perspective": "^2.8.1", - "@finos/perspective-viewer": "^2.8.1", - "@finos/perspective-viewer-d3fc": "^2.8.1", - "@finos/perspective-viewer-datagrid": "^2.8.1", + "@finos/perspective": "^2.9.0", + "@finos/perspective-viewer": "^2.9.0", + "@finos/perspective-viewer-d3fc": "^2.9.0", + "@finos/perspective-viewer-datagrid": "^2.9.0", "express": "^4.17.1", "express-ws": "^5.0.2" }, "devDependencies": { - "@finos/perspective-esbuild-plugin": "^2.8.1", + "@finos/perspective-esbuild-plugin": "^2.9.0", "esbuild": "^0.14.54" } } diff --git a/examples/git-history/package.json b/examples/git-history/package.json index 8dcce59b89..3e74e561af 100644 --- a/examples/git-history/package.json +++ b/examples/git-history/package.json @@ -1,7 +1,7 @@ { "name": "git-history", "private": true, - "version": "2.8.1", + "version": "2.9.0", "description": "An example of Perspective's own GIT history rendered in Perspective.", "scripts": { "start": "node server.js" @@ -9,9 +9,9 @@ "keywords": [], "license": "Apache-2.0", "dependencies": { - "@finos/perspective": "^2.8.1", - "@finos/perspective-viewer": "^2.8.1", - "@finos/perspective-viewer-d3fc": "^2.8.1", - "@finos/perspective-viewer-datagrid": "^2.8.1" + "@finos/perspective": "^2.9.0", + "@finos/perspective-viewer": "^2.9.0", + "@finos/perspective-viewer-d3fc": "^2.9.0", + "@finos/perspective-viewer-datagrid": "^2.9.0" } } diff --git a/examples/promo/package.json b/examples/promo/package.json index 1a16ad21a1..5dcc6b9322 100644 --- a/examples/promo/package.json +++ b/examples/promo/package.json @@ -1,7 +1,7 @@ { "name": "promo", "private": true, - "version": "2.8.1", + "version": "2.9.0", "description": "An puppeteer-guided demo of Perspective's functionality, as seen on Github.", "scripts": { "dev": "webpack-dev-server --open", @@ -13,14 +13,14 @@ "keywords": [], "license": "Apache-2.0", "dependencies": { - "@finos/perspective": "^2.8.1", - "@finos/perspective-viewer": "^2.8.1", - "@finos/perspective-viewer-d3fc": "^2.8.1", - "@finos/perspective-viewer-datagrid": "^2.8.1", - "@finos/perspective-workspace": "^2.8.1" + "@finos/perspective": "^2.9.0", + "@finos/perspective-viewer": "^2.9.0", + "@finos/perspective-viewer-d3fc": "^2.9.0", + "@finos/perspective-viewer-datagrid": "^2.9.0", + "@finos/perspective-workspace": "^2.9.0" }, "devDependencies": { - "@finos/perspective-webpack-plugin": "^2.8.1", + "@finos/perspective-webpack-plugin": "^2.9.0", "http-server": "^14.1.1", "npm-run-all": "^4.1.3", "rimraf": "^2.5.2" diff --git a/examples/python-aiohttp/package.json b/examples/python-aiohttp/package.json index bf758fff1c..76e652f79a 100644 --- a/examples/python-aiohttp/package.json +++ b/examples/python-aiohttp/package.json @@ -1,7 +1,7 @@ { "name": "python-aiohttp", "private": true, - "version": "2.8.1", + "version": "2.9.0", "description": "An example of editing a `perspective-python` server from the browser.", "scripts": { "start": "PYTHONPATH=../../python/perspective python3 server.py" @@ -9,15 +9,15 @@ "keywords": [], "license": "Apache-2.0", "dependencies": { - "@finos/perspective": "^2.8.1", - "@finos/perspective-viewer": "^2.8.1", - "@finos/perspective-viewer-d3fc": "^2.8.1", - "@finos/perspective-viewer-datagrid": "^2.8.1", - "@finos/perspective-workspace": "^2.8.1", + "@finos/perspective": "^2.9.0", + "@finos/perspective-viewer": "^2.9.0", + "@finos/perspective-viewer-d3fc": "^2.9.0", + "@finos/perspective-viewer-datagrid": "^2.9.0", + "@finos/perspective-workspace": "^2.9.0", "superstore-arrow": "^3.0.0" }, "devDependencies": { - "@finos/perspective-webpack-plugin": "^2.8.1", + "@finos/perspective-webpack-plugin": "^2.9.0", "npm-run-all": "^4.1.3", "rimraf": "^2.5.2" } diff --git a/examples/python-starlette/package.json b/examples/python-starlette/package.json index af72968762..bfb0b5af0a 100644 --- a/examples/python-starlette/package.json +++ b/examples/python-starlette/package.json @@ -1,7 +1,7 @@ { "name": "python-starlette", "private": true, - "version": "2.8.1", + "version": "2.9.0", "description": "An example of editing a `perspective-python` server from the browser.", "scripts": { "start": "PYTHONPATH=../../python/perspective python3 server.py" @@ -9,15 +9,15 @@ "keywords": [], "license": "Apache-2.0", "dependencies": { - "@finos/perspective": "^2.8.1", - "@finos/perspective-viewer": "^2.8.1", - "@finos/perspective-viewer-d3fc": "^2.8.1", - "@finos/perspective-viewer-datagrid": "^2.8.1", - "@finos/perspective-workspace": "^2.8.1", + "@finos/perspective": "^2.9.0", + "@finos/perspective-viewer": "^2.9.0", + "@finos/perspective-viewer-d3fc": "^2.9.0", + "@finos/perspective-viewer-datagrid": "^2.9.0", + "@finos/perspective-workspace": "^2.9.0", "superstore-arrow": "^3.0.0" }, "devDependencies": { - "@finos/perspective-webpack-plugin": "^2.8.1", + "@finos/perspective-webpack-plugin": "^2.9.0", "npm-run-all": "^4.1.3", "rimraf": "^2.5.2" } diff --git a/examples/python-tornado-streaming/package.json b/examples/python-tornado-streaming/package.json index 39a8471ed5..6c5ccd5343 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": "2.8.1", + "version": "2.9.0", "description": "An example of streaming a `perspective-python` server to the browser.", "scripts": { "start": "PYTHONPATH=../../python/perspective python3 server.py" @@ -9,15 +9,15 @@ "keywords": [], "license": "Apache-2.0", "dependencies": { - "@finos/perspective": "^2.8.1", - "@finos/perspective-viewer": "^2.8.1", - "@finos/perspective-viewer-d3fc": "^2.8.1", - "@finos/perspective-viewer-datagrid": "^2.8.1", - "@finos/perspective-workspace": "^2.8.1", + "@finos/perspective": "^2.9.0", + "@finos/perspective-viewer": "^2.9.0", + "@finos/perspective-viewer-d3fc": "^2.9.0", + "@finos/perspective-viewer-datagrid": "^2.9.0", + "@finos/perspective-workspace": "^2.9.0", "superstore-arrow": "^3.0.0" }, "devDependencies": { - "@finos/perspective-webpack-plugin": "^2.8.1", + "@finos/perspective-webpack-plugin": "^2.9.0", "npm-run-all": "^4.1.3", "rimraf": "^2.5.2" } diff --git a/examples/python-tornado/package.json b/examples/python-tornado/package.json index af335f443e..1b75f53953 100644 --- a/examples/python-tornado/package.json +++ b/examples/python-tornado/package.json @@ -1,7 +1,7 @@ { "name": "python-tornado", "private": true, - "version": "2.8.1", + "version": "2.9.0", "description": "An example of editing a `perspective-python` server from the browser.", "scripts": { "start": "PYTHONPATH=../../python/perspective python3 server.py" @@ -9,15 +9,15 @@ "keywords": [], "license": "Apache-2.0", "dependencies": { - "@finos/perspective": "^2.8.1", - "@finos/perspective-viewer": "^2.8.1", - "@finos/perspective-viewer-d3fc": "^2.8.1", - "@finos/perspective-viewer-datagrid": "^2.8.1", - "@finos/perspective-workspace": "^2.8.1", + "@finos/perspective": "^2.9.0", + "@finos/perspective-viewer": "^2.9.0", + "@finos/perspective-viewer-d3fc": "^2.9.0", + "@finos/perspective-viewer-datagrid": "^2.9.0", + "@finos/perspective-workspace": "^2.9.0", "superstore-arrow": "^3.0.0" }, "devDependencies": { - "@finos/perspective-webpack-plugin": "^2.8.1", + "@finos/perspective-webpack-plugin": "^2.9.0", "npm-run-all": "^4.1.3", "rimraf": "^2.5.2" } diff --git a/examples/react-example/package.json b/examples/react-example/package.json index 459248e0d0..9729548168 100644 --- a/examples/react-example/package.json +++ b/examples/react-example/package.json @@ -1,7 +1,7 @@ { "name": "react-example", "private": true, - "version": "2.8.1", + "version": "2.9.0", "description": "An example app built using `@finos/perspective-viewer`.", "scripts": { "start": "webpack serve --open", @@ -10,15 +10,15 @@ "keywords": [], "license": "Apache-2.0", "dependencies": { - "@finos/perspective": "^2.8.1", - "@finos/perspective-viewer": "^2.8.1", - "@finos/perspective-viewer-d3fc": "^2.8.1", - "@finos/perspective-viewer-datagrid": "^2.8.1", + "@finos/perspective": "^2.9.0", + "@finos/perspective-viewer": "^2.9.0", + "@finos/perspective-viewer-d3fc": "^2.9.0", + "@finos/perspective-viewer-datagrid": "^2.9.0", "react": "^16.14.0", "react-dom": "^16.9.17" }, "devDependencies": { - "@finos/perspective-webpack-plugin": "^2.8.1", + "@finos/perspective-webpack-plugin": "^2.9.0", "@types/react": "^16.14.0", "@types/react-dom": "^16.9.17", "source-map-loader": "^0.2.4", diff --git a/examples/webpack-cross-origin/package.json b/examples/webpack-cross-origin/package.json index 321d46a47a..36f97660c1 100644 --- a/examples/webpack-cross-origin/package.json +++ b/examples/webpack-cross-origin/package.json @@ -1,7 +1,7 @@ { "name": "webpack-cross-origin", "private": true, - "version": "2.8.1", + "version": "2.9.0", "description": "An example of using the Perspective Webpack plugin to build a JS file with Webpack.", "scripts": { "start": "npm-run-all -l -p webpack-watch host:app host:bundles", @@ -12,13 +12,13 @@ "keywords": [], "license": "Apache-2.0", "dependencies": { - "@finos/perspective": "^2.8.1", - "@finos/perspective-viewer": "^2.8.1", - "@finos/perspective-viewer-d3fc": "^2.8.1", - "@finos/perspective-viewer-datagrid": "^2.8.1" + "@finos/perspective": "^2.9.0", + "@finos/perspective-viewer": "^2.9.0", + "@finos/perspective-viewer-d3fc": "^2.9.0", + "@finos/perspective-viewer-datagrid": "^2.9.0" }, "devDependencies": { - "@finos/perspective-webpack-plugin": "^2.8.1", + "@finos/perspective-webpack-plugin": "^2.9.0", "css-loader": "^0.28.7", "html-webpack-plugin": "^5.1.0", "http-server": "^14.1.1", diff --git a/examples/webpack-example/package.json b/examples/webpack-example/package.json index 9cc106579e..fb93e53ba9 100644 --- a/examples/webpack-example/package.json +++ b/examples/webpack-example/package.json @@ -1,7 +1,7 @@ { "name": "webpack-example", "private": true, - "version": "2.8.1", + "version": "2.9.0", "description": "An example app built using `@finos/perspective-viewer`.", "scripts": { "build": "webpack", @@ -10,13 +10,13 @@ "keywords": [], "license": "Apache-2.0", "dependencies": { - "@finos/perspective": "^2.8.1", - "@finos/perspective-viewer": "^2.8.1", - "@finos/perspective-viewer-d3fc": "^2.8.1", - "@finos/perspective-viewer-datagrid": "^2.8.1" + "@finos/perspective": "^2.9.0", + "@finos/perspective-viewer": "^2.9.0", + "@finos/perspective-viewer-d3fc": "^2.9.0", + "@finos/perspective-viewer-datagrid": "^2.9.0" }, "devDependencies": { - "@finos/perspective-webpack-plugin": "^2.8.1", + "@finos/perspective-webpack-plugin": "^2.9.0", "css-loader": "^0.28.7", "html-webpack-plugin": "^5.1.0", "style-loader": "^0.18.2", diff --git a/examples/workspace-editing-python/package.json b/examples/workspace-editing-python/package.json index dc3b8cf5b7..1d47ac0349 100644 --- a/examples/workspace-editing-python/package.json +++ b/examples/workspace-editing-python/package.json @@ -1,7 +1,7 @@ { "name": "workspace-editing-python", "private": true, - "version": "2.8.1", + "version": "2.9.0", "description": "An example app demonstrating client/server editing, built using `@finos/perspective-workspace` and `perspective-python`.", "scripts": { "start": "yarn webpack && yarn start:server", @@ -12,14 +12,14 @@ "keywords": [], "license": "Apache-2.0", "dependencies": { - "@finos/perspective": "^2.8.1", - "@finos/perspective-viewer": "^2.8.1", - "@finos/perspective-viewer-d3fc": "^2.8.1", - "@finos/perspective-viewer-datagrid": "^2.8.1", - "@finos/perspective-workspace": "^2.8.1" + "@finos/perspective": "^2.9.0", + "@finos/perspective-viewer": "^2.9.0", + "@finos/perspective-viewer-d3fc": "^2.9.0", + "@finos/perspective-viewer-datagrid": "^2.9.0", + "@finos/perspective-workspace": "^2.9.0" }, "devDependencies": { - "@finos/perspective-webpack-plugin": "^2.8.1", + "@finos/perspective-webpack-plugin": "^2.9.0", "http-server": "^14.1.1", "npm-run-all": "^4.1.3", "rimraf": "^2.5.2" diff --git a/examples/workspace/package.json b/examples/workspace/package.json index 4aefd9dc1f..bdeb11b20a 100644 --- a/examples/workspace/package.json +++ b/examples/workspace/package.json @@ -1,7 +1,7 @@ { "name": "workspace", "private": true, - "version": "2.8.1", + "version": "2.9.0", "description": "An example app built using `@finos/perspective-workspace`.", "scripts": { "start": "webpack serve --open", @@ -10,14 +10,14 @@ "keywords": [], "license": "Apache-2.0", "dependencies": { - "@finos/perspective": "^2.8.1", - "@finos/perspective-viewer": "^2.8.1", - "@finos/perspective-viewer-d3fc": "^2.8.1", - "@finos/perspective-viewer-datagrid": "^2.8.1", - "@finos/perspective-workspace": "^2.8.1" + "@finos/perspective": "^2.9.0", + "@finos/perspective-viewer": "^2.9.0", + "@finos/perspective-viewer-d3fc": "^2.9.0", + "@finos/perspective-viewer-datagrid": "^2.9.0", + "@finos/perspective-workspace": "^2.9.0" }, "devDependencies": { - "@finos/perspective-webpack-plugin": "^2.8.1", + "@finos/perspective-webpack-plugin": "^2.9.0", "http-server": "^14.1.1", "npm-run-all": "^4.1.3", "rimraf": "^2.5.2" diff --git a/package.json b/package.json index bed8acf89b..6f55237c65 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "type": "git", "url": "https://github.com/finos/perspective" }, - "version": "2.8.1", + "version": "2.9.0", "changelog": { "labels": { "enhancement": "Added", diff --git a/packages/perspective-cli/package.json b/packages/perspective-cli/package.json index d502ce07dc..8d60a0531c 100644 --- a/packages/perspective-cli/package.json +++ b/packages/perspective-cli/package.json @@ -1,6 +1,6 @@ { "name": "@finos/perspective-cli", - "version": "2.8.1", + "version": "2.9.0", "description": "Perspective.js CLI", "main": "src/js/index.js", "publishConfig": { @@ -24,12 +24,12 @@ "perspective": "perspective" }, "dependencies": { - "@finos/perspective": "^2.8.1", - "@finos/perspective-viewer": "^2.8.1", - "@finos/perspective-viewer-d3fc": "^2.8.1", - "@finos/perspective-viewer-datagrid": "^2.8.1", - "@finos/perspective-viewer-openlayers": "^2.8.1", - "@finos/perspective-workspace": "^2.8.1", + "@finos/perspective": "^2.9.0", + "@finos/perspective-viewer": "^2.9.0", + "@finos/perspective-viewer-d3fc": "^2.9.0", + "@finos/perspective-viewer-datagrid": "^2.9.0", + "@finos/perspective-viewer-openlayers": "^2.9.0", + "@finos/perspective-workspace": "^2.9.0", "commander": "^2.19.0", "puppeteer": "^13.1.3" } diff --git a/packages/perspective-esbuild-plugin/package.json b/packages/perspective-esbuild-plugin/package.json index eb6488776e..4f97283c53 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": "2.8.1", + "version": "2.9.0", "description": "esbuild plugin for Perspective", "author": "", "license": "Apache-2.0", diff --git a/packages/perspective-jupyterlab/package.json b/packages/perspective-jupyterlab/package.json index dfe6ad7530..85aa19a76e 100644 --- a/packages/perspective-jupyterlab/package.json +++ b/packages/perspective-jupyterlab/package.json @@ -1,6 +1,6 @@ { "name": "@finos/perspective-jupyterlab", - "version": "2.8.1", + "version": "2.9.0", "description": "A Jupyterlab extension for the Perspective library, designed to be used with perspective-python.", "files": [ "dist/**/*", @@ -34,19 +34,19 @@ "version": "yarn build" }, "dependencies": { - "@finos/perspective": "^2.8.1", - "@finos/perspective-viewer": "^2.8.1", - "@finos/perspective-viewer-d3fc": "^2.8.1", - "@finos/perspective-viewer-datagrid": "^2.8.1", - "@finos/perspective-viewer-openlayers": "^2.8.1", + "@finos/perspective": "^2.9.0", + "@finos/perspective-viewer": "^2.9.0", + "@finos/perspective-viewer-d3fc": "^2.9.0", + "@finos/perspective-viewer-datagrid": "^2.9.0", + "@finos/perspective-viewer-openlayers": "^2.9.0", "@jupyter-widgets/base": "^2 || ^3 || ^4 || ^5 || ^6", "@jupyterlab/application": "^3.6.1", "@lumino/application": "^1.27.0", "@lumino/widgets": "^1.37.0" }, "devDependencies": { - "@finos/perspective-esbuild-plugin": "^2.8.1", - "@finos/perspective-test": "^2.8.1", + "@finos/perspective-esbuild-plugin": "^2.9.0", + "@finos/perspective-test": "^2.9.0", "@jupyterlab/builder": "^3.4.0", "@prospective.co/procss": "^0.1.15", "cpy": "^9.0.1" diff --git a/packages/perspective-jupyterlab/test/jupyter/widget.spec.js b/packages/perspective-jupyterlab/test/jupyter/widget.spec.js index 245d6b1bad..1764841b94 100644 --- a/packages/perspective-jupyterlab/test/jupyter/widget.spec.js +++ b/packages/perspective-jupyterlab/test/jupyter/widget.spec.js @@ -39,24 +39,18 @@ describe_jupyter( "w", ], async ({ page }) => { - const viewer = await default_body(page); - const num_columns = await viewer.evaluate(async (viewer) => { - const tbl = viewer - .querySelector("perspective-viewer-datagrid") - .shadowRoot.querySelector("regular-table"); - return tbl.querySelector("thead tr").childElementCount; - }); + await default_body(page); - expect(num_columns).toEqual(3); + const num_columns = await page + .locator("regular-table thead tr") + .first() + .evaluate((tr) => tr.childElementCount); - const num_rows = await viewer.evaluate(async (viewer) => { - const tbl = viewer - .querySelector("perspective-viewer-datagrid") - .shadowRoot.querySelector("regular-table"); - return tbl.querySelectorAll("tbody tr").length; - }); + expect(num_columns).toEqual(3); - expect(num_rows).toEqual(5); + await expect( + page.locator("regular-table tbody tr") + ).toHaveCount(5); } ); @@ -71,24 +65,17 @@ describe_jupyter( "table.update(arrow_data)", ], async ({ page }) => { - const viewer = await default_body(page); - const num_columns = await viewer.evaluate(async (viewer) => { - const tbl = viewer - .querySelector("perspective-viewer-datagrid") - .shadowRoot.querySelector("regular-table"); - return tbl.querySelector("thead tr").childElementCount; - }); + await default_body(page); + const num_columns = await page + .locator("regular-table thead tr") + .first() + .evaluate((tr) => tr.childElementCount); expect(num_columns).toEqual(3); - const num_rows = await viewer.evaluate(async (viewer) => { - const tbl = viewer - .querySelector("perspective-viewer-datagrid") - .shadowRoot.querySelector("regular-table"); - return tbl.querySelectorAll("tbody tr").length; - }); - - expect(num_rows).toEqual(10); + await expect( + page.locator("regular-table tbody tr") + ).toHaveCount(10); } ); test_jupyter( @@ -101,25 +88,18 @@ describe_jupyter( "w", ], async ({ page }) => { - const viewer = await default_body(page); - const num_columns = await viewer.evaluate(async (viewer) => { - const tbl = viewer - .querySelector("perspective-viewer-datagrid") - .shadowRoot.querySelector("regular-table"); + await default_body(page); - return tbl.querySelector("thead tr").childElementCount; - }); + const num_columns = await page + .locator("regular-table thead tr") + .first() + .evaluate((tr) => tr.childElementCount); expect(num_columns).toEqual(3); - const num_rows = await viewer.evaluate(async (viewer) => { - const tbl = viewer - .querySelector("perspective-viewer-datagrid") - .shadowRoot.querySelector("regular-table"); - return tbl.querySelectorAll("tbody tr").length; - }); - - expect(num_rows).toEqual(5); + await expect( + page.locator("regular-table tbody tr") + ).toHaveCount(5); } ); @@ -228,6 +208,7 @@ describe_jupyter( // Check default config expect(config).toEqual({ version: utils.API_VERSION, + columns_config: {}, aggregates: {}, columns: [ "ui8", @@ -281,6 +262,7 @@ w.theme = "Pro Dark"` // and check it expect(config).toEqual({ version: utils.API_VERSION, + columns_config: {}, aggregates: {}, columns: ["ui8"], expressions: {}, @@ -315,6 +297,7 @@ w.theme = "Pro Dark"` // Check default config expect(config).toEqual({ version: utils.API_VERSION, + columns_config: {}, aggregates: {}, columns: [ "ui8", diff --git a/packages/perspective-viewer-d3fc/package.json b/packages/perspective-viewer-d3fc/package.json index 7930648a97..b3d52f4a49 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": "2.8.1", + "version": "2.9.0", "description": "Perspective.js D3FC Plugin", "unpkg": "./dist/cdn/perspective-viewer-d3fc.js", "jsdelivr": "./dist/cdn/perspective-viewer-d3fc.js", @@ -46,8 +46,8 @@ "author": "", "license": "Apache-2.0", "dependencies": { - "@finos/perspective": "^2.8.1", - "@finos/perspective-viewer": "^2.8.1", + "@finos/perspective": "^2.9.0", + "@finos/perspective-viewer": "^2.9.0", "chroma-js": "^1.3.4", "d3fc": "^15.2.4", "d3-selection": "^3.0.0", @@ -58,8 +58,8 @@ }, "devDependencies": { "@types/d3": "^7.0.0", - "@finos/perspective-esbuild-plugin": "^2.8.1", - "@finos/perspective-test": "^2.8.1", + "@finos/perspective-esbuild-plugin": "^2.9.0", + "@finos/perspective-test": "^2.9.0", "@prospective.co/procss": "^0.1.15" } } diff --git a/packages/perspective-viewer-d3fc/src/ts/charts/xy-scatter.ts b/packages/perspective-viewer-d3fc/src/ts/charts/xy-scatter.ts index 4cbf419467..9e65dda609 100644 --- a/packages/perspective-viewer-d3fc/src/ts/charts/xy-scatter.ts +++ b/packages/perspective-viewer-d3fc/src/ts/charts/xy-scatter.ts @@ -26,6 +26,7 @@ import { symbolsObj } from "../series/seriesSymbols"; import { gridLayoutMultiChart } from "../layout/gridLayoutMultiChart"; import xyScatterSeries from "../series/xy-scatter/xyScatterSeries"; import { D3Scale, HTMLSelection, Settings } from "../types"; +import { Type } from "@finos/perspective"; /** * Overrides specific symbols based on plugin settings. This modifies in-place _and_ returns the value. @@ -46,8 +47,8 @@ function overrideSymbols(settings: Settings, symbols): D3Scale { for (let [i, _] of domain.entries()) { range[i] = range[(i as number) % len]; } - let maybeSymbols: Record = - settings.columns?.[symbolCol]?.symbols ?? {}; + let maybeSymbols = (settings.columns_config?.[symbolCol]?.["symbols"] ?? + {}) as Record; Object.entries(maybeSymbols).forEach(([key, value]) => { // TODO: Define custom symbol types based on the values passed in here. // https://d3js.org/d3-shape/symbol#custom-symbols @@ -182,4 +183,20 @@ xyScatter.plugin = { selectMode: "toggle", }; +xyScatter.can_render_column_styles = (type: Type, group?: string) => { + return type === "string" && group === "Symbol"; +}; +xyScatter.column_style_controls = (type: Type, group?: string) => { + if (type === "string" && group === "Symbol") { + return { + symbols: { + keys: "row", + values: Object.keys(symbolsObj), + }, + }; + } else { + return null; + } +}; + export default xyScatter; diff --git a/packages/perspective-viewer-d3fc/src/ts/data/ohlcData.ts b/packages/perspective-viewer-d3fc/src/ts/data/ohlcData.ts index 534d17749c..6ec57fc126 100644 --- a/packages/perspective-viewer-d3fc/src/ts/data/ohlcData.ts +++ b/packages/perspective-viewer-d3fc/src/ts/data/ohlcData.ts @@ -36,22 +36,6 @@ export function ohlcData(settings: Settings, data: DataRowsWithKey) { ); } -function getOHLCValue(realValue: string, col: DataRow): number | undefined { - if (!!realValue) { - let value = col[realValue]; - - if (typeof value === "string") { - return parseFloat(value); - } - - if (typeof value !== "number") { - return undefined; - } - } - - return undefined; -} - function seriesToOHLC( settings: Settings, data: DataRowsWithKey @@ -60,8 +44,12 @@ function seriesToOHLC( const getNextOpen = (i) => data[i < data.length - 1 ? i + 1 : i][settings.realValues[0]]; const mappedSeries: any = data.map((col, i) => { - const openValue = getOHLCValue(settings.realValues[0], col); - const closeValue = getOHLCValue(settings.realValues[1], col); + const openValue = !!settings.realValues[0] + ? col[settings.realValues[0]] + : undefined; + const closeValue = !!settings.realValues[1] + ? col[settings.realValues[1]] + : getNextOpen(i); return { crossValue: labelfn(col, i), @@ -70,10 +58,10 @@ function seriesToOHLC( closeValue: closeValue, highValue: !!settings.realValues[2] ? col[settings.realValues[2]] - : Math.max(openValue, closeValue), + : Math.max(openValue as number, closeValue as number), lowValue: !!settings.realValues[3] ? col[settings.realValues[3]] - : Math.min(openValue, closeValue), + : Math.min(openValue as number, closeValue as number), key: data.key, row: col, }; diff --git a/packages/perspective-viewer-d3fc/src/ts/plugin/plugin.ts b/packages/perspective-viewer-d3fc/src/ts/plugin/plugin.ts index 2bf6013d37..252e52c9e4 100644 --- a/packages/perspective-viewer-d3fc/src/ts/plugin/plugin.ts +++ b/packages/perspective-viewer-d3fc/src/ts/plugin/plugin.ts @@ -14,11 +14,15 @@ import "./polyfills/index"; import charts from "../charts/charts"; import { initialiseStyles } from "../series/colorStyles"; import style from "../../../dist/css/perspective-viewer-d3fc.css"; -import { HTMLPerspectiveViewerElement } from "@finos/perspective-viewer"; +import { + PerspectiveColumnConfig, + HTMLPerspectiveViewerElement, +} from "@finos/perspective-viewer"; import * as d3 from "d3"; import { symbolsObj } from "../series/seriesSymbols"; import { Chart, Settings } from "../types"; +import { Type } from "@finos/perspective"; const DEFAULT_PLUGIN_SETTINGS = { initial: { @@ -43,6 +47,7 @@ const EXCLUDED_SETTINGS = [ "agg_paths", "treemaps", "axisMemo", + "columns_config", ]; function getD3FCStyles(): string { @@ -152,30 +157,20 @@ export function register(...plugin_names: string[]) { chart.plugin.max_columns = x; } - get plugin_attributes() { - let symbols = Object.entries(symbolsObj).map( - ([name, ty]) => { - let container = document.createElement("div"); - d3.select(container) - .append("svg") - .attr("viewbox", "0, 0, 15, 15") //eyeballed this, it's probably wrong - .append("path") - .attr("transform", "translate(7.5, 7.5)") - .attr("d", d3.symbol(ty)()); - let html = d3.select(container).html(); - container.remove(); - - return { - name, - html, - }; - } + can_render_column_styles(type: Type, group: string) { + return chart.can_render_column_styles?.call( + this, + type, + group + ); + } + + column_style_controls(type: Type, group: string) { + return chart.column_style_controls?.call( + this, + type, + group ); - return { - symbol: { - symbols, - }, - }; } async render() { @@ -579,14 +574,21 @@ export function register(...plugin_names: string[]) { return settings; } - restore(settings: Settings) { - const new_settings = {}; + restore( + settings: Settings, + columns_config: PerspectiveColumnConfig + ) { + const new_settings: Partial = {}; for (const name of EXCLUDED_SETTINGS) { if (this._settings?.[name] !== undefined) { new_settings[name] = this._settings?.[name]; } } - this._settings = { ...new_settings, ...settings }; + this._settings = { + ...new_settings, + ...settings, + columns_config, + }; } } ); diff --git a/packages/perspective-viewer-d3fc/src/ts/types.ts b/packages/perspective-viewer-d3fc/src/ts/types.ts index 425f96f592..d72667a867 100644 --- a/packages/perspective-viewer-d3fc/src/ts/types.ts +++ b/packages/perspective-viewer-d3fc/src/ts/types.ts @@ -10,7 +10,10 @@ // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃ // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ -import { IPerspectiveViewerPlugin } from "@finos/perspective-viewer"; +import { + IPerspectiveViewerPlugin, + PerspectiveColumnConfig, +} from "@finos/perspective-viewer"; import { DataRow, Type } from "@finos/perspective"; export interface Element { @@ -32,6 +35,9 @@ export interface Chart { }; selectMode?: string; }; + can_render_column_styles?: (type: Type, group?: string) => boolean; + // TODO: Generate the type for the column style schema. + column_style_controls?: (type: Type, group?: string) => unknown; } export type PadUnit = "percent" | "domain"; @@ -80,13 +86,6 @@ export type TreemapValue = { treemapRoute?: any[]; // string[]? }; -export type ColumnSettingsConfig = { - symbols: Record; -}; -export type ColumnSettings< - T extends Record = ColumnSettingsConfig -> = Record; - export type Settings = { hideKeys?: any[]; agg_paths?: any; // any[]? @@ -102,7 +101,7 @@ export type Settings = { splitValues: any[]; textStyles: TextStyles; sunburstLevel?: any; - columns?: ColumnSettings; + columns_config?: PerspectiveColumnConfig; treemaps?: Record; }; @@ -195,15 +194,6 @@ export interface ChartElement extends IPerspectiveViewerPlugin { get max_columns(): number; set max_columns(value: number); - get plugin_attributes(): { - symbol: { - symbols: { - name: string; - html: string; - }[]; - }; - }; - _draw(): void; getContainer(): HTMLElement; diff --git a/packages/perspective-viewer-d3fc/test/js/barWidth.spec.ts b/packages/perspective-viewer-d3fc/test/js/barWidth.spec.ts index 58c12110ca..31f768f05a 100644 --- a/packages/perspective-viewer-d3fc/test/js/barWidth.spec.ts +++ b/packages/perspective-viewer-d3fc/test/js/barWidth.spec.ts @@ -10,7 +10,7 @@ // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃ // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ -import { test, expect } from "@finos/perspective-test"; +import { test, expect, DEFAULT_CONFIG } from "@finos/perspective-test"; import { API_VERSION, compareSVGContentsToSnapshot, @@ -41,19 +41,12 @@ test.describe("Bar Width", () => { ); expect(config).toEqual({ - version: API_VERSION, + ...DEFAULT_CONFIG, plugin: "Y Bar", columns: ["Profit"], group_by: ["Order Date"], split_by: ["Profit"], - aggregates: {}, - filter: [], - sort: [], - plugin_config: {}, - settings: false, - expressions: {}, theme: "Pro Light", - title: null, }); await compareSVGContentsToSnapshot( diff --git a/packages/perspective-viewer-d3fc/test/js/scatter.spec.ts b/packages/perspective-viewer-d3fc/test/js/scatter.spec.ts index 57f9dfe7fc..0bbe2b1f1c 100644 --- a/packages/perspective-viewer-d3fc/test/js/scatter.spec.ts +++ b/packages/perspective-viewer-d3fc/test/js/scatter.spec.ts @@ -56,7 +56,7 @@ test.describe("Scatter Tests", () => { await compareSVGContentsToSnapshot( page, "perspective-viewer perspective-viewer-d3fc-xyscatter", - ["xyscatter-label"] + ["xyscatter-label.txt"] ); }); @@ -75,7 +75,7 @@ test.describe("Scatter Tests", () => { await compareSVGContentsToSnapshot( page, "perspective-viewer perspective-viewer-d3fc-xyscatter", - ["xyscatter-label-grouped"] + ["xyscatter-label-grouped.txt"] ); }); }); diff --git a/packages/perspective-viewer-datagrid/package.json b/packages/perspective-viewer-datagrid/package.json index 42f61e8520..9a1ce4309c 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": "2.8.1", + "version": "2.9.0", "description": "Perspective datagrid plugin based on `regular-table`", "unpkg": "dist/cdn/perspective-viewer-datagrid.js", "jsdelivr": "dist/cdn/perspective-viewer-datagrid.js", @@ -29,14 +29,14 @@ "author": "", "license": "Apache-2.0", "dependencies": { - "@finos/perspective": "^2.8.1", - "@finos/perspective-viewer": "^2.8.1", + "@finos/perspective": "^2.9.0", + "@finos/perspective-viewer": "^2.9.0", "chroma-js": "^1.3.4", "regular-table": "=0.6.4" }, "devDependencies": { "@prospective.co/procss": "^0.1.13", - "@finos/perspective-esbuild-plugin": "^2.8.1", - "@finos/perspective-test": "^2.8.1" + "@finos/perspective-esbuild-plugin": "^2.9.0", + "@finos/perspective-test": "^2.9.0" } } diff --git a/packages/perspective-viewer-datagrid/src/js/custom_elements/datagrid.js b/packages/perspective-viewer-datagrid/src/js/custom_elements/datagrid.js index d87bab1c54..7da7a38023 100644 --- a/packages/perspective-viewer-datagrid/src/js/custom_elements/datagrid.js +++ b/packages/perspective-viewer-datagrid/src/js/custom_elements/datagrid.js @@ -14,7 +14,7 @@ import { activate } from "../plugin/activate.js"; import { restore } from "../plugin/restore.js"; import { save } from "../plugin/save"; import { draw } from "../plugin/draw"; -import getDefaultConfig from "../default_config.js"; +import column_style_controls from "../plugin/column_style_controls.js"; import datagridStyles from "../../../dist/css/perspective-viewer-datagrid.css"; /** @@ -87,11 +87,12 @@ export class HTMLPerspectiveViewerDatagridPluginElement extends HTMLElement { return 1; } - /** opt-in to column styling */ - get plugin_attributes() { - return { - style: getDefaultConfig.call(this), - }; + can_render_column_styles(type, _group) { + return type !== "boolean"; + } + + column_style_controls(type, group) { + return column_style_controls.call(this, type, group); } async draw(view) { @@ -122,8 +123,8 @@ export class HTMLPerspectiveViewerDatagridPluginElement extends HTMLElement { return save.call(this); } - restore(token) { - return restore.call(this, token); + restore(token, columns_config) { + return restore.call(this, token, columns_config); } async restyle(view) { diff --git a/packages/perspective-viewer-datagrid/src/js/custom_elements/toolbar.js b/packages/perspective-viewer-datagrid/src/js/custom_elements/toolbar.js index a9cfbf2778..45c8b78931 100644 --- a/packages/perspective-viewer-datagrid/src/js/custom_elements/toolbar.js +++ b/packages/perspective-viewer-datagrid/src/js/custom_elements/toolbar.js @@ -34,9 +34,9 @@ export class HTMLPerspectiveViewerDatagridToolbarElement extends HTMLElement {
- Free Scroll + - Read Only +
`; diff --git a/packages/perspective-viewer-datagrid/src/js/data_listener/formatter_cache.js b/packages/perspective-viewer-datagrid/src/js/data_listener/formatter_cache.js index e4a4b16a53..5855323053 100644 --- a/packages/perspective-viewer-datagrid/src/js/data_listener/formatter_cache.js +++ b/packages/perspective-viewer-datagrid/src/js/data_listener/formatter_cache.js @@ -32,12 +32,12 @@ export class FormatterCache { create_datetime_formatter(type, plugin) { const type_config = get_type_config(type); if (type === "datetime") { - if (plugin.format !== "custom") { + if (plugin.date_format?.format !== "custom") { const options = { ...type_config.format, - timeZone: plugin.timeZone, - dateStyle: plugin.dateStyle, - timeStyle: plugin.timeStyle, + timeZone: plugin.date_format?.timeZone, + dateStyle: plugin.date_format?.dateStyle, + timeStyle: plugin.date_format?.timeStyle, }; if (options.dateStyle === "disabled") { options.dateStyle = undefined; @@ -51,20 +51,21 @@ export class FormatterCache { options.timeStyle = type_config.format.timeStyle; } - return new Intl.DateTimeFormat([], options); + return new Intl.DateTimeFormat(navigator.languages, options); } else { const options = { // ...type_config.format, - timeZone: plugin.timeZone, - second: plugin.second, - minute: plugin.minute, - hour: plugin.hour, - day: plugin.day, - weekday: plugin.weekday, - month: plugin.month, - year: plugin.year, - hour12: plugin.hour12, - fractionalSecondDigits: plugin.fractionalSecondDigits, + timeZone: plugin.date_format?.timeZone, + second: plugin.date_format?.second, + minute: plugin.date_format?.minute, + hour: plugin.date_format?.hour, + day: plugin.date_format?.day, + weekday: plugin.date_format?.weekday, + month: plugin.date_format?.month, + year: plugin.date_format?.year, + hour12: plugin.date_format?.hour12, + fractionalSecondDigits: + plugin.date_format?.fractionalSecondDigits, }; if (options.year === "disabled") { @@ -111,12 +112,12 @@ export class FormatterCache { options.hour12 = true; } - return new Intl.DateTimeFormat([], options); + return new Intl.DateTimeFormat(navigator.languages, options); } } else { const options = { ...type_config.format, - dateStyle: plugin.dateStyle, + dateStyle: plugin.date_format?.dateStyle, }; if (options.dateStyle === "disabled") { @@ -125,42 +126,32 @@ export class FormatterCache { options.dateStyle = type_config.format.dateStyle; } - return new Intl.DateTimeFormat([], options); + return new Intl.DateTimeFormat(navigator.languages, options); } } create_number_formatter(type, plugin) { - const { format } = get_type_config(type); - if (plugin.fixed !== undefined) { - format.minimumFractionDigits = plugin.fixed; - format.maximumFractionDigits = plugin.fixed; + let { format } = get_type_config(type); + if (plugin.number_format !== undefined) { + format = plugin.number_format; } - return new FORMATTER_CONS[type]([], format); + return new FORMATTER_CONS[type](navigator.languages, format); } create_boolean_formatter(type, plugin) { const type_config = get_type_config(type); - return new FORMATTER_CONS[type]([], type_config.format); + return new FORMATTER_CONS[type]( + navigator.languages, + type_config.format + ); } get(type, plugin) { let formatter_key = [ type, - plugin.fixed, - plugin.timeZone, - plugin.dateStyle, - plugin.timeStyle, - plugin.fractionalSecondDigits, - plugin.format, - plugin.year, - plugin.month, - plugin.day, - plugin.weekday, - plugin.hour, - plugin.minute, - plugin.second, - plugin.hour12, + ...Object.values(plugin.date_format ?? {}), + ...Object.values(plugin.number_format ?? {}), ].join("-"); if (!this._formatters.has(formatter_key)) { diff --git a/packages/perspective-viewer-datagrid/src/js/data_listener/index.js b/packages/perspective-viewer-datagrid/src/js/data_listener/index.js index cb932c4a78..9bbed7b989 100644 --- a/packages/perspective-viewer-datagrid/src/js/data_listener/index.js +++ b/packages/perspective-viewer-datagrid/src/js/data_listener/index.js @@ -94,7 +94,7 @@ export function createDataListener(viewer) { ); metadata.push(column); if (is_settings_open) { - path_parts.push("Edit"); + path_parts.push(""); } column_headers.push(path_parts); diff --git a/packages/perspective-viewer-datagrid/src/js/model/toolbar.js b/packages/perspective-viewer-datagrid/src/js/model/toolbar.js index 236a49f483..6d0c88a19e 100644 --- a/packages/perspective-viewer-datagrid/src/js/model/toolbar.js +++ b/packages/perspective-viewer-datagrid/src/js/model/toolbar.js @@ -19,11 +19,6 @@ export function toggle_edit_mode(force = undefined) { this.classList.toggle("editable", force); if (this._edit_mode !== undefined) { this._edit_mode.classList.toggle("editable", force); - if (force) { - this._edit_mode.children[0].textContent = "Editable"; - } else { - this._edit_mode.children[0].textContent = "Read Only"; - } } } @@ -36,10 +31,5 @@ export function toggle_scroll_lock(force = undefined) { this.classList.toggle("sub-cell-scroll-disabled", force); if (this._scroll_lock !== undefined) { this._scroll_lock.classList.toggle("lock-scroll", force); - if (!force) { - this._scroll_lock.children[0].textContent = "Free Scroll"; - } else { - this._scroll_lock.children[0].textContent = "Align Scroll"; - } } } diff --git a/packages/perspective-viewer-datagrid/src/js/default_config.js b/packages/perspective-viewer-datagrid/src/js/plugin/column_style_controls.js similarity index 75% rename from packages/perspective-viewer-datagrid/src/js/default_config.js rename to packages/perspective-viewer-datagrid/src/js/plugin/column_style_controls.js index 7c733094e3..0acd74ce9a 100644 --- a/packages/perspective-viewer-datagrid/src/js/default_config.js +++ b/packages/perspective-viewer-datagrid/src/js/plugin/column_style_controls.js @@ -11,14 +11,14 @@ // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ /** - * Gets the default column configurations used for styling. - * @returns The default configuration per type. + * @param {import("@finos/perspective").Type} type + * @param {string} _group + * @returns {import("@finos/perspective-viewer").PerspectiveColumnConfigValue} */ -export default function getDefaultConfig() { - const get_type_default = (column_type) => { - let type_default; - if (column_type === "integer" || column_type === "float") { - type_default = { +export default function column_style_opts(type, _group) { + if (type === "integer" || type === "float") + return { + datagrid_number_style: { fg_gradient: 0, pos_fg_color: this.model._pos_fg_color[0], neg_fg_color: this.model._neg_fg_color[0], @@ -27,28 +27,21 @@ export default function getDefaultConfig() { pos_bg_color: this.model._pos_bg_color[0], neg_bg_color: this.model._neg_bg_color[0], number_bg_mode: "disabled", - fixed: column_type === "float" ? 2 : 0, - }; - } else { - // date, datetime, string, boolean - type_default = { + }, + number_string_format: true, + }; + else if (type === "date" || type === "datetime" || type === "string") { + let control = + type === "date" || type === "datetime" + ? "datagrid_datetime_style" + : `datagrid_string_style`; + return { + [control]: { color: this.model._color[0], bg_color: this.model._color[0], - }; - } - return type_default; - }; - - let default_config = {}; - for (let val of [ - "string", - "float", - "integer", - "bool", - "date", - "datetime", - ]) { - default_config[val] = get_type_default(val); + }, + }; + } else { + return null; } - return default_config; } diff --git a/packages/perspective-viewer-datagrid/src/js/plugin/restore.js b/packages/perspective-viewer-datagrid/src/js/plugin/restore.js index 75f4ae0d1c..c80c6549b4 100644 --- a/packages/perspective-viewer-datagrid/src/js/plugin/restore.js +++ b/packages/perspective-viewer-datagrid/src/js/plugin/restore.js @@ -19,43 +19,43 @@ import { make_color_record } from "../color_utils.js"; * Restore this plugin's state from a previously saved `token`. * * @param {*} token A token returned from `save()`. + * @param {import("@finos/perspective-viewer").PerspectiveColumnConfig} columns Viewer column settings */ -export function restore(token) { +export function restore(token, columns) { token = JSON.parse(JSON.stringify(token)); + columns = JSON.parse(JSON.stringify(columns)); const overrides = {}; - if (token.columns) { - for (const col of Object.keys(token.columns)) { - const col_config = token.columns[col]; - if (col_config.column_size_override !== undefined) { - overrides[col] = col_config.column_size_override; - delete col_config["column_size_override"]; - } - - if (col_config?.pos_fg_color) { - col_config.pos_fg_color = make_color_record( - col_config.pos_fg_color - ); - col_config.neg_fg_color = make_color_record( - col_config.neg_fg_color - ); - } - - if (col_config?.pos_bg_color) { - col_config.pos_bg_color = make_color_record( - col_config.pos_bg_color - ); - col_config.neg_bg_color = make_color_record( - col_config.neg_bg_color - ); - } - if (col_config?.color) { - col_config.color = make_color_record(col_config.color); + if (token.columns) { + for (const [col, value] of Object.entries(token.columns)) { + if (value.column_size_override !== undefined) { + overrides[col] = value.column_size_override; + delete value["column_size_override"]; } + } + } - if (Object.keys(col_config).length === 0) { - delete token.columns[col]; - } + let styles = {}; + if (columns) { + for (const [col_name, controls] of Object.entries(columns)) { + styles[col_name] = { + ...controls, + pos_fg_color: controls.pos_fg_color + ? make_color_record(controls.pos_fg_color) + : undefined, + neg_fg_color: controls.neg_fg_color + ? make_color_record(controls.neg_fg_color) + : undefined, + pos_bg_color: controls.pos_bg_color + ? make_color_record(controls.pos_bg_color) + : undefined, + neg_bg_color: controls.neg_bg_color + ? make_color_record(controls.neg_bg_color) + : undefined, + color: controls.color + ? make_color_record(controls.color) + : undefined, + }; } } @@ -69,5 +69,5 @@ export function restore(token) { const datagrid = this.regular_table; restore_column_size_overrides.call(this, overrides, true); - datagrid[PRIVATE_PLUGIN_SYMBOL] = token.columns; + datagrid[PRIVATE_PLUGIN_SYMBOL] = styles; } diff --git a/packages/perspective-viewer-datagrid/src/js/plugin/save.js b/packages/perspective-viewer-datagrid/src/js/plugin/save.js index 54abbfa217..c5646bd209 100644 --- a/packages/perspective-viewer-datagrid/src/js/plugin/save.js +++ b/packages/perspective-viewer-datagrid/src/js/plugin/save.js @@ -20,32 +20,12 @@ import { save_column_size_overrides } from "../model/column_overrides.js"; */ export function save() { if (this.regular_table) { - const datagrid = this.regular_table; const token = { columns: {}, scroll_lock: !!this._is_scroll_lock, editable: !!this._is_edit_mode, }; - for (const col of Object.keys(datagrid[PRIVATE_PLUGIN_SYMBOL] || {})) { - const config = Object.assign( - {}, - datagrid[PRIVATE_PLUGIN_SYMBOL][col] - ); - if (config?.pos_fg_color || config?.pos_bg_color) { - config.pos_fg_color = config.pos_fg_color?.[0]; - config.neg_fg_color = config.neg_fg_color?.[0]; - config.pos_bg_color = config.pos_bg_color?.[0]; - config.neg_bg_color = config.neg_bg_color?.[0]; - } - - if (config?.color) { - config.color = config.color[0]; - } - - token.columns[col] = config; - } - const column_size_overrides = save_column_size_overrides.call(this); for (const col of Object.keys(column_size_overrides || {})) { diff --git a/packages/perspective-viewer-datagrid/src/less/regular_table.less b/packages/perspective-viewer-datagrid/src/less/regular_table.less index bb731d551a..0a82fdac68 100644 --- a/packages/perspective-viewer-datagrid/src/less/regular_table.less +++ b/packages/perspective-viewer-datagrid/src/less/regular_table.less @@ -319,9 +319,18 @@ regular-table table { content: none; } } + +regular-table + #psp-column-edit-buttons + th:not(.rt-group-corner) + span:not(.rt-column-resize):before { + content: var(--datagrid-column-edit-button--content, "Edit"); +} + perspective-viewer:not([settings]) { @include design-slash-architecture-bug; } + :host-context(perspective-viewer:not([settings])) { @include design-slash-architecture-bug; } diff --git a/packages/perspective-viewer-datagrid/src/less/toolbar.less b/packages/perspective-viewer-datagrid/src/less/toolbar.less index b2ce63a65f..1df187f8fd 100644 --- a/packages/perspective-viewer-datagrid/src/less/toolbar.less +++ b/packages/perspective-viewer-datagrid/src/less/toolbar.less @@ -58,6 +58,22 @@ -webkit-mask-image: var(--toolbar-edit-mode-active--content); } +#edit_mode span:before { + content: var(--edit-mode-toggle--content, "Read Only"); +} + +#edit_mode.editable span:before { + content: var(--edit-mode-alt-toggle--content, "Editable"); +} + +#scroll_lock span:before { + content: var(--scroll-lock-toggle--content, "Free Scroll"); +} + +#scroll_lock.lock-scroll span:before { + content: var(--scroll-lock-alt-toggle--content, "Align Scroll"); +} + :host(.aggregated) #toolbar #edit_mode { display: none; } @@ -78,15 +94,16 @@ } .button { - display: inline-flex; align-items: center; - user-select: none; - width: 37px; - height: 22px; + border-radius: 3px; + border: 1px solid transparent; box-sizing: border-box; + display: inline-flex; + font-size: var(--label--font-size, 0.75em); + height: 22px; + user-select: none; white-space: nowrap; - border: 1px solid transparent; - border-radius: 3px; + width: 37px; } .button > span { @@ -112,6 +129,5 @@ .button:hover > span { display: contents; - font-size: 9px; line-height: 36px; } diff --git a/packages/perspective-viewer-datagrid/test/js/column_style.spec.js b/packages/perspective-viewer-datagrid/test/js/column_style.spec.js index 880d37d85a..14410d6c75 100644 --- a/packages/perspective-viewer-datagrid/test/js/column_style.spec.js +++ b/packages/perspective-viewer-datagrid/test/js/column_style.spec.js @@ -48,7 +48,7 @@ async function test_column(page, selector, selector2) { } test.describe("Column Style Tests", () => { - test("perspective-config-update event is fired when column style is changed", async ({ + test.skip("perspective-config-update event is fired when column style is changed", async ({ page, }) => { await page.goto("/tools/perspective-test/src/html/basic-test.html"); @@ -81,10 +81,11 @@ test.describe("Column Style Tests", () => { viewer.addEventListener( "perspective-column-style-change", (evt) => { - console.log(evt.type, evt.detail); + // console.log(evt.type, evt.detail); window.__events__.push(evt); } ); + // Find the column config menu button const header_button = viewer .querySelector("perspective-viewer-datagrid") @@ -112,9 +113,7 @@ test.describe("Column Style Tests", () => { const { x: xx, y: yy } = await page.evaluate(async (style_menu) => { // Find the 'bar' button - const bar_button = style_menu.querySelector( - '#radio-list-1[name="foreground-list"]' - ); + const bar_button = style_menu.querySelector("select"); // Get its coords const rect = bar_button.getBoundingClientRect(); @@ -153,9 +152,9 @@ test.describe("Column Style Tests", () => { await viewer.restore({ plugin: "Datagrid", columns: ["Row ID", "Sales"], - plugin_config: { - columns: { - Sales: { number_bg_mode: "pulse" }, + columns_config: { + Sales: { + datagrid_number_style: { number_bg_mode: "pulse" }, }, }, }); @@ -190,9 +189,9 @@ test.describe("Column Style Tests", () => { plugin: "Datagrid", columns: ["Row ID", "Sales"], settings: true, - plugin_config: { - columns: { - Sales: { number_bg_mode: "pulse" }, + columns_config: { + Sales: { + datagrid_number_style: { number_bg_mode: "pulse" }, }, }, }); diff --git a/packages/perspective-viewer-datagrid/test/js/superstore.spec.js b/packages/perspective-viewer-datagrid/test/js/superstore.spec.js index 65eaa9b887..c2e07abbfe 100644 --- a/packages/perspective-viewer-datagrid/test/js/superstore.spec.js +++ b/packages/perspective-viewer-datagrid/test/js/superstore.spec.js @@ -59,7 +59,7 @@ test.describe("Datagrid with superstore data set", () => { compareContentsToSnapshot( await getDatagridContents(page), - "row-headers-are-printed-correctly" + "row-headers-are-printed-correctly.txt" ); }); @@ -85,7 +85,7 @@ test.describe("Datagrid with superstore data set", () => { compareContentsToSnapshot( await getDatagridContents(page), - "column-headers-are-printed-correctly-split-by-a-date-column" + "column-headers-are-printed-correctly-split-by-a-date-column.txt" ); }); }); diff --git a/packages/perspective-viewer-openlayers/package.json b/packages/perspective-viewer-openlayers/package.json index f7e650ef36..5eef8502c1 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": "2.8.1", + "version": "2.9.0", "unpkg": "dist/cdn/perspective-viewer-openlayers.js", "jsdelivr": "dist/cdn/perspective-viewer-openlayers.js", "exports": { @@ -24,8 +24,8 @@ "clean:screenshots": "rimraf \"test/screenshots/**/*.@(failed|diff).png\"" }, "dependencies": { - "@finos/perspective": "^2.8.1", - "@finos/perspective-viewer": "^2.8.1", + "@finos/perspective": "^2.9.0", + "@finos/perspective-viewer": "^2.9.0", "d3": "^7.1.1", "d3-color": "^3.0.1", "gradient-parser": "1.0.2", @@ -33,6 +33,6 @@ "ol": "^5.3.2" }, "devDependencies": { - "@finos/perspective-esbuild-plugin": "^2.8.1" + "@finos/perspective-esbuild-plugin": "^2.9.0" } } diff --git a/packages/perspective-viewer-openlayers/src/js/plugin/plugin.js b/packages/perspective-viewer-openlayers/src/js/plugin/plugin.js index 1bc4c0b753..8ef32cdb74 100644 --- a/packages/perspective-viewer-openlayers/src/js/plugin/plugin.js +++ b/packages/perspective-viewer-openlayers/src/js/plugin/plugin.js @@ -49,10 +49,6 @@ views.forEach(async (plugin) => { return "OpenStreetMap"; } - get plugin_attributes() { - return {}; - } - async restyle(view) { mapView.restyle(this.shadowRoot.children[1]); } diff --git a/packages/perspective-webpack-plugin/package.json b/packages/perspective-webpack-plugin/package.json index 8e9ee501dd..62c586bbba 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": "2.8.1", + "version": "2.9.0", "description": "Perspective.js Webpack Plugin", "main": "index.js", "publishConfig": { @@ -25,8 +25,8 @@ "worker-loader": "^3.0.7" }, "peerDependencies": { - "@finos/perspective": "^2.8.1", - "@finos/perspective-viewer": "^2.8.1", + "@finos/perspective": "^2.9.0", + "@finos/perspective-viewer": "^2.9.0", "webpack": "^5.60.0" } } diff --git a/packages/perspective-workspace/package.json b/packages/perspective-workspace/package.json index 64ff6a3d4b..425219543b 100644 --- a/packages/perspective-workspace/package.json +++ b/packages/perspective-workspace/package.json @@ -1,6 +1,6 @@ { "name": "@finos/perspective-workspace", - "version": "2.8.1", + "version": "2.9.0", "description": "Perspective Workspace", "files": [ "dist/**/*", @@ -31,7 +31,7 @@ "author": "", "license": "Apache-2.0", "dependencies": { - "@finos/perspective-viewer": "^2.8.1", + "@finos/perspective-viewer": "^2.9.0", "@lumino/algorithm": "^1.9.1", "@lumino/commands": "^1.20.0", "@lumino/domutils": "^1.8.1", @@ -42,7 +42,7 @@ }, "devDependencies": { "@prospective.co/procss": "^0.1.15", - "@finos/perspective-esbuild-plugin": "^2.8.1", - "@finos/perspective-test": "^2.8.1" + "@finos/perspective-esbuild-plugin": "^2.9.0", + "@finos/perspective-test": "^2.9.0" } } diff --git a/packages/perspective-workspace/test/js/migrate_workspace.spec.js b/packages/perspective-workspace/test/js/migrate_workspace.spec.js index 3a602c131f..6418ac17c4 100644 --- a/packages/perspective-workspace/test/js/migrate_workspace.spec.js +++ b/packages/perspective-workspace/test/js/migrate_workspace.spec.js @@ -65,6 +65,7 @@ const TESTS = [ table: "superstore", title: "One", plugin: "Y Area", + columns_config: {}, plugin_config: {}, group_by: ["bucket(\"Order Date\", 'M')"], split_by: ["Ship Mode"], diff --git a/packages/perspective/package.json b/packages/perspective/package.json index 9f3813d2ac..ca4dceeeac 100644 --- a/packages/perspective/package.json +++ b/packages/perspective/package.json @@ -1,6 +1,6 @@ { "name": "@finos/perspective", - "version": "2.8.1", + "version": "2.9.0", "description": "Perspective.js", "repository": { "type": "git", @@ -46,8 +46,8 @@ "ws": "^6.1.2" }, "devDependencies": { - "@finos/perspective-cpp": "^2.8.1", - "@finos/perspective-esbuild-plugin": "^2.8.1", + "@finos/perspective-cpp": "^2.9.0", + "@finos/perspective-esbuild-plugin": "^2.9.0", "cpy": "^9.0.1", "jsverify": "^0.8.4", "lodash": "^4.17.4", diff --git a/packages/perspective/src/js/perspective.node.js b/packages/perspective/src/js/perspective.node.js index cfaa16ddd4..2f09fd2430 100644 --- a/packages/perspective/src/js/perspective.node.js +++ b/packages/perspective/src/js/perspective.node.js @@ -89,9 +89,9 @@ const DEFAULT_ASSETS = [ ]; const CONTENT_TYPES = { - ".js": "text/javascript", - ".css": "text/css", - ".json": "application/json", + ".js": "text/javascript; charset=utf-8", + ".css": "text/css; charset=utf-8", + ".json": "application/json; charset=utf-8", ".arrow": "arraybuffer", ".feather": "arraybuffer", ".wasm": "application/wasm", diff --git a/python/perspective/package.json b/python/perspective/package.json index 3a749d0a3e..bf407c9d16 100644 --- a/python/perspective/package.json +++ b/python/perspective/package.json @@ -1,16 +1,16 @@ { "private": true, "name": "perspective-python-internal", - "version": "2.8.1", + "version": "2.9.0", "scripts": { "bench": "python3 bench/perspective_benchmark.py", "docs": "python3 docs/generate.py" }, "devDependencies": { - "@finos/perspective": "^2.8.1", - "@finos/perspective-viewer": "^2.8.1", - "@finos/perspective-viewer-d3fc": "^2.8.1", - "@finos/perspective-viewer-datagrid": "^2.8.1", - "@finos/perspective-webpack-plugin": "^2.8.1" + "@finos/perspective": "^2.9.0", + "@finos/perspective-viewer": "^2.9.0", + "@finos/perspective-viewer-d3fc": "^2.9.0", + "@finos/perspective-viewer-datagrid": "^2.9.0", + "@finos/perspective-webpack-plugin": "^2.9.0" } } diff --git a/python/perspective/perspective/client/base.py b/python/perspective/perspective/client/base.py index 94e4fe64ac..4a738a2bef 100644 --- a/python/perspective/perspective/client/base.py +++ b/python/perspective/perspective/client/base.py @@ -97,7 +97,9 @@ def send(self, msg): def get_hosted_table_names(self): msg = {"cmd": "get_hosted_table_names"} - return self.post(msg) + future = asyncio.Future() + self.post(msg, future=future) + return future def post(self, msg, future=None, keep_alive=False): """Given a message and an associated `Future` object, store the future diff --git a/python/perspective/perspective/core/_version.py b/python/perspective/perspective/core/_version.py index c780466961..a05b7f1caf 100644 --- a/python/perspective/perspective/core/_version.py +++ b/python/perspective/perspective/core/_version.py @@ -1,2 +1,2 @@ -__version__ = "2.8.1" -major_minor_version = "2.8" +__version__ = "2.9.0" +major_minor_version = "2.9" diff --git a/python/perspective/perspective/tests/handlers/test_starlette_handler.py b/python/perspective/perspective/tests/handlers/test_starlette_handler.py index 2a25bb5b1d..05123f54d3 100644 --- a/python/perspective/perspective/tests/handlers/test_starlette_handler.py +++ b/python/perspective/perspective/tests/handlers/test_starlette_handler.py @@ -16,6 +16,7 @@ import pytest import random +from contextlib import asynccontextmanager from datetime import datetime from fastapi import FastAPI, WebSocket @@ -58,11 +59,16 @@ def setup_method(self): MANAGER._tables = {} MANAGER._views = {} - async def websocket_client(self): - """Connect and initialize a websocket client connection to the + @asynccontextmanager + async def managed_websocket_client(self): + """Connect and manage a websocket client connection to the Perspective starlette server. """ - return await websocket(CLIENT, "/websocket") + client = await websocket(CLIENT, "/websocket") + try: + yield client + finally: + await client.terminate() @pytest.mark.asyncio async def test_starlette_handler_init_terminate(self): @@ -71,8 +77,9 @@ async def test_starlette_handler_init_terminate(self): All test methods must import `app`, `http_client`, and `http_port`, otherwise a mysterious timeout will occur.""" - client = await self.websocket_client() - await client.terminate() + async with self.managed_websocket_client() as client: + # terminate() is called by the context manager + assert client is not None @pytest.mark.asyncio async def test_starlette_handler_table_method(self): @@ -80,37 +87,33 @@ async def test_starlette_handler_table_method(self): _table = Table(data) MANAGER.host_table(table_name, _table) - client = await self.websocket_client() - table = client.open_table(table_name) + async with self.managed_websocket_client() as client: + table = client.open_table(table_name) - schema = await table.schema() - size = await table.size() + schema = await table.schema() + size = await table.size() - assert schema == { - "a": "integer", - "b": "float", - "c": "string", - "d": "datetime", - } + assert schema == { + "a": "integer", + "b": "float", + "c": "string", + "d": "datetime", + } - assert size == 10 - - await client.terminate() + assert size == 10 @pytest.mark.asyncio async def test_starlette_handler_make_table(self): - client = await self.websocket_client() - table = await client.table(data) - size = await table.size() - - assert size == 10 + async with self.managed_websocket_client() as client: + table = await client.table(data) + size = await table.size() - table.update(data) + assert size == 10 - size2 = await table.size() - assert size2 == 20 + table.update(data) - await client.terminate() + size2 = await table.size() + assert size2 == 20 @pytest.mark.asyncio async def test_starlette_handler_table_update(self): @@ -118,18 +121,16 @@ async def test_starlette_handler_table_update(self): _table = Table(data) MANAGER.host_table(table_name, _table) - client = await self.websocket_client() - table = client.open_table(table_name) - size = await table.size() + async with self.managed_websocket_client() as client: + table = client.open_table(table_name) + size = await table.size() - assert size == 10 + assert size == 10 - table.update(data) + table.update(data) - size2 = await table.size() - assert size2 == 20 - - await client.terminate() + size2 = await table.size() + assert size2 == 20 @pytest.mark.asyncio async def test_starlette_handler_table_update_port(self, sentinel): @@ -137,34 +138,32 @@ async def test_starlette_handler_table_update_port(self, sentinel): _table = Table(data) MANAGER.host_table(table_name, _table) - client = await self.websocket_client() - table = client.open_table(table_name) - view = await table.view() - - size = await table.size() + async with self.managed_websocket_client() as client: + table = client.open_table(table_name) + view = await table.view() - assert size == 10 + size = await table.size() - for i in range(5): - await table.make_port() + assert size == 10 - port = await table.make_port() + for i in range(5): + await table.make_port() - s = sentinel(False) + port = await table.make_port() - def updater(port_id): - s.set(True) - assert port_id == port + s = sentinel(False) - view.on_update(updater) + def updater(port_id): + s.set(True) + assert port_id == port - table.update(data, port_id=port) + view.on_update(updater) - size2 = await table.size() - assert size2 == 20 - assert s.get() is True + table.update(data, port_id=port) - await client.terminate() + size2 = await table.size() + assert size2 == 20 + assert s.get() is True @pytest.mark.asyncio async def test_starlette_handler_table_update_row_delta(self, sentinel): @@ -172,31 +171,29 @@ async def test_starlette_handler_table_update_row_delta(self, sentinel): _table = Table(data) MANAGER.host_table(table_name, _table) - client = await self.websocket_client() - table = client.open_table(table_name) - view = await table.view() + async with self.managed_websocket_client() as client: + table = client.open_table(table_name) + view = await table.view() - size = await table.size() + size = await table.size() - assert size == 10 + assert size == 10 - s = sentinel(False) + s = sentinel(False) - def updater(port_id, delta): - s.set(True) - assert port_id == 0 - t2 = Table(delta) - assert t2.view().to_dict() == data + def updater(port_id, delta): + s.set(True) + assert port_id == 0 + t2 = Table(delta) + assert t2.view().to_dict() == data - view.on_update(updater, mode="row") + view.on_update(updater, mode="row") - table.update(data) + table.update(data) - size2 = await table.size() - assert size2 == 20 - assert s.get() is True - - await client.terminate() + size2 = await table.size() + assert size2 == 20 + assert s.get() is True @pytest.mark.asyncio async def test_starlette_handler_table_update_row_delta_port(self, sentinel): @@ -204,37 +201,35 @@ async def test_starlette_handler_table_update_row_delta_port(self, sentinel): _table = Table(data) MANAGER.host_table(table_name, _table) - client = await self.websocket_client() - table = client.open_table(table_name) - view = await table.view() - - size = await table.size() + async with self.managed_websocket_client() as client: + table = client.open_table(table_name) + view = await table.view() - assert size == 10 + size = await table.size() - for i in range(5): - await table.make_port() + assert size == 10 - port = await table.make_port() + for i in range(5): + await table.make_port() - s = sentinel(False) + port = await table.make_port() - def updater(port_id, delta): - s.set(True) - assert port_id == port + s = sentinel(False) - t2 = Table(delta) - assert t2.view().to_dict() == data + def updater(port_id, delta): + s.set(True) + assert port_id == port - view.on_update(updater, mode="row") + t2 = Table(delta) + assert t2.view().to_dict() == data - table.update(data, port_id=port) + view.on_update(updater, mode="row") - size2 = await table.size() - assert size2 == 20 - assert s.get() is True + table.update(data, port_id=port) - await client.terminate() + size2 = await table.size() + assert size2 == 20 + assert s.get() is True @pytest.mark.asyncio async def test_starlette_handler_table_remove(self): @@ -242,20 +237,18 @@ async def test_starlette_handler_table_remove(self): _table = Table(data, index="a") MANAGER.host_table(table_name, _table) - client = await self.websocket_client() - table = client.open_table(table_name) - size = await table.size() + async with self.managed_websocket_client() as client: + table = client.open_table(table_name) + size = await table.size() - assert size == 10 + assert size == 10 - table.remove([i for i in range(5)]) + table.remove([i for i in range(5)]) - view = await table.view(columns=["a"]) - output = await view.to_dict() + view = await table.view(columns=["a"]) + output = await view.to_dict() - assert output == {"a": [i for i in range(5, 10)]} - - await client.terminate() + assert output == {"a": [i for i in range(5, 10)]} @pytest.mark.asyncio async def test_starlette_handler_create_view(self): @@ -263,16 +256,14 @@ async def test_starlette_handler_create_view(self): _table = Table(data) MANAGER.host_table(table_name, _table) - client = await self.websocket_client() - table = client.open_table(table_name) - view = await table.view(columns=["a"]) - output = await view.to_dict() - - assert output == { - "a": [i for i in range(10)], - } + async with self.managed_websocket_client() as client: + table = client.open_table(table_name) + view = await table.view(columns=["a"]) + output = await view.to_dict() - await client.terminate() + assert output == { + "a": [i for i in range(10)], + } @pytest.mark.asyncio async def test_starlette_handler_create_view_errors(self): @@ -280,15 +271,13 @@ async def test_starlette_handler_create_view_errors(self): _table = Table(data) MANAGER.host_table(table_name, _table) - client = await self.websocket_client() - table = client.open_table(table_name) + async with self.managed_websocket_client() as client: + table = client.open_table(table_name) - with pytest.raises(PerspectiveError) as exc: - await table.view(columns=["abcde"]) + with pytest.raises(PerspectiveError) as exc: + await table.view(columns=["abcde"]) - assert str(exc.value) == "Invalid column 'abcde' found in View columns.\n" - - await client.terminate() + assert str(exc.value) == "Invalid column 'abcde' found in View columns.\n" @pytest.mark.asyncio async def test_starlette_handler_create_view_to_arrow(self): @@ -296,15 +285,13 @@ async def test_starlette_handler_create_view_to_arrow(self): _table = Table(data) MANAGER.host_table(table_name, _table) - client = await self.websocket_client() - table = client.open_table(table_name) - view = await table.view() - output = await view.to_arrow() - expected = await table.schema() - - assert Table(output).schema(as_string=True) == expected + async with self.managed_websocket_client() as client: + table = client.open_table(table_name) + view = await table.view() + output = await view.to_arrow() + expected = await table.schema() - await client.terminate() + assert Table(output).schema(as_string=True) == expected @pytest.mark.asyncio async def test_starlette_handler_create_view_to_arrow_update(self): @@ -312,16 +299,24 @@ async def test_starlette_handler_create_view_to_arrow_update(self): _table = Table(data) MANAGER.host_table(table_name, _table) - client = await self.websocket_client() - table = client.open_table(table_name) - view = await table.view() + async with self.managed_websocket_client() as client: + table = client.open_table(table_name) + view = await table.view() - output = await view.to_arrow() + output = await view.to_arrow() - for _ in range(10): - await table.update(output) + for _ in range(10): + await table.update(output) - size2 = await table.size() - assert size2 == 110 + size2 = await table.size() + assert size2 == 110 + + @pytest.mark.asyncio + async def test_starlette_handler_get_hosted_table_names(self): + table_name = str(random.random()) + _table = Table(data) + MANAGER.host_table(table_name, _table) - await client.terminate() + async with self.managed_websocket_client() as client: + names = await client.get_hosted_table_names() + assert names == [table_name] diff --git a/rust/perspective-viewer/Cargo.lock b/rust/perspective-viewer/Cargo.lock index 7d44a87956..d2da8f306f 100644 --- a/rust/perspective-viewer/Cargo.lock +++ b/rust/perspective-viewer/Cargo.lock @@ -101,12 +101,6 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c" -[[package]] -name = "arrayvec" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" - [[package]] name = "async-lock" version = "2.8.0" @@ -153,6 +147,12 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + [[package]] name = "basic-toml" version = "0.1.7" @@ -233,6 +233,7 @@ dependencies = [ "iana-time-zone", "js-sys", "num-traits", + "serde", "wasm-bindgen", "windows-targets", ] @@ -256,7 +257,7 @@ dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim", + "strsim 0.11.0", ] [[package]] @@ -362,6 +363,51 @@ dependencies = [ "syn 2.0.39", ] +[[package]] +name = "darling" +version = "0.20.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c376d08ea6aa96aafe61237c7200d1241cb177b7d3a542d791f2d118e9cbb955" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33043dcd19068b8192064c704b3f83eb464f91f1ff527b44a4e2b08d9cdb8855" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2 1.0.69", + "quote 1.0.33", + "strsim 0.10.0", + "syn 2.0.39", +] + +[[package]] +name = "darling_macro" +version = "0.20.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5a91391accf613803c2a9bf9abccdbaa07c54b4244a5b64883f9c3c137c86be" +dependencies = [ + "darling_core", + "quote 1.0.33", + "syn 2.0.39", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", + "serde", +] + [[package]] name = "derivative" version = "2.2.0" @@ -961,6 +1007,12 @@ dependencies = [ "syn 2.0.39", ] +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + [[package]] name = "hashbrown" version = "0.14.2" @@ -988,6 +1040,12 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "http" version = "0.2.10" @@ -1028,6 +1086,12 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "implicit-clone" version = "0.4.7" @@ -1035,7 +1099,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16c5448a864f9abf124ef8bf2a3cc37eb9fd99fe2e804a8b3235d7357bca2c25" dependencies = [ "implicit-clone-derive", - "indexmap", + "indexmap 2.1.0", ] [[package]] @@ -1048,6 +1112,17 @@ dependencies = [ "syn 2.0.39", ] +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + [[package]] name = "indexmap" version = "2.1.0" @@ -1055,7 +1130,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.14.2", + "serde", ] [[package]] @@ -1248,14 +1324,10 @@ dependencies = [ ] [[package]] -name = "num-format" -version = "0.4.4" +name = "num-conv" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3" -dependencies = [ - "arrayvec", - "itoa", -] +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" [[package]] name = "num-traits" @@ -1317,7 +1389,7 @@ checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "perspective" -version = "2.8.1" +version = "2.9.0" dependencies = [ "anyhow", "async-lock", @@ -1332,12 +1404,13 @@ dependencies = [ "js-intern", "js-sys", "nom", - "num-format", "procss", "rmp-serde", "serde", "serde-wasm-bindgen 0.6.1", "serde_json", + "serde_with", + "strum 0.26.1", "tracing", "tracing-subscriber", "wasm-bindgen", @@ -1349,7 +1422,7 @@ dependencies = [ [[package]] name = "perspective-bootstrap" -version = "2.8.1" +version = "2.9.0" dependencies = [ "clap", "flate2", @@ -1358,14 +1431,14 @@ dependencies = [ [[package]] name = "perspective-bootstrap-runtime" -version = "2.8.1" +version = "2.9.0" dependencies = [ "flate2", ] [[package]] name = "perspective-bundle" -version = "2.8.1" +version = "2.9.0" dependencies = [ "wasm-bindgen-cli-support", "wasm-opt", @@ -1373,7 +1446,7 @@ dependencies = [ [[package]] name = "perspective-lint" -version = "2.8.1" +version = "2.9.0" dependencies = [ "glob", "yew-fmt", @@ -1422,6 +1495,12 @@ dependencies = [ "thiserror", ] +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "predicates" version = "2.1.5" @@ -1762,6 +1841,36 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_with" +version = "3.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15d167997bd841ec232f5b2b8e0e26606df2e7caa4c31b95ea9ca52b200bd270" +dependencies = [ + "base64 0.21.7", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.1.0", + "serde", + "serde_derive", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "865f9743393e638991566a8b7a479043c2c8da94a33e0a31f18214c9cae0a64d" +dependencies = [ + "darling", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 2.0.39", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -1786,6 +1895,12 @@ version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "strsim" version = "0.11.0" @@ -1798,6 +1913,15 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" +[[package]] +name = "strum" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "723b93e8addf9aa965ebe2d11da6d7540fa2283fcea14b3371ff055f7ba13f5f" +dependencies = [ + "strum_macros 0.26.1", +] + [[package]] name = "strum_macros" version = "0.24.3" @@ -1811,6 +1935,19 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "strum_macros" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a3417fc93d76740d974a01654a09777cb500428cc874ca9f45edfe0c4d4cd18" +dependencies = [ + "heck 0.4.1", + "proc-macro2 1.0.69", + "quote 1.0.33", + "rustversion", + "syn 2.0.39", +] + [[package]] name = "syn" version = "0.15.44" @@ -1902,6 +2039,37 @@ dependencies = [ "once_cell", ] +[[package]] +name = "time" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" +dependencies = [ + "num-conv", + "time-core", +] + [[package]] name = "tokio" version = "1.34.0" @@ -1935,7 +2103,7 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap", + "indexmap 2.1.0", "toml_datetime", "winnow", ] @@ -2246,8 +2414,8 @@ checksum = "fc942673e7684671f0c5708fc18993569d184265fd5223bb51fc8e5b9b6cfd52" dependencies = [ "anyhow", "libc", - "strum", - "strum_macros", + "strum 0.24.1", + "strum_macros 0.24.3", "tempfile", "thiserror", "wasm-opt-cxx-sys", @@ -2419,7 +2587,7 @@ dependencies = [ "futures", "gloo 0.10.0", "implicit-clone", - "indexmap", + "indexmap 2.1.0", "js-sys", "prokio", "rustversion", @@ -2436,9 +2604,9 @@ dependencies = [ [[package]] name = "yew-fmt" -version = "0.3.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "341d52569c5c43344e18de1101a2243c08010d3e37fd282dbec0f21657c7eec3" +checksum = "5dd6092bf0a95eb3d61a085a9f079c67fa19ba365455df6b6036c544e22b5ce0" dependencies = [ "anyhow", "basic-toml", diff --git a/rust/perspective-viewer/Cargo.toml b/rust/perspective-viewer/Cargo.toml index fef8198338..da4ac8272e 100644 --- a/rust/perspective-viewer/Cargo.toml +++ b/rust/perspective-viewer/Cargo.toml @@ -12,7 +12,7 @@ [package] name = "perspective" -version = "2.8.1" +version = "2.9.0" authors = ["Andrew Stein "] edition = "2021" description = "A data visualization and analytics component, especially well-suited for large and/or streaming datasets." @@ -99,9 +99,6 @@ js-sys = "0.3.64" # Parse ExprTK for syntax highlighting. nom = "7.1.1" -# Comma-sep numeric representation -num-format = "0.4.4" - # MessagePack serialization rmp-serde = "1.1.1" @@ -129,6 +126,8 @@ yew = { version = "0.21.0", features = ["csr"] } # Browser stdlib bindings web-sys.version = "0.3.64" + +# Browser stdlib bindings web-sys.features = [ "Blob", "Clipboard", @@ -179,3 +178,6 @@ web-sys.features = [ "VisibilityState", "Window", ] + +serde_with = "3.6.1" +strum = { version = "0.26.1", features = ["derive"] } diff --git a/rust/perspective-viewer/build.js b/rust/perspective-viewer/build.js index 4ed5b35a70..d9a2ddb80e 100644 --- a/rust/perspective-viewer/build.js +++ b/rust/perspective-viewer/build.js @@ -40,6 +40,11 @@ const BUILD = [ plugins: [PerspectiveEsbuildPlugin({ wasm: { inline: true } })], outfile: "dist/esm/perspective-viewer.inline.js", }, + { + entryPoints: ["src/ts/migrate.ts"], + format: "esm", + outfile: "dist/esm/migrate.js", + }, { entryPoints: ["src/ts/migrate.ts"], format: "cjs", diff --git a/rust/perspective-viewer/build.rs b/rust/perspective-viewer/build.rs index 633742f2b4..1684f19b5a 100644 --- a/rust/perspective-viewer/build.rs +++ b/rust/perspective-viewer/build.rs @@ -58,6 +58,7 @@ fn main() -> Result<(), anyhow::Error> { if !cfg!(feature = "define_custom_elements_async") { build.add_file("variables.less"); build.add_file("icons.less"); + build.add_file("intl.less"); build.add_file("pro.less"); build.add_file("pro-dark.less"); build.add_file("monokai.less"); @@ -68,6 +69,12 @@ fn main() -> Result<(), anyhow::Error> { build.add_file("gruvbox-dark.less"); build.add_file("dracula.less"); build.add_file("themes.less"); + build.add_file("intl/de.less"); + build.add_file("intl/es.less"); + build.add_file("intl/fr.less"); + build.add_file("intl/ja.less"); + build.add_file("intl/pt.less"); + build.add_file("intl/zh.less"); build.compile()?.write("./target/themes")?; } diff --git a/rust/perspective-viewer/package.json b/rust/perspective-viewer/package.json index 2a523909bc..4e44e3b752 100644 --- a/rust/perspective-viewer/package.json +++ b/rust/perspective-viewer/package.json @@ -1,6 +1,6 @@ { "name": "@finos/perspective-viewer", - "version": "2.8.1", + "version": "2.9.0", "description": "The `` Custom Element, frontend for Perspective.js", "repository": { "type": "git", @@ -40,12 +40,12 @@ "access": "public" }, "dependencies": { - "@finos/perspective": "^2.8.1" + "@finos/perspective": "^2.9.0" }, "devDependencies": { "react": "^16.14.0", - "@finos/perspective-esbuild-plugin": "^2.8.1", - "@finos/perspective-test": "^2.8.1", + "@finos/perspective-esbuild-plugin": "^2.9.0", + "@finos/perspective-test": "^2.9.0", "cpy": "^9.0.1" } } diff --git a/rust/perspective-viewer/rustfmt.toml b/rust/perspective-viewer/rustfmt.toml index 6ea11f7060..526324e83b 100644 --- a/rust/perspective-viewer/rustfmt.toml +++ b/rust/perspective-viewer/rustfmt.toml @@ -38,4 +38,5 @@ group_imports = "StdExternalCrate" imports_granularity = "Module" newline_style = "Unix" condense_wildcard_suffixes = true -match_block_trailing_comma = true \ No newline at end of file +match_block_trailing_comma = true +yew.use_small_heuristics = "Max" diff --git a/rust/perspective-viewer/src/less/column-dropdown.less b/rust/perspective-viewer/src/less/column-dropdown.less index 9261292d95..f49c5eff75 100644 --- a/rust/perspective-viewer/src/less/column-dropdown.less +++ b/rust/perspective-viewer/src/less/column-dropdown.less @@ -24,7 +24,7 @@ position: fixed; z-index: 10000; outline: none; - font-size: 12px; + font-size: 0.75em; border: inherit; // box-shadow: 0 2px 4px 0 rgb(0 0 0 / 10%); user-select: none; @@ -56,6 +56,10 @@ font-size: 8px; } + .no-results:before { + content: var(--no-results--content, "Invalid Column"); + } + #add-expression { &:before { @include icon; diff --git a/rust/perspective-viewer/src/less/column-selector.less b/rust/perspective-viewer/src/less/column-selector.less index 8233913f2b..e2fe08dd7f 100644 --- a/rust/perspective-viewer/src/less/column-selector.less +++ b/rust/perspective-viewer/src/less/column-selector.less @@ -10,6 +10,8 @@ // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃ // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ +@import "dom/scrollbar.less"; + // TODO this could be a class @mixin icon { background-repeat: no-repeat; @@ -21,6 +23,10 @@ } :host { + #add-expression-button:before { + content: var(--add-expression-button--content, "New Column"); + } + .column-selector-column-title { display: flex; width: 100%; @@ -78,7 +84,6 @@ border: var(--column-add--border, 1px solid transparent); min-height: 24px; flex-direction: row; - font-size: 12px; background-color: #8b868045; border-radius: 2px; display: flex; @@ -182,7 +187,6 @@ display: flex; flex-direction: row-reverse; align-items: center; - font-size: 12px; // Expression column toolbar buttons span.expression-edit-button, @@ -231,7 +235,6 @@ cursor: move; display: flex; align-items: start; - font-size: 12px; flex-grow: 1; color: inherit; box-sizing: border-box; @@ -309,30 +312,16 @@ overflow-x: hidden !important; overflow-y: scroll; min-height: 20%; - - &::-webkit-scrollbar-thumb { - border: 0 solid var(--icon--color); - border-left-width: 1px; - } - - &::-webkit-scrollbar-thumb:hover { - background-color: rgba(0, 0, 0, 0.1); - } - - &::-webkit-scrollbar, - &::-webkit-scrollbar-corner { - background-color: transparent; - width: 6px; - } + @include scrollbar; } #sub-columns:before { - font-size: 9px; + font-size: var(--label--font-size, 0.75em); padding: var(--column_type--padding, 0px 0px 0px 0px); position: absolute; margin-top: 14px; top: 0; - content: "All Columns"; + content: var(--all-columns-label--content, "All Columns"); } #sub-columns #add-expression { @@ -405,6 +394,30 @@ } } + // .column-selector-column[data-index="0"]:before { + // content: var(--column-selector-column-0--label, attr(data-label)); + // } + + // .column-selector-column[data-index="1"]:before { + // content: var(--column-selector-column-1--label, attr(data-label)); + // } + + // .column-selector-column[data-index="2"]:before { + // content: var(--column-selector-column-2--label, attr(data-label)); + // } + + // .column-selector-column[data-index="3"]:before { + // content: var(--column-selector-column-3--label, attr(data-label)); + // } + + // .column-selector-column[data-index="4"]:before { + // content: var(--column-selector-column-4--label, attr(data-label)); + // } + + // .column-selector-column[data-index="5"]:before { + // content: var(--column-selector-column-5--label, attr(data-label)); + // } + .column-selector-column { position: relative; @@ -418,12 +431,12 @@ } &:before { - font-size: 9px; + font-size: var(--label--font-size, 0.75em); left: 0px; padding: var(--column_type--padding, 0px 0px 0px 0px); position: absolute; margin-top: -43px; - content: attr(data-label); + content: var(--default-column-title, attr(data-label)); } &[data-label] { diff --git a/rust/perspective-viewer/src/less/column-settings-panel.less b/rust/perspective-viewer/src/less/column-settings-panel.less index aeb435a796..5275b7bf72 100644 --- a/rust/perspective-viewer/src/less/column-settings-panel.less +++ b/rust/perspective-viewer/src/less/column-settings-panel.less @@ -25,7 +25,7 @@ .item_title { flex: 0 0 auto; - font-size: 9px; + font-size: var(--label--font-size, 0.75em); } .radio-list-item label { @@ -41,7 +41,7 @@ border: none; margin-bottom: 0.5em; font-family: inherit; - font-size: 12px; + font-size: 1em; &:disabled { background-color: var(--inactive--color); } @@ -94,4 +94,138 @@ } } } + + label#color-label:before { + content: var(--color-label--content, "Color"); + } + + label#format-label:before { + content: var(--format-label--content, "Format"); + } + + label#timezone-label:before { + content: var(--timezone-label--content, "Timezone"); + } + + label#date-style-label:before { + content: var(--date-style-label--content, "Date Style"); + } + + label#time-style-label:before { + content: var(--time-style-label--content, "Time Style"); + } + + label#foreground-label:before { + content: var(--foreground-label--content, "Foreground"); + } + + label#background-label:before { + content: var(--background-label--content, "Background"); + } + + label#series-label:before { + content: var(--series-label--content, "Series"); + } + + label#color-range-label:before { + content: var(--color-range-label--content, "Color Range"); + } + + label#style-label:before { + content: var(--style-label--content, "Style"); + } + + label#minimum-integer-digits-label:before { + content: var( + --minimum-integer-digits-label--content, + "Minimum Integer Digits" + ); + } + + label#rounding-increment-label:before { + content: var(--rounding-increment-label--content, "Rounding Increment"); + } + + label#notation-label:before { + content: var(--notation-label--content, "Notation"); + } + + label#use-grouping-label:before { + content: var(--use-grouping-label--content, "Use Grouping"); + } + + label#sign-display-label:before { + content: var(--sign-display-label--content, "Sign Display"); + } + + label#max-value-label:before { + content: var(--max-value-label--content, "Max Value"); + } + + label#rounding-priority-label:before { + content: var(--rounding-priority-label--content, "Rounding Priority"); + } + + label#rounding-mode-label:before { + content: var(--rounding-mode-label--content, "Rounding Mode"); + } + + label#trailing-zero-display-label:before { + content: var( + --trailing-zero-display-label--content, + "Trailing Zero Display" + ); + } + + label#significant-digits-label:before { + content: var(--significant-digits-label--content, "Significant Digits"); + } + + label#fractional-digits-label:before { + content: var(--fractional-digits-label--content, "Fractional Digits"); + } + + label#year-label:before { + content: var(--year-label--content, "Year"); + } + + label#month-label:before { + content: var(--month-label--content, "Month"); + } + + label#day-label:before { + content: var(--day-label--content, "Day"); + } + + label#weekday-label:before { + content: var(--weekday-label--content, "Weekday"); + } + + label#hour-label:before { + content: var(--hour-label--content, "Hour"); + } + + label#minute-label:before { + content: var(--minute-label--content, "Minute"); + } + + label#second-label:before { + content: var(--second-label--content, "Second"); + } + + label#fractional-seconds-label:before { + content: var(--fractional-seconds-label--content, "Fractional Seconds"); + } + + label#hours-label:before { + content: var(--hours-label--content, "12/24 Hours"); + } + + div.tab-title#Style:before { + content: var(--style-tab-label--content, "Style"); + } + + div.tab-title#Attributes:before { + content: var(--attributes-tab-label--content, "Attributes"); + } } diff --git a/rust/perspective-viewer/src/less/column-style.less b/rust/perspective-viewer/src/less/column-style.less index 432d978496..b801e231b1 100644 --- a/rust/perspective-viewer/src/less/column-style.less +++ b/rust/perspective-viewer/src/less/column-style.less @@ -22,8 +22,6 @@ :host { #column-style-container { outline: none; - font-size: 12px; - border: inherit; user-select: none; &.no-style { @@ -31,20 +29,27 @@ background-color: #8b868045; } + .is-default-value .dropdown-width-container { + background-color: var(--plugin--background); + } + .dropdown-width-container { + cursor: pointer; height: 24px; border: 1px solid transparent; border-radius: 2px; padding: 0 8px; border-radius: 2px; - border: 1px solid transparent; + // border: 1px solid transparent; + border-color: var(--inactive--color, #666); &:hover { - border-color: var(--inactive--color, #666); + background-color: var(--plugin--background); } } select { + cursor: pointer; font-size: 10px; height: 24px; // padding-bottom: 2px; @@ -53,10 +58,39 @@ } label { - font-size: 9px; + display: block; + font-size: var(--label--font-size, 0.75em); + margin: 4px 0 2px 0; width: 100%; } + span.reset-default-style-disabled { + margin-left: 4px; + width: 14px; + height: 14px; + border: 1px solid var(--inactive--border-color); + border-radius: 2px; + } + + span.reset-default-style { + cursor: pointer; + width: 22px; + height: 22px; + margin-right: -4px; + + &:before { + @include icon; + width: 22px; + height: 22px; + -webkit-mask-image: var(--close-icon--mask-image); + mask-image: var(--close-icon--mask-image); + } + } + + .is-default-value input.parameter { + background-color: var(--plugin--background); + } + input.parameter { background: none; color: inherit; @@ -66,14 +100,29 @@ input.parameter[type="number"] { flex: 1 1 auto; - text-align: right; - border-bottom-width: 1px; + text-align: left; + font-family: inherit; + width: 0; + border-radius: 2px; + font-size: 10px; + height: 24px; + padding-left: 8px; + border-width: 1px; border-color: var( --input--border-color, var(--inactive--color, inherit) ); } + input.parameter.parameter-min[type="number"] { + border-radius: 2px 0 0 2px; + } + + input.parameter.parameter-max[type="number"] { + border-radius: 0 2px 2px 0; + border-left-width: 0px; + } + input[type="number"]::-webkit-inner-spin-button, input[type="number"]::-webkit-outer-spin-button { opacity: 1; @@ -113,11 +162,18 @@ } div.section { - margin-right: 6px; - margin-bottom: 8px; + display: flex; + align-items: center; flex: 1 1 100%; } + div.bool-field { + display: flex; + label { + padding: 0 8px; + } + } + div.inner_section { margin-top: 4px; width: 0px; @@ -129,54 +185,74 @@ display: flex; align-items: center; flex-wrap: wrap; + margin-bottom: 10px; + } + + fieldset { + margin: 0; + } + + fieldset.style-control { + border: none; + padding: 0px 0; } .color-gradient-container { display: flex; + align-items: center; flex-wrap: nowrap; - flex: 1 1 auto; + flex: 1 1 100%; .color-thermometer { flex: 1 1 auto; height: 24px; } } + .color-selector { + display: grid; + + input { + grid-column: 1; + grid-row: 1; + } + .color-label { + font-size: 18.5px; + grid-column: 1; + grid-row: 1; + margin: 0; + font-family: var(--button--font-family, inherit); + color: var(--sign--color, white); + width: 36px; + height: 24px; + text-align: center; + z-index: 1; + } + } + input[type="color"] { - width: 36px; + width: 100%; height: 24px; cursor: pointer; padding: 0; font-family: inherit; overflow: hidden; - border-radius: 6px; + border-radius: 12px; &:hover { opacity: 0.8; } - &:before { - position: absolute; - font-family: var(--button--font-family, inherit); - margin-top: -1px; - margin-left: 12px; - font-size: 20px; - content: var(--column-style-pos-color--content, "+"); - color: var(--sign--color, white); - } - - &#pos-color-param { - border-radius: 6px 0 0 6px; + &.pos-color-param { + width: 36px; + border-radius: 12px 0 0 12px; margin: 0 1px 0 0; } - &#neg-color-param { - border-radius: 0 6px 6px 0; + &.neg-color-param { + width: 36px; + border-radius: 0 12px 12px 0; margin: 0 0 0 1px; } - - &#neg-color-param:before { - content: var(--column-style-neg-color--content, "-"); - } } ::-webkit-color-swatch-wrapper { diff --git a/rust/perspective-viewer/src/less/column-symbol-attributes.less b/rust/perspective-viewer/src/less/column-symbol-attributes.less index 9bd247e2f4..83ac78dee5 100644 --- a/rust/perspective-viewer/src/less/column-symbol-attributes.less +++ b/rust/perspective-viewer/src/less/column-symbol-attributes.less @@ -49,7 +49,7 @@ } &:before { - font-size: 9px; + font-size: var(--label--font-size, 0.75em); left: 0px; padding: var(--column_type--padding, 0px 0px 0px 0px); position: absolute; diff --git a/rust/perspective-viewer/src/less/config-selector.less b/rust/perspective-viewer/src/less/config-selector.less index ce9a4b2872..aa22a451f2 100644 --- a/rust/perspective-viewer/src/less/config-selector.less +++ b/rust/perspective-viewer/src/less/config-selector.less @@ -162,23 +162,19 @@ } #group_by label.pivot-selector-label:before { - content: var(--group_by--content, "Group By"); + content: var(--group-by-label--content, "Group By"); } #split_by label.pivot-selector-label:before { - content: var(--split_by--content, "Split By"); + content: var(--split-by-label--content, "Split By"); } #sort label.pivot-selector-label:before { - content: "Order By"; + content: var(--sort-label--content, "Order By"); } - // #filter { - // min-width: 300px; - // } - #filter label.pivot-selector-label:before { - content: "Where"; + content: var(--filter-label--content, "Where"); } .highlight-drop { @@ -235,7 +231,6 @@ display: none; margin: 0; padding: 0 0 0 0; - font-size: 12px; min-height: 26px; // margin-bottom: -1px; @@ -259,7 +254,7 @@ // color: var(--inactive--color); white-space: nowrap; padding: var(--column-drop-container--padding, 0px); - font-size: 12px; + font-size: var(--label--font-size, 0.75em); display: inline-block; } @@ -271,7 +266,7 @@ position: absolute; top: 0px; margin: var(--column-drop-label--margin, -16px 0px 0px 0px); - font-size: 9px; //var(--column-drop-label--font-size, 10px); + font-size: var(--label--font-size, 0.75em); display: var(--column-drop-label--display, none); } @@ -279,7 +274,7 @@ cursor: pointer; flex-grow: 0; font-family: inherit; - font-size: 9px; + font-size: var(--label--font-size, 0.75em); user-select: none; padding: 0; align-self: center; diff --git a/rust/perspective-viewer/src/less/containers/dropdown-menu.less b/rust/perspective-viewer/src/less/containers/dropdown-menu.less index 98e1b22308..10b5e22714 100644 --- a/rust/perspective-viewer/src/less/containers/dropdown-menu.less +++ b/rust/perspective-viewer/src/less/containers/dropdown-menu.less @@ -14,7 +14,7 @@ position: fixed; z-index: 10000; outline: none; - font-size: 14px; + font-size: 0.75em; border: inherit; box-shadow: 0 2px 4px 0 rgb(0 0 0 / 10%); user-select: none; @@ -26,9 +26,7 @@ min-width: 80px; code { - font-size: 12px; - font-family: var(--interface-monospace--font-family), - monospace; + font-family: var(--interface-monospace--font-family), monospace; } input { @@ -38,9 +36,7 @@ border: none; border-bottom: 1px solid var(--inactive--color, #ccc); background: transparent; - font-family: var(--interface-monospace--font-family), - monospace; - font-size: 12px; + font-family: var(--interface-monospace--font-family), monospace; color: inherit; outline: none; } @@ -55,7 +51,7 @@ } .dropdown-group-label { - font-size: 10px; + font-size: var(--label--font-size, 0.75em); } .dropdown-group-container { @@ -90,4 +86,4 @@ .dropdown-group-container span:hover { background-color: rgba(0, 0, 0, 0.05); } -} \ No newline at end of file +} diff --git a/rust/perspective-viewer/src/less/containers/tabs.less b/rust/perspective-viewer/src/less/containers/tabs.less index fa36bd1b37..32577c08d1 100644 --- a/rust/perspective-viewer/src/less/containers/tabs.less +++ b/rust/perspective-viewer/src/less/containers/tabs.less @@ -10,6 +10,8 @@ // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃ // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ +@import "dom/scrollbar.less"; + :host { .tab-gutter { border-color: var(--inactive--color, #6e6e6e); @@ -34,7 +36,7 @@ cursor: pointer; .tab-title { - font-size: 12px; + font-size: 10px; padding: 10px; border-bottom: 1px solid var(--inactive--color, #6e6e6e); } @@ -61,12 +63,15 @@ } } .tab-content { + overflow-y: scroll; + max-height: calc(100% - 90px); + @include scrollbar; + .tab-section { - padding: 8px; + padding: 8px 4px 8px 8px; // border-bottom: 1px solid var(--inactive--border-color); } .text { - font-size: 14px; margin-left: 1em; } } diff --git a/rust/perspective-viewer/src/less/dom/scrollbar.less b/rust/perspective-viewer/src/less/dom/scrollbar.less new file mode 100644 index 0000000000..0d57dd1cba --- /dev/null +++ b/rust/perspective-viewer/src/less/dom/scrollbar.less @@ -0,0 +1,28 @@ +// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃ +// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃ +// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃ +// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃ +// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫ +// ┃ Copyright (c) 2017, the Perspective Authors. ┃ +// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃ +// ┃ This file is part of the Perspective library, distributed under the terms ┃ +// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃ +// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + +@mixin scrollbar { + &::-webkit-scrollbar-thumb { + border: 0 solid var(--icon--color); + border-left-width: 1px; + } + + &::-webkit-scrollbar-thumb:hover { + background-color: rgba(0, 0, 0, 0.1); + } + + &::-webkit-scrollbar, + &::-webkit-scrollbar-corner { + background-color: transparent; + width: 6px; + } +} \ No newline at end of file diff --git a/rust/perspective-viewer/src/less/empty-column.less b/rust/perspective-viewer/src/less/empty-column.less index cb7e912031..6aab52ca04 100644 --- a/rust/perspective-viewer/src/less/empty-column.less +++ b/rust/perspective-viewer/src/less/empty-column.less @@ -59,7 +59,6 @@ width: calc(100% - 7px); outline: none; padding-left: 10px; - font-size: 12px; font-family: inherit; } diff --git a/rust/perspective-viewer/src/less/expression-editor.less b/rust/perspective-viewer/src/less/expression-editor.less index 5736230f8e..6250d3a407 100644 --- a/rust/perspective-viewer/src/less/expression-editor.less +++ b/rust/perspective-viewer/src/less/expression-editor.less @@ -24,7 +24,6 @@ white-space: nowrap; overflow: hidden; text-overflow: ellipsis; - font-size: 12px; min-height: 1.5em; color: var(--error--color); top: 0; @@ -125,7 +124,6 @@ flex: 1; justify-content: center; font-family: inherit; - font-size: 12px; border: 1px solid var(--icon--color); height: 18px; padding: 2px 12px; diff --git a/rust/perspective-viewer/src/less/filter-dropdown.less b/rust/perspective-viewer/src/less/filter-dropdown.less index 2a0d1ed399..3ddbd15023 100644 --- a/rust/perspective-viewer/src/less/filter-dropdown.less +++ b/rust/perspective-viewer/src/less/filter-dropdown.less @@ -14,7 +14,7 @@ position: fixed; z-index: 10000; outline: none; - font-size: 12px; + font-size: 0.75em; border: inherit; box-shadow: 0 2px 4px 0 rgb(0 0 0 / 10%); user-select: none; diff --git a/rust/perspective-viewer/src/less/filter-item.less b/rust/perspective-viewer/src/less/filter-item.less index 5bbf333efd..f3a74bc845 100644 --- a/rust/perspective-viewer/src/less/filter-item.less +++ b/rust/perspective-viewer/src/less/filter-item.less @@ -22,7 +22,6 @@ align-items: center; position: relative; margin-top: 1px; - font-size: 12px; min-width: 45px; overflow: hidden; height: 16px; @@ -42,7 +41,6 @@ border: none; border-bottom: 1px solid var(--inactive--color, #ccc); background: transparent; - font-size: 12px; color: inherit; } diff --git a/rust/perspective-viewer/src/less/form/code-editor.less b/rust/perspective-viewer/src/less/form/code-editor.less index 671376d600..d2e0b9b2c2 100644 --- a/rust/perspective-viewer/src/less/form/code-editor.less +++ b/rust/perspective-viewer/src/less/form/code-editor.less @@ -21,7 +21,6 @@ padding: 6px; box-sizing: border-box; position: absolute; - font-size: 12px; font-weight: 400; font-family: var(--interface-monospace--font-family, monospace); top: 0; @@ -45,7 +44,6 @@ left: 0; right: 0; pointer-events: none; - font-size: 12px; font-weight: 400; font-family: var(--interface-monospace--font-family, monospace); color: var(--code-editor-error--color, red); @@ -92,8 +90,8 @@ position: absolute; width: 100%; height: 100%; - font-size: 12px; font-family: var(--interface-monospace--font-family, monospace); + font-size: 1em; resize: none; padding: 6px; padding-left: 36px; @@ -107,4 +105,4 @@ background-color: transparent; } } -} \ No newline at end of file +} diff --git a/rust/perspective-viewer/src/less/function-dropdown.less b/rust/perspective-viewer/src/less/function-dropdown.less index 6437cdb252..54be01fb8a 100644 --- a/rust/perspective-viewer/src/less/function-dropdown.less +++ b/rust/perspective-viewer/src/less/function-dropdown.less @@ -14,7 +14,7 @@ position: fixed; z-index: 10000; outline: none; - font-size: 12px; + font-size: 0.75em; border: inherit; box-shadow: 0 2px 4px 0 rgb(0 0 0 / 10%); user-select: none; diff --git a/rust/perspective-viewer/src/less/plugin-selector.less b/rust/perspective-viewer/src/less/plugin-selector.less index f723e49241..070aeee050 100644 --- a/rust/perspective-viewer/src/less/plugin-selector.less +++ b/rust/perspective-viewer/src/less/plugin-selector.less @@ -58,6 +58,12 @@ .plugin-select-item-name { padding-left: 10px; + font-size: 1.33333em; + } + + .plugin-select-item-name:before { + // This value is set in a `style` tag from Yew! + content: var(--default-column-title); } .plugin-select-item { diff --git a/rust/perspective-viewer/src/less/render-warning.less b/rust/perspective-viewer/src/less/render-warning.less index 430e7e02e0..dd0bb6e126 100644 --- a/rust/perspective-viewer/src/less/render-warning.less +++ b/rust/perspective-viewer/src/less/render-warning.less @@ -34,7 +34,6 @@ } .plugin_information__text { - font-size: 12px; margin-right: 0.25rem; display: flex; flex-wrap: wrap; @@ -58,7 +57,6 @@ } .plugin_information__action { - font-size: 12px; text-decoration: underline; cursor: pointer; margin-right: 0.25rem; diff --git a/rust/perspective-viewer/src/less/status-bar.less b/rust/perspective-viewer/src/less/status-bar.less index ba53022f35..ae6b83b23a 100644 --- a/rust/perspective-viewer/src/less/status-bar.less +++ b/rust/perspective-viewer/src/less/status-bar.less @@ -47,6 +47,14 @@ right: 0; height: var(--status-bar--height, 48px); border-radius: var(--status-bar--border-radius); + #status-bar-placeholder { + margin: 0px; + } + + input:placeholder-shown + #status-bar-placeholder:before { + content: var(--untitled--content, "untitled"); + color: var(--inactive--color); + } .input-sizer { display: inline-block; @@ -74,7 +82,6 @@ padding: 0; border: none; background: transparent; - font-size: 12px; color: inherit; height: 100%; } @@ -85,7 +92,6 @@ visibility: hidden; white-space: nowrap; padding-right: 12px; - font-size: 12px; } &:focus-within { @@ -111,7 +117,6 @@ overflow: hidden; span { white-space: nowrap; - font-size: 12px; } } @@ -124,7 +129,7 @@ height: 22px; select { height: 20px; - font-size: 9px; + font-size: var(--label--font-size, 0.75em); } } } @@ -138,7 +143,7 @@ } span { - font-size: 9px; + // font-size: var(--label--font-size, 0.75em); margin: 0px 10px; user-select: none; height: 100%; @@ -210,6 +215,9 @@ -webkit-mask-image: url("../svg/export-icon.svg"); mask-image: url("../svg/export-icon.svg"); } + span:before { + content: var(--export-button--content, "Export"); + } } span#lock { @@ -217,6 +225,9 @@ -webkit-mask-image: url("../svg/free-scroll-icon.svg"); mask-image: url("../svg/free-scroll-icon.svg"); } + // span:before { + // content: var(--export-button--content, "Export"); + // } } span#reset { @@ -224,6 +235,9 @@ -webkit-mask-image: url("../svg/revert-icon.svg"); mask-image: url("../svg/revert-icon.svg"); } + span:before { + content: var(--reset-button--content, "Reset"); + } } span#copy { @@ -231,6 +245,9 @@ -webkit-mask-image: url("../svg/duplicate-icon.svg"); mask-image: url("../svg/duplicate-icon.svg"); } + span:before { + content: var(--copy-button--content, "Copy"); + } } span#theme { @@ -248,7 +265,7 @@ color: var(--inactive--color, #ccc); border: 1px solid transparent; border-radius: 3px; - font-size: 9px; + font-size: var(--label--font-size, 0.75em); & > span, & > .dropdown-width-container { @@ -259,7 +276,6 @@ &:before { z-index: 1; - font-size: 14px; } &:hover select { diff --git a/rust/perspective-viewer/src/less/viewer.less b/rust/perspective-viewer/src/less/viewer.less index 0861d8df9c..79bf5c1695 100644 --- a/rust/perspective-viewer/src/less/viewer.less +++ b/rust/perspective-viewer/src/less/viewer.less @@ -57,6 +57,7 @@ flex-direction: column; align-items: stretch; font-family: Arial; + font-size: 0.75em; * { box-sizing: border-box; @@ -227,7 +228,7 @@ &:before { font-feature-settings: "liga"; - content: var(--config-button-icon--content, "\1f527"); + content: var(--config-button-icon--content, "Configure"); } } @@ -293,7 +294,6 @@ #expr_panel_header_title, .sidebar_header_title { padding-left: 9px; - font-size: 12px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; diff --git a/rust/perspective-viewer/src/rust/components/column_dropdown.rs b/rust/perspective-viewer/src/rust/components/column_dropdown.rs index 8f2369bb9b..b6df9a4b8f 100644 --- a/rust/perspective-viewer/src/rust/components/column_dropdown.rs +++ b/rust/perspective-viewer/src/rust/components/column_dropdown.rs @@ -147,7 +147,7 @@ impl Component for ColumnDropDown { } }) } } else { - { "Invalid Column" } + } } }; @@ -157,6 +157,6 @@ impl Component for ColumnDropDown { self.width, self.width ); - html! { <>{ body } } + html! { <>{ body } } } } diff --git a/rust/perspective-viewer/src/rust/components/column_selector.rs b/rust/perspective-viewer/src/rust/components/column_selector.rs index df4d3f7577..258e7a1b33 100644 --- a/rust/perspective-viewer/src/rust/components/column_selector.rs +++ b/rust/perspective-viewer/src/rust/components/column_selector.rs @@ -239,10 +239,7 @@ impl Component for ColumnSelector { ); let config_selector = html_nested! { - + + + + +
- + Option { + fn _get_view_type(&self) -> Option { self.get_name() .as_ref() .and_then(|x| self.session.metadata().get_column_view_type(x)) @@ -286,6 +286,20 @@ impl Component for ActiveColumn { } }); + let path: String = name + .0 + .clone() + .unwrap_or_default() + .chars() + .map(|x| { + if x.is_alphanumeric() { + x.to_ascii_lowercase() + } else { + '-' + } + }) + .collect(); + let col_type = ctx.props().get_table_type(); match (name, col_type) { ((label, ColumnState::Empty), _) => { @@ -306,6 +320,7 @@ impl Component for ActiveColumn {
@@ -319,6 +334,7 @@ impl Component for ActiveColumn {
@@ -367,34 +383,23 @@ impl Component for ActiveColumn { class.push("required"); }; - // TODO: This doesn't scale well. Need a better attrs API. - // Thankfully this will be removed when we unify expression and table columns. - let show_edit_btn = match &*ctx.props().renderer.get_active_plugin().unwrap().name() - { - "Datagrid" => col_type != Type::Bool, - "X/Y Scatter" => { - ctx.props() - .get_view_type() - .map(|ty| ty == Type::String) - .unwrap_or_default() - && label.as_deref() == Some("Symbol") - }, - _ => false, - } || is_expression; + let can_render_styles = ctx + .props() + .can_render_column_styles(&name) + .unwrap_or_default(); + let show_edit_btn = is_expression || can_render_styles; html! {
- +
-
- +
+ if ctx.props().is_aggregated { +
diff --git a/rust/perspective-viewer/src/rust/components/column_selector/add_expression_button.rs b/rust/perspective-viewer/src/rust/components/column_selector/add_expression_button.rs index 3f1dcd94b5..99a4a0411b 100644 --- a/rust/perspective-viewer/src/rust/components/column_selector/add_expression_button.rs +++ b/rust/perspective-viewer/src/rust/components/column_selector/add_expression_button.rs @@ -41,23 +41,19 @@ pub fn AddExpressionButton(p: &AddExpressionButtonProps) -> Html { mo.set(false); }); - let onmousedown = p.on_open_expr_panel.reform(|_| ColumnLocator::Expr(None)); - let class = if *is_mouseover || matches!(p.selected_column, Some(ColumnLocator::Expr(None))) { + let onmousedown = p + .on_open_expr_panel + .reform(|_| ColumnLocator::NewExpression); + let class = if *is_mouseover || matches!(p.selected_column, Some(ColumnLocator::NewExpression)) + { classes!("dragdrop-hover") } else { classes!() }; html! { -
- { "New Column" } +
+
} } diff --git a/rust/perspective-viewer/src/rust/components/column_selector/aggregate_selector.rs b/rust/perspective-viewer/src/rust/components/column_selector/aggregate_selector.rs index 8c47af08c7..f18be32da5 100644 --- a/rust/perspective-viewer/src/rust/components/column_selector/aggregate_selector.rs +++ b/rust/perspective-viewer/src/rust/components/column_selector/aggregate_selector.rs @@ -93,12 +93,8 @@ impl Component for AggregateSelector { html! { <> - -
+ +
wrapper_class="aggregate-selector" {values} diff --git a/rust/perspective-viewer/src/rust/components/column_selector/config_selector.rs b/rust/perspective-viewer/src/rust/components/column_selector/config_selector.rs index 7585b9566e..716b32b909 100644 --- a/rust/perspective-viewer/src/rust/components/column_selector/config_selector.rs +++ b/rust/perspective-viewer/src/rust/components/column_selector/config_selector.rs @@ -471,15 +471,8 @@ impl Component for ConfigSelector { }); html! { -
- +
+ if !config.group_by.is_empty() && config.split_by.is_empty() { - +
+ Html { let onmousedown = yew::use_callback(p.clone(), |_, p| { let name = if p.is_expression { - ColumnLocator::Expr(Some(p.name.clone())) + ColumnLocator::Expression(p.name.clone()) } else { - ColumnLocator::Plain(p.name.clone()) + ColumnLocator::Table(p.name.clone()) }; p.on_open_expr_panel.emit(name) }); diff --git a/rust/perspective-viewer/src/rust/components/column_selector/filter_column.rs b/rust/perspective-viewer/src/rust/components/column_selector/filter_column.rs index 7a2a4c8481..508b45869d 100644 --- a/rust/perspective-viewer/src/rust/components/column_selector/filter_column.rs +++ b/rust/perspective-viewer/src/rust/components/column_selector/filter_column.rs @@ -422,7 +422,7 @@ impl Component for FilterColumn { class="string-filter" spellcheck="false" // TODO This is dirty and it may not work in the future. - onInput="this.parentNode.dataset.value=this.value" + onInput="this.parentNode.dataset.value=this.value" ref={noderef.clone()} onkeydown={keydown} onfocus={focus} @@ -484,24 +484,19 @@ impl Component for FilterColumn { ondragstart={dragstart} ondragend={dragend} > - -
+ +
{ filter.0.to_owned() } if !matches!(&filter.1, FilterOp::IsNotNull | FilterOp::IsNull) { - if col_type == Some(Type::Bool) { - { input_elem } - } else { + if col_type == Some(Type::Bool) { { input_elem } } else {